feat: add SpecDialog component for editing spec frontmatter

- Implement SpecDialog for managing spec details including title, read mode, priority, and keywords.
- Add validation and keyword management functionality.
- Integrate SpecDialog into SpecsSettingsPage for editing specs.

feat: create index file for specs components

- Export SpecCard, SpecDialog, and related types from a new index file for better organization.

feat: implement SpecsSettingsPage for managing specs and hooks

- Create main settings page with tabs for Project Specs, Personal Specs, Hooks, Injection, and Settings.
- Integrate SpecDialog and HookDialog for editing specs and hooks.
- Add search functionality and mock data for specs and hooks.

feat: add spec management API routes

- Implement API endpoints for listing specs, getting spec details, updating frontmatter, rebuilding indices, and initializing the spec system.
- Handle errors and responses appropriately for each endpoint.
This commit is contained in:
catlog22
2026-02-26 22:03:13 +08:00
parent 430d817e43
commit 6155fcc7b8
115 changed files with 4883 additions and 21127 deletions

View File

@@ -139,11 +139,14 @@ export function useIssues(options: UseIssuesOptions = {}): UseIssuesReturn {
// Group by status
const issuesByStatus: Record<Issue['status'], Issue[]> = {
open: [],
in_progress: [],
resolved: [],
closed: [],
registered: [],
planning: [],
planned: [],
queued: [],
executing: [],
completed: [],
failed: [],
paused: [],
};
for (const issue of allIssues) {
@@ -184,7 +187,7 @@ export function useIssues(options: UseIssuesOptions = {}): UseIssuesReturn {
allIssues,
issuesByStatus,
issuesByPriority,
openCount: issuesByStatus.open.length + issuesByStatus.in_progress.length,
openCount: issuesByStatus.registered.length + issuesByStatus.planning.length + issuesByStatus.planned.length,
criticalCount: issuesByPriority.critical.length,
isLoading: issuesQuery.isLoading,
isFetching: issuesQuery.isFetching || historyQuery.isFetching,

View File

@@ -326,3 +326,140 @@ export function useImportSettings() {
error: mutation.error,
};
}
// ========================================
// Specs Settings Hooks
// ========================================
import {
getSystemSettings,
updateSystemSettings,
installRecommendedHooks,
getSpecStats,
type SystemSettings,
type UpdateSystemSettingsInput,
type InstallRecommendedHooksResponse,
type SpecStats,
} from '../lib/api';
// Query keys for specs settings
export const specsSettingsKeys = {
all: ['specsSettings'] as const,
systemSettings: () => [...specsSettingsKeys.all, 'systemSettings'] as const,
specStats: (projectPath?: string) => [...specsSettingsKeys.all, 'specStats', projectPath] as const,
};
// ========================================
// System Settings Query Hook
// ========================================
export interface UseSystemSettingsReturn {
data: SystemSettings | undefined;
isLoading: boolean;
error: Error | null;
refetch: () => void;
}
/**
* Hook to fetch system settings (injection control, personal spec defaults, recommended hooks)
*/
export function useSystemSettings(): UseSystemSettingsReturn {
const query = useQuery({
queryKey: specsSettingsKeys.systemSettings(),
queryFn: getSystemSettings,
staleTime: STALE_TIME,
retry: 1,
});
return {
data: query.data,
isLoading: query.isLoading,
error: query.error,
refetch: () => { query.refetch(); },
};
}
// ========================================
// Update System Settings Mutation Hook
// ========================================
export function useUpdateSystemSettings() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (data: UpdateSystemSettingsInput) => updateSystemSettings(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: specsSettingsKeys.systemSettings() });
},
});
return {
updateSettings: mutation.mutateAsync,
isPending: mutation.isPending,
error: mutation.error,
};
}
// ========================================
// Install Recommended Hooks Mutation Hook
// ========================================
export function useInstallRecommendedHooks() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: ({ hookIds, scope }: { hookIds: string[]; scope?: 'global' | 'project' }) =>
installRecommendedHooks(hookIds, scope),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: specsSettingsKeys.systemSettings() });
},
});
return {
installHooks: (hookIds: string[], scope?: 'global' | 'project') =>
mutation.mutateAsync({ hookIds, scope }),
isPending: mutation.isPending,
error: mutation.error,
data: mutation.data,
};
}
// ========================================
// Spec Stats Query Hook
// ========================================
export interface UseSpecStatsOptions {
projectPath?: string;
enabled?: boolean;
staleTime?: number;
}
export interface UseSpecStatsReturn {
data: SpecStats | undefined;
isLoading: boolean;
error: Error | null;
refetch: () => void;
}
/**
* Hook to fetch spec statistics (dimensions count, injection length info)
* @param options - Options including projectPath for workspace isolation
*/
export function useSpecStats(options: UseSpecStatsOptions = {}): UseSpecStatsReturn {
const { projectPath, enabled = true, staleTime = STALE_TIME } = options;
const query = useQuery({
queryKey: specsSettingsKeys.specStats(projectPath),
queryFn: () => getSpecStats(projectPath),
staleTime,
enabled,
retry: 1,
});
return {
data: query.data,
isLoading: query.isLoading,
error: query.error,
refetch: () => { query.refetch(); },
};
}