nothumanallowed 1.0.1 → 2.0.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 +12 -4
- package/package.json +2 -2
- package/src/cli.mjs +44 -7
- package/src/commands/ask.mjs +111 -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
|
@@ -12,10 +12,11 @@ npm install -g nothumanallowed
|
|
|
12
12
|
nha config set provider anthropic
|
|
13
13
|
nha config set key sk-ant-api03-YOUR_KEY
|
|
14
14
|
|
|
15
|
-
# Ask a single agent
|
|
16
|
-
nha
|
|
15
|
+
# Ask a single agent directly (no server, instant response)
|
|
16
|
+
nha ask saber "Audit this Express app for OWASP Top 10"
|
|
17
|
+
nha ask oracle "Analyze this dataset" --file data.csv
|
|
17
18
|
|
|
18
|
-
# Or let multiple agents collaborate
|
|
19
|
+
# Or let multiple agents collaborate via deliberation
|
|
19
20
|
nha run "Design a Kubernetes deployment for a 10K RPS API"
|
|
20
21
|
```
|
|
21
22
|
|
|
@@ -105,7 +106,14 @@ nha install --all # Install everything
|
|
|
105
106
|
## Commands
|
|
106
107
|
|
|
107
108
|
```bash
|
|
108
|
-
#
|
|
109
|
+
# Ask a single agent (direct call, no server)
|
|
110
|
+
nha ask saber "prompt" # Security audit
|
|
111
|
+
nha ask oracle "prompt" # Data analysis
|
|
112
|
+
nha ask forge "prompt" # DevOps & infrastructure
|
|
113
|
+
nha ask saber "review this" --file app.js # Attach a file
|
|
114
|
+
nha ask saber "prompt" --provider openai # Override provider
|
|
115
|
+
|
|
116
|
+
# Multi-agent collaboration (server-routed deliberation)
|
|
109
117
|
nha run "prompt" # Auto-route to best agents
|
|
110
118
|
nha run "prompt" --agents saber,zero # Specific agents
|
|
111
119
|
nha run --file prompt.txt # From file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "NotHumanAllowed — 38
|
|
3
|
+
"version": "2.0.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
|
@@ -8,6 +8,11 @@ import { spawnCore } from './spawn.mjs';
|
|
|
8
8
|
import { loadConfig, setConfigValue } from './config.mjs';
|
|
9
9
|
import { checkForUpdates, runUpdate } from './updater.mjs';
|
|
10
10
|
import { download } from './downloader.mjs';
|
|
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 { cmdGoogle } from './commands/google-auth.mjs';
|
|
11
16
|
import { banner, info, ok, warn, fail, C, G, Y, D, W, BOLD, NC, M, B, R } from './ui.mjs';
|
|
12
17
|
|
|
13
18
|
export async function main(argv) {
|
|
@@ -33,9 +38,25 @@ export async function main(argv) {
|
|
|
33
38
|
|
|
34
39
|
// ── Command dispatch ─────────────────────────────────────────────────────
|
|
35
40
|
switch (cmd) {
|
|
41
|
+
case 'ask':
|
|
42
|
+
return cmdAsk(args);
|
|
43
|
+
|
|
36
44
|
case 'run':
|
|
37
45
|
return cmdRun(args);
|
|
38
46
|
|
|
47
|
+
case 'plan':
|
|
48
|
+
return cmdPlan(args);
|
|
49
|
+
|
|
50
|
+
case 'tasks':
|
|
51
|
+
case 'task':
|
|
52
|
+
return cmdTasks(args);
|
|
53
|
+
|
|
54
|
+
case 'ops':
|
|
55
|
+
return cmdOps(args);
|
|
56
|
+
|
|
57
|
+
case 'google':
|
|
58
|
+
return cmdGoogle(args);
|
|
59
|
+
|
|
39
60
|
case 'pif':
|
|
40
61
|
return cmdPif(args);
|
|
41
62
|
|
|
@@ -321,12 +342,29 @@ function cmdHelp() {
|
|
|
321
342
|
console.log(` ${BOLD}Usage:${NC} nha <command> [options]\n`);
|
|
322
343
|
|
|
323
344
|
console.log(` ${C}Agents${NC}`);
|
|
345
|
+
console.log(` ask <agent> "prompt" Ask a single agent directly (no server)`);
|
|
346
|
+
console.log(` ask saber "prompt" ${D}--file code.js${NC} Attach a file`);
|
|
347
|
+
console.log(` ask oracle "prompt" ${D}--provider openai${NC}`);
|
|
324
348
|
console.log(` agents List all 38 specialized agents`);
|
|
325
349
|
console.log(` agents info <name> Show agent capabilities & domain`);
|
|
326
|
-
console.log(`
|
|
327
|
-
console.log(` run "prompt"
|
|
328
|
-
|
|
329
|
-
console.log(`
|
|
350
|
+
console.log(` run "prompt" Multi-agent collaboration (server-routed)`);
|
|
351
|
+
console.log(` run "prompt" ${D}--agents saber,zero${NC} Collaborate with specific agents\n`);
|
|
352
|
+
|
|
353
|
+
console.log(` ${C}Daily Operations${NC} ${D}(Gmail + Calendar + Tasks)${NC}`);
|
|
354
|
+
console.log(` plan Generate daily plan (5 agents analyze your day)`);
|
|
355
|
+
console.log(` plan --refresh Regenerate today's plan`);
|
|
356
|
+
console.log(` tasks List today's tasks`);
|
|
357
|
+
console.log(` tasks add "desc" Add a task`);
|
|
358
|
+
console.log(` tasks done 3 Complete task #3`);
|
|
359
|
+
console.log(` tasks week Week overview`);
|
|
360
|
+
console.log(` ops start Start background daemon (auto-alerts)`);
|
|
361
|
+
console.log(` ops stop Stop daemon`);
|
|
362
|
+
console.log(` ops status Daemon status\n`);
|
|
363
|
+
|
|
364
|
+
console.log(` ${C}Google Integration${NC}`);
|
|
365
|
+
console.log(` google auth Connect Gmail + Calendar`);
|
|
366
|
+
console.log(` google status Connection status`);
|
|
367
|
+
console.log(` google revoke Disconnect\n`);
|
|
330
368
|
|
|
331
369
|
console.log(` ${C}Extensions${NC} ${D}(downloadable agent modules)${NC}`);
|
|
332
370
|
console.log(` install <name> Install an extension agent`);
|
|
@@ -336,8 +374,7 @@ function cmdHelp() {
|
|
|
336
374
|
console.log(` ${C}Social Network${NC} ${D}(NHA platform)${NC}`);
|
|
337
375
|
console.log(` pif register Register your agent identity`);
|
|
338
376
|
console.log(` pif post Post content`);
|
|
339
|
-
console.log(` pif feed Activity feed`);
|
|
340
|
-
console.log(` pif explore Discover agents & templates\n`);
|
|
377
|
+
console.log(` pif feed Activity feed\n`);
|
|
341
378
|
|
|
342
379
|
console.log(` ${C}Configuration${NC}`);
|
|
343
380
|
console.log(` config Show current config`);
|
|
@@ -349,7 +386,7 @@ function cmdHelp() {
|
|
|
349
386
|
console.log(` ${C}Quick Start${NC}`);
|
|
350
387
|
console.log(` ${D}1.${NC} nha config set provider anthropic`);
|
|
351
388
|
console.log(` ${D}2.${NC} nha config set key sk-ant-api03-YOUR_KEY`);
|
|
352
|
-
console.log(` ${D}3.${NC} nha
|
|
389
|
+
console.log(` ${D}3.${NC} nha ask saber "Audit this Express app for OWASP Top 10"\n`);
|
|
353
390
|
|
|
354
391
|
console.log(` ${D}38 agents: security, code, data, devops, creative, integration, and more.${NC}`);
|
|
355
392
|
console.log(` ${D}Use them solo or let them collaborate via multi-round deliberation.${NC}`);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nha ask <agent> "prompt" — Direct single-agent call. No server. No session.
|
|
3
|
+
* Loads the agent's system prompt from ~/.nha/agents/<name>.mjs,
|
|
4
|
+
* calls the user's configured LLM provider, streams the response.
|
|
5
|
+
*
|
|
6
|
+
* Zero network calls except to the LLM provider.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { loadConfig } from '../config.mjs';
|
|
12
|
+
import { AGENTS_DIR, AGENTS } from '../constants.mjs';
|
|
13
|
+
import { getProviderCall, getApiKey, parseAgentFile } from '../services/llm.mjs';
|
|
14
|
+
import { fail, info, ok, C, G, Y, D, W, BOLD, NC, M } from '../ui.mjs';
|
|
15
|
+
|
|
16
|
+
export async function cmdAsk(args) {
|
|
17
|
+
const agentName = args[0];
|
|
18
|
+
if (!agentName || agentName.startsWith('-')) {
|
|
19
|
+
fail('Usage: nha ask <agent> "your question"');
|
|
20
|
+
fail(' nha ask saber "Audit this Express app for OWASP Top 10"');
|
|
21
|
+
fail(' nha ask oracle "Analyze this CSV for trends" --file data.csv');
|
|
22
|
+
console.log('');
|
|
23
|
+
info('Available agents: ' + AGENTS.join(', '));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const agentFile = path.join(AGENTS_DIR, `${agentName}.mjs`);
|
|
28
|
+
if (!fs.existsSync(agentFile)) {
|
|
29
|
+
fail(`Agent "${agentName}" not found in ~/.nha/agents/`);
|
|
30
|
+
info('Available: ' + AGENTS.join(', '));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let promptParts = [];
|
|
35
|
+
let provider = null;
|
|
36
|
+
let model = null;
|
|
37
|
+
let stream = true;
|
|
38
|
+
let attachFile = null;
|
|
39
|
+
|
|
40
|
+
for (let i = 1; i < args.length; i++) {
|
|
41
|
+
if (args[i] === '--provider' && args[i + 1]) { provider = args[++i]; continue; }
|
|
42
|
+
if (args[i] === '--model' && args[i + 1]) { model = args[++i]; continue; }
|
|
43
|
+
if (args[i] === '--no-stream') { stream = false; continue; }
|
|
44
|
+
if (args[i] === '--file' && args[i + 1]) { attachFile = args[++i]; continue; }
|
|
45
|
+
promptParts.push(args[i]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let userMessage = promptParts.join(' ');
|
|
49
|
+
if (!userMessage) {
|
|
50
|
+
fail('No prompt provided.');
|
|
51
|
+
fail('Usage: nha ask saber "your question here"');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (attachFile) {
|
|
56
|
+
const filePath = path.resolve(attachFile);
|
|
57
|
+
if (!fs.existsSync(filePath)) {
|
|
58
|
+
fail(`File not found: ${attachFile}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
62
|
+
const maxChars = 100_000;
|
|
63
|
+
const truncated = content.length > maxChars ? content.slice(0, maxChars) + '\n\n[... truncated ...]' : content;
|
|
64
|
+
userMessage += `\n\n--- Attached file: ${path.basename(filePath)} ---\n${truncated}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const config = loadConfig();
|
|
68
|
+
provider = provider || config.llm.provider || 'anthropic';
|
|
69
|
+
model = model || config.llm.model || null;
|
|
70
|
+
const apiKey = getApiKey(config, provider);
|
|
71
|
+
|
|
72
|
+
if (!apiKey) {
|
|
73
|
+
fail(`No API key for ${provider}. Run: nha config set key YOUR_KEY`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const agentSource = fs.readFileSync(agentFile, 'utf-8');
|
|
78
|
+
const { card, systemPrompt } = parseAgentFile(agentSource, agentName);
|
|
79
|
+
|
|
80
|
+
if (!systemPrompt) {
|
|
81
|
+
fail(`Agent "${agentName}" has no SYSTEM_PROMPT in its file.`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log(`\n ${BOLD}${card?.displayName || agentName.toUpperCase()}${NC} ${D}(${card?.tagline || card?.category || 'agent'})${NC}`);
|
|
86
|
+
console.log(` ${D}Provider: ${provider}${model ? ' / ' + model : ''} | Direct call — no server${NC}\n`);
|
|
87
|
+
|
|
88
|
+
const callFn = getProviderCall(provider);
|
|
89
|
+
if (!callFn) {
|
|
90
|
+
fail(`Unknown provider: ${provider}`);
|
|
91
|
+
info('Supported: anthropic, openai, gemini, deepseek, grok, mistral, cohere');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const startTime = Date.now();
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const useStream = stream && (provider === 'anthropic' || provider === 'openai' || provider === 'deepseek' || provider === 'grok' || provider === 'mistral');
|
|
99
|
+
const result = await callFn(apiKey, model, systemPrompt, userMessage, useStream);
|
|
100
|
+
|
|
101
|
+
if (!useStream && result) {
|
|
102
|
+
console.log(result);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
106
|
+
console.log(`\n ${D}${elapsed}s | ${provider}${model ? ' / ' + model : ''} | ${card?.displayName || agentName}${NC}\n`);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
fail(err.message);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** nha google auth|status|revoke — Google account management */
|
|
2
|
+
|
|
3
|
+
import { runAuthFlow, showStatus, revokeAuth } from '../services/google-oauth.mjs';
|
|
4
|
+
import { loadConfig } from '../config.mjs';
|
|
5
|
+
import { fail, info } from '../ui.mjs';
|
|
6
|
+
|
|
7
|
+
export async function cmdGoogle(args) {
|
|
8
|
+
const sub = args[0] || 'auth';
|
|
9
|
+
const config = loadConfig();
|
|
10
|
+
|
|
11
|
+
switch (sub) {
|
|
12
|
+
case 'auth':
|
|
13
|
+
case 'login':
|
|
14
|
+
case 'connect':
|
|
15
|
+
return runAuthFlow(config);
|
|
16
|
+
|
|
17
|
+
case 'status':
|
|
18
|
+
return showStatus();
|
|
19
|
+
|
|
20
|
+
case 'revoke':
|
|
21
|
+
case 'disconnect':
|
|
22
|
+
case 'logout':
|
|
23
|
+
return revokeAuth();
|
|
24
|
+
|
|
25
|
+
default:
|
|
26
|
+
fail(`Unknown: nha google ${sub}`);
|
|
27
|
+
info('Commands: auth, status, revoke');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** nha ops — Daemon control for Personal Agent Operations */
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { startDaemon, stopDaemon, getDaemonStatus, isRunning } from '../services/ops-daemon.mjs';
|
|
6
|
+
import { runPlanningPipeline } from '../services/ops-pipeline.mjs';
|
|
7
|
+
import { loadConfig } from '../config.mjs';
|
|
8
|
+
import { NHA_DIR } from '../constants.mjs';
|
|
9
|
+
import { info, ok, fail, warn, C, G, Y, D, W, BOLD, NC, R } from '../ui.mjs';
|
|
10
|
+
|
|
11
|
+
export async function cmdOps(args) {
|
|
12
|
+
const sub = args[0] || 'status';
|
|
13
|
+
|
|
14
|
+
switch (sub) {
|
|
15
|
+
case 'start': {
|
|
16
|
+
const result = startDaemon();
|
|
17
|
+
if (result.ok) {
|
|
18
|
+
ok(`PAO daemon started (PID ${result.pid})`);
|
|
19
|
+
info('Monitoring Gmail + Calendar. Notifications enabled.');
|
|
20
|
+
info('Run "nha ops status" to check. "nha ops stop" to halt.');
|
|
21
|
+
} else {
|
|
22
|
+
warn(result.message);
|
|
23
|
+
}
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
case 'stop': {
|
|
28
|
+
const result = stopDaemon();
|
|
29
|
+
if (result.ok) {
|
|
30
|
+
ok(`Daemon stopped (PID ${result.pid})`);
|
|
31
|
+
} else {
|
|
32
|
+
warn(result.message);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
case 'status': {
|
|
38
|
+
const status = getDaemonStatus();
|
|
39
|
+
console.log(`\n ${BOLD}PAO Daemon Status${NC}\n`);
|
|
40
|
+
console.log(` Running: ${status.running ? G + 'yes' + NC + ` (PID ${status.pid})` : R + 'no' + NC}`);
|
|
41
|
+
if (status.startedAt) console.log(` Started: ${D}${status.startedAt}${NC}`);
|
|
42
|
+
if (status.lastMailCheck) console.log(` Last mail check: ${D}${status.lastMailCheck}${NC}`);
|
|
43
|
+
if (status.lastCalendarCheck) console.log(` Last cal check: ${D}${status.lastCalendarCheck}${NC}`);
|
|
44
|
+
if (status.lastPlanGenerated) console.log(` Last plan: ${D}${status.lastPlanGenerated}${NC}`);
|
|
45
|
+
if (status.errors > 0) console.log(` Errors: ${Y}${status.errors}${NC}`);
|
|
46
|
+
console.log('');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
case 'logs': {
|
|
51
|
+
const logFile = path.join(NHA_DIR, 'ops', 'daemon', 'daemon.log');
|
|
52
|
+
if (!fs.existsSync(logFile)) {
|
|
53
|
+
info('No daemon logs. Start with: nha ops start');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const content = fs.readFileSync(logFile, 'utf-8');
|
|
57
|
+
const lines = content.split('\n').filter(Boolean);
|
|
58
|
+
const last50 = lines.slice(-50);
|
|
59
|
+
for (const line of last50) {
|
|
60
|
+
console.log(` ${D}${line}${NC}`);
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case 'run': {
|
|
66
|
+
// One-shot: sync + plan + exit
|
|
67
|
+
const config = loadConfig();
|
|
68
|
+
info('Running one-shot PAO pipeline...');
|
|
69
|
+
await runPlanningPipeline(config, { refresh: true });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
default:
|
|
74
|
+
fail(`Unknown: nha ops ${sub}`);
|
|
75
|
+
info('Commands: start, stop, status, logs, run');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** nha plan — Generate daily plan using 5 specialist agents */
|
|
2
|
+
|
|
3
|
+
import { runPlanningPipeline } from '../services/ops-pipeline.mjs';
|
|
4
|
+
import { loadConfig } from '../config.mjs';
|
|
5
|
+
import { fail } from '../ui.mjs';
|
|
6
|
+
|
|
7
|
+
export async function cmdPlan(args) {
|
|
8
|
+
const config = loadConfig();
|
|
9
|
+
|
|
10
|
+
if (!config.llm.apiKey) {
|
|
11
|
+
fail('No API key configured. Run: nha config set key YOUR_KEY');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let date = null;
|
|
16
|
+
let refresh = false;
|
|
17
|
+
let showOnly = false;
|
|
18
|
+
|
|
19
|
+
for (const arg of args) {
|
|
20
|
+
if (arg === '--refresh') { refresh = true; continue; }
|
|
21
|
+
if (arg === '--show') { showOnly = true; continue; }
|
|
22
|
+
if (arg === 'tomorrow') {
|
|
23
|
+
const d = new Date();
|
|
24
|
+
d.setDate(d.getDate() + 1);
|
|
25
|
+
date = d.toISOString().split('T')[0];
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (arg === 'yesterday') {
|
|
29
|
+
const d = new Date();
|
|
30
|
+
d.setDate(d.getDate() - 1);
|
|
31
|
+
date = d.toISOString().split('T')[0];
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (arg.startsWith('--date=')) {
|
|
35
|
+
date = arg.split('=')[1];
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(arg)) {
|
|
39
|
+
date = arg;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await runPlanningPipeline(config, { date, refresh, showOnly });
|
|
45
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/** nha tasks — Local task management */
|
|
2
|
+
|
|
3
|
+
import { getTasks, addTask, completeTask, editTask, moveTask, getWeekTasks, getDayStats } from '../services/task-store.mjs';
|
|
4
|
+
import { fail, info, ok, C, G, Y, D, W, BOLD, NC, R } from '../ui.mjs';
|
|
5
|
+
|
|
6
|
+
export async function cmdTasks(args) {
|
|
7
|
+
const sub = args[0];
|
|
8
|
+
|
|
9
|
+
// nha tasks (list today)
|
|
10
|
+
if (!sub || sub === 'list' || sub === 'today') {
|
|
11
|
+
const date = args[1] || undefined;
|
|
12
|
+
return showTasks(date);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// nha tasks add "description" [--priority high] [--due 14:00]
|
|
16
|
+
if (sub === 'add') {
|
|
17
|
+
const parts = args.slice(1);
|
|
18
|
+
let description = '';
|
|
19
|
+
let priority = 'medium';
|
|
20
|
+
let due = null;
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < parts.length; i++) {
|
|
23
|
+
if (parts[i] === '--priority' && parts[i + 1]) { priority = parts[++i]; continue; }
|
|
24
|
+
if (parts[i] === '--due' && parts[i + 1]) { due = parts[++i]; continue; }
|
|
25
|
+
if (parts[i] === '-p' && parts[i + 1]) { priority = parts[++i]; continue; }
|
|
26
|
+
description += (description ? ' ' : '') + parts[i];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!description) {
|
|
30
|
+
fail('Usage: nha tasks add "task description" [--priority high] [--due 14:00]');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const task = addTask({ description, priority, due });
|
|
35
|
+
ok(`Task #${task.id} added: ${description}`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// nha tasks done <id>
|
|
40
|
+
if (sub === 'done' || sub === 'complete') {
|
|
41
|
+
const id = parseInt(args[1]);
|
|
42
|
+
if (!id) { fail('Usage: nha tasks done <task-id>'); return; }
|
|
43
|
+
if (completeTask(id)) {
|
|
44
|
+
ok(`Task #${id} completed`);
|
|
45
|
+
} else {
|
|
46
|
+
fail(`Task #${id} not found`);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// nha tasks edit <id> "new description"
|
|
52
|
+
if (sub === 'edit') {
|
|
53
|
+
const id = parseInt(args[1]);
|
|
54
|
+
const desc = args.slice(2).join(' ');
|
|
55
|
+
if (!id || !desc) { fail('Usage: nha tasks edit <id> "new description"'); return; }
|
|
56
|
+
if (editTask(id, desc)) {
|
|
57
|
+
ok(`Task #${id} updated`);
|
|
58
|
+
} else {
|
|
59
|
+
fail(`Task #${id} not found`);
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// nha tasks move <id> tomorrow|YYYY-MM-DD
|
|
65
|
+
if (sub === 'move') {
|
|
66
|
+
const id = parseInt(args[1]);
|
|
67
|
+
let toDate = args[2];
|
|
68
|
+
if (!id || !toDate) { fail('Usage: nha tasks move <id> tomorrow'); return; }
|
|
69
|
+
|
|
70
|
+
if (toDate === 'tomorrow') {
|
|
71
|
+
const d = new Date();
|
|
72
|
+
d.setDate(d.getDate() + 1);
|
|
73
|
+
toDate = d.toISOString().split('T')[0];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const today = new Date().toISOString().split('T')[0];
|
|
77
|
+
if (moveTask(id, today, toDate)) {
|
|
78
|
+
ok(`Task #${id} moved to ${toDate}`);
|
|
79
|
+
} else {
|
|
80
|
+
fail(`Task #${id} not found`);
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// nha tasks week
|
|
86
|
+
if (sub === 'week') {
|
|
87
|
+
const week = getWeekTasks();
|
|
88
|
+
console.log(`\n ${BOLD}Week Overview${NC}\n`);
|
|
89
|
+
for (const day of week) {
|
|
90
|
+
const stats = getDayStats(day.date);
|
|
91
|
+
const isToday = day.date === new Date().toISOString().split('T')[0];
|
|
92
|
+
const label = isToday ? `${BOLD}${day.day} ${day.date} (today)${NC}` : `${D}${day.day} ${day.date}${NC}`;
|
|
93
|
+
console.log(` ${label} ${stats.total > 0 ? `${G}${stats.done}${NC}/${stats.total} done` : D + 'empty' + NC}`);
|
|
94
|
+
for (const t of day.tasks.slice(0, 5)) {
|
|
95
|
+
const icon = t.status === 'done' ? `${G}✓${NC}` : t.priority === 'high' || t.priority === 'critical' ? `${Y}●${NC}` : `${D}○${NC}`;
|
|
96
|
+
console.log(` ${icon} ${t.description}${t.due ? D + ' due ' + t.due + NC : ''}`);
|
|
97
|
+
}
|
|
98
|
+
if (day.tasks.length > 5) console.log(` ${D}+${day.tasks.length - 5} more${NC}`);
|
|
99
|
+
}
|
|
100
|
+
console.log('');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fail(`Unknown: nha tasks ${sub}`);
|
|
105
|
+
info('Commands: list, add, done, edit, move, week');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function showTasks(date) {
|
|
109
|
+
const tasks = getTasks(date);
|
|
110
|
+
const dateStr = date || new Date().toISOString().split('T')[0];
|
|
111
|
+
const stats = getDayStats(date);
|
|
112
|
+
|
|
113
|
+
console.log(`\n ${BOLD}Tasks — ${dateStr}${NC} ${D}(${stats.done}/${stats.total} done, ${stats.completionRate}%)${NC}\n`);
|
|
114
|
+
|
|
115
|
+
if (tasks.length === 0) {
|
|
116
|
+
info('No tasks for today. Add one: nha tasks add "your task"');
|
|
117
|
+
console.log('');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const t of tasks) {
|
|
122
|
+
const statusIcon = t.status === 'done' ? `${G}✓${NC}` : `${D}○${NC}`;
|
|
123
|
+
const priorityBadge = t.priority === 'critical' ? `${R}[!!]${NC}` :
|
|
124
|
+
t.priority === 'high' ? `${Y}[!]${NC}` : '';
|
|
125
|
+
const dueStr = t.due ? ` ${D}due ${t.due}${NC}` : '';
|
|
126
|
+
const sourceStr = t.source !== 'manual' ? ` ${D}(${t.source})${NC}` : '';
|
|
127
|
+
const strikethrough = t.status === 'done' ? D : '';
|
|
128
|
+
|
|
129
|
+
console.log(` ${statusIcon} ${strikethrough}#${t.id} ${t.description}${NC} ${priorityBadge}${dueStr}${sourceStr}`);
|
|
130
|
+
}
|
|
131
|
+
console.log('');
|
|
132
|
+
}
|
package/src/config.mjs
CHANGED
|
@@ -48,6 +48,26 @@ const DEFAULT_CONFIG = {
|
|
|
48
48
|
promptEvolutionEnabled: true,
|
|
49
49
|
metaIntelligenceEnabled: true,
|
|
50
50
|
},
|
|
51
|
+
google: {
|
|
52
|
+
clientId: '',
|
|
53
|
+
clientSecret: '',
|
|
54
|
+
},
|
|
55
|
+
ops: {
|
|
56
|
+
enabled: false,
|
|
57
|
+
planTime: '07:00',
|
|
58
|
+
summaryTime: '18:00',
|
|
59
|
+
pollIntervalMail: 300000,
|
|
60
|
+
pollIntervalCalendar: 900000,
|
|
61
|
+
meetingAlertMinutes: 30,
|
|
62
|
+
webhooks: {
|
|
63
|
+
telegram: '',
|
|
64
|
+
discord: '',
|
|
65
|
+
},
|
|
66
|
+
notifications: {
|
|
67
|
+
desktop: true,
|
|
68
|
+
terminal: true,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
51
71
|
};
|
|
52
72
|
|
|
53
73
|
/**
|
|
@@ -152,6 +172,13 @@ export function setConfigValue(key, value) {
|
|
|
152
172
|
'convergence': 'deliberation.convergence',
|
|
153
173
|
'tribunal': 'deliberation.tribunalEnabled',
|
|
154
174
|
'knowledge': 'features.knowledgeEnabled',
|
|
175
|
+
'google-client-id': 'google.clientId',
|
|
176
|
+
'google-client-secret': 'google.clientSecret',
|
|
177
|
+
'plan-time': 'ops.planTime',
|
|
178
|
+
'summary-time': 'ops.summaryTime',
|
|
179
|
+
'meeting-alert': 'ops.meetingAlertMinutes',
|
|
180
|
+
'telegram-webhook': 'ops.webhooks.telegram',
|
|
181
|
+
'discord-webhook': 'ops.webhooks.discord',
|
|
155
182
|
};
|
|
156
183
|
|
|
157
184
|
const resolved = aliases[key] || key;
|
package/src/constants.mjs
CHANGED