baton-issue-tracker 1.7.0 → 1.9.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.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "A CLI issue tracker for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
package/source/cli.js CHANGED
@@ -18,12 +18,14 @@ import { run as runNext } from './commands/next.js';
18
18
  import { run as runLoop } from './commands/loop.js';
19
19
  import { run as runStatus } from './commands/status.js';
20
20
  import { run as runApprove } from './commands/approve.js';
21
+ import { run as runReject } from './commands/reject.js';
21
22
  import { wantsHelp } from './util.js';
22
23
  import { run as runView} from './commands/view.js';
23
24
  import { run as runSearch } from './commands/search.js';
24
25
  import { run as runList } from './commands/list.js';
25
26
  import { run as runCreate } from './commands/create.js'
26
27
  import { run as runUpdate } from './commands/update.js';
28
+ import { run as runDelete } from './commands/delete.js';
27
29
  import { run as runPriority } from './commands/priority.js';
28
30
  import { run as runLog } from './commands/log.js';
29
31
  import { run as runRegister } from './commands/register.js';
@@ -48,6 +50,7 @@ Commands:
48
50
  approve Move an issue from in-review to closed
49
51
  priority Set an issue's priority level
50
52
  update Updates an issue's specified fields
53
+ delete Deletes an issue
51
54
  log Show activity history for an issue
52
55
 
53
56
  Options:
@@ -76,6 +79,7 @@ Options:
76
79
  create --token-limit <n> Optional token budget for this issue
77
80
  create --json Output as JSON (for AI agents)
78
81
  approve <id> [--json]
82
+ reject <id> --reason <text> Reject an issue with a given reason
79
83
  priority <id> <level> [--json] low | medium | high
80
84
  update --title <text> New title
81
85
  update --description <text> New description
@@ -83,6 +87,7 @@ Options:
83
87
  update --status <s> open | in-progress | closed
84
88
  update --priority <level> low | medium | high
85
89
  update --json Output as JSON (for AI agents)
90
+ delete <id> [--yes]
86
91
  log <id> [--json]
87
92
 
88
93
  Examples:
@@ -135,9 +140,11 @@ async function main() {
135
140
  search: () => runSearch(args),
136
141
  list: () => runList(args),
137
142
  approve: () => runApprove(args),
143
+ reject: () => runReject(args),
138
144
  priority: () => runPriority(args),
139
145
  create: () => runCreate(args),
140
146
  update: () => runUpdate(args),
147
+ delete: () => runDelete(args),
141
148
  log: () => runLog(args),
142
149
  };
143
150
 
