baton-issue-tracker 1.13.2 → 1.13.3

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 CHANGED
@@ -1,14 +1,84 @@
1
- # Fantastic Four
1
+ <p align="center">
2
+ <img src="./admin/branding/teamLogo.png" width="100">
3
+ </p>
2
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
+ # Baton Issue Tracker (Baton)
6
6
 
7
- ## Project Details:
8
- TBD
7
+ [![NPM Version](https://img.shields.io/npm/v/baton-issue-tracker)](https://www.npmjs.com/package/baton-issue-tracker)
9
8
 
10
- # Team Page
11
- [Click Here](/admin/team.md)
9
+ **Baton** is a terminal-first issue tracker designed for the modern AI-augmented software engineering workflow. It provides a structured interface for human supervisors to manage, track, and approve tasks performed by autonomous AI agents.
12
10
 
13
- # Agile: Team Status Video
14
- [https://youtu.be/xToi68JAcwk](https://youtu.be/xToi68JAcwk)
11
+ In an environment where AI does much of the heavy lifting, Baton ensures accountability, tracks token budgets, and maintains a rigorous audit trail of every change made to your project's backlog.
12
+
13
+ **Baton** is developed and maintained by **Team Fantastic Four**.
14
+
15
+ ---
16
+
17
+ ## Quickstart
18
+
19
+ Get up and running with Baton in seconds.
20
+
21
+ ### 1. Installation
22
+ Install Baton globally via NPM:
23
+ ```bash
24
+ npm install -g baton-issue-tracker
25
+ ```
26
+
27
+ ### 2. Initialization
28
+ Initialize a new tracker in your project root. This creates a local SQLite database at `.baton/baton.db`.
29
+ ```bash
30
+ baton init
31
+ ```
32
+
33
+ ### 3. Check Identity
34
+ Verify your registration or add yourself to the project:
35
+ ```bash
36
+ baton whoami
37
+ # If not registered:
38
+ baton register --name "your-name" --type human
39
+ ```
40
+
41
+ ### 4. Basic Workflow
42
+ * **Create a task:** `baton create --title "Fix login bug" --priority high`
43
+ * **Check status:** `baton status`
44
+ * **Claim next task (for agents):** `baton claim`
45
+ * **Submit work (for agents):** `baton submit <id>`
46
+ * **Approve work (for humans):** `baton approve <id>`
47
+
48
+ ---
49
+
50
+ ## Features
51
+
52
+ * **AI-Native API:** All commands support a `--json` flag for easy integration with AI agent loops.
53
+ * **Human-in-the-Loop:** A strict state machine ensuring agents can only resolve issues with human approval.
54
+ * **Full Activity History:** Track every edit, status change, and assignment with `baton log`.
55
+ * **Token Management:** Assign token limits to specific issues to control agent costs.
56
+ * **Zero-Config Storage:** Fully local SQLite storage—no external servers required.
57
+
58
+ ---
59
+
60
+ ## Documentation Index
61
+
62
+ Baton is extensively documented to help both humans and agents get the most out of the system:
63
+
64
+ ### **Guides**
65
+ * [**CLI Reference**](./docs/cli/README.md): Detailed documentation for every command and flag.
66
+ * [**Setup & Workflow**](./docs/cli/setup-commands.md): How to initialize and manage the agent work-loop.
67
+ * [**Issue Management**](./docs/cli/issue-commands.md): Deep dive into ticket CRUD and review processes.
68
+ * [**Contributing**](./CONTRIBUTING.md): Guidelines for developing and extending Baton.
69
+
70
+ ### **Specifications**
71
+ * [**Product Requirements**](./docs/specs/project-requirements.md): The functional goals of the project.
72
+ * [**Data Model**](./docs/specs/issue-data-model.md): Detailed schema for issues and activity logs.
73
+ * [**ADRs (Architecture Decisions)**](./docs/adr/): Why we built Baton the way we did.
74
+
75
+ ---
76
+
77
+ ## Team
78
+ Baton is developed and maintained by **Team Fantastic Four** (CSE 110, SP26).
79
+ Meet the team on our [**Team Page**](./admin/team.md).
80
+
81
+ ---
82
+
83
+ ## License
84
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baton-issue-tracker",
3
- "version": "1.13.2",
3
+ "version": "1.13.3",
4
4
  "description": "A CLI issue tracker for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
package/source/cli.js CHANGED
@@ -136,7 +136,7 @@ async function main() {
136
136
  }
137
137
 
138
138
  // Authenticate the user and context before executing any command.
139
- authenticateContext(command);
139
+ authenticateContext(command, args);
140
140
 
141
141
  const handlers = {
142
142
  init: () => runInit(args),
@@ -22,8 +22,9 @@ function serializeLogEntry(entry) {
22
22
  return {
23
23
  log_id: entry.logId,
24
24
  issue_id: entry.issueId,
25
+ actor_id: entry.actorId,
25
26
  action: entry.action,
26
- details: entry.details ?? null,
27
+ details: entry.details,
27
28
  created_at: entry.createdAt,
28
29
  };
29
30
  }
@@ -18,7 +18,8 @@ if (!fs.existsSync(dataDir)) {
18
18
  }
19
19
 
20
20
  // Initialize better-sqlite3 connection
21
- const sqliteConnection = new Database(dbPath);
21
+ // Using let here so we can reassign it in tests with an in-memory database instance.
22
+ let sqliteConnection = new Database(dbPath);
22
23
 
23
24
  // Initialize Drizzle ORM, passing in the schema for relational queries
24
25
  // Using let here so we can reassign it in tests with an in-memory database instance.
@@ -71,6 +72,7 @@ export function getSQLiteDB() {
71
72
  }
72
73
 
73
74
  // function for the test suite mocking
74
- export function setTestDB(testDbInstance) {
75
+ export function setTestDB(testDbInstance, testSqliteConnection) {
75
76
  db = testDbInstance;
77
+ if (testSqliteConnection) sqliteConnection = testSqliteConnection;
76
78
  }
@@ -14,12 +14,14 @@ export class ActivityLog {
14
14
  issueId,
15
15
  actorId = null,
16
16
  action,
17
+ details = null,
17
18
  createdAt = new Date().toISOString(),
18
19
  } = {}) {
19
20
  this.logId = logId;
20
21
  this.issueId = issueId;
21
22
  this.actorId = actorId;
22
23
  this.action = action;
24
+ this.details = details,
23
25
  this.createdAt = createdAt;
24
26
  }
25
27
  }
@@ -0,0 +1,26 @@
1
+ // agentRestrictions.js
2
+
3
+ const AGENT_RESTRICTED_COMMANDS = new Set(['init', 'approve', 'reject', 'delete', 'priority']);
4
+ const AGENT_RESTRICTED_FLAGS = new Set(['--status', '--token-limit']);
5
+
6
+ /**
7
+ * Validates whether an actor has permission to execute a given command and arguments.
8
+ * @param {string} command - The primary CLI command name.
9
+ * @throws {Error} If the agent entered a restricted command or flag
10
+ */
11
+ export function authorizeAction(command, args = []) {
12
+ if (AGENT_RESTRICTED_COMMANDS.has(command)) {
13
+ throw new Error(`Command "${command}" is restricted to human users only.`);
14
+ }
15
+
16
+ if (command === 'update') {
17
+ const args = process.argv.slice(2);
18
+ const usedRestricted = args.filter(arg => AGENT_RESTRICTED_FLAGS.has(arg));
19
+ if (usedRestricted.length > 0) {
20
+ throw new Error(
21
+ `Agents cannot update parameters: ${usedRestricted.join(', ')}. ` +
22
+ `Use 'baton claim', 'baton submit', or 'baton unclaim' for status changes.`
23
+ );
24
+ }
25
+ }
26
+ }
@@ -1,6 +1,8 @@
1
1
  import os from 'os';
2
2
  import { getAgentByName } from './agentsService.js';
3
3
  import { setCurrentActor, getCurrentActor } from './context.js';
4
+ import { isTrackerReady } from './issuesService.js';
5
+ import { authorizeAction } from './agentRestrictions.js';
4
6
 
5
7
  export { getCurrentActor };
6
8
 
@@ -11,29 +13,33 @@ export { getCurrentActor };
11
13
  * If not, it terminates the process with a helpful error message.
12
14
  * @param {string} command - The command being executed
13
15
  */
14
- export function authenticateContext(command) {
15
- const exemptCommands = ['init', 'register', 'help'];
16
+ export function authenticateContext(command, args = []) {
17
+ const exemptCommands = ['register', 'help'];
16
18
  if (exemptCommands.includes(command)) {
17
19
  return;
18
20
  }
19
21
 
20
- const activeName = process.env.BATON_AGENT || os.userInfo().username;
21
-
22
- try {
23
- const agent = getAgentByName(activeName);
22
+ if (!isTrackerReady()) {
23
+ if (command === 'init') {
24
+ return;
25
+ }
26
+ console.error("Error: Tracker is not initialized. Please run 'baton init' first.");
27
+ process.exit(1);
28
+ }
24
29
 
25
- if (!agent) {
30
+ const activeName = process.env.BATON_AGENT || os.userInfo().username;
31
+ const agent = getAgentByName(activeName);
32
+
33
+ if (!agent) {
26
34
  console.error(`Error: Agent/User "${activeName}" is not registered in this Baton tracker.`);
27
35
  console.error(`Please run 'baton register --name "${activeName}" --type "human"' (or "agent") first.`);
28
36
  process.exit(1);
29
- }
37
+ }
30
38
 
31
- setCurrentActor(agent);
32
- } catch (error) {
33
- if (error.message && error.message.includes('no such table: agents')) {
34
- console.error("Error: Tracker is not initialized. Please run 'baton init' first.");
35
- process.exit(1);
36
- }
37
- throw error;
39
+ // Check if agent entered a restricted command
40
+ if (agent.type === 'agent') {
41
+ authorizeAction(command, args);
38
42
  }
43
+
44
+ setCurrentActor(agent);
39
45
  }
@@ -384,7 +384,7 @@ export function deleteIssue(id) {
384
384
  */
385
385
  export function getActivityLog(issueId) {
386
386
  const db = getDB();
387
- return db.select().from(activityTable).where(eq(activityTable.issueId, issueId)).all();
387
+ return db.select().from(activityTable).where(eq(activityTable.issueId, issueId)).all().map(rowToLog);
388
388
  }
389
389
 
390
390
  /**
@@ -394,7 +394,7 @@ export function getActivityLog(issueId) {
394
394
  */
395
395
  export function getRecentActivity({ limit = 20 } = {}) {
396
396
  const db = getDB();
397
- return db.select().from(activityTable).orderBy(sql`${activityTable.logId} DESC`).limit(limit).all();
397
+ return db.select().from(activityTable).orderBy(sql`${activityTable.logId} DESC`).limit(limit).all().map(rowToLog);
398
398
  }
399
399
  // =============================================================================
400
400
  // Tracker operations (CLI: init / next / status / claim)