baton-issue-tracker 1.3.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 ADDED
@@ -0,0 +1,13 @@
1
+ # Fantastic Four
2
+
3
+ ## Developer Guide
4
+ [Contributing guide](CONTRIBUTING.md)
5
+
6
+ ## Project Details:
7
+ TBD
8
+
9
+ # Team Page
10
+ [Click Here](/admin/team.md)
11
+
12
+ # Agile: Team Status Video
13
+ [https://youtu.be/xToi68JAcwk](https://youtu.be/xToi68JAcwk)
@@ -0,0 +1,18 @@
1
+ CREATE TABLE `activity` (
2
+ `log_id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
3
+ `issue_id` integer,
4
+ `created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
5
+ `action` text NOT NULL,
6
+ `details` text
7
+ );
8
+ --> statement-breakpoint
9
+ CREATE TABLE `issues` (
10
+ `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
11
+ `created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
12
+ `attempt_num` integer DEFAULT 0 NOT NULL,
13
+ `title` text DEFAULT 'PENDING' NOT NULL,
14
+ `status` text DEFAULT 'Open' NOT NULL,
15
+ `priority` text DEFAULT 'Low',
16
+ `token_limit` integer,
17
+ `description` text
18
+ );
@@ -0,0 +1,16 @@
1
+ ALTER TABLE `issues` ADD `last_updated` text DEFAULT CURRENT_TIMESTAMP NOT NULL;--> statement-breakpoint
2
+ ALTER TABLE `issues` ADD `assignees` text DEFAULT '[]';--> statement-breakpoint
3
+ PRAGMA foreign_keys=OFF;--> statement-breakpoint
4
+ CREATE TABLE `__new_activity` (
5
+ `log_id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
6
+ `issue_id` integer,
7
+ `created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
8
+ `action` text NOT NULL,
9
+ `details` text,
10
+ CONSTRAINT "activity_action_check" CHECK("__new_activity"."action" IN ('state_change', 'priority_change', 'edit', 'read', 'creation', 'deletion', 'rejection'))
11
+ );
12
+ --> statement-breakpoint
13
+ INSERT INTO `__new_activity`("log_id", "issue_id", "created_at", "action", "details") SELECT "log_id", "issue_id", "created_at", "action", "details" FROM `activity`;--> statement-breakpoint
14
+ DROP TABLE `activity`;--> statement-breakpoint
15
+ ALTER TABLE `__new_activity` RENAME TO `activity`;--> statement-breakpoint
16
+ PRAGMA foreign_keys=ON;
@@ -0,0 +1,135 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "3a9e6e25-7620-4cef-9154-95ab093513fc",
5
+ "prevId": "00000000-0000-0000-0000-000000000000",
6
+ "tables": {
7
+ "activity": {
8
+ "name": "activity",
9
+ "columns": {
10
+ "log_id": {
11
+ "name": "log_id",
12
+ "type": "integer",
13
+ "primaryKey": true,
14
+ "notNull": true,
15
+ "autoincrement": true
16
+ },
17
+ "issue_id": {
18
+ "name": "issue_id",
19
+ "type": "integer",
20
+ "primaryKey": false,
21
+ "notNull": false,
22
+ "autoincrement": false
23
+ },
24
+ "created_at": {
25
+ "name": "created_at",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": true,
29
+ "autoincrement": false,
30
+ "default": "CURRENT_TIMESTAMP"
31
+ },
32
+ "action": {
33
+ "name": "action",
34
+ "type": "text",
35
+ "primaryKey": false,
36
+ "notNull": true,
37
+ "autoincrement": false
38
+ },
39
+ "details": {
40
+ "name": "details",
41
+ "type": "text",
42
+ "primaryKey": false,
43
+ "notNull": false,
44
+ "autoincrement": false
45
+ }
46
+ },
47
+ "indexes": {},
48
+ "foreignKeys": {},
49
+ "compositePrimaryKeys": {},
50
+ "uniqueConstraints": {},
51
+ "checkConstraints": {}
52
+ },
53
+ "issues": {
54
+ "name": "issues",
55
+ "columns": {
56
+ "id": {
57
+ "name": "id",
58
+ "type": "integer",
59
+ "primaryKey": true,
60
+ "notNull": true,
61
+ "autoincrement": true
62
+ },
63
+ "created_at": {
64
+ "name": "created_at",
65
+ "type": "text",
66
+ "primaryKey": false,
67
+ "notNull": true,
68
+ "autoincrement": false,
69
+ "default": "CURRENT_TIMESTAMP"
70
+ },
71
+ "attempt_num": {
72
+ "name": "attempt_num",
73
+ "type": "integer",
74
+ "primaryKey": false,
75
+ "notNull": true,
76
+ "autoincrement": false,
77
+ "default": 0
78
+ },
79
+ "title": {
80
+ "name": "title",
81
+ "type": "text",
82
+ "primaryKey": false,
83
+ "notNull": true,
84
+ "autoincrement": false,
85
+ "default": "'PENDING'"
86
+ },
87
+ "status": {
88
+ "name": "status",
89
+ "type": "text",
90
+ "primaryKey": false,
91
+ "notNull": true,
92
+ "autoincrement": false,
93
+ "default": "'Open'"
94
+ },
95
+ "priority": {
96
+ "name": "priority",
97
+ "type": "text",
98
+ "primaryKey": false,
99
+ "notNull": false,
100
+ "autoincrement": false,
101
+ "default": "'Low'"
102
+ },
103
+ "token_limit": {
104
+ "name": "token_limit",
105
+ "type": "integer",
106
+ "primaryKey": false,
107
+ "notNull": false,
108
+ "autoincrement": false
109
+ },
110
+ "description": {
111
+ "name": "description",
112
+ "type": "text",
113
+ "primaryKey": false,
114
+ "notNull": false,
115
+ "autoincrement": false
116
+ }
117
+ },
118
+ "indexes": {},
119
+ "foreignKeys": {},
120
+ "compositePrimaryKeys": {},
121
+ "uniqueConstraints": {},
122
+ "checkConstraints": {}
123
+ }
124
+ },
125
+ "views": {},
126
+ "enums": {},
127
+ "_meta": {
128
+ "schemas": {},
129
+ "tables": {},
130
+ "columns": {}
131
+ },
132
+ "internal": {
133
+ "indexes": {}
134
+ }
135
+ }
@@ -0,0 +1,156 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "af5467aa-0757-4c91-8afb-c6f0202dd90c",
5
+ "prevId": "3a9e6e25-7620-4cef-9154-95ab093513fc",
6
+ "tables": {
7
+ "activity": {
8
+ "name": "activity",
9
+ "columns": {
10
+ "log_id": {
11
+ "name": "log_id",
12
+ "type": "integer",
13
+ "primaryKey": true,
14
+ "notNull": true,
15
+ "autoincrement": true
16
+ },
17
+ "issue_id": {
18
+ "name": "issue_id",
19
+ "type": "integer",
20
+ "primaryKey": false,
21
+ "notNull": false,
22
+ "autoincrement": false
23
+ },
24
+ "created_at": {
25
+ "name": "created_at",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": true,
29
+ "autoincrement": false,
30
+ "default": "CURRENT_TIMESTAMP"
31
+ },
32
+ "action": {
33
+ "name": "action",
34
+ "type": "text",
35
+ "primaryKey": false,
36
+ "notNull": true,
37
+ "autoincrement": false
38
+ },
39
+ "details": {
40
+ "name": "details",
41
+ "type": "text",
42
+ "primaryKey": false,
43
+ "notNull": false,
44
+ "autoincrement": false
45
+ }
46
+ },
47
+ "indexes": {},
48
+ "foreignKeys": {},
49
+ "compositePrimaryKeys": {},
50
+ "uniqueConstraints": {},
51
+ "checkConstraints": {
52
+ "activity_action_check": {
53
+ "name": "activity_action_check",
54
+ "value": "\"activity\".\"action\" IN ('state_change', 'priority_change', 'edit', 'read', 'creation', 'deletion', 'rejection')"
55
+ }
56
+ }
57
+ },
58
+ "issues": {
59
+ "name": "issues",
60
+ "columns": {
61
+ "id": {
62
+ "name": "id",
63
+ "type": "integer",
64
+ "primaryKey": true,
65
+ "notNull": true,
66
+ "autoincrement": true
67
+ },
68
+ "created_at": {
69
+ "name": "created_at",
70
+ "type": "text",
71
+ "primaryKey": false,
72
+ "notNull": true,
73
+ "autoincrement": false,
74
+ "default": "CURRENT_TIMESTAMP"
75
+ },
76
+ "last_updated": {
77
+ "name": "last_updated",
78
+ "type": "text",
79
+ "primaryKey": false,
80
+ "notNull": true,
81
+ "autoincrement": false,
82
+ "default": "CURRENT_TIMESTAMP"
83
+ },
84
+ "attempt_num": {
85
+ "name": "attempt_num",
86
+ "type": "integer",
87
+ "primaryKey": false,
88
+ "notNull": true,
89
+ "autoincrement": false,
90
+ "default": 0
91
+ },
92
+ "title": {
93
+ "name": "title",
94
+ "type": "text",
95
+ "primaryKey": false,
96
+ "notNull": true,
97
+ "autoincrement": false,
98
+ "default": "'PENDING'"
99
+ },
100
+ "status": {
101
+ "name": "status",
102
+ "type": "text",
103
+ "primaryKey": false,
104
+ "notNull": true,
105
+ "autoincrement": false,
106
+ "default": "'Open'"
107
+ },
108
+ "priority": {
109
+ "name": "priority",
110
+ "type": "text",
111
+ "primaryKey": false,
112
+ "notNull": false,
113
+ "autoincrement": false,
114
+ "default": "'Low'"
115
+ },
116
+ "token_limit": {
117
+ "name": "token_limit",
118
+ "type": "integer",
119
+ "primaryKey": false,
120
+ "notNull": false,
121
+ "autoincrement": false
122
+ },
123
+ "description": {
124
+ "name": "description",
125
+ "type": "text",
126
+ "primaryKey": false,
127
+ "notNull": false,
128
+ "autoincrement": false
129
+ },
130
+ "assignees": {
131
+ "name": "assignees",
132
+ "type": "text",
133
+ "primaryKey": false,
134
+ "notNull": false,
135
+ "autoincrement": false,
136
+ "default": "'[]'"
137
+ }
138
+ },
139
+ "indexes": {},
140
+ "foreignKeys": {},
141
+ "compositePrimaryKeys": {},
142
+ "uniqueConstraints": {},
143
+ "checkConstraints": {}
144
+ }
145
+ },
146
+ "views": {},
147
+ "enums": {},
148
+ "_meta": {
149
+ "schemas": {},
150
+ "tables": {},
151
+ "columns": {}
152
+ },
153
+ "internal": {
154
+ "indexes": {}
155
+ }
156
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "sqlite",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "6",
8
+ "when": 1779934723405,
9
+ "tag": "0000_tidy_enchantress",
10
+ "breakpoints": true
11
+ },
12
+ {
13
+ "idx": 1,
14
+ "version": "6",
15
+ "when": 1780114722528,
16
+ "tag": "0001_salty_luke_cage",
17
+ "breakpoints": true
18
+ }
19
+ ]
20
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "baton-issue-tracker",
3
+ "version": "1.3.1",
4
+ "description": "A CLI issue tracker for AI agents",
5
+ "type": "module",
6
+ "bin": {
7
+ "baton": "source/cli.js"
8
+ },
9
+ "files": [
10
+ "source/",
11
+ "drizzle/"
12
+ ],
13
+ "scripts": {
14
+ "test": "node --test",
15
+ "lint": "eslint source/",
16
+ "lint:fix": "eslint source/ --fix",
17
+ "prepare": "husky",
18
+ "commitlint": "commitlint --edit",
19
+ "semantic-release": "semantic-release",
20
+ "build-docs": "jsdoc --pedantic -r source -d docs/jsdoc"
21
+ },
22
+ "devDependencies": {
23
+ "@commitlint/cli": "^21.0.1",
24
+ "@commitlint/config-conventional": "^21.0.1",
25
+ "@eslint/js": "^9.0.0",
26
+ "@semantic-release/changelog": "^6.0.3",
27
+ "@semantic-release/git": "^10.0.1",
28
+ "@semantic-release/npm": "^13.1.5",
29
+ "drizzle-kit": "^0.31.10",
30
+ "eslint": "^9.0.0",
31
+ "globals": "^17.6.0",
32
+ "husky": "^9.1.7",
33
+ "jsdoc": "^4.0.5",
34
+ "semantic-release": "^25.0.3"
35
+ },
36
+ "engines": {
37
+ "node": ">=22.0.0"
38
+ },
39
+ "dependencies": {
40
+ "better-sqlite3": "^12.10.0",
41
+ "drizzle-orm": "^0.45.2"
42
+ }
43
+ }
package/source/cli.js ADDED
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ // cli.js
3
+ // AI was consulted for large portions of this file.
4
+ // CLI for the issue tracker which allows the user to interact with the tracker.
5
+ // usage: baton [command] [options]
6
+ // commands:
7
+ // init: initialize the tracker
8
+ // next: work on the next issue
9
+ // loop: run the agent autonomously for multiple steps
10
+ // status: show issue counts and overall progress
11
+ //
12
+ // see each command's file for more detailed flags specifications.
13
+ /**
14
+ * Imports the run functions from each command.
15
+ */
16
+ 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
+ import { run as runStatus } from './commands/status.js';
20
+ import { run as runApprove } from './commands/approve.js';
21
+ import { wantsHelp } from './util.js';
22
+ import { run as runView} from './commands/view.js';
23
+ import { run as runSearch } from './commands/search.js';
24
+ import { run as runList } from './commands/list.js';
25
+ import { run as runCreate } from './commands/create.js'
26
+ import { run as runUpdate } from './commands/update.js'
27
+
28
+ const HELP = `baton — AI agent issue tracker CLI
29
+
30
+ Usage:
31
+ baton <command> [options]
32
+
33
+ Commands:
34
+ init Initialize storage and seed issues from product specs
35
+ next Work on the highest-priority open issue
36
+ loop Run the agent autonomously for multiple steps
37
+ status Show issue counts and overall progress
38
+ view View all issue fields for a given issue ID
39
+ search Search issues by title and description (case insensitive)
40
+ list Lists issues filtered by status and priority
41
+ create Creates an issue with specified fields
42
+ approve Move an issue from in-review to closed
43
+ update Updates an issue's specified fields
44
+
45
+ Options:
46
+ init --force Re-initialize an existing tracker database
47
+ init --specs <path> Path to product specs file (overrides default)
48
+ init <path> Same as --specs <path> (positional)
49
+ Default specs: docs/specs/project-requirements.md
50
+ loop --steps <n> Number of autonomous steps (alias: -n)
51
+ loop -n <n>
52
+ view <id>
53
+ search <query>
54
+ list --status <s> Filter by status: open | in-progress | closed
55
+ list --priority <p> Filter by priority: low | medium | high
56
+ list --limit <n> Max results (default: 50)
57
+ list --offset <n> Skip first n results (default: 0)
58
+ create --title <text> Issue title (defaults to "Issue #<id>" if omitted)
59
+ create --description <text> Issue description
60
+ create --priority <level> low | medium | high (default: low)
61
+ create --token-limit <n> Optional token budget for this issue
62
+ approve <id>
63
+ update --title <text> New title
64
+ update --description <text> New description
65
+ update --token-limit <n> New token budget
66
+ update --status <s> open | in-progress | closed
67
+ update --priority <level> low | medium | high
68
+
69
+
70
+ Examples:
71
+ baton init
72
+ baton init --specs ./my-specs.md
73
+ baton init ./my-specs.md
74
+ baton init --force
75
+ baton next
76
+ baton loop --steps 5
77
+ baton status
78
+ baton view 29
79
+ baton search system
80
+ baton list
81
+ baton list --status open --priority high
82
+ baton list --limit 10 --offset 20
83
+ baton create --title "Fix login bug" --priority high
84
+ baton create --title "Refactor auth" --description "Clean up JWT logic" --token-limit 4000
85
+ baton approve 5
86
+ baton update 3 --title "Revised title"
87
+ baton update 7 --status closed --priority medium
88
+ `;
89
+
90
+ /**
91
+ * Main function that runs the CLI.
92
+ * @returns {Promise<void>} The exit code: 0 is success, 1 is error, 2 is invalid input.
93
+ */
94
+ async function main() {
95
+ const [, , command, ...args] = process.argv;
96
+
97
+ if (!command || command === 'help' || wantsHelp(args) || command === '--help') {
98
+ console.log(HELP);
99
+ process.exit(command ? 0 : 1);
100
+ return;
101
+ }
102
+
103
+ const handlers = {
104
+ init: () => runInit(args),
105
+ next: () => runNext(args),
106
+ loop: () => runLoop(args),
107
+ status: () => runStatus(args),
108
+ view: () => runView(args),
109
+ search: () => runSearch(args),
110
+ list: () => runList(args),
111
+ approve: () => runApprove(args),
112
+ create: () => runCreate(args),
113
+ update: () => runUpdate(args)
114
+ };
115
+
116
+ const handler = handlers[command];
117
+ if (!handler) {
118
+ console.error(`Error: Unknown command "${command}".`);
119
+ console.error('Run `baton --help` for usage.');
120
+ process.exit(1);
121
+ return;
122
+ }
123
+
124
+ try {
125
+ const exitCode = await handler();
126
+ process.exit(exitCode ?? 0);
127
+ } catch (error) {
128
+ console.error(`Error: ${error.message}`);
129
+ process.exit(2);
130
+ }
131
+ }
132
+
133
+ main();
@@ -0,0 +1,43 @@
1
+ // approve.js
2
+ // AI was (regrettably) consulted for some portions of this file.
3
+ // approve command which allows a user to approve an issue currently under review.
4
+ // Usage: baton approve <id>
5
+
6
+ import { approveIssue } from '../services/issuesService.js';
7
+
8
+ /**
9
+ * Approves an issue and moves it to the closed state.
10
+ * @param {string[]} args - the command line arguments.
11
+ * @returns {Promise<number>} the exit code: 0 is success, 1 is error
12
+ */
13
+ export async function run(args) {
14
+ //check if id argument is empty
15
+ if (args.length == 0) {
16
+ throw new Error(
17
+ 'Invalid input: Missing issue ID.\nUsage: baton approve <id>'
18
+ );
19
+ }
20
+
21
+ const id = args.join(' ');
22
+
23
+ //check if ID argument isn't a number
24
+ if (isNaN(id)) {
25
+ throw new Error(
26
+ 'Invalid input: ID must be a number.\nUsage: baton approve <id>'
27
+ );
28
+ }
29
+
30
+ //try to approve the issue
31
+ try {
32
+ const issue = await approveIssue(id);
33
+ console.log(
34
+ `Issue #${issue.id} approved and moved to ${issue.status}.`
35
+ );
36
+
37
+ return 0;
38
+ } catch (error) {
39
+ console.error('Error: Failed to approve issue.');
40
+ console.error(error.message);
41
+ return 1;
42
+ }
43
+ }
@@ -0,0 +1,48 @@
1
+ // create.js
2
+ // AI was consulted for some portions of this file.
3
+ // create command allows the user to create an issue
4
+ // Usage: baton create --title <text> --description <text> --priority <level> --token-limit <n>
5
+ //
6
+ // Examples:
7
+ // baton create --title "Fix login bug" --priority high
8
+ // baton create --title "Refactor auth" --description "Clean up JWT logic" --token-limit 4000
9
+ // Options:
10
+ // --title <text> Issue title (defaults to "Issue #<id>" if omitted)
11
+ // --description <text> Issue description
12
+ // --priority <level> low | medium | high (default: low)
13
+ // --token-limit <n> Optional token budget for this issue
14
+ // -h, --help Show this help
15
+
16
+ import { createIssue } from "../services/issuesService.js";
17
+ import { getFlagValue, getNumericFlag } from "../util.js";
18
+ import { parseArgs } from '../util.js';
19
+
20
+ /**
21
+ * Initializes a new issue in the database with the specified fields
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 validFlags = ['--title', '--description', '--priority', '--token-limit'];
27
+ // Check if user misspelled a flag
28
+ for (const arg of args) {
29
+ if (arg.startsWith('--')) {
30
+ if (!validFlags.includes(arg)) {
31
+ throw new Error(`Unknown flag provided: ${arg}. \nFlags: --title <text>, --description <text>, --priority <level>, --token-limit <n>`);
32
+ }
33
+ }
34
+ }
35
+
36
+ try {
37
+ const options = parseArgs(args);
38
+
39
+ const issue = await createIssue(options);
40
+
41
+ // program reaches this line if issue was successfully created
42
+ console.log(`Successfully created issue #${issue.id}: "${issue.title}"`);
43
+ return 0;
44
+ } catch (error) {
45
+ console.error(`Failed to create issue: ${error.message}`);
46
+ return 1;
47
+ }
48
+ }