gbos 1.3.3 → 1.3.5

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": "gbos",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "GBOS - Command line interface for GBOS services",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -6,7 +6,7 @@ const program = new Command();
6
6
  const authCommand = require('./commands/auth');
7
7
  const connectCommand = require('./commands/connect');
8
8
  const logoutCommand = require('./commands/logout');
9
- const { tasksCommand, nextTaskCommand, continueCommand, fallbackCommand, autoCommand } = require('./commands/tasks');
9
+ const { tasksCommand, nextTaskCommand, continueCommand, fallbackCommand, autoCommand, addTaskCommand } = require('./commands/tasks');
10
10
  const config = require('./lib/config');
11
11
  const { displayStatus, printBanner } = require('./lib/display');
12
12
 
@@ -123,6 +123,12 @@ program
123
123
  .description('Automatically work through all tasks and poll for new ones')
124
124
  .action(autoCommand);
125
125
 
126
+ program
127
+ .command('add_task')
128
+ .alias('add')
129
+ .description('Create a new task interactively')
130
+ .action(addTaskCommand);
131
+
126
132
  program
127
133
  .command('logout')
128
134
  .description('Log out from GBOS services and clear credentials')
@@ -139,7 +145,7 @@ program
139
145
  cmd.outputHelp();
140
146
  } else {
141
147
  console.log(`Unknown command: ${command}`);
142
- console.log('Available commands: auth, connect, disconnect, status, tasks, next, continue, fallback, auto, logout, help');
148
+ console.log('Available commands: auth, connect, disconnect, status, tasks, next, continue, fallback, auto, add_task, logout, help');
143
149
  }
