baton-issue-tracker 1.13.4 → 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 +1 -1
- package/source/cli.js +2 -0
- package/source/commands/assign.js +66 -0
- package/source/commands/init.js +18 -0
- package/source/services/issuesService.js +25 -0
package/package.json
CHANGED
package/source/cli.js
CHANGED
|
@@ -31,6 +31,7 @@ import { run as runSubmit } from './commands/submit.js';
|
|
|
31
31
|
import { run as runUnassign } from './commands/unassign.js';
|
|
32
32
|
import { run as runUnclaim } from './commands/unclaim.js';
|
|
33
33
|
import { run as runClaim } from './commands/claim.js';
|
|
34
|
+
import { run as runAssign} from './commands/assign.js';
|
|
34
35
|
|
|
35
36
|
import { authenticateContext } from './services/authService.js';
|
|
36
37
|
|
|
@@ -163,6 +164,7 @@ async function main() {
|
|
|
163
164
|
submit: () => runSubmit(args),
|
|
164
165
|
unassign: () => runUnassign(args),
|
|
165
166
|
unclaim: () => runUnclaim(args),
|
|
167
|
+
assign: () => runAssign(args),
|
|
166
168
|
};
|
|
167
169
|
|
|
168
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
|
+
}
|
package/source/commands/init.js
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
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,22 @@ 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
|
+
|
|
159
177
|
const templatePath = join('docs', 'BATON_AGENT_RULES.md');
|
|
160
178
|
|
|
161
179
|
let rulesContent = '';
|
|
@@ -232,6 +232,7 @@ export function updateIssue(id, oldIssue, { title, description, tokenLimit, stat
|
|
|
232
232
|
updates.status = toUpdate || status;
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
|
|
235
236
|
// Normalize priority argument
|
|
236
237
|
if (priority !== undefined) {
|
|
237
238
|
const priorityValues = Object.values(Priority);
|
|
@@ -276,6 +277,30 @@ export function unassignIssue(issueId){
|
|
|
276
277
|
return getIssue(issueId);
|
|
277
278
|
}
|
|
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
|
+
|
|
279
304
|
/**
|
|
280
305
|
* Change the status of an issue from in-review to closed
|
|
281
306
|
* Logs a closed event.
|