total-recall-brain 3.32.3 → 3.33.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 (86) hide show
  1. package/README.md +3 -1
  2. package/bin/total-recall.mjs +3 -0
  3. package/docs/ARCHITECTURE.md +21 -4
  4. package/docs/SSSS.md +230 -0
  5. package/docs/reference/CLI_INVENTORY.md +77 -0
  6. package/docs/reference/cli-reference.md +610 -0
  7. package/frontend/dist/assets/{index-DGMNZi_1.js → index-BPrkIYkZ.js} +174 -130
  8. package/frontend/dist/index.html +1 -1
  9. package/frontend/src/App.tsx +8 -4
  10. package/frontend/src/api/plugins.ts +80 -0
  11. package/frontend/src/components/brand/AnimatedBrandMark.tsx +163 -0
  12. package/frontend/src/components/brand/BrandMark.spec.tsx +4 -4
  13. package/frontend/src/components/brand/BrandMark.tsx +12 -3
  14. package/frontend/src/components/plugins/previews/CodeQualityPreview.tsx +130 -0
  15. package/frontend/src/components/plugins/previews/ComposableCliPreview.tsx +131 -0
  16. package/frontend/src/components/plugins/previews/DecisionPreview.tsx +156 -0
  17. package/frontend/src/components/plugins/previews/DesignPreview.tsx +118 -0
  18. package/frontend/src/components/plugins/previews/DomainsPreview.tsx +139 -0
  19. package/frontend/src/components/plugins/previews/PhonePreview.tsx +86 -0
  20. package/frontend/src/components/plugins/previews/SigningPreview.tsx +109 -0
  21. package/frontend/src/components/plugins/previews/TextPreview.tsx +144 -0
  22. package/frontend/src/components/plugins/previews/index.tsx +83 -0
  23. package/package.json +4 -1
  24. package/plugins/creative-search/README.md +26 -0
  25. package/plugins/creative-search/cli.mjs +421 -0
  26. package/plugins/creative-search/config.mjs +103 -0
  27. package/plugins/creative-search/generator.mjs +44 -0
  28. package/plugins/creative-search/plugin.json +59 -0
  29. package/plugins/creative-search/ui/DESIGN.md +50 -0
  30. package/plugins/creative-search/ui/csearch-main.js +311 -0
  31. package/plugins/creative-search/ui/csearch-settings.js +392 -0
  32. package/plugins/creative-search/ui/index.js +2 -0
  33. package/plugins/creative-search/ui/tokens.css +34 -0
  34. package/plugins/dsh/README.md +20 -0
  35. package/plugins/dsh/cli.mjs +232 -0
  36. package/plugins/dsh/generator.mjs +65 -0
  37. package/plugins/dsh/plugin.json +57 -0
  38. package/plugins/dsh/ui/DESIGN.md +48 -0
  39. package/plugins/dsh/ui/dsh-main.js +226 -0
  40. package/plugins/dsh/ui/dsh-status.js +265 -0
  41. package/plugins/dsh/ui/index.js +2 -0
  42. package/plugins/dsh/ui/tokens.css +32 -0
  43. package/plugins/git-sentinel/README.md +2 -0
  44. package/plugins/operator-alerts/README.md +9 -4
  45. package/plugins/operator-alerts/notify-core.mjs +26 -3
  46. package/plugins/system-monitor/README.md +2 -0
  47. package/scaffold/.agent/skills/start/SKILL.md +75 -0
  48. package/scaffold/.agent/skills/start/evals/evals.json +25 -0
  49. package/scaffold/.agent/skills/start/evals/resolve-startup-entry.test.mjs +17 -0
  50. package/scaffold/.agent/skills/start/references/runtime-contract.md +9 -0
  51. package/scaffold/.agent/skills/start/references/startup-check-registration.mjs +14 -0
  52. package/scaffold/.agent/skills/start/scripts/resolve-startup-entry.mjs +39 -0
  53. package/scaffold/.agent/skills/start/scripts/start-session.mjs +28 -0
  54. package/scaffold/.agent/skills/start/subagents/runtime-review.md +3 -0
  55. package/scaffold/.agent/skills/total-recall/SKILL.md +31 -664
  56. package/scaffold/.agent/skills/total-recall/references/operations.md +671 -0
  57. package/src/cli/brief.mjs +5 -1
  58. package/src/cli/command.mjs +44 -2
  59. package/src/cli/context.mjs +35 -0
  60. package/src/cli/doctor.mjs +24 -1
  61. package/src/cli/help.mjs +2 -1
  62. package/src/cli/mesh.mjs +15 -1
  63. package/src/cli/plugin/install.mjs +6 -1
  64. package/src/cli/plugin/remove.mjs +5 -1
  65. package/src/cli/recall.mjs +22 -4
  66. package/src/cli/repo-expert-generate.mjs +35 -2
  67. package/src/cli/skill.mjs +6 -0
  68. package/src/cli/startup.mjs +41 -0
  69. package/src/cli/status.mjs +2 -1
  70. package/src/core/command-surface.mjs +13 -1
  71. package/src/core/context-compiler.mjs +42 -311
  72. package/src/core/context-policy.mjs +59 -0
  73. package/src/core/env-import.mjs +4 -7
  74. package/src/core/fast-recall.mjs +130 -70
  75. package/src/core/memory-layers.mjs +2 -0
  76. package/src/core/mesh.mjs +34 -7
  77. package/src/core/skills-registry.mjs +113 -21
  78. package/src/core/startup-health.mjs +174 -0
  79. package/src/core/surface.mjs +40 -227
  80. package/src/server/route-manifest.json +12 -0
  81. package/src/server/routes/_shared.mjs +5 -2
  82. package/src/server/routes/context.mjs +17 -3
  83. package/src/server/routes/instructions.mjs +5 -4
  84. package/src/server/routes/plugins.mjs +137 -1
  85. package/frontend/dist/assets/index-BUEZPLim.js +0 -5742
  86. package/frontend/dist/assets/index-FOJG4jQh.js +0 -5742
