Spike 61: filesystem watcher and UI simplification
Add notify-based filesystem watcher for .story_kit/work/ that auto-commits changes with deterministic messages and broadcasts events over WebSocket. Push full pipeline state (Upcoming, Current, QA, To Merge) to frontend on connect and after every watcher event. Strip dead UI: remove ReviewPanel, GatePanel, TodoPanel, UpcomingPanel and all associated REST polling. Replace with 4 generic StagePanel components driven by WebSocket. Simplify AgentPanel to roster-only. Delete all 11 workflow HTTP endpoints and 16 request/response types from the server. Clean dead code from workflow module. MCP tools call Rust functions directly and need none of the HTTP layer. Net: ~4,100 lines deleted, ~400 added. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,11 +8,25 @@ export type WsRequest =
|
||||
type: "cancel";
|
||||
};
|
||||
|
||||
export interface PipelineStageItem {
|
||||
story_id: string;
|
||||
name: string | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export interface PipelineState {
|
||||
upcoming: PipelineStageItem[];
|
||||
current: PipelineStageItem[];
|
||||
qa: PipelineStageItem[];
|
||||
merge: PipelineStageItem[];
|
||||
}
|
||||
|
||||
export type WsResponse =
|
||||
| { type: "token"; content: string }
|
||||
| { type: "update"; messages: Message[] }
|
||||
| { type: "session_id"; session_id: string }
|
||||
| { type: "error"; message: string };
|
||||
| { type: "error"; message: string }
|
||||
| { type: "pipeline_state"; upcoming: PipelineStageItem[]; current: PipelineStageItem[]; qa: PipelineStageItem[]; merge: PipelineStageItem[] };
|
||||
|
||||
export interface ProviderConfig {
|
||||
provider: string;
|
||||
@@ -216,6 +230,7 @@ export class ChatWebSocket {
|
||||
private onUpdate?: (messages: Message[]) => void;
|
||||
private onSessionId?: (sessionId: string) => void;
|
||||
private onError?: (message: string) => void;
|
||||
private onPipelineState?: (state: PipelineState) => void;
|
||||
private connected = false;
|
||||
private closeTimer?: number;
|
||||
|
||||
@@ -225,6 +240,7 @@ export class ChatWebSocket {
|
||||
onUpdate?: (messages: Message[]) => void;
|
||||
onSessionId?: (sessionId: string) => void;
|
||||
onError?: (message: string) => void;
|
||||
onPipelineState?: (state: PipelineState) => void;
|
||||
},
|
||||
wsPath = DEFAULT_WS_PATH,
|
||||
) {
|
||||
@@ -232,6 +248,7 @@ export class ChatWebSocket {
|
||||
this.onUpdate = handlers.onUpdate;
|
||||
this.onSessionId = handlers.onSessionId;
|
||||
this.onError = handlers.onError;
|
||||
this.onPipelineState = handlers.onPipelineState;
|
||||
|
||||
if (this.connected) {
|
||||
return;
|
||||
@@ -263,6 +280,7 @@ export class ChatWebSocket {
|
||||
if (data.type === "update") this.onUpdate?.(data.messages);
|
||||
if (data.type === "session_id") this.onSessionId?.(data.session_id);
|
||||
if (data.type === "error") this.onError?.(data.message);
|
||||
if (data.type === "pipeline_state") this.onPipelineState?.({ upcoming: data.upcoming, current: data.current, qa: data.qa, merge: data.merge });
|
||||
} catch (err) {
|
||||
this.onError?.(String(err));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user