helixmind 0.2.28 → 0.2.30

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.

Potentially problematic release.


This version of helixmind might be problematic. Click here for more details.

@@ -34,6 +34,8 @@ import { getSuggestions, writeSuggestions, clearSuggestions } from '../ui/comman
34
34
  import { selectMenu } from '../ui/select-menu.js';
35
35
  import { BugJournal } from '../bugs/journal.js';
36
36
  import { detectBugReport } from '../bugs/detector.js';
37
+ import { JarvisQueue } from '../jarvis/queue.js';
38
+ import { runJarvisDaemon } from '../jarvis/daemon.js';
37
39
  import { BrowserController } from '../browser/controller.js';
38
40
  import { VisionProcessor } from '../browser/vision.js';
39
41
  import { classifyTask } from '../validation/classifier.js';
@@ -87,6 +89,19 @@ const HELP_CATEGORIES = [
87
89
  { cmd: '/local', label: '/local', description: 'Local LLM setup (Ollama)' },
88
90
  ],
89
91
  },
92
+ {
93
+ category: 'Jarvis', color: '#ff00ff',
94
+ items: [
95
+ { cmd: '/jarvis', label: '/jarvis', description: 'Start Jarvis daemon' },
96
+ { cmd: '/jarvis task', label: '/jarvis task "..."', description: 'Add task to queue' },
97
+ { cmd: '/jarvis tasks', label: '/jarvis tasks', description: 'List all tasks' },
98
+ { cmd: '/jarvis status', label: '/jarvis status [id]', description: 'Show task/daemon status' },
99
+ { cmd: '/jarvis stop', label: '/jarvis stop', description: 'Stop Jarvis daemon' },
100
+ { cmd: '/jarvis pause', label: '/jarvis pause', description: 'Pause daemon' },
101
+ { cmd: '/jarvis resume', label: '/jarvis resume', description: 'Resume daemon' },
102
+ { cmd: '/jarvis clear', label: '/jarvis clear', description: 'Clear completed tasks' },
103
+ ],
104
+ },
90
105
  {
91
106
  category: 'Validation Matrix', color: '#00cc66',
92
107
  items: [
@@ -274,6 +289,10 @@ export async function chatCommand(options) {
274
289
  const sessionBuffer = new SessionBuffer();
275
290
  // Bug journal (persistent bug tracking)
276
291
  const bugJournal = new BugJournal(process.cwd());
292
+ // Jarvis task queue (persistent task orchestration)
293
+ const jarvisQueue = new JarvisQueue(process.cwd());
294
+ let jarvisDaemonSession = null;
295
+ let jarvisPaused = false;
277
296
  // Browser controller (lazy — instantiated on /browser or agent tool use)
278
297
  let browserController;
279
298
  let visionProcessor;
@@ -441,8 +460,8 @@ export async function chatCommand(options) {
441
460
  // === Register CLI ↔ Web control protocol ===
442
461
  if (brainUrl) {
443
462
  try {
444
- const { registerControlHandlers, setInstanceMeta, getBrainToken, pushSessionCreated, pushSessionUpdate, pushSessionRemoved, pushOutputLine, pushBugCreated, pushBugUpdated, pushBrowserScreenshot, pushControlEvent, startRelayClient, } = await import('../brain/generator.js');
445
- const { serializeSession, buildInstanceMeta, resetInstanceStartTime } = await import('../brain/control-protocol.js');
463
+ const { registerControlHandlers, setInstanceMeta, getBrainToken, pushSessionCreated, pushSessionUpdate, pushSessionRemoved, pushOutputLine, pushBugCreated, pushBugUpdated, pushBrowserScreenshot, pushControlEvent, startRelayClient, pushJarvisTaskCreated, pushJarvisTaskUpdated, pushJarvisStatusChanged, } = await import('../brain/generator.js');
464
+ const { serializeSession, buildInstanceMeta, resetInstanceStartTime, serializeJarvisTask } = await import('../brain/control-protocol.js');
446
465
  resetInstanceStartTime();
447
466
  // Collected findings for getFindings() handler
448
467
  const collectedFindings = [];
@@ -696,6 +715,79 @@ export async function chatCommand(options) {
696
715
  fixedAt: b.fixedAt,
697
716
  fixDescription: b.fixDescription,
698
717
  })),
718
+ startJarvis: () => {
719
+ if (jarvisDaemonSession && jarvisDaemonSession.status === 'running')
720
+ return jarvisDaemonSession.id;
721
+ const bgSession = sessionMgr.create('\u{1F916} Jarvis', '\u{1F916}', agentHistory);
722
+ bgSession.start();
723
+ wireSessionOutput(bgSession);
724
+ pushSessionCreated(serializeSession(bgSession));
725
+ jarvisDaemonSession = bgSession;
726
+ jarvisPaused = false;
727
+ (async () => {
728
+ try {
729
+ await runJarvisDaemon(jarvisQueue, {
730
+ sendMessage: async (prompt) => {
731
+ bgSession.controller.reset();
732
+ const rth = { text: '' };
733
+ const orig = bgSession.buffer.addAssistantSummary.bind(bgSession.buffer);
734
+ bgSession.buffer.addAssistantSummary = (t) => { rth.text = t; orig(t); };
735
+ await sendAgentMessage(prompt, bgSession.history, provider, project, spiralEngine, config, permissions, bgSession.undoStack, checkpointStore, bgSession.controller, new ActivityIndicator(), bgSession.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; }, undefined, { enabled: false, verbose: false, strict: false });
736
+ bgSession.buffer.addAssistantSummary = orig;
737
+ return rth.text;
738
+ },
739
+ isAborted: () => bgSession.controller.isAborted,
740
+ isPaused: () => jarvisPaused,
741
+ onTaskStart: (task) => { bgSession.capture(`\u25B6 Task #${task.id}: ${task.title}`); pushSessionUpdate(serializeSession(bgSession)); },
742
+ onTaskComplete: (task, result) => { bgSession.capture(`\u2713 Task #${task.id} done: ${result.slice(0, 100)}`); pushSessionUpdate(serializeSession(bgSession)); },
743
+ onTaskFailed: (task, error) => { bgSession.capture(`\u2717 Task #${task.id} failed: ${error.slice(0, 100)}`); pushSessionUpdate(serializeSession(bgSession)); },
744
+ updateStatus: () => updateStatusBar(),
745
+ storeInSpiral: spiralEngine ? async (content, type, tags) => {
746
+ try {
747
+ await spiralEngine.add(content, { type: type, tags });
748
+ }
749
+ catch { }
750
+ } : undefined,
751
+ });
752
+ }
753
+ catch (err) {
754
+ if (!(err instanceof AgentAbortError))
755
+ bgSession.capture(`Jarvis error: ${err}`);
756
+ }
757
+ sessionMgr.complete(bgSession.id, { text: 'Jarvis stopped', steps: [], errors: bgSession.controller.isAborted ? ['Stopped'] : [], durationMs: bgSession.elapsed });
758
+ pushSessionUpdate(serializeSession(bgSession));
759
+ jarvisDaemonSession = null;
760
+ })();
761
+ return bgSession.id;
762
+ },
763
+ stopJarvis: () => {
764
+ if (!jarvisDaemonSession)
765
+ return false;
766
+ jarvisDaemonSession.abort();
767
+ pushSessionUpdate(serializeSession(jarvisDaemonSession));
768
+ jarvisDaemonSession = null;
769
+ return true;
770
+ },
771
+ pauseJarvis: () => {
772
+ if (!jarvisDaemonSession || jarvisPaused)
773
+ return false;
774
+ jarvisPaused = true;
775
+ jarvisQueue.setDaemonState('paused');
776
+ return true;
777
+ },
778
+ resumeJarvis: () => {
779
+ if (!jarvisDaemonSession || !jarvisPaused)
780
+ return false;
781
+ jarvisPaused = false;
782
+ jarvisQueue.setDaemonState('running');
783
+ return true;
784
+ },
785
+ addJarvisTask: (title, description, opts) => {
786
+ const task = jarvisQueue.addTask(title, description, opts);
787
+ return serializeJarvisTask(task);
788
+ },
789
+ listJarvisTasks: () => jarvisQueue.getAllTasks().map(serializeJarvisTask),
790
+ getJarvisStatus: () => jarvisQueue.getStatus(),
699
791
  });
700
792
  // Wire bug journal change events to brain server
701
793
  bugJournal.setOnChange((event, bug) => {
@@ -717,6 +809,16 @@ export async function chatCommand(options) {
717
809
  pushBugUpdated(bugInfo);
718
810
  }
719
811
  });
812
+ // Wire Jarvis queue change events to brain server
813
+ jarvisQueue.setOnChange((event, task) => {
814
+ const info = serializeJarvisTask(task);
815
+ if (event === 'task_created') {
816
+ pushJarvisTaskCreated(info);
817
+ }
818
+ else {
819
+ pushJarvisTaskUpdated(info);
820
+ }
821
+ });
720
822
  // Wire browser screenshots to brain server
721
823
  pushScreenshotToBrainFn = (info) => {
722
824
  pushBrowserScreenshot({
@@ -797,6 +899,13 @@ export async function chatCommand(options) {
797
899
  status: b.status, createdAt: b.createdAt, updatedAt: b.updatedAt,
798
900
  fixedAt: b.fixedAt, fixDescription: b.fixDescription,
799
901
  })),
902
+ startJarvis: () => '',
903
+ stopJarvis: () => false,
904
+ pauseJarvis: () => false,
905
+ resumeJarvis: () => false,
906
+ addJarvisTask: () => ({ id: 0, title: '', description: '', status: 'pending', priority: 'medium', createdAt: Date.now(), updatedAt: Date.now(), retries: 0, maxRetries: 3, tags: [] }),
907
+ listJarvisTasks: () => [],
908
+ getJarvisStatus: () => ({ daemonState: 'stopped', currentTaskId: null, pendingCount: 0, completedCount: 0, failedCount: 0, totalCount: 0, uptimeMs: 0 }),
800
909
  }, updateMeta).catch(() => { });