@@ -17,7 +17,7 @@
17
17
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
18
18
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
19
19
  <title>Total Recall — Portable Memory</title>
20
- <script type="module" crossorigin src="/assets/index-FOJG4jQh.js"></script>
20
+ <script type="module" crossorigin src="/assets/index-BPrkIYkZ.js"></script>
21
21
  <link rel="stylesheet" crossorigin href="/assets/index-C7_Xpk27.css">
22
22
  </head>
23
23
  <body>
@@ -95,11 +95,15 @@ function Sidebar({ onLogout, health, activeBrainId, onBrainChange }: SidebarProp
95
95
 
96
96
  return (
97
97
  <aside className="sidebar">
98
- <div className="sidebar-brand">
99
- <div className="sidebar-brand-lockup" title="Total Recall">
100
- <BrandMark variant="lockup" height={48} alt="Total Recall" />
98
+ <div className="sidebar-brand" style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '16px 14px' }}>
99
+ <BrandMark animated height={44} alt="Total Recall" />
100
+ <div style={{ display: 'flex', flexDirection: 'column' }}>
101
+ <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
102
+ <span style={{ fontSize: 15, fontWeight: 700, letterSpacing: '-0.02em', color: '#f8fafc' }}>Total Recall</span>
103
+ <span style={{ fontSize: 10, fontWeight: 600, padding: '2px 5px', borderRadius: 4, background: 'rgba(59,130,246,0.15)', color: '#60a5fa', border: '1px solid rgba(59,130,246,0.3)' }}>v3.32</span>
104
+ </div>
105
+ <span style={{ fontSize: 11, color: 'var(--text-tertiary)', letterSpacing: '0.01em' }}>Portable memory · any IDE</span>
101
106
  </div>
102
- <span style={{ paddingLeft: 2 }}>Portable memory · any IDE</span>
103
107
  </div>
104
108
  <nav className="sidebar-nav">
105
109
  <div className="nav-section-label">Memory</div>
@@ -197,3 +197,83 @@ export async function fetchPluginReadme(id: string): Promise<string> {
197
197
  return ""
198
198
  }
199
199
  }
