navada-edge-cli 4.0.0 → 4.2.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.
@@ -1,40 +1,12 @@
1
1
  'use strict';
2
2
 
3
- const navada = require('navada-edge-sdk');
4
3
  const ui = require('../ui');
5
4
 
6
5
  module.exports = function(reg) {
7
- reg('lucas', 'Lucas CTO agent commands', async (args) => {
8
- const sub = args[0];
9
- if (sub === 'exec' && args[1]) {
10
- const cmd = args.slice(1).join(' ');
11
- const result = await navada.lucas.exec(cmd);
12
- console.log(typeof result === 'object' ? ui.jsonColorize(result) : result);
13
- } else if (sub === 'ssh' && args[1] && args[2]) {
14
- const result = await navada.lucas.ssh(args[1], args.slice(2).join(' '));
15
- console.log(typeof result === 'object' ? ui.jsonColorize(result) : result);
16
- } else if (sub === 'docker' && args[1] && args[2]) {
17
- const result = await navada.lucas.docker(args[1], args.slice(2).join(' '));
18
- console.log(typeof result === 'object' ? ui.jsonColorize(result) : result);
19
- } else if (sub === 'deploy' && args[1] && args[2]) {
20
- const ora = require('ora');
21
- const spinner = ora({ text: ` Deploying ${args[1]} to ${args[2]}...`, color: 'white' }).start();
22
- const result = await navada.lucas.deploy(args[1], args[2]);
23
- spinner.stop();
24
- console.log(ui.success(`Deployed ${args[1]} to ${args[2]}`));
25
- console.log(typeof result === 'object' ? ui.jsonColorize(result) : result);
26
- } else if (sub === 'status') {
27
- const result = await navada.lucas.networkStatus();
28
- console.log(ui.header('LUCAS NETWORK STATUS'));
29
- console.log(typeof result === 'object' ? ui.jsonColorize(result) : result);
30
- } else if (sub === 'files' && args[1]) {
31
- const result = await navada.lucas.listFiles(args[1]);
32
- console.log(typeof result === 'object' ? ui.jsonColorize(result) : result);
33
- } else if (sub === 'read' && args[1]) {
34
- const result = await navada.lucas.readFile(args[1]);
35
- console.log(typeof result === 'string' ? result : ui.jsonColorize(result));
36
- } else {
37
- console.log(ui.dim('Usage: /lucas exec <cmd> | ssh <node> <cmd> | docker <ctr> <cmd> | deploy <name> <node> | status | files <dir> | read <file>'));
38
- }
39
- }, { category: 'AGENTS', subs: ['exec', 'ssh', 'docker', 'deploy', 'status', 'files', 'read'] });
6
+ reg('lucas', 'Lucas CTO agent (admin only)', async () => {
7
+ console.log(ui.header('LUCAS CTO'));
8
+ console.log(ui.dim('Lucas CTO is a private admin agent and is not available in the public CLI.'));
9
+ console.log(ui.dim('Use /automate to submit automation requests instead.'));
10
+ console.log(ui.dim('Or use /compute for quick cloud execution.'));
11
+ }, { category: 'AGENTS' });
40
12
  };
@@ -1,35 +1,12 @@
1
1
  'use strict';
2
2
 
3
- const navada = require('navada-edge-sdk');
4
- const Table = require('cli-table3');
5
3
  const ui = require('../ui');
6
- const { tableChars } = require('./helpers');
7
4
 
8
5
  module.exports = function(reg) {
9
- reg('mcp', 'MCP server tools', async (args) => {
10
- const sub = args[0];
11
- if (sub === 'tools' || !sub) {
12
- const ora = require('ora');
13
- const spinner = ora({ text: ' Fetching MCP tools...', color: 'white' }).start();
14
- const tools = await navada.mcp.tools();
15
- spinner.stop();
16
- console.log(ui.header(`MCP TOOLS (${tools.length})`));
17
- if (tools.length === 0) { console.log(ui.dim('No tools returned (check API key tier)')); return; }
18
- const t = new Table({ head: ['Tool', 'Description'], style: { head: ['white'], border: ['gray'] }, chars: tableChars() });
19
- tools.forEach(tool => t.push([tool.name, tool.description || '']));
20
- console.log(t.toString());
21
- } else if (sub === 'call' && args[1]) {
22
- const toolName = args[1];
23
- let toolArgs = {};
24
- if (args[2]) { try { toolArgs = JSON.parse(args.slice(2).join(' ')); } catch { toolArgs = { input: args.slice(2).join(' ') }; } }
25
- const ora = require('ora');
26
- const spinner = ora({ text: ` Calling ${toolName}...`, color: 'white' }).start();
27
- const result = await navada.mcp.call(toolName, toolArgs);
28
- spinner.stop();
29
- console.log(ui.header(`MCP: ${toolName}`));
30
- console.log(typeof result === 'object' ? ui.jsonColorize(result) : result);
31
- } else {
32
- console.log(ui.dim('Usage: /mcp tools | /mcp call <tool> [json]'));
33
- }
34
- }, { category: 'MCP', subs: ['tools', 'call'] });
6
+ reg('mcp', 'MCP tools (admin only)', async () => {
7
+ console.log(ui.header('MCP TOOLS'));
8
+ console.log(ui.dim('MCP tools require admin access to the NAVADA Edge Network.'));
9
+ console.log(ui.dim('Use /automate or /compute for cloud compute instead.'));
10
+ console.log(ui.dim('Use /edge login <key> to connect to the network.'));
11
+ }, { category: 'MCP' });
35
12
  };
