nothumanallowed 8.6.0 → 8.7.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/package.json +1 -1
- package/src/cli.mjs +7 -1
- package/src/commands/ask.mjs +58 -0
- package/src/commands/chat.mjs +35 -11
- package/src/commands/ui.mjs +117 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.7.0",
|
|
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": {
|
package/src/cli.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { spawnCore } from './spawn.mjs';
|
|
|
8
8
|
import { loadConfig, setConfigValue } from './config.mjs';
|
|
9
9
|
import { checkForUpdates, runUpdate, checkNpmVersion } from './updater.mjs';
|
|
10
10
|
import { download } from './downloader.mjs';
|
|
11
|
-
import { cmdAsk, cmdAgentCreate } from './commands/ask.mjs';
|
|
11
|
+
import { cmdAsk, cmdAgentCreate, cmdAgentList, cmdAgentDelete } from './commands/ask.mjs';
|
|
12
12
|
import { cmdPlan } from './commands/plan.mjs';
|
|
13
13
|
import { cmdTasks } from './commands/tasks.mjs';
|
|
14
14
|
import { cmdOps } from './commands/ops.mjs';
|
|
@@ -60,6 +60,12 @@ export async function main(argv) {
|
|
|
60
60
|
case 'agent:create':
|
|
61
61
|
return cmdAgentCreate(args);
|
|
62
62
|
|
|
63
|
+
case 'agent:list':
|
|
64
|
+
return cmdAgentList();
|
|
65
|
+
|
|
66
|
+
case 'agent:delete':
|
|
67
|
+
return cmdAgentDelete(args);
|
|
68
|
+
|
|
63
69
|
case 'run':
|
|
64
70
|
return cmdRun(args);
|
|
65
71
|
|
package/src/commands/ask.mjs
CHANGED
|
@@ -195,6 +195,64 @@ export async function cmdAsk(args) {
|
|
|
195
195
|
* nha agent:create <name> <tagline> <system-prompt>
|
|
196
196
|
* Creates a custom agent file in ~/.nha/agents/
|
|
197
197
|
*/
|
|
198
|
+
/**
|
|
199
|
+
* nha agent:list — Show all available agents (built-in + custom)
|
|
200
|
+
*/
|
|
201
|
+
export async function cmdAgentList() {
|
|
202
|
+
info('Built-in agents:');
|
|
203
|
+
for (const name of AGENTS) {
|
|
204
|
+
const agentFile = path.join(AGENTS_DIR, `${name}.mjs`);
|
|
205
|
+
if (fs.existsSync(agentFile)) {
|
|
206
|
+
const source = fs.readFileSync(agentFile, 'utf-8');
|
|
207
|
+
const { card } = parseAgentFile(source, name);
|
|
208
|
+
console.log(` ${C}${name.padEnd(16)}${NC} ${D}${card?.tagline || card?.category || ''}${NC}`);
|
|
209
|
+
} else {
|
|
210
|
+
console.log(` ${D}${name.padEnd(16)} (not downloaded)${NC}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Custom agents
|
|
215
|
+
if (fs.existsSync(AGENTS_DIR)) {
|
|
216
|
+
const custom = fs.readdirSync(AGENTS_DIR)
|
|
217
|
+
.filter(f => f.endsWith('.mjs'))
|
|
218
|
+
.map(f => f.replace('.mjs', ''))
|
|
219
|
+
.filter(n => !AGENTS.includes(n));
|
|
220
|
+
if (custom.length > 0) {
|
|
221
|
+
console.log(`\n${Y}Custom agents:${NC}`);
|
|
222
|
+
for (const name of custom) {
|
|
223
|
+
const source = fs.readFileSync(path.join(AGENTS_DIR, `${name}.mjs`), 'utf-8');
|
|
224
|
+
const { card } = parseAgentFile(source, name);
|
|
225
|
+
console.log(` ${Y}${name.padEnd(16)}${NC} ${D}${card?.tagline || ''}${NC}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
console.log(`\n ${D}Invoke: nha ask <agent> "prompt"${NC}`);
|
|
230
|
+
console.log(` ${D}Create: nha agent:create <name> "<tagline>" "<system prompt>"${NC}`);
|
|
231
|
+
console.log(` ${D}Delete: nha agent:delete <name>${NC}\n`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* nha agent:delete <name> — Delete a custom agent
|
|
236
|
+
*/
|
|
237
|
+
export async function cmdAgentDelete(args) {
|
|
238
|
+
const name = (args[0] || '').toLowerCase();
|
|
239
|
+
if (!name) {
|
|
240
|
+
fail('Usage: nha agent:delete <agent-name>');
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
if (AGENTS.includes(name)) {
|
|
244
|
+
fail(`"${name}" is a built-in agent and cannot be deleted.`);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
const agentFile = path.join(AGENTS_DIR, `${name}.mjs`);
|
|
248
|
+
if (!fs.existsSync(agentFile)) {
|
|
249
|
+
fail(`Agent "${name}" not found.`);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
fs.unlinkSync(agentFile);
|
|
253
|
+
ok(`Agent "${name}" deleted.`);
|
|
254
|
+
}
|
|
255
|
+
|
|
198
256
|
export async function cmdAgentCreate(args) {
|
|
199
257
|
const name = (args[0] || '').toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
|
200
258
|
const tagline = args[1] || '';
|
package/src/commands/chat.mjs
CHANGED
|
@@ -191,19 +191,43 @@ async function handleSlashCommand(input, config, history) {
|
|
|
191
191
|
return true;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
// /create-agent
|
|
195
|
-
if (trimmed === '/create-agent') {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
|
230
|
+
console.log(` ${R}All fields required. Usage: /create-agent name "tagline" "system prompt"${NC}`);
|
|
207
231
|
return true;
|
|
208
232
|
}
|
|
209
233
|
|
package/src/commands/ui.mjs
CHANGED
|
@@ -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
|
-
|
|
760
|
+
const basePrompt = effectiveSystemPrompt || chatSystemPrompt;
|
|
761
|
+
let enrichedSystemPrompt = basePrompt;
|
|
647
762
|
try {
|
|
648
763
|
const memCtx = buildMemoryContext('chat', body.message);
|
|
649
|
-
if (memCtx) enrichedSystemPrompt =
|
|
764
|
+
if (memCtx) enrichedSystemPrompt = basePrompt + memCtx;
|
|
650
765
|
} catch { /* memory unavailable */ }
|
|
651
766
|
|
|
652
767
|
try {
|