feat: initial prototype
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 47s

This commit is contained in:
2026-01-14 20:56:28 -08:00
parent daf1bfecb8
commit f393dfb359
68 changed files with 9391 additions and 12 deletions
+40
View File
@@ -0,0 +1,40 @@
import { writable, derived } from "svelte/store";
import { CHARACTER_STATES, type CharacterState } from "$lib/types/states";
function createCharacterStore() {
const { subscribe, set, update } = writable<CharacterState>("idle");
let stateTimeout: ReturnType<typeof setTimeout> | null = null;
return {
subscribe,
setState: (state: CharacterState) => {
if (stateTimeout) {
clearTimeout(stateTimeout);
stateTimeout = null;
}
set(state);
},
setTemporaryState: (state: CharacterState, durationMs: number = 2000) => {
if (stateTimeout) {
clearTimeout(stateTimeout);
}
set(state);
stateTimeout = setTimeout(() => {
set("idle");
stateTimeout = null;
}, durationMs);
},
reset: () => {
if (stateTimeout) {
clearTimeout(stateTimeout);
stateTimeout = null;
}
set("idle");
},
};
}
export const characterState = createCharacterStore();
export const characterInfo = derived(characterState, ($state) => CHARACTER_STATES[$state]);
+131
View File
@@ -0,0 +1,131 @@
import { writable, derived } from "svelte/store";
import type {
ConnectionStatus,
PermissionRequest,
ClaudeStreamMessage,
} from "$lib/types/messages";
export interface TerminalLine {
id: string;
type: "user" | "assistant" | "system" | "tool" | "error";
content: string;
timestamp: Date;
toolName?: string;
}
function createClaudeStore() {
const connectionStatus = writable<ConnectionStatus>("disconnected");
const sessionId = writable<string | null>(null);
const currentWorkingDirectory = writable<string>("");
const terminalLines = writable<TerminalLine[]>([]);
const pendingPermission = writable<PermissionRequest | null>(null);
const isProcessing = writable<boolean>(false);
const grantedTools = writable<Set<string>>(new Set());
const pendingRetryMessage = writable<string | null>(null);
let lineIdCounter = 0;
function generateLineId(): string {
return `line-${Date.now()}-${lineIdCounter++}`;
}
return {
connectionStatus: { subscribe: connectionStatus.subscribe },
sessionId: { subscribe: sessionId.subscribe },
currentWorkingDirectory: { subscribe: currentWorkingDirectory.subscribe },
terminalLines: { subscribe: terminalLines.subscribe },
pendingPermission: { subscribe: pendingPermission.subscribe },
isProcessing: { subscribe: isProcessing.subscribe },
grantedTools: { subscribe: grantedTools.subscribe },
pendingRetryMessage: { subscribe: pendingRetryMessage.subscribe },
setConnectionStatus: (status: ConnectionStatus) => connectionStatus.set(status),
setSessionId: (id: string | null) => sessionId.set(id),
setWorkingDirectory: (dir: string) => currentWorkingDirectory.set(dir),
setProcessing: (processing: boolean) => isProcessing.set(processing),
addLine: (type: TerminalLine["type"], content: string, toolName?: string) => {
const line: TerminalLine = {
id: generateLineId(),
type,
content,
timestamp: new Date(),
toolName,
};
terminalLines.update((lines) => [...lines, line]);
return line.id;
},
updateLine: (id: string, content: string) => {
terminalLines.update((lines) =>
lines.map((line) => (line.id === id ? { ...line, content } : line))
);
},
appendToLine: (id: string, additionalContent: string) => {
terminalLines.update((lines) =>
lines.map((line) =>
line.id === id ? { ...line, content: line.content + additionalContent } : line
)
);
},
clearTerminal: () => terminalLines.set([]),
getConversationHistory: (): string => {
let lines: TerminalLine[] = [];
terminalLines.subscribe((l) => (lines = l))();
// Filter to just user and assistant messages, skip system/tool noise
const relevantLines = lines.filter(
(line) => line.type === "user" || line.type === "assistant"
);
if (relevantLines.length === 0) return "";
return relevantLines
.map((line) => {
const role = line.type === "user" ? "User" : "Assistant";
return `${role}: ${line.content}`;
})
.join("\n\n");
},
requestPermission: (request: PermissionRequest) => pendingPermission.set(request),
clearPermission: () => pendingPermission.set(null),
grantTool: (toolName: string) => {
grantedTools.update((tools) => {
const newTools = new Set(tools);
newTools.add(toolName);
return newTools;
});
},
getGrantedTools: (): string[] => {
let tools: string[] = [];
grantedTools.subscribe((t) => (tools = Array.from(t)))();
return tools;
},
setPendingRetryMessage: (message: string | null) => pendingRetryMessage.set(message),
reset: () => {
connectionStatus.set("disconnected");
sessionId.set(null);
currentWorkingDirectory.set("");
terminalLines.set([]);
pendingPermission.set(null);
isProcessing.set(false);
grantedTools.set(new Set());
pendingRetryMessage.set(null);
},
};
}
export const claudeStore = createClaudeStore();
export const hasPermissionPending = derived(
claudeStore.pendingPermission,
($permission) => $permission !== null
);