mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
feat: implement E2E testing workflow and enhance XML syntax editor
- Added a new GitHub Actions workflow for end-to-end (E2E) testing, including setup for Node.js, Playwright, and server initialization. - Introduced a setup script for E2E test fixtures to create necessary directories and files. - Integrated CodeMirror for XML syntax editing in the XmlSyntaxEditor component, improving code highlighting and editing experience. - Updated package dependencies in package.json and package-lock.json to include new libraries for XML handling and theming. - Refactored various components for improved readability and consistency, including the sidebar and file browser dialog. - Added tests for spec editor persistence to ensure data integrity across sessions.
This commit is contained in:
88
apps/server/src/lib/app-spec-format.ts
Normal file
88
apps/server/src/lib/app-spec-format.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* XML Template Format Specification for app_spec.txt
|
||||
*
|
||||
* This format must be included in all prompts that generate, modify, or regenerate
|
||||
* app specifications to ensure consistency across the application.
|
||||
*/
|
||||
export const APP_SPEC_XML_FORMAT = `
|
||||
The app_spec.txt file MUST follow this exact XML format:
|
||||
|
||||
<project_specification>
|
||||
<project_name>Project Name</project_name>
|
||||
|
||||
<overview>
|
||||
A comprehensive description of what the project does, its purpose, and key goals.
|
||||
</overview>
|
||||
|
||||
<technology_stack>
|
||||
<technology>Technology 1</technology>
|
||||
<technology>Technology 2</technology>
|
||||
<!-- List all technologies, frameworks, libraries, and tools used -->
|
||||
</technology_stack>
|
||||
|
||||
<core_capabilities>
|
||||
<capability>Core capability 1</capability>
|
||||
<capability>Core capability 2</capability>
|
||||
<!-- List main features and capabilities the project provides -->
|
||||
</core_capabilities>
|
||||
|
||||
<implemented_features>
|
||||
<!-- Features that have been implemented (populated by AI agent based on code analysis) -->
|
||||
</implemented_features>
|
||||
|
||||
<!-- Optional sections that may be included: -->
|
||||
<additional_requirements>
|
||||
<!-- Any additional requirements or constraints -->
|
||||
</additional_requirements>
|
||||
|
||||
<development_guidelines>
|
||||
<guideline>Guideline 1</guideline>
|
||||
<guideline>Guideline 2</guideline>
|
||||
<!-- Development standards and practices -->
|
||||
</development_guidelines>
|
||||
|
||||
<implementation_roadmap>
|
||||
<!-- Phases or roadmap items for implementation -->
|
||||
</implementation_roadmap>
|
||||
</project_specification>
|
||||
|
||||
IMPORTANT:
|
||||
- All content must be wrapped in valid XML tags
|
||||
- Use proper XML escaping for special characters (<, >, &)
|
||||
- Maintain proper indentation (2 spaces)
|
||||
- All sections should be populated based on project analysis
|
||||
- The format must be strictly followed - do not use markdown, JSON, or any other format
|
||||
`;
|
||||
|
||||
/**
|
||||
* Returns a prompt suffix that instructs the AI to format the response as XML
|
||||
* following the app_spec.txt template format.
|
||||
*/
|
||||
export function getAppSpecFormatInstruction(): string {
|
||||
return `
|
||||
${APP_SPEC_XML_FORMAT}
|
||||
|
||||
CRITICAL FORMATTING REQUIREMENTS:
|
||||
- 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 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
|
||||
- Use ONLY XML tags as shown in the template
|
||||
- Properly escape XML special characters (< for <, > for >, & for &)
|
||||
- Maintain 2-space indentation for readability
|
||||
- The output will be saved directly to app_spec.txt and must be parseable as valid XML
|
||||
- The response must contain exactly ONE root XML element: <project_specification>
|
||||
- Do not include code blocks, markdown fences, or any other formatting
|
||||
|
||||
VERIFICATION: Before responding, verify that:
|
||||
1. Your response starts with <project_specification> (no spaces, no text before it)
|
||||
2. Your response ends with </project_specification> (no spaces, no text after it)
|
||||
3. There is exactly one root XML element
|
||||
4. There is no explanatory text, analysis, or commentary outside the XML tags
|
||||
|
||||
Your response should be ONLY the XML content, nothing else.
|
||||
`;
|
||||
}
|
||||
@@ -15,13 +15,29 @@ let currentAbortController: AbortController | null = null;
|
||||
function logAuthStatus(context: string): void {
|
||||
const hasOAuthToken = !!process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
||||
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
||||
|
||||
|
||||
console.log(`[SpecRegeneration] ${context} - Auth Status:`);
|
||||
console.log(`[SpecRegeneration] CLAUDE_CODE_OAUTH_TOKEN: ${hasOAuthToken ? 'SET (' + process.env.CLAUDE_CODE_OAUTH_TOKEN?.substring(0, 20) + '...)' : 'NOT SET'}`);
|
||||
console.log(`[SpecRegeneration] ANTHROPIC_API_KEY: ${hasApiKey ? 'SET (' + process.env.ANTHROPIC_API_KEY?.substring(0, 20) + '...)' : 'NOT SET'}`);
|
||||
|
||||
console.log(
|
||||
`[SpecRegeneration] CLAUDE_CODE_OAUTH_TOKEN: ${
|
||||
hasOAuthToken
|
||||
? "SET (" +
|
||||
process.env.CLAUDE_CODE_OAUTH_TOKEN?.substring(0, 20) +
|
||||
"...)"
|
||||
: "NOT SET"
|
||||
}`
|
||||
);
|
||||
console.log(
|
||||
`[SpecRegeneration] ANTHROPIC_API_KEY: ${
|
||||
hasApiKey
|
||||
? "SET (" + process.env.ANTHROPIC_API_KEY?.substring(0, 20) + "...)"
|
||||
: "NOT SET"
|
||||
}`
|
||||
);
|
||||
|
||||
if (!hasOAuthToken && !hasApiKey) {
|
||||
console.error(`[SpecRegeneration] ⚠️ WARNING: No authentication configured! SDK will fail.`);
|
||||
console.error(
|
||||
`[SpecRegeneration] ⚠️ WARNING: No authentication configured! SDK will fail.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +46,14 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
|
||||
// Create project spec from overview
|
||||
router.post("/create", async (req: Request, res: Response) => {
|
||||
console.log("[SpecRegeneration] ========== /create endpoint called ==========");
|
||||
console.log("[SpecRegeneration] Request body:", JSON.stringify(req.body, null, 2));
|
||||
|
||||
console.log(
|
||||
"[SpecRegeneration] ========== /create endpoint called =========="
|
||||
);
|
||||
console.log(
|
||||
"[SpecRegeneration] Request body:",
|
||||
JSON.stringify(req.body, null, 2)
|
||||
);
|
||||
|
||||
try {
|
||||
const { projectPath, projectOverview, generateFeatures } = req.body as {
|
||||
projectPath: string;
|
||||
@@ -42,7 +63,11 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
|
||||
console.log(`[SpecRegeneration] Parsed params:`);
|
||||
console.log(`[SpecRegeneration] projectPath: ${projectPath}`);
|
||||
console.log(`[SpecRegeneration] projectOverview length: ${projectOverview?.length || 0} chars`);
|
||||
console.log(
|
||||
`[SpecRegeneration] projectOverview length: ${
|
||||
projectOverview?.length || 0
|
||||
} chars`
|
||||
);
|
||||
console.log(`[SpecRegeneration] generateFeatures: ${generateFeatures}`);
|
||||
|
||||
if (!projectPath || !projectOverview) {
|
||||
@@ -55,7 +80,9 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
}
|
||||
|
||||
if (isRunning) {
|
||||
console.warn("[SpecRegeneration] Generation already running, rejecting request");
|
||||
console.warn(
|
||||
"[SpecRegeneration] Generation already running, rejecting request"
|
||||
);
|
||||
res.json({ success: false, error: "Spec generation already running" });
|
||||
return;
|
||||
}
|
||||
@@ -79,19 +106,26 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
console.error("[SpecRegeneration] Error name:", error?.name);
|
||||
console.error("[SpecRegeneration] Error message:", error?.message);
|
||||
console.error("[SpecRegeneration] Error stack:", error?.stack);
|
||||
console.error("[SpecRegeneration] Full error object:", JSON.stringify(error, Object.getOwnPropertyNames(error), 2));
|
||||
console.error(
|
||||
"[SpecRegeneration] Full error object:",
|
||||
JSON.stringify(error, Object.getOwnPropertyNames(error), 2)
|
||||
);
|
||||
events.emit("spec-regeneration:event", {
|
||||
type: "spec_error",
|
||||
error: error.message || String(error),
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
console.log("[SpecRegeneration] Generation task finished (success or error)");
|
||||
console.log(
|
||||
"[SpecRegeneration] Generation task finished (success or error)"
|
||||
);
|
||||
isRunning = false;
|
||||
currentAbortController = null;
|
||||
});
|
||||
|
||||
console.log("[SpecRegeneration] Returning success response (generation running in background)");
|
||||
console.log(
|
||||
"[SpecRegeneration] Returning success response (generation running in background)"
|
||||
);
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("[SpecRegeneration] ❌ Route handler exception:");
|
||||
@@ -103,9 +137,14 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
|
||||
// Generate from project definition
|
||||
router.post("/generate", async (req: Request, res: Response) => {
|
||||
console.log("[SpecRegeneration] ========== /generate endpoint called ==========");
|
||||
console.log("[SpecRegeneration] Request body:", JSON.stringify(req.body, null, 2));
|
||||
|
||||
console.log(
|
||||
"[SpecRegeneration] ========== /generate endpoint called =========="
|
||||
);
|
||||
console.log(
|
||||
"[SpecRegeneration] Request body:",
|
||||
JSON.stringify(req.body, null, 2)
|
||||
);
|
||||
|
||||
try {
|
||||
const { projectPath, projectDefinition } = req.body as {
|
||||
projectPath: string;
|
||||
@@ -114,7 +153,11 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
|
||||
console.log(`[SpecRegeneration] Parsed params:`);
|
||||
console.log(`[SpecRegeneration] projectPath: ${projectPath}`);
|
||||
console.log(`[SpecRegeneration] projectDefinition length: ${projectDefinition?.length || 0} chars`);
|
||||
console.log(
|
||||
`[SpecRegeneration] projectDefinition length: ${
|
||||
projectDefinition?.length || 0
|
||||
} chars`
|
||||
);
|
||||
|
||||
if (!projectPath || !projectDefinition) {
|
||||
console.error("[SpecRegeneration] Missing required parameters");
|
||||
@@ -126,7 +169,9 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
}
|
||||
|
||||
if (isRunning) {
|
||||
console.warn("[SpecRegeneration] Generation already running, rejecting request");
|
||||
console.warn(
|
||||
"[SpecRegeneration] Generation already running, rejecting request"
|
||||
);
|
||||
res.json({ success: false, error: "Spec generation already running" });
|
||||
return;
|
||||
}
|
||||
@@ -149,19 +194,26 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
console.error("[SpecRegeneration] Error name:", error?.name);
|
||||
console.error("[SpecRegeneration] Error message:", error?.message);
|
||||
console.error("[SpecRegeneration] Error stack:", error?.stack);
|
||||
console.error("[SpecRegeneration] Full error object:", JSON.stringify(error, Object.getOwnPropertyNames(error), 2));
|
||||
console.error(
|
||||
"[SpecRegeneration] Full error object:",
|
||||
JSON.stringify(error, Object.getOwnPropertyNames(error), 2)
|
||||
);
|
||||
events.emit("spec-regeneration:event", {
|
||||
type: "spec_error",
|
||||
error: error.message || String(error),
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
console.log("[SpecRegeneration] Generation task finished (success or error)");
|
||||
console.log(
|
||||
"[SpecRegeneration] Generation task finished (success or error)"
|
||||
);
|
||||
isRunning = false;
|
||||
currentAbortController = null;
|
||||
});
|
||||
|
||||
console.log("[SpecRegeneration] Returning success response (generation running in background)");
|
||||
console.log(
|
||||
"[SpecRegeneration] Returning success response (generation running in background)"
|
||||
);
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("[SpecRegeneration] ❌ Route handler exception:");
|
||||
@@ -173,9 +225,14 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
|
||||
// Generate features from existing spec
|
||||
router.post("/generate-features", async (req: Request, res: Response) => {
|
||||
console.log("[SpecRegeneration] ========== /generate-features endpoint called ==========");
|
||||
console.log("[SpecRegeneration] Request body:", JSON.stringify(req.body, null, 2));
|
||||
|
||||
console.log(
|
||||
"[SpecRegeneration] ========== /generate-features endpoint called =========="
|
||||
);
|
||||
console.log(
|
||||
"[SpecRegeneration] Request body:",
|
||||
JSON.stringify(req.body, null, 2)
|
||||
);
|
||||
|
||||
try {
|
||||
const { projectPath } = req.body as { projectPath: string };
|
||||
|
||||
@@ -188,7 +245,9 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
}
|
||||
|
||||
if (isRunning) {
|
||||
console.warn("[SpecRegeneration] Generation already running, rejecting request");
|
||||
console.warn(
|
||||
"[SpecRegeneration] Generation already running, rejecting request"
|
||||
);
|
||||
res.json({ success: false, error: "Generation already running" });
|
||||
return;
|
||||
}
|
||||
@@ -197,27 +256,38 @@ export function createSpecRegenerationRoutes(events: EventEmitter): Router {
|
||||
|
||||
isRunning = true;
|
||||
currentAbortController = new AbortController();
|
||||
console.log("[SpecRegeneration] Starting background feature generation task...");
|
||||
console.log(
|
||||
"[SpecRegeneration] Starting background feature generation task..."
|
||||
);
|
||||
|
||||
generateFeaturesFromSpec(projectPath, events, currentAbortController)
|
||||
.catch((error) => {
|
||||
console.error("[SpecRegeneration] ❌ Feature generation failed with error:");
|
||||
console.error(
|
||||
"[SpecRegeneration] ❌ Feature generation failed with error:"
|
||||
);
|
||||
console.error("[SpecRegeneration] Error name:", error?.name);
|
||||
console.error("[SpecRegeneration] Error message:", error?.message);
|
||||
console.error("[SpecRegeneration] Error stack:", error?.stack);
|
||||
console.error("[SpecRegeneration] Full error object:", JSON.stringify(error, Object.getOwnPropertyNames(error), 2));
|
||||
console.error(
|
||||
"[SpecRegeneration] Full error object:",
|
||||
JSON.stringify(error, Object.getOwnPropertyNames(error), 2)
|
||||
);
|
||||
events.emit("spec-regeneration:event", {
|
||||
type: "features_error",
|
||||
error: error.message || String(error),
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
console.log("[SpecRegeneration] Feature generation task finished (success or error)");
|
||||
console.log(
|
||||
"[SpecRegeneration] Feature generation task finished (success or error)"
|
||||
);
|
||||
isRunning = false;
|
||||
currentAbortController = null;
|
||||
});
|
||||
|
||||
console.log("[SpecRegeneration] Returning success response (generation running in background)");
|
||||
console.log(
|
||||
"[SpecRegeneration] Returning success response (generation running in background)"
|
||||
);
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("[SpecRegeneration] ❌ Route handler exception:");
|
||||
@@ -261,11 +331,15 @@ async function generateSpec(
|
||||
abortController: AbortController,
|
||||
generateFeatures?: boolean
|
||||
) {
|
||||
console.log("[SpecRegeneration] ========== generateSpec() started ==========");
|
||||
console.log(
|
||||
"[SpecRegeneration] ========== generateSpec() started =========="
|
||||
);
|
||||
console.log(`[SpecRegeneration] projectPath: ${projectPath}`);
|
||||
console.log(`[SpecRegeneration] projectOverview length: ${projectOverview.length} chars`);
|
||||
console.log(
|
||||
`[SpecRegeneration] projectOverview length: ${projectOverview.length} chars`
|
||||
);
|
||||
console.log(`[SpecRegeneration] generateFeatures: ${generateFeatures}`);
|
||||
|
||||
|
||||
const prompt = `You are helping to define a software project specification.
|
||||
|
||||
Project Overview:
|
||||
@@ -281,19 +355,23 @@ Based on this overview, analyze the project and create a comprehensive specifica
|
||||
6. **API Design** - Main endpoints/interfaces needed
|
||||
7. **User Experience** - Key user flows and interactions
|
||||
|
||||
${generateFeatures ? `
|
||||
${
|
||||
generateFeatures
|
||||
? `
|
||||
Also generate a list of features to implement. For each feature provide:
|
||||
- ID (lowercase-hyphenated)
|
||||
- Title
|
||||
- Description
|
||||
- Priority (1=high, 2=medium, 3=low)
|
||||
- Estimated complexity (simple, moderate, complex)
|
||||
` : ""}
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
Format your response as markdown. Be specific and actionable.`;
|
||||
|
||||
console.log(`[SpecRegeneration] Prompt length: ${prompt.length} chars`);
|
||||
|
||||
|
||||
events.emit("spec-regeneration:event", {
|
||||
type: "spec_progress",
|
||||
content: "Starting spec generation...\n",
|
||||
@@ -308,9 +386,12 @@ Format your response as markdown. Be specific and actionable.`;
|
||||
abortController,
|
||||
};
|
||||
|
||||
console.log("[SpecRegeneration] SDK Options:", JSON.stringify(options, null, 2));
|
||||
console.log(
|
||||
"[SpecRegeneration] SDK Options:",
|
||||
JSON.stringify(options, null, 2)
|
||||
);
|
||||
console.log("[SpecRegeneration] Calling Claude Agent SDK query()...");
|
||||
|
||||
|
||||
// Log auth status right before the SDK call
|
||||
logAuthStatus("Right before SDK query()");
|
||||
|
||||
@@ -332,13 +413,22 @@ Format your response as markdown. Be specific and actionable.`;
|
||||
try {
|
||||
for await (const msg of stream) {
|
||||
messageCount++;
|
||||
console.log(`[SpecRegeneration] Stream message #${messageCount}:`, JSON.stringify({ type: msg.type, subtype: (msg as any).subtype }, null, 2));
|
||||
|
||||
console.log(
|
||||
`[SpecRegeneration] Stream message #${messageCount}:`,
|
||||
JSON.stringify(
|
||||
{ type: msg.type, subtype: (msg as any).subtype },
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
|
||||
if (msg.type === "assistant" && msg.message.content) {
|
||||
for (const block of msg.message.content) {
|
||||
if (block.type === "text") {
|
||||
responseText = block.text;
|
||||
console.log(`[SpecRegeneration] Text block received (${block.text.length} chars)`);
|
||||
console.log(
|
||||
`[SpecRegeneration] Text block received (${block.text.length} chars)`
|
||||
);
|
||||
events.emit("spec-regeneration:event", {
|
||||
type: "spec_progress",
|
||||
content: block.text,
|
||||
@@ -356,8 +446,13 @@ Format your response as markdown. Be specific and actionable.`;
|
||||
console.log("[SpecRegeneration] Received success result");
|
||||
responseText = (msg as any).result || responseText;
|
||||
} else if ((msg as { type: string }).type === "error") {
|
||||
console.error("[SpecRegeneration] ❌ Received error message from stream:");
|
||||
console.error("[SpecRegeneration] Error message:", JSON.stringify(msg, null, 2));
|
||||
console.error(
|
||||
"[SpecRegeneration] ❌ Received error message from stream:"
|
||||
);
|
||||
console.error(
|
||||
"[SpecRegeneration] Error message:",
|
||||
JSON.stringify(msg, null, 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (streamError) {
|
||||
@@ -366,15 +461,19 @@ Format your response as markdown. Be specific and actionable.`;
|
||||
throw streamError;
|
||||
}
|
||||
|
||||
console.log(`[SpecRegeneration] Stream iteration complete. Total messages: ${messageCount}`);
|
||||
console.log(`[SpecRegeneration] Response text length: ${responseText.length} chars`);
|
||||
console.log(
|
||||
`[SpecRegeneration] Stream iteration complete. Total messages: ${messageCount}`
|
||||
);
|
||||
console.log(
|
||||
`[SpecRegeneration] Response text length: ${responseText.length} chars`
|
||||
);
|
||||
|
||||
// Save spec
|
||||
const specDir = path.join(projectPath, ".automaker");
|
||||
const specPath = path.join(specDir, "app_spec.txt");
|
||||
|
||||
console.log(`[SpecRegeneration] Saving spec to: ${specPath}`);
|
||||
|
||||
|
||||
await fs.mkdir(specDir, { recursive: true });
|
||||
await fs.writeFile(specPath, responseText);
|
||||
|
||||
@@ -391,8 +490,10 @@ Format your response as markdown. Be specific and actionable.`;
|
||||
console.log("[SpecRegeneration] Starting feature generation...");
|
||||
await parseAndCreateFeatures(projectPath, responseText, events);
|
||||
}
|
||||
|
||||
console.log("[SpecRegeneration] ========== generateSpec() completed ==========");
|
||||
|
||||
console.log(
|
||||
"[SpecRegeneration] ========== generateSpec() completed =========="
|
||||
);
|
||||
}
|
||||
|
||||
async function generateFeaturesFromSpec(
|
||||
@@ -400,9 +501,11 @@ async function generateFeaturesFromSpec(
|
||||
events: EventEmitter,
|
||||
abortController: AbortController
|
||||
) {
|
||||
console.log("[SpecRegeneration] ========== generateFeaturesFromSpec() started ==========");
|
||||
console.log(
|
||||
"[SpecRegeneration] ========== generateFeaturesFromSpec() started =========="
|
||||
);
|
||||
console.log(`[SpecRegeneration] projectPath: ${projectPath}`);
|
||||
|
||||
|
||||
// Read existing spec
|
||||
const specPath = path.join(projectPath, ".automaker", "app_spec.txt");
|
||||
let spec: string;
|
||||
@@ -411,7 +514,9 @@ async function generateFeaturesFromSpec(
|
||||
|
||||
try {
|
||||
spec = await fs.readFile(specPath, "utf-8");
|
||||
console.log(`[SpecRegeneration] Spec loaded successfully (${spec.length} chars)`);
|
||||
console.log(
|
||||
`[SpecRegeneration] Spec loaded successfully (${spec.length} chars)`
|
||||
);
|
||||
} catch (readError) {
|
||||
console.error("[SpecRegeneration] ❌ Failed to read spec file:", readError);
|
||||
events.emit("spec-regeneration:event", {
|
||||
@@ -466,9 +571,14 @@ Generate 5-15 features that build on each other logically.`;
|
||||
abortController,
|
||||
};
|
||||
|
||||
console.log("[SpecRegeneration] SDK Options:", JSON.stringify(options, null, 2));
|
||||
console.log("[SpecRegeneration] Calling Claude Agent SDK query() for features...");
|
||||
|
||||
console.log(
|
||||
"[SpecRegeneration] SDK Options:",
|
||||
JSON.stringify(options, null, 2)
|
||||
);
|
||||
console.log(
|
||||
"[SpecRegeneration] Calling Claude Agent SDK query() for features..."
|
||||
);
|
||||
|
||||
logAuthStatus("Right before SDK query() for features");
|
||||
|
||||
let stream;
|
||||
@@ -489,13 +599,22 @@ Generate 5-15 features that build on each other logically.`;
|
||||
try {
|
||||
for await (const msg of stream) {
|
||||
messageCount++;
|
||||
console.log(`[SpecRegeneration] Feature stream message #${messageCount}:`, JSON.stringify({ type: msg.type, subtype: (msg as any).subtype }, null, 2));
|
||||
|
||||
console.log(
|
||||
`[SpecRegeneration] Feature stream message #${messageCount}:`,
|
||||
JSON.stringify(
|
||||
{ type: msg.type, subtype: (msg as any).subtype },
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
|
||||
if (msg.type === "assistant" && msg.message.content) {
|
||||
for (const block of msg.message.content) {
|
||||
if (block.type === "text") {
|
||||
responseText = block.text;
|
||||
console.log(`[SpecRegeneration] Feature text block received (${block.text.length} chars)`);
|
||||
console.log(
|
||||
`[SpecRegeneration] Feature text block received (${block.text.length} chars)`
|
||||
);
|
||||
events.emit("spec-regeneration:event", {
|
||||
type: "features_progress",
|
||||
content: block.text,
|
||||
@@ -506,22 +625,35 @@ Generate 5-15 features that build on each other logically.`;
|
||||
console.log("[SpecRegeneration] Received success result for features");
|
||||
responseText = (msg as any).result || responseText;
|
||||
} else if ((msg as { type: string }).type === "error") {
|
||||
console.error("[SpecRegeneration] ❌ Received error message from feature stream:");
|
||||
console.error("[SpecRegeneration] Error message:", JSON.stringify(msg, null, 2));
|
||||
console.error(
|
||||
"[SpecRegeneration] ❌ Received error message from feature stream:"
|
||||
);
|
||||
console.error(
|
||||
"[SpecRegeneration] Error message:",
|
||||
JSON.stringify(msg, null, 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (streamError) {
|
||||
console.error("[SpecRegeneration] ❌ Error while iterating feature stream:");
|
||||
console.error(
|
||||
"[SpecRegeneration] ❌ Error while iterating feature stream:"
|
||||
);
|
||||
console.error("[SpecRegeneration] Stream error:", streamError);
|
||||
throw streamError;
|
||||
}
|
||||
|
||||
console.log(`[SpecRegeneration] Feature stream complete. Total messages: ${messageCount}`);
|
||||
console.log(`[SpecRegeneration] Feature response length: ${responseText.length} chars`);
|
||||
console.log(
|
||||
`[SpecRegeneration] Feature stream complete. Total messages: ${messageCount}`
|
||||
);
|
||||
console.log(
|
||||
`[SpecRegeneration] Feature response length: ${responseText.length} chars`
|
||||
);
|
||||
|
||||
await parseAndCreateFeatures(projectPath, responseText, events);
|
||||
|
||||
console.log("[SpecRegeneration] ========== generateFeaturesFromSpec() completed ==========");
|
||||
|
||||
console.log(
|
||||
"[SpecRegeneration] ========== generateFeaturesFromSpec() completed =========="
|
||||
);
|
||||
}
|
||||
|
||||
async function parseAndCreateFeatures(
|
||||
@@ -529,24 +661,33 @@ async function parseAndCreateFeatures(
|
||||
content: string,
|
||||
events: EventEmitter
|
||||
) {
|
||||
console.log("[SpecRegeneration] ========== parseAndCreateFeatures() started ==========");
|
||||
console.log(
|
||||
"[SpecRegeneration] ========== parseAndCreateFeatures() started =========="
|
||||
);
|
||||
console.log(`[SpecRegeneration] Content length: ${content.length} chars`);
|
||||
|
||||
|
||||
try {
|
||||
// Extract JSON from response
|
||||
console.log("[SpecRegeneration] Extracting JSON from response...");
|
||||
const jsonMatch = content.match(/\{[\s\S]*"features"[\s\S]*\}/);
|
||||
if (!jsonMatch) {
|
||||
console.error("[SpecRegeneration] ❌ No valid JSON found in response");
|
||||
console.error("[SpecRegeneration] Content preview:", content.substring(0, 500));
|
||||
console.error(
|
||||
"[SpecRegeneration] Content preview:",
|
||||
content.substring(0, 500)
|
||||
);
|
||||
throw new Error("No valid JSON found in response");
|
||||
}
|
||||
|
||||
console.log(`[SpecRegeneration] JSON match found (${jsonMatch[0].length} chars)`);
|
||||
|
||||
console.log(
|
||||
`[SpecRegeneration] JSON match found (${jsonMatch[0].length} chars)`
|
||||
);
|
||||
|
||||
const parsed = JSON.parse(jsonMatch[0]);
|
||||
console.log(`[SpecRegeneration] Parsed ${parsed.features?.length || 0} features`);
|
||||
|
||||
console.log(
|
||||
`[SpecRegeneration] Parsed ${parsed.features?.length || 0} features`
|
||||
);
|
||||
|
||||
const featuresDir = path.join(projectPath, ".automaker", "features");
|
||||
await fs.mkdir(featuresDir, { recursive: true });
|
||||
|
||||
@@ -561,7 +702,7 @@ async function parseAndCreateFeatures(
|
||||
id: feature.id,
|
||||
title: feature.title,
|
||||
description: feature.description,
|
||||
status: "backlog", // Features go to backlog - user must manually start them
|
||||
status: "backlog", // Features go to backlog - user must manually start them
|
||||
priority: feature.priority || 2,
|
||||
complexity: feature.complexity || "moderate",
|
||||
dependencies: feature.dependencies || [],
|
||||
@@ -577,7 +718,9 @@ async function parseAndCreateFeatures(
|
||||
createdFeatures.push({ id: feature.id, title: feature.title });
|
||||
}
|
||||
|
||||
console.log(`[SpecRegeneration] ✓ Created ${createdFeatures.length} features successfully`);
|
||||
console.log(
|
||||
`[SpecRegeneration] ✓ Created ${createdFeatures.length} features successfully`
|
||||
);
|
||||
|
||||
events.emit("spec-regeneration:event", {
|
||||
type: "features_complete",
|
||||
@@ -592,6 +735,8 @@ async function parseAndCreateFeatures(
|
||||
error: (error as Error).message,
|
||||
});
|
||||
}
|
||||
|
||||
console.log("[SpecRegeneration] ========== parseAndCreateFeatures() completed ==========");
|
||||
|
||||
console.log(
|
||||
"[SpecRegeneration] ========== parseAndCreateFeatures() completed =========="
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user