mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
style: fix formatting with Prettier
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
export { SpecHeader } from "./spec-header";
|
||||
export { SpecEditor } from "./spec-editor";
|
||||
export { SpecEmptyState } from "./spec-empty-state";
|
||||
export { SpecHeader } from './spec-header';
|
||||
export { SpecEditor } from './spec-editor';
|
||||
export { SpecEmptyState } from './spec-empty-state';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { XmlSyntaxEditor } from "@/components/ui/xml-syntax-editor";
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { XmlSyntaxEditor } from '@/components/ui/xml-syntax-editor';
|
||||
|
||||
interface SpecEditorProps {
|
||||
value: string;
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
FileText,
|
||||
FilePlus2,
|
||||
Loader2,
|
||||
} from "lucide-react";
|
||||
import { PHASE_LABELS } from "../constants";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { FileText, FilePlus2, Loader2 } from 'lucide-react';
|
||||
import { PHASE_LABELS } from '../constants';
|
||||
|
||||
interface SpecEmptyStateProps {
|
||||
projectPath: string;
|
||||
@@ -27,19 +23,14 @@ export function SpecEmptyState({
|
||||
const phaseLabel = PHASE_LABELS[currentPhase] || currentPhase;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex-1 flex flex-col overflow-hidden content-bg"
|
||||
data-testid="spec-view-empty"
|
||||
>
|
||||
<div className="flex-1 flex flex-col overflow-hidden content-bg" data-testid="spec-view-empty">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-border bg-glass backdrop-blur-md">
|
||||
<div className="flex items-center gap-3">
|
||||
<FileText className="w-5 h-5 text-muted-foreground" />
|
||||
<div>
|
||||
<h1 className="text-xl font-bold">App Specification</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{projectPath}/.automaker/app_spec.txt
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">{projectPath}/.automaker/app_spec.txt</p>
|
||||
</div>
|
||||
</div>
|
||||
{isProcessing && (
|
||||
@@ -50,9 +41,7 @@ export function SpecEmptyState({
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 min-w-0">
|
||||
<span className="text-sm font-semibold text-primary leading-tight tracking-tight">
|
||||
{isCreating
|
||||
? "Generating Specification"
|
||||
: "Regenerating Specification"}
|
||||
{isCreating ? 'Generating Specification' : 'Regenerating Specification'}
|
||||
</span>
|
||||
{currentPhase && (
|
||||
<span className="text-xs text-muted-foreground/90 leading-tight font-medium">
|
||||
@@ -96,13 +85,13 @@ export function SpecEmptyState({
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
"No App Specification Found"
|
||||
'No App Specification Found'
|
||||
)}
|
||||
</h2>
|
||||
<p className="text-muted-foreground mb-6">
|
||||
{isCreating
|
||||
? currentPhase === "feature_generation"
|
||||
? "The app specification has been created! Now generating features from the implementation roadmap..."
|
||||
? currentPhase === 'feature_generation'
|
||||
? 'The app specification has been created! Now generating features from the implementation roadmap...'
|
||||
: "We're analyzing your project and generating a comprehensive specification. This may take a few moments..."
|
||||
: "Create an app specification to help our system understand your project. We'll analyze your codebase and generate a comprehensive spec based on your description."}
|
||||
</p>
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Save,
|
||||
Sparkles,
|
||||
Loader2,
|
||||
FileText,
|
||||
AlertCircle,
|
||||
} from "lucide-react";
|
||||
import { PHASE_LABELS } from "../constants";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Save, Sparkles, Loader2, FileText, AlertCircle } from 'lucide-react';
|
||||
import { PHASE_LABELS } from '../constants';
|
||||
|
||||
interface SpecHeaderProps {
|
||||
projectPath: string;
|
||||
@@ -42,9 +36,7 @@ export function SpecHeader({
|
||||
<FileText className="w-5 h-5 text-muted-foreground" />
|
||||
<div>
|
||||
<h1 className="text-xl font-bold">App Specification</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{projectPath}/.automaker/app_spec.txt
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">{projectPath}/.automaker/app_spec.txt</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -57,10 +49,10 @@ export function SpecHeader({
|
||||
<div className="flex flex-col gap-1 min-w-0">
|
||||
<span className="text-sm font-semibold text-primary leading-tight tracking-tight">
|
||||
{isGeneratingFeatures
|
||||
? "Generating Features"
|
||||
? 'Generating Features'
|
||||
: isCreating
|
||||
? "Generating Specification"
|
||||
: "Regenerating Specification"}
|
||||
? 'Generating Specification'
|
||||
: 'Regenerating Specification'}
|
||||
</span>
|
||||
{currentPhase && (
|
||||
<span className="text-xs text-muted-foreground/90 leading-tight font-medium">
|
||||
@@ -96,7 +88,7 @@ export function SpecHeader({
|
||||
) : (
|
||||
<Sparkles className="w-4 h-4 mr-2" />
|
||||
)}
|
||||
{isRegenerating ? "Regenerating..." : "Regenerate"}
|
||||
{isRegenerating ? 'Regenerating...' : 'Regenerate'}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -105,7 +97,7 @@ export function SpecHeader({
|
||||
data-testid="save-spec"
|
||||
>
|
||||
<Save className="w-4 h-4 mr-2" />
|
||||
{isSaving ? "Saving..." : hasChanges ? "Save Changes" : "Saved"}
|
||||
{isSaving ? 'Saving...' : hasChanges ? 'Save Changes' : 'Saved'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FeatureCount } from "./types";
|
||||
import type { FeatureCount } from './types';
|
||||
|
||||
// Delay before reloading spec file to ensure it's written to disk
|
||||
export const SPEC_FILE_WRITE_DELAY = 500;
|
||||
@@ -12,18 +12,18 @@ export const FEATURE_COUNT_OPTIONS: {
|
||||
label: string;
|
||||
warning?: string;
|
||||
}[] = [
|
||||
{ value: 20, label: "20" },
|
||||
{ value: 50, label: "50", warning: "May take up to 5 minutes" },
|
||||
{ value: 100, label: "100", warning: "May take up to 5 minutes" },
|
||||
{ value: 20, label: '20' },
|
||||
{ value: 50, label: '50', warning: 'May take up to 5 minutes' },
|
||||
{ value: 100, label: '100', warning: 'May take up to 5 minutes' },
|
||||
];
|
||||
|
||||
// Phase display labels for UI
|
||||
export const PHASE_LABELS: Record<string, string> = {
|
||||
initialization: "Initializing...",
|
||||
setup: "Setting up tools...",
|
||||
analysis: "Analyzing project structure...",
|
||||
spec_complete: "Spec created! Generating features...",
|
||||
feature_generation: "Creating features from roadmap...",
|
||||
complete: "Complete!",
|
||||
error: "Error occurred",
|
||||
initialization: 'Initializing...',
|
||||
setup: 'Setting up tools...',
|
||||
analysis: 'Analyzing project structure...',
|
||||
spec_complete: 'Spec created! Generating features...',
|
||||
feature_generation: 'Creating features from roadmap...',
|
||||
complete: 'Complete!',
|
||||
error: 'Error occurred',
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Sparkles, Clock, Loader2 } from "lucide-react";
|
||||
import { Sparkles, Clock, Loader2 } from 'lucide-react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { HotkeyButton } from "@/components/ui/hotkey-button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { FEATURE_COUNT_OPTIONS } from "../constants";
|
||||
import type { CreateSpecDialogProps, FeatureCount } from "../types";
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { HotkeyButton } from '@/components/ui/hotkey-button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { FEATURE_COUNT_OPTIONS } from '../constants';
|
||||
import type { CreateSpecDialogProps, FeatureCount } from '../types';
|
||||
|
||||
export function CreateSpecDialog({
|
||||
open,
|
||||
@@ -29,12 +29,10 @@ export function CreateSpecDialog({
|
||||
onSkip,
|
||||
isCreatingSpec,
|
||||
showSkipButton = false,
|
||||
title = "Create App Specification",
|
||||
title = 'Create App Specification',
|
||||
description = "We didn't find an app_spec.txt file. Let us help you generate your app_spec.txt to help describe your project for our system. We'll analyze your project's tech stack and create a comprehensive specification.",
|
||||
}: CreateSpecDialogProps) {
|
||||
const selectedOption = FEATURE_COUNT_OPTIONS.find(
|
||||
(o) => o.value === featureCount
|
||||
);
|
||||
const selectedOption = FEATURE_COUNT_OPTIONS.find((o) => o.value === featureCount);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
@@ -49,18 +47,15 @@ export function CreateSpecDialog({
|
||||
<DialogContent className="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogDescription className="text-muted-foreground">
|
||||
{description}
|
||||
</DialogDescription>
|
||||
<DialogDescription className="text-muted-foreground">{description}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Project Overview</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Describe what your project does and what features you want to
|
||||
build. Be as detailed as you want - this will help us create a
|
||||
better specification.
|
||||
Describe what your project does and what features you want to build. Be as detailed as
|
||||
you want - this will help us create a better specification.
|
||||
</p>
|
||||
<textarea
|
||||
className="w-full h-48 p-3 rounded-md border border-border bg-background font-mono text-sm resize-none focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
@@ -76,25 +71,20 @@ export function CreateSpecDialog({
|
||||
<Checkbox
|
||||
id="create-analyze-project"
|
||||
checked={analyzeProject}
|
||||
onCheckedChange={(checked) =>
|
||||
onAnalyzeProjectChange(checked === true)
|
||||
}
|
||||
onCheckedChange={(checked) => onAnalyzeProjectChange(checked === true)}
|
||||
disabled={isCreatingSpec}
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<label
|
||||
htmlFor="create-analyze-project"
|
||||
className={`text-sm font-medium ${
|
||||
isCreatingSpec ? "" : "cursor-pointer"
|
||||
}`}
|
||||
className={`text-sm font-medium ${isCreatingSpec ? '' : 'cursor-pointer'}`}
|
||||
>
|
||||
Analyze current project for additional context
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
If checked, the agent will research your existing codebase to
|
||||
understand the tech stack. If unchecked, defaults to TanStack
|
||||
Start, Drizzle ORM, PostgreSQL, shadcn/ui, Tailwind CSS, and
|
||||
React.
|
||||
If checked, the agent will research your existing codebase to understand the tech
|
||||
stack. If unchecked, defaults to TanStack Start, Drizzle ORM, PostgreSQL, shadcn/ui,
|
||||
Tailwind CSS, and React.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,23 +93,19 @@ export function CreateSpecDialog({
|
||||
<Checkbox
|
||||
id="create-generate-features"
|
||||
checked={generateFeatures}
|
||||
onCheckedChange={(checked) =>
|
||||
onGenerateFeaturesChange(checked === true)
|
||||
}
|
||||
onCheckedChange={(checked) => onGenerateFeaturesChange(checked === true)}
|
||||
disabled={isCreatingSpec}
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<label
|
||||
htmlFor="create-generate-features"
|
||||
className={`text-sm font-medium ${
|
||||
isCreatingSpec ? "" : "cursor-pointer"
|
||||
}`}
|
||||
className={`text-sm font-medium ${isCreatingSpec ? '' : 'cursor-pointer'}`}
|
||||
>
|
||||
Generate feature list
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Automatically create features in the features folder from the
|
||||
implementation roadmap after the spec is generated.
|
||||
Automatically create features in the features folder from the implementation roadmap
|
||||
after the spec is generated.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -133,19 +119,15 @@ export function CreateSpecDialog({
|
||||
<Button
|
||||
key={option.value}
|
||||
type="button"
|
||||
variant={
|
||||
featureCount === option.value ? "default" : "outline"
|
||||
}
|
||||
variant={featureCount === option.value ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
onFeatureCountChange(option.value as FeatureCount)
|
||||
}
|
||||
onClick={() => onFeatureCountChange(option.value as FeatureCount)}
|
||||
disabled={isCreatingSpec}
|
||||
className={cn(
|
||||
"flex-1 transition-all",
|
||||
'flex-1 transition-all',
|
||||
featureCount === option.value
|
||||
? "bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||
: "bg-muted/30 hover:bg-muted/50 border-border"
|
||||
? 'bg-primary hover:bg-primary/90 text-primary-foreground'
|
||||
: 'bg-muted/30 hover:bg-muted/50 border-border'
|
||||
)}
|
||||
data-testid={`feature-count-${option.value}`}
|
||||
>
|
||||
@@ -169,18 +151,14 @@ export function CreateSpecDialog({
|
||||
Skip for now
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => onOpenChange(false)}
|
||||
disabled={isCreatingSpec}
|
||||
>
|
||||
<Button variant="ghost" onClick={() => onOpenChange(false)} disabled={isCreatingSpec}>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
<HotkeyButton
|
||||
onClick={onCreateSpec}
|
||||
disabled={!projectOverview.trim() || isCreatingSpec}
|
||||
hotkey={{ key: "Enter", cmdCtrl: true }}
|
||||
hotkey={{ key: 'Enter', cmdCtrl: true }}
|
||||
hotkeyActive={open && !isCreatingSpec}
|
||||
>
|
||||
{isCreatingSpec ? (
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { CreateSpecDialog } from "./create-spec-dialog";
|
||||
export { RegenerateSpecDialog } from "./regenerate-spec-dialog";
|
||||
export { CreateSpecDialog } from './create-spec-dialog';
|
||||
export { RegenerateSpecDialog } from './regenerate-spec-dialog';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Sparkles, Clock, Loader2 } from "lucide-react";
|
||||
import { Sparkles, Clock, Loader2 } from 'lucide-react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { HotkeyButton } from "@/components/ui/hotkey-button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { FEATURE_COUNT_OPTIONS } from "../constants";
|
||||
import type { RegenerateSpecDialogProps, FeatureCount } from "../types";
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { HotkeyButton } from '@/components/ui/hotkey-button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { FEATURE_COUNT_OPTIONS } from '../constants';
|
||||
import type { RegenerateSpecDialogProps, FeatureCount } from '../types';
|
||||
|
||||
export function RegenerateSpecDialog({
|
||||
open,
|
||||
@@ -29,9 +29,7 @@ export function RegenerateSpecDialog({
|
||||
isRegenerating,
|
||||
isGeneratingFeatures = false,
|
||||
}: RegenerateSpecDialogProps) {
|
||||
const selectedOption = FEATURE_COUNT_OPTIONS.find(
|
||||
(o) => o.value === featureCount
|
||||
);
|
||||
const selectedOption = FEATURE_COUNT_OPTIONS.find((o) => o.value === featureCount);
|
||||
const isDisabled = isRegenerating || isGeneratingFeatures;
|
||||
|
||||
return (
|
||||
@@ -47,10 +45,9 @@ export function RegenerateSpecDialog({
|
||||
<DialogHeader>
|
||||
<DialogTitle>Regenerate App Specification</DialogTitle>
|
||||
<DialogDescription className="text-muted-foreground">
|
||||
We will regenerate your app spec based on a short project definition
|
||||
and the current tech stack found in your project. The agent will
|
||||
analyze your codebase to understand your existing technologies and
|
||||
create a comprehensive specification.
|
||||
We will regenerate your app spec based on a short project definition and the current
|
||||
tech stack found in your project. The agent will analyze your codebase to understand
|
||||
your existing technologies and create a comprehensive specification.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -58,9 +55,8 @@ export function RegenerateSpecDialog({
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Describe your project</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Provide a clear description of what your app should do. Be as
|
||||
detailed as you want - the more context you provide, the more
|
||||
comprehensive the spec will be.
|
||||
Provide a clear description of what your app should do. Be as detailed as you want -
|
||||
the more context you provide, the more comprehensive the spec will be.
|
||||
</p>
|
||||
<textarea
|
||||
className="w-full h-40 p-3 rounded-md border border-border bg-background font-mono text-sm resize-none focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
@@ -75,25 +71,20 @@ export function RegenerateSpecDialog({
|
||||
<Checkbox
|
||||
id="regenerate-analyze-project"
|
||||
checked={analyzeProject}
|
||||
onCheckedChange={(checked) =>
|
||||
onAnalyzeProjectChange(checked === true)
|
||||
}
|
||||
onCheckedChange={(checked) => onAnalyzeProjectChange(checked === true)}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<label
|
||||
htmlFor="regenerate-analyze-project"
|
||||
className={`text-sm font-medium ${
|
||||
isDisabled ? "" : "cursor-pointer"
|
||||
}`}
|
||||
className={`text-sm font-medium ${isDisabled ? '' : 'cursor-pointer'}`}
|
||||
>
|
||||
Analyze current project for additional context
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
If checked, the agent will research your existing codebase to
|
||||
understand the tech stack. If unchecked, defaults to TanStack
|
||||
Start, Drizzle ORM, PostgreSQL, shadcn/ui, Tailwind CSS, and
|
||||
React.
|
||||
If checked, the agent will research your existing codebase to understand the tech
|
||||
stack. If unchecked, defaults to TanStack Start, Drizzle ORM, PostgreSQL, shadcn/ui,
|
||||
Tailwind CSS, and React.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,23 +93,19 @@ export function RegenerateSpecDialog({
|
||||
<Checkbox
|
||||
id="regenerate-generate-features"
|
||||
checked={generateFeatures}
|
||||
onCheckedChange={(checked) =>
|
||||
onGenerateFeaturesChange(checked === true)
|
||||
}
|
||||
onCheckedChange={(checked) => onGenerateFeaturesChange(checked === true)}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<label
|
||||
htmlFor="regenerate-generate-features"
|
||||
className={`text-sm font-medium ${
|
||||
isDisabled ? "" : "cursor-pointer"
|
||||
}`}
|
||||
className={`text-sm font-medium ${isDisabled ? '' : 'cursor-pointer'}`}
|
||||
>
|
||||
Generate feature list
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Automatically create features in the features folder from the
|
||||
implementation roadmap after the spec is regenerated.
|
||||
Automatically create features in the features folder from the implementation roadmap
|
||||
after the spec is regenerated.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,19 +119,15 @@ export function RegenerateSpecDialog({
|
||||
<Button
|
||||
key={option.value}
|
||||
type="button"
|
||||
variant={
|
||||
featureCount === option.value ? "default" : "outline"
|
||||
}
|
||||
variant={featureCount === option.value ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
onFeatureCountChange(option.value as FeatureCount)
|
||||
}
|
||||
onClick={() => onFeatureCountChange(option.value as FeatureCount)}
|
||||
disabled={isDisabled}
|
||||
className={cn(
|
||||
"flex-1 transition-all",
|
||||
'flex-1 transition-all',
|
||||
featureCount === option.value
|
||||
? "bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||
: "bg-muted/30 hover:bg-muted/50 border-border"
|
||||
? 'bg-primary hover:bg-primary/90 text-primary-foreground'
|
||||
: 'bg-muted/30 hover:bg-muted/50 border-border'
|
||||
)}
|
||||
data-testid={`regenerate-feature-count-${option.value}`}
|
||||
>
|
||||
@@ -164,17 +147,13 @@ export function RegenerateSpecDialog({
|
||||
|
||||
<DialogFooter>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => onOpenChange(false)}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
<Button variant="ghost" onClick={() => onOpenChange(false)} disabled={isDisabled}>
|
||||
Cancel
|
||||
</Button>
|
||||
<HotkeyButton
|
||||
onClick={onRegenerate}
|
||||
disabled={!projectDefinition.trim() || isDisabled}
|
||||
hotkey={{ key: "Enter", cmdCtrl: true }}
|
||||
hotkey={{ key: 'Enter', cmdCtrl: true }}
|
||||
hotkeyActive={open && !isDisabled}
|
||||
>
|
||||
{isRegenerating ? (
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { useSpecLoading } from "./use-spec-loading";
|
||||
export { useSpecSave } from "./use-spec-save";
|
||||
export { useSpecGeneration } from "./use-spec-generation";
|
||||
export { useSpecLoading } from './use-spec-loading';
|
||||
export { useSpecSave } from './use-spec-save';
|
||||
export { useSpecGeneration } from './use-spec-generation';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useEffect, useState, useCallback, useRef } from "react";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import { toast } from "sonner";
|
||||
import { CheckCircle2 } from "lucide-react";
|
||||
import { createElement } from "react";
|
||||
import { SPEC_FILE_WRITE_DELAY, STATUS_CHECK_INTERVAL_MS } from "../constants";
|
||||
import type { FeatureCount } from "../types";
|
||||
import type { SpecRegenerationEvent } from "@/types/electron";
|
||||
import { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { toast } from 'sonner';
|
||||
import { CheckCircle2 } from 'lucide-react';
|
||||
import { createElement } from 'react';
|
||||
import { SPEC_FILE_WRITE_DELAY, STATUS_CHECK_INTERVAL_MS } from '../constants';
|
||||
import type { FeatureCount } from '../types';
|
||||
import type { SpecRegenerationEvent } from '@/types/electron';
|
||||
|
||||
interface UseSpecGenerationOptions {
|
||||
loadSpec: () => Promise<void>;
|
||||
@@ -20,33 +20,29 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
const [showRegenerateDialog, setShowRegenerateDialog] = useState(false);
|
||||
|
||||
// Create spec state
|
||||
const [projectOverview, setProjectOverview] = useState("");
|
||||
const [projectOverview, setProjectOverview] = useState('');
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [generateFeatures, setGenerateFeatures] = useState(true);
|
||||
const [analyzeProjectOnCreate, setAnalyzeProjectOnCreate] = useState(true);
|
||||
const [featureCountOnCreate, setFeatureCountOnCreate] =
|
||||
useState<FeatureCount>(50);
|
||||
const [featureCountOnCreate, setFeatureCountOnCreate] = useState<FeatureCount>(50);
|
||||
|
||||
// Regenerate spec state
|
||||
const [projectDefinition, setProjectDefinition] = useState("");
|
||||
const [projectDefinition, setProjectDefinition] = useState('');
|
||||
const [isRegenerating, setIsRegenerating] = useState(false);
|
||||
const [generateFeaturesOnRegenerate, setGenerateFeaturesOnRegenerate] =
|
||||
useState(true);
|
||||
const [analyzeProjectOnRegenerate, setAnalyzeProjectOnRegenerate] =
|
||||
useState(true);
|
||||
const [featureCountOnRegenerate, setFeatureCountOnRegenerate] =
|
||||
useState<FeatureCount>(50);
|
||||
const [generateFeaturesOnRegenerate, setGenerateFeaturesOnRegenerate] = useState(true);
|
||||
const [analyzeProjectOnRegenerate, setAnalyzeProjectOnRegenerate] = useState(true);
|
||||
const [featureCountOnRegenerate, setFeatureCountOnRegenerate] = useState<FeatureCount>(50);
|
||||
|
||||
// Generate features only state
|
||||
const [isGeneratingFeatures, setIsGeneratingFeatures] = useState(false);
|
||||
|
||||
// Logs state (kept for internal tracking)
|
||||
const [logs, setLogs] = useState<string>("");
|
||||
const logsRef = useRef<string>("");
|
||||
const [logs, setLogs] = useState<string>('');
|
||||
const logsRef = useRef<string>('');
|
||||
|
||||
// Phase tracking and status
|
||||
const [currentPhase, setCurrentPhase] = useState<string>("");
|
||||
const [errorMessage, setErrorMessage] = useState<string>("");
|
||||
const [currentPhase, setCurrentPhase] = useState<string>('');
|
||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
||||
const statusCheckRef = useRef<boolean>(false);
|
||||
const stateRestoredRef = useRef<boolean>(false);
|
||||
const pendingStatusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
@@ -56,10 +52,10 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("");
|
||||
setErrorMessage("");
|
||||
setLogs("");
|
||||
logsRef.current = "";
|
||||
setCurrentPhase('');
|
||||
setErrorMessage('');
|
||||
setLogs('');
|
||||
logsRef.current = '';
|
||||
stateRestoredRef.current = false;
|
||||
statusCheckRef.current = false;
|
||||
|
||||
@@ -84,15 +80,15 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
|
||||
const status = await api.specRegeneration.status();
|
||||
console.log(
|
||||
"[useSpecGeneration] Status check on mount:",
|
||||
'[useSpecGeneration] Status check on mount:',
|
||||
status,
|
||||
"for project:",
|
||||
'for project:',
|
||||
currentProject.path
|
||||
);
|
||||
|
||||
if (status.success && status.isRunning) {
|
||||
console.log(
|
||||
"[useSpecGeneration] Spec generation is running globally. Tentatively showing loader."
|
||||
'[useSpecGeneration] Spec generation is running globally. Tentatively showing loader.'
|
||||
);
|
||||
|
||||
setIsCreating(true);
|
||||
@@ -100,7 +96,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
if (status.currentPhase) {
|
||||
setCurrentPhase(status.currentPhase);
|
||||
} else {
|
||||
setCurrentPhase("initialization");
|
||||
setCurrentPhase('initialization');
|
||||
}
|
||||
|
||||
if (pendingStatusTimeoutRef.current) {
|
||||
@@ -108,21 +104,21 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
}
|
||||
pendingStatusTimeoutRef.current = setTimeout(() => {
|
||||
console.log(
|
||||
"[useSpecGeneration] No events received for current project - clearing tentative state"
|
||||
'[useSpecGeneration] No events received for current project - clearing tentative state'
|
||||
);
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase("");
|
||||
setCurrentPhase('');
|
||||
pendingStatusTimeoutRef.current = null;
|
||||
}, 3000);
|
||||
} else if (status.success && !status.isRunning) {
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase("");
|
||||
setCurrentPhase('');
|
||||
stateRestoredRef.current = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[useSpecGeneration] Failed to check status:", error);
|
||||
console.error('[useSpecGeneration] Failed to check status:', error);
|
||||
} finally {
|
||||
statusCheckRef.current = false;
|
||||
}
|
||||
@@ -145,52 +141,36 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
if (!api.specRegeneration) return;
|
||||
|
||||
const status = await api.specRegeneration.status();
|
||||
console.log(
|
||||
"[useSpecGeneration] Visibility change - status check:",
|
||||
status
|
||||
);
|
||||
console.log('[useSpecGeneration] Visibility change - status check:', status);
|
||||
|
||||
if (!status.isRunning) {
|
||||
console.log(
|
||||
"[useSpecGeneration] Visibility change: Backend indicates generation complete - clearing state"
|
||||
'[useSpecGeneration] Visibility change: Backend indicates generation complete - clearing state'
|
||||
);
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("");
|
||||
setCurrentPhase('');
|
||||
stateRestoredRef.current = false;
|
||||
loadSpec();
|
||||
} else if (status.currentPhase) {
|
||||
setCurrentPhase(status.currentPhase);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[useSpecGeneration] Failed to check status on visibility change:",
|
||||
error
|
||||
);
|
||||
console.error('[useSpecGeneration] Failed to check status on visibility change:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("visibilitychange", handleVisibilityChange);
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
return () => {
|
||||
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
};
|
||||
}, [
|
||||
currentProject,
|
||||
isCreating,
|
||||
isRegenerating,
|
||||
isGeneratingFeatures,
|
||||
loadSpec,
|
||||
]);
|
||||
}, [currentProject, isCreating, isRegenerating, isGeneratingFeatures, loadSpec]);
|
||||
|
||||
// Periodic status check
|
||||
useEffect(() => {
|
||||
if (
|
||||
!currentProject ||
|
||||
(!isCreating && !isRegenerating && !isGeneratingFeatures)
|
||||
)
|
||||
return;
|
||||
if (!currentProject || (!isCreating && !isRegenerating && !isGeneratingFeatures)) return;
|
||||
|
||||
const intervalId = setInterval(async () => {
|
||||
try {
|
||||
@@ -201,46 +181,30 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
|
||||
if (!status.isRunning) {
|
||||
console.log(
|
||||
"[useSpecGeneration] Periodic check: Backend indicates generation complete - clearing state"
|
||||
'[useSpecGeneration] Periodic check: Backend indicates generation complete - clearing state'
|
||||
);
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("");
|
||||
setCurrentPhase('');
|
||||
stateRestoredRef.current = false;
|
||||
loadSpec();
|
||||
} else if (
|
||||
status.currentPhase &&
|
||||
status.currentPhase !== currentPhase
|
||||
) {
|
||||
console.log(
|
||||
"[useSpecGeneration] Periodic check: Phase updated from backend",
|
||||
{
|
||||
old: currentPhase,
|
||||
new: status.currentPhase,
|
||||
}
|
||||
);
|
||||
} else if (status.currentPhase && status.currentPhase !== currentPhase) {
|
||||
console.log('[useSpecGeneration] Periodic check: Phase updated from backend', {
|
||||
old: currentPhase,
|
||||
new: status.currentPhase,
|
||||
});
|
||||
setCurrentPhase(status.currentPhase);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[useSpecGeneration] Periodic status check error:",
|
||||
error
|
||||
);
|
||||
console.error('[useSpecGeneration] Periodic status check error:', error);
|
||||
}
|
||||
}, STATUS_CHECK_INTERVAL_MS);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, [
|
||||
currentProject,
|
||||
isCreating,
|
||||
isRegenerating,
|
||||
isGeneratingFeatures,
|
||||
currentPhase,
|
||||
loadSpec,
|
||||
]);
|
||||
}, [currentProject, isCreating, isRegenerating, isGeneratingFeatures, currentPhase, loadSpec]);
|
||||
|
||||
// Subscribe to spec regeneration events
|
||||
useEffect(() => {
|
||||
@@ -249,198 +213,179 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) return;
|
||||
|
||||
const unsubscribe = api.specRegeneration.onEvent(
|
||||
(event: SpecRegenerationEvent) => {
|
||||
const unsubscribe = api.specRegeneration.onEvent((event: SpecRegenerationEvent) => {
|
||||
console.log(
|
||||
'[useSpecGeneration] Regeneration event:',
|
||||
event.type,
|
||||
'for project:',
|
||||
event.projectPath,
|
||||
'current project:',
|
||||
currentProject?.path
|
||||
);
|
||||
|
||||
if (event.projectPath !== currentProject?.path) {
|
||||
console.log('[useSpecGeneration] Ignoring event - not for current project');
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingStatusTimeoutRef.current) {
|
||||
clearTimeout(pendingStatusTimeoutRef.current);
|
||||
pendingStatusTimeoutRef.current = null;
|
||||
console.log(
|
||||
"[useSpecGeneration] Regeneration event:",
|
||||
event.type,
|
||||
"for project:",
|
||||
event.projectPath,
|
||||
"current project:",
|
||||
currentProject?.path
|
||||
'[useSpecGeneration] Event confirmed this is for current project - clearing timeout'
|
||||
);
|
||||
}
|
||||
|
||||
if (event.projectPath !== currentProject?.path) {
|
||||
console.log(
|
||||
"[useSpecGeneration] Ignoring event - not for current project"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (event.type === 'spec_regeneration_progress') {
|
||||
setIsCreating(true);
|
||||
setIsRegenerating(true);
|
||||
|
||||
if (pendingStatusTimeoutRef.current) {
|
||||
clearTimeout(pendingStatusTimeoutRef.current);
|
||||
pendingStatusTimeoutRef.current = null;
|
||||
console.log(
|
||||
"[useSpecGeneration] Event confirmed this is for current project - clearing timeout"
|
||||
);
|
||||
}
|
||||
const phaseMatch = event.content.match(/\[Phase:\s*([^\]]+)\]/);
|
||||
if (phaseMatch) {
|
||||
const phase = phaseMatch[1];
|
||||
setCurrentPhase(phase);
|
||||
console.log(`[useSpecGeneration] Phase updated: ${phase}`);
|
||||
|
||||
if (event.type === "spec_regeneration_progress") {
|
||||
setIsCreating(true);
|
||||
setIsRegenerating(true);
|
||||
|
||||
const phaseMatch = event.content.match(/\[Phase:\s*([^\]]+)\]/);
|
||||
if (phaseMatch) {
|
||||
const phase = phaseMatch[1];
|
||||
setCurrentPhase(phase);
|
||||
console.log(`[useSpecGeneration] Phase updated: ${phase}`);
|
||||
|
||||
if (phase === "complete") {
|
||||
console.log(
|
||||
"[useSpecGeneration] Phase is complete - clearing state"
|
||||
);
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
stateRestoredRef.current = false;
|
||||
setTimeout(() => {
|
||||
loadSpec();
|
||||
}, SPEC_FILE_WRITE_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
event.content.includes("All tasks completed") ||
|
||||
event.content.includes("✓ All tasks completed")
|
||||
) {
|
||||
console.log(
|
||||
"[useSpecGeneration] Detected completion in progress message - clearing state"
|
||||
);
|
||||
if (phase === 'complete') {
|
||||
console.log('[useSpecGeneration] Phase is complete - clearing state');
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase("");
|
||||
stateRestoredRef.current = false;
|
||||
setTimeout(() => {
|
||||
loadSpec();
|
||||
}, SPEC_FILE_WRITE_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
const newLog = logsRef.current + event.content;
|
||||
logsRef.current = newLog;
|
||||
setLogs(newLog);
|
||||
if (
|
||||
event.content.includes('All tasks completed') ||
|
||||
event.content.includes('✓ All tasks completed')
|
||||
) {
|
||||
console.log(
|
||||
"[useSpecGeneration] Progress:",
|
||||
event.content.substring(0, 100)
|
||||
'[useSpecGeneration] Detected completion in progress message - clearing state'
|
||||
);
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase('');
|
||||
stateRestoredRef.current = false;
|
||||
setTimeout(() => {
|
||||
loadSpec();
|
||||
}, SPEC_FILE_WRITE_DELAY);
|
||||
}
|
||||
|
||||
if (errorMessage) {
|
||||
setErrorMessage("");
|
||||
}
|
||||
} else if (event.type === "spec_regeneration_tool") {
|
||||
const isFeatureTool =
|
||||
event.tool === "mcp__automaker-tools__UpdateFeatureStatus" ||
|
||||
event.tool === "UpdateFeatureStatus" ||
|
||||
event.tool?.includes("Feature");
|
||||
const newLog = logsRef.current + event.content;
|
||||
logsRef.current = newLog;
|
||||
setLogs(newLog);
|
||||
console.log('[useSpecGeneration] Progress:', event.content.substring(0, 100));
|
||||
|
||||
if (isFeatureTool) {
|
||||
if (currentPhase !== "feature_generation") {
|
||||
setCurrentPhase("feature_generation");
|
||||
setIsCreating(true);
|
||||
setIsRegenerating(true);
|
||||
console.log(
|
||||
"[useSpecGeneration] Detected feature creation tool - setting phase to feature_generation"
|
||||
);
|
||||
}
|
||||
}
|
||||
if (errorMessage) {
|
||||
setErrorMessage('');
|
||||
}
|
||||
} else if (event.type === 'spec_regeneration_tool') {
|
||||
const isFeatureTool =
|
||||
event.tool === 'mcp__automaker-tools__UpdateFeatureStatus' ||
|
||||
event.tool === 'UpdateFeatureStatus' ||
|
||||
event.tool?.includes('Feature');
|
||||
|
||||
const toolInput = event.input
|
||||
? ` (${JSON.stringify(event.input).substring(0, 100)}...)`
|
||||
: "";
|
||||
const toolLog = `\n[Tool] ${event.tool}${toolInput}\n`;
|
||||
const newLog = logsRef.current + toolLog;
|
||||
logsRef.current = newLog;
|
||||
setLogs(newLog);
|
||||
console.log("[useSpecGeneration] Tool:", event.tool, event.input);
|
||||
} else if (event.type === "spec_regeneration_complete") {
|
||||
const completionLog =
|
||||
logsRef.current + `\n[Complete] ${event.message}\n`;
|
||||
logsRef.current = completionLog;
|
||||
setLogs(completionLog);
|
||||
|
||||
const isFinalCompletionMessage =
|
||||
event.message?.includes("All tasks completed") ||
|
||||
event.message === "All tasks completed!" ||
|
||||
event.message === "All tasks completed" ||
|
||||
event.message === "Spec regeneration complete!" ||
|
||||
event.message === "Initial spec creation complete!";
|
||||
|
||||
const hasCompletePhase =
|
||||
logsRef.current.includes("[Phase: complete]");
|
||||
|
||||
const isIntermediateCompletion =
|
||||
event.message?.includes("Features are being generated") ||
|
||||
event.message?.includes("features are being generated");
|
||||
|
||||
const shouldComplete =
|
||||
(isFinalCompletionMessage || hasCompletePhase) &&
|
||||
!isIntermediateCompletion;
|
||||
|
||||
if (shouldComplete) {
|
||||
console.log(
|
||||
"[useSpecGeneration] Final completion detected - clearing state",
|
||||
{
|
||||
isFinalCompletionMessage,
|
||||
hasCompletePhase,
|
||||
message: event.message,
|
||||
}
|
||||
);
|
||||
setIsRegenerating(false);
|
||||
setIsCreating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("");
|
||||
setShowRegenerateDialog(false);
|
||||
setShowCreateDialog(false);
|
||||
setProjectDefinition("");
|
||||
setProjectOverview("");
|
||||
setErrorMessage("");
|
||||
stateRestoredRef.current = false;
|
||||
|
||||
setTimeout(() => {
|
||||
loadSpec();
|
||||
}, SPEC_FILE_WRITE_DELAY);
|
||||
|
||||
const isRegeneration = event.message?.includes("regeneration");
|
||||
const isFeatureGeneration =
|
||||
event.message?.includes("Feature generation");
|
||||
toast.success(
|
||||
isFeatureGeneration
|
||||
? "Feature Generation Complete"
|
||||
: isRegeneration
|
||||
? "Spec Regeneration Complete"
|
||||
: "Spec Creation Complete",
|
||||
{
|
||||
description: isFeatureGeneration
|
||||
? "Features have been created from the app specification."
|
||||
: "Your app specification has been saved.",
|
||||
icon: createElement(CheckCircle2, { className: "w-4 h-4" }),
|
||||
}
|
||||
);
|
||||
} else if (isIntermediateCompletion) {
|
||||
if (isFeatureTool) {
|
||||
if (currentPhase !== 'feature_generation') {
|
||||
setCurrentPhase('feature_generation');
|
||||
setIsCreating(true);
|
||||
setIsRegenerating(true);
|
||||
setCurrentPhase("feature_generation");
|
||||
console.log(
|
||||
"[useSpecGeneration] Intermediate completion, continuing with feature generation"
|
||||
'[useSpecGeneration] Detected feature creation tool - setting phase to feature_generation'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
"[useSpecGeneration] Spec generation event:",
|
||||
event.message
|
||||
);
|
||||
} else if (event.type === "spec_regeneration_error") {
|
||||
const toolInput = event.input
|
||||
? ` (${JSON.stringify(event.input).substring(0, 100)}...)`
|
||||
: '';
|
||||
const toolLog = `\n[Tool] ${event.tool}${toolInput}\n`;
|
||||
const newLog = logsRef.current + toolLog;
|
||||
logsRef.current = newLog;
|
||||
setLogs(newLog);
|
||||
console.log('[useSpecGeneration] Tool:', event.tool, event.input);
|
||||
} else if (event.type === 'spec_regeneration_complete') {
|
||||
const completionLog = logsRef.current + `\n[Complete] ${event.message}\n`;
|
||||
logsRef.current = completionLog;
|
||||
setLogs(completionLog);
|
||||
|
||||
const isFinalCompletionMessage =
|
||||
event.message?.includes('All tasks completed') ||
|
||||
event.message === 'All tasks completed!' ||
|
||||
event.message === 'All tasks completed' ||
|
||||
event.message === 'Spec regeneration complete!' ||
|
||||
event.message === 'Initial spec creation complete!';
|
||||
|
||||
const hasCompletePhase = logsRef.current.includes('[Phase: complete]');
|
||||
|
||||
const isIntermediateCompletion =
|
||||
event.message?.includes('Features are being generated') ||
|
||||
event.message?.includes('features are being generated');
|
||||
|
||||
const shouldComplete =
|
||||
(isFinalCompletionMessage || hasCompletePhase) && !isIntermediateCompletion;
|
||||
|
||||
if (shouldComplete) {
|
||||
console.log('[useSpecGeneration] Final completion detected - clearing state', {
|
||||
isFinalCompletionMessage,
|
||||
hasCompletePhase,
|
||||
message: event.message,
|
||||
});
|
||||
setIsRegenerating(false);
|
||||
setIsCreating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("error");
|
||||
setErrorMessage(event.error);
|
||||
setCurrentPhase('');
|
||||
setShowRegenerateDialog(false);
|
||||
setShowCreateDialog(false);
|
||||
setProjectDefinition('');
|
||||
setProjectOverview('');
|
||||
setErrorMessage('');
|
||||
stateRestoredRef.current = false;
|
||||
const errorLog = logsRef.current + `\n\n[ERROR] ${event.error}\n`;
|
||||
logsRef.current = errorLog;
|
||||
setLogs(errorLog);
|
||||
console.error("[useSpecGeneration] Regeneration error:", event.error);
|
||||
|
||||
setTimeout(() => {
|
||||
loadSpec();
|
||||
}, SPEC_FILE_WRITE_DELAY);
|
||||
|
||||
const isRegeneration = event.message?.includes('regeneration');
|
||||
const isFeatureGeneration = event.message?.includes('Feature generation');
|
||||
toast.success(
|
||||
isFeatureGeneration
|
||||
? 'Feature Generation Complete'
|
||||
: isRegeneration
|
||||
? 'Spec Regeneration Complete'
|
||||
: 'Spec Creation Complete',
|
||||
{
|
||||
description: isFeatureGeneration
|
||||
? 'Features have been created from the app specification.'
|
||||
: 'Your app specification has been saved.',
|
||||
icon: createElement(CheckCircle2, { className: 'w-4 h-4' }),
|
||||
}
|
||||
);
|
||||
} else if (isIntermediateCompletion) {
|
||||
setIsCreating(true);
|
||||
setIsRegenerating(true);
|
||||
setCurrentPhase('feature_generation');
|
||||
console.log(
|
||||
'[useSpecGeneration] Intermediate completion, continuing with feature generation'
|
||||
);
|
||||
}
|
||||
|
||||
console.log('[useSpecGeneration] Spec generation event:', event.message);
|
||||
} else if (event.type === 'spec_regeneration_error') {
|
||||
setIsRegenerating(false);
|
||||
setIsCreating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(event.error);
|
||||
stateRestoredRef.current = false;
|
||||
const errorLog = logsRef.current + `\n\n[ERROR] ${event.error}\n`;
|
||||
logsRef.current = errorLog;
|
||||
setLogs(errorLog);
|
||||
console.error('[useSpecGeneration] Regeneration error:', event.error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
@@ -453,18 +398,15 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
|
||||
setIsCreating(true);
|
||||
setShowCreateDialog(false);
|
||||
setCurrentPhase("initialization");
|
||||
setErrorMessage("");
|
||||
logsRef.current = "";
|
||||
setLogs("");
|
||||
console.log(
|
||||
"[useSpecGeneration] Starting spec creation, generateFeatures:",
|
||||
generateFeatures
|
||||
);
|
||||
setCurrentPhase('initialization');
|
||||
setErrorMessage('');
|
||||
logsRef.current = '';
|
||||
setLogs('');
|
||||
console.log('[useSpecGeneration] Starting spec creation, generateFeatures:', generateFeatures);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) {
|
||||
console.error("[useSpecGeneration] Spec regeneration not available");
|
||||
console.error('[useSpecGeneration] Spec regeneration not available');
|
||||
setIsCreating(false);
|
||||
return;
|
||||
}
|
||||
@@ -477,13 +419,10 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
const errorMsg = result.error || "Unknown error";
|
||||
console.error(
|
||||
"[useSpecGeneration] Failed to start spec creation:",
|
||||
errorMsg
|
||||
);
|
||||
const errorMsg = result.error || 'Unknown error';
|
||||
console.error('[useSpecGeneration] Failed to start spec creation:', errorMsg);
|
||||
setIsCreating(false);
|
||||
setCurrentPhase("error");
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
const errorLog = `[Error] Failed to start spec creation: ${errorMsg}\n`;
|
||||
logsRef.current = errorLog;
|
||||
@@ -491,9 +430,9 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
console.error("[useSpecGeneration] Failed to create spec:", errorMsg);
|
||||
console.error('[useSpecGeneration] Failed to create spec:', errorMsg);
|
||||
setIsCreating(false);
|
||||
setCurrentPhase("error");
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
const errorLog = `[Error] Failed to create spec: ${errorMsg}\n`;
|
||||
logsRef.current = errorLog;
|
||||
@@ -512,18 +451,18 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
|
||||
setIsRegenerating(true);
|
||||
setShowRegenerateDialog(false);
|
||||
setCurrentPhase("initialization");
|
||||
setErrorMessage("");
|
||||
logsRef.current = "";
|
||||
setLogs("");
|
||||
setCurrentPhase('initialization');
|
||||
setErrorMessage('');
|
||||
logsRef.current = '';
|
||||
setLogs('');
|
||||
console.log(
|
||||
"[useSpecGeneration] Starting spec regeneration, generateFeatures:",
|
||||
'[useSpecGeneration] Starting spec regeneration, generateFeatures:',
|
||||
generateFeaturesOnRegenerate
|
||||
);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) {
|
||||
console.error("[useSpecGeneration] Spec regeneration not available");
|
||||
console.error('[useSpecGeneration] Spec regeneration not available');
|
||||
setIsRegenerating(false);
|
||||
return;
|
||||
}
|
||||
@@ -536,13 +475,10 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
const errorMsg = result.error || "Unknown error";
|
||||
console.error(
|
||||
"[useSpecGeneration] Failed to start regeneration:",
|
||||
errorMsg
|
||||
);
|
||||
const errorMsg = result.error || 'Unknown error';
|
||||
console.error('[useSpecGeneration] Failed to start regeneration:', errorMsg);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase("error");
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
const errorLog = `[Error] Failed to start regeneration: ${errorMsg}\n`;
|
||||
logsRef.current = errorLog;
|
||||
@@ -550,9 +486,9 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
console.error("[useSpecGeneration] Failed to regenerate spec:", errorMsg);
|
||||
console.error('[useSpecGeneration] Failed to regenerate spec:', errorMsg);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase("error");
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
const errorLog = `[Error] Failed to regenerate spec: ${errorMsg}\n`;
|
||||
logsRef.current = errorLog;
|
||||
@@ -571,32 +507,25 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
|
||||
setIsGeneratingFeatures(true);
|
||||
setShowRegenerateDialog(false);
|
||||
setCurrentPhase("initialization");
|
||||
setErrorMessage("");
|
||||
logsRef.current = "";
|
||||
setLogs("");
|
||||
console.log(
|
||||
"[useSpecGeneration] Starting feature generation from existing spec"
|
||||
);
|
||||
setCurrentPhase('initialization');
|
||||
setErrorMessage('');
|
||||
logsRef.current = '';
|
||||
setLogs('');
|
||||
console.log('[useSpecGeneration] Starting feature generation from existing spec');
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) {
|
||||
console.error("[useSpecGeneration] Spec regeneration not available");
|
||||
console.error('[useSpecGeneration] Spec regeneration not available');
|
||||
setIsGeneratingFeatures(false);
|
||||
return;
|
||||
}
|
||||
const result = await api.specRegeneration.generateFeatures(
|
||||
currentProject.path
|
||||
);
|
||||
const result = await api.specRegeneration.generateFeatures(currentProject.path);
|
||||
|
||||
if (!result.success) {
|
||||
const errorMsg = result.error || "Unknown error";
|
||||
console.error(
|
||||
"[useSpecGeneration] Failed to start feature generation:",
|
||||
errorMsg
|
||||
);
|
||||
const errorMsg = result.error || 'Unknown error';
|
||||
console.error('[useSpecGeneration] Failed to start feature generation:', errorMsg);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("error");
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
const errorLog = `[Error] Failed to start feature generation: ${errorMsg}\n`;
|
||||
logsRef.current = errorLog;
|
||||
@@ -604,12 +533,9 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
console.error(
|
||||
"[useSpecGeneration] Failed to generate features:",
|
||||
errorMsg
|
||||
);
|
||||
console.error('[useSpecGeneration] Failed to generate features:', errorMsg);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("error");
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
const errorLog = `[Error] Failed to generate features: ${errorMsg}\n`;
|
||||
logsRef.current = errorLog;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
export function useSpecLoading() {
|
||||
const { currentProject, setAppSpec } = useAppStore();
|
||||
@@ -13,20 +13,18 @@ export function useSpecLoading() {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.readFile(
|
||||
`${currentProject.path}/.automaker/app_spec.txt`
|
||||
);
|
||||
const result = await api.readFile(`${currentProject.path}/.automaker/app_spec.txt`);
|
||||
|
||||
if (result.success && result.content) {
|
||||
setAppSpec(result.content);
|
||||
setSpecExists(true);
|
||||
} else {
|
||||
// File doesn't exist
|
||||
setAppSpec("");
|
||||
setAppSpec('');
|
||||
setSpecExists(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load spec:", error);
|
||||
console.error('Failed to load spec:', error);
|
||||
setSpecExists(false);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import { useState } from 'react';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
export function useSpecSave() {
|
||||
const { currentProject, appSpec, setAppSpec } = useAppStore();
|
||||
@@ -13,13 +13,10 @@ export function useSpecSave() {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
await api.writeFile(
|
||||
`${currentProject.path}/.automaker/app_spec.txt`,
|
||||
appSpec
|
||||
);
|
||||
await api.writeFile(`${currentProject.path}/.automaker/app_spec.txt`, appSpec);
|
||||
setHasChanges(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to save spec:", error);
|
||||
console.error('Failed to save spec:', error);
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ export type FeatureCount = 20 | 50 | 100;
|
||||
|
||||
// Generation phases for UI display
|
||||
export type GenerationPhase =
|
||||
| "initialization"
|
||||
| "setup"
|
||||
| "analysis"
|
||||
| "spec_complete"
|
||||
| "feature_generation"
|
||||
| "complete"
|
||||
| "error";
|
||||
| 'initialization'
|
||||
| 'setup'
|
||||
| 'analysis'
|
||||
| 'spec_complete'
|
||||
| 'feature_generation'
|
||||
| 'complete'
|
||||
| 'error';
|
||||
|
||||
// Props for the unified create spec dialog
|
||||
export interface CreateSpecDialogProps {
|
||||
|
||||
Reference in New Issue
Block a user