shimwrappercheck 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +20 -3
  3. package/dashboard/app/agents/page.tsx +4 -4
  4. package/dashboard/app/api/run-checks/route.ts +49 -22
  5. package/dashboard/app/api/settings/route.ts +11 -1
  6. package/dashboard/app/api/status/route.ts +14 -0
  7. package/dashboard/app/config/page.tsx +3 -3
  8. package/dashboard/app/globals.css +2 -9
  9. package/dashboard/app/layout.tsx +11 -5
  10. package/dashboard/app/page.tsx +43 -14
  11. package/dashboard/app/settings/page.tsx +62 -17
  12. package/dashboard/components/CheckCard.tsx +116 -0
  13. package/dashboard/components/CheckCardList.tsx +151 -0
  14. package/dashboard/components/Header.tsx +29 -0
  15. package/dashboard/components/SidebarMyShim.tsx +44 -0
  16. package/dashboard/components/StatusCard.tsx +4 -4
  17. package/dashboard/components/TriggerCommandos.tsx +100 -0
  18. package/dashboard/lib/checks.ts +116 -0
  19. package/dashboard/lib/presets.ts +41 -0
  20. package/dashboard/next-env.d.ts +5 -0
  21. package/dashboard/package.json +1 -1
  22. package/dashboard/scripts/find-port-and-dev.js +30 -0
  23. package/package.json +26 -2
  24. package/scripts/ai-code-review.sh +217 -0
  25. package/scripts/ai-deductive-review.js +142 -0
  26. package/scripts/cli.js +8 -1
  27. package/scripts/find-free-port.js +21 -0
  28. package/scripts/git-checked.sh +25 -9
  29. package/scripts/init.js +81 -4
  30. package/scripts/prepublish-clean.js +11 -0
  31. package/scripts/run-checks.sh +67 -0
  32. package/scripts/setup.js +1 -0
  33. package/scripts/shim-runner.js +194 -0
  34. package/templates/.dependency-cruiser.json +35 -0
  35. package/templates/.semgrep.example.yml +19 -0
  36. package/templates/eslint.complexity.json +12 -0
  37. package/templates/git-pre-push +13 -9
  38. package/templates/husky-pre-push +10 -7
  39. package/templates/stryker.config.json +16 -0
  40. package/dashboard/.next/cache/config.json +0 -7
  41. package/dashboard/.next/package.json +0 -1
  42. package/dashboard/.next/routes-manifest.json +0 -1
  43. package/dashboard/.next/trace +0 -1
  44. package/dashboard/package-lock.json +0 -5307
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Single check card with Info and Settings tabs; optional drag handle.
3
+ * Location: /components/CheckCard.tsx
4
+ */
5
+ "use client";
6
+
7
+ import { useState } from "react";
8
+ import type { CheckDef } from "@/lib/checks";
9
+
10
+ export default function CheckCard({
11
+ def,
12
+ enabled,
13
+ onToggle,
14
+ dragHandle,
15
+ checkSettings,
16
+ onSettingsChange,
17
+ }: {
18
+ def: CheckDef;
19
+ enabled: boolean;
20
+ onToggle: (v: boolean) => void;
21
+ dragHandle?: React.ReactNode;
22
+ checkSettings?: Record<string, unknown>;
23
+ onSettingsChange?: (partial: Record<string, unknown>) => void;
24
+ }) {
25
+ const [tab, setTab] = useState<"info" | "settings">("info");
26
+ const hasEnabledToggle = def.settings.some((s) => s.key === "enabled");
27
+
28
+ return (
29
+ <div className="border border-neutral-600 rounded-lg bg-neutral-800/80 overflow-hidden">
30
+ <div className="flex items-center gap-2 px-3 py-2 border-b border-neutral-600">
31
+ {dragHandle}
32
+ {hasEnabledToggle && (
33
+ <label className="flex items-center gap-2 flex-1 cursor-pointer">
34
+ <input
35
+ type="checkbox"
36
+ className="toggle toggle-sm"
37
+ checked={enabled}
38
+ onChange={(e) => onToggle(e.target.checked)}
39
+ />
40
+ <span className="font-medium text-sm">{def.label}</span>
41
+ </label>
42
+ )}
43
+ {!hasEnabledToggle && <span className="font-medium text-sm flex-1">{def.label}</span>}
44
+ </div>
45
+ <div className="tabs tabs-boxed px-2 pt-2">
46
+ <button
47
+ type="button"
48
+ className={`tab tab-sm ${tab === "info" ? "tab-active" : ""}`}
49
+ onClick={() => setTab("info")}
50
+ >
51
+ Info
52
+ </button>
53
+ <button
54
+ type="button"
55
+ className={`tab tab-sm ${tab === "settings" ? "tab-active" : ""}`}
56
+ onClick={() => setTab("settings")}
57
+ >
58
+ Settings
59
+ </button>
60
+ </div>
61
+ <div className="p-3 text-sm text-neutral-300 min-h-[4rem]">
62
+ {tab === "info" && <p className="whitespace-pre-wrap">{def.info}</p>}
63
+ {tab === "settings" && (
64
+ <div className="space-y-2">
65
+ {def.settings.map((s) => {
66
+ if (s.type === "boolean" && s.key === "enabled") return null;
67
+ const val = checkSettings?.[s.key] ?? s.default;
68
+ return (
69
+ <div key={s.key}>
70
+ <label className="text-xs text-neutral-400">{s.label}</label>
71
+ {s.type === "boolean" && (
72
+ <input
73
+ type="checkbox"
74
+ className="toggle toggle-sm ml-2"
75
+ checked={val as boolean}
76
+ onChange={(e) => onSettingsChange?.({ [s.key]: e.target.checked })}
77
+ />
78
+ )}
79
+ {s.type === "number" && (
80
+ <input
81
+ type="number"
82
+ className="input input-sm input-bordered bg-neutral-900 border-neutral-600 text-white w-full mt-1"
83
+ value={val != null ? String(val) : ""}
84
+ onChange={(e) => onSettingsChange?.({ [s.key]: e.target.value ? Number(e.target.value) : s.default })}
85
+ />
86
+ )}
87
+ {s.type === "string" && (
88
+ <input
89
+ type="text"
90
+ className="input input-sm input-bordered bg-neutral-900 border-neutral-600 text-white w-full mt-1"
91
+ value={val != null ? String(val) : ""}
92
+ onChange={(e) => onSettingsChange?.({ [s.key]: e.target.value })}
93
+ />
94
+ )}
95
+ {s.type === "select" && (
96
+ <select
97
+ className="select select-sm select-bordered bg-neutral-900 border-neutral-600 text-white w-full mt-1"
98
+ value={val != null ? String(val) : ""}
99
+ onChange={(e) => onSettingsChange?.({ [s.key]: e.target.value })}
100
+ >
101
+ {s.options?.map((o) => (
102
+ <option key={o.value} value={o.value}>
103
+ {o.label}
104
+ </option>
105
+ ))}
106
+ </select>
107
+ )}
108
+ </div>
109
+ );
110
+ })}
111
+ </div>
112
+ )}
113
+ </div>
114
+ </div>
115
+ );
116
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * List of checks: "simple" = search + name-only cards; default = full cards with drag and Info/Settings tabs.
3
+ * Location: /components/CheckCardList.tsx
4
+ */
5
+ "use client";
6
+
7
+ import { useState, useCallback } from "react";
8
+ import type { SettingsData } from "@/lib/presets";
9
+ import { CHECK_DEFINITIONS } from "@/lib/checks";
10
+ import CheckCard from "./CheckCard";
11
+
12
+ const DEFAULT_ORDER: string[] = [
13
+ "frontend",
14
+ "backend",
15
+ "aiReview",
16
+ "sast",
17
+ "architecture",
18
+ "complexity",
19
+ "mutation",
20
+ "e2e",
21
+ "healthPing",
22
+ "edgeLogs",
23
+ ];
24
+
25
+ export default function CheckCardList({
26
+ settings,
27
+ onSave,
28
+ variant = "full",
29
+ }: {
30
+ settings: SettingsData | null;
31
+ onSave: (s: SettingsData) => void;
32
+ variant?: "simple" | "full";
33
+ }) {
34
+ const order = settings?.checkOrder ?? DEFAULT_ORDER;
35
+ const [search, setSearch] = useState("");
36
+ const [dragId, setDragId] = useState<string | null>(null);
37
+ const [dropIndex, setDropIndex] = useState<number | null>(null);
38
+
39
+ const toggles = settings?.checkToggles ?? {};
40
+ const ordered = order
41
+ .map((id) => CHECK_DEFINITIONS.find((c) => c.id === id))
42
+ .filter(Boolean) as typeof CHECK_DEFINITIONS;
43
+ const rest = CHECK_DEFINITIONS.filter((c) => !order.includes(c.id));
44
+ const list = [...ordered, ...rest];
45
+ const filtered =
46
+ variant === "simple" && search.trim()
47
+ ? list.filter((c) => c.label.toLowerCase().includes(search.trim().toLowerCase()))
48
+ : list;
49
+
50
+ const moveOrder = useCallback(
51
+ (fromId: string, toIndex: number) => {
52
+ const idx = order.indexOf(fromId);
53
+ if (idx === -1 || idx === toIndex) return;
54
+ const next = [...order];
55
+ next.splice(idx, 1);
56
+ next.splice(toIndex, 0, fromId);
57
+ onSave({ ...settings!, checkOrder: next });
58
+ },
59
+ [order, settings, onSave]
60
+ );
61
+
62
+ const handleToggle = (id: string, value: boolean) => {
63
+ if (!settings) return;
64
+ onSave({
65
+ ...settings,
66
+ checkToggles: { ...settings.checkToggles, [id]: value },
67
+ });
68
+ };
69
+
70
+ const handleSettingsChange = (checkId: string, partial: Record<string, unknown>) => {
71
+ if (!settings) return;
72
+ onSave({
73
+ ...settings,
74
+ checkSettings: {
75
+ ...settings.checkSettings,
76
+ [checkId]: { ...(settings.checkSettings as Record<string, Record<string, unknown>>)?.[checkId], ...partial },
77
+ },
78
+ });
79
+ };
80
+
81
+ if (variant === "simple") {
82
+ return (
83
+ <div className="space-y-2">
84
+ <div className="relative">
85
+ <input
86
+ type="text"
87
+ placeholder="Suchen..."
88
+ className="input input-bordered w-full input-sm bg-neutral-800 border-neutral-600 text-white pl-8"
89
+ value={search}
90
+ onChange={(e) => setSearch(e.target.value)}
91
+ />
92
+ <svg
93
+ className="absolute left-2 top-1/2 -translate-y-1/2 w-4 h-4 text-neutral-500 pointer-events-none"
94
+ fill="none"
95
+ stroke="currentColor"
96
+ viewBox="0 0 24 24"
97
+ >
98
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
99
+ </svg>
100
+ </div>
101
+ <ul className="space-y-2">
102
+ {filtered.map((def) => (
103
+ <li
104
+ key={def.id}
105
+ className="border border-neutral-600 rounded-lg bg-neutral-800/80 px-3 py-2 text-sm text-white"
106
+ >
107
+ {def.label}
108
+ </li>
109
+ ))}
110
+ </ul>
111
+ </div>
112
+ );
113
+ }
114
+
115
+ return (
116
+ <ul className="space-y-3">
117
+ {list.map((def, index) => (
118
+ <li
119
+ key={def.id}
120
+ className={dropIndex === index ? "ring-1 ring-primary rounded-lg" : ""}
121
+ draggable
122
+ onDragStart={() => setDragId(def.id)}
123
+ onDragEnd={() => {
124
+ setDragId(null);
125
+ setDropIndex(null);
126
+ }}
127
+ onDragOver={(e) => {
128
+ e.preventDefault();
129
+ setDropIndex(index);
130
+ }}
131
+ onDragLeave={() => setDropIndex(null)}
132
+ onDrop={(e) => {
133
+ e.preventDefault();
134
+ if (dragId) moveOrder(dragId, index);
135
+ setDragId(null);
136
+ setDropIndex(null);
137
+ }}
138
+ >
139
+ <CheckCard
140
+ def={def}
141
+ enabled={(toggles as Record<string, boolean>)[def.id] ?? true}
142
+ onToggle={(v) => handleToggle(def.id, v)}
143
+ checkSettings={(settings?.checkSettings as Record<string, Record<string, unknown>>)?.[def.id]}
144
+ onSettingsChange={(partial) => handleSettingsChange(def.id, partial)}
145
+ dragHandle={<span className="cursor-grab text-neutral-500 select-none" title="Ziehen zum Sortieren">⋮⋮</span>}
146
+ />
147
+ </li>
148
+ ))}
149
+ </ul>
150
+ );
151
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Header: shimwrappercheck centered at top; nav left.
3
+ * Location: /components/Header.tsx
4
+ */
5
+ "use client";
6
+
7
+ import Link from "next/link";
8
+
9
+ export default function Header() {
10
+ return (
11
+ <header className="h-16 border-b border-neutral-700 flex items-center justify-between px-6 bg-[#0f0f0f]">
12
+ <nav className="flex gap-2">
13
+ <Link href="/" className="text-sm text-neutral-300 hover:text-white">
14
+ Dashboard
15
+ </Link>
16
+ <Link href="/settings" className="text-sm text-neutral-300 hover:text-white">
17
+ Einstellungen
18
+ </Link>
19
+ <Link href="/config" className="text-sm text-neutral-300 hover:text-white">
20
+ Config
21
+ </Link>
22
+ <Link href="/agents" className="text-sm text-neutral-300 hover:text-white">
23
+ AGENTS.md
24
+ </Link>
25
+ </nav>
26
+ <div className="absolute left-1/2 -translate-x-1/2 text-xl font-semibold text-white">shimwrappercheck</div>
27
+ </header>
28
+ );
29
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Left sidebar "My Shim": Trigger commandos + Checks (simple cards with search).
3
+ * Location: /components/SidebarMyShim.tsx
4
+ */
5
+ "use client";
6
+
7
+ import { useEffect, useState } from "react";
8
+ import type { SettingsData } from "@/lib/presets";
9
+ import TriggerCommandos from "@/components/TriggerCommandos";
10
+ import CheckCardList from "@/components/CheckCardList";
11
+
12
+ export default function SidebarMyShim() {
13
+ const [settings, setSettings] = useState<SettingsData | null>(null);
14
+
15
+ useEffect(() => {
16
+ fetch("/api/settings")
17
+ .then((r) => r.json())
18
+ .then((data) => setSettings(data))
19
+ .catch(() => setSettings(null));
20
+ }, []);
21
+
22
+ const saveSettings = (next: SettingsData) => {
23
+ setSettings(next);
24
+ fetch("/api/settings", {
25
+ method: "POST",
26
+ headers: { "Content-Type": "application/json" },
27
+ body: JSON.stringify(next),
28
+ }).catch(() => {});
29
+ };
30
+
31
+ return (
32
+ <div className="p-4 space-y-6">
33
+ <h2 className="text-lg font-semibold text-white">My Shim</h2>
34
+ <section>
35
+ <h3 className="text-sm font-medium text-white mb-2">Trigger commandos</h3>
36
+ <TriggerCommandos settings={settings} onSave={saveSettings} />
37
+ </section>
38
+ <section>
39
+ <h3 className="text-sm font-medium text-white mb-2">Checks</h3>
40
+ <CheckCardList settings={settings} onSave={saveSettings} variant="simple" />
41
+ </section>
42
+ </div>
43
+ );
44
+ }
@@ -14,13 +14,13 @@ export default function StatusCard({
14
14
  detail?: string;
15
15
  }) {
16
16
  return (
17
- <div className="card bg-base-100 shadow-md">
17
+ <div className="card bg-neutral-800 border border-neutral-600 shadow-md">
18
18
  <div className="card-body p-4">
19
- <h3 className="card-title text-sm">{label}</h3>
20
- <p className={ok ? "text-success" : "text-warning"}>
19
+ <h3 className="card-title text-sm text-white">{label}</h3>
20
+ <p className={ok ? "text-green-400" : "text-amber-400"}>
21
21
  {ok ? "✓ Vorhanden" : "— Nicht gefunden"}
22
22
  </p>
23
- {detail && <p className="text-xs opacity-80">{detail}</p>}
23
+ {detail && <p className="text-xs text-neutral-400">{detail}</p>}
24
24
  </div>
25
25
  </div>
26
26
  );
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Single text field for trigger commands (comma-separated); reads/writes Preset supabase/git enforce.
3
+ * Location: /components/TriggerCommandos.tsx
4
+ */
5
+ "use client";
6
+
7
+ import { useState, useEffect } from "react";
8
+ import type { SettingsData, Preset } from "@/lib/presets";
9
+ import { GIT_COMMAND_IDS } from "@/lib/presets";
10
+
11
+ function getTriggerLabels(preset: Preset | undefined): string[] {
12
+ if (!preset) return [];
13
+ const out: string[] = [];
14
+ if (preset.supabase?.enforce?.length) {
15
+ for (const c of preset.supabase.enforce) {
16
+ if (c === "functions") out.push("supabase functions deploy");
17
+ else if (c === "db") out.push("supabase db push");
18
+ else if (c === "migration") out.push("supabase migration");
19
+ else if (c === "push") out.push("supabase push");
20
+ else out.push(`supabase ${c}`);
21
+ }
22
+ }
23
+ if (preset.git?.enforce?.length) {
24
+ for (const c of preset.git.enforce) {
25
+ out.push(`git ${c}`);
26
+ }
27
+ }
28
+ return out;
29
+ }
30
+
31
+ function parseTriggerLabel(label: string): { type: "supabase" | "git"; id: string } | null {
32
+ const t = label.trim().toLowerCase();
33
+ if (t.startsWith("git ")) {
34
+ const id = t.slice(4).trim();
35
+ if ((GIT_COMMAND_IDS as readonly string[]).includes(id)) return { type: "git", id };
36
+ return { type: "git", id };
37
+ }
38
+ if (t.startsWith("supabase ")) {
39
+ const rest = t.slice(9).trim();
40
+ if (rest.startsWith("functions")) return { type: "supabase", id: "functions" };
41
+ if (rest.startsWith("db")) return { type: "supabase", id: "db" };
42
+ if (rest.startsWith("migration")) return { type: "supabase", id: "migration" };
43
+ if (rest.startsWith("push")) return { type: "supabase", id: "push" };
44
+ }
45
+ return null;
46
+ }
47
+
48
+ function labelsToPresetEnforce(labels: string[]): { supabase: string[]; git: string[] } {
49
+ const supabase: string[] = [];
50
+ const git: string[] = [];
51
+ for (const label of labels) {
52
+ const parsed = parseTriggerLabel(label);
53
+ if (!parsed) continue;
54
+ if (parsed.type === "supabase" && !supabase.includes(parsed.id)) supabase.push(parsed.id);
55
+ if (parsed.type === "git" && !git.includes(parsed.id)) git.push(parsed.id);
56
+ }
57
+ return { supabase, git };
58
+ }
59
+
60
+ export default function TriggerCommandos({
61
+ settings,
62
+ onSave,
63
+ }: {
64
+ settings: SettingsData | null;
65
+ onSave: (s: SettingsData) => void;
66
+ }) {
67
+ const preset = settings?.presets?.find((p) => p.id === settings.activePresetId);
68
+ const initialLabels = getTriggerLabels(preset);
69
+ const [value, setValue] = useState(initialLabels.join(", "));
70
+
71
+ useEffect(() => {
72
+ setValue(getTriggerLabels(preset).join(", "));
73
+ }, [preset?.id, preset?.supabase?.enforce, preset?.git?.enforce]);
74
+
75
+ const applyToPreset = () => {
76
+ if (!settings) return;
77
+ const labels = value.split(",").map((s) => s.trim()).filter(Boolean);
78
+ const { supabase: supabaseIds, git: gitIds } = labelsToPresetEnforce(labels);
79
+ const next = { ...settings };
80
+ const p = next.presets.find((x) => x.id === next.activePresetId);
81
+ if (!p) return;
82
+ if (p.supabase) p.supabase.enforce = supabaseIds;
83
+ if (p.git) p.git.enforce = gitIds;
84
+ onSave(next);
85
+ };
86
+
87
+ return (
88
+ <div className="space-y-2">
89
+ <input
90
+ type="text"
91
+ placeholder="z.B. git push, supabase push"
92
+ className="input input-bordered w-full bg-neutral-800 border-neutral-600 text-white text-sm"
93
+ value={value}
94
+ onChange={(e) => setValue(e.target.value)}
95
+ onBlur={applyToPreset}
96
+ onKeyDown={(e) => e.key === "Enter" && applyToPreset()}
97
+ />
98
+ </div>
99
+ );
100
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Check definitions: id, label, info text, and optional settings schema for each shim check.
3
+ * Location: /lib/checks.ts
4
+ */
5
+
6
+ import type { CheckToggles } from "./presets";
7
+
8
+ export type CheckId = keyof CheckToggles | "healthPing" | "edgeLogs";;
9
+
10
+ export interface CheckSettingOption {
11
+ key: string;
12
+ label: string;
13
+ type: "boolean" | "number" | "string" | "select";
14
+ default?: unknown;
15
+ options?: { value: string; label: string }[];
16
+ }
17
+
18
+ export interface CheckDef {
19
+ id: CheckId;
20
+ label: string;
21
+ info: string;
22
+ settings: CheckSettingOption[];
23
+ }
24
+
25
+ export const CHECK_DEFINITIONS: CheckDef[] = [
26
+ {
27
+ id: "frontend",
28
+ label: "Frontend",
29
+ info: "Führt Frontend-Checks aus: npm run lint, check:mock-data, build, test:run, npm audit (Security). Optional Snyk (Dependency-Scan), falls installiert.",
30
+ settings: [
31
+ { key: "enabled", label: "Aktiv", type: "boolean", default: true },
32
+ { key: "skipSnyk", label: "Snyk überspringen", type: "boolean", default: false },
33
+ { key: "auditLevel", label: "npm audit Stufe", type: "select", default: "high", options: [
34
+ { value: "critical", label: "critical" },
35
+ { value: "high", label: "high" },
36
+ { value: "moderate", label: "moderate" },
37
+ { value: "low", label: "low" },
38
+ ]},
39
+ ],
40
+ },
41
+ {
42
+ id: "backend",
43
+ label: "Backend",
44
+ info: "Supabase Edge Functions: deno fmt --check, deno lint, deno audit für supabase/functions.",
45
+ settings: [
46
+ { key: "enabled", label: "Aktiv", type: "boolean", default: true },
47
+ ],
48
+ },
49
+ {
50
+ id: "aiReview",
51
+ label: "AI Review (Codex)",
52
+ info: "Code-Review per Codex: Git-Diff (staged/unstaged oder Push-Range), strukturierte Bewertung (Rating, Warnings, Errors, Verdict). PASS nur bei Rating ≥ 95 und ohne Warnings/Errors. Review wird in .shimwrapper/reviews/ gespeichert.",
53
+ settings: [
54
+ { key: "enabled", label: "Aktiv", type: "boolean", default: true },
55
+ { key: "timeoutSec", label: "Timeout (Sekunden)", type: "number", default: 180 },
56
+ { key: "diffLimitBytes", label: "Max. Diff-Größe (Bytes)", type: "number", default: 51200 },
57
+ { key: "minRating", label: "Mindest-Rating für PASS", type: "number", default: 95 },
58
+ { key: "reviewDir", label: "Ausgabeordner Reviews", type: "string", default: ".shimwrapper/reviews" },
59
+ ],
60
+ },
61
+ {
62
+ id: "sast",
63
+ label: "SAST",
64
+ info: "Geplant / in Konfiguration vorhanden. Noch nicht im run-checks.sh Template umgesetzt.",
65
+ settings: [{ key: "enabled", label: "Aktiv", type: "boolean", default: true }],
66
+ },
67
+ {
68
+ id: "architecture",
69
+ label: "Architecture",
70
+ info: "Geplant / in Konfiguration vorhanden. Noch nicht im run-checks.sh Template umgesetzt.",
71
+ settings: [{ key: "enabled", label: "Aktiv", type: "boolean", default: true }],
72
+ },
73
+ {
74
+ id: "complexity",
75
+ label: "Complexity",
76
+ info: "Geplant / in Konfiguration vorhanden. Noch nicht im run-checks.sh Template umgesetzt.",
77
+ settings: [{ key: "enabled", label: "Aktiv", type: "boolean", default: true }],
78
+ },
79
+ {
80
+ id: "mutation",
81
+ label: "Mutation",
82
+ info: "Geplant / in Konfiguration vorhanden. Noch nicht im run-checks.sh Template umgesetzt.",
83
+ settings: [{ key: "enabled", label: "Aktiv", type: "boolean", default: true }],
84
+ },
85
+ {
86
+ id: "e2e",
87
+ label: "E2E",
88
+ info: "Geplant / in Konfiguration vorhanden. Noch nicht im run-checks.sh Template umgesetzt.",
89
+ settings: [{ key: "enabled", label: "Aktiv", type: "boolean", default: true }],
90
+ },
91
+ {
92
+ id: "healthPing",
93
+ label: "Post-Deploy: Health Ping",
94
+ info: "Nach Supabase-Deploy werden Health-Endpoints der Edge Functions aufgerufen (ping-edge-health.sh). Nutzt Project Ref (SUPABASE_PROJECT_REF oder supabase/project-ref) und optional benutzerdefinierte Pfade.",
95
+ settings: [
96
+ { key: "defaultFunction", label: "Standard-Funktion", type: "string", default: "server" },
97
+ { key: "healthFunctions", label: "Zusätzliche Funktionen (kommasepariert)", type: "string", default: "" },
98
+ { key: "healthPaths", label: "Health-Pfade (kommasepariert, {fn})", type: "string", default: "" },
99
+ { key: "projectRef", label: "Supabase Project Ref", type: "string", default: "" },
100
+ ],
101
+ },
102
+ {
103
+ id: "edgeLogs",
104
+ label: "Post-Deploy: Edge Logs",
105
+ info: "Holt nach Deploy die letzten Logs der deployten Edge Function(s) (fetch-edge-logs.sh).",
106
+ settings: [
107
+ { key: "defaultFunction", label: "Standard-Funktion", type: "string", default: "server" },
108
+ { key: "logFunctions", label: "Funktionen für Logs (kommasepariert)", type: "string", default: "" },
109
+ { key: "logLimit", label: "Anzahl Log-Zeilen", type: "number", default: 30 },
110
+ ],
111
+ },
112
+ ];
113
+
114
+ export function getCheckDef(id: CheckId): CheckDef | undefined {
115
+ return CHECK_DEFINITIONS.find((c) => c.id === id);
116
+ }
@@ -37,12 +37,28 @@ export interface CheckToggles {
37
37
  frontend: boolean;
38
38
  backend: boolean;
39
39
  aiReview: boolean;
40
+ sast: boolean;
41
+ architecture: boolean;
42
+ complexity: boolean;
43
+ mutation: boolean;
44
+ e2e: boolean;
45
+ }
46
+
47
+ /** Per-check options (persisted in presets JSON and written to rc as env where supported) */
48
+ export interface CheckSettings {
49
+ frontend?: { skipSnyk?: boolean; auditLevel?: string };
50
+ backend?: Record<string, unknown>;
51
+ aiReview?: { timeoutSec?: number; diffLimitBytes?: number; minRating?: number; reviewDir?: string };
52
+ healthPing?: { defaultFunction?: string; healthFunctions?: string; healthPaths?: string; projectRef?: string };
53
+ edgeLogs?: { defaultFunction?: string; logFunctions?: string; logLimit?: number };
40
54
  }
41
55
 
42
56
  export interface SettingsData {
43
57
  presets: Preset[];
44
58
  activePresetId: string;
45
59
  checkToggles: CheckToggles;
60
+ checkSettings?: CheckSettings;
61
+ checkOrder?: string[];
46
62
  }
47
63
 
48
64
  const VIBE_CODE_ID = "vibe-code";
@@ -51,6 +67,11 @@ export const DEFAULT_CHECK_TOGGLES: CheckToggles = {
51
67
  frontend: true,
52
68
  backend: true,
53
69
  aiReview: true,
70
+ sast: true,
71
+ architecture: true,
72
+ complexity: true,
73
+ mutation: true,
74
+ e2e: true,
54
75
  };
55
76
 
56
77
  export const DEFAULT_VIBE_CODE_PRESET: Preset = {
@@ -91,7 +112,27 @@ export function buildRcContent(settings: SettingsData): string {
91
112
  if (!settings.checkToggles.frontend) args.push("--no-frontend");
92
113
  if (!settings.checkToggles.backend) args.push("--no-backend");
93
114
  if (!settings.checkToggles.aiReview) args.push("--no-ai-review");
115
+ if (!settings.checkToggles.sast) args.push("--no-sast");
116
+ if (!settings.checkToggles.architecture) args.push("--no-architecture");
117
+ if (!settings.checkToggles.complexity) args.push("--no-complexity");
118
+ if (!settings.checkToggles.mutation) args.push("--no-mutation");
119
+ if (!settings.checkToggles.e2e) args.push("--no-e2e");
94
120
  if (args.length) lines.push(`SHIM_CHECKS_ARGS="${args.join(" ")}"`);
95
121
 
122
+ const cs = settings.checkSettings;
123
+ if (cs?.frontend?.skipSnyk) lines.push("SKIP_SNYK=1");
124
+ if (cs?.frontend?.auditLevel) lines.push(`SHIM_AUDIT_LEVEL="${cs.frontend.auditLevel}"`);
125
+ if (cs?.aiReview?.timeoutSec != null) lines.push(`SHIM_AI_TIMEOUT_SEC=${cs.aiReview.timeoutSec}`);
126
+ if (cs?.aiReview?.diffLimitBytes != null) lines.push(`SHIM_AI_DIFF_LIMIT_BYTES=${cs.aiReview.diffLimitBytes}`);
127
+ if (cs?.aiReview?.minRating != null) lines.push(`SHIM_AI_MIN_RATING=${cs.aiReview.minRating}`);
128
+ if (cs?.aiReview?.reviewDir) lines.push(`SHIM_AI_REVIEW_DIR="${cs.aiReview.reviewDir}"`);
129
+ const defaultFn = cs?.healthPing?.defaultFunction || cs?.edgeLogs?.defaultFunction;
130
+ if (defaultFn) lines.push(`SHIM_DEFAULT_FUNCTION="${defaultFn}"`);
131
+ if (cs?.healthPing?.healthFunctions) lines.push(`SHIM_HEALTH_FUNCTIONS="${cs.healthPing.healthFunctions}"`);
132
+ if (cs?.healthPing?.healthPaths) lines.push(`SHIM_HEALTH_PATHS="${cs.healthPing.healthPaths}"`);
133
+ if (cs?.healthPing?.projectRef) lines.push(`SUPABASE_PROJECT_REF="${cs.healthPing.projectRef}"`);
134
+ if (cs?.edgeLogs?.logFunctions) lines.push(`SHIM_LOG_FUNCTIONS="${cs.edgeLogs.logFunctions}"`);
135
+ if (cs?.edgeLogs?.logLimit != null) lines.push(`SHIM_LOG_LIMIT=${cs.edgeLogs.logLimit}`);
136
+
96
137
  return lines.join("\n") + "\n";
97
138
  }
@@ -0,0 +1,5 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
@@ -3,7 +3,7 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "dev": "next dev",
6
+ "dev": "node scripts/find-port-and-dev.js",
7
7
  "build": "next build",
8
8
  "start": "next start",
9
9
  "lint": "next lint"