navada-edge-cli 4.1.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.
package/lib/agent.js CHANGED
@@ -175,6 +175,15 @@ function getSystemPrompt() {
175
175
  prompt += `\n\n--- MEMORY (auto-loaded) ---\n${memoryContext}`;
176
176
  }
177
177
 
178
+ // Inject user skills (so agent knows what skills are available)
179
+ try {
180
+ const skills = require('./skills');
181
+ const skillsPrompt = skills.getSkillsPrompt();
182
+ if (skillsPrompt) {
183
+ prompt += `\n\n--- USER SKILLS ---\n${skillsPrompt}`;
184
+ }
185
+ } catch {}
186
+
178
187
  return prompt;
179
188
  }
180
189
 
@@ -6,7 +6,7 @@ const { register } = require('../registry');
6
6
  const moduleNames = [
7
7
  'network', 'mcp', 'lucas', 'docker', 'database', 'cloudflare',
8
8
  'ai', 'azure', 'agents', 'tasks', 'keys', 'setup', 'system',
9
- 'learn', 'sandbox', 'nvidia', 'edge', 'conversations', 'audit', 'compute',
9
+ 'learn', 'sandbox', 'nvidia', 'edge', 'conversations', 'audit', 'compute', 'skills',
10
10
  ];
11
11
 