@@ -0,0 +1,99 @@
1
+ // delete.js
2
+ // supports deleting an issue.
3
+ //
4
+ // AI was used to modify this file to support JSON outputs.
5
+ //
6
+ // Usage:
7
+ // baton delete <id> [--yes] [--json]
8
+ //
9
+ // Options:
10
+ // --yes Skip confirmation prompt
11
+ // --json Output in JSON format
12
+ // -h, --help Show this help
13
+ //
14
+ // Examples:
15
+ // baton delete 4
16
+ // baton delete 4 --yes
17
+
18
+ import { deleteIssue, getIssue } from '../services/issuesService.js';
19
+ import { hasFlag, wantsHelp, renderOutput, renderError } from '../util.js';
20
+ import { confirm } from '@inquirer/prompts';
21
+
22
+ const USAGE = "Usage: baton delete <id> [options]\n\nOptions:\n --yes Skip confirmation prompt\n --json Output in JSON format\n -h, --help Show this help";
23
+
24
+ /**
25
+ * Deletes an issue for a specified ID.
26
+ *
27
+ * @param {string[]} args - The command line arguments
28
+ * @returns {Promise<number>} The exit code: 0 is success, 1 is error.
29
+ */
30
+ export async function run(args) {
31
+ const isJson = hasFlag(args, '--json');
32
+
33
+ // (0) Help check
34
+ if (wantsHelp(args)) {
35
+ console.log(USAGE);
36
+ return 0;
37
+ }
38
+
39
+ // (1) Parse arguments
40
+ const idArgs = args.filter(arg => !arg.startsWith('-'));
41
+ if (idArgs.length === 0) {
42
+ renderError(isJson, `No ID provided.\n${USAGE}`, 'MISSING_ID');
43
+ return 1;
44
+ }
45
+
46
+ const id = Number(idArgs[0]);
47
+ if (!Number.isInteger(id)) {
48
+ renderError(isJson, `Invalid ID "${idArgs[0]}". ID must be an integer.`, 'INVALID_ID');
49
+ return 1;
50
+ }
51
+
52
+ const isYes = hasFlag(args, "--yes");
53
+
54
+ try {
55
+ // (2) Check if issue exists before confirming
56
+ try {
57
+ await getIssue(id);
58
+ } catch (error) {
59
+ if (error.message.includes("not found")) {
60
+ renderError(isJson, error.message, 'NOT_FOUND');
61
+ } else {
62
+ renderError(isJson, error.message);
63
+ }
64
+ return 1;
65
+ }
66
+
67
+ // (3) Confirmation prompt
68
+ let confirmed = isYes;
69
+ if (!confirmed && !isJson) { // Only prompt if not JSON and not --yes
70
+ try {
71
+ confirmed = await confirm({ message: `Are you sure you want to delete issue #${id}?`, default: false });
72
+ } catch {
73
+ return 1;
74
+ }
75
+ } else if (!confirmed && isJson) {
76
+ // In JSON mode, do not prompt. If --yes is missing, we fail or assume no.
77
+ renderError(isJson, "Confirmation required. Use --yes to confirm deletion in JSON mode.", 'CONFIRMATION_REQUIRED');
78
+ return 1;
79
+ }
80
+
81
+ if (!confirmed) {
82
+ console.log("Deletion cancelled.");
83
+ return 0;
84
+ }
85
+
86
+ // (4) Execution
87
+ await deleteIssue(id);
88
+
89
+ const envelope = { status: 'success', id: id, message: `Issue #${id} deleted successfully.` };
90
+ renderOutput(isJson, envelope, () => {
91
+ console.log(`Issue #${id} deleted successfully.`);
92
+ });
93
+
94
+ return 0;
95
+ } catch (error) {
96
+ renderError(isJson, error.message);
97
+ return 1;
98
+ }
99
+ }
@@ -0,0 +1,99 @@
1
+ // reject.js
2
+ // supports rejecting an issue.
3
+ //
4
+ // AI Was used in generating this file.
5
+ // Usage:
6
+ // baton reject <id> [options]
7
+ //
8
+ // Options:
9
+ // --reason <text> Reason for rejection (required)
10
+ // --json Output in JSON format
11
+ // -h, --help Show this help
12
+ //
13
+ // Examples:
14
+ // baton reject 5 --reason "Needs more detail"
15
+
16
+ import { rejectIssue, getIssue } from '../services/issuesService.js';
17
+ import { Status } from '../models/issue.js';
18
+ import { hasFlag, getFlagValue, wantsHelp, renderOutput, renderError, serializeIssue } from '../util.js';
19
+
20
+ const USAGE = "Usage:\n baton reject <id> [options]\n\nOptions:\n --reason <text> Reason for rejection (required)\n --json Output in JSON format\n -h, --help Show this help";
21
+
22
+ /**
23
+ * Rejects an issue for a specified ID.
24
+ *
25
+ * @param {string[]} args - The command line arguments
26
+ * @returns {Promise<number>} The exit code: 0 is success, 1 is error.
27
+ */
28
+ export async function run(args) {
29
+ const isJson = hasFlag(args, '--json');
30
+
31
+ // (0) Help check
32
+ if (wantsHelp(args)) {
33
+ console.log(USAGE);
34
+ return 0;
35
+ }
36
+
37
+ // (1) Parse arguments
38
+ const idArgs = args.filter(arg => !arg.startsWith('-'));
39
+ if (idArgs.length === 0) {
40
+ renderError(isJson, `No ID provided.\n${USAGE}`, 'MISSING_ID');
41
+ return 1;
42
+ }
43
+
44
+ const id = Number(idArgs[0]);
45
+ if (!Number.isInteger(id)) {
46
+ renderError(isJson, `Invalid ID "${idArgs[0]}". ID must be an integer.`, 'INVALID_ID');
47
+ return 1;
48
+ }
49
+
50
+ if (!hasFlag(args, "--reason")) {
51
+ renderError(isJson, `Missing reason for reject.\n${USAGE}`, 'MISSING_REASON');
52
+ return 1;
53
+ }
54
+
55
+ let reasonText;
56
+ try {
57
+ reasonText = getFlagValue(args, "--reason");
58
+ } catch (error) {
59
+ renderError(isJson, error.message, 'INVALID_REASON');
60
+ return 1;
61
+ }
62
+
63
+ if (!reasonText || reasonText.trim() === "") {
64
+ renderError(isJson, "Reason for rejection cannot be empty.", 'EMPTY_REASON');
65
+ return 1;
66
+ }
67
+
68
+ let issue;
69
+ try {
70
+ issue = await getIssue(id);
71
+ } catch (error) {
72
+ if (error.message.includes("not found")) {
73
+ renderError(isJson, error.message, 'NOT_FOUND');
74
+ } else {
75
+ renderError(isJson, error.message);
76
+ }
77
+ return 1;
78
+ }
79
+
80
+ if (issue.status !== Status.IN_REVIEW) {
81
+ renderError(isJson, `Issue #${id} is currently "${issue.status}". Only issues in "${Status.IN_REVIEW}" status can be rejected.`, 'INVALID_STATE');
82
+ return 1;
83
+ }
84
+
85
+ try {
86
+ // (3) Perform rejection
87
+ const updatedIssue = await rejectIssue(id, reasonText);
88
+ const envelope = { status: 'success', issue: serializeIssue(updatedIssue) };
89
+
90
+ renderOutput(isJson, envelope, () => {
91
+ console.log(`Issue #${id} rejected successfully and moved back to "${updatedIssue.status}".`);
92
+ });
93
+
94
+ return 0;
95
+ } catch (error) {
96
+ renderError(isJson, error.message);
97
+ return 1;
98
+ }
99
+ }