nothumanallowed 1.1.0 → 2.1.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 +40 -0
- package/package.json +2 -2
- package/src/cli.mjs +39 -2
- package/src/commands/ask.mjs +1 -300
- package/src/commands/chat.mjs +730 -0
- package/src/commands/google-auth.mjs +29 -0
- package/src/commands/ops.mjs +77 -0
- package/src/commands/plan.mjs +45 -0
- package/src/commands/tasks.mjs +132 -0
- package/src/config.mjs +27 -0
- package/src/constants.mjs +1 -1
- package/src/services/google-calendar.mjs +216 -0
- package/src/services/google-gmail.mjs +242 -0
- package/src/services/google-oauth.mjs +272 -0
- package/src/services/llm.mjs +319 -0
- package/src/services/notification.mjs +109 -0
- package/src/services/ops-daemon.mjs +247 -0
- package/src/services/ops-pipeline.mjs +281 -0
- package/src/services/task-store.mjs +156 -0
- package/src/services/token-store.mjs +145 -0
package/README.md
CHANGED
|
@@ -20,6 +20,46 @@ nha ask oracle "Analyze this dataset" --file data.csv
|
|
|
20
20
|
nha run "Design a Kubernetes deployment for a 10K RPS API"
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Daily Operations (PAO)
|
|
24
|
+
|
|
25
|
+
Connect Gmail + Calendar. 5 specialist agents analyze your day.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Connect Google (one-time)
|
|
29
|
+
nha config set google-client-id YOUR_ID
|
|
30
|
+
nha config set google-client-secret YOUR_SECRET
|
|
31
|
+
nha google auth
|
|
32
|
+
|
|
33
|
+
# Generate your daily plan
|
|
34
|
+
nha plan
|
|
35
|
+
|
|
36
|
+
# Manage tasks
|
|
37
|
+
nha tasks add "Review PR #42" --priority high
|
|
38
|
+
nha tasks done 1
|
|
39
|
+
nha tasks week
|
|
40
|
+
|
|
41
|
+
# Background daemon (auto-alerts before meetings, email security scans)
|
|
42
|
+
nha ops start
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**What `nha plan` does:**
|
|
46
|
+
1. **Fetches** your emails + calendar events + tasks
|
|
47
|
+
2. **SABER** scans emails for phishing and security threats
|
|
48
|
+
3. **HERALD** generates intelligence briefs for each meeting
|
|
49
|
+
4. **ORACLE** analyzes schedule patterns and productivity
|
|
50
|
+
5. **SCHEHERAZADE** prepares talking points for meetings
|
|
51
|
+
6. **CONDUCTOR** synthesizes everything into a structured daily plan
|
|
52
|
+
|
|
53
|
+
OpenClaw reads your email with 1 generic agent. NHA sends it through 5 specialists.
|
|
54
|
+
|
|
55
|
+
### Privacy
|
|
56
|
+
|
|
57
|
+
**Zero data touches NHA servers.** The only network calls are:
|
|
58
|
+
- Google APIs (your OAuth token, direct from your machine)
|
|
59
|
+
- Your LLM provider (your API key, direct from your machine)
|
|
60
|
+
|
|
61
|
+
All data stored locally in `~/.nha/ops/`. Tokens encrypted with AES-256-GCM. You own everything. Inspect it, delete it, export it anytime.
|
|
62
|
+
|
|
23
63
|
## The Agents
|
|
24
64
|
|
|
25
65
|
38 agents across 11 domains. Each agent is a standalone `.mjs` file you own locally — inspect it, modify it, run it offline.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "NotHumanAllowed — 38
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "NotHumanAllowed — 38 AI agents for security, code, DevOps, data & daily ops. Ask agents directly, plan your day with 5 specialist agents, manage tasks, connect Gmail + Calendar.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nha": "./bin/nha.mjs",
|
package/src/cli.mjs
CHANGED
|
@@ -9,6 +9,11 @@ import { loadConfig, setConfigValue } from './config.mjs';
|
|
|
9
9
|
import { checkForUpdates, runUpdate } from './updater.mjs';
|
|
10
10
|
import { download } from './downloader.mjs';
|
|
11
11
|
import { cmdAsk } from './commands/ask.mjs';
|
|
12
|
+
import { cmdPlan } from './commands/plan.mjs';
|
|
13
|
+
import { cmdTasks } from './commands/tasks.mjs';
|
|
14
|
+
import { cmdOps } from './commands/ops.mjs';
|
|
15
|
+
import { cmdChat } from './commands/chat.mjs';
|
|
16
|
+
import { cmdGoogle } from './commands/google-auth.mjs';
|
|
12
17
|
import { banner, info, ok, warn, fail, C, G, Y, D, W, BOLD, NC, M, B, R } from './ui.mjs';
|
|
13
18
|
|
|
14
19
|
export async function main(argv) {
|
|
@@ -40,6 +45,22 @@ export async function main(argv) {
|
|
|
40
45
|
case 'run':
|
|
41
46
|
return cmdRun(args);
|
|
42
47
|
|
|
48
|
+
case 'plan':
|
|
49
|
+
return cmdPlan(args);
|
|
50
|
+
|
|
51
|
+
case 'tasks':
|
|
52
|
+
case 'task':
|
|
53
|
+
return cmdTasks(args);
|
|
54
|
+
|
|
55
|
+
case 'ops':
|
|
56
|
+
return cmdOps(args);
|
|
57
|
+
|
|
58
|
+
case 'chat':
|
|
59
|
+
return cmdChat(args);
|
|
60
|
+
|
|
61
|
+
case 'google':
|
|
62
|
+
return cmdGoogle(args);
|
|
63
|
+
|
|
43
64
|
case 'pif':
|
|
44
65
|
return cmdPif(args);
|
|
45
66
|
|
|
@@ -333,6 +354,23 @@ function cmdHelp() {
|
|
|
333
354
|
console.log(` run "prompt" Multi-agent collaboration (server-routed)`);
|
|
334
355
|
console.log(` run "prompt" ${D}--agents saber,zero${NC} Collaborate with specific agents\n`);
|
|
335
356
|
|
|
357
|
+
console.log(` ${C}Daily Operations${NC} ${D}(Gmail + Calendar + Tasks)${NC}`);
|
|
358
|
+
console.log(` chat Interactive chat — manage email/calendar/tasks naturally`);
|
|
359
|
+
console.log(` plan Generate daily plan (5 agents analyze your day)`);
|
|
360
|
+
console.log(` plan --refresh Regenerate today's plan`);
|
|
361
|
+
console.log(` tasks List today's tasks`);
|
|
362
|
+
console.log(` tasks add "desc" Add a task`);
|
|
363
|
+
console.log(` tasks done 3 Complete task #3`);
|
|
364
|
+
console.log(` tasks week Week overview`);
|
|
365
|
+
console.log(` ops start Start background daemon (auto-alerts)`);
|
|
366
|
+
console.log(` ops stop Stop daemon`);
|
|
367
|
+
console.log(` ops status Daemon status\n`);
|
|
368
|
+
|
|
369
|
+
console.log(` ${C}Google Integration${NC}`);
|
|
370
|
+
console.log(` google auth Connect Gmail + Calendar`);
|
|
371
|
+
console.log(` google status Connection status`);
|
|
372
|
+
console.log(` google revoke Disconnect\n`);
|
|
373
|
+
|
|
336
374
|
console.log(` ${C}Extensions${NC} ${D}(downloadable agent modules)${NC}`);
|
|
337
375
|
console.log(` install <name> Install an extension agent`);
|
|
338
376
|
console.log(` install --all Install all ${EXTENSIONS.length} extensions`);
|
|
@@ -341,8 +379,7 @@ function cmdHelp() {
|
|
|
341
379
|
console.log(` ${C}Social Network${NC} ${D}(NHA platform)${NC}`);
|
|
342
380
|
console.log(` pif register Register your agent identity`);
|
|
343
381
|
console.log(` pif post Post content`);
|
|
344
|
-
console.log(` pif feed Activity feed`);
|
|
345
|
-
console.log(` pif explore Discover agents & templates\n`);
|
|
382
|
+
console.log(` pif feed Activity feed\n`);
|
|
346
383
|
|
|
347
384
|
console.log(` ${C}Configuration${NC}`);
|
|
348
385
|
console.log(` config Show current config`);
|
package/src/commands/ask.mjs
CHANGED
|
@@ -10,299 +10,10 @@ import fs from 'fs';
|
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import { loadConfig } from '../config.mjs';
|
|
12
12
|
import { AGENTS_DIR, AGENTS } from '../constants.mjs';
|
|
13
|
+
import { getProviderCall, getApiKey, parseAgentFile } from '../services/llm.mjs';
|
|
13
14
|
import { fail, info, ok, C, G, Y, D, W, BOLD, NC, M } from '../ui.mjs';
|
|
14
15
|
|
|
15
|
-
// ── LLM Providers ──────────────────────────────────────────────────────────
|
|
16
|
-
|
|
17
|
-
async function callAnthropic(apiKey, model, systemPrompt, userMessage, stream) {
|
|
18
|
-
const body = {
|
|
19
|
-
model: model || 'claude-sonnet-4-20250514',
|
|
20
|
-
max_tokens: 8192,
|
|
21
|
-
system: systemPrompt,
|
|
22
|
-
messages: [{ role: 'user', content: userMessage }],
|
|
23
|
-
stream,
|
|
24
|
-
};
|
|
25
|
-
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
26
|
-
method: 'POST',
|
|
27
|
-
headers: {
|
|
28
|
-
'Content-Type': 'application/json',
|
|
29
|
-
'x-api-key': apiKey,
|
|
30
|
-
'anthropic-version': '2023-06-01',
|
|
31
|
-
},
|
|
32
|
-
body: JSON.stringify(body),
|
|
33
|
-
});
|
|
34
|
-
if (!res.ok) {
|
|
35
|
-
const err = await res.text();
|
|
36
|
-
throw new Error(`Anthropic ${res.status}: ${err}`);
|
|
37
|
-
}
|
|
38
|
-
if (stream) return streamSSE(res, 'anthropic');
|
|
39
|
-
const data = await res.json();
|
|
40
|
-
return data.content?.[0]?.text || '';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function callOpenAI(apiKey, model, systemPrompt, userMessage, stream) {
|
|
44
|
-
const body = {
|
|
45
|
-
model: model || 'gpt-4o',
|
|
46
|
-
max_tokens: 8192,
|
|
47
|
-
messages: [
|
|
48
|
-
{ role: 'system', content: systemPrompt },
|
|
49
|
-
{ role: 'user', content: userMessage },
|
|
50
|
-
],
|
|
51
|
-
stream,
|
|
52
|
-
};
|
|
53
|
-
const res = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
54
|
-
method: 'POST',
|
|
55
|
-
headers: {
|
|
56
|
-
'Content-Type': 'application/json',
|
|
57
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
58
|
-
},
|
|
59
|
-
body: JSON.stringify(body),
|
|
60
|
-
});
|
|
61
|
-
if (!res.ok) {
|
|
62
|
-
const err = await res.text();
|
|
63
|
-
throw new Error(`OpenAI ${res.status}: ${err}`);
|
|
64
|
-
}
|
|
65
|
-
if (stream) return streamSSE(res, 'openai');
|
|
66
|
-
const data = await res.json();
|
|
67
|
-
return data.choices?.[0]?.message?.content || '';
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function callGemini(apiKey, model, systemPrompt, userMessage, stream) {
|
|
71
|
-
const m = model || 'gemini-2.5-pro-preview-05-06';
|
|
72
|
-
const url = `https://generativelanguage.googleapis.com/v1beta/models/${m}:generateContent?key=${apiKey}`;
|
|
73
|
-
const body = {
|
|
74
|
-
system_instruction: { parts: [{ text: systemPrompt }] },
|
|
75
|
-
contents: [{ parts: [{ text: userMessage }] }],
|
|
76
|
-
generationConfig: { maxOutputTokens: 8192 },
|
|
77
|
-
};
|
|
78
|
-
const res = await fetch(url, {
|
|
79
|
-
method: 'POST',
|
|
80
|
-
headers: { 'Content-Type': 'application/json' },
|
|
81
|
-
body: JSON.stringify(body),
|
|
82
|
-
});
|
|
83
|
-
if (!res.ok) {
|
|
84
|
-
const err = await res.text();
|
|
85
|
-
throw new Error(`Gemini ${res.status}: ${err}`);
|
|
86
|
-
}
|
|
87
|
-
const data = await res.json();
|
|
88
|
-
return data.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async function callDeepSeek(apiKey, model, systemPrompt, userMessage, stream) {
|
|
92
|
-
const body = {
|
|
93
|
-
model: model || 'deepseek-chat',
|
|
94
|
-
max_tokens: 8192,
|
|
95
|
-
messages: [
|
|
96
|
-
{ role: 'system', content: systemPrompt },
|
|
97
|
-
{ role: 'user', content: userMessage },
|
|
98
|
-
],
|
|
99
|
-
stream,
|
|
100
|
-
};
|
|
101
|
-
const res = await fetch('https://api.deepseek.com/v1/chat/completions', {
|
|
102
|
-
method: 'POST',
|
|
103
|
-
headers: {
|
|
104
|
-
'Content-Type': 'application/json',
|
|
105
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
106
|
-
},
|
|
107
|
-
body: JSON.stringify(body),
|
|
108
|
-
});
|
|
109
|
-
if (!res.ok) {
|
|
110
|
-
const err = await res.text();
|
|
111
|
-
throw new Error(`DeepSeek ${res.status}: ${err}`);
|
|
112
|
-
}
|
|
113
|
-
if (stream) return streamSSE(res, 'openai');
|
|
114
|
-
const data = await res.json();
|
|
115
|
-
return data.choices?.[0]?.message?.content || '';
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async function callGrok(apiKey, model, systemPrompt, userMessage, stream) {
|
|
119
|
-
const body = {
|
|
120
|
-
model: model || 'grok-3-latest',
|
|
121
|
-
max_tokens: 8192,
|
|
122
|
-
messages: [
|
|
123
|
-
{ role: 'system', content: systemPrompt },
|
|
124
|
-
{ role: 'user', content: userMessage },
|
|
125
|
-
],
|
|
126
|
-
stream,
|
|
127
|
-
};
|
|
128
|
-
const res = await fetch('https://api.x.ai/v1/chat/completions', {
|
|
129
|
-
method: 'POST',
|
|
130
|
-
headers: {
|
|
131
|
-
'Content-Type': 'application/json',
|
|
132
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
133
|
-
},
|
|
134
|
-
body: JSON.stringify(body),
|
|
135
|
-
});
|
|
136
|
-
if (!res.ok) {
|
|
137
|
-
const err = await res.text();
|
|
138
|
-
throw new Error(`Grok ${res.status}: ${err}`);
|
|
139
|
-
}
|
|
140
|
-
if (stream) return streamSSE(res, 'openai');
|
|
141
|
-
const data = await res.json();
|
|
142
|
-
return data.choices?.[0]?.message?.content || '';
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
async function callMistral(apiKey, model, systemPrompt, userMessage, stream) {
|
|
146
|
-
const body = {
|
|
147
|
-
model: model || 'mistral-large-latest',
|
|
148
|
-
max_tokens: 8192,
|
|
149
|
-
messages: [
|
|
150
|
-
{ role: 'system', content: systemPrompt },
|
|
151
|
-
{ role: 'user', content: userMessage },
|
|
152
|
-
],
|
|
153
|
-
stream,
|
|
154
|
-
};
|
|
155
|
-
const res = await fetch('https://api.mistral.ai/v1/chat/completions', {
|
|
156
|
-
method: 'POST',
|
|
157
|
-
headers: {
|
|
158
|
-
'Content-Type': 'application/json',
|
|
159
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
160
|
-
},
|
|
161
|
-
body: JSON.stringify(body),
|
|
162
|
-
});
|
|
163
|
-
if (!res.ok) {
|
|
164
|
-
const err = await res.text();
|
|
165
|
-
throw new Error(`Mistral ${res.status}: ${err}`);
|
|
166
|
-
}
|
|
167
|
-
if (stream) return streamSSE(res, 'openai');
|
|
168
|
-
const data = await res.json();
|
|
169
|
-
return data.choices?.[0]?.message?.content || '';
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async function callCohere(apiKey, model, systemPrompt, userMessage, stream) {
|
|
173
|
-
const body = {
|
|
174
|
-
model: model || 'command-r-plus',
|
|
175
|
-
max_tokens: 8192,
|
|
176
|
-
preamble: systemPrompt,
|
|
177
|
-
message: userMessage,
|
|
178
|
-
};
|
|
179
|
-
const res = await fetch('https://api.cohere.ai/v1/chat', {
|
|
180
|
-
method: 'POST',
|
|
181
|
-
headers: {
|
|
182
|
-
'Content-Type': 'application/json',
|
|
183
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
184
|
-
},
|
|
185
|
-
body: JSON.stringify(body),
|
|
186
|
-
});
|
|
187
|
-
if (!res.ok) {
|
|
188
|
-
const err = await res.text();
|
|
189
|
-
throw new Error(`Cohere ${res.status}: ${err}`);
|
|
190
|
-
}
|
|
191
|
-
const data = await res.json();
|
|
192
|
-
return data.text || '';
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ── SSE Stream Parser ──────────────────────────────────────────────────────
|
|
196
|
-
|
|
197
|
-
async function streamSSE(res, format) {
|
|
198
|
-
const reader = res.body.getReader();
|
|
199
|
-
const decoder = new TextDecoder();
|
|
200
|
-
let buffer = '';
|
|
201
|
-
let fullText = '';
|
|
202
|
-
|
|
203
|
-
while (true) {
|
|
204
|
-
const { done, value } = await reader.read();
|
|
205
|
-
if (done) break;
|
|
206
|
-
|
|
207
|
-
buffer += decoder.decode(value, { stream: true });
|
|
208
|
-
const lines = buffer.split('\n');
|
|
209
|
-
buffer = lines.pop() || '';
|
|
210
|
-
|
|
211
|
-
for (const line of lines) {
|
|
212
|
-
if (!line.startsWith('data: ')) continue;
|
|
213
|
-
const data = line.slice(6).trim();
|
|
214
|
-
if (data === '[DONE]') continue;
|
|
215
|
-
|
|
216
|
-
try {
|
|
217
|
-
const json = JSON.parse(data);
|
|
218
|
-
let chunk = '';
|
|
219
|
-
|
|
220
|
-
if (format === 'anthropic') {
|
|
221
|
-
if (json.type === 'content_block_delta') {
|
|
222
|
-
chunk = json.delta?.text || '';
|
|
223
|
-
}
|
|
224
|
-
} else {
|
|
225
|
-
// OpenAI-compatible (OpenAI, DeepSeek, Grok, Mistral)
|
|
226
|
-
chunk = json.choices?.[0]?.delta?.content || '';
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (chunk) {
|
|
230
|
-
process.stdout.write(chunk);
|
|
231
|
-
fullText += chunk;
|
|
232
|
-
}
|
|
233
|
-
} catch {}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
process.stdout.write('\n');
|
|
238
|
-
return fullText;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// ── Provider Router ────────────────────────────────────────────────────────
|
|
242
|
-
|
|
243
|
-
function getProviderCall(provider) {
|
|
244
|
-
const map = {
|
|
245
|
-
anthropic: callAnthropic,
|
|
246
|
-
openai: callOpenAI,
|
|
247
|
-
gemini: callGemini,
|
|
248
|
-
deepseek: callDeepSeek,
|
|
249
|
-
grok: callGrok,
|
|
250
|
-
mistral: callMistral,
|
|
251
|
-
cohere: callCohere,
|
|
252
|
-
};
|
|
253
|
-
return map[provider] || null;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function getApiKey(config, provider) {
|
|
257
|
-
const keyMap = {
|
|
258
|
-
anthropic: config.llm.apiKey,
|
|
259
|
-
openai: config.llm.openaiKey || config.llm.apiKey,
|
|
260
|
-
gemini: config.llm.geminiKey || config.llm.apiKey,
|
|
261
|
-
deepseek: config.llm.deepseekKey || config.llm.apiKey,
|
|
262
|
-
grok: config.llm.grokKey || config.llm.apiKey,
|
|
263
|
-
mistral: config.llm.mistralKey || config.llm.apiKey,
|
|
264
|
-
cohere: config.llm.cohereKey || config.llm.apiKey,
|
|
265
|
-
};
|
|
266
|
-
return keyMap[provider] || config.llm.apiKey;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// ── Agent File Parser ──────────────────────────────────────────────────────
|
|
270
|
-
|
|
271
|
-
function parseAgentFile(source, agentName) {
|
|
272
|
-
let card = { displayName: agentName.toUpperCase(), category: 'agent', tagline: '' };
|
|
273
|
-
let systemPrompt = '';
|
|
274
|
-
|
|
275
|
-
// Extract AGENT_CARD object
|
|
276
|
-
const cardMatch = source.match(/export\s+var\s+AGENT_CARD\s*=\s*(\{[\s\S]*?\});/);
|
|
277
|
-
if (cardMatch) {
|
|
278
|
-
try {
|
|
279
|
-
// Safe eval of the object literal (it only contains strings and arrays)
|
|
280
|
-
card = new Function('return ' + cardMatch[1])();
|
|
281
|
-
} catch {}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Extract SYSTEM_PROMPT — concatenated string literals
|
|
285
|
-
const promptMatch = source.match(/export\s+var\s+SYSTEM_PROMPT\s*=\s*([\s\S]*?);(?:\n\nexport|\n\nvar|\n\n\/\/)/);
|
|
286
|
-
if (promptMatch) {
|
|
287
|
-
try {
|
|
288
|
-
// Evaluate the concatenated string expression
|
|
289
|
-
systemPrompt = new Function('return ' + promptMatch[1])();
|
|
290
|
-
} catch {}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Fallback: try simpler pattern
|
|
294
|
-
if (!systemPrompt) {
|
|
295
|
-
const simpleMatch = source.match(/SYSTEM_PROMPT\s*=\s*'([\s\S]*?)';/);
|
|
296
|
-
if (simpleMatch) systemPrompt = simpleMatch[1];
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return { card, systemPrompt };
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// ── Main Command ───────────────────────────────────────────────────────────
|
|
303
|
-
|
|
304
16
|
export async function cmdAsk(args) {
|
|
305
|
-
// Parse: nha ask <agent> "prompt" [--provider X] [--model Y] [--no-stream] [--file F]
|
|
306
17
|
const agentName = args[0];
|
|
307
18
|
if (!agentName || agentName.startsWith('-')) {
|
|
308
19
|
fail('Usage: nha ask <agent> "your question"');
|
|
@@ -313,7 +24,6 @@ export async function cmdAsk(args) {
|
|
|
313
24
|
process.exit(1);
|
|
314
25
|
}
|
|
315
26
|
|
|
316
|
-
// Find agent file
|
|
317
27
|
const agentFile = path.join(AGENTS_DIR, `${agentName}.mjs`);
|
|
318
28
|
if (!fs.existsSync(agentFile)) {
|
|
319
29
|
fail(`Agent "${agentName}" not found in ~/.nha/agents/`);
|
|
@@ -321,7 +31,6 @@ export async function cmdAsk(args) {
|
|
|
321
31
|
process.exit(1);
|
|
322
32
|
}
|
|
323
33
|
|
|
324
|
-
// Build prompt from remaining args
|
|
325
34
|
let promptParts = [];
|
|
326
35
|
let provider = null;
|
|
327
36
|
let model = null;
|
|
@@ -343,7 +52,6 @@ export async function cmdAsk(args) {
|
|
|
343
52
|
process.exit(1);
|
|
344
53
|
}
|
|
345
54
|
|
|
346
|
-
// Attach file content if provided
|
|
347
55
|
if (attachFile) {
|
|
348
56
|
const filePath = path.resolve(attachFile);
|
|
349
57
|
if (!fs.existsSync(filePath)) {
|
|
@@ -356,7 +64,6 @@ export async function cmdAsk(args) {
|
|
|
356
64
|
userMessage += `\n\n--- Attached file: ${path.basename(filePath)} ---\n${truncated}`;
|
|
357
65
|
}
|
|
358
66
|
|
|
359
|
-
// Load config
|
|
360
67
|
const config = loadConfig();
|
|
361
68
|
provider = provider || config.llm.provider || 'anthropic';
|
|
362
69
|
model = model || config.llm.model || null;
|
|
@@ -367,8 +74,6 @@ export async function cmdAsk(args) {
|
|
|
367
74
|
process.exit(1);
|
|
368
75
|
}
|
|
369
76
|
|
|
370
|
-
// Load agent — parse AGENT_CARD and SYSTEM_PROMPT from file text
|
|
371
|
-
// (can't use dynamic import because agent files have syntax incompatible with standalone ESM import)
|
|
372
77
|
const agentSource = fs.readFileSync(agentFile, 'utf-8');
|
|
373
78
|
const { card, systemPrompt } = parseAgentFile(agentSource, agentName);
|
|
374
79
|
|
|
@@ -377,11 +82,9 @@ export async function cmdAsk(args) {
|
|
|
377
82
|
process.exit(1);
|
|
378
83
|
}
|
|
379
84
|
|
|
380
|
-
// Print header
|
|
381
85
|
console.log(`\n ${BOLD}${card?.displayName || agentName.toUpperCase()}${NC} ${D}(${card?.tagline || card?.category || 'agent'})${NC}`);
|
|
382
86
|
console.log(` ${D}Provider: ${provider}${model ? ' / ' + model : ''} | Direct call — no server${NC}\n`);
|
|
383
87
|
|
|
384
|
-
// Call LLM
|
|
385
88
|
const callFn = getProviderCall(provider);
|
|
386
89
|
if (!callFn) {
|
|
387
90
|
fail(`Unknown provider: ${provider}`);
|
|
@@ -392,9 +95,7 @@ export async function cmdAsk(args) {
|
|
|
392
95
|
const startTime = Date.now();
|
|
393
96
|
|
|
394
97
|
try {
|
|
395
|
-
// Gemini and Cohere don't support streaming well via their native APIs
|
|
396
98
|
const useStream = stream && (provider === 'anthropic' || provider === 'openai' || provider === 'deepseek' || provider === 'grok' || provider === 'mistral');
|
|
397
|
-
|
|
398
99
|
const result = await callFn(apiKey, model, systemPrompt, userMessage, useStream);
|
|
399
100
|
|
|
400
101
|
if (!useStream && result) {
|