Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions kompass.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"merge": { "enabled": true },
"pr/create": { "enabled": true },
"pr/fix": { "enabled": true },
"pr/analyze": { "enabled": true },
"pr/review": { "enabled": true },
"review": { "enabled": true },
"skill/create": { "enabled": true },
Expand Down
5 changes: 5 additions & 0 deletions kompass.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
"pr/fix": {
"$ref": "#/$defs/commandConfig"
},
"pr/analyze": {
"$ref": "#/$defs/commandConfig"
},
"pr/review": {
"$ref": "#/$defs/commandConfig"
},
Expand Down Expand Up @@ -113,6 +116,7 @@
"merge",
"pr/create",
"pr/fix",
"pr/analyze",
"pr/review",
"review",
"skill/create",
Expand Down Expand Up @@ -144,6 +148,7 @@
"merge",
"pr/create",
"pr/fix",
"pr/analyze",
"pr/review",
"review",
"skill/create",
Expand Down
5 changes: 5 additions & 0 deletions packages/core/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ export const commandDefinitions: Record<string, CommandDefinition> = {
templatePath: "commands/pr/fix.md",
subtask: false,
},
"pr/analyze": {
description: "Load and filter PR review history into an actionable brief",
agent: "reviewer",
templatePath: "commands/pr/analyze.md",
},
"pr/review": {
description: "Review the current PR and publish review feedback",
agent: "reviewer",
Expand Down
23 changes: 16 additions & 7 deletions packages/core/commands/loop/pr/fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@ $ARGUMENTS
- If empty, leave `<pr-ref>` undefined and let `<%= it.config.tools.pr_load.name %>` resolve the default PR context
- Initialize `<completed-fix-passes>` as `0`

### Load PR Context
### Delegate PR Analysis

<%~ include("@load-pr", { config: it.config, ref: "<pr-ref>", result: "<pr-context>" }) %>
<delegate agent="<%= it.config.agents.reviewer.name %>" command="<%= it.config.commands["pr/analyze"].name %>">
<pr-ref>

- Store `<pr-url>` as `<pr-context.pr.url>`
- Store `<pr-number>` as `<pr-context.pr.number>`
Additional context: <additional-context>
</delegate>

- Store the delegated result as `<pr-analysis>`
- Extract `<pr-url>` from the PR_URL section of `<pr-analysis>`
- Extract `<pr-number>` from the PR_NUMBER section of `<pr-analysis>`
- STOP if `<pr-url>` or `<pr-number>` is unavailable

### Watch CI And Comments
Expand All @@ -41,10 +46,14 @@ $ARGUMENTS

### Reload PR Feedback

Call `<%= it.config.tools.pr_load.name %>` with `<pr-url>` and store the refreshed result as `<fresh-pr-context>`.
<delegate agent="<%= it.config.agents.reviewer.name %>" command="<%= it.config.commands["pr/analyze"].name %>">
<pr-url>

Additional context: <additional-context>
</delegate>

- Review `<fresh-pr-context.threads>`, `<fresh-pr-context.reviews>`, and `<fresh-pr-context.issueComments>`
- Identify open, unresolved, actionable reviewer feedback that has not already been answered by the author or superseded by later commits
- Store the refreshed result as `<fresh-pr-analysis>`
- Use the THREADS and SUMMARY sections from `<fresh-pr-analysis>` to identify open, unresolved, actionable reviewer feedback that has not already been answered by the author or superseded by later commits
- Combine actionable reviewer feedback and `<ci-failures>` into `<actionable-work>`
- Store the number of actionable reviewer items as `<actionable-feedback-count>`
- Store the number of actionable CI failures as `<actionable-ci-count>`
Expand Down
102 changes: 102 additions & 0 deletions packages/core/commands/pr/analyze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
## Goal

Load a pull request's full review history, filter out resolved and outdated threads, and return a compact actionable brief so the caller never receives stale or already-addressed feedback in its context.

## Additional Context

Use `<additional-context>` to focus the analysis on specific feedback categories, reviewer priorities, or CI concerns that should influence which threads are flagged as actionable.

## Workflow

### Arguments

<arguments>
$ARGUMENTS
</arguments>

### Interpret Arguments

- If `<arguments>` looks like a PR number (e.g., "123") or URL, store it as `<pr-ref>`
- If `<arguments>` includes extra analysis guidance, scope constraints, or priorities, store it as `<additional-context>`
- If empty, leave `<pr-ref>` undefined and let `<%= it.config.tools.pr_load.name %>` resolve the default PR context

### Load PR Context

<%~ include("@load-pr", { config: it.config, ref: "<pr-ref>", result: "<pr-context>" }) %>

- Store `<pr-url>` as `<pr-context.pr.url>`
- Store `<pr-number>` as `<pr-context.pr.number>`
- Store `<pr-branch>` as `<pr-context.pr.headRefName>`
- Store `<base-branch>` as `<pr-context.pr.baseRefName>`
- Store `<commit-count>` as `<pr-context.pr.commitCount>`
- STOP if `<pr-url>` or `<pr-number>` is unavailable

### Filter Resolved Threads

- Exclude every thread in `<pr-context.threads>` where `isResolved` is `true`
- Exclude every thread where `isOutdated` is `true`
- For each remaining thread, check whether the latest reply from the PR author indicates the feedback was already addressed (phrases like "Fixed", "Done", "Addressed") and a later commit in `<pr-context.pr.commits>` plausibly implements that fix
- Move effectively-resolved threads to a `<resolved-silently>` list with a one-line reason
- Store the remaining threads as `<actionable-threads>`

### Categorize Threads

For each thread in `<actionable-threads>`:
- Classify as `critical`, `important`, `style`, `question`, or `noise`
- Record `<comment-id>`, `<file>`, `<lines>`, `<category>`, `<suggestion>` (one-line), `<current-heading>` (`yes` or `no`)
- Threads marked `noise` or `question` go to the `<not-fixing>` list with reasons

### Partition Groups

- Group actionable threads (`critical`, `important`, actionable `style`) into file-disjoint groups for parallel implementation
- Threads sharing the same file or having cross-thread dependencies stay in the same group
- Store the partition as `<groups>`

### Output

Return a single fenced markdown block with this structure:

```
PR_NUMBER: <pr-number>
PR_BRANCH: <pr-branch>
BASE_BRANCH: <base-branch>
PR_URL: <pr-url>
COMMIT_COUNT: <commit-count>

SUMMARY:
- One-line overall assessment
- Recommended fix order: critical first, then important, then style
- Total actionable threads: <count>
- Total resolved-silently threads: <count>

THREADS:
- [critical] <comment-id> <file>:<lines> - <suggestion> (current-heading: yes/no)
- [important] <comment-id> <file>:<lines> - <suggestion> (current-heading: yes/no)
- [style] <comment-id> <file>:<lines> - <suggestion> (current-heading: yes/no)

NOT_FIXING:
- [noise] <comment-id> - <reason>
- [question] <comment-id> - <reason>

RESOLVED_SILENTLY:
- <comment-id> - <reason>

GROUPS:
- group-1: <file-a>, <file-b>
- group-2: <file-c>
```

If no actionable threads remain, output:
```
PR_NUMBER: <pr-number>
PR_BRANCH: <pr-branch>
BASE_BRANCH: <base-branch>
PR_URL: <pr-url>
COMMIT_COUNT: <commit-count>

SUMMARY:
- No actionable feedback remaining
- Total actionable threads: 0

No additional steps are required.
```
44 changes: 27 additions & 17 deletions packages/core/commands/pr/fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,41 @@ $ARGUMENTS
- Otherwise, store `<execution-mode>` as `review`
- If empty, leave `<pr-ref>` undefined and let `<%= it.config.tools.pr_load.name %>` resolve the default PR context

### Load PR Context
### Delegate PR Analysis

<%~ include("@load-pr", { config: it.config, ref: "<pr-ref>", result: "<pr-context>" }) %>
<delegate agent="<%= it.config.agents.reviewer.name %>" command="<%= it.config.commands["pr/analyze"].name %>">
<pr-ref>

Additional context: <additional-context>
</delegate>

- Store the delegated result as `<pr-analysis>`
- Extract `<pr-number>` from the PR_NUMBER section of `<pr-analysis>`
- Extract `<pr-branch>` from the PR_BRANCH section of `<pr-analysis>`
- Extract `<base-branch>` from the BASE_BRANCH section of `<pr-analysis>`
- Extract `<pr-url>` from the PR_URL section of `<pr-analysis>`
- Extract `<commit-count>` from the COMMIT_COUNT section of `<pr-analysis>`
- STOP if `<pr-analysis>` is unavailable or reports no actionable feedback

### Align Local Branch

- If `<pr-branch>` is unavailable, STOP and report that the PR head branch could not be determined
- Run `gh pr checkout <pr-context.pr.number>` before analyzing repository files or making code changes for this PR
- Run `gh pr checkout <pr-number>` before analyzing repository files or making code changes for this PR
- After checkout, store the active branch as `<active-branch>`
- If checkout fails or times out, STOP and report that the PR branch could not be checked out locally; do not retry checkout unless the user explicitly asks
- Do not inspect or modify local code for this PR until `<active-branch>` equals `<pr-branch>`

### Load Changes

Call `<%= it.config.tools.changes_load.name %>` with `base: <pr-context.pr.baseRefName>`, `head: <active-branch>`, and `depthHint: <pr-context.pr.commitCount>` only when it is a positive integer. Store as `<changes>`.
Call `<%= it.config.tools.changes_load.name %>` with `base: <base-branch>`, `head: <active-branch>`, and `depthHint: <commit-count>` only when it is a positive integer. Store as `<changes>`.

### Analyze Feedback

Separate true course corrections from noise or already-resolved feedback:
1. Review `<pr-context.threads>` for open, unresolved conversations
2. Check `<pr-context.reviews>` for state changes (CHANGES_REQUESTED, etc.)
3. Include any CI failures, logs, failing check names, or reproduction details provided in `<additional-context>` as actionable feedback
4. Use `<changes>` to understand the current PR diff before deciding what to adjust
5. Prioritize critical issues (bugs, security, broken contracts, failing required checks)
6. Identify which files need changes
- Use the THREADS, SUMMARY, and GROUPS sections from `<pr-analysis>` as the filtered, actionable feedback source
- Include any CI failures, logs, failing check names, or reproduction details provided in `<additional-context>` as actionable feedback
- Use `<changes>` to understand the current PR diff before deciding what to adjust
- Prioritize critical issues (bugs, security, broken contracts, failing required checks)
- Identify which files need changes

Do not blindly follow every suggestion—some may lead you off course.

Expand Down Expand Up @@ -122,13 +132,13 @@ Only after commit and push succeed, reply to addressed threads:

```
# General PR comment
<%= it.config.tools.pr_sync.name %> refUrl="<pr-context.pr.url>" commentBody="<reply-text>"
<%= it.config.tools.pr_sync.name %> refUrl="<pr-url>" commentBody="<reply-text>"

# Reply to a specific review thread (use comment.id from threads.comments)
<%= it.config.tools.pr_sync.name %> refUrl="<pr-context.pr.url>" replies=[{"inReplyTo": <comment-id>, "body": "<reply-text>"}]
<%= it.config.tools.pr_sync.name %> refUrl="<pr-url>" replies=[{"inReplyTo": <comment-id>, "body": "<reply-text>"}]

# Follow-up inline review comment on a specific line
<%= it.config.tools.pr_sync.name %> refUrl="<pr-context.pr.url>" commitId="<commit-sha>" review={"comments": [{"path": "<file-path>", "line": <line-number>, "body": "<reply-text>"}]}
<%= it.config.tools.pr_sync.name %> refUrl="<pr-url>" commitId="<commit-sha>" review={"comments": [{"path": "<file-path>", "line": <line-number>, "body": "<reply-text>"}]}
```

Confirm which feedback was addressed and which was intentionally not followed.
Expand All @@ -138,7 +148,7 @@ Confirm which feedback was addressed and which was intentionally not followed.

When waiting for approval or revision feedback, display:
```
Review fixes for PR #<pr-context.pr.number>
Review fixes for PR #<pr-number>

- Changes made: <changes-count> files modified
- Validation passing: <validation-passing>
Expand All @@ -147,7 +157,7 @@ Review fixes for PR #<pr-context.pr.number>

If the workflow stops after a no-change pass, display:
```
No changes made for PR #<pr-context.pr.number>
No changes made for PR #<pr-number>

- Changes made: 0 files modified
- Validation passing: <validation-passing>
Expand All @@ -158,7 +168,7 @@ No additional steps are required.

When fixes are complete, display exactly this final completion summary and stop. Do not continue with extra analysis, planning, or follow-up tasks unless the workflow is blocked or the user asked for more:
```
PR fix complete for #<pr-context.pr.number>
PR fix complete for #<pr-number>

- Changes made: <changes-count> files modified
- Threads resolved: <threads-resolved>
Expand Down
1 change: 1 addition & 0 deletions packages/core/kompass.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"merge": { "enabled": true },
"pr/create": { "enabled": true },
"pr/fix": { "enabled": true },
"pr/analyze": { "enabled": true },
"pr/review": { "enabled": true },
"review": { "enabled": true },
"skill/create": { "enabled": true },
Expand Down
1 change: 1 addition & 0 deletions packages/core/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const DEFAULT_COMMAND_NAMES = [
"merge",
"pr/create",
"pr/fix",
"pr/analyze",
"pr/review",
"review",
"skill/create",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe("resolveCommands", () => {
const commands = await resolveCommands(process.cwd());
const template = commands["pr/fix"]?.template ?? "";

assert.match(template, /Run `gh pr checkout <pr-context\.pr\.number>` before analyzing repository files or making code changes for this PR/);
assert.match(template, /Run `gh pr checkout <pr-number>` before analyzing repository files or making code changes for this PR/);
assert.match(template, /Do not inspect or modify local code for this PR until `<active-branch>` equals `<pr-branch>`/);
assert.doesNotMatch(template, /`<current-branch>` differs from `<pr-branch>` or `<current-head>` differs from `<pr-context\.pr\.headRefOid>`/);
});
Expand Down
37 changes: 18 additions & 19 deletions packages/opencode/.opencode/commands/loop/pr/fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,17 @@ $ARGUMENTS
- If empty, leave `<pr-ref>` undefined and let `kompass_pr_load` resolve the default PR context
- Initialize `<completed-fix-passes>` as `0`

### Load PR Context

- Use `kompass_pr_load` as the source of truth for PR selection
- If `<pr-ref>` is defined, call `kompass_pr_load` with `pr: <pr-ref>`
- Otherwise, call `kompass_pr_load` with no arguments
- Do not run separate git or GitHub commands just to discover the PR before calling `kompass_pr_load`
- Store the result as `<pr-context>`
- Store the PR head branch as `<pr-branch>` from `<pr-context>.pr.headRefName` when it is available
- Run `git branch --show-current` and store the trimmed result as `<current-branch>` when it is available
- Run `git rev-parse HEAD` and store the trimmed result as `<current-head>` when it is available
- Treat the loaded PR body, discussion, review history, and any attachments or linked artifacts returned by the loader as part of the source context
- Review attached images, screenshots, videos, PDFs, and other linked files whenever they can affect the requested fix, review outcome, reproduction steps, or acceptance criteria
- If any relevant attachment cannot be accessed, note that gap and continue only when the remaining PR context is still sufficient to proceed reliably

- Store `<pr-url>` as `<pr-context.pr.url>`
- Store `<pr-number>` as `<pr-context.pr.number>`
### Delegate PR Analysis

<delegate agent="reviewer" command="pr/analyze">
<pr-ref>

Additional context: <additional-context>
</delegate>

- Store the delegated result as `<pr-analysis>`
- Extract `<pr-url>` from the PR_URL section of `<pr-analysis>`
- Extract `<pr-number>` from the PR_NUMBER section of `<pr-analysis>`
- STOP if `<pr-url>` or `<pr-number>` is unavailable

### Watch CI And Comments
Expand All @@ -56,10 +51,14 @@ $ARGUMENTS

### Reload PR Feedback

Call `kompass_pr_load` with `<pr-url>` and store the refreshed result as `<fresh-pr-context>`.
<delegate agent="reviewer" command="pr/analyze">
<pr-url>

Additional context: <additional-context>
</delegate>

- Review `<fresh-pr-context.threads>`, `<fresh-pr-context.reviews>`, and `<fresh-pr-context.issueComments>`
- Identify open, unresolved, actionable reviewer feedback that has not already been answered by the author or superseded by later commits
- Store the refreshed result as `<fresh-pr-analysis>`
- Use the THREADS and SUMMARY sections from `<fresh-pr-analysis>` to identify open, unresolved, actionable reviewer feedback that has not already been answered by the author or superseded by later commits
- Combine actionable reviewer feedback and `<ci-failures>` into `<actionable-work>`
- Store the number of actionable reviewer items as `<actionable-feedback-count>`
- Store the number of actionable CI failures as `<actionable-ci-count>`
Expand Down
Loading