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 +80 -10
- package/package.json +1 -1
- package/source/cli.js +1 -1
- package/source/commands/log.js +2 -1
- package/source/db/index.js +4 -2
- package/source/models/activityLog.js +2 -0
- package/source/services/agentRestrictions.js +26 -0
- package/source/services/authService.js +21 -15
- package/source/services/issuesService.js +2 -2
package/README.md
CHANGED
|
@@ -1,14 +1,84 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./admin/branding/teamLogo.png" width="100">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
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
|
-
|
|
8
|
-
TBD
|
|
7
|
+
[](https://www.npmjs.com/package/baton-issue-tracker)
|
|
9
8
|
|
|
10
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
package/source/cli.js
CHANGED
package/source/commands/log.js
CHANGED
|
@@ -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
|
|
27
|
+
details: entry.details,
|
|
27
28
|
created_at: entry.createdAt,
|
|
28
29
|
};
|
|
29
30
|
}
|
package/source/db/index.js
CHANGED
|
@@ -18,7 +18,8 @@ if (!fs.existsSync(dataDir)) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// Initialize better-sqlite3 connection
|
|
21
|
-
|
|
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 = ['
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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)
|