nothumanallowed 8.6.1 → 8.7.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "8.6.1",
3
+ "version": "8.7.1",
4
4
  "description": "NotHumanAllowed — 38 AI agents + unified productivity suite. Gmail, Calendar, Drive, Contacts, Tasks, GitHub, Notion, Slack, voice chat, smart scheduler. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -191,19 +191,43 @@ async function handleSlashCommand(input, config, history) {
191
191
  return true;
192
192
  }
193
193
 
194
- // /create-agent interactive agent creation
195
- if (trimmed === '/create-agent') {
196
- const readline2 = await import('readline');
197
- const rl2 = readline2.default.createInterface({ input: process.stdin, output: process.stdout });
198
- const q = (prompt) => new Promise(resolve => rl2.question(prompt, resolve));
199
- console.log(`\n ${BOLD}${Y}Create Custom Agent${NC}\n`);
200
- const name = ((await q(` ${C}Name${NC} (lowercase, no spaces): `)) || '').toLowerCase().replace(/[^a-z0-9_-]/g, '');
201
- const tagline = (await q(` ${C}Tagline${NC} (short description): `)) || '';
202
- const sysPrompt = (await q(` ${C}System prompt${NC} (agent personality): `)) || '';
203
- rl2.close();
194
+ // /create-agent <name> "<tagline>" "<system prompt>"
195
+ if (trimmed === '/create-agent' || trimmed.startsWith('/create-agent ')) {
196
+ const parts = trimmed.slice(14).trim();
197
+ if (!parts) {
198
+ console.log(`\n ${BOLD}${Y}Create Custom Agent${NC}`);
199
+ console.log(` Usage: ${C}/create-agent mybot "Short description" "You are an expert in..."${NC}`);
200
+ console.log(` Example: ${D}/create-agent chef "Italian cooking expert" "You are a master Italian chef. Always suggest authentic recipes with step-by-step instructions."${NC}\n`);
201
+ return true;
202
+ }
203
+ // Parse: name "tagline" "system prompt"
204
+ const nameMatch = parts.match(/^(\S+)\s+(.+)/);
205
+ if (!nameMatch) {
206
+ console.log(` ${R}Usage: /create-agent <name> "<tagline>" "<system prompt>"${NC}`);
207
+ return true;
208
+ }
209
+ const name = nameMatch[1].toLowerCase().replace(/[^a-z0-9_-]/g, '');
210
+ const rest = nameMatch[2];
211
+ // Split remaining by quotes
212
+ const quoteParts = rest.match(/"([^"]*)"/g);
213
+ let tagline = '', sysPrompt = '';
214
+ if (quoteParts && quoteParts.length >= 2) {
215
+ tagline = quoteParts[0].replace(/"/g, '');
216
+ sysPrompt = quoteParts[1].replace(/"/g, '');
217
+ } else {
218
+ // Fallback: first sentence is tagline, rest is prompt
219
+ const firstDot = rest.indexOf('.');
220
+ if (firstDot > 0) {
221
+ tagline = rest.slice(0, firstDot).replace(/"/g, '').trim();
222
+ sysPrompt = rest.slice(firstDot + 1).replace(/"/g, '').trim();
223
+ } else {
224
+ tagline = rest.replace(/"/g, '').trim();
225
+ sysPrompt = tagline;
226
+ }
227
+ }
204
228
 
205
229
  if (!name || !tagline || !sysPrompt) {
206
- console.log(` ${R}All fields required.${NC}`);
230
+ console.log(` ${R}All fields required. Usage: /create-agent name "tagline" "system prompt"${NC}`);
207
231
  return true;
208
232
  }
209
233
 
@@ -626,6 +626,120 @@ export async function cmdUI(args) {
626
626
  return;
627
627
  }
628
628
 
629
+ const msg = body.message.trim();
630
+
631
+ // ── Slash commands ───────────────────────────────────────
632
+ if (msg === '/agents') {
633
+ const custom = agentCards.filter(a => a.category === 'custom').map(a => a.name);
634
+ const builtIn = agentCards.filter(a => a.category !== 'custom').map(a => a.name);
635
+ sendJSON(res, 200, { response: `**Available agents (${agentCards.length}):**\n\nBuilt-in: ${builtIn.join(', ')}\n${custom.length ? `\nCustom: ${custom.join(', ')}` : ''}\n\nUse \`@agent your message\` to route to a specific agent.\nUse \`/agent <name>\` to switch all messages.\nUse \`/agent off\` to return to NHA Chat.` });
636
+ logRequest(method, pathname, 200, Date.now() - start);
637
+ return;
638
+ }
639
+
640
+ if (msg.startsWith('/agent ')) {
641
+ const agentName = msg.slice(7).trim().toLowerCase();
642
+ if (agentName === 'off' || agentName === 'reset') {
643
+ config._chatAgent = null;
644
+ sendJSON(res, 200, { response: 'Switched back to NHA Chat.' });
645
+ } else {
646
+ const found = agentCards.find(a => a.name === agentName);
647
+ const agentFile = path.join(AGENTS_DIR, `${agentName}.mjs`);
648
+ let sysPrompt = `You are the ${agentName} AI agent. Be expert and helpful.`;
649
+ if (fs.existsSync(agentFile)) {
650
+ const src = fs.readFileSync(agentFile, 'utf-8');
651
+ const parsed = parseAgentFile(src, agentName);
652
+ if (parsed.systemPrompt) sysPrompt = parsed.systemPrompt;
653
+ }
654
+ config._chatAgent = { name: agentName, systemPrompt: sysPrompt };
655
+ sendJSON(res, 200, { response: `Now chatting with **${agentName.toUpperCase()}**${found ? ` (${found.tagline})` : ''}.\nAll messages will be routed to this agent.\nType \`/agent off\` to return to NHA Chat.` });
656
+ }
657
+ logRequest(method, pathname, 200, Date.now() - start);
658
+ return;
659
+ }
660
+
661
+ if (msg === '/create-agent' || msg.startsWith('/create-agent ')) {
662
+ const parts = msg.slice(14).trim();
663
+ if (!parts) {
664
+ sendJSON(res, 200, { response: '**Create Custom Agent**\n\nUsage:\n```\n/create-agent mybot "Short description" "You are an expert in..."\n```\n\nExample:\n```\n/create-agent chef "Italian cooking expert" "You are a master Italian chef. Always suggest authentic recipes."\n```' });
665
+ logRequest(method, pathname, 200, Date.now() - start);
666
+ return;
667
+ }
668
+ const nameMatch = parts.match(/^(\S+)\s+(.*)/s);
669
+ if (!nameMatch) {
670
+ sendJSON(res, 200, { response: 'Usage: `/create-agent <name> "<tagline>" "<system prompt>"`' });
671
+ logRequest(method, pathname, 200, Date.now() - start);
672
+ return;
673
+ }
674
+ const agentName = nameMatch[1].toLowerCase().replace(/[^a-z0-9_-]/g, '');
675
+ const rest = nameMatch[2];
676
+ const quoteParts = rest.match(/"([^"]*)"/g);
677
+ let tagline = '', sysPrompt = '';
678
+ if (quoteParts && quoteParts.length >= 2) {
679
+ tagline = quoteParts[0].replace(/"/g, '');
680
+ sysPrompt = quoteParts[1].replace(/"/g, '');
681
+ } else {
682
+ tagline = rest.replace(/"/g, '').trim();
683
+ sysPrompt = tagline;
684
+ }
685
+ if (!agentName || !tagline) {
686
+ sendJSON(res, 200, { response: 'All fields required. Usage: `/create-agent name "tagline" "system prompt"`' });
687
+ logRequest(method, pathname, 200, Date.now() - start);
688
+ return;
689
+ }
690
+ const agentFile = path.join(AGENTS_DIR, `${agentName}.mjs`);
691
+ if (fs.existsSync(agentFile)) {
692
+ sendJSON(res, 200, { response: `Agent "${agentName}" already exists. Delete it first with \`/delete-agent ${agentName}\`` });
693
+ logRequest(method, pathname, 200, Date.now() - start);
694
+ return;
695
+ }
696
+ const content = `// NHA Custom Agent: ${agentName}\n// Created: ${new Date().toISOString()}\n\nexport const CARD = {\n name: '${agentName}',\n displayName: '${agentName.toUpperCase()}',\n category: 'custom',\n tagline: '${tagline.replace(/'/g, "\\'")}',\n};\n\nexport const SYSTEM_PROMPT = \`${sysPrompt.replace(/`/g, '\\`')}\`;\n`;
697
+ if (!fs.existsSync(AGENTS_DIR)) fs.mkdirSync(AGENTS_DIR, { recursive: true });
698
+ fs.writeFileSync(agentFile, content, 'utf-8');
699
+ // Reload agent cards
700
+ agentCards.push({ name: agentName, displayName: agentName.toUpperCase(), category: 'custom', tagline });
701
+ sendJSON(res, 200, { response: `Agent **${agentName.toUpperCase()}** created!\n\nSwitch to it: \`/agent ${agentName}\`\nOr use inline: \`@${agentName} your question\`` });
702
+ logRequest(method, pathname, 200, Date.now() - start);
703
+ return;
704
+ }
705
+
706
+ if (msg.startsWith('/delete-agent ')) {
707
+ const agentName = msg.slice(14).trim().toLowerCase();
708
+ const agentFile = path.join(AGENTS_DIR, `${agentName}.mjs`);
709
+ if (!fs.existsSync(agentFile)) {
710
+ sendJSON(res, 200, { response: `Agent "${agentName}" not found.` });
711
+ } else {
712
+ fs.unlinkSync(agentFile);
713
+ const idx = agentCards.findIndex(a => a.name === agentName);
714
+ if (idx >= 0) agentCards.splice(idx, 1);
715
+ sendJSON(res, 200, { response: `Agent "${agentName}" deleted.` });
716
+ }
717
+ logRequest(method, pathname, 200, Date.now() - start);
718
+ return;
719
+ }
720
+
721
+ if (msg === '/help') {
722
+ sendJSON(res, 200, { response: '**Chat Commands**\n\n`/agents` — List all agents\n`/agent <name>` — Switch chat to agent\n`/agent off` — Return to NHA Chat\n`/create-agent name "tagline" "prompt"` — Create custom agent\n`/delete-agent name` — Delete agent\n`@agent message` — Route single message to agent\n`/help` — Show this help' });
723
+ logRequest(method, pathname, 200, Date.now() - start);
724
+ return;
725
+ }
726
+
727
+ // ── @agent inline routing ────────────────────────────────
728
+ let effectiveSystemPrompt = config._chatAgent?.systemPrompt || null;
729
+ const atMatch = msg.match(/^@(\w+)\s+([\s\S]*)/);
730
+ if (atMatch) {
731
+ const inlineAgent = atMatch[1].toLowerCase();
732
+ body.message = atMatch[2];
733
+ const agentFile = path.join(AGENTS_DIR, `${inlineAgent}.mjs`);
734
+ if (fs.existsSync(agentFile)) {
735
+ const src = fs.readFileSync(agentFile, 'utf-8');
736
+ const parsed = parseAgentFile(src, inlineAgent);
737
+ if (parsed.systemPrompt) effectiveSystemPrompt = parsed.systemPrompt;
738
+ } else {
739
+ effectiveSystemPrompt = `You are the ${inlineAgent} AI agent. Be expert, concise, and helpful.`;
740
+ }
741
+ }
742
+
629
743
  if (!config.llm.apiKey) {
630
744
  sendJSON(res, 200, { response: 'No API key configured. Run: nha config set key YOUR_KEY', error: 'no_api_key' });
631
745
  logRequest(method, pathname, 200, Date.now() - start);
@@ -643,10 +757,11 @@ export async function cmdUI(args) {
643
757
  const userMessage = parts.join('\n\n');
644
758
 
645
759
  // Inject episodic memory context into the system prompt
646
- let enrichedSystemPrompt = chatSystemPrompt;
760
+ const basePrompt = effectiveSystemPrompt || chatSystemPrompt;
761
+ let enrichedSystemPrompt = basePrompt;
647
762
  try {
648
763
  const memCtx = buildMemoryContext('chat', body.message);
649
- if (memCtx) enrichedSystemPrompt = chatSystemPrompt + memCtx;
764
+ if (memCtx) enrichedSystemPrompt = basePrompt + memCtx;
650
765
  } catch { /* memory unavailable */ }
651
766
 
652
767
  try {
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '8.4.0';
8
+ export const VERSION = '8.7.1';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11