nothumanallowed 8.0.3 → 8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "8.0.3",
3
+ "version": "8.1.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": {
@@ -575,6 +575,28 @@ export async function cmdUI(args) {
575
575
  return;
576
576
  }
577
577
 
578
+ // POST /api/tasks/:id/delete
579
+ const taskDeleteMatch = pathname.match(/^\/api\/tasks\/(\d+)\/delete$/);
580
+ if (method === 'POST' && taskDeleteMatch) {
581
+ const { deleteTask } = await import('../services/task-store.mjs');
582
+ const taskId = parseInt(taskDeleteMatch[1], 10);
583
+ const success = deleteTask(taskId);
584
+ sendJSON(res, 200, { ok: success, id: taskId });
585
+ logRequest(method, pathname, 200, Date.now() - start);
586
+ return;
587
+ }
588
+
589
+ // POST /api/tasks/clear
590
+ if (method === 'POST' && pathname === '/api/tasks/clear') {
591
+ const { clearTasks } = await import('../services/task-store.mjs');
592
+ const body = await parseBody(req);
593
+ const mode = body.mode || 'all';
594
+ const count = clearTasks(mode);
595
+ sendJSON(res, 200, { ok: true, removed: count });
596
+ logRequest(method, pathname, 200, Date.now() - start);
597
+ return;
598
+ }
599
+
578
600
  // GET /api/plan
579
601
  if (method === 'GET' && pathname === '/api/plan') {
580
602
  const plan = loadTodayPlan();
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.0.3';
8
+ export const VERSION = '8.1.0';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -113,6 +113,55 @@ export function moveTask(taskId, fromDate, toDate) {
113
113
  return true;
114
114
  }
115
115
 
116
+ /**
117
+ * Delete a single task by ID.
118
+ * @param {number} taskId
119
+ * @param {string} [date]
120
+ * @returns {boolean} success
121
+ */
122
+ export function deleteTask(taskId, date) {
123
+ const data = loadDay(date);
124
+ const idx = data.tasks.findIndex(t => t.id === taskId);
125
+ if (idx === -1) return false;
126
+ data.tasks.splice(idx, 1);
127
+ saveDay(date, data);
128
+ return true;
129
+ }
130
+
131
+ /**
132
+ * Clear all tasks for a date (delete completed, or all).
133
+ * @param {'done'|'all'} mode — 'done' removes only completed, 'all' removes everything
134
+ * @param {string} [date]
135
+ * @returns {number} count of removed tasks
136
+ */
137
+ export function clearTasks(mode = 'all', date) {
138
+ const data = loadDay(date);
139
+ const before = data.tasks.length;
140
+ if (mode === 'done') {
141
+ data.tasks = data.tasks.filter(t => t.status !== 'done');
142
+ } else {
143
+ data.tasks = [];
144
+ }
145
+ saveDay(date, data);
146
+ return before - data.tasks.length;
147
+ }
148
+
149
+ /**
150
+ * Edit task priority.
151
+ * @param {number} taskId
152
+ * @param {string} priority
153
+ * @param {string} [date]
154
+ * @returns {boolean}
155
+ */
156
+ export function editTaskPriority(taskId, priority, date) {
157
+ const data = loadDay(date);
158
+ const task = data.tasks.find(t => t.id === taskId);
159
+ if (!task) return false;
160
+ task.priority = priority;
161
+ saveDay(date, data);
162
+ return true;
163
+ }
164
+
116
165
  /**
117
166
  * Get tasks for the week (Mon-Sun).
118
167
  */
@@ -36,6 +36,10 @@ import {
36
36
  addTask,
37
37
  completeTask,
38
38
  moveTask,
39
+ deleteTask,
40
+ clearTasks,
41
+ editTask,
42
+ editTaskPriority,
39
43
  } from './task-store.mjs';
40
44
 
41
45
  import { notify } from './notification.mjs';
@@ -53,6 +57,8 @@ export const DESTRUCTIVE_ACTIONS = new Set([
53
57
  'calendar_update',
54
58
  'contact_delete',
55
59
  'task_done',
60
+ 'task_delete',
61
+ 'task_clear',
56
62
  'notify_remind',
57
63
  'slack_send',
58
64
  'github_create_issue',
@@ -157,6 +163,15 @@ TOOLS:
157
163
  23. task_move(id: number, toDate: string)
158
164
  Move a task to another date (YYYY-MM-DD).
159
165
 
166
+ 24. task_delete(id: number)
167
+ Delete a specific task permanently. ALWAYS confirm before deleting.
168
+
169
+ 25. task_clear(mode?: "all"|"done")
170
+ Clear tasks. mode="done" removes only completed tasks. mode="all" removes ALL tasks. Default: "all". ALWAYS confirm before clearing.
171
+
172
+ 26. task_edit(id: number, description?: string, priority?: "low"|"medium"|"high"|"critical")
173
+ Edit a task's description and/or priority.
174
+
160
175
  --- CONTACTS ---
161
176
 
162
177
  24. contact_search(query: string)
@@ -381,6 +396,10 @@ export function describeAction(action, params) {
381
396
  return `Delete contact "${params.query}"`;
382
397
  case 'task_done':
383
398
  return `Mark task #${params.id} as completed`;
399
+ case 'task_delete':
400
+ return `Delete task #${params.id} permanently`;
401
+ case 'task_clear':
402
+ return `Clear ${params.mode === 'done' ? 'completed' : 'ALL'} tasks`;
384
403
  case 'notify_remind':
385
404
  return `Set reminder: "${params.message}" at ${params.atTime}`;
386
405
  case 'slack_send':
@@ -759,6 +778,32 @@ export async function executeTool(action, params, config) {
759
778
  return success ? `Task #${params.id} moved to ${toDate}.` : `Task #${params.id} not found.`;
760
779
  }
761
780
 
781
+ case 'task_delete': {
782
+ const success = deleteTask(params.id);
783
+ return success ? `Task #${params.id} deleted.` : `Task #${params.id} not found.`;
784
+ }
785
+
786
+ case 'task_clear': {
787
+ const mode = params.mode || 'all';
788
+ const count = clearTasks(mode);
789
+ return count > 0
790
+ ? `${count} task${count !== 1 ? 's' : ''} ${mode === 'done' ? 'completed' : ''} removed.`
791
+ : 'No tasks to remove.';
792
+ }
793
+
794
+ case 'task_edit': {
795
+ if (params.description) {
796
+ editTask(params.id, params.description);
797
+ }
798
+ if (params.priority) {
799
+ editTaskPriority(params.id, params.priority);
800
+ }
801
+ const parts = [];
802
+ if (params.description) parts.push(`description updated`);
803
+ if (params.priority) parts.push(`priority set to ${params.priority}`);
804
+ return parts.length > 0 ? `Task #${params.id}: ${parts.join(', ')}.` : `No changes specified for task #${params.id}.`;
805
+ }
806
+
762
807
  // ── Notifications ─────────────────────────────────────────────────────
763
808
  case 'notify_remind': {
764
809
  const atTime = resolveTime(params.atTime);
@@ -360,10 +360,13 @@ function sendChat(){
360
360
  function renderTasks(el){
361
361
  var t=dash.tasks;
362
362
  var h='<div class="task-bar"><input id="taskInput" placeholder="Add a new task..."><select id="taskPriority"><option value="medium">Medium</option><option value="high">High</option><option value="critical">Critical</option><option value="low">Low</option></select><button onclick="addTaskUI()">Add</button></div>';
363
+ if(t.length>0){
364
+ h+='<div style="display:flex;gap:8px;margin-bottom:12px"><button onclick="clearTasksUI(\\x27done\\x27)" style="background:var(--bg3);color:var(--dim);border:1px solid var(--border);padding:6px 12px;border-radius:var(--r);font-size:11px;cursor:pointer">Clear completed</button><button onclick="clearTasksUI(\\x27all\\x27)" style="background:var(--bg3);color:var(--red);border:1px solid var(--border);padding:6px 12px;border-radius:var(--r);font-size:11px;cursor:pointer">Clear all</button></div>';
365
+ }
363
366
  t.sort(function(a,b){if(a.status==='done'&&b.status!=='done')return 1;if(a.status!=='done'&&b.status==='done')return -1;return 0});
364
367
  t.forEach(function(x){
365
368
  var isDone=x.status==='done';
366
- h+='<div class="card task'+(isDone?' task--done':'')+'" onclick="toggleTask('+x.id+')"><span class="task__check'+(isDone?' task__check--done':'')+'">'+(isDone?'\\u2713':'')+'</span><span class="task__desc">'+esc(x.description)+'</span><span class="task__priority task__priority--'+esc(x.priority)+'">'+esc(x.priority)+'</span></div>';
369
+ h+='<div class="card task'+(isDone?' task--done':'')+'"><span class="task__check'+(isDone?' task__check--done':'')+'" onclick="toggleTask('+x.id+')">'+(isDone?'\\u2713':'')+'</span><span class="task__desc" onclick="toggleTask('+x.id+')">'+esc(x.description)+'</span><span class="task__priority task__priority--'+esc(x.priority)+'">'+esc(x.priority)+'</span><span onclick="deleteTaskUI('+x.id+')" style="color:var(--dim);cursor:pointer;padding:4px 8px;font-size:14px;opacity:0.5" title="Delete task">&times;</span></div>';
367
370
  });
368
371
  el.innerHTML=h;
369
372
  var inp=document.getElementById('taskInput');
@@ -377,7 +380,16 @@ function addTaskUI(){
377
380
  });
378
381
  }
379
382
  function toggleTask(id){
380
- apiPatch('/api/tasks/'+id+'/done').then(function(){loadDash().then(function(){if(currentView==='tasks')render()})});
383
+ apiPatch('/api/tasks/'+id+'/done').then(function(){loadDash().then(function(){if(currentView==='tasks'||currentView==='dashboard')render()})});
384
+ }
385
+ function deleteTaskUI(id){
386
+ if(!confirm('Delete task #'+id+'?'))return;
387
+ apiPost('/api/tasks/'+id+'/delete',{}).then(function(){loadDash().then(function(){render()})});
388
+ }
389
+ function clearTasksUI(mode){
390
+ var msg=mode==='all'?'Delete ALL tasks? This cannot be undone.':'Remove all completed tasks?';
391
+ if(!confirm(msg))return;
392
+ apiPost('/api/tasks/clear',{mode:mode}).then(function(){loadDash().then(function(){render()})});
381
393
  }
382
394
 
383
395
  // ---- PLAN ----
@@ -1403,7 +1415,7 @@ init();
1403
1415
  </div>
1404
1416
  <div class="sidebar__section">
1405
1417
  <div class="sidebar__label">Google</div>
1406
- <div class="nav-item" data-view="emails" onclick="switchView('emails')"><span class="nav-item__icon">&#9993;</span> Emails <span class="nav-item__badge" id="emailBadge" style="display:none">0</span></div>
1418
+ <div class="nav-item" data-view="emails" onclick="switchView('emails')"><span class="nav-item__icon">&#128231;</span> Emails <span class="nav-item__badge" id="emailBadge" style="display:none">0</span></div>
1407
1419
  <div class="nav-item" data-view="calendar" onclick="switchView('calendar')"><span class="nav-item__icon">&#128197;</span> Calendar <span class="nav-item__badge" id="calBadge" style="display:none;background:var(--amber)">0</span></div>
1408
1420
  <div class="nav-item" data-view="drive" onclick="switchView('drive')"><span class="nav-item__icon">&#128193;</span> Drive</div>
1409
1421
  <div class="nav-item" data-view="contacts" onclick="switchView('contacts')"><span class="nav-item__icon">&#128101;</span> Contacts</div>