holo-codex 0.1.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 (149) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/CONTRIBUTING.md +54 -0
  3. package/LICENSE +21 -0
  4. package/README.md +215 -0
  5. package/README.zh-CN.md +215 -0
  6. package/SECURITY.md +39 -0
  7. package/assets/brand/README.md +35 -0
  8. package/assets/brand/holo-codex-icon.svg +28 -0
  9. package/assets/brand/holo-codex-lockup.svg +49 -0
  10. package/assets/brand/holo-codex-mark.svg +33 -0
  11. package/assets/brand/holo-codex-plugin-card.png +0 -0
  12. package/assets/brand/holo-codex-plugin-card.svg +81 -0
  13. package/assets/brand/holo-codex-readme-hero.png +0 -0
  14. package/assets/brand/holo-codex-readme-hero.svg +140 -0
  15. package/assets/brand/holo-codex-social-preview.png +0 -0
  16. package/assets/brand/holo-codex-social-preview.svg +130 -0
  17. package/assets/brand/holo-codex-wordmark-options.svg +52 -0
  18. package/docs/checklists/agent-loop-first-delivery-audit.md +129 -0
  19. package/docs/examples/generic-loop-repo-hygiene.md +168 -0
  20. package/docs/install.md +190 -0
  21. package/docs/local-release-readiness.md +206 -0
  22. package/docs/release-checklist.md +144 -0
  23. package/docs/self-bootstrap.md +150 -0
  24. package/docs/trust-and-safety.md +45 -0
  25. package/package.json +83 -0
  26. package/plugins/autonomous-pr-loop/.codex-plugin/plugin.json +17 -0
  27. package/plugins/autonomous-pr-loop/.mcp.json +13 -0
  28. package/plugins/autonomous-pr-loop/bin/agent-loop.mjs +31 -0
  29. package/plugins/autonomous-pr-loop/core/artifacts.ts +164 -0
  30. package/plugins/autonomous-pr-loop/core/autonomy-policy.ts +206 -0
  31. package/plugins/autonomous-pr-loop/core/ci.ts +131 -0
  32. package/plugins/autonomous-pr-loop/core/cli-i18n.ts +123 -0
  33. package/plugins/autonomous-pr-loop/core/cli.ts +1413 -0
  34. package/plugins/autonomous-pr-loop/core/command-runner.ts +446 -0
  35. package/plugins/autonomous-pr-loop/core/command.ts +47 -0
  36. package/plugins/autonomous-pr-loop/core/config-editor.ts +140 -0
  37. package/plugins/autonomous-pr-loop/core/config.ts +293 -0
  38. package/plugins/autonomous-pr-loop/core/controller-host.ts +19 -0
  39. package/plugins/autonomous-pr-loop/core/dashboard-server.ts +536 -0
  40. package/plugins/autonomous-pr-loop/core/delivery-work-item.ts +217 -0
  41. package/plugins/autonomous-pr-loop/core/doctor.ts +335 -0
  42. package/plugins/autonomous-pr-loop/core/errors.ts +82 -0
  43. package/plugins/autonomous-pr-loop/core/gate-recovery.ts +176 -0
  44. package/plugins/autonomous-pr-loop/core/gates.ts +26 -0
  45. package/plugins/autonomous-pr-loop/core/generic-lifecycle.ts +399 -0
  46. package/plugins/autonomous-pr-loop/core/git.ts +213 -0
  47. package/plugins/autonomous-pr-loop/core/github.ts +269 -0
  48. package/plugins/autonomous-pr-loop/core/gitnexus.ts +90 -0
  49. package/plugins/autonomous-pr-loop/core/happy.ts +42 -0
  50. package/plugins/autonomous-pr-loop/core/hook-capture.ts +115 -0
  51. package/plugins/autonomous-pr-loop/core/hook-events.ts +22 -0
  52. package/plugins/autonomous-pr-loop/core/hook-installation.ts +85 -0
  53. package/plugins/autonomous-pr-loop/core/hook-observer.ts +84 -0
  54. package/plugins/autonomous-pr-loop/core/hook-policy.ts +423 -0
  55. package/plugins/autonomous-pr-loop/core/hook-router.ts +452 -0
  56. package/plugins/autonomous-pr-loop/core/index.ts +32 -0
  57. package/plugins/autonomous-pr-loop/core/local-install.ts +778 -0
  58. package/plugins/autonomous-pr-loop/core/locale.ts +60 -0
  59. package/plugins/autonomous-pr-loop/core/loop-shapes.ts +190 -0
  60. package/plugins/autonomous-pr-loop/core/mcp-controller.ts +1479 -0
  61. package/plugins/autonomous-pr-loop/core/notification-feed.ts +263 -0
  62. package/plugins/autonomous-pr-loop/core/plan-parser.ts +206 -0
  63. package/plugins/autonomous-pr-loop/core/plugin-paths.ts +32 -0
  64. package/plugins/autonomous-pr-loop/core/policy.ts +65 -0
  65. package/plugins/autonomous-pr-loop/core/pr-lifecycle.ts +464 -0
  66. package/plugins/autonomous-pr-loop/core/pr-selector.ts +284 -0
  67. package/plugins/autonomous-pr-loop/core/profiles.ts +439 -0
  68. package/plugins/autonomous-pr-loop/core/redaction.ts +17 -0
  69. package/plugins/autonomous-pr-loop/core/repo-root.ts +22 -0
  70. package/plugins/autonomous-pr-loop/core/review-comments.ts +77 -0
  71. package/plugins/autonomous-pr-loop/core/scope-guard.ts +179 -0
  72. package/plugins/autonomous-pr-loop/core/state-machine.ts +828 -0
  73. package/plugins/autonomous-pr-loop/core/state-types.ts +130 -0
  74. package/plugins/autonomous-pr-loop/core/storage.ts +2527 -0
  75. package/plugins/autonomous-pr-loop/core/types.ts +567 -0
  76. package/plugins/autonomous-pr-loop/core/worker-events.ts +412 -0
  77. package/plugins/autonomous-pr-loop/core/worker-policy.ts +72 -0
  78. package/plugins/autonomous-pr-loop/core/worker-prompts.ts +182 -0
  79. package/plugins/autonomous-pr-loop/core/worker.ts +809 -0
  80. package/plugins/autonomous-pr-loop/core/workflow-board.ts +1515 -0
  81. package/plugins/autonomous-pr-loop/hooks/dist/permission-request.js +2462 -0
  82. package/plugins/autonomous-pr-loop/hooks/dist/post-compact.js +2462 -0
  83. package/plugins/autonomous-pr-loop/hooks/dist/post-tool-use.js +2462 -0
  84. package/plugins/autonomous-pr-loop/hooks/dist/pre-compact.js +2462 -0
  85. package/plugins/autonomous-pr-loop/hooks/dist/pre-tool-use.js +3460 -0
  86. package/plugins/autonomous-pr-loop/hooks/dist/session-start.js +2462 -0
  87. package/plugins/autonomous-pr-loop/hooks/dist/stop.js +2462 -0
  88. package/plugins/autonomous-pr-loop/hooks/dist/user-prompt-submit.js +2462 -0
  89. package/plugins/autonomous-pr-loop/hooks/hooks.json +106 -0
  90. package/plugins/autonomous-pr-loop/hooks/observe-runner.ts +25 -0
  91. package/plugins/autonomous-pr-loop/hooks/permission-request.ts +4 -0
  92. package/plugins/autonomous-pr-loop/hooks/post-compact.ts +4 -0
  93. package/plugins/autonomous-pr-loop/hooks/post-tool-use.ts +4 -0
  94. package/plugins/autonomous-pr-loop/hooks/pre-compact.ts +4 -0
  95. package/plugins/autonomous-pr-loop/hooks/pre-tool-use.ts +44 -0
  96. package/plugins/autonomous-pr-loop/hooks/session-start.ts +4 -0
  97. package/plugins/autonomous-pr-loop/hooks/stop.ts +4 -0
  98. package/plugins/autonomous-pr-loop/hooks/user-prompt-submit.ts +4 -0
  99. package/plugins/autonomous-pr-loop/mcp-server/src/index.ts +87 -0
  100. package/plugins/autonomous-pr-loop/mcp-server/src/tools.ts +205 -0
  101. package/plugins/autonomous-pr-loop/package.json +9 -0
  102. package/plugins/autonomous-pr-loop/schemas/config.schema.json +74 -0
  103. package/plugins/autonomous-pr-loop/schemas/marketplace.schema.json +46 -0
  104. package/plugins/autonomous-pr-loop/schemas/plugin.schema.json +32 -0
  105. package/plugins/autonomous-pr-loop/schemas/state.schema.json +19 -0
  106. package/plugins/autonomous-pr-loop/schemas/worker-event.schema.json +19 -0
  107. package/plugins/autonomous-pr-loop/schemas/worker-result.schema.json +58 -0
  108. package/plugins/autonomous-pr-loop/scripts/agent-loop.ts +44 -0
  109. package/plugins/autonomous-pr-loop/skills/autonomous-pr-loop/SKILL.md +26 -0
  110. package/plugins/autonomous-pr-loop/skills/autonomous-pr-loop/agents/openai.yaml +6 -0
  111. package/plugins/autonomous-pr-loop/ui/index.html +26 -0
  112. package/plugins/autonomous-pr-loop/ui/public/favicon.svg +7 -0
  113. package/plugins/autonomous-pr-loop/ui/src/api.ts +639 -0
  114. package/plugins/autonomous-pr-loop/ui/src/app.tsx +238 -0
  115. package/plugins/autonomous-pr-loop/ui/src/components/ActivityBadge.tsx +31 -0
  116. package/plugins/autonomous-pr-loop/ui/src/components/BrandMark.tsx +36 -0
  117. package/plugins/autonomous-pr-loop/ui/src/components/Collapsible.tsx +6 -0
  118. package/plugins/autonomous-pr-loop/ui/src/components/CommandPreview.tsx +15 -0
  119. package/plugins/autonomous-pr-loop/ui/src/components/ConfigEditor.tsx +389 -0
  120. package/plugins/autonomous-pr-loop/ui/src/components/EmptyState.tsx +10 -0
  121. package/plugins/autonomous-pr-loop/ui/src/components/ErrorState.tsx +12 -0
  122. package/plugins/autonomous-pr-loop/ui/src/components/List.tsx +7 -0
  123. package/plugins/autonomous-pr-loop/ui/src/components/MetricRow.tsx +6 -0
  124. package/plugins/autonomous-pr-loop/ui/src/components/ResponsiveTable.tsx +65 -0
  125. package/plugins/autonomous-pr-loop/ui/src/components/RiskBadge.tsx +10 -0
  126. package/plugins/autonomous-pr-loop/ui/src/components/StatusBadge.tsx +29 -0
  127. package/plugins/autonomous-pr-loop/ui/src/components/TopMetric.tsx +10 -0
  128. package/plugins/autonomous-pr-loop/ui/src/fixtures.ts +1152 -0
  129. package/plugins/autonomous-pr-loop/ui/src/i18n.ts +1105 -0
  130. package/plugins/autonomous-pr-loop/ui/src/main.tsx +14 -0
  131. package/plugins/autonomous-pr-loop/ui/src/pages/CommandCenter.tsx +470 -0
  132. package/plugins/autonomous-pr-loop/ui/src/pages/CommandCenterParts.tsx +276 -0
  133. package/plugins/autonomous-pr-loop/ui/src/pages/agent-timeline/AgentTimelineView.tsx +73 -0
  134. package/plugins/autonomous-pr-loop/ui/src/pages/artifact-viewer/ArtifactViewer.tsx +44 -0
  135. package/plugins/autonomous-pr-loop/ui/src/pages/dry-run-preview/DryRunPreview.tsx +66 -0
  136. package/plugins/autonomous-pr-loop/ui/src/pages/event-ledger/EventLedger.tsx +17 -0
  137. package/plugins/autonomous-pr-loop/ui/src/pages/gate-center/GateCenter.tsx +34 -0
  138. package/plugins/autonomous-pr-loop/ui/src/pages/mission-control/MissionControl.tsx +104 -0
  139. package/plugins/autonomous-pr-loop/ui/src/pages/mission-control/WorkflowBoard.tsx +577 -0
  140. package/plugins/autonomous-pr-loop/ui/src/pages/notifications/NotificationsView.tsx +30 -0
  141. package/plugins/autonomous-pr-loop/ui/src/pages/plan-navigator/PlanNavigator.tsx +19 -0
  142. package/plugins/autonomous-pr-loop/ui/src/pages/policy-config/PolicyConfig.tsx +22 -0
  143. package/plugins/autonomous-pr-loop/ui/src/pages/pr-inbox/PrInbox.tsx +26 -0
  144. package/plugins/autonomous-pr-loop/ui/src/pages/recovery-center/RecoveryCenter.tsx +125 -0
  145. package/plugins/autonomous-pr-loop/ui/src/pages/scope-guard/ScopeGuard.tsx +16 -0
  146. package/plugins/autonomous-pr-loop/ui/src/pages/worker-runs/WorkerRuns.tsx +39 -0
  147. package/plugins/autonomous-pr-loop/ui/src/styles.css +2673 -0
  148. package/plugins/autonomous-pr-loop/ui/src/theme.ts +57 -0
  149. package/tsconfig.json +18 -0
