mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +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}
|
${APP_SPEC_XML_FORMAT}
|
||||||
|
|
||||||
CRITICAL FORMATTING REQUIREMENTS:
|
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
|
- 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 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 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>
|
- 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 start IMMEDIATELY with <project_specification> with no preceding text
|
||||||
- Your response must end IMMEDIATELY with </project_specification> with no following text
|
- Your response must end IMMEDIATELY with </project_specification> with no following text
|
||||||
|
|||||||
@@ -164,6 +164,10 @@ export function createSpecGenerationOptions(
|
|||||||
): Options {
|
): Options {
|
||||||
return {
|
return {
|
||||||
...getBaseOptions(),
|
...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),
|
model: getModelForUseCase("spec", config.model),
|
||||||
maxTurns: MAX_TURNS.maximum,
|
maxTurns: MAX_TURNS.maximum,
|
||||||
cwd: config.cwd,
|
cwd: config.cwd,
|
||||||
@@ -186,6 +190,8 @@ export function createFeatureGenerationOptions(
|
|||||||
): Options {
|
): Options {
|
||||||
return {
|
return {
|
||||||
...getBaseOptions(),
|
...getBaseOptions(),
|
||||||
|
// Override permissionMode - feature generation only needs read-only tools
|
||||||
|
permissionMode: "default",
|
||||||
model: getModelForUseCase("features", config.model),
|
model: getModelForUseCase("features", config.model),
|
||||||
maxTurns: MAX_TURNS.quick,
|
maxTurns: MAX_TURNS.quick,
|
||||||
cwd: config.cwd,
|
cwd: config.cwd,
|
||||||
|
|||||||
@@ -38,11 +38,13 @@ export async function generateSpec(
|
|||||||
|
|
||||||
if (analyzeProject !== false) {
|
if (analyzeProject !== false) {
|
||||||
// Default to true - analyze the project
|
// 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
|
- Existing technologies and frameworks
|
||||||
- Project structure and architecture
|
- Project structure and architecture
|
||||||
- Current features and capabilities
|
- 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 {
|
} else {
|
||||||
// Use default tech stack
|
// Use default tech stack
|
||||||
techStackDefaults = `Default Technology Stack:
|
techStackDefaults = `Default Technology Stack:
|
||||||
@@ -52,7 +54,9 @@ export async function generateSpec(
|
|||||||
- Styling: Tailwind CSS
|
- Styling: Tailwind CSS
|
||||||
- Frontend: React
|
- 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.
|
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") {
|
} else if (msg.type === "result" && (msg as any).subtype === "success") {
|
||||||
logger.info("Received success result");
|
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(
|
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
|
// Don't overwrite responseText - the accumulated text contains the XML spec
|
||||||
if ((msg as any).result && (msg as any).result.length > 0) {
|
// The result.result field contains Claude's conversational summary (e.g., "I've created the spec...")
|
||||||
logger.info("Using result value as responseText");
|
// which is NOT what we want to save to app_spec.txt
|
||||||
responseText = (msg as any).result;
|
// See: https://github.com/AutoMaker-Org/automaker/issues/149
|
||||||
} else {
|
|
||||||
logger.info("Result is empty, keeping accumulated responseText");
|
|
||||||
}
|
|
||||||
} else if (msg.type === "result") {
|
} else if (msg.type === "result") {
|
||||||
// Handle all result types
|
// Handle all result types
|
||||||
const subtype = (msg as any).subtype;
|
const subtype = (msg as any).subtype;
|
||||||
@@ -210,14 +211,30 @@ ${getAppSpecFormatInstruction()}`;
|
|||||||
logger.error("❌ WARNING: responseText is empty! Nothing to save.");
|
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
|
// Save spec to .automaker directory
|
||||||
const specDir = await ensureAutomakerDir(projectPath);
|
const specDir = await ensureAutomakerDir(projectPath);
|
||||||
const specPath = getAppSpecPath(projectPath);
|
const specPath = getAppSpecPath(projectPath);
|
||||||
|
|
||||||
logger.info("Saving spec to:", specPath);
|
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
|
// Verify the file was written
|
||||||
const savedContent = await fs.readFile(specPath, "utf-8");
|
const savedContent = await fs.readFile(specPath, "utf-8");
|
||||||
|
|||||||
Reference in New Issue
Block a user