opencode-pilot 0.20.1 → 0.20.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-pilot",
3
- "version": "0.20.1",
3
+ "version": "0.20.3",
4
4
  "type": "module",
5
5
  "main": "plugin/index.js",
6
6
  "description": "Automation daemon for OpenCode - polls for work and spawns sessions",
@@ -50,6 +50,7 @@ export function buildActionConfigFromSource(source, repoConfig) {
50
50
  ...(source.agent && { agent: source.agent }),
51
51
  ...(source.model && { model: source.model }),
52
52
  ...(source.working_dir && { working_dir: source.working_dir }),
53
+ ...(source.worktree_name && { worktree_name: source.worktree_name }),
53
54
  };
54
55
  }
55
56
 
@@ -10,6 +10,7 @@ _provider:
10
10
  html_url: url
11
11
  repository_full_name: repository.nameWithOwner
12
12
  updated_at: updatedAt
13
+ comments: commentsCount
13
14
  # Reprocess items when state changes (e.g., reopened issues)
14
15
  # Note: updated_at is NOT included because our own changes would trigger reprocessing
15
16
  reprocess_on:
@@ -23,6 +24,7 @@ my-issues:
23
24
  item:
24
25
  id: "{url}"
25
26
  repo: "{repository.nameWithOwner}"
27
+ worktree_name: "issue-{number}"
26
28
  session:
27
29
  name: "{title}"
28
30
 
@@ -34,6 +36,7 @@ review-requests:
34
36
  id: "{url}"
35
37
  repo: "{repository.nameWithOwner}"
36
38
  prompt: review
39
+ worktree_name: "pr-{number}"
37
40
  session:
38
41
  name: "Review: {title}"
39
42
 
@@ -46,6 +49,7 @@ my-prs-attention:
46
49
  id: "{url}"
47
50
  repo: "{repository.nameWithOwner}"
48
51
  prompt: review-feedback
52
+ worktree_name: "pr-{number}"
49
53
  session:
50
54
  # Dynamic name showing which conditions triggered (set by enrichment)
51
55
  name: "{_attention_label}: {title}"
@@ -55,7 +59,7 @@ my-prs-attention:
55
59
  readiness:
56
60
  # Require at least one attention condition (conflicts or human feedback)
57
61
  require_attention: true
58
- # Reprocess when PR is updated
62
+ # Only reprocess when state changes (e.g., reopened)
63
+ # Note: updatedAt is NOT included - CI status changes would trigger reprocessing
59
64
  reprocess_on:
60
65
  - state
61
- - updatedAt
@@ -21,5 +21,6 @@ my-issues:
21
21
  # teamId and assigneeId are required - user must provide
22
22
  item:
23
23
  id: "linear:{id}"
24
+ worktree_name: "{number}"
24
25
  session:
25
26
  name: "{title}"
@@ -154,6 +154,56 @@ sources:
154
154
  assert.strictEqual(config.agent, 'code');
155
155
  });
156
156
 
157
+ test('includes worktree_name from source config', async () => {
158
+ const { buildActionConfigFromSource } = await import('../../service/poll-service.js');
159
+
160
+ const source = {
161
+ name: 'test-source',
162
+ worktree_name: 'pr-{number}'
163
+ };
164
+ const repoConfig = {
165
+ path: '~/code/default'
166
+ };
167
+
168
+ const config = buildActionConfigFromSource(source, repoConfig);
169
+
170
+ assert.strictEqual(config.worktree_name, 'pr-{number}');
171
+ });
172
+
173
+ test('worktree_name from source overrides repoConfig', async () => {
174
+ const { buildActionConfigFromSource } = await import('../../service/poll-service.js');
175
+
176
+ const source = {
177
+ name: 'test-source',
178
+ worktree_name: 'pr-{number}'
179
+ };
180
+ const repoConfig = {
181
+ path: '~/code/default',
182
+ worktree_name: 'issue-{number}' // Should be overridden
183
+ };
184
+
185
+ const config = buildActionConfigFromSource(source, repoConfig);
186
+
187
+ assert.strictEqual(config.worktree_name, 'pr-{number}');
188
+ });
189
+
190
+ test('falls back to repoConfig worktree_name when source has none', async () => {
191
+ const { buildActionConfigFromSource } = await import('../../service/poll-service.js');
192
+
193
+ const source = {
194
+ name: 'test-source'
195
+ // No worktree_name
196
+ };
197
+ const repoConfig = {
198
+ path: '~/code/default',
199
+ worktree_name: 'issue-{number}'
200
+ };
201
+
202
+ const config = buildActionConfigFromSource(source, repoConfig);
203
+
204
+ assert.strictEqual(config.worktree_name, 'issue-{number}');
205
+ });
206
+
157
207
  });
