baton-issue-tracker 1.13.3 → 1.14.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": "baton-issue-tracker",
3
- "version": "1.13.3",
3
+ "version": "1.14.0",
4
4
  "description": "A CLI issue tracker for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
package/source/cli.js CHANGED
@@ -28,8 +28,10 @@ import { run as runRegister } from './commands/register.js';
28
28
  import { run as runAgents } from './commands/agents.js';
29
29
  import { run as runWhoami } from './commands/whoami.js';
30
30
  import { run as runSubmit } from './commands/submit.js';
31
+ import { run as runUnassign } from './commands/unassign.js';
31
32
  import { run as runUnclaim } from './commands/unclaim.js';
32
33
  import { run as runClaim } from './commands/claim.js';
34
+ import { run as runAssign} from './commands/assign.js';
33
35
 
34
36
  import { authenticateContext } from './services/authService.js';
35
37
 
@@ -56,6 +58,7 @@ Commands:
56
58
  update Updates an issue's specified fields
57
59
  delete Deletes an issue
58
60
  log Show activity history for an issue
61
+ unassign Removes all assignees from issue
59
62
 
60
63
  Options:
61
64
  init --force Re-initialize an existing tracker database
@@ -94,6 +97,7 @@ Options:
94
97
  update --json Output as JSON (for AI agents)
95
98
  delete <id> [--yes]
96
99
  log <id> [--json]
100
+ unassign <id> [--json] Output as JSON (for AI agents)
97
101
 
98
102
  Examples:
99
103
  baton init
@@ -120,6 +124,8 @@ Examples:
120
124
  baton update 3 --title "Revised title"
121
125
  baton update 7 --status closed --priority medium
122
126
  baton log 5
127
+ baton unassign 12
128
+ baton unassign 11 --json
123
129
  `;
124
130
 
125
131
  /**
@@ -156,7 +162,9 @@ async function main() {
156
162
  delete: () => runDelete(args),
157
163
  log: () => runLog(args),
158
164
  submit: () => runSubmit(args),
165
+ unassign: () => runUnassign(args),
159
166
  unclaim: () => runUnclaim(args),
167
+ assign: () => runAssign(args),
160
168
  };
161
169
 
162
170
  const handler = handlers[command];
@@ -0,0 +1,66 @@
1
+ // commands/assign.js
2
+ // assign command for the issue tracker
3
+ // usage: baton assign <id> <agent-name> [--json]
4
+ // AI was consulted for a portion of this file
5
+
6
+ import { getAgentByName } from '../services/agentsService.js';
7
+ import { assignIssue } from '../services/issuesService.js';
8
+ import { getCurrentActor } from '../services/authService.js';
9
+ import { hasFlag, renderOutput } from '../util.js';
10
+
11
+ const DECIMAL_BASE = 10;
12
+
13
+ /**
14
+ * Runs the assign command to associate an issue with a registered agent.
15
+ * Usage: baton assign <id> <agent-name> [--json]
16
+ * @param {string[]} args - The command line arguments
17
+ * @returns {Promise<number>} The exit code: 0 is success, 1 is error.
18
+ */
19
+ export async function run(args = []) {
20
+ // Check if the user requested JSON output formatting via the --json flag
21
+ const isJson = hasFlag(args, '--json');
22
+
23
+ // Strip out any flags starting with '-' to isolate the raw [issueIdStr, agentName] positional arguments
24
+ const [issueIdStr, agentName] = args.filter(arg => !arg.startsWith('-'));
25
+
26
+ // Guard Clause: Ensure both the issue ID and the agent name were provided
27
+ if (!issueIdStr || !agentName) {
28
+ console.error('Usage Error: Missing arguments.\nUsage: baton assign <id> <agent-name> [--json]');
29
+ return 1;
30
+ }
31
+
32
+ // Convert the extracted issue ID string into a safe decimal integer
33
+ const issueId = parseInt(issueIdStr, DECIMAL_BASE);
34
+
35
+ // Guard Clause: If parsing failed (e.g., the user typed letters instead of an ID), reject it
36
+ if (isNaN(issueId)) {
37
+ console.error(`Error: Invalid issue ID "${issueIdStr}". Must be an integer.`);
38
+ return 1;
39
+ }
40
+
41
+ try {
42
+ // Look up the current operator. If null, error
43
+ const actor = getCurrentActor() || Object.assign(new Error('No authenticated context found.'), { isAuthErr: true });
44
+ // If that identifier property exists, it means the lookup failed; error
45
+ if (actor.isAuthErr) throw actor;
46
+
47
+ // Look up the target assignee by name. If not found, error
48
+ const target = getAgentByName(agentName) || Object.assign(new Error(`Agent/User "${agentName}" is not registered.`), { isTargetErr: true });
49
+ // If the lookup failed, throw the target error
50
+ if (target.isTargetErr) throw target;
51
+
52
+ // Execute the assignment in the database layer, passing the parsed ID, assignee ID, and operator ID
53
+ const issue = assignIssue(issueId, target.id, actor.id);
54
+
55
+ // Render the final output payload. If --json is on, it prints raw data. Otherwise, executes callback.
56
+ renderOutput(isJson, { status: 'success', issueId: issue.id, assignee: { id: target.id, name: target.name, type: target.type } }, () => {
57
+ console.log(`Success: Issue #${issueId} assigned to "${agentName}"`);
58
+ });
59
+
60
+ return 0; // Command completed successfully
61
+ } catch (error) {
62
+ // Intercepts any thrown operational error (or database crash) and outputs a clean message
63
+ console.error(`Error: ${error.message}`);
64
+ return 1; // Return error exit code
65
+ }
66
+ }
@@ -14,6 +14,7 @@
14
14
  // --description <text> Issue description