200
+
201
+ // ── Reviews ─────────────────────────────────────────────────────────────────
202
+
203
+ export interface PluginReview {
204
+ type: string
205
+ plugin_id: string
206
+ rating: number
207
+ reviewer_node: string
208
+ verified_conformance: boolean
209
+ timestamp: string
210
+ content: string
211
+ _file: string
212
+ portability?: string
213
+ tags?: string[]
214
+ }
215
+
216
+ export interface ReviewsResponse {
217
+ success: boolean
218
+ pluginId: string
219
+ count: number
220
+ averageRating: number | null
221
+ reviews: PluginReview[]
222
+ }
223
+
224
+ export async function fetchPluginReviews(id: string, minRating?: number): Promise<ReviewsResponse> {
225
+ try {
226
+ let url = `${API_BASE}/api/plugins/${encodeURIComponent(id)}/reviews`
227
+ if (minRating !== undefined && minRating > 0) {
228
+ url += `?min_rating=${minRating}`
229
+ }
230
+ const res = await apiFetch(url)
231
+ if (!res.ok) return { success: false, pluginId: id, count: 0, averageRating: null, reviews: [] }
232
+ const data = await readJson(res)
233
+ return {
234
+ success: true,
235
+ pluginId: data.pluginId,
236
+ count: data.count,
237
+ averageRating: data.averageRating,
238
+ reviews: Array.isArray(data.reviews) ? data.reviews : [],
239
+ }
240
+ } catch {
241
+ return { success: false, pluginId: id, count: 0, averageRating: null, reviews: [] }
242
+ }
243
+ }
244
+
245
+ export function submitPluginReview(id: string, options: {
246
+ rating: number
247
+ text: string
248
+ reviewer_node?: string
249
+ verified_conformance?: boolean
250
+ }): Promise<Result> {
251
+ return post(`${API_BASE}/api/plugins/${encodeURIComponent(id)}/reviews`, options)
252
+ }
253
+
254
+ // ── Conformance ──────────────────────────────────────────────────────────────
255
+
256
+ export interface ConformanceResponse {
257
+ success: boolean
258
+ pluginId: string
259
+ ssss_version: string
260
+ ssss_conformant: boolean
261
+ white_label_verified: boolean
262
+ tested: boolean
263
+ test_count: number
264
+ test_pass_count: number
265
+ test_pass_rate: number | null
266
+ capabilities: string[]
267
+ portability: string
268
+ }
269
+
270
+ export async function fetchPluginConformance(id: string): Promise<ConformanceResponse | null> {
271
+ try {
272
+ const res = await apiFetch(`${API_BASE}/api/plugins/${encodeURIComponent(id)}/conformance`)
273
+ if (!res.ok) return null
274
+ const data = await readJson(res)
275
+ return data.success ? data : null
276
+ } catch {
277
+ return null
278
+ }
279
+ }
@@ -0,0 +1,163 @@
1
+ import type { CSSProperties } from 'react'
2
+
3
+ interface AnimatedBrandMarkProps {
4
+ size?: number
5
+ className?: string
6
+ alt?: string
7
+ }
8
+
9
+ export default function AnimatedBrandMark({
10
+ size = 42,
11
+ className = '',
12
+ alt = 'Total Recall Animated Mark',
13
+ }: AnimatedBrandMarkProps) {
14
+ const containerStyle: CSSProperties = {
15
+ width: size,
16
+ height: size,
17
+ display: 'inline-flex',
18
+ alignItems: 'center',
19
+ justifyContent: 'center',
20
+ position: 'relative',
21
+ perspective: 800,
22
+ }
23
+
24
+ return (
25
+ <div className={`animated-brand-mark ${className}`} style={containerStyle} title={alt}>
26
+ <svg
27
+ viewBox="0 0 100 100"
28
+ width={size}
29
+ height={size}
30
+ fill="none"
31
+ xmlns="http://www.w3.org/2000/svg"
32
+ style={{ overflow: 'visible' }}
33
+ >
34
+ <defs>
35
+ {/* Radial glow filter for high-end glass look */}
36
+ <filter id="tr-glow" x="-20%" y="-20%" width="140%" height="140%">
37
+ <feGaussianBlur stdDeviation="3" result="blur" />
38
+ <feComposite in="SourceGraphic" in2="blur" operator="over" />
39
+ </filter>
40
+
41
+ <linearGradient id="tr-top-facet" x1="50" y1="20" x2="80" y2="40" gradientUnits="userSpaceOnUse">
42
+ <stop offset="0%" stopColor="#60a5fa" stopOpacity="0.95" />
43
+ <stop offset="100%" stopColor="#3b82f6" stopOpacity="0.8" />
44
+ </linearGradient>
45
+
46
+ <linearGradient id="tr-left-facet" x1="20" y1="38" x2="50" y2="80" gradientUnits="userSpaceOnUse">
47
+ <stop offset="0%" stopColor="#2563eb" stopOpacity="0.9" />
48
+ <stop offset="100%" stopColor="#1e3a8a" stopOpacity="0.85" />
49
+ </linearGradient>
50
+
51
+ <linearGradient id="tr-right-facet" x1="80" y1="38" x2="50" y2="80" gradientUnits="userSpaceOnUse">
52
+ <stop offset="0%" stopColor="#3b82f6" stopOpacity="0.85" />
53
+ <stop offset="100%" stopColor="#1d4ed8" stopOpacity="0.95" />
54
+ </linearGradient>
55
+
56
+ <linearGradient id="tr-orbit-ring" x1="10" y1="50" x2="90" y2="50" gradientUnits="userSpaceOnUse">
57
+ <stop offset="0%" stopColor="#38bdf8" stopOpacity="0.1" />
58
+ <stop offset="50%" stopColor="#60a5fa" stopOpacity="0.7" />
59
+ <stop offset="100%" stopColor="#818cf8" stopOpacity="0.1" />
60
+ </linearGradient>
61
+ </defs>
62
+
63
+ <style>{`
64
+ @keyframes tr-float {
65
+ 0%, 100% {
66
+ transform: translateY(0px) rotate(0deg);
67
+ }
68
+ 50% {
69
+ transform: translateY(-3px) rotate(1deg);
70
+ }
71
+ }
72
+
73
+ @keyframes tr-orbit {
74
+ 0% {
75
+ transform: rotateX(68deg) rotateZ(0deg);
76
+ }
77
+ 100% {
78
+ transform: rotateX(68deg) rotateZ(360deg);
79
+ }
80
+ }
81
+
82
+ @keyframes tr-pulse {
83
+ 0%, 100% {
84
+ opacity: 0.6;
85
+ transform: scale(0.96);
86
+ }
87
+ 50% {
88
+ opacity: 1;
89
+ transform: scale(1.04);
90
+ }
91
+ }
92
+
93
+ .tr-cube-group {
94
+ transform-origin: 50px 50px;
95
+ animation: tr-float 4.5s ease-in-out infinite;
96
+ }
97
+
98
+ .tr-orbit-plane {
99
+ transform-origin: 50px 50px;
100
+ animation: tr-orbit 7s linear infinite;
101
+ }
102
+
103
+ .tr-inner-core {
104
+ transform-origin: 50px 50px;
105
+ animation: tr-pulse 3s ease-in-out infinite;
106
+ }
107
+ `}</style>
108
+
109
+ {/* Orbit ring rotating in 3D perspective */}
110
+ <g className="tr-orbit-plane">
111
+ <ellipse
112
+ cx="50"
113
+ cy="50"
114
+ rx="42"
115
+ ry="42"
116
+ stroke="url(#tr-orbit-ring)"
117
+ strokeWidth="1.5"
118
+ strokeDasharray="16 8"
119
+ />
120
+ {/* Orbiting glowing memory node */}
121
+ <circle cx="92" cy="50" r="3.5" fill="#38bdf8" filter="url(#tr-glow)" />
122
+ <circle cx="92" cy="50" r="1.8" fill="#ffffff" />
123
+ </g>
124
+
125
+ {/* Floating isometric memory cube */}
126
+ <g className="tr-cube-group">
127
+ {/* Top Facet */}
128
+ <polygon
129
+ points="50,22 80,38 50,54 20,38"
130
+ fill="url(#tr-top-facet)"
131
+ stroke="rgba(255,255,255,0.25)"
132
+ strokeWidth="0.8"
133
+ />
134
+
135
+ {/* Left Facet */}
136
+ <polygon
137
+ points="20,38 50,54 50,86 20,70"
138
+ fill="url(#tr-left-facet)"
139
+ stroke="rgba(255,255,255,0.15)"
140
+ strokeWidth="0.8"
141
+ />
142
+
143
+ {/* Right Facet */}
144
+ <polygon
145
+ points="50,54 80,38 80,70 50,86"
146
+ fill="url(#tr-right-facet)"
147
+ stroke="rgba(255,255,255,0.15)"
148
+ strokeWidth="0.8"
149
+ />
150
+
151
+ {/* Glowing central core node */}
152
+ <g className="tr-inner-core">
153
+ <polygon
154
+ points="50,42 62,49 50,56 38,49"
155
+ fill="rgba(147, 197, 253, 0.4)"
156
+ />
157
+ <circle cx="50" cy="49" r="2.5" fill="#ffffff" filter="url(#tr-glow)" />
158
+ </g>
159
+ </g>
160
+ </svg>
161
+ </div>
162
+ )
163
+ }
@@ -47,10 +47,10 @@ describe('BrandMark', () => {
47
47
  expect(screen.getByAltText('Custom Alt')).toBeInTheDocument();
48
48
  });