158
208
 
159
209
  describe('per-item repo resolution', () => {
@@ -786,6 +786,31 @@ describe('poller.js', () => {
786
786
 
787
787
  assert.strictEqual(mapped.number, undefined);
788
788
  });
789
+
790
+ test('maps commentsCount to comments for GitHub PR enrichment', async () => {
791
+ const { applyMappings } = await import('../../service/poller.js');
792
+
793
+ // gh search prs returns commentsCount, but enrichItemsWithComments checks for 'comments'
794
+ const item = {
795
+ number: 123,
796
+ title: 'Fix mobile overflow',
797
+ commentsCount: 4,
798
+ repository: { nameWithOwner: 'anomalyco/opencode' }
799
+ };
800
+ const mappings = {
801
+ comments: 'commentsCount',
802
+ repository_full_name: 'repository.nameWithOwner'
803
+ };
804
+
805
+ const mapped = applyMappings(item, mappings);
806
+
807
+ // comments field should be set from commentsCount
808
+ assert.strictEqual(mapped.comments, 4);
809
+ // Original commentsCount preserved
810
+ assert.strictEqual(mapped.commentsCount, 4);
811
+ // Other mappings work too
812
+ assert.strictEqual(mapped.repository_full_name, 'anomalyco/opencode');
813
+ });
789
814
  });
790
815
 
791
816
  describe('fetchGitHubComments', () => {
@@ -874,6 +874,45 @@ sources:
874
874
  assert.strictEqual(sources[0].session.name, '{title}', 'linear preset should use title');
875
875
  });
876
876
 
877
+ test('github presets include worktree_name for sandbox reuse', async () => {
878
+ writeFileSync(configPath, `
879
+ sources:
880
+ - preset: github/my-issues
881
+ - preset: github/review-requests
882
+ - preset: github/my-prs-attention
883
+ `);
884
+
885
+ const { loadRepoConfig, getSources } = await import('../../service/repo-config.js');
886
+ loadRepoConfig(configPath);
887
+ const sources = getSources();
888
+
889
+ // my-issues: worktree_name: "issue-{number}"
890
+ assert.strictEqual(sources[0].worktree_name, 'issue-{number}', 'my-issues should use issue-{number}');
891
+
892
+ // review-requests: worktree_name: "pr-{number}"
893
+ assert.strictEqual(sources[1].worktree_name, 'pr-{number}', 'review-requests should use pr-{number}');
894
+
895
+ // my-prs-attention: worktree_name: "pr-{number}"
896
+ assert.strictEqual(sources[2].worktree_name, 'pr-{number}', 'my-prs-attention should use pr-{number}');
897
+ });
898
+
899
+ test('linear preset includes worktree_name for sandbox reuse', async () => {
900
+ writeFileSync(configPath, `
901
+ sources:
902
+ - preset: linear/my-issues
903
+ args:
904
+ teamId: "team-uuid"
905
+ assigneeId: "user-uuid"
906
+ `);
907
+
908
+ const { loadRepoConfig, getSources } = await import('../../service/repo-config.js');
909
+ loadRepoConfig(configPath);
910
+ const sources = getSources();
911
+
912
+ // linear uses the issue identifier (e.g., "ABC-123")
913
+ assert.strictEqual(sources[0].worktree_name, '{number}', 'linear preset should use {number} (identifier)');
914
+ });
915
+
877
916
  });
878
917
 
879
918
  describe('shorthand syntax', () => {