@p31/andromeda-cli 1.0.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.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # P31 Oasis CLI
2
+
3
+ Interactive terrain user interface (TUI) for the P31 ecosystem.
4
+
5
+ Package: `@p31/andromeda-cli`, binary: `andromeda`.
6
+
7
+ ## Install globally (npm)
8
+
9
+ ```bash
10
+ npm install -g @p31/andromeda-cli
11
+ andromeda --agent
12
+ ```
13
+
14
+ This makes `andromeda` available in any environment (local shell, CI/CD, GitHub Actions) without cloning the monorepo. The MCP servers (`mcp-server.js`, `component-registry.js`) can then be invoked directly:
15
+
16
+ ```bash
17
+ node /usr/lib/node_modules/@p31/andromeda-cli/mcp-server.js
18
+ ```
19
+
20
+ ## Quick Start (from source)
21
+
22
+ ```bash
23
+ cd /path/to/P31-local-workspace/cli
24
+ npm install
25
+ node index.js
26
+ ```
27
+
28
+ **Requires a TTY.** Without one, only `--version` and `--help` respond.
29
+
30
+ ## Usage
31
+
32
+ Inside the TUI, type text to send commands to the SANDBOX shell (your default `$SHELL`). Use slash-commands for UI actions:
33
+
34
+ | Command | Action |
35
+ |---------|--------|
36
+ | `/exit` | Save session and exit |
37
+ | `/clear` | Clear the LOG pane |
38
+ | `/sandbox clear` | Clear the SANDBOX pane |
39
+ | `/export log` | Write LOG to `p31-oasis-log-<timestamp>.txt` |
40
+ | `/save` | Save session to `~/.p31/cli-session.json` |
41
+ | `/notify test` | Show test notifications |
42
+ | `/help` | Display help box |
43
+ | `/theme <name>` | Switch theme (cyberpunk, nord, dracula, catppuccin, warm) |
44
+ | `/mode <name>` | Set mode (build/plan/review/debug) |
45
+
46
+ ## Keyboard Shortcuts
47
+
48
+ | Key | Action |
49
+ |-----|--------|
50
+ | Tab / S-Tab | Cycle focus between panes |
51
+ | Ctrl+P | Open command palette |
52
+ | Ctrl+T | Cycle themes |
53
+ | Ctrl+L | Clear LOG |
54
+ | Ctrl+S | Save session |
55
+ | Esc / Ctrl+C | Save and exit |
56
+
57
+ ## Flags
58
+
59
+ | Flag | Output |
60
+ |------|--------|
61
+ | `--version`, `-v` | `@p31/andromeda-cli v1.0.0` |
62
+ | `--help`, `-h` | Usage information |
63
+ | `--agent`, `-a` | JSON output of session state + design tokens |
64
+
65
+ Flags work in both TTY and non-TTY environments. `--agent` always outputs JSON regardless of TTY.
66
+
67
+ ## Agent Mode (`--agent`)
68
+
69
+ For programmatic use by AI agents, the CLI supports a `--agent` flag that outputs machine-readable JSON:
70
+
71
+ ```bash
72
+ andromeda --agent
73
+ ```
74
+
75
+ Output includes: `version`, `mode`, `theme`, `todos`, `sandboxCwd`, `design` (tokens from `DESIGN.md`), `capabilities` (slash-commands, shortcuts, themes, modes), and `status`.
76
+
77
+ Example:
78
+
79
+ ```json
80
+ {
81
+ "version": "1.0.0",
82
+ "mode": "BUILD",
83
+ "theme": "warm",
84
+ "design": { "colors": { "quantum-cyan": "#00F0FF", "void": "#0A0A0F" } },
85
+ "capabilities": { "themes": ["cyberpunk", "nord", "dracula", "catppuccin", "warm"] },
86
+ "status": "ok"
87
+ }
88
+ ```
89
+
90
+ ## MCP Server
91
+
92
+ The CLI exposes its capabilities via an [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) server for agent tool invocation:
93
+
94
+ ```bash
95
+ node cli/mcp-server.js
96
+ ```
97
+
98
+ The server reads JSON-RPC requests from stdin and writes responses to stdout. Supported methods:
99
+
100
+ | Method | Description |
101
+ |--------|-------------|
102
+ | `initialize` | Handshake with protocol version |
103
+ | `tools/list` | List available tools (10 tools) |
104
+ | `tools/call` | Execute a tool by name |
105
+
106
+ Available tools:
107
+
108
+ | Tool | Description |
109
+ |------|-------------|
110
+ | `oasis_status` | Get session state, design tokens, capabilities |
111
+ | `oasis_save` | Persist session to disk |
112
+ | `oasis_theme` | Switch CLI theme |
113
+ | `oasis_mode` | Set CLI mode |
114
+ | `oasis_clear` | Clear log buffer |
115
+ | `oasis_export_log` | Export log to file |
116
+ | `oasis_sandbox_clear` | Clear sandbox output |
117
+ | `oasis_notify` | Queue a notification |
118
+ | `oasis_add_todo` | Add a todo item |
119
+ | `oasis_toggle_todo` | Toggle a todo's done state |
120
+ | `oasis_execute` | Execute a shell command in sandbox directory |
121
+
122
+ ## Component Registry MCP Server
123
+
124
+ A separate MCP server exposes the PHOS design system as a queryable component registry:
125
+
126
+ ```bash
127
+ node cli/component-registry.js
128
+ ```
129
+
130
+ 5 tools for design-system-aware agent workflows:
131
+
132
+ | Tool | Description |
133
+ |------|-------------|
134
+ | `design_list_components` | List all PHOS components with descriptions and token deps |
135
+ | `design_get_component` | Get full details: props, tokens, CSS, usage example |
136
+ | `design_get_tokens` | Get all design tokens and invariants |
137
+ | `design_search` | Search components by keyword or token |
138
+ | `design_spoon_guide` | Get spoon-level UI behavior guide (0–5) |
139
+
140
+ ## Architecture
141
+
142
+ The CLI is built with [blessed](https://github.com/chjj/blessed) for the TUI and [node-pty](https://github.com/microsoft/node-pty) for the embedded shell. It provides four panes:
143
+
144
+ - **LOG** — session log output
145
+ - **SANDBOX** — interactive shell
146
+ - **META** — metadata display
147
+ - **TODOS** — task list
148
+
149
+ ## License
150
+
151
+ MIT
package/SECURITY.md ADDED
@@ -0,0 +1,11 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a Vulnerability
4
+
5
+ Report vulnerabilities to security@p31labs.org.
6
+
7
+ ## Supported Versions
8
+
9
+ | Version | Supported |
10
+ |---------|-----------|
11
+ | 0.x | Yes |
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ═════════════════════════════════════════════════════════════════════════════
4
+ // P31 Component Registry MCP Server
5
+ // Exposes PHOS design system components via Model Context Protocol
6
+ // Agents query this to get correct-by-construction component code
7
+ // ═════════════════════════════════════════════════════════════════════════════
8
+
9
+ const { designTokens } = require('./design-tokens-registry');
10
+
11
+ // ─── Component Registry ──────────────────────────────────────────────────────
12
+
13
+ const COMPONENTS = [
14
+ {
15
+ name: 'GlassPanel',
16
+ description: 'Glassmorphic elevated surface with backdrop blur. Use for cards, modals, drawers.',
17
+ tokens: ['glass-surface', 'glass-border', 'rounded.lg'],
18
+ props: {
19
+ children: { type: 'ReactNode', required: true },
20
+ className: { type: 'string', required: false },
21
+ hover: { type: 'boolean', default: false, description: 'Enable hover lift effect' },
22
+ },
23
+ css: `background: ${designTokens.colors['glass-surface']};
24
+ backdrop-filter: blur(12px);
25
+ -webkit-backdrop-filter: blur(12px);
26
+ border: 1px solid ${designTokens.colors['glass-border']};
27
+ border-radius: ${designTokens.rounded.lg};
28
+ box-shadow: 0 8px 32px rgba(0,0,0,0.15);
29
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);`,
30
+ example: `<div className="phos-glass rounded-3xl p-6">
31
+ {children}
32
+ </div>`,
33
+ },
34
+ {
35
+ name: 'GlassCard',
36
+ description: 'Compact glassmorphic card with padding. Use for list items, data tiles.',
37
+ tokens: ['glass-surface', 'glass-border', 'rounded.lg', 'spacing.lg'],
38
+ props: {
39
+ children: { type: 'ReactNode', required: true },
40
+ className: { type: 'string', required: false },
41
+ },
42
+ css: `background: ${designTokens.colors['glass-surface']};
43
+ backdrop-filter: blur(12px);
44
+ border: 1px solid ${designTokens.colors['glass-border']};
45
+ border-radius: ${designTokens.rounded.lg};
46
+ padding: ${designTokens.spacing.lg};
47
+ box-shadow: 0 8px 32px rgba(0,0,0,0.15);`,
48
+ example: `<div className="phos-glass-card">
49
+ <p className="text-[var(--phos-text)]">{content}</p>
50
+ </div>`,
51
+ },
52
+ {
53
+ name: 'PillButton',
54
+ description: 'Rounded pill-shaped button. Use for mode switches, toggles, actions.',
55
+ tokens: ['quantum-cyan', 'rounded.md', 'spacing.sm', 'spacing.lg'],
56
+ props: {
57
+ children: { type: 'ReactNode', required: true },
58
+ active: { type: 'boolean', default: false },
59
+ onClick: { type: '() => void', required: true },
60
+ variant: { type: "'primary' | 'secondary' | 'ghost'", default: 'primary' },
61
+ },
62
+ css: {
63
+ primary: `background: ${designTokens.colors['quantum-cyan']};
64
+ color: ${designTokens.colors.void};
65
+ font-weight: 700;
66
+ padding: ${designTokens.spacing.sm} ${designTokens.spacing.lg};
67
+ border-radius: ${designTokens.rounded.md};
68
+ transition: all 0.2s ease;
69
+ box-shadow: 0 4px 16px rgba(0,240,255,0.2);`,
70
+ secondary: `background: rgba(167,139,250,0.1);
71
+ color: ${designTokens.colors['quantum-violet']};
72
+ font-weight: 700;
73
+ border: 1px solid rgba(167,139,250,0.3);
74
+ border-radius: ${designTokens.rounded.md};`,
75
+ ghost: `background: rgba(255,255,255,0.05);
76
+ color: ${designTokens.colors['text-secondary']};
77
+ font-weight: 700;
78
+ border: 1px solid rgba(255,255,255,0.1);
79
+ border-radius: ${designTokens.rounded.md};`,
80
+ },
81
+ example: `<button className="px-4 py-2 rounded-xl bg-[var(--phos-primary)] text-[var(--phos-bg)] font-bold
82
+ hover:translate-y-[-2px] transition-all duration-200 shadow-[0_4px_16px_rgba(0,240,255,0.2)]">
83
+ {label}
84
+ </button>`,
85
+ },
86
+ {
87
+ name: 'SpoonDots',
88
+ description: 'Energy level indicator dots (0–5). Use in headers for spoon state display.',
89
+ tokens: ['quantum-cyan', 'void'],
90
+ props: {
91
+ level: { type: '0 | 1 | 2 | 3 | 4 | 5', required: true },
92
+ onSet: { type: '(level: number) => void', required: true },
93
+ },
94
+ example: '<div className="flex gap-2">\n {[0,1,2,3,4,5].map(sp => (\n <button key={sp} onClick={() => onSet(sp)}\n className="w-2.5 h-2.5 rounded-full transition-all duration-500"\n style={{\n backgroundColor: level >= sp ? \'var(--phos-primary)\' : \'rgba(255,255,255,0.1)\',\n boxShadow: level >= sp && level > 0 ? \'0 0 6px var(--phos-primary)\' : \'none\',\n }}\n aria-label={"Energy level " + sp} />\n ))}\n</div>',
95
+ },
96
+ {
97
+ name: 'CrisisOverlay',
98
+ description: 'Full-screen crisis mode overlay. ONLY rendered at spoons === 0. Hard invariant: no other UI chrome.',
99
+ tokens: ['void', 'quantum-cyan'],
100
+ props: {
101
+ onExit: { type: '() => void', required: true, description: 'Resets spoons to 3' },
102
+ },
103
+ invariant: 'At spoons === 0, CrisisMode is the ONLY rendered component. No sidebar, no prompt bar, no navigation.',
104
+ example: `{spoons === 0 && <CrisisMode onExit={() => spoonsStore.set(3)} />}`,
105
+ },
106
+ {
107
+ name: 'Sidebar',
108
+ description: 'Vertical icon sidebar for surface navigation. Hidden on mobile.',
109
+ tokens: ['glass-surface', 'glass-border'],
110
+ props: {
111
+ surfaces: { type: 'SurfaceNavItem[]', required: true },
112
+ active: { type: 'string', required: true },
113
+ onSelect: { type: '(id: string) => void', required: true },
114
+ spoons: { type: 'number', required: true },
115
+ },
116
+ example: `<div className="w-16 md:w-20 z-30 flex-shrink-0 border-r border-white/5 bg-black/20 backdrop-blur-md">
117
+ <PHOSSidebar surfaces={SURFACE_NAV} active={cs} onSelect={ss} spoons={s} />
118
+ </div>`,
119
+ },
120
+ {
121
+ name: 'PromptBar',
122
+ description: 'Chat input bar with send button. Use for message composition.',
123
+ tokens: ['glass-surface', 'quantum-cyan', 'rounded.md'],
124
+ props: {
125
+ onSend: { type: '(text: string) => void', required: true },
126
+ disabled: { type: 'boolean', default: false },
127
+ },
128
+ example: `<PHOSPromptBar onSend={handleSend} disabled={s === 0} />`,
129
+ },
130
+ {
131
+ name: 'SurfaceContent',
132
+ description: 'Dynamic surface renderer. Routes to the correct surface component by ID.',
133
+ tokens: [],
134
+ props: {
135
+ currentSurface: { type: 'string', required: true },
136
+ setSurface: { type: '(id: string) => void', required: true },
137
+ spoons: { type: 'number', required: true },
138
+ theme: { type: 'object', required: true },
139
+ isGuest: { type: 'boolean', required: true },
140
+ },
141
+ example: `<SurfaceContent currentSurface={cs} setSurface={ss} spoons={s} theme={theme} isGuest={false} />`,
142
+ },
143
+ ];
144
+
145
+ // ─── Design Token Reference ──────────────────────────────────────────────────
146
+
147
+ const TOKEN_REFERENCE = {
148
+ colors: designTokens.colors,
149
+ typography: {
150
+ sans: designTokens.typography.sans,
151
+ mono: designTokens.typography.mono,
152
+ },
153
+ rounded: designTokens.rounded,
154
+ spacing: designTokens.spacing,
155
+ invariants: [
156
+ 'Never use pure white (#FFFFFF) or pure black (#000000)',
157
+ 'Primary accent is quantum-cyan (#00F0FF)',
158
+ 'All glass surfaces require backdrop-filter: blur(12px)',
159
+ 'Border radius for elevated surfaces: 24px (rounded.lg)',
160
+ 'Crisis mode (spoons=0): no UI chrome, only breathing overlay',
161
+ 'Motion disabled at spoons 0-1, slowed at 2, baseline at 3, accelerated at 4-5',
162
+ ],
163
+ };
164
+
165
+ // ─── Tool Definitions ────────────────────────────────────────────────────────
166
+
167
+ const TOOLS = [
168
+ {
169
+ name: 'design_list_components',
170
+ description: 'List all available PHOS design system components with their descriptions.',
171
+ inputSchema: { type: 'object', properties: {} },
172
+ },
173
+ {
174
+ name: 'design_get_component',
175
+ description: 'Get full component details: props, tokens, CSS, and usage example.',
176
+ inputSchema: {
177
+ type: 'object',
178
+ properties: {
179
+ name: { type: 'string', description: 'Component name (e.g. GlassPanel, CrisisOverlay)' },
180
+ },
181
+ required: ['name'],
182
+ },
183
+ },
184
+ {
185
+ name: 'design_get_tokens',
186
+ description: 'Get all design system tokens (colors, typography, spacing, rounding) and invariants.',
187
+ inputSchema: { type: 'object', properties: {} },
188
+ },
189
+ {
190
+ name: 'design_search',
191
+ description: 'Search components by keyword or token usage.',
192
+ inputSchema: {
193
+ type: 'object',
194
+ properties: {
195
+ query: { type: 'string', description: 'Search term (e.g. "glass", "button", "crisis")' },
196
+ },
197
+ required: ['query'],
198
+ },
199
+ },
200
+ {
201
+ name: 'design_spoon_guide',
202
+ description: 'Get spoon-level behavior guide — how the UI adapts at each energy level (0-5).',
203
+ inputSchema: { type: 'object', properties: {} },
204
+ },
205
+ ];
206
+
207
+ // ─── Tool Execution ──────────────────────────────────────────────────────────
208
+
209
+ function executeTool(name, args) {
210
+ switch (name) {
211
+ case 'design_list_components':
212
+ return {
213
+ components: COMPONENTS.map(c => ({
214
+ name: c.name,
215
+ description: c.description,
216
+ tokens: c.tokens,
217
+ })),
218
+ total: COMPONENTS.length,
219
+ status: 'ok',
220
+ };
221
+
222
+ case 'design_get_component': {
223
+ const comp = COMPONENTS.find(c => c.name.toLowerCase() === (args.name || '').toLowerCase());
224
+ if (!comp) {
225
+ const available = COMPONENTS.map(c => c.name).join(', ');
226
+ return { error: `Unknown component "${args.name}". Available: ${available}`, status: 'error' };
227
+ }
228
+ return { component: comp, status: 'ok' };
229
+ }
230
+
231
+ case 'design_get_tokens':
232
+ return { tokens: TOKEN_REFERENCE, status: 'ok' };
233
+
234
+ case 'design_search': {
235
+ const q = (args.query || '').toLowerCase();
236
+ const results = COMPONENTS.filter(c =>
237
+ c.name.toLowerCase().includes(q) ||
238
+ c.description.toLowerCase().includes(q) ||
239
+ c.tokens.some(t => t.includes(q))
240
+ );
241
+ return {
242
+ query: args.query,
243
+ results: results.map(c => ({ name: c.name, description: c.description, tokens: c.tokens })),
244
+ total: results.length,
245
+ status: 'ok',
246
+ };
247
+ }
248
+
249
+ case 'design_spoon_guide':
250
+ return {
251
+ spoonGuide: {
252
+ 0: 'CRISIS: No UI chrome. Only breathing overlay + exit button. No interactive elements.',
253
+ 1: 'LOW: Minimal UI. Single-action buttons only. Large touch targets. No multi-step flows.',
254
+ 2: 'RECOVERING: Simple cards. One action per view. Generous spacing. No time pressure.',
255
+ 3: 'BASELINE: Standard glass panels. Balanced density. Normal interaction patterns.',
256
+ 4: 'ENERGIZED: Dense layouts welcome. Multi-step workflows. Detailed information display.',
257
+ 5: 'HIGH ENERGY: Full complexity. Parallel workflows. Dense data, many actions.',
258
+ },
259
+ cssAttribute: 'data-spoons="{0-5}" on <html>',
260
+ motionScaling: '0-1: disabled, 2: slowed (1.5x duration), 3: baseline, 4-5: accelerated (0.75x duration)',
261
+ status: 'ok',
262
+ };
263
+
264
+ default:
265
+ return { error: `Unknown tool: ${name}`, status: 'error' };
266
+ }
267
+ }
268
+
269
+ // ─── JSON-RPC over stdio ─────────────────────────────────────────────────────
270
+
271
+ let buffer = '';
272
+
273
+ process.stdin.setEncoding('utf8');
274
+ process.stdin.on('data', (chunk) => {
275
+ buffer += chunk;
276
+ const lines = buffer.split('\n');
277
+ buffer = lines.pop();
278
+
279
+ for (const line of lines) {
280
+ const trimmed = line.trim();
281
+ if (!trimmed) continue;
282
+ try {
283
+ const req = JSON.parse(trimmed);
284
+ handleRequest(req);
285
+ } catch (e) { /* ignore malformed */ }
286
+ }
287
+ });
288
+
289
+ function handleRequest(req) {
290
+ const { id, method, params } = req;
291
+
292
+ switch (method) {
293
+ case 'initialize':
294
+ respond(id, {
295
+ protocolVersion: '2024-11-05',
296
+ capabilities: { tools: {} },
297
+ serverInfo: { name: 'p31-component-registry', version: '1.0.0' },
298
+ });
299
+ break;
300
+
301
+ case 'notifications/initialized':
302
+ break;
303
+
304
+ case 'tools/list':
305
+ respond(id, { tools: TOOLS });
306
+ break;
307
+
308
+ case 'tools/call': {
309
+ const toolName = params?.name;
310
+ const toolArgs = params?.arguments || {};
311
+ const tool = TOOLS.find(t => t.name === toolName);
312
+ if (!tool) {
313
+ respondError(id, -32602, `Unknown tool: ${toolName}`);
314
+ break;
315
+ }
316
+ try {
317
+ const result = executeTool(toolName, toolArgs);
318
+ respond(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });
319
+ } catch (e) {
320
+ respond(id, {
321
+ content: [{ type: 'text', text: JSON.stringify({ error: e.message, status: 'error' }) }],
322
+ isError: true,
323
+ });
324
+ }
325
+ break;
326
+ }
327
+
328
+ case 'ping':
329
+ respond(id, {});
330
+ break;
331
+
332
+ default:
333
+ if (id !== undefined) respondError(id, -32601, `Method not found: ${method}`);
334
+ }
335
+ }
336
+
337
+ function respond(id, result) {
338
+ process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id, result }) + '\n');
339
+ }
340
+
341
+ function respondError(id, code, message) {
342
+ process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } }) + '\n');
343
+ }
344
+
345
+ process.stderr.write('[p31-component-registry] Server started. Listening on stdin (JSON-RPC).\n');
@@ -0,0 +1,33 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "environment": "development",
4
+ "services": {
5
+ "frontend": {
6
+ "port": 3000,
7
+ "path": "software/spaceship-earth"
8
+ },
9
+ "backend": {
10
+ "port": 3001,
11
+ "path": "software"
12
+ },
13
+ "monitoring": {
14
+ "port": 3002,
15
+ "path": "monitoring"
16
+ }
17
+ },
18
+ "oauth": {
19
+ "providers": [
20
+ "google",
21
+ "github",
22
+ "discord"
23
+ ],
24
+ "callbackUrl": "http://localhost:3000/auth/callback"
25
+ },
26
+ "secrets": {
27
+ "encryption": {
28
+ "algorithm": "aes-256-gcm",
29
+ "keyLength": 32,
30
+ "ivLength": 16
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,42 @@
1
+ // ═════════════════════════════════════════════════════════════════════════════
2
+ // Design Tokens Registry — shared between MCP server and component registry
3
+ // Source of truth: apps/p31ca/src/styles/global.css + DESIGN.md
4
+ // ═════════════════════════════════════════════════════════════════════════════
5
+
6
+ const designTokens = {
7
+ colors: {
8
+ void: '#0A0A0F',
9
+ surface: '#12121A',
10
+ surface2: '#1C1C2A',
11
+ cloud: '#A1A1AA',
12
+ 'text-primary': '#F5F5F7',
13
+ 'text-secondary': 'rgba(245,245,247,0.6)',
14
+ 'text-tertiary': 'rgba(245,245,247,0.3)',
15
+ 'quantum-cyan': '#00F0FF',
16
+ 'quantum-violet': '#A78BFA',
17
+ 'quantum-gold': '#FBBF24',
18
+ 'quantum-green': '#34D399',
19
+ 'quantum-red': '#FB7185',
20
+ 'quantum-iris': '#818CF8',
21
+ 'glass-surface': 'rgba(255,255,255,0.04)',
22
+ 'glass-border': 'rgba(255,255,255,0.08)',
23
+ 'glass-border-hover': 'rgba(255,255,255,0.15)',
24
+ 'glass-surface-hover': 'rgba(255,255,255,0.06)',
25
+ },
26
+ typography: {
27
+ sans: 'Inter, ui-sans-serif, system-ui, -apple-system, sans-serif',
28
+ mono: 'JetBrains Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
29
+ },
30
+ rounded: {
31
+ sm: '8px',
32
+ md: '12px',
33
+ lg: '24px',
34
+ },
35
+ spacing: {
36
+ sm: '8px',
37
+ md: '16px',
38
+ lg: '24px',
39
+ },
40
+ };
41
+
42
+ module.exports = { designTokens };