801
910
  }
802
911
  }
@@ -1682,7 +1791,64 @@ export async function chatCommand(options) {
1682
1791
  default:
1683
1792
  renderInfo(`Validation Matrix: ${validationEnabled ? 'ON' : 'OFF'} | Verbose: ${validationVerbose ? 'ON' : 'OFF'} | Strict: ${validationStrict ? 'ON' : 'OFF'}`);
1684
1793
  }
1685
- }, sessionMgr, registerBrainHandlers, (active) => { isAtPrompt = active; }, bugJournal);
1794
+ }, sessionMgr, registerBrainHandlers, (active) => { isAtPrompt = active; }, bugJournal, {
1795
+ queue: jarvisQueue,
1796
+ getSession: () => jarvisDaemonSession,
1797
+ setSession: (s) => { jarvisDaemonSession = s; },
1798
+ isPaused: () => jarvisPaused,
1799
+ setPaused: (v) => { jarvisPaused = v; },
1800
+ startDaemon: () => {
1801
+ const bgSession = sessionMgr.create('\u{1F916} Jarvis', '\u{1F916}', agentHistory);
1802
+ bgSession.start();
1803
+ jarvisDaemonSession = bgSession;
1804
+ jarvisPaused = false;
1805
+ renderInfo(chalk.hex('#ff00ff').bold('\u{1F916} Jarvis daemon started'));
1806
+ renderInfo(chalk.dim('Add tasks with /jarvis task "description"'));
1807
+ (async () => {
1808
+ try {
1809
+ await runJarvisDaemon(jarvisQueue, {
1810
+ sendMessage: async (prompt) => {
1811
+ bgSession.controller.reset();
1812
+ const rth = { text: '' };
1813
+ const orig = bgSession.buffer.addAssistantSummary.bind(bgSession.buffer);
1814
+ bgSession.buffer.addAssistantSummary = (t) => { rth.text = t; orig(t); };
1815
+ await sendAgentMessage(prompt, bgSession.history, provider, project, spiralEngine, config, permissions, bgSession.undoStack, checkpointStore, bgSession.controller, new ActivityIndicator(), bgSession.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; }, undefined, { enabled: false, verbose: false, strict: false });
1816
+ bgSession.buffer.addAssistantSummary = orig;
1817
+ return rth.text;
1818
+ },
1819
+ isAborted: () => bgSession.controller.isAborted,
1820
+ isPaused: () => jarvisPaused,
1821
+ onTaskStart: (task) => {
1822
+ bgSession.capture(`\u25B6 Task #${task.id}: ${task.title}`);
1823
+ renderInfo(chalk.hex('#ff00ff')(`\u25B6 Jarvis: Starting task #${task.id} \u2014 ${task.title}`));
1824
+ },
1825
+ onTaskComplete: (task, result) => {
1826
+ bgSession.capture(`\u2713 Task #${task.id} done: ${result.slice(0, 100)}`);
1827
+ renderInfo(chalk.hex('#ff00ff')(`\u2713 Jarvis: Task #${task.id} completed`));
1828
+ },
1829
+ onTaskFailed: (task, error) => {
1830
+ bgSession.capture(`\u2717 Task #${task.id} failed: ${error.slice(0, 100)}`);
1831
+ renderInfo(chalk.hex('#ff00ff')(`\u2717 Jarvis: Task #${task.id} failed \u2014 ${error.slice(0, 60)}`));
1832
+ },
1833
+ updateStatus: () => updateStatusBar(),
1834
+ storeInSpiral: spiralEngine ? async (content, type, tags) => {
1835
+ try {
1836
+ await spiralEngine.add(content, { type: type, tags });
1837
+ }
1838
+ catch { }
1839
+ } : undefined,
1840
+ });
1841
+ }
1842
+ catch (err) {
1843
+ if (!(err instanceof AgentAbortError))
1844
+ bgSession.capture(`Jarvis error: ${err}`);
1845
+ }
1846
+ sessionMgr.complete(bgSession.id, { text: 'Jarvis stopped', steps: [], errors: bgSession.controller.isAborted ? ['Stopped'] : [], durationMs: bgSession.elapsed });
1847
+ jarvisDaemonSession = null;
1848
+ renderInfo(chalk.hex('#ff00ff')('\u{1F916} Jarvis daemon stopped'));
1849
+ })();
1850
+ },
1851
+ });
1686
1852
  if (handled === 'exit') {
1687
1853
  spiralEngine?.close();
1688
1854
  rl.close();
@@ -2256,7 +2422,7 @@ function showFullAutonomousWarning() {
2256
2422
  process.stdout.write(d('\u2502 ') + d('ESC = stop agent if needed.') + d(' \u2502') + '\n');
2257
2423
  process.stdout.write(d('\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F') + '\n\n');
2258
2424
  }
2259
- async function handleSlashCommand(input, messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal) {
2425
+ async function handleSlashCommand(input, messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx) {
2260
2426
  const parts = input.split(/\s+/);
2261
2427
  const cmd = parts[0].toLowerCase();
2262
2428
  switch (cmd) {
@@ -2279,7 +2445,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
2279
2445
  rl.resume();
2280
2446
  if (helpIdx >= 0 && helpCmds[helpIdx]) {
2281
2447
  // Execute the selected command
2282
- return handleSlashCommand(helpCmds[helpIdx], messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal);
2448
+ return handleSlashCommand(helpCmds[helpIdx], messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx);
2283
2449
  }
2284
2450
  break;
2285
2451
  }
@@ -2835,6 +3001,150 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
2835
3001
  await onAutonomous('security');
2836
3002
  }
2837
3003
  break;
3004
+ case '/jarvis': {
3005
+ if (!jarvisCtx) {
3006
+ renderInfo('Jarvis not available.');
3007
+ break;
3008
+ }
3009
+ const sub = parts[1]?.toLowerCase();
3010
+ const jq = jarvisCtx.queue;
3011
+ if (!sub || sub === 'start') {
3012
+ const existing = jarvisCtx.getSession();
3013
+ if (existing && existing.status === 'running') {
3014
+ renderInfo(chalk.hex('#ff00ff')('Jarvis is already running.'));
3015
+ const sl = jq.getStatusLine();
3016
+ if (sl)
3017
+ renderInfo(sl);
3018
+ break;
3019
+ }
3020
+ jarvisCtx.startDaemon();
3021
+ }
3022
+ else if (sub === 'task') {
3023
+ const taskText = input.replace(/^\/jarvis\s+task\s*/i, '').trim();
3024
+ if (!taskText) {
3025
+ renderInfo('Usage: /jarvis task "Do something"');
3026
+ break;
3027
+ }
3028
+ let title = taskText;
3029
+ let description = '';
3030
+ const quoteMatch = taskText.match(/^["'](.+?)["']\s*(.*)/s);
3031
+ if (quoteMatch) {
3032
+ title = quoteMatch[1];
3033
+ description = quoteMatch[2] || title;
3034
+ }
3035
+ else if (taskText.includes('\u2014') || taskText.includes('--')) {
3036
+ const sep = taskText.includes('\u2014') ? '\u2014' : '--';
3037
+ const sepIdx = taskText.indexOf(sep);
3038
+ title = taskText.slice(0, sepIdx).trim();
3039
+ description = taskText.slice(sepIdx + sep.length).trim() || title;
3040
+ }
3041
+ else {
3042
+ description = title;
3043
+ }
3044
+ const task = jq.addTask(title, description);
3045
+ renderInfo(chalk.hex('#ff00ff')(`\u2714 Task #${task.id} added: ${task.title}`));
3046
+ }
3047
+ else if (sub === 'tasks') {
3048
+ const tasks = jq.getAllTasks();
3049
+ if (tasks.length === 0) {
3050
+ renderInfo('No tasks in queue. Add with /jarvis task "..."');
3051
+ break;
3052
+ }
3053
+ const statusIcons = {
3054
+ pending: chalk.yellow('\u25CB'), running: chalk.hex('#ff00ff')('\u25CF'),
3055
+ completed: chalk.green('\u2713'), failed: chalk.red('\u2717'), paused: chalk.dim('\u25CB'),
3056
+ };
3057
+ process.stdout.write('\n');
3058
+ for (const t of tasks) {
3059
+ const icon = statusIcons[t.status] || '\u25CB';
3060
+ const priColor = t.priority === 'high' ? '#ff3333' : t.priority === 'medium' ? '#ffaa00' : '#888888';
3061
+ process.stdout.write(` ${icon} ${chalk.bold('#' + t.id)} ${chalk.hex(priColor)(`[${t.priority}]`)} ${t.title}\n`);
3062
+ if (t.result)
3063
+ process.stdout.write(chalk.dim(` Result: ${t.result.slice(0, 80)}\n`));
3064
+ if (t.error)
3065
+ process.stdout.write(chalk.red(` Error: ${t.error.slice(0, 80)}\n`));
3066
+ }
3067
+ process.stdout.write('\n');
3068
+ const sl = jq.getStatusLine();
3069
+ if (sl)
3070
+ renderInfo(sl);
3071
+ }
3072
+ else if (sub === 'status') {
3073
+ const taskId = parts[2] ? parseInt(parts[2], 10) : NaN;
3074
+ if (!isNaN(taskId)) {
3075
+ const task = jq.getAllTasks().find((t) => t.id === taskId);
3076
+ if (!task) {
3077
+ renderInfo(`Task #${taskId} not found.`);
3078
+ }
3079
+ else {
3080
+ process.stdout.write('\n');
3081
+ process.stdout.write(chalk.hex('#ff00ff').bold(` Task #${task.id}: ${task.title}\n`));
3082
+ process.stdout.write(` Status: ${task.status} | Priority: ${task.priority}\n`);
3083
+ process.stdout.write(` Created: ${new Date(task.createdAt).toLocaleString()}\n`);
3084
+ if (task.startedAt)
3085
+ process.stdout.write(` Started: ${new Date(task.startedAt).toLocaleString()}\n`);
3086
+ if (task.completedAt)
3087
+ process.stdout.write(` Completed: ${new Date(task.completedAt).toLocaleString()}\n`);
3088
+ if (task.description !== task.title)
3089
+ process.stdout.write(chalk.dim(` Description: ${task.description}\n`));
3090
+ if (task.result)
3091
+ process.stdout.write(chalk.green(` Result: ${task.result}\n`));
3092
+ if (task.error)
3093
+ process.stdout.write(chalk.red(` Error: ${task.error}\n`));
3094
+ process.stdout.write('\n');
3095
+ }
3096
+ }
3097
+ else {
3098
+ const status = jq.getStatus();
3099
+ process.stdout.write('\n');
3100
+ process.stdout.write(chalk.hex('#ff00ff').bold(' \u{1F916} Jarvis Status\n'));
3101
+ process.stdout.write(` Daemon: ${status.daemonState}`);
3102
+ if (status.currentTaskId)
3103
+ process.stdout.write(` (task #${status.currentTaskId})`);
3104
+ process.stdout.write('\n');
3105
+ process.stdout.write(` Pending: ${status.pendingCount} | Completed: ${status.completedCount} | Failed: ${status.failedCount}\n`);
3106
+ if (status.uptimeMs > 0)
3107
+ process.stdout.write(` Uptime: ${Math.floor(status.uptimeMs / 1000)}s\n`);
3108
+ process.stdout.write('\n');
3109
+ }
3110
+ }
3111
+ else if (sub === 'stop') {
3112
+ const s = jarvisCtx.getSession();
3113
+ if (!s) {
3114
+ renderInfo('Jarvis is not running.');
3115
+ break;
3116
+ }
3117
+ s.abort();
3118
+ jarvisCtx.setSession(null);
3119
+ renderInfo(chalk.hex('#ff00ff')('\u{1F916} Jarvis daemon stopped'));
3120
+ }
3121
+ else if (sub === 'pause') {
3122
+ if (!jarvisCtx.getSession() || jarvisCtx.isPaused()) {
3123
+ renderInfo(jarvisCtx.isPaused() ? 'Jarvis is already paused.' : 'Jarvis is not running.');
3124
+ break;
3125
+ }
3126
+ jarvisCtx.setPaused(true);
3127
+ jq.setDaemonState('paused');
3128
+ renderInfo(chalk.hex('#ff00ff')('\u23F8 Jarvis paused'));
3129
+ }
3130
+ else if (sub === 'resume') {
3131
+ if (!jarvisCtx.getSession() || !jarvisCtx.isPaused()) {
3132
+ renderInfo(!jarvisCtx.isPaused() ? 'Jarvis is not paused.' : 'Jarvis is not running.');
3133
+ break;
3134
+ }
3135
+ jarvisCtx.setPaused(false);
3136
+ jq.setDaemonState('running');
3137
+ renderInfo(chalk.hex('#ff00ff')('\u25B6 Jarvis resumed'));
3138
+ }
3139
+ else if (sub === 'clear') {
3140
+ const removed = jq.clearCompleted();
3141
+ renderInfo(`Cleared ${removed} completed task(s).`);
3142
+ }
3143
+ else {
3144
+ renderInfo('Usage: /jarvis [start|task|tasks|status|stop|pause|resume|clear]');
3145
+ }
3146
+ break;
3147
+ }
2838
3148
  case '/monitor': {
2839
3149
  if (!onAutonomous)
2840
3150
  break;