@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
package/bin/package.json
CHANGED
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
|
|
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
|
|
194232
|
-
import { dirname as
|
|
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
|
|
290897
|
+
return await execFileAsync2("bd", args2, {
|
|
290716
290898
|
maxBuffer: 50 * 1024 * 1024,
|
|
290717
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
290823
|
-
|
|
290824
|
-
|
|
290825
|
-
|
|
290826
|
-
|
|
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
|
|
290843
|
-
import { promisify as
|
|
290844
|
-
var
|
|
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
|
|
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:
|
|
291426
|
+
moduleDir: join35(dirname20(fileURLToPath6(import.meta.url)), "tracker", "beads")
|
|
291160
291427
|
},
|
|
291161
291428
|
github: {
|
|
291162
291429
|
definition: github_default,
|
|
291163
|
-
moduleDir:
|
|
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 =
|
|
291182
|
-
if (!
|
|
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) =>
|
|
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("/") ?
|
|
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(
|
|
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));
|