15
15
  // --priority <level> low | medium | high (default: low)
16
16
  // --token-limit <n> Optional token budget for this issue
17
+ // --assignee <name> Agent assigned to this issue
17
18
  // -h, --help Show this help
18
19
 
19
20
  import { createIssue } from "../services/issuesService.js";
@@ -24,8 +25,9 @@ import { spawnSync } from "child_process";
24
25
  import { writeFileSync, readFileSync, unlinkSync } from "fs";
25
26
  import { tmpdir } from "os";
26
27
  import { join } from "path";
28
+ import { listAgents, getAgentByName } from '../services/agentsService.js';
27
29
 
28
- const ALLOWED_CREATE_FIELDS = ['title', 'priority', 'tokenLimit', 'description'];
30
+ const ALLOWED_CREATE_FIELDS = ['title', 'priority', 'tokenLimit', 'description', 'assigneeId'];
29
31
 
30
32
  const VALID_FLAGS = new Set([
31
33
  ...ALLOWED_CREATE_FIELDS.map(key => issueSchema[key].flag),
@@ -157,6 +159,19 @@ async function runInteractiveMode() {
157
159
  description = await openEditorForDescription();
158
160
  }
159
161
 
162
+ // Assignee
163
+ const agents = listAgents();
164
+ const assigneeChoices = [
165
+ { name: '(none)', value: null },
166
+ ...agents.map(a => ({ name: `${a.name} (${a.type})`, value: a.id }))
167
+ ];
168
+
169
+ const assigneeId = await select({
170
+ message: 'Assign to:',
171
+ choices: assigneeChoices,
172
+ default: null,
173
+ });
174
+
160
175
  // Preview & confirm
161
176
  console.log("\n" + "-".repeat(48));
162
177
  console.log(` Title : ${title}`);
@@ -181,7 +196,7 @@ async function runInteractiveMode() {
181
196
  process.exit(0);
182
197
  }
183
198
 
184
- return { title, priority, tokenLimit, description };
199
+ return { title, priority, tokenLimit, description, assigneeId };
185
200
  }
186
201
 
187
202
  /**
@@ -217,6 +232,17 @@ export async function run(args) {
217
232
  ? await runInteractiveMode()
218
233
  : parseArgs(args);
219
234
 
235
+ // Getting agent name from ID
236
+ if (options.assigneeId) {
237
+ const target = getAgentByName(options.assigneeId);
238
+ if (!target) {
239
+ const agents = listAgents();
240
+ const names = agents.map(agent => `${agent.name} (${agent.type})`).join(', ');
241
+ throw new Error(`Agent/User "${options.assigneeId}" is not registered.\nRegistered agents: ${names}`);
242
+ }
243
+ options.assigneeId = target.id;
244
+ }
245
+
220
246
  const issue = await createIssue(options);
221
247
  const envelope = { status: "success", issue: serializeIssue(issue) };
222
248
 
@@ -11,9 +11,11 @@
11
11
  // baton init --specs ./path/to/my-specs.md
12
12
  // baton init --specs C:\full\path\to\specs.md
13
13
 
14
- import { readFileSync, existsSync } from 'node:fs';
14
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
15
15
  import { join } from 'node:path';
16
16
  import { initDB } from '../db/index.js';
17
+ import os from 'node:os';
18
+ import { registerAgent } from '../services/agentsService.js';
17
19
  import { Priority } from '../models/issue.js';
18
20
  import {
19
21
  createIssue,
@@ -156,6 +158,37 @@ export async function run(args = []) {
156
158
  clearAllIssues();
157
159
  }
158
160
 
161
+ // auto-register default deployment human user
162
+ let autoRegisterMessage = '';
163
+ try {
164
+ const activeName = process.env.BATON_AGENT || os.userInfo().username;
165
+ registerAgent(activeName, 'human');
166
+ autoRegisterMessage = `Auto-registered default human user: "${activeName}"`;
167
+ } catch (error){
168
+ if (!error.message?.includes('UNIQUE constraint failed')) {
169
+ autoRegisterMessage = `Warning: Could not auto-register default user: ${error.message}`;
170
+ }
171
+ }
172
+
173
+ if (autoRegisterMessage && !isJson) {
174
+ console.log(autoRegisterMessage);
175
+ }
176
+
177
+ const templatePath = join('docs', 'BATON_AGENT_RULES.md');
178
+
179
+ let rulesContent = '';
180
+ if (existsSync(templatePath)) {
181
+ rulesContent = readFileSync(templatePath, 'utf8');
182
+ }
183
+
184
+ const outputPath = join(process.cwd(), 'BATON_AGENT_RULES.md');
185
+ if (existsSync(outputPath) && !flags.force) {
186
+ console.error('Error: BATON_AGENT_RULES.md already exists. Use --force to overwrite.');
187
+ return 1;
188
+ }
189
+
190
+ writeFileSync(outputPath, rulesContent, 'utf8');
191
+
159
192
  const resolvedSpecsPath = resolvePath(flags.specs, DEFAULT_SPECS_PATH);
160
193
  const createdIssues = generateIssuesFromSpecs(flags.specs);
161
194
  const envelope = {
@@ -180,4 +213,4 @@ export async function run(args = []) {
180
213
  });
181
214
 
182
215
  return 0;
183
- }
216
+ }
@@ -8,13 +8,15 @@
8
8
  // --priority <p> Filter by priority: low | medium | high
9
9
  // --limit <n> Max results (default: 50)
10
10
  // --offset <n> Skip first n results (default: 0)
11
+ // --assignee <name> Filter by assignee name
11
12
  // --json Output as JSON (for AI agents)
12
13
  // -h, --help Show this help
13
14
 
14
15
  import { listIssues } from '../services/issuesService.js';
16
+ import { issueSchema } from '../models/schema.js';
17
+ import { listAgents, getAgentByName } from '../services/agentsService.js';
18
+
15
19
  import {
16
- getFlagValue,
17
- getNumericFlag,
18
20
  hasFlag,
19
21
  parseArgs,
20
22
  printIssueTable,
@@ -23,6 +25,13 @@ import {
23
25
  serializeIssue,
24
26
  } from '../util.js';
25
27
 
28
+ const ALLOWED_LIST_FIELDS = ['status', 'priority', 'limit', 'offset', 'assigneeId'];
29
+
30
+ const VALID_FLAGS = new Set([
31
+ ...ALLOWED_LIST_FIELDS.map(key => issueSchema[key].flag),
32
+ '--json',
33
+ ]);
34
+
26
35
  /**
27
36
  * Lists issues matching the filters and pagination settings
28
37
  * @param {string[]} args - The command line arguments
@@ -31,19 +40,26 @@ import {
31
40
 
32
41
  export async function run(args) {
33
42
  const isJson = hasFlag(args, '--json');
34
- const validFlags = ['--status', '--priority', '--limit', '--offset', '--json'];
35
- // Check if user misspelled a flag
36
43
  for (const arg of args) {
37
- if (arg.startsWith('--')) {
38
- if (!validFlags.includes(arg)) {
39
- throw new Error(`Unknown flag provided: ${arg}. \nFlags: --status <s>, --priority <p>, --limit <n>, --offset <n>, --json`);
40
- }
44
+ if (arg.startsWith('--') && !VALID_FLAGS.has(arg)) {
45
+ throw new Error(`Unknown flag provided: ${arg}.\nFlags: ${[...VALID_FLAGS].join(', ')}`);
41
46
  }
42
47
  }
43
48
 
44
49
  try {
45
50
  const options = parseArgs(args);
46
51
 
52
+ // Getting agent name from ID
53
+ if (options.assigneeId) {
54
+ const target = getAgentByName(options.assigneeId);
55
+ if (!target) {
56
+ const agents = listAgents();
57
+ const names = agents.map(agent => `${agent.name} (${agent.type})`).join(', ');
58
+ throw new Error(`Agent/User "${options.assigneeId}" is not registered.\nRegistered agents: ${names}`);
59
+ }
60
+ options.assigneeId = target.id;
61
+ }
62
+
47
63
  const result = await listIssues(options);
48
64
  const issues = result.map(serializeIssue);
49
65
  const envelope = { status: 'success', count: issues.length, issues };
@@ -0,0 +1,95 @@
1
+ // unassign.js
2
+ // AI was consulted to generate the majority of the contents of this file.
3
+ // However, a human has reviewed and editted the generated code.
4
+ // Removes the current assignee from an issue.
5
+ //
6
+ // Usage:
7
+ // baton unassign <id> [--json]
8
+ //
9
+ // Examples:
10
+ // baton unassign 12
11
+ // baton unassign 12 --json
12
+
13
+ import { getIssue, unassignIssue } from "../services/issuesService.js";
14
+ import { getCurrentActor } from "../services/context.js";
15
+ import { AgentType } from "../models/agents.js";
16
+ import { hasFlag, renderOutput, renderError, serializeIssue } from "../util.js";
17
+
18
+ const VALID_FLAGS = new Set(["--json"]);
19
+
20
+ /**
21
+ * Removes the current assignee from an issue.
22
+ * @param {string[]} args - The command-line arguments
23
+ * @returns {Promise<number>} The exit code: 0 is success, 1 is error
24
+ */
25
+ export async function run(args) {
26
+ const isJson = hasFlag(args, "--json");
27
+
28
+ // Validate flags
29
+ const providedFlags = args.filter((a) => a.startsWith("--"));
30
+ for (const flag of providedFlags) {
31
+ if (!VALID_FLAGS.has(flag)) {
32
+ throw new Error(`Unknown flag: ${flag}`);
33
+ }
34
+ }
35
+
36
+ const idArg = args.find((a) => !a.startsWith("-"));
37
+ const issueId = Number(idArg);
38
+
39
+ if (!Number.isInteger(issueId) || issueId <= 0) {
40
+ renderError(
41
+ isJson,
42
+ `Invalid ID "${idArg}". ID must be a positive integer.`,
43
+ "INVALID_ID",
44
+ );
45
+ return 1;
46
+ }
47
+
48
+ const actor = getCurrentActor();
49
+
50
+ if (!actor || actor.type !== AgentType.HUMAN) {
51
+ renderError(isJson, "Only human users can unassign issues.", "FORBIDDEN");
52
+ return 1;
53
+ }
54
+
55
+ let issue;
56
+
57
+ try {
58
+ issue = getIssue(issueId);
59
+ } catch (error) {
60
+ if (error.message.includes("not found")) {
61
+ renderError(isJson, error.message, "NOT_FOUND");
62
+ } else {
63
+ renderError(isJson, error.message);
64
+ }
65
+ return 1;
66
+ }
67
+
68
+ if (!issue.assigneeId) {
69
+ renderError(
70
+ isJson,
71
+ `Issue #${issueId} is not assigned.`,
72
+ "INVALID_STATE"
73
+ );
74
+ return 1;
75
+ }
76
+
77
+ try {
78
+ // Use unassignIssue from issuesService.js
79
+ const updatedIssue = unassignIssue(issueId);
80
+
81
+ const envelope = {
82
+ status: "success",
83
+ issue: serializeIssue(updatedIssue),
84
+ };
85
+
86
+ renderOutput(isJson, envelope, () => {
87
+ console.log(`Success: Issue #${issueId} is now unassigned.`);
88
+ });
89
+
90
+ return 0;
91
+ } catch (error) {
92
+ renderError(isJson, error.message);
93
+ return 1;
94
+ }
95
+ }
@@ -25,8 +25,9 @@ import { spawnSync } from "child_process";
25
25
  import { writeFileSync, readFileSync, unlinkSync } from "fs";
26
26
  import { tmpdir } from "os";
27
27
  import { join } from "path";
28
+ import { listAgents, getAgentByName } from '../services/agentsService.js';
28
29
 
29
- const ALLOWED_UPDATE_FIELDS = ['title', 'status', 'priority', 'tokenLimit', 'description'];
30
+ const ALLOWED_UPDATE_FIELDS = ['title', 'status', 'priority', 'tokenLimit', 'description', 'assigneeId'];
30
31
 
31
32
  const VALID_FLAGS = new Set([
32
33
  ...ALLOWED_UPDATE_FIELDS.map(key => issueSchema[key].flag),
@@ -161,6 +162,24 @@ async function runInteractiveMode(issue) {
161
162
  if (edited !== null) results.description = edited;
162
163
  }
163
164
 
165
+ // Assignee
166
+ const wantsAssignee = await confirm({
167
+ message: "Edit assignee?",
168
+ default: false,
169
+ });
170
+ if (wantsAssignee) {
171
+ const agents = listAgents();
172
+ const assigneeChoices = [
173
+ { name: '(none)', value: null },
174
+ ...agents.map(agent => ({ name: `${agent.name} (${agent.type})`, value: agent.id }))
175
+ ];
176
+ results.assigneeId = await select({
177
+ message: 'Assign to:',
178
+ choices: assigneeChoices,
179
+ default: issue.assigneeId ?? null
180
+ });
181
+ }
182
+
164
183
  // Diff: loop over results and collect only what changed.
165
184
  // Adding a new prompt above is all that is needed -- nothing to update here.
166
185
  const pending = Object.fromEntries(
@@ -236,6 +255,17 @@ export async function run(args) {
236
255
  ? await runInteractiveMode(oldIssue)
237
256
  : parseArgs(cmdArgs.slice(1));
238
257
 
258
+ // Getting agent name from ID
259
+ if (options.assigneeId) {
260
+ const target = getAgentByName(options.assigneeId);
261
+ if (!target) {
262
+ const agents = listAgents();
263
+ const names = agents.map(agent => `${agent.name} (${agent.type})`).join(', ');
264
+ throw new Error(`Agent/User "${options.assigneeId}" is not registered.\nRegistered agents: ${names}`);
265
+ }
266
+ options.assigneeId = target.id;
267
+ }
268
+
239
269
  const newIssue = await updateIssue(id, oldIssue, options);
240
270
  const envelope = { status: "success", issue: serializeIssue(newIssue) };
241
271
 
@@ -53,7 +53,7 @@ export const issueSchema = {
53
53
  tokenLimit: { flag: '--token-limit', type: 'number' },
54
54
  priority: { flag: '--priority', type: 'enum', values: Object.values(Priority) },
55
55
  description:{ flag: '--description', type: 'string' },
56
- assigneeId: { flag: '--assignee-id', type: 'number'},
56
+ assigneeId: { flag: '--assignee', type: 'string'},
57
57
  // Pagination options
58
58
  limit: { flag: '--limit', type: 'number' },
59
59
  offset: { flag: '--offset', type: 'number' }
@@ -1,11 +1,12 @@
1
1
  // agentRestrictions.js
2
2
 
3
3
  const AGENT_RESTRICTED_COMMANDS = new Set(['init', 'approve', 'reject', 'delete', 'priority']);
4
- const AGENT_RESTRICTED_FLAGS = new Set(['--status', '--token-limit']);
4
+ const AGENT_RESTRICTED_FLAGS = new Set(['--status', '--token-limit', '--assignee']);
5
5
 
6
6
  /**
7
7
  * Validates whether an actor has permission to execute a given command and arguments.
8
8
  * @param {string} command - The primary CLI command name.
9
+ * @param {string[]} [args=[]] - The command line arguments.
9
10
  * @throws {Error} If the agent entered a restricted command or flag
10
11
  */
11
12
  export function authorizeAction(command, args = []) {
@@ -14,7 +15,6 @@ export function authorizeAction(command, args = []) {
14
15
  }
15
16
 
16
17
  if (command === 'update') {
17
- const args = process.argv.slice(2);
18
18
  const usedRestricted = args.filter(arg => AGENT_RESTRICTED_FLAGS.has(arg));
19
19
  if (usedRestricted.length > 0) {
20
20
  throw new Error(
@@ -12,6 +12,7 @@ export { getCurrentActor };
12
12
  * If the agent/user is registered, it sets them as the active actor for this session.
13
13
  * If not, it terminates the process with a helpful error message.
14
14
  * @param {string} command - The command being executed
15
+ * @param {string[]} [args=[]] - The command line arguments.
15
16
  */
16
17
  export function authenticateContext(command, args = []) {
17
18
  const exemptCommands = ['register', 'help'];
@@ -1,3 +1,4 @@
1
+ // AI was consulted to guide implementation of part of the file.
1
2
  import { getDB } from '../db/index.js';
2
3
  import { eq, and, or, like, sql } from "drizzle-orm";
3
4
  import {issuesTable, activityTable} from "../models/schema.js";
@@ -231,6 +232,7 @@ export function updateIssue(id, oldIssue, { title, description, tokenLimit, stat
231
232
  updates.status = toUpdate || status;
232
233
  }
233
234
 
235
+
234
236
  // Normalize priority argument
235
237
  if (priority !== undefined) {
236
238
  const priorityValues = Object.values(Priority);
@@ -257,6 +259,48 @@ export function updateIssue(id, oldIssue, { title, description, tokenLimit, stat
257
259
  return getIssue(id);
258
260
  }
259
261
 
262
+ /**
263
+ *
264
+ * Sets assigneeId to null
265
+ * Logs an edit event
266
+ * @param {number} issueId - ID of the issue to be editted
267
+ * @returns {Issue}
268
+ */
269
+ export function unassignIssue(issueId){
270
+ const db = getDB();
271
+
272
+ // Check that issue exists
273
+ findById(db, issueId);
274
+
275
+ db.update(issuesTable).set({ status: Status.OPEN, assigneeId: null }).where(eq(issuesTable.id, issueId)).run();
276
+ logActivity(db, issueId, Action.EDIT, `Success: Issue #${issueId} is now unassigned.`);
277
+ return getIssue(issueId);
278
+ }
279
+
280
+ /**
281
+ * Assigns an issue to a specific registered agent or human.
282
+ * Logs an edit event.
283
+ * @param {number} issueId
284
+ * @param {number} assigneeId
285
+ * @returns {Issue} - the issue that matches the ID
286
+ */
287
+ export function assignIssue(issueId, assigneeId) {
288
+ const db = getDB();
289
+
290
+ // Verify the issue exists first (will throw if not found)
291
+ findById(db, issueId);
292
+
293
+ db.update(issuesTable)
294
+ .set({ assigneeId: assigneeId })
295
+ .where(eq(issuesTable.id, issueId))
296
+ .run();
297
+
298
+ // Pass actorId directly instead of hacking the global module variable
299
+ logActivity(db, issueId, Action.EDIT, `Issue #${issueId} was assigned.`);
300
+
301
+ return getIssue(issueId);
302
+ }
303
+
260
304
  /**
261
305
  * Change the status of an issue from in-review to closed
262
306
  * Logs a closed event.
package/source/util.js CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { isAbsolute, resolve } from 'node:path';
7
7
  import { issueSchema } from '../source/models/schema.js';
8
+ import { getAgentById } from '../source/services/agentsService.js';
8
9
 
9
10
  /**
10
11
  * Formats a timestamp as `HH:MM:SS YYYY-MM-DD`.
@@ -269,7 +270,7 @@ export const WIDTHS = {
269
270
  title: 20,
270
271
  status: 15,
271
272
  priority: 10,
272
- //assignees: 10,
273
+ assignee: 10,
273
274
  description: 50
274
275
  };
275
276
 
@@ -283,7 +284,7 @@ export function printTableHeader() {
283
284
  "TITLE".padEnd(WIDTHS.title) + " │ " +
284
285
  "STATUS".padEnd(WIDTHS.status) + " │ " +
285
286
  "PRIORITY".padEnd(WIDTHS.priority) + " │ " +
286
- //"ASSIGNEE".padEnd(WIDTHS.assignees) + " │ " +
287
+ "ASSIGNEE".padEnd(WIDTHS.assignee) + " │ " +
287
288
  "DESCRIPTION".padEnd(WIDTHS.description)
288
289
  );
289
290
  console.log(
@@ -291,7 +292,7 @@ export function printTableHeader() {
291
292
  "─".repeat(WIDTHS.title) + "─┼─" +
292
293
  "─".repeat(WIDTHS.status) + "─┼─" +
293
294
  "─".repeat(WIDTHS.priority) + "─┼─" +
294
- //"─".repeat(WIDTHS.assignees) + "─┼─" +
295
+ "─".repeat(WIDTHS.assignee) + "─┼─" +
295
296
  "─".repeat(WIDTHS.description)
296
297
  );
297
298
  }
@@ -324,9 +325,10 @@ export function printIssueTable(issue) {
324
325
  const priorityVal = truncate(issue.priority, WIDTHS.priority).padEnd(WIDTHS.priority);
325
326
  const descVal = truncate(issue.description, WIDTHS.description).padEnd(WIDTHS.description);
326
327
 
327
- // Handling the assignee array
328
- //const assigneesVal = Array.isArray(issue.assignees) ? issue.assignees.join(', ') : 'None';
329
- //const assigneeVal = truncate(assigneesVal, WIDTHS.assignees).padEnd(WIDTHS.assignees);
328
+ // Getting Agent name from id
329
+ const assigneeId = issue.assigneeId ?? issue.assignee_id ?? null;
330
+ const assignee = assigneeId ? getAgentById(assigneeId) : null;
331
+ const assigneeVal = truncate(assignee?.name ?? null, WIDTHS.assignee).padEnd(WIDTHS.assignee);
330
332
 
331
- console.log(`${idVal} │ ${titleVal} │ ${statusVal} │ ${priorityVal} │ ${descVal}`);
333
+ console.log(`${idVal} │ ${titleVal} │ ${statusVal} │ ${priorityVal} │ ${assigneeVal} │ ${descVal}`);
332
334
  }