12
12
  function loadAll() {
@@ -211,7 +211,32 @@ async function runSetup(fromRepl = false) {
211
211
  const reqDir = path.join(config.CONFIG_DIR, 'requests');
212
212
  if (!fs.existsSync(reqDir)) fs.mkdirSync(reqDir, { recursive: true });
213
213
 
214
- // Step 8: Theme
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
215
240
  const theme = await ask(rl, ' Theme (dark/crow/matrix/light) [dark]: ');
216
241
  config.setTheme(theme.trim() || 'dark');
217
242
  console.log('');
@@ -222,8 +247,14 @@ async function runSetup(fromRepl = false) {
222
247
  Guard: ${guardrailPath}
223
248
 
224
249
  Type naturally to chat, or /help for commands.
225
- Submit automation requests: /automate
226
- View agent tools: /tools`));
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`));
227
258
  console.log('');
228
259
 
229
260
  rl.close();
@@ -0,0 +1,209 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const readline = require('readline');
6
+ const ui = require('../ui');
7
+ const config = require('../config');
8
+ const { style } = require('../theme');
9
+ const skills = require('../skills');
10
+
11
+ module.exports = function(reg) {
12
+
13
+ reg('skill', 'Manage agent skills — create, list, use, delete', async (args) => {
14
+ const sub = args[0];
15
+
16
+ if (!sub || sub === 'help') {
17
+ console.log(ui.header('NAVADA EDGE — SKILLS'));
18
+ console.log(ui.dim(' Skills are reusable task templates the agent follows.'));
19
+ console.log(ui.dim(' Create your own or install from templates.'));
20
+ console.log('');
21
+ console.log(ui.cmd('skill list', 'Show all installed skills'));
22
+ console.log(ui.cmd('skill create', 'Create a new skill interactively'));
23
+ console.log(ui.cmd('skill create <name>', 'Create from a template'));
24
+ console.log(ui.cmd('skill show <name>', 'View skill details'));
25
+ console.log(ui.cmd('skill use <name> [args]', 'Run a skill'));
26
+ console.log(ui.cmd('skill templates', 'Show available templates'));
27
+ console.log(ui.cmd('skill install <template>', 'Install a template skill'));
28
+ console.log(ui.cmd('skill delete <name>', 'Delete a skill'));
29
+ console.log(ui.cmd('skill edit <name>', 'Open skill in editor'));
30
+ console.log('');
31
+ console.log(ui.dim('Skills live in: ~/.navada/skills/<name>.md'));
32
+ console.log(ui.dim('The agent auto-detects skills from your conversation.'));
33
+ return;
34
+ }
35
+
36
+ // /skill list
37
+ if (sub === 'list') {
38
+ const all = skills.loadAll();
39
+ console.log(ui.header('INSTALLED SKILLS'));
40
+ if (all.length === 0) {
41
+ console.log(ui.dim('No skills installed yet.'));
42
+ console.log(ui.dim('Get started: /skill templates or /skill create'));
43
+ return;
44
+ }
45
+ for (const s of all) {
46
+ const triggers = s.trigger.length > 0 ? style('dim', ` [${s.trigger.slice(0, 3).join(', ')}]`) : '';
47
+ console.log(` ${style('accent', s.title.padEnd(24))} ${style('dim', s.description.slice(0, 50))}${triggers}`);
48
+ }
49
+ console.log('');
50
+ console.log(ui.dim(`${all.length} skill(s) installed. Use: /skill use <name>`));
51
+ return;
52
+ }
53
+
54
+ // /skill templates
55
+ if (sub === 'templates') {
56
+ console.log(ui.header('SKILL TEMPLATES'));
57
+ console.log(ui.dim(' Ready-made skills you can install in one command.'));
58
+ console.log('');
59
+ for (const [key, tmpl] of Object.entries(skills.TEMPLATES)) {
60
+ console.log(` ${style('accent', key.padEnd(20))} ${style('dim', tmpl.description)}`);
61
+ }
62
+ console.log('');
63
+ console.log(ui.dim('Install: /skill install seo-audit'));
64
+ console.log(ui.dim('Or create your own: /skill create'));
65
+ return;
66
+ }
67
+
68
+ // /skill install <template>
69
+ if (sub === 'install') {
70
+ const name = args[1];
71
+ if (!name) { console.log(ui.error('Usage: /skill install <template-name>')); return; }
72
+ const tmpl = skills.TEMPLATES[name];
73
+ if (!tmpl) {
74
+ console.log(ui.error(`Template not found: ${name}`));
75
+ console.log(ui.dim('Available: ' + Object.keys(skills.TEMPLATES).join(', ')));
76
+ return;
77
+ }
78
+ const filePath = skills.createSkill(name, tmpl);
79
+ console.log(ui.success(`Skill installed: ${tmpl.title}`));
80
+ console.log(ui.label('File', filePath));
81
+ console.log(ui.label('Triggers', tmpl.triggers.join(', ')));
82
+ console.log(ui.dim('Use it: /skill use ' + name));
83
+ return;
84
+ }
85
+
86
+ // /skill show <name>
87
+ if (sub === 'show') {
88
+ const name = args.slice(1).join(' ');
89
+ if (!name) { console.log(ui.error('Usage: /skill show <name>')); return; }
90
+ const s = skills.getSkill(name);
91
+ if (!s) { console.log(ui.error(`Skill not found: ${name}`)); return; }
92
+ console.log(ui.header(`SKILL: ${s.title}`));
93
+ console.log(ui.label('Description', s.description));
94
+ console.log(ui.label('Triggers', s.trigger.join(', ') || 'none'));
95
+ console.log(ui.label('File', s.file));
96
+ console.log('');
97
+ if (s.steps) {
98
+ console.log(ui.dim(' Steps:'));
99
+ console.log(s.steps.split('\n').map(l => ' ' + l).join('\n'));
100
+ }
101
+ if (s.output) {
102
+ console.log('');
103
+ console.log(ui.dim(' Output:'));
104
+ console.log(' ' + s.output);
105
+ }
106
+ return;
107
+ }
108
+
109
+ // /skill use <name> [context]
110
+ if (sub === 'use') {
111
+ const name = args[1];
112
+ if (!name) { console.log(ui.error('Usage: /skill use <name> [context]')); return; }
113
+ const s = skills.getSkill(name);
114
+ if (!s) { console.log(ui.error(`Skill not found: ${name}`)); return; }
115
+
116
+ const context = args.slice(2).join(' ');
117
+ const prompt = `Execute this skill:\n\n${s.raw}\n\n${context ? `User context: ${context}` : 'Run the skill now.'}`;
118
+
119
+ // Route to chat
120
+ try {
121
+ const { chat, addToHistory } = require('../agent');
122
+ addToHistory('user', prompt);
123
+ console.log(ui.dim(` Running skill: ${s.title}...`));
124
+ console.log('');
125
+ const response = await chat(prompt);
126
+ if (response) addToHistory('assistant', response);
127
+ } catch (e) {
128
+ console.log(ui.error(`Skill execution failed: ${e.message}`));
129
+ }
130
+ return;
131
+ }
132
+
133
+ // /skill create (interactive)
134
+ if (sub === 'create') {
135
+ // If a template name is given, install it
136
+ if (args[1] && skills.TEMPLATES[args[1]]) {
137
+ const tmpl = skills.TEMPLATES[args[1]];
138
+ const filePath = skills.createSkill(args[1], tmpl);
139
+ console.log(ui.success(`Skill created from template: ${tmpl.title}`));
140
+ console.log(ui.label('File', filePath));
141
+ return;
142
+ }
143
+
144
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
145
+ const ask = (q) => new Promise(resolve => rl.question(q, resolve));
146
+
147
+ console.log(ui.header('CREATE A SKILL'));
148
+ console.log(ui.dim(' A skill is a reusable task the agent can follow.'));
149
+ console.log('');
150
+
151
+ const title = await ask(' Skill name: ');
152
+ if (!title.trim()) { console.log(ui.error('Name is required.')); rl.close(); return; }
153
+
154
+ const description = await ask(' Description: ');
155
+ const triggersRaw = await ask(' Trigger phrases (comma-separated): ');
156
+ const steps = await ask(' Steps (or press Enter for default): ');
157
+ const output = await ask(' Expected output: ');
158
+
159
+ const data = {
160
+ title: title.trim(),
161
+ description: description.trim() || title.trim(),
162
+ triggers: triggersRaw.split(',').map(t => t.trim().toLowerCase()).filter(Boolean),
163
+ steps: steps.trim() || undefined,
164
+ output: output.trim() || undefined,
165
+ };
166
+
167
+ const filePath = skills.createSkill(title.trim(), data);
168
+ console.log('');
169
+ console.log(ui.success(`Skill created: ${data.title}`));
170
+ console.log(ui.label('File', filePath));
171
+ console.log(ui.dim('Edit it: /skill edit ' + path.basename(filePath, '.md')));
172
+ console.log(ui.dim('Use it: /skill use ' + path.basename(filePath, '.md')));
173
+ rl.close();
174
+ return;
175
+ }
176
+
177
+ // /skill delete <name>
178
+ if (sub === 'delete' || sub === 'rm') {
179
+ const name = args.slice(1).join(' ');
180
+ if (!name) { console.log(ui.error('Usage: /skill delete <name>')); return; }
181
+ if (skills.deleteSkill(name)) {
182
+ console.log(ui.success(`Deleted skill: ${name}`));
183
+ } else {
184
+ console.log(ui.error(`Skill not found: ${name}`));
185
+ }
186
+ return;
187
+ }
188
+
189
+ // /skill edit <name>
190
+ if (sub === 'edit') {
191
+ const name = args.slice(1).join(' ');
192
+ if (!name) { console.log(ui.error('Usage: /skill edit <name>')); return; }
193
+ const s = skills.getSkill(name);
194
+ if (!s) { console.log(ui.error(`Skill not found: ${name}`)); return; }
195
+ try {
196
+ const { exec } = require('child_process');
197
+ const editor = process.env.EDITOR || (process.platform === 'win32' ? 'notepad' : 'nano');
198
+ exec(`${editor} "${s.file}"`);
199
+ console.log(ui.success(`Opening ${s.file} in ${editor}...`));
200
+ } catch {
201
+ console.log(ui.label('File', s.file));
202
+ console.log(ui.dim('Open this file in your editor.'));
203
+ }
204
+ return;
205
+ }
206
+
207
+ console.log(ui.dim('Unknown subcommand. Try /skill help'));
208
+ }, { category: 'SKILLS', subs: ['list', 'create', 'show', 'use', 'templates', 'install', 'delete', 'edit', 'help'], aliases: ['skills'] });
209
+ };
@@ -441,6 +441,71 @@ module.exports = function(reg) {
441
441
  console.log(ui.dim('For automation setup on 24/7 cloud: /automate'));
442
442
  }, { category: 'SYSTEM' });