49
49
 
50
- it('accepts custom className', () => {
51
- render(<BrandMark className="test-class" />);
52
- const img = screen.getByAltText('Total Recall');
53
- expect(img.className).toContain('test-class');
50
+ it('renders animated variant when animated=true', () => {
51
+ render(<BrandMark animated height={44} />);
52
+ const animatedEl = document.querySelector('.animated-brand-mark');
53
+ expect(animatedEl).toBeInTheDocument();
54
54
  });
55
55
  });
56
56
 
@@ -1,6 +1,6 @@
1
- /** Official Total Recall brand marks. */
1
+ import AnimatedBrandMark from './AnimatedBrandMark'
2
2
 
3
- export type BrandVariant = 'lockup' | 'icon' | 'badge' | 'mark'
3
+ export type BrandVariant = 'lockup' | 'icon' | 'badge' | 'mark' | 'animated'
4
4
 
5
5
  interface BrandMarkProps {
6
6
  variant?: BrandVariant
@@ -10,22 +10,31 @@ interface BrandMarkProps {
10
10
  alt?: string
11
11
  /** Light plate behind lockup for dark surfaces (PNG has white bg). */
12
12
  plate?: boolean
13
+ /** Render dynamic looping 3D vector animation mark. */
14
+ animated?: boolean
13
15
  }
14
16
 
15
- const SRC: Record<BrandVariant, string> = {
17
+ const SRC: Record<Exclude<BrandVariant, 'animated'>, string> = {
16
18
  lockup: '/brand/total-recall-lockup.png',
17
19
  icon: '/brand/total-recall-icon.svg',
18
20
  badge: '/brand/total-recall-badge.jpg',
19
21
  mark: '/brand/total-recall-mark.svg',
20
22
  }
21
23
 
24
+ export { AnimatedBrandMark }
25
+
22
26
  export default function BrandMark({
23
27
  variant = 'lockup',
24
28
  height = 40,
25
29
  className = '',
26
30
  alt = 'Total Recall',
27
31
  plate = false,
32
+ animated = false,
28
33
  }: BrandMarkProps) {
34
+ if (animated || variant === 'animated') {
35
+ return <AnimatedBrandMark size={height} className={className} alt={alt} />
36
+ }
37
+
29
38
  const isSquare = variant === 'icon' || variant === 'badge' || variant === 'mark'
30
39
  const img = (
31
40
  <img
@@ -0,0 +1,130 @@
1
+ import { type CSSProperties } from "react"
2
+
3
+ const styles: Record<string, CSSProperties> = {
4
+ container: {
5
+ background: "linear-gradient(135deg, #0a1628, #1a1a2e)",
6
+ borderRadius: "12px",
7
+ padding: "24px",
8
+ fontFamily: "var(--font-mono, 'SF Mono', monospace)",
9
+ color: "#e0e0e0",
10
+ maxWidth: "580px",
11
+ },
12
+ header: {
13
+ display: "flex",
14
+ justifyContent: "space-between",
15
+ marginBottom: "16px",
16
+ },
17
+ label: {
18
+ color: "#888",
19
+ fontSize: "11px",
20
+ textTransform: "uppercase" as const,
21
+ letterSpacing: "1px",
22
+ },
23
+ badge: {
24
+ background: "#1a2a1a",
25
+ color: "#4caf50",
26
+ padding: "4px 10px",
27
+ borderRadius: "20px",
28
+ fontSize: "11px",
29
+ },
30
+ summary: {
31
+ display: "flex",
32
+ gap: "16px",
33
+ marginBottom: "16px",
34
+ },
35
+ statCard: {
36
+ background: "#0d1117",
37
+ border: "1px solid #2a2a4a",
38
+ borderRadius: "8px",
39
+ padding: "12px 16px",
40
+ flex: 1,
41
+ },
42
+ statValue: {
43
+ fontSize: "20px",
44
+ fontWeight: 700,
45
+ },
46
+ statLabel: {
47
+ fontSize: "10px",
48
+ color: "#888",
49
+ textTransform: "uppercase" as const,
50
+ letterSpacing: "1px",
51
+ marginTop: "4px",
52
+ },
53
+ gateTable: {
54
+ width: "100%",
55
+ borderCollapse: "collapse" as const,
56
+ },
57
+ th: {
58
+ color: "#888",
59
+ fontSize: "11px",
60
+ textTransform: "uppercase" as const,
61
+ padding: "8px 12px",
62
+ textAlign: "left" as const,
63
+ borderBottom: "1px solid #2a2a4a",
64
+ },
65
+ td: {
66
+ padding: "10px 12px",
67
+ fontSize: "12px",
68
+ borderBottom: "1px solid #1a1a2e",
69
+ },
70
+ pass: {
71
+ color: "#4caf50",
72
+ },
73
+ fail: {
74
+ color: "#ff5252",
75
+ },
76
+ }
77
+
78
+ const gates = [
79
+ { name: "TypeScript", status: "pass", detail: "0 errors" },
80
+ { name: "ESLint", status: "pass", detail: "0 warnings" },
81
+ { name: "Vitest", status: "pass", detail: "42/42 tests" },
82
+ { name: "Bundle Size", status: "pass", detail: "12.4 kB" },
83
+ { name: "SSSS Schema", status: "fail", detail: "1 warning" },
84
+ { name: "Mesh Sync", status: "pass", detail: "2 peers synced" },
85
+ ]
86
+
87
+ export function CodeQualityPreview() {
88
+ return (
89
+ <div style={styles.container}>
90
+ <div style={styles.header}>
91
+ <span style={styles.label}>Gate Report</span>
92
+ <span style={styles.badge}>5/6 passing</span>
93
+ </div>
94
+ <div style={styles.summary}>
95
+ <div style={{ ...styles.statCard, borderColor: "#1a3a1a" }}>
96
+ <div style={{ ...styles.statValue, color: "#4caf50" }}>5</div>
97
+ <div style={styles.statLabel}>Passing</div>
98
+ </div>
99
+ <div style={styles.statCard}>
100
+ <div style={{ ...styles.statValue, color: "#ff5252" }}>1</div>
101
+ <div style={styles.statLabel}>Failing</div>
102
+ </div>
103
+ <div style={styles.statCard}>
104
+ <div style={{ ...styles.statValue, color: "#64b5f6" }}>6</div>
105
+ <div style={styles.statLabel}>Total Gates</div>
106
+ </div>
107
+ </div>
108
+ <table style={styles.gateTable}>
109
+ <thead>
110
+ <tr>
111
+ <th style={styles.th}>Gate</th>
112
+ <th style={styles.th}>Status</th>
113
+ <th style={styles.th}>Detail</th>
114
+ </tr>
115
+ </thead>
116
+ <tbody>
117
+ {gates.map((g, i) => (
118
+ <tr key={i}>
119
+ <td style={styles.td}>{g.name}</td>
120
+ <td style={{ ...styles.td, ...(g.status === "pass" ? styles.pass : styles.fail) }}>
121
+ {g.status === "pass" ? "✓" : "✗"}
122
+ </td>
123
+ <td style={styles.td}>{g.detail}</td>
124
+ </tr>
125
+ ))}
126
+ </tbody>
127
+ </table>
128
+ </div>
129
+ )
130
+ }
@@ -0,0 +1,131 @@
1
+ import { useState, type CSSProperties } from "react"
2
+
3
+ const styles: Record<string, CSSProperties> = {
4
+ container: {
5
+ background: "linear-gradient(135deg, #0d1117, #1a1a2e)",
6
+ borderRadius: "12px",
7
+ padding: "24px",
8
+ fontFamily: "var(--font-mono, 'SF Mono', 'Fira Code', monospace)",
9
+ color: "#e0e0e0",
10
+ maxWidth: "520px",
11
+ },
12
+ header: {
13
+ display: "flex",
14
+ justifyContent: "space-between",
15
+ marginBottom: "16px",
16
+ },
17
+ label: {
18
+ color: "#888",
19
+ fontSize: "11px",
20
+ textTransform: "uppercase" as const,
21
+ letterSpacing: "1px",
22
+ },
23
+ badge: {
24
+ background: "#1a2a3a",
25
+ color: "#64b5f6",
26
+ padding: "4px 10px",
27
+ borderRadius: "20px",
28
+ fontSize: "11px",
29
+ },
30
+ terminal: {
31
+ background: "#0a0a1a",
32
+ border: "1px solid #2a2a4a",
33
+ borderRadius: "8px",
34
+ padding: "12px",
35
+ marginBottom: "12px",
36
+ minHeight: "100px",
37
+ overflow: "auto" as const,
38
+ },
39
+ line: {
40
+ fontSize: "12px",
41
+ lineHeight: 1.6,
42
+ whiteSpace: "pre-wrap" as const,
43
+ },
44
+ prompt: {
45
+ color: "#4caf50",
46
+ },
47
+ cmd: {
48
+ color: "#e0e0e0",
49
+ },
50
+ output: {
51
+ color: "#888",
52
+ },
53
+ inputRow: {
54
+ display: "flex",
55
+ alignItems: "center",
56
+ gap: "6px",
57
+ background: "#0a0a1a",
58
+ border: "1px solid #2a2a4a",
59
+ borderRadius: "8px",
60
+ padding: "8px 12px",
61
+ },
62
+ input: {
63
+ flex: 1,
64
+ background: "transparent",
65
+ border: "none",
66
+ color: "#4caf50",
67
+ fontFamily: "var(--font-mono, 'SF Mono', monospace)",
68
+ fontSize: "12px",
69
+ outline: "none",
70
+ },
71
+ commands: {
72
+ display: "flex",
73
+ flexWrap: "wrap" as const,
74
+ gap: "6px",
75
+ marginTop: "12px",
76
+ },
77
+ cmdChip: {
78
+ background: "#1e1e3a",
79
+ border: "1px solid #2a2a4a",
80
+ borderRadius: "6px",
81
+ padding: "4px 10px",
82
+ fontSize: "11px",
83
+ color: "#aaa",
84
+ cursor: "pointer",
85
+ },
86
+ }
87
+
88
+ const initialLines = [
89
+ { kind: "prompt", text: "total-recall@mesh:~$ " },
90
+ { kind: "cmd", text: "total-recall --help" },
91
+ { kind: "output", text: "\nTotal Recall Composable CLI v2.1.0" },
92
+ { kind: "output", text: "\nCommands:" },
93
+ { kind: "output", text: "\n recall <query> Search memory" },
94
+ { kind: "output", text: "\n remember <category> Write memory" },
95
+ { kind: "output", text: "\n compile Rebuild surfaces" },
96
+ { kind: "output", text: "\n mesh nodes List mesh peers" },
97
+ { kind: "output", text: "\n plugin list Show installed plugins" },
98
+ { kind: "output", text: "\n vercel-rotate Rotate API token" },
99
+ { kind: "prompt", text: "\ntotal-recall@mesh:~$ " },
100
+ ]
101
+
102
+ export function ComposableCliPreview() {
103
+ const [cmdHistory, _setCmdHistory] = useState(initialLines)
104
+
105
+ return (
106
+ <div style={styles.container}>
107
+ <div style={styles.header}>
108
+ <span style={styles.label}>Composable CLI</span>
109
+ <span style={styles.badge}>Active</span>
110
+ </div>
111
+ <div style={styles.terminal}>
112
+ {cmdHistory.map((l, i) => (
113
+ <div key={i} style={styles.line}>
114
+ {l.kind === "prompt" && <span style={styles.prompt}>{l.text}</span>}
115
+ {l.kind === "cmd" && <span style={styles.cmd}>{l.text}</span>}
116
+ {l.kind === "output" && <span style={styles.output}>{l.text}</span>}
117
+ </div>
118
+ ))}
119
+ </div>
120
+ <div style={styles.inputRow}>
121
+ <span style={{ ...styles.prompt, fontSize: "12px" }}>$</span>
122
+ <input style={styles.input} placeholder="Type a command..." readOnly />
123
+ </div>
124
+ <div style={styles.commands}>
125
+ {["help", "recall", "remember", "compile", "mesh", "plugins"].map((c) => (
126
+ <span key={c} style={styles.cmdChip}>{c}</span>
127
+ ))}
128
+ </div>
129
+ </div>
130
+ )
131
+ }