prodboard 0.1.3 → 0.2.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.
@@ -0,0 +1,75 @@
1
+ import * as path from "path";
2
+ import * as fs from "fs";
3
+
4
+ export class WorktreeManager {
5
+ constructor(private basePath: string) {}
6
+
7
+ async create(runId: string, sourceDir: string): Promise<string> {
8
+ const worktreesDir = path.join(this.basePath, ".worktrees");
9
+ const worktreePath = path.join(worktreesDir, runId);
10
+ const branchName = `prodboard/${runId}`;
11
+
12
+ if (!fs.existsSync(worktreesDir)) {
13
+ fs.mkdirSync(worktreesDir, { recursive: true });
14
+ }
15
+
16
+ const result = Bun.spawnSync(
17
+ ["git", "worktree", "add", worktreePath, "-b", branchName],
18
+ { cwd: sourceDir, stdout: "pipe", stderr: "pipe" }
19
+ );
20
+
21
+ if (result.exitCode !== 0) {
22
+ const stderr = new TextDecoder().decode(result.stderr);
23
+ throw new Error(`Failed to create worktree: ${stderr}`);
24
+ }
25
+
26
+ return path.resolve(worktreePath);
27
+ }
28
+
29
+ async remove(runId: string): Promise<void> {
30
+ const worktreePath = path.join(this.basePath, ".worktrees", runId);
31
+ const branchName = `prodboard/${runId}`;
32
+
33
+ // Remove worktree
34
+ try {
35
+ Bun.spawnSync(
36
+ ["git", "worktree", "remove", worktreePath, "--force"],
37
+ { cwd: this.basePath, stdout: "pipe", stderr: "pipe" }
38
+ );
39
+ } catch {}
40
+
41
+ // Clean up the directory if it still exists
42
+ try {
43
+ if (fs.existsSync(worktreePath)) {
44
+ fs.rmSync(worktreePath, { recursive: true, force: true });
45
+ }
46
+ } catch {}
47
+
48
+ // Delete the branch (try safe delete first, force-delete as fallback)
49
+ try {
50
+ const result = Bun.spawnSync(
51
+ ["git", "branch", "-d", branchName],
52
+ { cwd: this.basePath, stdout: "pipe", stderr: "pipe" }
53
+ );
54
+ if (result.exitCode !== 0) {
55
+ console.error(`[prodboard] Warning: Branch ${branchName} has unmerged commits, force-deleting`);
56
+ Bun.spawnSync(
57
+ ["git", "branch", "-D", branchName],
58
+ { cwd: this.basePath, stdout: "pipe", stderr: "pipe" }
59
+ );
60
+ }
61
+ } catch {}
62
+ }
63
+
64
+ isGitRepo(dir: string): boolean {
65
+ try {
66
+ const result = Bun.spawnSync(
67
+ ["git", "rev-parse", "--git-dir"],
68
+ { cwd: dir, stdout: "pipe", stderr: "pipe" }
69
+ );
70
+ return result.exitCode === 0;
71
+ } catch {
72
+ return false;
73
+ }
74
+ }
75
+ }
@@ -2,6 +2,9 @@
2
2
 
3
3
  This project uses prodboard for issue tracking. The prodboard MCP server is configured and available.
4
4
 
5
+ ## Supported Agents
6
+ prodboard supports multiple AI coding agents (Claude Code, OpenCode). The daemon is configured to use one agent at a time.
7
+
5
8
  ## Issue Workflow
6
9
  - Check `board_summary` at the start of each session
7
10
  - Use `pick_next_issue` to claim work
@@ -14,10 +14,26 @@
14
14
  },
15
15
 
16
16
  "daemon": {
17
+ // Agent to use: "claude" or "opencode"
18
+ // "agent": "claude",
19
+
20
+ // Base path for worktrees and agent runs (null = use schedule workdir)
21
+ // "basePath": null,
22
+
23
+ // Wrap agent runs in tmux sessions (allows attaching to running agents)
24
+ // "useTmux": true,
25
+
26
+ // OpenCode-specific settings (only used when agent is "opencode")
27
+ // "opencode": {
28
+ // "serverUrl": null, // auto-detect or override (e.g., "http://localhost:4096")
29
+ // "model": null, // e.g., "anthropic/claude-sonnet-4-20250514"
30
+ // "agent": null // opencode agent name
31
+ // },
32
+
17
33
  // Maximum concurrent scheduled runs
18
34
  // "maxConcurrentRuns": 2,
19
35
 
20
- // Default max turns for Claude invocations
36
+ // Default max turns for agent invocations
21
37
  // "maxTurns": 50,
22
38
 
23
39
  // Absolute maximum turns (cannot be overridden by schedules)
@@ -32,7 +48,33 @@
32
48
  // Log level: "debug", "info", "warn", "error"
33
49
  // "logLevel": "info",
34
50
 
51
+ // Max size per log file in MB
52
+ // "logMaxSizeMb": 10,
53
+
54
+ // Max number of rotated log files
55
+ // "logMaxFiles": 5,
56
+
57
+ // Tools allowed for scheduled runs in git repos
58
+ // "defaultAllowedTools": ["Read", "Edit", "Write", "Glob", "Grep", "Bash", ...],
59
+
60
+ // Tools allowed for scheduled runs in non-git directories
61
+ // "nonGitDefaultAllowedTools": ["Read", "Edit", "Write", "Glob", "Grep", "Bash", ...],
62
+
35
63
  // Worktree usage: "auto", "always", "never"
36
64
  // "useWorktrees": "auto"
65
+ },
66
+
67
+ "webui": {
68
+ // Enable the web UI (served by the daemon)
69
+ // "enabled": false,
70
+
71
+ // Port for the web UI
72
+ // "port": 3838,
73
+
74
+ // Hostname to bind to
75
+ // "hostname": "127.0.0.1",
76
+
77
+ // Password for web UI access (null = no auth)
78
+ // "password": null
37
79
  }
38
80
  }