@plot-ai/linux-x64-musl 0.0.2 → 0.0.4

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/bin/CHANGELOG.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # changelog
2
2
 
3
- ## 0.0.2
3
+ ## 0.0.4
4
4
 
5
5
  - packaged plot-ai release
package/bin/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plot-ai/linux-x64-musl",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "piConfig": {
5
5
  "name": "plot",
6
6
  "configDir": ".plot"
package/bin/plot-ai CHANGED
Binary file
@@ -0,0 +1,241 @@
1
+ ---
2
+ name: plot-beads-tracker
3
+ description: "beads issue state management for plot orchestration. hybrid model: native beads status for primary lifecycle, labels for orchestrator sub-states (rework, merging). workpad comment lifecycle and bd queries. use when moving issues between states, creating/updating workpad comments, or querying issue status."
4
+ ---
5
+
6
+ # plot-beads-tracker
7
+
8
+ manages issue state and progress tracking for the plot orchestrator via beads status, labels, and comments.
9
+
10
+ use `bd` for all tracker operations. the git/PR lifecycle uses `gh` separately.
11
+
12
+ ## state model
13
+
14
+ beads uses a hybrid state model with two layers:
15
+
16
+ - **status** (native beads lifecycle): `open`, `in_progress`, `blocked`, `deferred`, `closed`
17
+ - **labels** (orchestrator sub-states): `plot:rework`, `plot:merging`
18
+
19
+ the orchestrator checks labels first. if a configured label exists on the issue, it becomes the routing state. otherwise the native beads status is the state.
20
+
21
+ ```
22
+ ┌─────────┐
23
+ │ open │ agent picks up, starts work
24
+ └────┬────┘
25
+ │ bd update <id> --status in_progress
26
+
27
+ ┌─────────────┐
28
+ │ in_progress │ agent implements, pushes PR
29
+ └──────┬──────┘
30
+ │ bd update <id> --status blocked
31
+
32
+ ┌─────────┐
33
+ │ blocked │ human reviews PR
34
+ └────┬────┘
35
+
36
+ ┌──────────┼──────────┐
37
+ ▼ ▼ ▼
38
+ ┌────────────┐ ┌─────────────────┐ ┌────────┐
39
+ │plot:rework │ │ plot:merging │ │closed │
40
+ │(label) │ │ (label) │ │(reject)│
41
+ └─────┬──────┘ └───────┬─────────┘ └────────┘
42
+ │ │
43
+ │ agent fixes, │ agent merges PR,
44
+ │ removes label, │ removes label,
45
+ │ → blocked │ closes issue
46
+ │ │
47
+ ▼ ▼
48
+ ┌─────────┐ ┌────────┐
49
+ │ blocked │ │ closed │
50
+ └─────────┘ └────────┘
51
+ ```
52
+
53
+ ## state transitions
54
+
55
+ ```bash
56
+ # open → in_progress (starting work)
57
+ bd update <id> --status in_progress
58
+
59
+ # in_progress → blocked (PR ready for review)
60
+ bd update <id> --status blocked
61
+
62
+ # human approves → add merging label (human does this)
63
+ bd label add <id> plot:merging
64
+
65
+ # human requests changes → add rework label (human does this)
66
+ bd label add <id> plot:rework
67
+
68
+ # plot:rework → blocked (rework complete, remove label first)
69
+ bd label remove <id> plot:rework
70
+ bd update <id> --status blocked
71
+
72
+ # plot:merging → closed (PR merged, remove label first)
73
+ bd label remove <id> plot:merging
74
+ bd close <id> -r "completed"
75
+
76
+ # reopen closed → in_progress
77
+ bd reopen <id> -r "needs rework"
78
+ ```
79
+
80
+ ## available states
81
+
82
+ | source | state | meaning | agent action |
83
+ | ------ | -------------- | --------------------------- | ----------------------------------------------------- |
84
+ | status | `open` | queued for work | move to `in_progress`, start implementation |
85
+ | status | `in_progress` | implementation underway | continue implementation, push PR, move to `blocked` |
86
+ | status | `blocked` | waiting on human review | do nothing, wait |
87
+ | status | `deferred` | intentionally postponed | do nothing, wait |
88
+ | label | `plot:rework` | reviewer requested changes | address feedback, remove label, move to `blocked` |
89
+ | label | `plot:merging` | human approved PR | merge PR, remove label, close issue |
90
+ | status | `closed` | terminal | stop |
91
+
92
+ workflow routing:
93
+
94
+ - dispatch_states: `open`, `in_progress`, `plot:rework`, `plot:merging`
95
+ - parked_states: `blocked`, `deferred`
96
+ - terminal_states: `closed`
97
+
98
+ ## workpad comment
99
+
100
+ a persistent beads comment used as a living scratchpad across agent sessions.
101
+
102
+ ### find current workpad
103
+
104
+ comments are append-only. there is no edit-in-place. the current workpad is the latest comment whose body starts with `## Plot Workpad`.
105
+
106
+ ```bash
107
+ bd comments <id> --json
108
+ ```
109
+
110
+ scan the returned comments and pick the latest one starting with `## Plot Workpad`.
111
+
112
+ if none exists, create one.
113
+
114
+ ### create workpad
115
+
116
+ write the full workpad body to a temp file, then add it as a comment:
117
+
118
+ ````bash
119
+ cat > /tmp/workpad.md <<'WORKPAD'
120
+ ## Plot Workpad
121
+
122
+ ```text
123
+ <workspace-id>@<short-sha>
124
+ ```
125
+
126
+ ### Plan
127
+ - [ ] 1. <task>
128
+
129
+ ### Acceptance Criteria
130
+ - [ ] <criterion>
131
+
132
+ ### Validation
133
+ - [ ] targeted tests: `<command>`
134
+
135
+ ### Latest Attempt Summary
136
+ - changed: <files or none>
137
+ - validated: <commands + outcome>
138
+ - failed: <remaining failure or none>
139
+ - blocked: <blocker or none>
140
+
141
+ ### Notes
142
+ - <durable context>
143
+ WORKPAD
144
+
145
+ bd comments add <id> -f /tmp/workpad.md
146
+ ````
147
+
148
+ ### update workpad
149
+
150
+ do not try to edit an old comment. add a new full workpad comment with the complete updated body.
151
+
152
+ ````bash
153
+ cat > /tmp/workpad.md <<'WORKPAD'
154
+ ...full updated workpad body...
155
+ WORKPAD
156
+
157
+ bd comments add <id> -f /tmp/workpad.md
158
+ ````
159
+
160
+ ### rules
161
+
162
+ - treat the latest `## Plot Workpad` comment as canonical
163
+ - never rely on older workpad comments once a newer one exists
164
+ - update the workpad after every meaningful milestone
165
+ - never leave completed items unchecked
166
+ - include workspace id and short sha at top: `<workspace-id>@<short-sha>`
167
+ - keep the full workpad body in each update so the latest comment is self-contained
168
+
169
+ ## workpad template
170
+
171
+ ````markdown
172
+ ## Plot Workpad
173
+
174
+ ```text
175
+ <workspace-id>@<short-sha>
176
+ ```
177
+
178
+ ### Plan
179
+ - [ ] 1. Parent task
180
+ - [ ] 1.1 Child task
181
+ - [ ] 2. Parent task
182
+
183
+ ### Acceptance Criteria
184
+ - [ ] Criterion 1
185
+ - [ ] Criterion 2
186
+
187
+ ### Validation
188
+ - [ ] targeted tests: `<command>`
189
+
190
+ ### Latest Attempt Summary
191
+ - changed: <files or none>
192
+ - validated: <commands + outcome>
193
+ - failed: <remaining failure or none>
194
+ - blocked: <blocker or none>
195
+
196
+ ### Notes
197
+ - <short durable context>
198
+
199
+ ````
200
+
201
+ ## issue queries
202
+
203
+ ```bash
204
+ # full issue details
205
+ bd show <id> --json
206
+
207
+ # all comments (for workpad lookup)
208
+ bd comments <id> --json
209
+
210
+ # list by status
211
+ bd list --status open --json
212
+ bd list --status in_progress --json
213
+ bd list --status blocked --json
214
+
215
+ # list by label
216
+ bd list --label plot:rework --json
217
+ bd list --label plot:merging --json
218
+
219
+ # ready work: open + no blockers
220
+ bd ready --json
221
+
222
+ # complex filters
223
+ bd query "status=open AND priority<=1" --json
224
+ bd query "label=plot:rework" --json
225
+
226
+ # text search
227
+ bd search "keyword" --json
228
+
229
+ # dependencies
230
+ bd dep list <id>
231
+ ```
232
+
233
+ ## out-of-scope issue creation
234
+
235
+ if you discover unrelated work, file a separate beads issue instead of expanding scope:
236
+
237
+ ```bash
238
+ bd create "discovered: <title>" -t task -p 2 -d "<description>"
239
+ ```
240
+
241
+ keep the new issue narrow and factual. reference the triggering issue in the description when useful.
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: plot-github-tracker
3
+ description: "github issue state management for plot orchestration. state transitions via labels, workpad comment lifecycle, and issue/PR linkage. use when moving issues between states, creating/updating workpad comments, or linking PRs to issues."
4
+ ---
5
+
6
+ # plot-github-tracker
7
+
8
+ manages issue state and progress tracking for the plot orchestrator via GitHub labels and comments.
9
+
10
+ use `gh` CLI for all GitHub operations. `$GITHUB_REPO` must be set to `owner/repo`.
11
+
12
+ ## state transitions
13
+
14
+ issue state is determined by labels. plot only routes issues that have an explicit `plot:*` state label. unlabeled issues are ignored.
15
+
16
+ transition by removing the previous `plot:*` label and adding the new one:
17
+
18
+ ```bash
19
+ # todo -> in-progress
20
+ gh issue edit <number> --repo "$GITHUB_REPO" --remove-label "plot:todo" --add-label "plot:in-progress"
21
+
22
+ # in-progress -> human-review
23
+ gh issue edit <number> --repo "$GITHUB_REPO" --remove-label "plot:in-progress" --add-label "plot:human-review"
24
+
25
+ # rework -> human-review
26
+ gh issue edit <number> --repo "$GITHUB_REPO" --remove-label "plot:rework" --add-label "plot:human-review"
27
+
28
+ # merging -> done, then close
29
+ gh issue edit <number> --repo "$GITHUB_REPO" --remove-label "plot:merging" --add-label "plot:done"
30
+ gh issue close <number> --repo "$GITHUB_REPO"
31
+ ```
32
+
33
+ if you need to move a terminal issue back into active work, reopen first, then swap labels:
34
+
35
+ ```bash
36
+ gh issue reopen <number> --repo "$GITHUB_REPO"
37
+ gh issue edit <number> --repo "$GITHUB_REPO" --remove-label "plot:done" --add-label "plot:rework"
38
+ ```
39
+
40
+ ## available states
41
+
42
+ | label | meaning | agent action |
43
+ | ----------------- | -------------------------- | ------------------------------------------------------ |
44
+ | plot:todo | queued for work | move to plot:in-progress, start execution |
45
+ | plot:in-progress | implementation underway | implement, verify, open PR, move to plot:human-review |
46
+ | plot:human-review | PR open, waiting on human | do nothing, wait |
47
+ | plot:rework | reviewer requested changes | read feedback, fix, move to plot:human-review |
48
+ | plot:merging | human approved | merge PR, move to plot:done |
49
+ | plot:done | terminal | stop |
50
+
51
+ ## workpad comment
52
+
53
+ a single persistent GitHub issue comment used as a living scratchpad across agent sessions.
54
+
55
+ ### find or create
56
+
57
+ ```bash
58
+ # search for existing workpad
59
+ gh api repos/$GITHUB_REPO/issues/<number>/comments --jq '.[] | select(.body | startswith("## Plot Workpad")) | .id' | head -1
60
+ ```
61
+
62
+ if not found, create it:
63
+
64
+ ```bash
65
+ gh api repos/$GITHUB_REPO/issues/<number>/comments -X POST -f body='## Plot Workpad
66
+
67
+ ### Plan
68
+
69
+ - [ ] 1. <task>
70
+
71
+ ### Acceptance Criteria
72
+
73
+ - [ ] <criterion>
74
+
75
+ ### Validation
76
+
77
+ - [ ] <test command>
78
+
79
+ ### Latest Attempt Summary
80
+
81
+ - changed: <files or none>
82
+ - validated: <commands + outcome>
83
+ - failed: <remaining failure or none>
84
+ - blocked: <blocker or none>
85
+
86
+ ### Notes
87
+
88
+ - <durable context>'
89
+ ```
90
+
91
+ ### update existing
92
+
93
+ replace the full workpad body in place:
94
+
95
+ ```bash
96
+ gh api repos/$GITHUB_REPO/issues/comments/<id> -X PATCH -f body='...full updated workpad body...'
97
+ ```
98
+
99
+ ### rules
100
+
101
+ - exactly one workpad comment per issue, identified by `## Plot Workpad` header
102
+ - reuse existing comment on continuation runs — do not create duplicates
103
+ - update the workpad after every meaningful milestone
104
+ - never leave completed items unchecked
105
+ - include workspace id and short sha at top: `<workspace-id>@<short-sha>`
106
+
107
+ ## workpad template
108
+
109
+ ````markdown
110
+ ## Plot Workpad
111
+
112
+ ```text
113
+ <workspace-id>@<short-sha>
114
+ ```
115
+
116
+ ### Plan
117
+
118
+ - [ ] 1. Parent task
119
+ - [ ] 1.1 Child task
120
+ - [ ] 2. Parent task
121
+
122
+ ### Acceptance Criteria
123
+
124
+ - [ ] Criterion 1
125
+ - [ ] Criterion 2
126
+
127
+ ### Validation
128
+
129
+ - [ ] targeted tests: `<command>`
130
+
131
+ ### Latest Attempt Summary
132
+
133
+ - changed: <files or none>
134
+ - validated: <commands + outcome>
135
+ - failed: <remaining failure or none>
136
+ - blocked: <blocker or none>
137
+
138
+ ### Notes
139
+
140
+ - <short durable context>
141
+
142
+ ````
143
+
144
+ ## issue queries
145
+
146
+ ```bash
147
+ # view issue details
148
+ gh issue view <number> --repo "$GITHUB_REPO" --json title,body,state,labels,comments
149
+
150
+ # list open issues by label
151
+ gh issue list --repo "$GITHUB_REPO" --label "plot:in-progress" --state open
152
+ ```
153
+
154
+ ## PR linkage
155
+
156
+ after creating or updating a PR, ensure the PR body includes `Resolves #<number>`. that is the durable issue/PR link.
157
+
158
+ if you need to post the PR URL back to the issue, add a normal issue comment:
159
+
160
+ ```bash
161
+ gh api repos/$GITHUB_REPO/issues/<number>/comments -X POST -f body='linked PR: <pr-url>'
162
+ ```
package/bin/tui-worker.js CHANGED
@@ -148024,7 +148024,7 @@ var require_lib4 = __commonJS((exports, module2) => {
148024
148024
  // packages/plot/src/tui-worker.ts
148025
148025
  import { Console } from "console";
148026
148026
  import { createWriteStream as createWriteStream5, mkdirSync as mkdirSync9, readFileSync as readFileSync22 } from "fs";
148027
- import { dirname as dirname20 } from "path";
148027
+ import { dirname as dirname21 } from "path";
148028
148028
 
148029
148029
  // node_modules/.bun/effect@3.19.19/node_modules/effect/dist/esm/Function.js
148030
148030
  var isFunction = (input) => typeof input === "function";
@@ -194228,8 +194228,8 @@ class ObservabilityApi extends exports_Effect.Service()("ObservabilityApi", {
194228
194228
  }
194229
194229
 
194230
194230
  // packages/plot/src/runtime-builder.ts
194231
- import { existsSync as existsSync24, readdirSync as readdirSync12 } from "fs";
194232
- import { dirname as dirname19, join as join34, resolve as resolve14 } from "path";
194231
+ import { existsSync as existsSync25, readdirSync as readdirSync12 } from "fs";
194232
+ import { dirname as dirname20, join as join35, resolve as resolve14 } from "path";
194233
194233
  import { fileURLToPath as fileURLToPath6 } from "url";
194234
194234
 
194235
194235
  // node_modules/.bun/@effect+platform-bun@0.87.1+6d9c3e47916ac965/node_modules/@effect/platform-bun/dist/esm/BunContext.js
@@ -290688,9 +290688,189 @@ var PiAgentLive = exports_Layer.succeed(AgentService, AgentService.of({
290688
290688
  run: (config2) => createEventStream(config2)
290689
290689
  }));
290690
290690
  // packages/plot/src/tracker/beads/index.ts
290691
+ import { execFile as execFile2 } from "child_process";
290692
+ import { promisify as promisify2 } from "util";
290693
+
290694
+ // packages/plot/src/tracker/beads/daemon-transport.ts
290691
290695
  import { execFile } from "child_process";
290696
+ import { existsSync as existsSync24 } from "fs";
290697
+ import { homedir as homedir14 } from "os";
290698
+ import { dirname as dirname19, join as join34 } from "path";
290699
+ import { createConnection } from "net";
290692
290700
  import { promisify } from "util";
290693
290701
  var execFileAsync = promisify(execFile);
290702
+ function walkUp(startDir, filename, globalFallback = false) {
290703
+ let dir = startDir;
290704
+ while (true) {
290705
+ const candidate = join34(dir, ".beads", filename);
290706
+ if (existsSync24(candidate))
290707
+ return candidate;
290708
+ const parent = dirname19(dir);
290709
+ if (parent === dir)
290710
+ break;
290711
+ dir = parent;
290712
+ }
290713
+ if (globalFallback) {
290714
+ const globalPath = join34(homedir14(), ".beads", filename);
290715
+ if (existsSync24(globalPath))
290716
+ return globalPath;
290717
+ }
290718
+ return null;
290719
+ }
290720
+ function findSocketPath(workspaceRoot) {
290721
+ return walkUp(workspaceRoot, "bd.sock", true);
290722
+ }
290723
+ function findBeadsDirectory(workspaceRoot) {
290724
+ let dir = workspaceRoot;
290725
+ while (true) {
290726
+ const candidate = join34(dir, ".beads");
290727
+ if (existsSync24(candidate))
290728
+ return candidate;
290729
+ const parent = dirname19(dir);
290730
+ if (parent === dir)
290731
+ break;
290732
+ dir = parent;
290733
+ }
290734
+ return null;
290735
+ }
290736
+
290737
+ class BeadsDaemonTransport {
290738
+ workspaceRoot;
290739
+ requestTimeoutMs;
290740
+ actor;
290741
+ socketPath = null;
290742
+ constructor(workspaceRoot, requestTimeoutMs = 5000, actor = "plot") {
290743
+ this.workspaceRoot = workspaceRoot;
290744
+ this.requestTimeoutMs = requestTimeoutMs;
290745
+ this.actor = actor;
290746
+ }
290747
+ async listAllIssues() {
290748
+ return this.send("list", { status: "all" });
290749
+ }
290750
+ async listOpenIssues() {
290751
+ return this.send("list", { limit: 0 });
290752
+ }
290753
+ async viewIssue(id3) {
290754
+ return this.send("show", { id: id3 });
290755
+ }
290756
+ async send(operation, args2) {
290757
+ const socketPath = await this.ensureRunning();
290758
+ return await new Promise((resolve14, reject) => {
290759
+ const socket = createConnection(socketPath);
290760
+ let responseData = "";
290761
+ let settled = false;
290762
+ const settle = (fn2, value6) => {
290763
+ if (settled)
290764
+ return;
290765
+ settled = true;
290766
+ clearTimeout(timeout5);
290767
+ fn2(value6);
290768
+ };
290769
+ const handleResponse = (raw2) => {
290770
+ const trimmed2 = raw2.trim();
290771
+ if (!trimmed2) {
290772
+ settle(reject, new Error("beads daemon returned empty response"));
290773
+ return;
290774
+ }
290775
+ try {
290776
+ const response = JSON.parse(trimmed2);
290777
+ if (response.success) {
290778
+ settle(resolve14, response.data);
290779
+ return;
290780
+ }
290781
+ settle(reject, new Error(response.error ?? "unknown beads daemon error"));
290782
+ } catch {
290783
+ settle(reject, new Error(`failed to parse beads daemon response: ${trimmed2}`));
290784
+ }
290785
+ };
290786
+ const timeout5 = setTimeout(() => {
290787
+ socket.destroy();
290788
+ settle(reject, new Error(`beads daemon request timed out after ${this.requestTimeoutMs}ms`));
290789
+ }, this.requestTimeoutMs);
290790
+ socket.on("connect", () => {
290791
+ socket.write(JSON.stringify({
290792
+ operation,
290793
+ args: args2,
290794
+ cwd: this.workspaceRoot,
290795
+ actor: this.actor
290796
+ }) + `
290797
+ `);
290798
+ });
290799
+ socket.on("data", (chunk5) => {
290800
+ responseData += chunk5.toString();
290801
+ if (responseData.includes(`
290802
+ `)) {
290803
+ socket.destroy();
290804
+ handleResponse(responseData);
290805
+ }
290806
+ });
290807
+ socket.on("end", () => {
290808
+ if (!settled && responseData.length > 0) {
290809
+ handleResponse(responseData);
290810
+ return;
290811
+ }
290812
+ if (!settled) {
290813
+ settle(reject, new Error("beads daemon closed connection without a response"));
290814
+ }
290815
+ });
290816
+ socket.on("error", (error4) => {
290817
+ this.socketPath = null;
290818
+ settle(reject, new Error(`beads daemon connection error: ${error4.message}`));
290819
+ });
290820
+ });
290821
+ }
290822
+ async ensureRunning() {
290823
+ if (this.socketPath)
290824
+ return this.socketPath;
290825
+ const found = findSocketPath(this.workspaceRoot);
290826
+ if (found) {
290827
+ this.socketPath = found;
290828
+ return found;
290829
+ }
290830
+ await this.startDaemon();
290831
+ if (!this.socketPath)
290832
+ throw new Error("beads daemon socket unavailable");
290833
+ return this.socketPath;
290834
+ }
290835
+ async startDaemon() {
290836
+ const beadsDirectory = findBeadsDirectory(this.workspaceRoot);
290837
+ if (!beadsDirectory) {
290838
+ throw new Error("no .beads directory found");
290839
+ }
290840
+ await execFileAsync("bd", ["daemon", "start"], {
290841
+ cwd: this.workspaceRoot,
290842
+ maxBuffer: 50 * 1024 * 1024
290843
+ });
290844
+ const expectedSocketPath = join34(beadsDirectory, "bd.sock");
290845
+ await new Promise((resolve14, reject) => {
290846
+ const startedAt = Date.now();
290847
+ const timer = setInterval(() => {
290848
+ if (existsSync24(expectedSocketPath)) {
290849
+ clearInterval(timer);
290850
+ this.socketPath = expectedSocketPath;
290851
+ resolve14();
290852
+ return;
290853
+ }
290854
+ if (Date.now() - startedAt >= this.requestTimeoutMs) {
290855
+ clearInterval(timer);
290856
+ reject(new Error(`beads daemon socket did not appear within ${this.requestTimeoutMs}ms`));
290857
+ }
290858
+ }, 100);
290859
+ });
290860
+ }
290861
+ }
290862
+ async function tryCreateBeadsDaemonTransport(workspaceRoot) {
290863
+ const transport = new BeadsDaemonTransport(workspaceRoot);
290864
+ try {
290865
+ await transport.listAllIssues();
290866
+ return transport;
290867
+ } catch {
290868
+ return null;
290869
+ }
290870
+ }
290871
+
290872
+ // packages/plot/src/tracker/beads/index.ts
290873
+ var execFileAsync2 = promisify2(execFile2);
290694
290874
  var normalizeState2 = (s) => s.trim().toLowerCase();
290695
290875
  function mapBdFailure(error4, resourceId) {
290696
290876
  const message = error4 instanceof Error ? error4.message : String(error4);
@@ -290709,36 +290889,65 @@ function mapBdFailure(error4, resourceId) {
290709
290889
  }
290710
290890
  return new Error(`bd command failed: ${details}`);
290711
290891
  }
290712
- function createBeadsOps(config2) {
290892
+ async function createBeadsOps(config2) {
290893
+ const workspaceRoot = config2.beadsDir ?? process.cwd();
290894
+ const daemon = await tryCreateBeadsDaemonTransport(workspaceRoot);
290713
290895
  const runBd = async (args2, options5) => {
290714
290896
  try {
290715
- return await execFileAsync("bd", args2, {
290897
+ return await execFileAsync2("bd", args2, {
290716
290898
  maxBuffer: 50 * 1024 * 1024,
290717
- ...config2.beadsDir ? { cwd: config2.beadsDir } : {}
290899
+ cwd: workspaceRoot
290718
290900
  });
290719
290901
  } catch (error4) {
290720
290902
  throw mapBdFailure(error4, options5?.resourceId);
290721
290903
  }
290722
290904
  };
290905
+ const parseIssueList = (value6) => value6;
290906
+ const parseDetailedIssue = (value6, id3) => {
290907
+ const issue2 = Array.isArray(value6) ? value6[0] : value6;
290908
+ if (!issue2)
290909
+ throw new Error(`bd show returned empty result for ${id3}`);
290910
+ return issue2;
290911
+ };
290912
+ const runDaemon = async (operation) => {
290913
+ if (!daemon)
290914
+ return null;
290915
+ try {
290916
+ return await operation(daemon);
290917
+ } catch {
290918
+ return null;
290919
+ }
290920
+ };
290723
290921
  const listIssues = async (status3) => {
290922
+ const daemonResult = await runDaemon((transport) => transport.listAllIssues());
290923
+ if (daemonResult) {
290924
+ if (status3 === "all")
290925
+ return daemonResult;
290926
+ return daemonResult.filter((issue2) => normalizeState2(issue2.status) === normalizeState2(status3));
290927
+ }
290724
290928
  const result = await runBd(["list", "--json", "--status", status3]);
290725
- return JSON.parse(result.stdout);
290929
+ return parseIssueList(JSON.parse(result.stdout));
290726
290930
  };
290727
290931
  const listAllIssues = async () => {
290932
+ const daemonResult = await runDaemon((transport) => transport.listAllIssues());
290933
+ if (daemonResult)
290934
+ return daemonResult;
290728
290935
  const result = await runBd(["list", "--json", "--status", "all"]);
290729
- return JSON.parse(result.stdout);
290936
+ return parseIssueList(JSON.parse(result.stdout));
290730
290937
  };
290731
290938
  const listOpenIssues = async () => {
290939
+ const daemonResult = await runDaemon((transport) => transport.listOpenIssues());
290940
+ if (daemonResult)
290941
+ return daemonResult;
290732
290942
  const result = await runBd(["list", "--json", "--limit", "0"]);
290733
- return JSON.parse(result.stdout);
290943
+ return parseIssueList(JSON.parse(result.stdout));
290734
290944
  };
290735
290945
  const viewIssue = async (id3) => {
290946
+ const daemonResult = await runDaemon((transport) => transport.viewIssue(id3));
290947
+ if (daemonResult)
290948
+ return parseDetailedIssue(daemonResult, id3);
290736
290949
  const result = await runBd(["show", id3, "--json"], { resourceId: id3 });
290737
- const parsed = JSON.parse(result.stdout);
290738
- const issue2 = Array.isArray(parsed) ? parsed[0] : parsed;
290739
- if (!issue2)
290740
- throw new Error(`bd show returned empty result for ${id3}`);
290741
- return issue2;
290950
+ return parseDetailedIssue(JSON.parse(result.stdout), id3);
290742
290951
  };
290743
290952
  const mapState = (bd) => {
290744
290953
  const labelNames = (bd.labels ?? []).map((l) => normalizeState2(l));
@@ -290759,7 +290968,64 @@ function createBeadsOps(config2) {
290759
290968
  createdAt: bd.created_at || null,
290760
290969
  updatedAt: bd.updated_at || null
290761
290970
  });
290762
- const fetchRunContext = async (issueId, _state) => {
290971
+ const runGh = async (args2) => {
290972
+ return await execFileAsync2("gh", args2, {
290973
+ maxBuffer: 50 * 1024 * 1024,
290974
+ cwd: workspaceRoot
290975
+ });
290976
+ };
290977
+ const fetchPrReviews = async (issueId) => {
290978
+ const repoArgs = config2.githubRepo ? ["--repo", config2.githubRepo] : [];
290979
+ try {
290980
+ const prSearchResult = await runGh([
290981
+ "pr",
290982
+ "list",
290983
+ ...repoArgs,
290984
+ "--state",
290985
+ "open",
290986
+ "--json",
290987
+ "number,headRefName,body",
290988
+ "--limit",
290989
+ "50"
290990
+ ]);
290991
+ const prs = JSON.parse(prSearchResult.stdout);
290992
+ const linkedPr = prs.find((pr) => pr.body?.includes(issueId));
290993
+ if (!linkedPr)
290994
+ return null;
290995
+ const reviewResult = await runGh([
290996
+ "pr",
290997
+ "view",
290998
+ String(linkedPr.number),
290999
+ ...repoArgs,
291000
+ "--json",
291001
+ "reviews,comments"
291002
+ ]);
291003
+ const prData = JSON.parse(reviewResult.stdout);
291004
+ const parts2 = [];
291005
+ if (prData.reviews?.length) {
291006
+ for (const r of prData.reviews) {
291007
+ if (r.body)
291008
+ parts2.push(`**${r.author.login}** (${r.state}):
291009
+ ${r.body}`);
291010
+ }
291011
+ }
291012
+ if (prData.comments?.length) {
291013
+ for (const c of prData.comments) {
291014
+ if (c.body)
291015
+ parts2.push(`**${c.author.login}**:
291016
+ ${c.body}`);
291017
+ }
291018
+ }
291019
+ return parts2.length > 0 ? parts2.join(`
291020
+
291021
+ ---
291022
+
291023
+ `) : null;
291024
+ } catch {
291025
+ return null;
291026
+ }
291027
+ };
291028
+ const fetchRunContext = async (issueId, state) => {
290763
291029
  let comments = [];
290764
291030
  try {
290765
291031
  const issue2 = await viewIssue(issueId);
@@ -290771,7 +291037,12 @@ function createBeadsOps(config2) {
290771
291037
  const workpadComment = comments.find((c) => c.text.startsWith("## Plot Workpad"));
290772
291038
  if (workpadComment)
290773
291039
  workpad = workpadComment.text;
290774
- return buildRunContext({ workpad, reviewFeedback: null });
291040
+ let reviews = null;
291041
+ const normalizedDispatch = config2.dispatchStates.map(normalizeState2);
291042
+ if (normalizedDispatch.includes(normalizeState2(state))) {
291043
+ reviews = await fetchPrReviews(issueId);
291044
+ }
291045
+ return buildRunContext({ workpad, reviewFeedback: reviews });
290775
291046
  };
290776
291047
  return {
290777
291048
  runBd,
@@ -290790,6 +291061,7 @@ var plugin2 = {
290790
291061
  return {
290791
291062
  kind: String(raw2.kind),
290792
291063
  beadsDir: typeof raw2["beadsDir"] === "string" ? raw2["beadsDir"] : undefined,
291064
+ githubRepo: typeof raw2["githubRepo"] === "string" ? raw2["githubRepo"] : undefined,
290793
291065
  dispatchStates: Array.isArray(raw2["dispatchStates"]) ? raw2["dispatchStates"] : undefined,
290794
291066
  parkedStates: Array.isArray(raw2["parkedStates"]) ? raw2["parkedStates"] : undefined,
290795
291067
  terminalStates: Array.isArray(raw2["terminalStates"]) ? raw2["terminalStates"] : undefined
@@ -290801,13 +291073,15 @@ var plugin2 = {
290801
291073
  ...config2.parkedStates ?? [],
290802
291074
  ...config2.terminalStates ?? []
290803
291075
  ].filter((s, i, arr) => arr.indexOf(s) === i);
290804
- const ops = createBeadsOps({
291076
+ const ops = await createBeadsOps({
290805
291077
  beadsDir: config2.beadsDir,
291078
+ githubRepo: config2.githubRepo,
291079
+ dispatchStates: config2.dispatchStates ?? [],
290806
291080
  allStates
290807
291081
  });
290808
291082
  return {
290809
291083
  async fetchCandidateIssues(dispatchStates) {
290810
- const bdIssues = await ops.listOpenIssues();
291084
+ const bdIssues = await ops.listAllIssues();
290811
291085
  const issues = bdIssues.map(ops.mapIssue);
290812
291086
  const normalized = new Set(dispatchStates.map(normalizeState2));
290813
291087
  return issues.filter((i) => normalized.has(normalizeState2(i.state)));
@@ -290819,19 +291093,12 @@ var plugin2 = {
290819
291093
  return issues.filter((i) => normalized.has(normalizeState2(i.state)));
290820
291094
  },
290821
291095
  async fetchIssueStatesByIds(ids3) {
290822
- const results = await Promise.all(ids3.map(async (id3) => {
290823
- try {
290824
- const bd = await ops.viewIssue(id3);
290825
- return [
290826
- { id: bd.id, state: ops.mapState(bd) }
290827
- ];
290828
- } catch (e) {
290829
- if (e instanceof PluginNotFoundError)
290830
- return [];
290831
- throw e;
290832
- }
291096
+ const wantedIds = new Set(ids3);
291097
+ const allIssues = await ops.listAllIssues();
291098
+ return allIssues.filter((issue2) => wantedIds.has(issue2.id)).map((issue2) => ({
291099
+ id: issue2.id,
291100
+ state: ops.mapState(issue2)
290833
291101
  }));
290834
- return results.flat();
290835
291102
  },
290836
291103
  fetchRunContext: (issueId, state) => ops.fetchRunContext(issueId, state)
290837
291104
  };
@@ -290839,9 +291106,9 @@ var plugin2 = {
290839
291106
  };
290840
291107
  var beads_default = plugin2;
290841
291108
  // packages/plot/src/tracker/github/index.ts
290842
- import { execFile as execFile2 } from "child_process";
290843
- import { promisify as promisify2 } from "util";
290844
- var execFileAsync2 = promisify2(execFile2);
291109
+ import { execFile as execFile3 } from "child_process";
291110
+ import { promisify as promisify3 } from "util";
291111
+ var execFileAsync3 = promisify3(execFile3);
290845
291112
  var normalizeState3 = (s) => s.trim().toLowerCase();
290846
291113
  function mapGhFailure(error4, resourceId) {
290847
291114
  const message = error4 instanceof Error ? error4.message : String(error4);
@@ -290870,7 +291137,7 @@ function createGithubOps(config2) {
290870
291137
  const ghFields = "number,title,body,state,labels,url,createdAt,updatedAt";
290871
291138
  const runGh = async (args2, options5) => {
290872
291139
  try {
290873
- return await execFileAsync2("gh", args2, {
291140
+ return await execFileAsync3("gh", args2, {
290874
291141
  maxBuffer: 50 * 1024 * 1024
290875
291142
  });
290876
291143
  } catch (error4) {
@@ -291156,11 +291423,11 @@ function adaptTrackerClient(plain) {
291156
291423
  var builtinTrackers = {
291157
291424
  beads: {
291158
291425
  definition: beads_default,
291159
- moduleDir: join34(dirname19(fileURLToPath6(import.meta.url)), "tracker", "beads")
291426
+ moduleDir: join35(dirname20(fileURLToPath6(import.meta.url)), "tracker", "beads")
291160
291427
  },
291161
291428
  github: {
291162
291429
  definition: github_default,
291163
- moduleDir: join34(dirname19(fileURLToPath6(import.meta.url)), "tracker", "github")
291430
+ moduleDir: join35(dirname20(fileURLToPath6(import.meta.url)), "tracker", "github")
291164
291431
  }
291165
291432
  };
291166
291433
  function buildPluginConfig(resolved) {
@@ -291178,11 +291445,11 @@ function syncGithubRepoEnv(resolved) {
291178
291445
  delete process.env["GITHUB_REPO"];
291179
291446
  }
291180
291447
  function discoverSkillPaths(moduleDir) {
291181
- const skillsDir = join34(moduleDir, "skills");
291182
- if (!existsSync24(skillsDir))
291448
+ const skillsDir = join35(moduleDir, "skills");
291449
+ if (!existsSync25(skillsDir))
291183
291450
  return [];
291184
291451
  try {
291185
- return readdirSync12(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join34(skillsDir, entry.name)).filter((dir) => existsSync24(join34(dir, "SKILL.md")));
291452
+ return readdirSync12(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join35(skillsDir, entry.name)).filter((dir) => existsSync25(join35(dir, "SKILL.md")));
291186
291453
  } catch {
291187
291454
  return [];
291188
291455
  }
@@ -291225,7 +291492,7 @@ function resolvePlugin(resolved) {
291225
291492
  return yield* exports_Effect.die(new Error(`Tracker plugin "${kind}" does not export a valid default tracker plugin definition`));
291226
291493
  }
291227
291494
  const config2 = yield* resolveDefinitionConfig(definition, rawConfig);
291228
- const moduleDir = kind.startsWith("./") || kind.startsWith("../") || kind.startsWith("/") ? dirname19(resolve14(kind)) : undefined;
291495
+ const moduleDir = kind.startsWith("./") || kind.startsWith("../") || kind.startsWith("/") ? dirname20(resolve14(kind)) : undefined;
291229
291496
  return yield* makeResolvedPlugin(definition, config2, moduleDir);
291230
291497
  });
291231
291498
  }
@@ -291379,7 +291646,7 @@ function postResponse(message) {
291379
291646
  function redirectProcessOutput(path14) {
291380
291647
  if (!path14)
291381
291648
  return;
291382
- mkdirSync9(dirname20(path14), { recursive: true });
291649
+ mkdirSync9(dirname21(path14), { recursive: true });
291383
291650
  const stream8 = createWriteStream5(path14, { flags: "a" });
291384
291651
  const write4 = (chunk5) => {
291385
291652
  stream8.write(typeof chunk5 === "string" ? chunk5 : Buffer.from(chunk5));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plot-ai/linux-x64-musl",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "orchestrate coding agents against an issue tracker",