baton-issue-tracker 1.11.0 → 1.11.1
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/README.md +3 -2
- package/package.json +1 -1
- package/source/cli.js +6 -15
- package/source/commands/claim.js +71 -0
- package/source/commands/init.js +1 -1
- package/source/services/authService.js +11 -0
- package/source/commands/loop.js +0 -67
- package/source/commands/next.js +0 -53
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Fantastic Four
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
[
|
|
3
|
+
## Documentation
|
|
4
|
+
- [User Guide](docs/USER_GUIDE.md) — How to use Baton CLI.
|
|
5
|
+
- [Developer Guide](docs/CONTRIBUTING.md) — Setting up the development environment.
|
|
5
6
|
|
|
6
7
|
## Project Details:
|
|
7
8
|
TBD
|
package/package.json
CHANGED
package/source/cli.js
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
// usage: baton [command] [options]
|
|
6
6
|
// commands:
|
|
7
7
|
// init: initialize the tracker
|
|
8
|
-
// next: work on the next issue
|
|
9
|
-
// loop: run the agent autonomously for multiple steps
|
|
10
8
|
// status: show issue counts and overall progress
|
|
11
9
|
//
|
|
12
10
|
// see each command's file for more detailed flags specifications.
|
|
@@ -14,8 +12,6 @@
|
|
|
14
12
|
* Imports the run functions from each command.
|
|
15
13
|
*/
|
|
16
14
|
import { run as runInit } from './commands/init.js';
|
|
17
|
-
import { run as runNext } from './commands/next.js';
|
|
18
|
-
import { run as runLoop } from './commands/loop.js';
|
|
19
15
|
import { run as runStatus } from './commands/status.js';
|
|
20
16
|
import { run as runApprove } from './commands/approve.js';
|
|
21
17
|
import { run as runReject } from './commands/reject.js';
|
|
@@ -31,6 +27,7 @@ import { run as runLog } from './commands/log.js';
|
|
|
31
27
|
import { run as runRegister } from './commands/register.js';
|
|
32
28
|
import { run as runAgents } from './commands/agents.js';
|
|
33
29
|
import { run as runSubmit } from './commands/submit.js';
|
|
30
|
+
import { run as runClaim } from './commands/claim.js';
|
|
34
31
|
|
|
35
32
|
import { authenticateContext } from './services/authService.js';
|
|
36
33
|
|
|
@@ -43,8 +40,6 @@ Commands:
|
|
|
43
40
|
init Initialize storage and seed issues from product specs
|
|
44
41
|
register Register a new AI agent or human user
|
|
45
42
|
agents List all registered agents and humans
|
|
46
|
-
next Work on the highest-priority open issue
|
|
47
|
-
loop Run the agent autonomously for multiple steps
|
|
48
43
|
status Show issue counts and overall progress
|
|
49
44
|
view View all issue fields for a given issue ID
|
|
50
45
|
search Search issues by title and description (case insensitive)
|
|
@@ -52,6 +47,7 @@ Commands:
|
|
|
52
47
|
create Creates an issue with specified fields
|
|
53
48
|
approve Move an issue from in-review to closed
|
|
54
49
|
submit Submit finished work for human review
|
|
50
|
+
claim Claim an issue as the authenticated agent
|
|
55
51
|
priority Set an issue's priority level
|
|
56
52
|
update Updates an issue's specified fields
|
|
57
53
|
delete Deletes an issue
|
|
@@ -66,10 +62,6 @@ Options:
|
|
|
66
62
|
register --name <name> Name of the agent or user
|
|
67
63
|
register --type <type> agent | human (default: agent)
|
|
68
64
|
agents [--json]
|
|
69
|
-
loop --steps <n> Number of autonomous steps (alias: -n)
|
|
70
|
-
loop -n <n>
|
|
71
|
-
loop --json Output as JSON (for AI agents)
|
|
72
|
-
next --json Output as JSON (for AI agents)
|
|
73
65
|
status --json Output as JSON (for AI agents)
|
|
74
66
|
view <id> [--json]
|
|
75
67
|
search <query> [--json]
|
|
@@ -85,6 +77,7 @@ Options:
|
|
|
85
77
|
create --json Output as JSON (for AI agents)
|
|
86
78
|
approve <id> [--json]
|
|
87
79
|
submit <id> [--json]
|
|
80
|
+
claim <id> [--json]
|
|
88
81
|
reject <id> --reason <text> Reject an issue with a given reason
|
|
89
82
|
priority <id> <level> [--json] low | medium | high
|
|
90
83
|
update --title <text> New title
|
|
@@ -103,8 +96,6 @@ Examples:
|
|
|
103
96
|
baton init --force
|
|
104
97
|
baton register --name claude-dev --type agent
|
|
105
98
|
baton agents
|
|
106
|
-
baton next
|
|
107
|
-
baton loop --steps 5
|
|
108
99
|
baton status
|
|
109
100
|
baton view 29
|
|
110
101
|
baton search system
|
|
@@ -115,6 +106,7 @@ Examples:
|
|
|
115
106
|
baton create --title "Refactor auth" --description "Clean up JWT logic" --token-limit 4000
|
|
116
107
|
baton approve 5
|
|
117
108
|
baton submit 14
|
|
109
|
+
baton claim 14
|
|
118
110
|
baton priority 5 high
|
|
119
111
|
baton priority 3 low
|
|
120
112
|
baton update 3 --title "Revised title"
|
|
@@ -129,7 +121,7 @@ Examples:
|
|
|
129
121
|
async function main() {
|
|
130
122
|
const [, , command, ...args] = process.argv;
|
|
131
123
|
|
|
132
|
-
if (!command || command === 'help' ||
|
|
124
|
+
if (!command || command === 'help' || command === '--help') {
|
|
133
125
|
console.log(HELP);
|
|
134
126
|
process.exit(command ? 0 : 1);
|
|
135
127
|
return;
|
|
@@ -142,14 +134,13 @@ async function main() {
|
|
|
142
134
|
init: () => runInit(args),
|
|
143
135
|
register: () => runRegister(args),
|
|
144
136
|
agents: () => runAgents(args),
|
|
145
|
-
next: () => runNext(args),
|
|
146
|
-
loop: () => runLoop(args),
|
|
147
137
|
status: () => runStatus(args),
|
|
148
138
|
view: () => runView(args),
|
|
149
139
|
search: () => runSearch(args),
|
|
150
140
|
list: () => runList(args),
|
|
151
141
|
approve: () => runApprove(args),
|
|
152
142
|
reject: () => runReject(args),
|
|
143
|
+
claim: () => runClaim(args),
|
|
153
144
|
priority: () => runPriority(args),
|
|
154
145
|
create: () => runCreate(args),
|
|
155
146
|
update: () => runUpdate(args),
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// claim.js
|
|
2
|
+
// Allows an agent to officially claim an issue to work on.
|
|
3
|
+
// Usage: baton claim <id> [--json]
|
|
4
|
+
|
|
5
|
+
import { isTrackerReady, claimIssue } from '../services/issuesService.js';
|
|
6
|
+
import { getCurrentActor } from '../services/authService.js';
|
|
7
|
+
import {
|
|
8
|
+
hasFlag,
|
|
9
|
+
renderOutput,
|
|
10
|
+
renderError,
|
|
11
|
+
getFirstPositionalArg,
|
|
12
|
+
serializeIssue,
|
|
13
|
+
reportTrackerNotReady,
|
|
14
|
+
} from '../util.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Claims an issue for the authenticated agent.
|
|
18
|
+
* @param {string[]} args - The command line arguments
|
|
19
|
+
* @returns {Promise<number>} The exit code: 0 is success, 1 is error
|
|
20
|
+
*/
|
|
21
|
+
export async function run(args) {
|
|
22
|
+
const isJson = hasFlag(args, '--json');
|
|
23
|
+
|
|
24
|
+
if (hasFlag(args, '-h') || hasFlag(args, '--help')) {
|
|
25
|
+
console.log(`Usage: baton claim <id> [--json]\n\nOptions:\n --json Output as JSON (for AI agents)\n -h, --help Show this help\n\nExamples:\n baton claim 14`);
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const idArg = getFirstPositionalArg(args, { ignoreFlags: ['--json'] });
|
|
30
|
+
|
|
31
|
+
if (!idArg) {
|
|
32
|
+
renderError(isJson, 'Missing issue ID.\nUsage: baton claim <id>', 'MISSING_ID');
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const id = Number(idArg);
|
|
37
|
+
if (!Number.isInteger(id)) {
|
|
38
|
+
renderError(isJson, `Invalid ID "${idArg}". ID must be an integer.`, 'INVALID_ID');
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!isTrackerReady()) {
|
|
43
|
+
reportTrackerNotReady();
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const actor = getCurrentActor();
|
|
48
|
+
if (!actor) {
|
|
49
|
+
renderError(isJson, 'No authenticated actor found. Please sign in with a registered agent.', 'UNAUTHORIZED');
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (actor.type !== 'agent') {
|
|
54
|
+
renderError(isJson, 'Only an agent may claim an issue.', 'UNAUTHORIZED');
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const updatedIssue = claimIssue(id);
|
|
60
|
+
const envelope = { status: 'success', issue: serializeIssue(updatedIssue) };
|
|
61
|
+
|
|
62
|
+
renderOutput(isJson, envelope, () => {
|
|
63
|
+
console.log(`Success: Issue #${updatedIssue.id} claimed by agent "${actor.name}" and moved to ${updatedIssue.status}.`);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return 0;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
renderError(isJson, error.message);
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
package/source/commands/init.js
CHANGED
|
@@ -176,7 +176,7 @@ export async function run(args = []) {
|
|
|
176
176
|
console.log(` #${issue.id} [${issue.priority}] ${issue.title}`);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
-
console.log('Run `baton status` to review progress
|
|
179
|
+
console.log('Run `baton status` to review progress.');
|
|
180
180
|
});
|
|
181
181
|
|
|
182
182
|
return 0;
|
|
@@ -2,6 +2,8 @@ import os from 'os';
|
|
|
2
2
|
import { getAgentByName } from './agentsService.js';
|
|
3
3
|
import { setActiveActor } from './issuesService.js';
|
|
4
4
|
|
|
5
|
+
let currentActor = null;
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Authenticates the current execution context.
|
|
7
9
|
* Looks for BATON_AGENT in the environment, falling back to the OS username.
|
|
@@ -30,6 +32,7 @@ export function authenticateContext(command) {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
// Set the global state for the remainder of this command's lifecycle
|
|
35
|
+
currentActor = agent;
|
|
33
36
|
setActiveActor(agent.id);
|
|
34
37
|
} catch (error) {
|
|
35
38
|
// Gracefully handle the scenario where the database hasn't been initialized yet
|
|
@@ -41,4 +44,12 @@ export function authenticateContext(command) {
|
|
|
41
44
|
// Rethrow if it's a completely different error
|
|
42
45
|
throw error;
|
|
43
46
|
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns the authenticated actor object for this session.
|
|
51
|
+
* @returns {object|null}
|
|
52
|
+
*/
|
|
53
|
+
export function getCurrentActor() {
|
|
54
|
+
return currentActor;
|
|
44
55
|
}
|
package/source/commands/loop.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// loop.js
|
|
2
|
-
// AI was consulted for some portions of this file.
|
|
3
|
-
// loop command for the tracker which allows the AI agent to work autonomously for multiple steps.
|
|
4
|
-
// usage: baton loop [options]
|
|
5
|
-
// options:
|
|
6
|
-
// --steps <n>: number of steps to run (default: 1)
|
|
7
|
-
// -n <n>: alias for --steps <n>
|
|
8
|
-
// <n>: same as --steps <n>
|
|
9
|
-
// examples usage of steps flag:
|
|
10
|
-
// baton loop --steps 5
|
|
11
|
-
// baton loop -n 5
|
|
12
|
-
|
|
13
|
-
import { isTrackerReady } from '../services/issuesService.js';
|
|
14
|
-
import { getNumericFlag, hasFlag, renderOutput, reportTrackerNotReady } from '../util.js';
|
|
15
|
-
import { run as runNext } from './next.js';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Parses the flags in the command line argument
|
|
19
|
-
* @param {string[]} args - The command line arguments
|
|
20
|
-
* @returns {{ steps: number }}
|
|
21
|
-
*/
|
|
22
|
-
function parseLoopFlags(args) {
|
|
23
|
-
const stepsFlag = getNumericFlag(args, '--steps') ?? getNumericFlag(args, '-n');
|
|
24
|
-
return { steps: stepsFlag ?? 1 };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Runs the loop command
|
|
29
|
-
* @param {string[]} args
|
|
30
|
-
* @returns {Promise<number>}
|
|
31
|
-
*/
|
|
32
|
-
export async function run(args = []) {
|
|
33
|
-
const isJson = hasFlag(args, '--json');
|
|
34
|
-
const loopArgs = args.filter((arg) => arg !== '--json');
|
|
35
|
-
const { steps } = parseLoopFlags(loopArgs);
|
|
36
|
-
|
|
37
|
-
if (!isTrackerReady()) {
|
|
38
|
-
reportTrackerNotReady();
|
|
39
|
-
return 1;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!Number.isInteger(steps) || steps < 1) {
|
|
43
|
-
console.error('Error: --steps must be a positive integer.');
|
|
44
|
-
console.error('Usage: baton loop --steps <n>');
|
|
45
|
-
return 1;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
console.log(`Running baton for ${steps} step(s)...\n`);
|
|
49
|
-
|
|
50
|
-
let completed = 0;
|
|
51
|
-
for (let step = 1; step <= steps; step += 1) {
|
|
52
|
-
console.log(`--- Step ${step}/${steps} ---`);
|
|
53
|
-
const code = await runNext();
|
|
54
|
-
if (code !== 0) {
|
|
55
|
-
return code;
|
|
56
|
-
}
|
|
57
|
-
completed += 1;
|
|
58
|
-
if (step < steps) {
|
|
59
|
-
console.log('');
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
renderOutput(isJson, { status: 'success', steps, completed }, () => {
|
|
64
|
-
console.log(`\nCompleted ${completed} autonomous step(s).`);
|
|
65
|
-
});
|
|
66
|
-
return 0;
|
|
67
|
-
}
|
package/source/commands/next.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// next.js
|
|
2
|
-
// AI was consulted for some portions of this file.
|
|
3
|
-
// next command for the issue tracker which allows the user to manually move the AI from issue to issue.
|
|
4
|
-
// usage: baton next
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
isTrackerReady,
|
|
8
|
-
selectNextIssue,
|
|
9
|
-
claimIssue,
|
|
10
|
-
} from '../services/issuesService.js';
|
|
11
|
-
import { formatTimestamp, hasFlag, renderOutput, reportTrackerNotReady, serializeIssue } from '../util.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Moves the AI agent to work on the next issue.
|
|
15
|
-
* Checks if the tracker is ready and if there are any open issues.
|
|
16
|
-
* Prompts user to initialize the tracker if it is not ready.
|
|
17
|
-
* Stats are updated on the issue through claimIssue function from init.js.
|
|
18
|
-
* @returns {Promise<number>} The exit code: 0 is success, 1 is error.
|
|
19
|
-
*/
|
|
20
|
-
export async function run(args = []) {
|
|
21
|
-
const isJson = hasFlag(args, '--json');
|
|
22
|
-
|
|
23
|
-
if (!isTrackerReady()) {
|
|
24
|
-
reportTrackerNotReady();
|
|
25
|
-
return 1;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const issue = selectNextIssue();
|
|
29
|
-
if (!issue) {
|
|
30
|
-
renderOutput(isJson, { status: 'success', issue: null }, () => {
|
|
31
|
-
console.log('No open issues available. All work is complete or the backlog is empty.');
|
|
32
|
-
});
|
|
33
|
-
return 0;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const updated = claimIssue(issue.id);
|
|
37
|
-
const envelope = { status: 'success', issue: serializeIssue(updated) };
|
|
38
|
-
|
|
39
|
-
renderOutput(isJson, envelope, () => {
|
|
40
|
-
console.log('Working on next issue:');
|
|
41
|
-
console.log(` ID: #${updated.id}`);
|
|
42
|
-
console.log(` Title: ${updated.title}`);
|
|
43
|
-
console.log(` Priority: ${updated.priority}`);
|
|
44
|
-
console.log(` Status: ${updated.status}`);
|
|
45
|
-
console.log(` Attempts: ${updated.attemptNum}`);
|
|
46
|
-
console.log(` Created: ${formatTimestamp(updated.createdAt)}`);
|
|
47
|
-
if (updated.description) {
|
|
48
|
-
console.log(` Description: ${updated.description}`);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
return 0;
|
|
53
|
-
}
|