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

+188
View File
@@ -0,0 +1,188 @@
import { useState } from "react";
import { post } from "../api";
import { useT } from "../i18n";
import { navigate } from "../router";
import { useStore } from "../store";
import type { PermissionRequest, QuestionRequest } from "../types";
import { Diff } from "./Diff";
function SubagentHint({ sessionId, currentId }: { sessionId: string; currentId: string }) {
const t = useT();
const title = useStore((s) => s.sessionById[sessionId]?.title);
if (sessionId === currentId) return null;
return (
<div className="from">
{t("interaction.fromSubagent")}{" "}
<a href={`#/s/${sessionId}`} onClick={(e) => (e.preventDefault(), navigate(`/s/${sessionId}`))}>
{title || sessionId.slice(-6)}
</a>
</div>
);
}
export function PermissionCard({ req, currentId }: { req: PermissionRequest; currentId: string }) {
const t = useT();
const [feedback, setFeedback] = useState("");
const [busy, setBusy] = useState(false);
const reply = async (r: "once" | "always" | "reject") => {
setBusy(true);
try {
await post(`/permissions/${req.id}`, { reply: r, message: r === "reject" ? feedback : undefined });
} finally {
setBusy(false);
}
};
const command: string | undefined = req.tool === "bash" ? req.input?.command : undefined;
const alwaysLabel =
req.tool === "bash"
? t("permission.alwaysCommand", { prefix: (command ?? "").split(/\s+/).slice(0, 2).join(" ") })
: t("permission.alwaysTool", { tool: req.tool });
return (
<div className="card" role="alertdialog" aria-label={t("permission.title")}>
<h3>
🔐 {t("permission.title")} <span className="chip mono">{req.tool}</span>
</h3>
<SubagentHint sessionId={req.session_id} currentId={currentId} />
<div style={{ margin: "6px 0" }}>{req.title}</div>
{command && <pre className="mono" style={{ whiteSpace: "pre-wrap", margin: "6px 0" }}>$ {command}</pre>}
{req.diff ? (
<Diff diff={req.diff} />
) : (
!command && (
<details>
<summary style={{ cursor: "pointer", color: "var(--muted)" }}>{t("tool.input")}</summary>
<pre className="mono" style={{ whiteSpace: "pre-wrap" }}>
{JSON.stringify(req.input, null, 2)}
</pre>
</details>
)
)}
<input
style={{ width: "100%", marginTop: 8 }}
placeholder={t("permission.feedback")}
value={feedback}
onChange={(e) => setFeedback(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && reply("reject")}
/>
<div className="actions">
<button className="primary" disabled={busy} onClick={() => reply("once")} autoFocus>
{t("permission.allowOnce")}
</button>
<button disabled={busy} onClick={() => reply("always")}>
{alwaysLabel}
</button>
<button className="danger" disabled={busy} onClick={() => reply("reject")}>
{feedback ? t("permission.rejectWithFeedback") : t("permission.reject")}
</button>
</div>
</div>
);
}
const OTHER = "__other__";
export function QuestionCard({ req, currentId }: { req: QuestionRequest; currentId: string }) {
const t = useT();
const [selected, setSelected] = useState<Record<number, string[]>>({});
const [other, setOther] = useState<Record<number, string>>({});
const [busy, setBusy] = useState(false);
const toggle = (qi: number, label: string, multi: boolean) => {
setSelected((s) => {
const cur = s[qi] ?? [];
if (!multi) return { ...s, [qi]: [label] };
return { ...s, [qi]: cur.includes(label) ? cur.filter((x) => x !== label) : [...cur, label] };
});
};
const answerFor = (qi: number): string | string[] | undefined => {
const q = req.questions[qi];
const sel = (selected[qi] ?? []).map((l) => (l === OTHER ? other[qi]?.trim() ?? "" : l)).filter(Boolean);
if (q.options.length === 0) return other[qi]?.trim() || undefined;
if (sel.length === 0) return undefined;
return q.multi_select ? sel : sel[0];
};
const complete = req.questions.every((_, i) => answerFor(i) !== undefined);
const submit = async (dismiss = false) => {
setBusy(true);
try {
const answers = dismiss ? null : Object.fromEntries(req.questions.map((q, i) => [q.question, answerFor(i)]));
await post(`/questions/${req.id}`, { answers });
} finally {
setBusy(false);
}
};
const source = req.source.startsWith("mcp:") ? t("question.fromMcp", { server: req.source.slice(4) }) : t("question.fromAgent");
return (
<div className="card" role="dialog" aria-label={t("question.title")}>
<h3>❓ {t("question.title")}</h3>
<div className="from">{source}</div>
<SubagentHint sessionId={req.session_id} currentId={currentId} />
{req.questions.map((q, qi) => (
<div key={qi} style={{ marginTop: 10 }}>
{q.header && <span className="q-header">{q.header}</span>}
<div style={{ whiteSpace: "pre-wrap", color: "var(--fg0)" }}>{q.question}</div>
{q.options.length > 0 ? (
<div className="q-options" role={q.multi_select ? "group" : "radiogroup"}>
{[...q.options, { label: OTHER, description: "" }].map((o) => {
const isSel = (selected[qi] ?? []).includes(o.label);
return (
<div
key={o.label}
className={`q-option ${isSel ? "selected" : ""}`}
role={q.multi_select ? "checkbox" : "radio"}
aria-checked={isSel}
tabIndex={0}
onClick={() => toggle(qi, o.label, !!q.multi_select)}
onKeyDown={(e) => (e.key === " " || e.key === "Enter") && toggle(qi, o.label, !!q.multi_select)}
>
<span>{q.multi_select ? (isSel ? "☑" : "☐") : isSel ? "◉" : "○"}</span>
<div style={{ flex: 1 }}>
{o.label === OTHER ? (
<input
style={{ width: "100%" }}
placeholder={t("question.other")}
value={other[qi] ?? ""}
onClick={(e) => e.stopPropagation()}
onFocus={() => !isSel && toggle(qi, OTHER, !!q.multi_select)}
onChange={(e) => setOther((s) => ({ ...s, [qi]: e.target.value }))}
/>
) : (
<>
<div>{o.label}</div>
{o.description && <div className="desc">{o.description}</div>}
</>
)}
</div>
</div>
);
})}
</div>
) : (
<textarea
style={{ width: "100%", marginTop: 6 }}
placeholder={t("question.answer")}
value={other[qi] ?? ""}
onChange={(e) => setOther((s) => ({ ...s, [qi]: e.target.value }))}
/>
)}
</div>
))}
<div className="actions">
<button className="primary" disabled={busy || !complete} onClick={() => submit()}>
{t("question.submit")}
</button>
<button disabled={busy} onClick={() => submit(true)}>
{t("question.dismiss")}
</button>
</div>
</div>
);
}