@@ -0,0 +1,238 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import type { JSX } from "react";
3
+ import { AlertTriangle, Loader2 } from "lucide-react";
4
+ import { createDashboardApi, storeDashboardToken, storedDashboardToken, type DashboardApi, type DashboardMetaData, type MissionControlData } from "./api.js";
5
+ import { BrandMark } from "./components/BrandMark.js";
6
+ import { createFixtureDashboardApi, dashboardFixture } from "./fixtures.js";
7
+ import {
8
+ readStoredLocaleSetting,
9
+ resolveDashboardLocale,
10
+ t,
11
+ writeStoredLocaleSetting
12
+ } from "./i18n.js";
13
+ import { CommandCenter } from "./pages/CommandCenter.js";
14
+ import {
15
+ applyDashboardTheme,
16
+ DEFAULT_THEME_SETTING,
17
+ readStoredThemeSetting,
18
+ resolveEffectiveTheme,
19
+ writeStoredThemeSetting,
20
+ type ThemeSetting
21
+ } from "./theme.js";
22
+ import { normalizeLocaleSetting, type LocaleSetting } from "../../core/locale.js";
23
+ import "./styles.css";
24
+
25
+ export interface AppProps {
26
+ api?: DashboardApi;
27
+ initialData?: MissionControlData;
28
+ }
29
+
30
+ export function App({ api, initialData }: AppProps): JSX.Element {
31
+ const fixtureData = useMemo(() => dashboardFixture(fixtureNameFromLocation()), []);
32
+ const requiresToken = !api && !fixtureData && !initialData;
33
+ const [dashboardToken, setDashboardToken] = useState(() => requiresToken ? storedDashboardToken() : "");
34
+ const client = useMemo(
35
+ () => api ?? (fixtureData ? createFixtureDashboardApi(fixtureData) : createDashboardApi(dashboardToken)),
36
+ [api, dashboardToken, fixtureData]
37
+ );
38
+ const [data, setData] = useState<MissionControlData | undefined>(initialData ?? fixtureData);
39
+ const [meta, setMeta] = useState<DashboardMetaData>();
40
+ const [error, setError] = useState<string>();
41
+ const [loading, setLoading] = useState(!initialData && !fixtureData);
42
+ const [lastRefresh, setLastRefresh] = useState(Date.now());
43
+ const [actionMessage, setActionMessage] = useState<string>();
44
+ const [localeSetting, setLocaleSetting] = useState<LocaleSetting>(() => readStoredLocaleSetting() ?? "system");
45
+ const [themeSetting, setThemeSetting] = useState<ThemeSetting>(() => readStoredThemeSetting() ?? DEFAULT_THEME_SETTING);
46
+ const [systemPrefersDark, setSystemPrefersDark] = useState(() => {
47
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
48
+ return true;
49
+ }
50
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
51
+ });
52
+ const [repoLocale, setRepoLocale] = useState<LocaleSetting>();
53
+ const pollDelayRef = useRef(3000);
54
+ const effectiveLocale = resolveDashboardLocale(localeSetting, repoLocale);
55
+ const effectiveTheme = resolveEffectiveTheme(themeSetting, systemPrefersDark);
56
+ const authenticated = !requiresToken || dashboardToken.length > 0;
57
+
58
+ const refresh = useCallback(async (): Promise<void> => {
59
+ setLoading(true);
60
+ const result = await client.missionControl();
61
+ if (result.ok && result.data) {
62
+ setData(result.data);
63
+ setError(undefined);
64
+ setLastRefresh(Date.now());
65
+ setActionMessage(t(effectiveLocale, "appUpdated"));
66
+ pollDelayRef.current = 3000;
67
+ } else {
68
+ setError(result.error?.message ?? t(effectiveLocale, "appUnknownError"));
69
+ pollDelayRef.current = Math.min(pollDelayRef.current * 2, 30000);
70
+ }
71
+ setLoading(false);
72
+ }, [client, effectiveLocale]);
73
+
74
+ useEffect(() => {
75
+ applyDashboardTheme(effectiveTheme);
76
+ }, [effectiveTheme]);
77
+
78
+ useEffect(() => {
79
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
80
+ return;
81
+ }
82
+ const query = window.matchMedia("(prefers-color-scheme: dark)");
83
+ const update = (): void => setSystemPrefersDark(query.matches);
84
+ update();
85
+ query.addEventListener("change", update);
86
+ return () => query.removeEventListener("change", update);
87
+ }, []);
88
+
89
+ useEffect(() => {
90
+ let active = true;
91
+ void client.dashboardMeta().then((result) => {
92
+ if (active && result.ok && result.data) {
93
+ setMeta(result.data);
94
+ }
95
+ });
96
+ void client.policyConfig().then((result) => {
97
+ if (!active || !result.ok || !result.data) {
98
+ return;
99
+ }
100
+ setRepoLocale(normalizeLocaleSetting(result.data.config.locale));
101
+ });
102
+ return () => {
103
+ active = false;
104
+ };
105
+ }, [client]);
106
+
107
+ useEffect(() => {
108
+ let active = true;
109
+ let timer: number | undefined;
110
+ const poll = async (): Promise<void> => {
111
+ await refresh();
112
+ if (active) {
113
+ timer = window.setTimeout(() => {
114
+ void poll();
115
+ }, pollDelayRef.current);
116
+ }
117
+ };
118
+ if (!authenticated) {
119
+ setLoading(false);
120
+ } else if (!initialData && !fixtureData) {
121
+ void poll();
122
+ } else {
123
+ timer = window.setTimeout(() => {
124
+ void poll();
125
+ }, pollDelayRef.current);
126
+ }
127
+ return () => {
128
+ active = false;
129
+ if (timer !== undefined) {
130
+ window.clearTimeout(timer);
131
+ }
132
+ };
133
+ }, [authenticated, fixtureData, initialData, refresh]);
134
+
135
+ if (!authenticated) {
136
+ return (
137
+ <LoginScreen
138
+ locale={effectiveLocale}
139
+ onLogin={(token) => {
140
+ storeDashboardToken(token);
141
+ setDashboardToken(token);
142
+ }}
143
+ />
144
+ );
145
+ }
146
+
147
+ if (loading && !data) {
148
+ return (
149
+ <div className="state-screen">
150
+ <Loader2 className="spin" size={30} />
151
+ <h1>{t(effectiveLocale, "appLoadingTitle")}</h1>
152
+ <p>{t(effectiveLocale, "appLoadingMessage")}</p>
153
+ </div>
154
+ );
155
+ }
156
+
157
+ if (error && !data) {
158
+ return (
159
+ <div className="state-screen state-screen--error">
160
+ <AlertTriangle size={30} />
161
+ <h1>{t(effectiveLocale, "appUnavailableTitle")}</h1>
162
+ <p>{error}</p>
163
+ </div>
164
+ );
165
+ }
166
+
167
+ if (!data) {
168
+ return (
169
+ <div className="state-screen">
170
+ <h1>{t(effectiveLocale, "appNoDataTitle")}</h1>
171
+ <p>{t(effectiveLocale, "appNoDataMessage")}</p>
172
+ </div>
173
+ );
174
+ }
175
+
176
+ const commandCenterProps = {
177
+ data,
178
+ meta,
179
+ api: client,
180
+ stale: Date.now() - lastRefresh > 7000,
181
+ onRefresh: () => void refresh(),
182
+ locale: effectiveLocale,
183
+ localeSetting,
184
+ onLocaleSettingChange: (next: LocaleSetting) => {
185
+ writeStoredLocaleSetting(next);
186
+ setLocaleSetting(next);
187
+ },
188
+ themeSetting,
189
+ effectiveTheme,
190
+ onThemeSettingChange: (next: ThemeSetting) => {
191
+ writeStoredThemeSetting(next);
192
+ setThemeSetting(next);
193
+ },
194
+ ...(error ?? actionMessage ? { actionMessage: error ?? actionMessage } : {})
195
+ };
196
+
197
+ return (
198
+ <CommandCenter
199
+ {...commandCenterProps}
200
+ />
201
+ );
202
+ }
203
+
204
+ function LoginScreen({ locale, onLogin }: { locale: ReturnType<typeof resolveDashboardLocale>; onLogin: (token: string) => void }): JSX.Element {
205
+ const [value, setValue] = useState("");
206
+ return (
207
+ <div className="state-screen login-screen">
208
+ <div className="login-brand">
209
+ <BrandMark className="brand-logo brand-logo--login" />
210
+ <strong>HOLO-Codex</strong>
211
+ </div>
212
+ <h1>{t(locale, "loginTitle")}</h1>
213
+ <p>{t(locale, "loginMessage")}</p>
214
+ <form
215
+ className="login-form"
216
+ onSubmit={(event) => {
217
+ event.preventDefault();
218
+ const token = value.trim();
219
+ if (token) onLogin(token);
220
+ }}
221
+ >
222
+ <input
223
+ aria-label={t(locale, "loginTokenLabel")}
224
+ value={value}
225
+ onChange={(event) => setValue(event.target.value)}
226
+ placeholder={t(locale, "loginTokenPlaceholder")}
227
+ type="password"
228
+ />
229
+ <button className="success-button" type="submit" disabled={value.trim().length === 0}>{t(locale, "loginSubmit")}</button>
230
+ </form>
231
+ </div>
232
+ );
233
+ }
234
+
235
+ function fixtureNameFromLocation(): string | undefined {
236
+ if (typeof window === "undefined") return undefined;
237
+ return new URL(window.location.href).searchParams.get("fixture") ?? undefined;
238
+ }
@@ -0,0 +1,31 @@
1
+ import type { JSX } from "react";
2
+ import type { ActivityState } from "../api.js";
3
+ import { displayValueLabel, t } from "../i18n.js";
4
+ import type { EffectiveLocale } from "../pages/CommandCenterParts.js";
5
+ import { StatusBadge } from "./StatusBadge.js";
6
+
7
+ export function ActivityBadge({ activity, reason, locale }: { activity: ActivityState | undefined; reason: string | undefined; locale: EffectiveLocale }): JSX.Element {
8
+ if (reason === "stale_worker_failure") {
9
+ return <StatusBadge value={t(locale, "activityStaleWorker")} tone="red" />;
10
+ }
11
+ if (reason === "overridden_by_reality") {
12
+ return <StatusBadge value={t(locale, "activityOverridden")} tone="blue" />;
13
+ }
14
+ if (reason === "marked_handled") {
15
+ return <StatusBadge value={t(locale, "activityMarkedHandled")} tone="muted" />;
16
+ }
17
+ if (activity === "active") {
18
+ return <StatusBadge value={t(locale, "activityActive")} tone="green" />;
19
+ }
20
+ if (activity === "historical") {
21
+ return <StatusBadge value={t(locale, "activityHistorical")} tone="yellow" />;
22
+ }
23
+ return <StatusBadge value={displayValueLabel(locale, "unknown")} tone="muted" />;
24
+ }
25
+
26
+ export function activityReasonLabel(locale: EffectiveLocale, reason: string | undefined): string {
27
+ if (!reason) return t(locale, "unknown");
28
+ const key = `activityReason.${reason}`;
29
+ const label = t(locale, key);
30
+ return label === key ? displayValueLabel(locale, reason) : label;
31
+ }
@@ -0,0 +1,36 @@
1
+ import type { JSX } from "react";
2
+
3
+ interface BrandMarkProps {
4
+ className?: string;
5
+ }
6
+
7
+ /** Renders the HOLO-Codex terminal prompt inside the human-on-loop control ring. */
8
+ export function BrandMark({ className = "brand-logo" }: BrandMarkProps): JSX.Element {
9
+ return (
10
+ <svg className={className} viewBox="0 0 128 128" role="img" aria-label="HOLO-Codex">
11
+ <path
12
+ d="M111.3 52.6A49 49 0 1 1 84 19.2"
13
+ fill="none"
14
+ stroke="currentColor"
15
+ strokeLinecap="round"
16
+ strokeWidth="8"
17
+ />
18
+ <circle className="brand-logo__gate" cx="98.6" cy="29.4" r="6.5" />
19
+ <path
20
+ d="M41 48L62 64L41 80"
21
+ fill="none"
22
+ stroke="currentColor"
23
+ strokeLinecap="round"
24
+ strokeLinejoin="round"
25
+ strokeWidth="8"
26
+ />
27
+ <path
28
+ d="M70 81H91"
29
+ fill="none"
30
+ stroke="currentColor"
31
+ strokeLinecap="round"
32
+ strokeWidth="8"
33
+ />
34
+ </svg>
35
+ );
36
+ }
@@ -0,0 +1,6 @@
1
+ import type { JSX } from "react";
2
+ import { StatusBadge } from "./StatusBadge.js";
3
+
4
+ export function Collapsible({ title, chip, children, defaultOpen = false }: { title: string; chip: string; children: JSX.Element; defaultOpen?: boolean }): JSX.Element {
5
+ return <details className="disclosure-panel" open={defaultOpen}><summary><span>{title}</span><StatusBadge value={chip} tone="blue" /></summary>{children}</details>;
6
+ }
@@ -0,0 +1,15 @@
1
+ import { TerminalSquare } from "lucide-react";
2
+ import type { JSX } from "react";
3
+
4
+ export function CommandPreview({ commands, emptyMessage }: { commands: string[]; emptyMessage: string }): JSX.Element {
5
+ return (
6
+ <div className="command-list">
7
+ {commands.length === 0 ? <p className="muted-copy">{emptyMessage}</p> : commands.map((command) => (
8
+ <div className="command-line" key={command}>
9
+ <TerminalSquare size={15} />
10
+ <code>{command}</code>
11
+ </div>
12
+ ))}
13
+ </div>
14
+ );
15
+ }