@@ -23,14 +23,14 @@ const DEFAULT_NVIDIA_MODEL = 'llama-3.3-70b';
23
23
  // ---------------------------------------------------------------------------
24
24
  // NVIDIA API chat (streaming)
25
25
  // ---------------------------------------------------------------------------
26
- function streamNvidia(apiKey, messages, modelKey = DEFAULT_NVIDIA_MODEL) {
26
+ function streamNvidia(apiKey, messages, modelKey = DEFAULT_NVIDIA_MODEL, systemPrompt = null) {
27
27
  const modelInfo = NVIDIA_MODELS[modelKey] || NVIDIA_MODELS[DEFAULT_NVIDIA_MODEL];
28
28
 
29
29
  return new Promise((resolve, reject) => {
30
30
  const body = JSON.stringify({
31
31
  model: modelInfo.id,
32
32
  messages: [
33
- { role: 'system', content: 'You are NAVADA, an AI infrastructure agent. Keep responses concise and technical. Use code blocks with language tags when showing code.' },
33
+ { role: 'system', content: systemPrompt || 'You are NAVADA, an AI infrastructure agent. Keep responses concise and technical. Use code blocks with language tags when showing code.' },
34
34
  ...messages,
35
35
  ],
36
36
  max_tokens: 4096,
@@ -95,14 +95,14 @@ function streamNvidia(apiKey, messages, modelKey = DEFAULT_NVIDIA_MODEL) {
95
95
  }
96
96
 
97
97
  // Non-streaming fallback
98
- async function chatNvidia(apiKey, messages, modelKey = DEFAULT_NVIDIA_MODEL) {
98
+ async function chatNvidia(apiKey, messages, modelKey = DEFAULT_NVIDIA_MODEL, systemPrompt = null) {
99
99
  const modelInfo = NVIDIA_MODELS[modelKey] || NVIDIA_MODELS[DEFAULT_NVIDIA_MODEL];
100
100
 
101
101
  return new Promise((resolve, reject) => {
102
102
  const body = JSON.stringify({
103
103
  model: modelInfo.id,
104
104
  messages: [
105
- { role: 'system', content: 'You are NAVADA, an AI infrastructure agent. Keep responses concise and technical. Use code blocks with language tags when showing code.' },
105
+ { role: 'system', content: systemPrompt || 'You are NAVADA, an AI infrastructure agent. Keep responses concise and technical. Use code blocks with language tags when showing code.' },
106
106
  ...messages,
107
107
  ],
108
108
  max_tokens: 4096,
@@ -1,16 +1,109 @@
1
1
  'use strict';
2
2
 
3
3
  const readline = require('readline');
4
+ const fs = require('fs');
5
+ const path = require('path');
4
6
  const navada = require('navada-edge-sdk');
5
7
  const ui = require('../ui');
6
8
  const config = require('../config');
7
- const fs = require('fs');
8
- const path = require('path');
9
9
 
10
10
  function ask(rl, question) {
11
11
  return new Promise(resolve => rl.question(question, resolve));
12
12
  }
13
13
 
14
+ // Default templates
15
+ const SOUL_TEMPLATE = `# Soul — Who I Am
16
+
17
+ ## Identity
18
+ name: ""
19
+ role: ""
20
+ industry: ""
21
+ experience_level: "" # junior, mid, senior, lead, principal
22
+
23
+ ## Goals
24
+ - What do you want to achieve with NAVADA Edge?
25
+ - What problems are you trying to solve?
26
+
27
+ ## Stack
28
+ languages: []
29
+ frameworks: []
30
+ cloud: []
31
+ databases: []
32
+
33
+ ## Communication Style
34
+ tone: "professional" # professional, casual, technical, creative
35
+ detail_level: "balanced" # brief, balanced, detailed
36
+ code_comments: true
37
+ explain_changes: true
38
+
39
+ ## Context
40
+ # Add anything the agent should know about you, your team, or your projects.
41
+ # This file is loaded every time you interact with the NAVADA agent.
42
+ `;
43
+
44
+ const GUARDRAIL_TEMPLATE = `# Guardrails — Agent Boundaries
45
+
46
+ ## Permissions
47
+ allow_shell_commands: true
48
+ allow_file_write: true
49
+ allow_file_delete: false
50
+ allow_python_exec: true
51
+ allow_pip_install: true
52
+ allow_network_requests: true
53
+
54
+ ## Safety
55
+ max_shell_timeout_seconds: 30
56
+ confirm_before_delete: true
57
+ confirm_before_overwrite: true
58
+ never_modify_paths:
59
+ - ~/.ssh/
60
+ - ~/.gnupg/
61
+ - ~/.aws/credentials
62
+
63
+ ## Content
64
+ no_offensive_content: true
65
+ no_personal_data_in_logs: true
66
+ language: "en"
67
+
68
+ ## Automation
69
+ auto_submit_requests: false # if true, agent can submit /automate without asking
70
+ max_automation_requests_per_day: 5
71
+ `;
72
+
73
+ const README_TEMPLATE = `# My NAVADA Edge Workspace
74
+
75
+ ## Setup
76
+ - CLI: navada-edge-cli v4.0.0
77
+ - Agent: NAVADA Edge AI
78
+ - Config: ~/.navada/
79
+
80
+ ## Files
81
+ - **soul.md** — Your identity, goals, and preferences (agent reads this)
82
+ - **guardrail.md** — Safety boundaries and permissions
83
+ - **agents/** — Custom sub-agent personas
84
+
85
+ ## Quick Start
86
+ \`\`\`bash
87
+ navada # Interactive mode
88
+ /help # All commands
89
+ /tools # Available agent tools
90
+ /automate # Submit automation request
91
+ /requests # Track your requests
92
+ /learn python # Learning mode
93
+ \`\`\`
94
+
95
+ ## Automation
96
+ Submit automation requests and the NAVADA team will set them up for you:
97
+ - Marketing emails and campaigns
98
+ - Scheduled reports and data pipelines
99
+ - Application builds and deployments
100
+ - Custom recurring tasks
101
+
102
+ ## Support
103
+ - Portal: https://portal.navada-edge-server.uk
104
+ - Docs: https://docs.navada-edge-server.uk
105
+ `;
106
+
14
107
  async function runSetup(fromRepl = false) {
15
108
  const rl = readline.createInterface({
16
109
  input: process.stdin,
@@ -19,96 +112,215 @@ async function runSetup(fromRepl = false) {
19
112
  });
20
113
 
21
114
  console.log('');
22
- console.log(ui.box('NAVADA EDGE SETUP', ' Let\'s configure your CLI in under a minute.'));
115
+ console.log(ui.box('NAVADA EDGE SETUP', ' Configure your AI agent for full access.'));
23
116
  console.log('');
24
117
 
25
- // 1. API Key
26
- console.log(ui.dim(' Add an AI API key for full agent capabilities.'));
27
- console.log(ui.dim(' Supported: OpenAI (sk-...), Anthropic (sk-ant-...), Gemini (AIza...)'));
28
- console.log(ui.dim(' Or press Enter to use the free tier (Grok, limited).'));
118
+ // Step 1: API Key
119
+ console.log(ui.dim(' STEP 1 API Key'));
120
+ console.log(ui.dim(' Free tier: NVIDIA AI with rate limit (no key needed)'));
121
+ console.log(ui.dim(' Full access: Register for unlimited + automations'));
122
+ console.log('');
123
+ console.log(ui.dim(' Accepts: NAVADA Edge key (nv_edge_...), Anthropic (sk-ant-...), OpenAI (sk-...), NVIDIA (nvapi-...)'));
29
124
  console.log('');
30
125
 
31
- const apiKey = await ask(rl, ' API key: ');
126
+ const apiKey = await ask(rl, ' API key (Enter to skip — free tier): ');
32
127
  if (apiKey.trim()) {
33
128
  const key = apiKey.trim();
129
+ config.setApiKey(key);
34
130
  if (key.startsWith('sk-ant')) {
35
131
  config.set('anthropicKey', key);
36
- config.setApiKey(key);
37
- console.log(ui.success('Anthropic key saved. Full agent mode with tool use enabled.'));
38
- } else if (key.startsWith('AIza')) {
39
- config.set('geminiKey', key);
40
- console.log(ui.success('Gemini key saved. Free unlimited access.'));
41
- } else if (key.startsWith('sk-')) {
42
- config.set('openaiKey', key);
43
- config.setApiKey(key);
44
- console.log(ui.success('OpenAI key saved. GPT-4o with tool use enabled.'));
132
+ console.log(ui.success('Anthropic key saved — full agent mode'));
133
+ } else if (key.startsWith('nv_edge')) {
134
+ config.set('edgeKey', key);
135
+ console.log(ui.success('NAVADA Edge key saved — full access'));
45
136
  } else if (key.startsWith('nvapi-')) {
46
137
  config.set('nvidiaKey', key);
47
- console.log(ui.success('NVIDIA key saved. Llama, Mistral, DeepSeek enabled.'));
48
- } else if (key.startsWith('hf_')) {
49
- config.set('hfToken', key);
50
- console.log(ui.success('HuggingFace key saved. Qwen Coder enabled.'));
138
+ console.log(ui.success('NVIDIA key saved'));
139
+ } else if (key.startsWith('sk-')) {
140
+ config.set('openaiKey', key);
141
+ console.log(ui.success('OpenAI key saved'));
51
142
  } else {
52
- config.setApiKey(key);
53
- console.log(ui.success('Key saved.'));
143
+ console.log(ui.success('Key saved'));
54
144
  }
55
145
  } else {
56
- console.log(ui.dim(' Skipped. Free tier active. Add a key anytime with /login <key>'));
146
+ console.log(ui.dim(' Skipped free NVIDIA tier active (rate limited)'));
147
+ console.log(ui.dim(' Unlock full access: /register or /login <key>'));
57
148
  }
58
149
  console.log('');
59
150
 
60
- // 2. Theme
61
- const theme = await ask(rl, ' Theme (dark/crow/matrix/light) [dark]: ');
62
- config.setTheme(theme.trim() || 'dark');
151
+ // Step 2: Soul.md
152
+ console.log(ui.dim(' STEP 2 soul.md (your identity)'));
153
+ console.log(ui.dim(' The agent reads this to understand who you are.'));
63
154
  console.log('');
64
155
 
65
- // 3. Create agent.md
66
- const agentMd = path.join(config.CONFIG_DIR, 'agent.md');
67
- if (!fs.existsSync(agentMd)) {
68
- const createAgent = await ask(rl, ' Create agent.md for custom AI personality? (Y/n): ');
69
- if (createAgent.trim().toLowerCase() !== 'n') {
70
- if (!fs.existsSync(config.CONFIG_DIR)) fs.mkdirSync(config.CONFIG_DIR, { recursive: true });
71
- fs.writeFileSync(agentMd, `# My NAVADA Agent
72
-
73
- ## About Me
74
- <!-- Tell the agent who you are and what you work on -->
75
- I am a developer working on...
76
-
77
- ## Preferences
78
- <!-- How should the agent behave? -->
79
- - Be concise and technical
80
- - Always explain before making changes
81
- - Prefer TypeScript for new code
82
-
83
- ## My Stack
84
- <!-- What tools and tech do you use? -->
85
- - ...
86
- `);
87
- console.log(ui.success('Created ~/.navada/agent.md'));
88
- console.log(ui.dim(' Edit this file to personalise your agent.'));
89
- }
156
+ const soulPath = path.join(config.CONFIG_DIR, 'soul.md');
157
+ if (fs.existsSync(soulPath)) {
158
+ console.log(ui.dim(' soul.md already exists — keeping current version'));
90
159
  } else {
91
- console.log(ui.dim(' agent.md already exists.'));
160
+ const name = await ask(rl, ' Your name: ');
161
+ const role = await ask(rl, ' Your role (e.g. developer, marketer, founder): ');
162
+ const goals = await ask(rl, ' What do you want to automate? ');
163
+
164
+ let soul = SOUL_TEMPLATE;
165
+ if (name.trim()) soul = soul.replace('name: ""', `name: "${name.trim()}"`);
166
+ if (role.trim()) soul = soul.replace('role: ""', `role: "${role.trim()}"`);
167
+ if (goals.trim()) soul = soul.replace('- What do you want to achieve with NAVADA Edge?', `- ${goals.trim()}`);
168
+
169
+ if (!fs.existsSync(config.CONFIG_DIR)) fs.mkdirSync(config.CONFIG_DIR, { recursive: true });
170
+ fs.writeFileSync(soulPath, soul);
171
+ console.log(ui.success('Created ~/.navada/soul.md'));
92
172
  }
93
173
  console.log('');
94
174
 
95
- // 4. Cloud compute (optional)
96
- console.log(ui.dim(' Want 24/7 cloud compute? Sign up at portal.navada-edge-server.uk'));
97
- console.log(ui.dim(' Generate an API key, then run: /edge login nv_edge_your_key'));
175
+ // Step 3: Guardrails
176
+ console.log(ui.dim(' STEP 3 guardrail.md (safety boundaries)'));
177
+ const guardrailPath = path.join(config.CONFIG_DIR, 'guardrail.md');
178
+ if (fs.existsSync(guardrailPath)) {
179
+ console.log(ui.dim(' guardrail.md already exists — keeping current version'));
180
+ } else {
181
+ const strictMode = await ask(rl, ' Strict mode? Confirms before file changes (y/N): ');
182
+ let guardrail = GUARDRAIL_TEMPLATE;
183
+ if (strictMode.trim().toLowerCase() === 'y') {
184
+ guardrail = guardrail.replace('confirm_before_overwrite: true', 'confirm_before_overwrite: true');
185
+ guardrail = guardrail.replace('allow_file_delete: false', 'allow_file_delete: false');
186
+ } else {
187
+ guardrail = guardrail.replace('confirm_before_delete: true', 'confirm_before_delete: false');
188
+ guardrail = guardrail.replace('confirm_before_overwrite: true', 'confirm_before_overwrite: false');
189
+ }
190
+ fs.writeFileSync(guardrailPath, guardrail);
191
+ console.log(ui.success('Created ~/.navada/guardrail.md'));
192
+ }
193
+ console.log('');
194
+
195
+ // Step 4: README
196
+ const readmePath = path.join(config.CONFIG_DIR, 'README.md');
197
+ if (!fs.existsSync(readmePath)) {
198
+ fs.writeFileSync(readmePath, README_TEMPLATE);
199
+ console.log(ui.success('Created ~/.navada/README.md'));
200
+ }
201
+
202
+ // Step 5: Agents directory
203
+ const agentDir = path.join(config.CONFIG_DIR, 'agents');
204
+ if (!fs.existsSync(agentDir)) fs.mkdirSync(agentDir, { recursive: true });
205
+
206
+ // Step 6: Memory directory
207
+ const memDir = path.join(config.CONFIG_DIR, 'memory');
208
+ if (!fs.existsSync(memDir)) fs.mkdirSync(memDir, { recursive: true });
209
+
210
+ // Step 7: Requests directory
211
+ const reqDir = path.join(config.CONFIG_DIR, 'requests');
212
+ if (!fs.existsSync(reqDir)) fs.mkdirSync(reqDir, { recursive: true });
213
+
214
+ // Step 8: Skills directory + install default skill
215
+ const skillsDir = path.join(config.CONFIG_DIR, 'skills');
216
+ if (!fs.existsSync(skillsDir)) fs.mkdirSync(skillsDir, { recursive: true });
217
+
218
+ // Offer to install template skills
219
+ const installSkills = await ask(rl, ' Install starter skills? (Y/n): ');
220
+ if (installSkills.trim().toLowerCase() !== 'n') {
221
+ const skills = require('../skills');
222
+ let installed = 0;
223
+ for (const [name, tmpl] of Object.entries(skills.TEMPLATES)) {
224
+ const skillFile = path.join(skillsDir, `${name}.md`);
225
+ if (!fs.existsSync(skillFile)) {
226
+ skills.createSkill(name, tmpl);
227
+ installed++;
228
+ }
229
+ }
230
+ if (installed > 0) {
231
+ console.log(ui.success(`Installed ${installed} starter skills`));
232
+ console.log(ui.dim(' View: /skill list | Create your own: /skill create'));
233
+ } else {
234
+ console.log(ui.dim(' All template skills already installed'));
235
+ }
236
+ }
237
+ console.log('');
238
+
239
+ // Step 9: Theme
240
+ const theme = await ask(rl, ' Theme (dark/crow/matrix/light) [dark]: ');
241
+ config.setTheme(theme.trim() || 'dark');
98
242
  console.log('');
99
243
 
100
244
  // Done
101
- const pkg = require('../../package.json');
102
- console.log(ui.box('READY', ` NAVADA Edge CLI v${pkg.version}\n Config: ${config.CONFIG_FILE}\n\n Type naturally to chat, or /help for commands.`));
245
+ console.log(ui.box('READY', ` Config: ${config.CONFIG_FILE}
246
+ Soul: ${soulPath}
247
+ Guard: ${guardrailPath}
248
+
249
+ Type naturally to chat, or /help for commands.
250
+
251
+ KEY COMMANDS
252
+ /tools — 16 agent tools across 7 categories
253
+ /skills — view and manage skills
254
+ /skill create — create your own reusable skills
255
+ /skill templates — install ready-made skills
256
+ /automate — submit automation requests
257
+ /about — learn about NAVADA Edge`));
103
258
  console.log('');
104
259
 
105
260
  rl.close();
106
261
  }
107
262
 
108
263
  module.exports = function(reg) {
109
- reg('setup', 'Run setup wizard', async () => {
264
+ reg('setup', 'Run full setup wizard (soul.md + guardrails + config)', async () => {
110
265
  await runSetup(true);
111
266
  }, { category: 'SYSTEM' });
267
+
268
+ reg('soul', 'View or edit your soul.md', (args) => {
269
+ const soulPath = path.join(config.CONFIG_DIR, 'soul.md');
270
+ if (args[0] === 'edit') {
271
+ if (!fs.existsSync(soulPath)) {
272
+ fs.writeFileSync(soulPath, SOUL_TEMPLATE);
273
+ }
274
+ console.log(ui.header('SOUL.MD'));
275
+ console.log(ui.label('Path', soulPath));
276
+ console.log(ui.dim(' Edit this file to customise your agent\'s understanding of you.'));
277
+ console.log(ui.dim(' The agent reads it on every interaction.'));
278
+ // Try to open in editor
279
+ try {
280
+ const { exec } = require('child_process');
281
+ const editor = process.env.EDITOR || (process.platform === 'win32' ? 'notepad' : 'nano');
282
+ exec(`${editor} "${soulPath}"`);
283
+ console.log(ui.success(`Opening in ${editor}...`));
284
+ } catch {
285
+ console.log(ui.dim(` Open manually: ${soulPath}`));
286
+ }
287
+ } else {
288
+ if (!fs.existsSync(soulPath)) {
289
+ console.log(ui.dim('No soul.md yet. Run /setup to create one.'));
290
+ return;
291
+ }
292
+ console.log(ui.header('SOUL.MD'));
293
+ console.log(fs.readFileSync(soulPath, 'utf-8'));
294
+ }
295
+ }, { category: 'SYSTEM', subs: ['edit'] });
296
+
297
+ reg('guardrails', 'View or edit your guardrail.md', (args) => {
298
+ const guardrailPath = path.join(config.CONFIG_DIR, 'guardrail.md');
299
+ if (args[0] === 'edit') {
300
+ if (!fs.existsSync(guardrailPath)) {
301
+ fs.writeFileSync(guardrailPath, GUARDRAIL_TEMPLATE);
302
+ }
303
+ console.log(ui.header('GUARDRAIL.MD'));
304
+ console.log(ui.label('Path', guardrailPath));
305
+ try {
306
+ const { exec } = require('child_process');
307
+ const editor = process.env.EDITOR || (process.platform === 'win32' ? 'notepad' : 'nano');
308
+ exec(`${editor} "${guardrailPath}"`);
309
+ console.log(ui.success(`Opening in ${editor}...`));
310
+ } catch {
311
+ console.log(ui.dim(` Open manually: ${guardrailPath}`));
312
+ }
313
+ } else {
314
+ if (!fs.existsSync(guardrailPath)) {
315
+ console.log(ui.dim('No guardrail.md yet. Run /setup to create one.'));
316
+ return;
317
+ }
318
+ console.log(ui.header('GUARDRAIL.MD'));
319
+ console.log(fs.readFileSync(guardrailPath, 'utf-8'));
320
+ }
321
+ }, { category: 'SYSTEM', subs: ['edit'] });
112
322
  };
113
323
 
114
324
  module.exports.runSetup = runSetup;
325
+ module.exports.SOUL_TEMPLATE = SOUL_TEMPLATE;
326
+ module.exports.GUARDRAIL_TEMPLATE = GUARDRAIL_TEMPLATE;