Initial CodeCraft: web-based coding harness for AI models

Rust backend (axum/tokio): agent loop with streaming, tools (fs, bash,
web_fetch, todo, ask_user, subagents, skills, craft snapshots/compare),
permissions with modes and rules, Anthropic + OpenAI-compatible providers,
MCP client (stdio/HTTP, elicitation), SQLite sessions, usage statistics,
compaction, commented TOML config editable via toml_edit.

React PWA: chat with live streaming, permission/question cards, modes
(build/plan/craft), settings for providers/models/prices/MCP/permissions,
raw config editor, statistics dashboard, i18n (en/de). Docker setup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-08 14:39:24 +01:00
commit f3a2fe3816
62 files changed
+21835

No files matched your search

+116
View File
@@ -0,0 +1,116 @@
import { useT } from "../i18n";
import { navigate } from "../router";
import { useStore } from "../store";
import type { Part, Todo } from "../types";
import { Diff } from "./Diff";
type ToolCall = Extract<Part, { type: "tool_call" }>;
type ToolResult = Extract<Part, { type: "tool_result" }>;
function short(s: unknown, n = 90): string {
const str = typeof s === "string" ? s : JSON.stringify(s);
return str.length > n ? `${str.slice(0, n)}…` : str;
}
/** One-line summary of a tool call. */
export function toolTitle(name: string, input: any): string {
switch (name) {
case "read":
case "write":
case "edit":
return input?.path ?? "";
case "list":
return input?.path ?? ".";
case "bash":
return input?.description || input?.command || "";
case "glob":
case "grep":
return `${input?.pattern ?? ""}${input?.path ? ` in ${input.path}` : ""}`;
case "web_fetch":
case "browser_snapshot":
return input?.url ?? "";
case "task":
return `${input?.subagent_type ?? "general"}: ${input?.description ?? ""}`;
case "image_compare":
return `${input?.original ?? ""} ↔ ${input?.candidate ?? ""}`;
case "skill":
return input?.name ?? "";
case "ask_user":
return input?.questions?.[0]?.question ?? "";
default:
return short(input);
}
}
export function TodoList({ todos }: { todos: Todo[] }) {
const icon = { completed: "☑", in_progress: "▶", pending: "☐" } as const;
return (
<ul>
{todos.map((td, i) => (
<li key={i} className={td.status}>
<span>{icon[td.status] ?? "☐"}</span>
<span>{td.content}</span>
</li>
))}
</ul>
);
}
export function ToolCard({ call, result, running }: { call: ToolCall; result?: ToolResult; running: boolean }) {
const t = useT();
const progress = useStore((s) => s.progress[call.id]);
const status = result ? (result.is_error ? "err" : "ok") : running ? "run" : "err";
const statusText = result ? (result.is_error ? "✗" : "✓") : running ? "…" : "–";
const meta = result?.meta;
// The todo list is shown in its own panel; keep the card compact.
if (call.name === "todo_write" && !result?.is_error) {
return (
<div className="todos">
<div style={{ color: "var(--muted)", fontSize: 12 }}>{t("tool.todos")}</div>
<TodoList todos={call.input?.todos ?? []} />
</div>
);
}
return (
<details className="tool" open={status === "err" && !!result}>
<summary>
<span className="tool-name">{call.name}</span>
<span className="tool-title">{toolTitle(call.name, call.input)}</span>
{meta?.similarity !== undefined && <span className="chip">{meta.similarity} %</span>}
<span className={`status ${status}`}>{statusText}</span>
</summary>
<div className="tool-body">
{call.name === "bash" && <pre className="mono">$ {call.input?.command}</pre>}
{(call.name === "edit" || call.name === "write") && meta?.diff && <Diff diff={meta.diff} />}
{call.name === "task" && meta?.session_id && (
<button className="small" onClick={() => navigate(`/s/${meta.session_id}`)}>
{t("tool.openSubagent")}
</button>
)}
{!["bash", "edit", "write", "task"].includes(call.name) && (
<>
<div className="label">{t("tool.input")}</div>
<pre className="mono">{JSON.stringify(call.input, null, 2)}</pre>
</>
)}
{!result && progress && (
<>
<div className="label">{t("tool.output")}</div>
<pre className="mono">{progress}</pre>
</>
)}
{result && !((call.name === "edit" || call.name === "write") && meta?.diff && !result.is_error) && (
<>
<div className="label">{t("tool.output")}</div>
<pre className="mono">{result.content}</pre>
</>
)}
{result?.images?.map((img, i) => (
<img key={i} src={`data:${img.media_type};base64,${img.data}`} alt="" />
))}
</div>
</details>
);
}