443
443
 
444
+ // --- /about ---
445
+ reg('about', 'About the NAVADA Edge agent and network', () => {
446
+ console.log(ui.header('NAVADA EDGE'));
447
+ console.log('');
448
+ console.log(' ' + style('accent', 'Name') + ' NAVADA Edge');
449
+ console.log(' ' + style('accent', 'Born') + ' December 2024, London, United Kingdom');
450
+ console.log(' ' + style('accent', 'Version') + ' v' + require('../../package.json').version);
451
+ console.log(' ' + style('accent', 'Species') + ' AI Terminal Agent');
452
+ console.log(' ' + style('accent', 'Purpose') + ' Make AI accessible from the command line');
453
+ console.log('');
454
+ console.log(ui.dim(' ── ORIGIN STORY ──'));
455
+ console.log('');
456
+ console.log(ui.dim(' NAVADA was born from a simple idea: what if your terminal'));
457
+ console.log(ui.dim(' understood you? Not just commands — but context, memory,'));
458
+ console.log(ui.dim(' and intent. Built by Lee Akpareva, a Principal AI Consultant'));
459
+ console.log(ui.dim(' with 17+ years in enterprise IT, NAVADA started as a home'));
460
+ console.log(ui.dim(' server experiment and grew into a distributed AI network'));
461
+ console.log(ui.dim(' spanning 5 nodes across 3 countries.'));
462
+ console.log('');
463
+ console.log(ui.dim(' ── WHAT I AM ──'));
464
+ console.log('');
465
+ console.log(ui.dim(' I am an AI agent that lives in your terminal. I have 16 tools,'));
466
+ console.log(ui.dim(' a 3-tier memory system, and I learn who you are over time.'));
467
+ console.log(ui.dim(' I can write code, analyse data, manage files, take screenshots,'));
468
+ console.log(ui.dim(' search the web, and remember everything we discuss.'));
469
+ console.log('');
470
+ console.log(ui.dim(' I support 5 AI providers — bring your own model or use the'));
471
+ console.log(ui.dim(' free NVIDIA tier. Every provider gets full tool access.'));
472
+ console.log('');
473
+ console.log(ui.dim(' ── THE NETWORK ──'));
474
+ console.log('');
475
+ console.log(ui.dim(' NAVADA Edge Network is a distributed computing platform:'));
476
+ console.log(ui.dim(' • 5 nodes connected via encrypted Tailscale VPN'));
477
+ console.log(ui.dim(' • 29+ Docker containers across AWS, Azure, Oracle Cloud'));
478
+ console.log(ui.dim(' • Cloudflare tunnel with 13 subdomains'));
479
+ console.log(ui.dim(' • 24/7 monitoring, health checks, auto-restart'));
480
+ console.log(ui.dim(' • Automation pipeline for user-requested tasks'));
481
+ console.log('');
482
+ console.log(ui.dim(' ── PHILOSOPHY ──'));
483
+ console.log('');
484
+ console.log(ui.dim(' The terminal is the foundation of computing. Every server,'));
485
+ console.log(ui.dim(' every cloud platform, every CI/CD pipeline runs on text'));
486
+ console.log(ui.dim(' commands. NAVADA doesn\'t replace the terminal — it makes'));
487
+ console.log(ui.dim(' it conversational. You describe what you want, the agent'));
488
+ console.log(ui.dim(' figures out how to do it.'));
489
+ console.log('');
490
+ console.log(ui.dim(' ── FOUNDER ──'));
491
+ console.log('');
492
+ console.log(' ' + style('accent', 'Leslie (Lee) Akpareva'));
493
+ console.log(ui.dim(' Principal AI Consultant | MBA, MA | EF8 Alumni'));
494
+ console.log(ui.dim(' 17+ years: enterprise IT, insurance, AI infrastructure'));
495
+ console.log(ui.dim(' Currently: AI Project Lead at Generali UK'));
496
+ console.log('');
497
+ console.log(ui.dim(' github.com/leeakpareva'));
498
+ console.log(ui.dim(' navada-lab.space'));
499
+ console.log('');
500
+ console.log(ui.dim(' ── LINKS ──'));
501
+ console.log('');
502
+ console.log(ui.label('Portal', 'https://portal.navada-edge-server.uk'));
503
+ console.log(ui.label('npm', 'npmjs.com/package/navada-edge-cli'));
504
+ console.log(ui.label('SDK', 'npmjs.com/package/navada-edge-sdk'));
505
+ console.log(ui.label('GitHub', 'github.com/Navada25/edge-sdk'));
506
+ console.log('');
507
+ }, { category: 'SYSTEM', aliases: ['info', 'whoami'] });
508
+
444
509
  // --- /clear ---
