opencode-pilot 0.18.1 → 0.18.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/AGENTS.md +6 -5
- package/README.md +2 -3
- package/examples/config.yaml +2 -5
- package/examples/templates/review-feedback.md +2 -2
- package/package.json +1 -1
- package/service/poller.js +43 -55
- package/service/presets/github.yaml +0 -17
- package/test/unit/repo-config.test.js +1 -24
package/AGENTS.md
CHANGED
|
@@ -61,13 +61,14 @@ npx opencode-pilot status
|
|
|
61
61
|
|
|
62
62
|
## Configuration
|
|
63
63
|
|
|
64
|
-
Config file: `~/.config/opencode
|
|
64
|
+
Config file: `~/.config/opencode/pilot/config.yaml`
|
|
65
65
|
|
|
66
66
|
Configuration has these sections:
|
|
67
|
-
- `
|
|
68
|
-
- `
|
|
69
|
-
- `
|
|
67
|
+
- `defaults` - default values applied to all sources
|
|
68
|
+
- `repos_dir` - directory to auto-discover repos via git remotes
|
|
69
|
+
- `sources` - polling sources with presets, shorthand, or full MCP tool config
|
|
70
|
+
- `tools` - field mappings to normalize different MCP APIs
|
|
70
71
|
|
|
71
|
-
Template files: `~/.config/opencode
|
|
72
|
+
Template files: `~/.config/opencode/pilot/templates/*.md`
|
|
72
73
|
|
|
73
74
|
See [examples/config.yaml](examples/config.yaml) for a complete example.
|
package/README.md
CHANGED
|
@@ -65,11 +65,10 @@ Three ways to configure sources, from simplest to most flexible:
|
|
|
65
65
|
|
|
66
66
|
- `github/my-issues` - Issues assigned to me
|
|
67
67
|
- `github/review-requests` - PRs needing my review
|
|
68
|
-
- `github/my-prs-
|
|
69
|
-
- `github/my-prs-attention` - My PRs needing attention (conflicts OR feedback, with dynamic labeling)
|
|
68
|
+
- `github/my-prs-attention` - My PRs needing attention (conflicts OR human feedback)
|
|
70
69
|
- `linear/my-issues` - Linear tickets (requires `teamId`, `assigneeId`)
|
|
71
70
|
|
|
72
|
-
|
|
71
|
+
Session names for `my-prs-attention` indicate the condition: "Conflicts: {title}", "Feedback: {title}", or "Conflicts+Feedback: {title}".
|
|
73
72
|
|
|
74
73
|
### Prompt Templates
|
|
75
74
|
|
package/examples/config.yaml
CHANGED
|
@@ -27,16 +27,13 @@ sources:
|
|
|
27
27
|
|
|
28
28
|
- preset: github/review-requests
|
|
29
29
|
|
|
30
|
-
# PRs needing attention (conflicts OR feedback)
|
|
30
|
+
# PRs needing attention (conflicts OR human feedback)
|
|
31
31
|
# Session names dynamically indicate the condition: "Conflicts: ...", "Feedback: ...", or "Conflicts+Feedback: ..."
|
|
32
32
|
- preset: github/my-prs-attention
|
|
33
33
|
repos:
|
|
34
34
|
- myorg/backend
|
|
35
35
|
- myorg/frontend
|
|
36
36
|
|
|
37
|
-
# Alternative: Simple feedback-only trigger (use my-prs-attention for conflicts too)
|
|
38
|
-
# - preset: github/my-prs-feedback
|
|
39
|
-
|
|
40
37
|
# Linear (requires teamId and assigneeId)
|
|
41
38
|
- preset: linear/my-issues
|
|
42
39
|
args:
|
|
@@ -92,4 +89,4 @@ sources:
|
|
|
92
89
|
# ttl_days: 30
|
|
93
90
|
|
|
94
91
|
# Available presets: github/my-issues, github/review-requests,
|
|
95
|
-
# github/my-prs-
|
|
92
|
+
# github/my-prs-attention, linear/my-issues
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Address the review feedback on this PR:
|
|
1
|
+
Address the review feedback or merge conflicts on this PR:
|
|
2
2
|
|
|
3
3
|
{title}
|
|
4
4
|
|
|
5
5
|
{html_url}
|
|
6
6
|
|
|
7
|
-
Focus
|
|
7
|
+
Focus on unresolved review comments and merge conflicts. Make the requested changes and respond to reviewers. Skip any conversations that are already resolved.
|
package/package.json
CHANGED
package/service/poller.js
CHANGED
|
@@ -379,9 +379,7 @@ export async function pollGenericSource(source, options = {}) {
|
|
|
379
379
|
/**
|
|
380
380
|
* Fetch issue comments using gh CLI
|
|
381
381
|
*
|
|
382
|
-
*
|
|
383
|
-
* so we use gh CLI directly. This fetches the conversation thread
|
|
384
|
-
* where bots like Linear post their comments.
|
|
382
|
+
* Fetches the conversation thread where bots like Linear post their comments.
|
|
385
383
|
*
|
|
386
384
|
* @param {string} owner - Repository owner
|
|
387
385
|
* @param {string} repo - Repository name
|
|
@@ -409,19 +407,50 @@ async function fetchIssueCommentsViaCli(owner, repo, number, timeout) {
|
|
|
409
407
|
}
|
|
410
408
|
}
|
|
411
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Fetch PR review comments using gh CLI
|
|
412
|
+
*
|
|
413
|
+
* Fetches inline code review comments on a PR.
|
|
414
|
+
*
|
|
415
|
+
* @param {string} owner - Repository owner
|
|
416
|
+
* @param {string} repo - Repository name
|
|
417
|
+
* @param {number} number - PR number
|
|
418
|
+
* @param {number} timeout - Timeout in ms
|
|
419
|
+
* @returns {Promise<Array>} Array of comment objects
|
|
420
|
+
*/
|
|
421
|
+
async function fetchPrReviewCommentsViaCli(owner, repo, number, timeout) {
|
|
422
|
+
const { exec } = await import('child_process');
|
|
423
|
+
const { promisify } = await import('util');
|
|
424
|
+
const execAsync = promisify(exec);
|
|
425
|
+
|
|
426
|
+
try {
|
|
427
|
+
const { stdout } = await Promise.race([
|
|
428
|
+
execAsync(`gh api repos/${owner}/${repo}/pulls/${number}/comments`),
|
|
429
|
+
createTimeout(timeout, "gh api call for PR comments"),
|
|
430
|
+
]);
|
|
431
|
+
|
|
432
|
+
const comments = JSON.parse(stdout);
|
|
433
|
+
return Array.isArray(comments) ? comments : [];
|
|
434
|
+
} catch (err) {
|
|
435
|
+
console.error(`[poller] Error fetching PR review comments via gh: ${err.message}`);
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
412
440
|
/**
|
|
413
441
|
* Fetch comments for a GitHub issue/PR and enrich the item
|
|
414
442
|
*
|
|
415
|
-
* Fetches BOTH types of comments:
|
|
416
|
-
* 1. PR review comments (inline code comments) via
|
|
417
|
-
* 2. Issue comments (conversation thread) via gh
|
|
443
|
+
* Fetches BOTH types of comments using gh CLI:
|
|
444
|
+
* 1. PR review comments (inline code comments) via gh api pulls/{number}/comments
|
|
445
|
+
* 2. Issue comments (conversation thread) via gh api issues/{number}/comments
|
|
418
446
|
*
|
|
419
|
-
* This is necessary because
|
|
447
|
+
* This is necessary because:
|
|
448
|
+
* - Bots like Linear post to issue comments, not PR review comments
|
|
449
|
+
* - Human reviewers post inline feedback as PR review comments
|
|
420
450
|
*
|
|
421
|
-
* @param {object} item - Item with
|
|
451
|
+
* @param {object} item - Item with repository_full_name and number fields
|
|
422
452
|
* @param {object} [options] - Options
|
|
423
453
|
* @param {number} [options.timeout] - Timeout in ms (default: 30000)
|
|
424
|
-
* @param {string} [options.opencodeConfigPath] - Path to opencode.json for MCP config
|
|
425
454
|
* @returns {Promise<Array>} Array of comment objects (merged from both endpoints)
|
|
426
455
|
*/
|
|
427
456
|
export async function fetchGitHubComments(item, options = {}) {
|
|
@@ -443,61 +472,20 @@ export async function fetchGitHubComments(item, options = {}) {
|
|
|
443
472
|
return [];
|
|
444
473
|
}
|
|
445
474
|
|
|
446
|
-
let mcpConfig;
|
|
447
475
|
try {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
const client = new Client({ name: "opencode-pilot", version: "1.0.0" });
|
|
455
|
-
|
|
456
|
-
try {
|
|
457
|
-
const transport = await createTransport(mcpConfig);
|
|
458
|
-
|
|
459
|
-
await Promise.race([
|
|
460
|
-
client.connect(transport),
|
|
461
|
-
createTimeout(timeout, "MCP connection for comments"),
|
|
462
|
-
]);
|
|
463
|
-
|
|
464
|
-
// Fetch both PR review comments (via MCP) AND issue comments (via gh CLI) in parallel
|
|
465
|
-
const [prCommentsResult, issueComments] = await Promise.all([
|
|
466
|
-
// PR review comments via MCP (may not be available on all MCP servers)
|
|
467
|
-
client.callTool({
|
|
468
|
-
name: "github_get_pull_request_comments",
|
|
469
|
-
arguments: { owner, repo, pull_number: number }
|
|
470
|
-
}).catch(() => null), // Gracefully handle if tool doesn't exist
|
|
471
|
-
// Issue comments via gh CLI (conversation thread where Linear bot posts)
|
|
476
|
+
// Fetch both PR review comments AND issue comments in parallel via gh CLI
|
|
477
|
+
const [prComments, issueComments] = await Promise.all([
|
|
478
|
+
// PR review comments (inline code comments from reviewers)
|
|
479
|
+
fetchPrReviewCommentsViaCli(owner, repo, number, timeout),
|
|
480
|
+
// Issue comments (conversation thread where Linear bot posts)
|
|
472
481
|
fetchIssueCommentsViaCli(owner, repo, number, timeout),
|
|
473
482
|
]);
|
|
474
483
|
|
|
475
|
-
// Parse PR review comments
|
|
476
|
-
let prComments = [];
|
|
477
|
-
const prText = prCommentsResult?.content?.[0]?.text;
|
|
478
|
-
if (prText) {
|
|
479
|
-
try {
|
|
480
|
-
const parsed = JSON.parse(prText);
|
|
481
|
-
prComments = Array.isArray(parsed) ? parsed : [];
|
|
482
|
-
} catch {
|
|
483
|
-
// Ignore parse errors
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
484
|
// Return merged comments from both sources
|
|
488
485
|
return [...prComments, ...issueComments];
|
|
489
486
|
} catch (err) {
|
|
490
487
|
console.error(`[poller] Error fetching comments: ${err.message}`);
|
|
491
488
|
return [];
|
|
492
|
-
} finally {
|
|
493
|
-
try {
|
|
494
|
-
await Promise.race([
|
|
495
|
-
client.close(),
|
|
496
|
-
new Promise(resolve => setTimeout(resolve, 3000)),
|
|
497
|
-
]);
|
|
498
|
-
} catch {
|
|
499
|
-
// Ignore close errors
|
|
500
|
-
}
|
|
501
489
|
}
|
|
502
490
|
}
|
|
503
491
|
|
|
@@ -37,23 +37,6 @@ review-requests:
|
|
|
37
37
|
session:
|
|
38
38
|
name: "Review: {title}"
|
|
39
39
|
|
|
40
|
-
my-prs-feedback:
|
|
41
|
-
name: my-prs-feedback
|
|
42
|
-
tool:
|
|
43
|
-
# comments:>0 filter ensures only PRs with feedback are returned
|
|
44
|
-
command: ["gh", "search", "prs", "--author=@me", "--state=open", "comments:>0", "--json", "number,title,url,repository,state,body,updatedAt,commentsCount"]
|
|
45
|
-
item:
|
|
46
|
-
id: "{url}"
|
|
47
|
-
repo: "{repository.nameWithOwner}"
|
|
48
|
-
prompt: review-feedback
|
|
49
|
-
session:
|
|
50
|
-
name: "Feedback: {title}"
|
|
51
|
-
# Reprocess when PR is updated (new commits pushed, new comments, etc.)
|
|
52
|
-
# This ensures we re-trigger after addressing review feedback
|
|
53
|
-
reprocess_on:
|
|
54
|
-
- state
|
|
55
|
-
- updatedAt
|
|
56
|
-
|
|
57
40
|
my-prs-attention:
|
|
58
41
|
name: my-prs-attention
|
|
59
42
|
tool:
|
|
@@ -679,24 +679,6 @@ sources:
|
|
|
679
679
|
assert.ok(sources[0].tool.command.includes('--review-requested=@me'), 'command should include review-requested filter');
|
|
680
680
|
});
|
|
681
681
|
|
|
682
|
-
test('expands github/my-prs-feedback preset', async () => {
|
|
683
|
-
writeFileSync(configPath, `
|
|
684
|
-
sources:
|
|
685
|
-
- preset: github/my-prs-feedback
|
|
686
|
-
`);
|
|
687
|
-
|
|
688
|
-
const { loadRepoConfig, getSources } = await import('../../service/repo-config.js');
|
|
689
|
-
loadRepoConfig(configPath);
|
|
690
|
-
const sources = getSources();
|
|
691
|
-
|
|
692
|
-
assert.strictEqual(sources[0].name, 'my-prs-feedback');
|
|
693
|
-
// GitHub presets now use gh CLI instead of MCP
|
|
694
|
-
assert.ok(sources[0].tool.command.includes('--author=@me'), 'command should include author filter');
|
|
695
|
-
assert.ok(sources[0].tool.command.includes('comments:>0'), 'command should filter for PRs with comments');
|
|
696
|
-
// This preset includes updatedAt in reprocess_on to catch new commits
|
|
697
|
-
assert.deepStrictEqual(sources[0].reprocess_on, ['state', 'updatedAt']);
|
|
698
|
-
});
|
|
699
|
-
|
|
700
682
|
test('expands github/my-prs-attention preset', async () => {
|
|
701
683
|
writeFileSync(configPath, `
|
|
702
684
|
sources:
|
|
@@ -774,7 +756,6 @@ sources:
|
|
|
774
756
|
sources:
|
|
775
757
|
- preset: github/my-issues
|
|
776
758
|
- preset: github/review-requests
|
|
777
|
-
- preset: github/my-prs-feedback
|
|
778
759
|
- preset: github/my-prs-attention
|
|
779
760
|
`);
|
|
780
761
|
|
|
@@ -860,7 +841,6 @@ sources:
|
|
|
860
841
|
sources:
|
|
861
842
|
- preset: github/my-issues
|
|
862
843
|
- preset: github/review-requests
|
|
863
|
-
- preset: github/my-prs-feedback
|
|
864
844
|
- preset: github/my-prs-attention
|
|
865
845
|
`);
|
|
866
846
|
|
|
@@ -874,11 +854,8 @@ sources:
|
|
|
874
854
|
// review-requests: "Review: {title}"
|
|
875
855
|
assert.strictEqual(sources[1].session.name, 'Review: {title}', 'review-requests should prefix with Review:');
|
|
876
856
|
|
|
877
|
-
// my-prs-feedback: "Feedback: {title}"
|
|
878
|
-
assert.strictEqual(sources[2].session.name, 'Feedback: {title}', 'my-prs-feedback should prefix with Feedback:');
|
|
879
|
-
|
|
880
857
|
// my-prs-attention: "{_attention_label}: {title}" (dynamic based on detected conditions)
|
|
881
|
-
assert.ok(sources[
|
|
858
|
+
assert.ok(sources[2].session.name.includes('_attention_label'), 'my-prs-attention should use dynamic label');
|
|
882
859
|
});
|
|
883
860
|
|
|
884
861
|
test('linear preset includes session name', async () => {
|