@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 +151 -0
- package/SECURITY.md +11 -0
- package/component-registry.js +345 -0
- package/config/default.json +33 -0
- package/design-tokens-registry.js +42 -0
- package/index.js +993 -0
- package/mcp-server.js +364 -0
- package/package.json +82 -0
- package/scripts/postinstall.js +99 -0
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,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 };
|