144
150
  } else {
145
151
  program.outputHelp();
@@ -1,6 +1,61 @@
1
1
  const api = require('../lib/api');
2
2
  const config = require('../lib/config');
3
3
  const { displayMessageBox, printBanner, printStatusTable, fg, LOGO_LIGHT, LOGO_NAVY, RESET, BOLD, DIM, getTerminalWidth } = require('../lib/display');
4
+ const readline = require('readline');
5
+
6
+ // Colors for prompts
7
+ const CYAN = '\x1b[36m';
8
+ const YELLOW = '\x1b[33m';
9
+ const GREEN = '\x1b[32m';
10
+
11
+ // Create readline interface for interactive prompts
12
+ function createReadlineInterface() {
13
+ return readline.createInterface({
14
+ input: process.stdin,
15
+ output: process.stdout,
16
+ });
17
+ }
18
+
19
+ // Prompt for text input
20
+ async function promptText(rl, question, defaultValue = '') {
21
+ return new Promise((resolve) => {
22
+ const defaultStr = defaultValue ? ` ${DIM}(${defaultValue})${RESET}` : '';
23
+ rl.question(` ${CYAN}?${RESET} ${question}${defaultStr}: `, (answer) => {
24
+ resolve(answer.trim() || defaultValue);
25
+ });
26
+ });
27
+ }
28
+
29
+ // Prompt for yes/no
30
+ async function promptYesNo(rl, question, defaultValue = false) {
31
+ return new Promise((resolve) => {
32
+ const defaultStr = defaultValue ? 'Y/n' : 'y/N';
33
+ rl.question(` ${CYAN}?${RESET} ${question} ${DIM}(${defaultStr})${RESET}: `, (answer) => {
34
+ const lower = answer.trim().toLowerCase();
35
+ if (lower === '') resolve(defaultValue);
36
+ else resolve(lower === 'y' || lower === 'yes');
37
+ });
38
+ });
39
+ }
40
+
41
+ // Prompt for selection from list
42
+ async function promptSelect(rl, question, options) {
43
+ console.log(`\n ${CYAN}?${RESET} ${question}`);
44
+ options.forEach((opt, i) => {
45
+ console.log(` ${DIM}${i + 1}.${RESET} ${opt.label || opt}`);
46
+ });
47
+
48
+ return new Promise((resolve) => {
49
+ rl.question(` ${DIM}Enter number (1-${options.length})${RESET}: `, (answer) => {
50
+ const num = parseInt(answer.trim(), 10);
51
+ if (num >= 1 && num <= options.length) {
52
+ resolve(options[num - 1].value || options[num - 1]);
53
+ } else {
54
+ resolve(options[0].value || options[0]);
55
+ }
56
+ });
57
+ });
58
+ }
4
59
 
5
60
  // Format task for display
6
61
  function formatTask(task, index) {
@@ -96,11 +151,6 @@ async function tasksCommand() {
96
151
  const response = await api.getTasks();
97
152
  const tasks = response.data || [];
98
153
 
99
- if (tasks.length === 0) {
100
- console.log(`${DIM}No tasks assigned to this node.${RESET}\n`);
101
- return;
102
- }
103
-
104
154
  // Display tasks in a table
105
155
  const termWidth = getTerminalWidth();
106
156
  const tableWidth = Math.min(100, termWidth - 4);
@@ -109,17 +159,42 @@ async function tasksCommand() {
109
159
  console.log(`${BOLD} Tasks for ${connection.node?.name || 'this node'}${RESET}`);
110
160
  console.log(`${fg(...LOGO_NAVY)}${'─'.repeat(tableWidth)}${RESET}\n`);
111
161
 
112
- tasks.forEach((task, index) => {
113
- const formatted = formatTask(task, index);
114
- console.log(` ${formatted.statusDisplay} ${BOLD}${formatted.title}${RESET}`);
115
- console.log(` ${DIM}ID: ${formatted.id} | Priority: ${formatted.priority}${RESET}`);
116
- if (index < tasks.length - 1) console.log('');
117
- });
162
+ if (tasks.length === 0) {
163
+ console.log(` ${DIM}No tasks assigned to this node.${RESET}\n`);
164
+ } else {
165
+ tasks.forEach((task, index) => {
166
+ const formatted = formatTask(task, index);
167
+ console.log(` ${formatted.statusDisplay} ${BOLD}${formatted.title}${RESET}`);
168
+ console.log(` ${DIM}ID: ${formatted.id} | Priority: ${formatted.priority}${RESET}`);
169
+ if (index < tasks.length - 1) console.log('');
170
+ });
171
+ }
118
172
 
119
173
  console.log(`\n${fg(...LOGO_NAVY)}${'─'.repeat(tableWidth)}${RESET}`);
120
174
  console.log(`${DIM} Total: ${tasks.length} task(s)${RESET}\n`);
121
175
 
176
+ // Show meta info if available
177
+ if (response.meta) {
178
+ if (response.meta.total > tasks.length) {
179
+ console.log(`${DIM} Showing ${tasks.length} of ${response.meta.total} total tasks${RESET}\n`);
180
+ }
181
+ }
182
+
122
183
  } catch (error) {
184
+ if (error.status === 404 || error.message?.includes('not found')) {
185
+ // Fallback: try to get info from next task endpoint
186
+ try {
187
+ const nextResponse = await api.getNextTask();
188
+ console.log(`${DIM}Tasks list endpoint not available yet.${RESET}`);
189
+ if (nextResponse.pending_tasks_count) {
190
+ console.log(`${DIM}Pending tasks in queue: ${nextResponse.pending_tasks_count}${RESET}`);
191
+ }
192
+ console.log(`${DIM}Use "gbos next" or "gbos continue" to get the next task.${RESET}\n`);
193
+ } catch (e) {
194
+ console.log(`${DIM}No tasks available.${RESET}\n`);
195
+ }
196
+ return;
197
+ }
123
198
  displayMessageBox('Error', error.message, 'error');
124
199
  process.exit(1);
125
200
  }
@@ -344,11 +419,203 @@ async function autoCommand() {
344
419
  await runLoop();
345
420
  }
346
421
 
422
+ // Add task command - interactive task creation
423
+ async function addTaskCommand() {
424
+ if (!config.isAuthenticated()) {
425
+ displayMessageBox('Not Authenticated', 'Please run "gbos auth" first.', 'warning');
426
+ process.exit(1);
427
+ }
428
+
429
+ const connection = config.getConnection();
430
+ if (!connection) {
431
+ displayMessageBox('Not Connected', 'Please run "gbos connect" first.', 'warning');
432
+ process.exit(1);
433
+ }
434
+
435
+ const rl = createReadlineInterface();
436
+
437
+ console.log(`\n${BOLD}Create New Task${RESET}`);
438
+ console.log(`${DIM}Fill in the task details below. Press Enter to use defaults.${RESET}\n`);
439
+
440
+ try {
441
+ // Required fields
442
+ const title = await promptText(rl, 'Task title', '');
443
+ if (!title) {
444
+ console.log(`\n${DIM}Task title is required. Cancelled.${RESET}\n`);
445
+ rl.close();
446
+ return;
447
+ }
448
+
449
+ const description = await promptText(rl, 'Description (what needs to be done)', '');
450
+
451
+ // Priority selection
452
+ const priority = await promptSelect(rl, 'Priority level:', [
453
+ { label: 'Low', value: 'low' },
454
+ { label: 'Normal', value: 'normal' },
455
+ { label: 'High', value: 'high' },
456
+ { label: 'Urgent', value: 'urgent' },
457
+ ]);
458
+
459
+ // Task type
460
+ const taskType = await promptSelect(rl, 'Task type:', [
461
+ { label: 'Feature - New functionality', value: 'feature' },
462
+ { label: 'Bug Fix - Fix an issue', value: 'bug' },
463
+ { label: 'Refactor - Improve code', value: 'refactor' },
464
+ { label: 'Documentation - Update docs', value: 'docs' },
465
+ { label: 'Test - Add/update tests', value: 'test' },
466
+ { label: 'Other', value: 'other' },
467
+ ]);
468
+
469
+ // Prompt/instructions for coding agent
470
+ console.log(`\n ${CYAN}?${RESET} Detailed instructions for the coding agent:`);
471
+ console.log(` ${DIM}(Enter a blank line to finish)${RESET}`);
472
+
473
+ let prompt = '';
474
+ const promptLines = [];
475
+
476
+ const collectPrompt = () => {
477
+ return new Promise((resolve) => {
478
+ const askLine = () => {
479
+ rl.question(' ', (line) => {
480
+ if (line === '') {
481
+ resolve(promptLines.join('\n'));
482
+ } else {
483
+ promptLines.push(line);
484
+ askLine();
485
+ }
486
+ });
487
+ };
488
+ askLine();
489
+ });
490
+ };
491
+
492
+ prompt = await collectPrompt();
493
+
494
+ // Optional: Add attachments
495
+ const hasAttachments = await promptYesNo(rl, 'Add attachment URLs?', false);
496
+ const attachments = [];
497
+
498
+ if (hasAttachments) {
499
+ console.log(` ${DIM}Enter attachment URLs (blank line to finish):${RESET}`);
500
+ let addingAttachments = true;
501
+ while (addingAttachments) {
502
+ const url = await promptText(rl, 'URL', '');
503
+ if (!url) {
504
+ addingAttachments = false;
505
+ } else {
506
+ const name = await promptText(rl, 'Name for this attachment', `Attachment ${attachments.length + 1}`);
507
+ attachments.push({ url, name });
508
+ }
509
+ }
510
+ }
511
+
512
+ // Optional: Add metadata
513
+ const hasMetadata = await promptYesNo(rl, 'Add custom metadata (JSON)?', false);
514
+ let metadata = {};
515
+
516
+ if (hasMetadata) {
517
+ const metadataStr = await promptText(rl, 'Enter JSON metadata', '{}');
518
+ try {
519
+ metadata = JSON.parse(metadataStr);
520
+ } catch (e) {
521
+ console.log(` ${YELLOW}Warning: Invalid JSON, using empty metadata${RESET}`);
522
+ }
523
+ }
524
+
525
+ // Due date (optional)
526
+ const hasDueDate = await promptYesNo(rl, 'Set a due date?', false);
527
+ let dueDate = null;
528
+
529
+ if (hasDueDate) {
530
+ const dueDateStr = await promptText(rl, 'Due date (YYYY-MM-DD)', '');
531
+ if (dueDateStr && /^\d{4}-\d{2}-\d{2}$/.test(dueDateStr)) {
532
+ dueDate = dueDateStr;
533
+ }
534
+ }
535
+
536
+ rl.close();
537
+
538
+ // Build task data
539
+ const taskData = {
540
+ title,
541
+ description,
542
+ prompt: prompt || description,
543
+ priority,
544
+ task_type: taskType,
545
+ node_id: connection.node?.id,
546
+ application_id: connection.application?.id || connection.node?.application_id,
547
+ };
548
+
549
+ if (attachments.length > 0) {
550
+ taskData.attachments = attachments;
551
+ }
552
+
553
+ if (Object.keys(metadata).length > 0) {
554
+ taskData.metadata = metadata;
555
+ }
556
+
557
+ if (dueDate) {
558
+ taskData.due_date = dueDate;
559
+ }
560
+
561
+ // Show summary
562
+ console.log(`\n${fg(...LOGO_NAVY)}${'─'.repeat(60)}${RESET}`);
563
+ console.log(`${BOLD} Task Summary${RESET}`);
564
+ console.log(`${fg(...LOGO_NAVY)}${'─'.repeat(60)}${RESET}\n`);
565
+ console.log(` ${DIM}Title:${RESET} ${title}`);
566
+ console.log(` ${DIM}Type:${RESET} ${taskType}`);
567
+ console.log(` ${DIM}Priority:${RESET} ${priority}`);
568
+ if (description) console.log(` ${DIM}Description:${RESET} ${description.substring(0, 50)}${description.length > 50 ? '...' : ''}`);
569
+ if (attachments.length > 0) console.log(` ${DIM}Attachments:${RESET} ${attachments.length} file(s)`);
570
+ if (dueDate) console.log(` ${DIM}Due date:${RESET} ${dueDate}`);
571
+ console.log('');
572
+
573
+ // Confirm and create
574
+ const rl2 = createReadlineInterface();
575
+ const confirm = await promptYesNo(rl2, 'Create this task?', true);
576
+ rl2.close();
577
+
578
+ if (!confirm) {
579
+ console.log(`\n${DIM}Task creation cancelled.${RESET}\n`);
580
+ return;
581
+ }
582
+
583
+ // Create task via API
584
+ console.log(`\n${DIM}Creating task...${RESET}`);
585
+
586
+ try {
587
+ const response = await api.createTask(taskData);
588
+ const task = response.data;
589
+
590
+ console.log(`\n${GREEN}✓${RESET} ${BOLD}Task created successfully!${RESET}`);
591
+ console.log(` ${DIM}Task ID:${RESET} ${task.id || 'N/A'}`);
592
+ console.log(` ${DIM}Status:${RESET} ${task.status || 'pending'}\n`);
593
+
594
+ } catch (error) {
595
+ if (error.status === 404 || error.message?.includes('not found')) {
596
+ console.log(`\n${YELLOW}Note:${RESET} The create task endpoint is not implemented yet.`);
597
+ console.log(`${DIM}Required API: POST /api/v1/cli/tasks${RESET}`);
598
+ console.log(`\n${DIM}Request body that would be sent:${RESET}`);
599
+ console.log(JSON.stringify(taskData, null, 2));
600
+ console.log('');
601
+ } else {
602
+ throw error;
603
+ }
604
+ }
605
+
606
+ } catch (error) {
607
+ rl.close();
608
+ displayMessageBox('Error', error.message, 'error');
609
+ process.exit(1);
610
+ }
611
+ }
612
+
347
613
  module.exports = {
348
614
  tasksCommand,
349
615
  nextTaskCommand,
350
616
  continueCommand,
351
617
  fallbackCommand,
352
618
  autoCommand,
619
+ addTaskCommand,
353
620
  generateAgentPrompt,
354
621
  };
package/src/lib/api.js CHANGED
@@ -176,6 +176,13 @@ class GbosApiClient {
176
176
  });
177
177
  }
178
178
 
179
+ async createTask(taskData) {
180
+ return this.request('/cli/tasks', {
181
+ method: 'POST',
182
+ body: taskData,
183
+ });
184
+ }
185
+
179
186
  // Activity logging
180
187
  async logActivity(activity) {
181
188
  return this.request('/cli/activity', {
@@ -583,6 +583,7 @@ function displayConnectBanner(data) {
583
583
  console.log(` ${cmd}continue${RESET} Continue working on current/next task`);
584
584
  console.log(` ${cmd}fallback${RESET} Cancel current task and revert to last completed state`);
585
585
  console.log(` ${cmd}auto${RESET} Automatically work through all tasks and poll for new ones`);
586
+ console.log(` ${cmd}add_task${RESET} Create a new task interactively`);
586
587
  console.log(` ${cmd}logout${RESET} ${dim}[options]${RESET} Log out from GBOS services and clear credentials`);
587
588
  console.log(` ${cmd}help${RESET} ${dim}[command]${RESET} Display help for a specific command\n`);
588
589