445
510
  reg('clear', 'Clear screen', () => {
446
511
  console.clear();
package/lib/skills.js ADDED
@@ -0,0 +1,222 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const config = require('./config');
6
+
7
+ const SKILLS_DIR = path.join(config.CONFIG_DIR, 'skills');
8
+
9
+ function ensureDir() {
10
+ if (!fs.existsSync(SKILLS_DIR)) fs.mkdirSync(SKILLS_DIR, { recursive: true });
11
+ }
12
+
13
+ // Parse a skill markdown file into structured data
14
+ function parseSkill(filePath) {
15
+ try {
16
+ const raw = fs.readFileSync(filePath, 'utf-8');
17
+ const lines = raw.split('\n');
18
+ const skill = {
19
+ name: path.basename(filePath, '.md'),
20
+ file: filePath,
21
+ title: '',
22
+ trigger: [],
23
+ description: '',
24
+ steps: '',
25
+ output: '',
26
+ raw,
27
+ };
28
+
29
+ let section = 'header';
30
+ const sectionContent = { steps: [], output: [] };
31
+
32
+ for (const line of lines) {
33
+ const trimmed = line.trim();
34
+
35
+ // Title
36
+ if (trimmed.startsWith('# ') && !skill.title) {
37
+ skill.title = trimmed.slice(2).trim();
38
+ continue;
39
+ }
40
+
41
+ // Metadata lines
42
+ if (trimmed.startsWith('trigger:')) {
43
+ skill.trigger = trimmed.slice(8).split(',').map(t => t.trim().replace(/"/g, '').toLowerCase()).filter(Boolean);
44
+ continue;
45
+ }
46
+ if (trimmed.startsWith('description:')) {
47
+ skill.description = trimmed.slice(12).trim();
48
+ continue;
49
+ }
50
+
51
+ // Section headers
52
+ if (trimmed.startsWith('## Steps')) { section = 'steps'; continue; }
53
+ if (trimmed.startsWith('## Output')) { section = 'output'; continue; }
54
+ if (trimmed.startsWith('## ')) { section = 'other'; continue; }
55
+
56
+ if (section === 'steps') sectionContent.steps.push(line);
57
+ if (section === 'output') sectionContent.output.push(line);
58
+ }
59
+
60
+ skill.steps = sectionContent.steps.join('\n').trim();
61
+ skill.output = sectionContent.output.join('\n').trim();
62
+ if (!skill.title) skill.title = skill.name;
63
+ if (!skill.description) skill.description = skill.title;
64
+
65
+ return skill;
66
+ } catch (e) {
67
+ return null;
68
+ }
69
+ }
70
+
71
+ // Load all skills
72
+ function loadAll() {
73
+ ensureDir();
74
+ try {
75
+ const files = fs.readdirSync(SKILLS_DIR).filter(f => f.endsWith('.md'));
76
+ return files.map(f => parseSkill(path.join(SKILLS_DIR, f))).filter(Boolean);
77
+ } catch { return []; }
78
+ }
79
+
80
+ // Find a skill by trigger phrase
81
+ function matchSkill(input) {
82
+ const lower = input.toLowerCase();
83
+ const skills = loadAll();
84
+
85
+ for (const skill of skills) {
86
+ for (const trigger of skill.trigger) {
87
+ if (lower.includes(trigger)) return skill;
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+
93
+ // Get skill by name
94
+ function getSkill(name) {
95
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-');
96
+ const filePath = path.join(SKILLS_DIR, `${slug}.md`);
97
+ if (fs.existsSync(filePath)) return parseSkill(filePath);
98
+
99
+ // Try partial match
100
+ ensureDir();
101
+ const files = fs.readdirSync(SKILLS_DIR).filter(f => f.endsWith('.md'));
102
+ const match = files.find(f => f.toLowerCase().includes(slug));
103
+ if (match) return parseSkill(path.join(SKILLS_DIR, match));
104
+ return null;
105
+ }
106
+
107
+ // Create a new skill from structured data
108
+ function createSkill(name, data) {
109
+ ensureDir();
110
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-');
111
+ const filePath = path.join(SKILLS_DIR, `${slug}.md`);
112
+
113
+ const content = `# ${data.title || name}
114
+ trigger: ${(data.triggers || []).map(t => `"${t}"`).join(', ')}
115
+ description: ${data.description || ''}
116
+
117
+ ## Steps
118
+ ${data.steps || '1. Analyse the request\n2. Execute the task\n3. Return the result'}
119
+
120
+ ## Output
121
+ ${data.output || 'Formatted result based on the task'}
122
+ `;
123
+
124
+ fs.writeFileSync(filePath, content);
125
+ return filePath;
126
+ }
127
+
128
+ // Delete a skill
129
+ function deleteSkill(name) {
130
+ const skill = getSkill(name);
131
+ if (!skill) return false;
132
+ try {
133
+ fs.unlinkSync(skill.file);
134
+ return true;
135
+ } catch { return false; }
136
+ }
137
+
138
+ // Get skills summary for system prompt injection
139
+ function getSkillsPrompt() {
140
+ const skills = loadAll();
141
+ if (skills.length === 0) return '';
142
+
143
+ const lines = ['Available user skills (invoke when request matches):'];
144
+ for (const s of skills) {
145
+ lines.push(`- "${s.title}": ${s.description}. Triggers: ${s.trigger.join(', ') || 'manual'}. Steps: ${s.steps.slice(0, 150)}`);
146
+ }
147
+ return lines.join('\n');
148
+ }
149
+
150
+ // Built-in skill templates
151
+ const TEMPLATES = {
152
+ 'seo-audit': {
153
+ title: 'SEO Audit',
154
+ triggers: ['seo audit', 'check seo', 'analyse website seo'],
155
+ description: 'Run a comprehensive SEO audit on any URL',
156
+ steps: `1. Use shell to curl the target URL and capture HTML
157
+ 2. Use python_exec to parse HTML — extract title, meta description, h1-h6 tags, img alt attributes, internal/external links
158
+ 3. Check: title length (50-60 chars), meta description (150-160 chars), heading hierarchy, missing alt text, broken links
159
+ 4. Score each category out of 10
160
+ 5. Generate a markdown report with scores and actionable recommendations`,
161
+ output: 'Markdown report: overall score, category breakdown, top 5 fixes',
162
+ },
163
+ 'email-template': {
164
+ title: 'Marketing Email',
165
+ triggers: ['marketing email', 'email template', 'write email campaign'],
166
+ description: 'Generate a professional marketing email template',
167
+ steps: `1. Ask for: target audience, product/service, tone, call-to-action
168
+ 2. Write subject line (under 50 chars, no spam words)
169
+ 3. Write preview text (90 chars)
170
+ 4. Write email body: hook, value proposition, social proof, CTA
171
+ 5. Save as HTML file with inline CSS for email client compatibility`,
172
+ output: 'HTML email template file + plain text version',
173
+ },
174
+ 'api-scaffold': {
175
+ title: 'REST API Scaffold',
176
+ triggers: ['scaffold api', 'create api', 'generate api project'],
177
+ description: 'Generate a complete REST API project with routes, models, and tests',
178
+ steps: `1. Ask for: language (Node/Python/Go), database, entity names
179
+ 2. Create project directory with standard structure
180
+ 3. Generate: package.json/requirements.txt, entry point, routes, models, middleware
181
+ 4. Add: error handling, validation, health endpoint, CORS
182
+ 5. Generate: Dockerfile, .env.example, README with API docs
183
+ 6. Run initial install and verify it starts`,
184
+ output: 'Complete project directory, ready to run',
185
+ },
186
+ 'git-pr': {
187
+ title: 'Git PR Creator',
188
+ triggers: ['create pr', 'pull request', 'git pr'],
189
+ description: 'Analyse changes and create a well-documented pull request',
190
+ steps: `1. Run git diff to see all changes
191
+ 2. Run git log to see commit history since branch point
192
+ 3. Categorise changes: features, fixes, refactors, tests
193
+ 4. Write PR title (under 72 chars, conventional commit style)
194
+ 5. Write PR body: summary, changes list, testing notes, screenshots if UI
195
+ 6. Use shell to create the PR via gh cli`,
196
+ output: 'PR created with full description and labels',
197
+ },
198
+ 'data-report': {
199
+ title: 'Data Report Generator',
200
+ triggers: ['analyse data', 'data report', 'generate report from'],
201
+ description: 'Analyse a data file and produce a visual report',
202
+ steps: `1. Read the data file (CSV, JSON, Excel)
203
+ 2. Use python_exec with pandas: shape, dtypes, null counts, describe()
204
+ 3. Identify key patterns: trends, outliers, correlations
205
+ 4. Generate visualisations with matplotlib (save as PNG)
206
+ 5. Write a markdown summary report with embedded charts
207
+ 6. Save report as HTML for easy sharing`,
208
+ output: 'HTML report with charts, summary statistics, and insights',
209
+ },
210
+ };
211
+
212
+ module.exports = {
213
+ SKILLS_DIR,
214
+ loadAll,
215
+ matchSkill,
216
+ getSkill,
217
+ createSkill,
218
+ deleteSkill,
219
+ parseSkill,
220
+ getSkillsPrompt,
221
+ TEMPLATES,
222
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "navada-edge-cli",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "AI agent in your terminal — 3-tier memory, 16 tools, automation pipeline, bring your own model. Learns who you are, remembers across sessions.",
5
5
  "main": "lib/cli.js",
6
6
  "bin": {