@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/mcp-server.js
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
4
|
+
// P31 Oasis MCP Server — Model Context Protocol interface for CLI agents
|
|
5
|
+
// Exposes session state, design tokens, and slash-command execution via JSON-RPC
|
|
6
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const yaml = require('yaml');
|
|
12
|
+
const { execSync } = require('child_process');
|
|
13
|
+
|
|
14
|
+
const SESSION_DIR = path.join(os.homedir(), '.p31');
|
|
15
|
+
const SESSION_FILE = path.join(SESSION_DIR, 'cli-session.json');
|
|
16
|
+
const DESIGN_PATH = path.join(__dirname, '..', 'DESIGN.md');
|
|
17
|
+
|
|
18
|
+
// ─── Session I/O ─────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
function loadSession() {
|
|
21
|
+
const defaults = { theme: 'warm', todos: [], log: [], mode: 'BUILD', sandboxCwd: process.cwd() };
|
|
22
|
+
try {
|
|
23
|
+
if (fs.existsSync(SESSION_FILE)) {
|
|
24
|
+
const raw = fs.readFileSync(SESSION_FILE, 'utf-8');
|
|
25
|
+
const saved = JSON.parse(raw);
|
|
26
|
+
return { ...defaults, ...saved };
|
|
27
|
+
}
|
|
28
|
+
} catch (_) { /* corrupt → fresh */ }
|
|
29
|
+
return defaults;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function saveSession(data) {
|
|
33
|
+
try {
|
|
34
|
+
fs.mkdirSync(SESSION_DIR, { recursive: true });
|
|
35
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify({ ...data, savedAt: new Date().toISOString() }, null, 2));
|
|
36
|
+
return true;
|
|
37
|
+
} catch (_) { return false; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ─── Design Tokens ───────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
function loadDesign() {
|
|
43
|
+
try {
|
|
44
|
+
const raw = fs.readFileSync(DESIGN_PATH, 'utf8');
|
|
45
|
+
const match = raw.match(/^---\n([\s\S]*?)\n---/);
|
|
46
|
+
if (match) return yaml.parse(match[1]) || {};
|
|
47
|
+
} catch (_) { /* ignore */ }
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ─── Tool Definitions ────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
const TOOLS = [
|
|
54
|
+
{
|
|
55
|
+
name: 'oasis_status',
|
|
56
|
+
description: 'Get current CLI session state, design tokens, and capabilities. Equivalent to `andromeda --agent`.',
|
|
57
|
+
inputSchema: { type: 'object', properties: {} },
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'oasis_save',
|
|
61
|
+
description: 'Persist the current session state (theme, todos, log, mode) to ~/.p31/cli-session.json.',
|
|
62
|
+
inputSchema: { type: 'object', properties: {} },
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'oasis_theme',
|
|
66
|
+
description: 'Switch the CLI theme. Persists to session.',
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {
|
|
70
|
+
name: { type: 'string', enum: ['cyberpunk', 'nord', 'dracula', 'catppuccin', 'warm'], description: 'Theme name' },
|
|
71
|
+
},
|
|
72
|
+
required: ['name'],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'oasis_mode',
|
|
77
|
+
description: 'Set the CLI working mode. Persists to session.',
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
name: { type: 'string', enum: ['BUILD', 'PLAN', 'REVIEW', 'DEBUG'], description: 'Mode name (auto-uppercased)' },
|
|
82
|
+
},
|
|
83
|
+
required: ['name'],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'oasis_clear',
|
|
88
|
+
description: 'Clear the in-memory log buffer.',
|
|
89
|
+
inputSchema: { type: 'object', properties: {} },
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'oasis_export_log',
|
|
93
|
+
description: 'Export the session log to a timestamped text file in the current directory.',
|
|
94
|
+
inputSchema: { type: 'object', properties: {} },
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'oasis_sandbox_clear',
|
|
98
|
+
description: 'Clear the sandbox pane content (in-memory).',
|
|
99
|
+
inputSchema: { type: 'object', properties: {} },
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'oasis_notify',
|
|
103
|
+
description: 'Queue a test notification message.',
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
message: { type: 'string', description: 'Notification text' },
|
|
108
|
+
type: { type: 'string', enum: ['info', 'ok', 'warn', 'err'], description: 'Notification severity' },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'oasis_add_todo',
|
|
114
|
+
description: 'Add a todo item to the session.',
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
text: { type: 'string', description: 'Todo description' },
|
|
119
|
+
},
|
|
120
|
+
required: ['text'],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'oasis_toggle_todo',
|
|
125
|
+
description: 'Toggle a todo item\'s done state by index (0-based).',
|
|
126
|
+
inputSchema: {
|
|
127
|
+
type: 'object',
|
|
128
|
+
properties: {
|
|
129
|
+
index: { type: 'number', description: '0-based index of the todo to toggle' },
|
|
130
|
+
},
|
|
131
|
+
required: ['index'],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'oasis_execute',
|
|
136
|
+
description: 'Execute a shell command in the session sandbox directory. Returns stdout, stderr, and exit code. Use for file operations, git commands, npm scripts, etc.',
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
command: { type: 'string', description: 'Shell command to execute' },
|
|
141
|
+
timeout: { type: 'number', description: 'Timeout in milliseconds (default: 30000)' },
|
|
142
|
+
},
|
|
143
|
+
required: ['command'],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
// ─── Tool Execution ──────────────────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
function executeTool(name, args) {
|
|
151
|
+
const session = loadSession();
|
|
152
|
+
|
|
153
|
+
switch (name) {
|
|
154
|
+
case 'oasis_status': {
|
|
155
|
+
const design = loadDesign();
|
|
156
|
+
return {
|
|
157
|
+
version: require('./package.json').version,
|
|
158
|
+
mode: session.mode,
|
|
159
|
+
theme: session.theme,
|
|
160
|
+
todos: session.todos.map(t => ({ text: t.text || t, done: t.done || false })),
|
|
161
|
+
sandboxCwd: session.sandboxCwd,
|
|
162
|
+
design: {
|
|
163
|
+
colors: design.colors || {},
|
|
164
|
+
typography: design.typography || {},
|
|
165
|
+
rounded: design.rounded || {},
|
|
166
|
+
spacing: design.spacing || {},
|
|
167
|
+
components: design.components || {},
|
|
168
|
+
},
|
|
169
|
+
capabilities: {
|
|
170
|
+
slashCommands: ['/exit', '/clear', '/sandbox clear', '/export log', '/save', '/notify test', '/help', '/theme <name>', '/mode <name>'],
|
|
171
|
+
themes: ['cyberpunk', 'nord', 'dracula', 'catppuccin', 'warm'],
|
|
172
|
+
modes: ['BUILD', 'PLAN', 'REVIEW', 'DEBUG'],
|
|
173
|
+
},
|
|
174
|
+
status: 'ok',
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'oasis_save': {
|
|
179
|
+
const ok = saveSession(session);
|
|
180
|
+
return { saved: ok, path: SESSION_FILE, status: ok ? 'ok' : 'error' };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
case 'oasis_theme': {
|
|
184
|
+
const themes = ['cyberpunk', 'nord', 'dracula', 'catppuccin', 'warm'];
|
|
185
|
+
const name = (args.name || '').toLowerCase();
|
|
186
|
+
if (!themes.includes(name)) {
|
|
187
|
+
return { error: `Unknown theme "${name}". Options: ${themes.join(', ')}`, status: 'error' };
|
|
188
|
+
}
|
|
189
|
+
session.theme = name;
|
|
190
|
+
saveSession(session);
|
|
191
|
+
return { theme: name, saved: true, status: 'ok' };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
case 'oasis_mode': {
|
|
195
|
+
const name = (args.name || 'BUILD').toUpperCase();
|
|
196
|
+
session.mode = name;
|
|
197
|
+
saveSession(session);
|
|
198
|
+
return { mode: name, saved: true, status: 'ok' };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
case 'oasis_clear': {
|
|
202
|
+
session.log = [];
|
|
203
|
+
saveSession(session);
|
|
204
|
+
return { cleared: true, status: 'ok' };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case 'oasis_export_log': {
|
|
208
|
+
const ts = Date.now();
|
|
209
|
+
const filename = `p31-oasis-log-${ts}.txt`;
|
|
210
|
+
const clean = (session.log || []).join('\n');
|
|
211
|
+
try {
|
|
212
|
+
fs.writeFileSync(filename, clean);
|
|
213
|
+
return { file: filename, lines: session.log.length, status: 'ok' };
|
|
214
|
+
} catch (e) {
|
|
215
|
+
return { error: e.message, status: 'error' };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
case 'oasis_sandbox_clear': {
|
|
220
|
+
return { cleared: true, note: 'Sandbox output cleared (in-memory). Next command刷新es it.', status: 'ok' };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
case 'oasis_notify': {
|
|
224
|
+
return {
|
|
225
|
+
queued: true,
|
|
226
|
+
message: args.message || 'Test notification',
|
|
227
|
+
type: args.type || 'info',
|
|
228
|
+
status: 'ok',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
case 'oasis_add_todo': {
|
|
233
|
+
if (!args.text) return { error: 'text is required', status: 'error' };
|
|
234
|
+
session.todos.push({ text: args.text, done: false });
|
|
235
|
+
saveSession(session);
|
|
236
|
+
return { todo: { text: args.text, done: false }, total: session.todos.length, status: 'ok' };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
case 'oasis_toggle_todo': {
|
|
240
|
+
const idx = args.index;
|
|
241
|
+
if (typeof idx !== 'number' || idx < 0 || idx >= session.todos.length) {
|
|
242
|
+
return { error: `Invalid index ${idx}. Range: 0–${session.todos.length - 1}`, status: 'error' };
|
|
243
|
+
}
|
|
244
|
+
session.todos[idx].done = !session.todos[idx].done;
|
|
245
|
+
saveSession(session);
|
|
246
|
+
return { todo: session.todos[idx], index: idx, status: 'ok' };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
case 'oasis_execute': {
|
|
250
|
+
if (!args.command) return { error: 'command is required', status: 'error' };
|
|
251
|
+
const cwd = session.sandboxCwd || process.cwd();
|
|
252
|
+
const timeout = args.timeout || 30000;
|
|
253
|
+
try {
|
|
254
|
+
const stdout = execSync(args.command, {
|
|
255
|
+
cwd,
|
|
256
|
+
timeout,
|
|
257
|
+
maxBuffer: 1024 * 1024,
|
|
258
|
+
encoding: 'utf8',
|
|
259
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
260
|
+
});
|
|
261
|
+
return { stdout: stdout.trim(), exitCode: 0, cwd, status: 'ok' };
|
|
262
|
+
} catch (e) {
|
|
263
|
+
return {
|
|
264
|
+
stdout: (e.stdout || '').trim(),
|
|
265
|
+
stderr: (e.stderr || '').trim(),
|
|
266
|
+
exitCode: e.status || 1,
|
|
267
|
+
cwd,
|
|
268
|
+
status: e.status === 0 ? 'ok' : 'error',
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
default:
|
|
274
|
+
return { error: `Unknown tool: ${name}`, status: 'error' };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ─── JSON-RPC over stdio ─────────────────────────────────────────────────────
|
|
279
|
+
|
|
280
|
+
let buffer = '';
|
|
281
|
+
|
|
282
|
+
process.stdin.setEncoding('utf8');
|
|
283
|
+
process.stdin.on('data', (chunk) => {
|
|
284
|
+
buffer += chunk;
|
|
285
|
+
const lines = buffer.split('\n');
|
|
286
|
+
buffer = lines.pop(); // keep incomplete line in buffer
|
|
287
|
+
|
|
288
|
+
for (const line of lines) {
|
|
289
|
+
const trimmed = line.trim();
|
|
290
|
+
if (!trimmed) continue;
|
|
291
|
+
try {
|
|
292
|
+
const req = JSON.parse(trimmed);
|
|
293
|
+
handleRequest(req);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
// ignore malformed lines
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
function handleRequest(req) {
|
|
301
|
+
const { id, method, params } = req;
|
|
302
|
+
|
|
303
|
+
switch (method) {
|
|
304
|
+
case 'initialize':
|
|
305
|
+
respond(id, {
|
|
306
|
+
protocolVersion: '2024-11-05',
|
|
307
|
+
capabilities: { tools: {} },
|
|
308
|
+
serverInfo: { name: 'p31-oasis-mcp', version: '1.0.0' },
|
|
309
|
+
});
|
|
310
|
+
break;
|
|
311
|
+
|
|
312
|
+
case 'notifications/initialized':
|
|
313
|
+
// no response needed for notifications
|
|
314
|
+
break;
|
|
315
|
+
|
|
316
|
+
case 'tools/list':
|
|
317
|
+
respond(id, { tools: TOOLS });
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case 'tools/call': {
|
|
321
|
+
const toolName = params?.name;
|
|
322
|
+
const toolArgs = params?.arguments || {};
|
|
323
|
+
const tool = TOOLS.find(t => t.name === toolName);
|
|
324
|
+
if (!tool) {
|
|
325
|
+
respondError(id, -32602, `Unknown tool: ${toolName}`);
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
const result = executeTool(toolName, toolArgs);
|
|
330
|
+
const content = [{ type: 'text', text: JSON.stringify(result, null, 2) }];
|
|
331
|
+
respond(id, { content });
|
|
332
|
+
} catch (e) {
|
|
333
|
+
respond(id, {
|
|
334
|
+
content: [{ type: 'text', text: JSON.stringify({ error: e.message, status: 'error' }) }],
|
|
335
|
+
isError: true,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
case 'ping':
|
|
342
|
+
respond(id, {});
|
|
343
|
+
break;
|
|
344
|
+
|
|
345
|
+
default:
|
|
346
|
+
if (id !== undefined) {
|
|
347
|
+
respondError(id, -32601, `Method not found: ${method}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function respond(id, result) {
|
|
353
|
+
const msg = JSON.stringify({ jsonrpc: '2.0', id, result });
|
|
354
|
+
process.stdout.write(msg + '\n');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function respondError(id, code, message) {
|
|
358
|
+
const msg = JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } });
|
|
359
|
+
process.stdout.write(msg + '\n');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ─── Entry ───────────────────────────────────────────────────────────────────
|
|
363
|
+
|
|
364
|
+
process.stderr.write('[p31-oasis-mcp] Server started. Listening on stdin (JSON-RPC).\n');
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@p31/andromeda-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Sovereign, agent-native CLI for the P31 ecosystem — Oasis TUI, MCP server, and --agent JSON mode for AI agents and CI/CD.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"andromeda": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node index.js",
|
|
11
|
+
"dev": "nodemon index.js",
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"postinstall": "node scripts/postinstall.js"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20.0.0"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"index.js",
|
|
20
|
+
"mcp-server.js",
|
|
21
|
+
"component-registry.js",
|
|
22
|
+
"design-tokens-registry.js",
|
|
23
|
+
"README.md",
|
|
24
|
+
"SECURITY.md",
|
|
25
|
+
"config/",
|
|
26
|
+
"scripts/postinstall.js"
|
|
27
|
+
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"axios": "^1.4.0",
|
|
30
|
+
"bcrypt": "^5.1.0",
|
|
31
|
+
"boxen": "^5.1.2",
|
|
32
|
+
"chalk": "^4.1.2",
|
|
33
|
+
"cli-table3": "^0.6.3",
|
|
34
|
+
"commander": "^11.0.0",
|
|
35
|
+
"crypto": "^1.0.1",
|
|
36
|
+
"dotenv": "^16.3.1",
|
|
37
|
+
"envfile": "^6.1.0",
|
|
38
|
+
"figlet": "^1.5.2",
|
|
39
|
+
"fs-extra": "^11.1.1",
|
|
40
|
+
"inquirer": "^8.2.5",
|
|
41
|
+
"jsonwebtoken": "^9.0.2",
|
|
42
|
+
"node-cron": "^3.0.2",
|
|
43
|
+
"ora": "^8.0.1",
|
|
44
|
+
"progress": "^2.0.3",
|
|
45
|
+
"update-notifier": "^6.0.2",
|
|
46
|
+
"yaml": "^2.3.1"
|
|
47
|
+
},
|
|
48
|
+
"optionalDependencies": {
|
|
49
|
+
"blessed": "^0.1.81",
|
|
50
|
+
"blessed-contrib": "^4.11.0",
|
|
51
|
+
"node-pty": "^1.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"babel-cli": "6.26.0",
|
|
55
|
+
"babel-preset-env": "^1.7.0",
|
|
56
|
+
"jest": "^29.6.1",
|
|
57
|
+
"nodemon": "^3.0.1"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
},
|
|
62
|
+
"repository": {
|
|
63
|
+
"type": "git",
|
|
64
|
+
"url": "git+https://github.com/p31labs/P31-local-workspace.git"
|
|
65
|
+
},
|
|
66
|
+
"bugs": {
|
|
67
|
+
"url": "https://github.com/p31labs/P31-local-workspace/issues"
|
|
68
|
+
},
|
|
69
|
+
"homepage": "https://p31ca.org/cli",
|
|
70
|
+
"author": "P31 Labs",
|
|
71
|
+
"license": "MIT",
|
|
72
|
+
"keywords": [
|
|
73
|
+
"cli",
|
|
74
|
+
"agent-native",
|
|
75
|
+
"neuroinclusive",
|
|
76
|
+
"MCP",
|
|
77
|
+
"sovereign",
|
|
78
|
+
"assistive-technology",
|
|
79
|
+
"p31",
|
|
80
|
+
"andromeda"
|
|
81
|
+
]
|
|
82
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
console.log('🔧 P31 Andromeda CLI Post-Installation Setup');
|
|
8
|
+
|
|
9
|
+
// Create necessary directories
|
|
10
|
+
const directories = [
|
|
11
|
+
'config',
|
|
12
|
+
'secrets',
|
|
13
|
+
'tokens',
|
|
14
|
+
'logs',
|
|
15
|
+
'deployment',
|
|
16
|
+
'monitoring'
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
directories.forEach(dir => {
|
|
20
|
+
const dirPath = path.join(__dirname, '..', dir);
|
|
21
|
+
if (!fs.existsSync(dirPath)) {
|
|
22
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
23
|
+
console.log(`✅ Created directory: ${dir}`);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Create default configuration files
|
|
28
|
+
const defaultConfig = {
|
|
29
|
+
version: '1.0.0',
|
|
30
|
+
environment: 'development',
|
|
31
|
+
services: {
|
|
32
|
+
frontend: {
|
|
33
|
+
port: 3000,
|
|
34
|
+
path: 'software/spaceship-earth'
|
|
35
|
+
},
|
|
36
|
+
backend: {
|
|
37
|
+
port: 3001,
|
|
38
|
+
path: 'software'
|
|
39
|
+
},
|
|
40
|
+
monitoring: {
|
|
41
|
+
port: 3002,
|
|
42
|
+
path: 'monitoring'
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
oauth: {
|
|
46
|
+
providers: ['google', 'github', 'discord'],
|
|
47
|
+
callbackUrl: 'http://localhost:3000/auth/callback'
|
|
48
|
+
},
|
|
49
|
+
secrets: {
|
|
50
|
+
encryption: {
|
|
51
|
+
algorithm: 'aes-256-gcm',
|
|
52
|
+
keyLength: 32,
|
|
53
|
+
ivLength: 16
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
fs.writeFileSync(
|
|
59
|
+
path.join(__dirname, '..', 'config', 'default.json'),
|
|
60
|
+
JSON.stringify(defaultConfig, null, 2)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Create .gitignore for CLI
|
|
64
|
+
const gitignore = `
|
|
65
|
+
# CLI-specific ignores
|
|
66
|
+
node_modules/
|
|
67
|
+
.env.local
|
|
68
|
+
.env.production
|
|
69
|
+
secrets/encrypted.json
|
|
70
|
+
tokens/jwt.json
|
|
71
|
+
logs/*.log
|
|
72
|
+
dist/
|
|
73
|
+
coverage/
|
|
74
|
+
.DS_Store
|
|
75
|
+
*.log
|
|
76
|
+
npm-debug.log*
|
|
77
|
+
yarn-debug.log*
|
|
78
|
+
yarn-error.log*
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
fs.writeFileSync(path.join(__dirname, '..', '.gitignore'), gitignore);
|
|
82
|
+
|
|
83
|
+
// Make CLI executable
|
|
84
|
+
const cliPath = path.join(__dirname, '..', 'index.js');
|
|
85
|
+
try {
|
|
86
|
+
fs.chmodSync(cliPath, '755');
|
|
87
|
+
console.log('✅ CLI executable permissions set');
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.log('⚠️ Could not set executable permissions (Windows)');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log('✅ P31 Andromeda CLI setup complete!');
|
|
93
|
+
console.log('');
|
|
94
|
+
console.log('🚀 Quick Start:');
|
|
95
|
+
console.log(' andromeda setup # Complete environment setup');
|
|
96
|
+
console.log(' andromeda dev # Start development environment');
|
|
97
|
+
console.log(' andromeda launch # Launch production environment');
|
|
98
|
+
console.log('');
|
|
99
|
+
console.log('📚 For more commands, run: andromeda --help');
|