swift-code-reviewer-skill 1.2.1 → 1.4.0
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/CHANGELOG.md +53 -0
- package/CONTRIBUTING.md +86 -3
- package/README.md +315 -118
- package/SKILL.md +122 -4
- package/bin/install.js +71 -90
- package/bin/lib/agents.js +134 -0
- package/bin/lib/prompt.js +58 -0
- package/core/swift-code-reviewer.core.md +281 -0
- package/examples/README.md +35 -0
- package/examples/claude-tca-review.md +140 -0
- package/examples/codex-async-algorithms-review.md +208 -0
- package/examples/gemini-isowords-review.md +207 -0
- package/package.json +6 -1
- package/references/agent-loop-feedback.md +148 -0
- package/references/spec-adherence.md +157 -0
- package/templates/agents/claude/swift-code-reviewer.md +78 -0
- package/templates/agents/codex/swift-code-reviewer.md +211 -0
- package/templates/agents/gemini/swift-code-reviewer.md +211 -0
- package/templates/agents/kiro/swift-code-reviewer.md +218 -0
- package/templates/commands/claude/review.md +56 -0
- package/templates/commands/gemini/review.toml +15 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Code Review — pointfreeco/isowords
|
|
2
|
+
|
|
3
|
+
**Agent**: Google Gemini CLI with swift-code-reviewer-skill
|
|
4
|
+
**Scope**: `Sources/` (Start using IssueReporting)
|
|
5
|
+
**Commit**: [`c727d3a`](https://github.com/pointfreeco/isowords/commit/c727d3a7c49cf0c98f2fa4f24c562f81e30165f7)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Files: 5 | Critical: 0 | High: 1 | Medium: 3 | Low: 2
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Spec Adherence
|
|
16
|
+
|
|
17
|
+
**Source**: commit message — "Start using IssueReporting. (#205)"
|
|
18
|
+
|
|
19
|
+
| Requirement | Status | Location |
|
|
20
|
+
| ---------------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------- |
|
|
21
|
+
| Replace `fatalError`/`assertionFailure` with `reportIssue` | ✅ Done | multiple files |
|
|
22
|
+
| Migrate `preconditionFailure` to `reportIssue` | ⚠️ Partial — 2 call sites missed | GameFeature/GameView.swift:134, AudioPlayerClient.swift:67 |
|
|
23
|
+
| No behavioral change for release builds | ✅ Confirmed — `reportIssue` is a no-op in release | — |
|
|
24
|
+
| Tests updated to assert `reportIssue` calls | ❌ Not implemented — no test changes in diff | — |
|
|
25
|
+
|
|
26
|
+
**Missing work**: Two `preconditionFailure` sites not migrated; no test assertions added for
|
|
27
|
+
`reportIssue` invocations (IssueReporting provides `withExpectedIssue` for exactly this).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## GameFeature/GameView.swift
|
|
32
|
+
|
|
33
|
+
**High** **SwiftUI Patterns** (line 89)
|
|
34
|
+
|
|
35
|
+
Current:
|
|
36
|
+
|
|
37
|
+
```swift
|
|
38
|
+
NavigationView {
|
|
39
|
+
WithViewStore(self.store) { viewStore in
|
|
40
|
+
GameBoardView(store: self.store)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Finding: `NavigationView` is deprecated as of iOS 16. `WithViewStore` is the older TCA
|
|
46
|
+
observation pattern — isowords targets iOS 16+ and should use `NavigationStack` with
|
|
47
|
+
the `@Bindable` store observation available in TCA 1.x.
|
|
48
|
+
|
|
49
|
+
Fix:
|
|
50
|
+
|
|
51
|
+
```swift
|
|
52
|
+
NavigationStack {
|
|
53
|
+
GameBoardView(store: self.store)
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
And in the view body, replace `WithViewStore` reads with direct `store.someState` access
|
|
58
|
+
via `@Bindable var store: StoreOf<GameFeature>`.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
**Medium** **SwiftUI Patterns** (line 134)
|
|
63
|
+
|
|
64
|
+
Current:
|
|
65
|
+
|
|
66
|
+
```swift
|
|
67
|
+
preconditionFailure("Unexpected game mode: \(mode)")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Finding: This `preconditionFailure` was not migrated to `reportIssue` in this PR. The PR
|
|
71
|
+
description states the goal is to migrate all hard crashes to `reportIssue`. In production,
|
|
72
|
+
`preconditionFailure` will crash the app; `reportIssue` would log the issue and allow the
|
|
73
|
+
app to degrade gracefully.
|
|
74
|
+
|
|
75
|
+
Fix:
|
|
76
|
+
|
|
77
|
+
```swift
|
|
78
|
+
reportIssue("Unexpected game mode: \(mode)")
|
|
79
|
+
return // or a safe default path
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## AudioPlayerClient.swift
|
|
85
|
+
|
|
86
|
+
**Medium** **Swift Quality** (line 67)
|
|
87
|
+
|
|
88
|
+
Current:
|
|
89
|
+
|
|
90
|
+
```swift
|
|
91
|
+
preconditionFailure("AudioPlayerClient not implemented")
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Finding: Same as above — not migrated to `reportIssue`. Additionally, `"not implemented"`
|
|
95
|
+
as a crash message suggests this may be a stub that should be a real implementation or at
|
|
96
|
+
least a `TODO` tracked in the issue tracker.
|
|
97
|
+
|
|
98
|
+
Fix:
|
|
99
|
+
|
|
100
|
+
```swift
|
|
101
|
+
reportIssue("AudioPlayerClient: \(#function) not implemented")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## AppFeature/AppReducer.swift
|
|
107
|
+
|
|
108
|
+
**Medium** **Architecture** (line 201)
|
|
109
|
+
|
|
110
|
+
Current:
|
|
111
|
+
|
|
112
|
+
```swift
|
|
113
|
+
case .game(.delegate(.gameOver)):
|
|
114
|
+
guard let result = state.game?.gameResult else {
|
|
115
|
+
reportIssue("gameOver delegate action fired without a game result")
|
|
116
|
+
return .none
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Finding: The `reportIssue` call is correct and well-placed. However, the `guard` falls
|
|
121
|
+
through to `.none` silently — callers observing the game-over flow will see no state
|
|
122
|
+
change and no indication that the invariant was violated. Consider adding a state flag
|
|
123
|
+
or `.send` to surface the degraded state to the UI.
|
|
124
|
+
|
|
125
|
+
Fix: After `reportIssue`, set a recoverable error state:
|
|
126
|
+
|
|
127
|
+
```swift
|
|
128
|
+
reportIssue("gameOver delegate action fired without a game result")
|
|
129
|
+
state.errorBanner = "Something went wrong. Please restart the game."
|
|
130
|
+
return .none
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Tests/GameFeatureTests/GameViewTests.swift
|
|
136
|
+
|
|
137
|
+
**Low** **Swift Quality** (line 12)
|
|
138
|
+
|
|
139
|
+
Current:
|
|
140
|
+
|
|
141
|
+
```swift
|
|
142
|
+
import XCTest
|
|
143
|
+
@testable import GameFeature
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Finding: isowords has started migrating to Swift Testing (visible in other test files in
|
|
147
|
+
this repo). New test files should use `import Testing` unless XCTest-specific APIs are
|
|
148
|
+
required. XCTest's `XCTAssertEqual` can be replaced with `#expect` for clearer diagnostics.
|
|
149
|
+
|
|
150
|
+
Fix:
|
|
151
|
+
|
|
152
|
+
```swift
|
|
153
|
+
import Testing
|
|
154
|
+
@testable import GameFeature
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Positive Observations
|
|
160
|
+
|
|
161
|
+
The adoption of `IssueReporting` is architecturally sound — replacing hard crashes with
|
|
162
|
+
soft issue reports significantly improves testability, since `withExpectedIssue` lets tests
|
|
163
|
+
assert that invalid states are reported rather than crashing the test suite.
|
|
164
|
+
|
|
165
|
+
`AppReducer.swift` correctly uses `reportIssue` (not `assertionFailure`) in the newly
|
|
166
|
+
migrated call sites, demonstrating consistent application of the pattern.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Prioritized Action Items
|
|
171
|
+
|
|
172
|
+
- [Must fix] Migrate remaining `preconditionFailure` calls to `reportIssue` (GameView.swift:134, AudioPlayerClient.swift:67)
|
|
173
|
+
- [Should fix] Add `withExpectedIssue` test assertions for newly reportable error paths
|
|
174
|
+
- [Should fix] Replace deprecated `NavigationView` + `WithViewStore` with `NavigationStack` + `@Bindable` (GameView.swift:89)
|
|
175
|
+
- [Consider] Surface degraded state to UI after `reportIssue` in AppReducer (AppReducer.swift:201)
|
|
176
|
+
- [Consider] Migrate GameViewTests to Swift Testing framework
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Agent Loop Feedback
|
|
181
|
+
|
|
182
|
+
### Pattern: Incomplete migration — `preconditionFailure` not replaced (2 occurrences)
|
|
183
|
+
|
|
184
|
+
**Files**: GameFeature/GameView.swift:134, AudioPlayerClient.swift:67
|
|
185
|
+
|
|
186
|
+
**Suggested rule for `GEMINI.md`**:
|
|
187
|
+
|
|
188
|
+
> When migrating crash calls (`fatalError`, `preconditionFailure`, `assertionFailure`) to
|
|
189
|
+
> `reportIssue`, search the entire diff for remaining instances before marking the PR complete.
|
|
190
|
+
> Use `grep -r "preconditionFailure\|fatalError\|assertionFailure" Sources/` to verify.
|
|
191
|
+
|
|
192
|
+
### Pattern: No tests added for new `reportIssue` call sites (covers 3+ sites)
|
|
193
|
+
|
|
194
|
+
**Files**: AppFeature/AppReducer.swift, GameFeature/GameView.swift, AudioPlayerClient.swift
|
|
195
|
+
|
|
196
|
+
**Suggested rule for `GEMINI.md`**:
|
|
197
|
+
|
|
198
|
+
> Every new `reportIssue` call site must have a corresponding `withExpectedIssue { }` test.
|
|
199
|
+
> IssueReporting is only valuable if tests verify the reports fire — otherwise it is
|
|
200
|
+
> indistinguishable from a no-op.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
_Representative demonstration. Generated against commit
|
|
205
|
+
[`c727d3a`](https://github.com/pointfreeco/isowords/commit/c727d3a7c49cf0c98f2fa4f24c562f81e30165f7)
|
|
206
|
+
of [pointfreeco/isowords](https://github.com/pointfreeco/isowords).
|
|
207
|
+
Line numbers and snippets are illustrative of the patterns the skill detects in this codebase._
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swift-code-reviewer-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Claude Code skill for comprehensive Swift/SwiftUI code reviews with multi-layer analysis",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
42
42
|
"bin/",
|
|
43
|
+
"core/",
|
|
44
|
+
"examples/",
|
|
43
45
|
"references/",
|
|
44
46
|
"skills/",
|
|
45
47
|
"templates/",
|
|
@@ -49,6 +51,9 @@
|
|
|
49
51
|
"CONTRIBUTING.md",
|
|
50
52
|
"CHANGELOG.md"
|
|
51
53
|
],
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@inquirer/prompts": "^7"
|
|
56
|
+
},
|
|
52
57
|
"engines": {
|
|
53
58
|
"node": ">=16.0.0"
|
|
54
59
|
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Agent Loop Feedback Reference
|
|
2
|
+
|
|
3
|
+
When the code under review was generated by an AI agent, recurring mistakes
|
|
4
|
+
are not just *code* problems — they are *instruction* problems. This document
|
|
5
|
+
defines how the reviewer aggregates per-finding signals into rule suggestions
|
|
6
|
+
that can be added to `.claude/CLAUDE.md` or an agent system prompt to prevent
|
|
7
|
+
the same class of issue next time.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. The ≥2 Threshold
|
|
12
|
+
|
|
13
|
+
A single instance of a mistake is a slip. Two is a pattern. Three or more
|
|
14
|
+
means the agent's instructions are silent on the topic and need an explicit
|
|
15
|
+
rule.
|
|
16
|
+
|
|
17
|
+
Rules of thumb:
|
|
18
|
+
|
|
19
|
+
| Occurrences in diff | Treatment |
|
|
20
|
+
| ------------------- | --------------------------------------------------------------------------------- |
|
|
21
|
+
| 1 | Per-file finding only. Do not surface in Agent Loop Feedback. |
|
|
22
|
+
| 2 | Recurring pattern. Suggest a rule. Mark priority **medium**. |
|
|
23
|
+
| 3+ | Strong signal. Suggest a rule. Mark priority **high**. |
|
|
24
|
+
| 2+ across PRs | If the same rule has been suggested before (see §3), escalate to **high**. |
|
|
25
|
+
|
|
26
|
+
Count occurrences by **rule**, not by raw findings. For example, four force-unwrap
|
|
27
|
+
findings in different files count as four occurrences of the rule
|
|
28
|
+
*"never force-unwrap"*, not four separate rules.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 2. Phrasing Rules as Directives
|
|
33
|
+
|
|
34
|
+
Rules go in an instruction file the agent will *read*. Write them so the
|
|
35
|
+
reader knows what to do without further interpretation.
|
|
36
|
+
|
|
37
|
+
### Strong forms
|
|
38
|
+
|
|
39
|
+
- **Never X.** — bans an action outright. Best for safety/security/crashes.
|
|
40
|
+
- **Always Y.** — mandates an action. Best for required patterns.
|
|
41
|
+
- **Prefer X over Y.** — gives a default with an implicit escape hatch. Best
|
|
42
|
+
for stylistic or modernization rules.
|
|
43
|
+
- **Use X. Y is deprecated / forbidden.** — adds the *why* in five words.
|
|
44
|
+
|
|
45
|
+
### Weak forms (avoid)
|
|
46
|
+
|
|
47
|
+
- **"Try to..."** — agents will skip it under pressure.
|
|
48
|
+
- **"It's a good idea to..."** — descriptive, not directive.
|
|
49
|
+
- **"Consider..."** — fine in code review prose, useless as a rule.
|
|
50
|
+
- **"X is bad"** — diagnostic, not prescriptive. Doesn't tell the agent what
|
|
51
|
+
to do instead.
|
|
52
|
+
|
|
53
|
+
### Examples
|
|
54
|
+
|
|
55
|
+
| Weak | Strong |
|
|
56
|
+
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
|
|
57
|
+
| Force unwraps are dangerous. | Never use `!`, `try!`, or `as!`. Use `guard let` with an early return, typed throws, or `as?`. |
|
|
58
|
+
| It's better to use `NavigationStack`. | Use `NavigationStack` exclusively. `NavigationView` is deprecated as of iOS 16. |
|
|
59
|
+
| Try to keep views simple. | Views must not contain business logic, network calls, or data transformations. Move all such work into the `@Observable` view model. |
|
|
60
|
+
| Make sure UI updates happen on the main thread. | Always annotate types that mutate `@Observable`/`@Published` state with `@MainActor`. |
|
|
61
|
+
| Don't put secrets in logs. | Never log values from `KeychainService`, `URLRequest.httpBody`, or types annotated `@Sensitive`. |
|
|
62
|
+
|
|
63
|
+
A good rule answers three questions in one sentence: *what is forbidden*,
|
|
64
|
+
*what is the alternative*, and (briefly) *why*.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 3. Checking Past Reviews
|
|
69
|
+
|
|
70
|
+
Before suggesting a rule, check whether something similar was already
|
|
71
|
+
suggested. If yes, the existing wording is not landing — escalate priority
|
|
72
|
+
and consider strengthening the wording rather than restating it.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Has anyone touched the rules file recently, and how?
|
|
76
|
+
git log --oneline --follow .claude/CLAUDE.md
|
|
77
|
+
git log -p --follow .claude/CLAUDE.md | grep -i "<keyword from new rule>"
|
|
78
|
+
|
|
79
|
+
# Search for existing wording on the topic
|
|
80
|
+
grep -in "force.unwrap\|navigationview\|mainactor" .claude/CLAUDE.md
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If a rule on the same topic exists:
|
|
84
|
+
|
|
85
|
+
1. Quote the current rule in the suggestion block.
|
|
86
|
+
2. Explain why it is not preventing the pattern (too soft, too narrow,
|
|
87
|
+
buried, conditional).
|
|
88
|
+
3. Propose a replacement, not an addition.
|
|
89
|
+
|
|
90
|
+
If no rule exists, propose adding one in the most relevant section
|
|
91
|
+
(`Concurrency`, `SwiftUI`, `Security`, `Architecture`, etc.).
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 4. Suggested-Rule Block — Template
|
|
96
|
+
|
|
97
|
+
One block per recurring pattern. Place all blocks under a single
|
|
98
|
+
`## Agent Loop Feedback` heading at the bottom of the report.
|
|
99
|
+
|
|
100
|
+
```markdown
|
|
101
|
+
### Pattern: <short name> (<N> occurrences)
|
|
102
|
+
**Files**: <file:line>, <file:line>, ...
|
|
103
|
+
|
|
104
|
+
**Suggested rule**:
|
|
105
|
+
> <One-sentence directive in strong form. What is forbidden, what to do
|
|
106
|
+
> instead, and one-clause why.>
|
|
107
|
+
|
|
108
|
+
**Existing rule** (if any): <quote, with line reference into `.claude/CLAUDE.md`>
|
|
109
|
+
|
|
110
|
+
**Why it's not landing** (only if existing rule): <too soft / too narrow / buried / etc.>
|
|
111
|
+
|
|
112
|
+
**Priority**: <medium | high>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Worked example:
|
|
116
|
+
|
|
117
|
+
```markdown
|
|
118
|
+
### Pattern: Force-unwraps (4 occurrences)
|
|
119
|
+
**Files**: LoginView.swift:89, NetworkService.swift:34, UserRepo.swift:12, UserRepo.swift:78
|
|
120
|
+
|
|
121
|
+
**Suggested rule**:
|
|
122
|
+
> Never use `!`, `try!`, or `as!`. Use `guard let` with explicit early return,
|
|
123
|
+
> typed throws, or `as?` with handling. Force-unwraps are crashes waiting to happen.
|
|
124
|
+
|
|
125
|
+
**Existing rule**: _.claude/CLAUDE.md:42_ — "Avoid force unwrapping when possible."
|
|
126
|
+
|
|
127
|
+
**Why it's not landing**: "When possible" gives the agent a built-in opt-out.
|
|
128
|
+
The replacement above bans the syntax outright and names the alternatives.
|
|
129
|
+
|
|
130
|
+
**Priority**: high
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 5. Human-Authored Code
|
|
136
|
+
|
|
137
|
+
If the PR was written by a human (no AI assistance disclosed, no agent
|
|
138
|
+
session metadata in commit messages), the same recurring patterns are still
|
|
139
|
+
useful — but frame them as **team coding standards**, not agent instructions:
|
|
140
|
+
|
|
141
|
+
- Replace "Suggested rule for the agent" with "Suggested team standard".
|
|
142
|
+
- Drop the "Why it's not landing" clause; humans benefit more from a short
|
|
143
|
+
rationale than from instruction-tuning analysis.
|
|
144
|
+
- Leave the directive phrasing intact — strong forms read better in human
|
|
145
|
+
style guides too.
|
|
146
|
+
|
|
147
|
+
When unsure whether the code is AI-generated, default to the team-standards
|
|
148
|
+
framing.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Spec Adherence Reference
|
|
2
|
+
|
|
3
|
+
This document describes how the reviewer extracts the *intent* of a change from
|
|
4
|
+
its PR description and linked issues, and how it then judges whether the code
|
|
5
|
+
delivers on that intent. Spec adherence runs before the language- and
|
|
6
|
+
framework-level checks: a clean diff that misses the point shouldn't pass.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Parsing `gh` / `glab` JSON Output
|
|
11
|
+
|
|
12
|
+
Always prefer the JSON output of the platform CLI over scraping the web UI —
|
|
13
|
+
it is stable, scriptable, and includes linked-issue metadata.
|
|
14
|
+
|
|
15
|
+
### GitHub
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# PR body, title, labels, and the issues this PR closes
|
|
19
|
+
gh pr view <num> --json title,body,closingIssuesReferences,labels
|
|
20
|
+
|
|
21
|
+
# Linked issue (one per closing reference)
|
|
22
|
+
gh issue view <num> --json title,body,labels
|
|
23
|
+
|
|
24
|
+
# Reviewer-friendly summary in one shot
|
|
25
|
+
gh pr view <num> --json title,body,closingIssuesReferences \
|
|
26
|
+
--jq '{title, body, issues: [.closingIssuesReferences[].number]}'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Fields to read:
|
|
30
|
+
|
|
31
|
+
| Field | Use |
|
|
32
|
+
| --------------------------- | ------------------------------------------------- |
|
|
33
|
+
| `title` | Short statement of intent — start here. |
|
|
34
|
+
| `body` | Acceptance criteria, scope, non-goals. |
|
|
35
|
+
| `closingIssuesReferences` | Numbers of issues that this PR will close. |
|
|
36
|
+
| `labels` | `bug`, `feature`, `tech-debt` shape expectations. |
|
|
37
|
+
|
|
38
|
+
### GitLab
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
glab mr view <num> # human-readable; pipe to less
|
|
42
|
+
glab mr view <num> --output json
|
|
43
|
+
glab issue view <num> --output json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
GitLab's MR description and linked issues serve the same role as GitHub's PR
|
|
47
|
+
body and `closingIssuesReferences`.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. Finding Acceptance Criteria
|
|
52
|
+
|
|
53
|
+
Acceptance criteria are rarely labeled as such. Look for these patterns, in
|
|
54
|
+
roughly this order:
|
|
55
|
+
|
|
56
|
+
1. **Markdown checkboxes** — `- [ ] ...` or `- [x] ...`. The most reliable
|
|
57
|
+
signal. Each box is a discrete requirement.
|
|
58
|
+
2. **Gherkin / Given-When-Then** — phrases starting with `Given`, `When`,
|
|
59
|
+
`Then`, or `And`. Common in BDD-flavored teams.
|
|
60
|
+
3. **Modal verbs** — `must`, `should`, `shall`, `will`, `needs to`. Each
|
|
61
|
+
sentence is a candidate requirement; `must`/`shall` outrank `should`.
|
|
62
|
+
4. **Numbered or bulleted lists** under headings like `Acceptance Criteria`,
|
|
63
|
+
`Requirements`, `Scope`, `Goals`, `What this PR does`.
|
|
64
|
+
5. **"Closes #N" / "Fixes #N"** — pull the linked issue and repeat 1–4 there.
|
|
65
|
+
|
|
66
|
+
If the PR has none of the above, treat the **title** as the single requirement
|
|
67
|
+
and note the lack of explicit criteria in the report.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 3. Handling PRs With No Description
|
|
72
|
+
|
|
73
|
+
A blank or near-blank description is itself a finding. Do not invent intent.
|
|
74
|
+
|
|
75
|
+
1. Note in the report: _"PR description is empty / minimal — spec adherence
|
|
76
|
+
inferred from diff and commit messages, may be incomplete."_
|
|
77
|
+
2. Use, in order: linked issues, commit messages (`git log <base>..HEAD`),
|
|
78
|
+
branch name, file paths touched.
|
|
79
|
+
3. List every inferred requirement explicitly so the author can correct any
|
|
80
|
+
misreading, prefixed with `(inferred)`.
|
|
81
|
+
4. Do not penalize the diff for failing to satisfy a requirement that was
|
|
82
|
+
only inferred — flag the missing description instead.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 4. Scope Creep vs. Legitimate Adjacent Fixes
|
|
87
|
+
|
|
88
|
+
Not every out-of-spec change is scope creep. Use this rubric:
|
|
89
|
+
|
|
90
|
+
| Change type | Verdict |
|
|
91
|
+
| ---------------------------------------------------------------------------- | ------------------------------------- |
|
|
92
|
+
| Touches a file required by the spec, fixes an obvious nearby bug, < ~10 LOC | **Allow** — note it; don't flag. |
|
|
93
|
+
| Renames or restructures a file the spec requires editing | **Allow if minimal**, otherwise flag. |
|
|
94
|
+
| Drive-by formatting / style changes across many files | **Flag** — recommend separate PR. |
|
|
95
|
+
| Refactor of a module unrelated to the spec | **Flag** — scope creep. |
|
|
96
|
+
| New feature not mentioned anywhere in spec | **Flag** — scope creep, must justify. |
|
|
97
|
+
| Dependency version bumps | **Flag** — separate PR by convention. |
|
|
98
|
+
| Test additions for the spec'd code | **Allow** — expected. |
|
|
99
|
+
| Test additions for unrelated existing code | **Allow but note** — usually welcome. |
|
|
100
|
+
|
|
101
|
+
When flagging scope creep, always recommend the concrete remediation
|
|
102
|
+
("split out into a follow-up PR" or "move to a separate commit if the team
|
|
103
|
+
allows partial review").
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 5. Intent Drift
|
|
108
|
+
|
|
109
|
+
The trickier failure mode: the diff *runs* but solves a subtly different
|
|
110
|
+
problem than the spec. Symptoms:
|
|
111
|
+
|
|
112
|
+
- Naming uses different domain terms than the spec (e.g., spec says
|
|
113
|
+
"session", code says "token").
|
|
114
|
+
- Data flow contradicts the spec's mental model (e.g., spec says the server
|
|
115
|
+
is the source of truth, code caches and treats local as authoritative).
|
|
116
|
+
- Edge cases the spec called out are silently excluded by an early `return`.
|
|
117
|
+
- The PR title says "fix" but the diff is a rewrite, or vice versa.
|
|
118
|
+
|
|
119
|
+
When you suspect intent drift, quote both the spec sentence and the code
|
|
120
|
+
location side-by-side in the finding.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 6. Requirement Coverage Table — Template
|
|
125
|
+
|
|
126
|
+
Drop this into the Spec Adherence section of the report, one row per
|
|
127
|
+
requirement extracted in step 2.
|
|
128
|
+
|
|
129
|
+
```markdown
|
|
130
|
+
## Spec Adherence
|
|
131
|
+
|
|
132
|
+
**Source**: PR #<num> / Issue #<num>
|
|
133
|
+
|
|
134
|
+
| Requirement | Status | Location |
|
|
135
|
+
|------------------------------------------|-------------------------------------|--------------------------------|
|
|
136
|
+
| <verbatim or paraphrased criterion> | ✅ Implemented | `<file>:<line>` |
|
|
137
|
+
| <criterion with edge case> | ⚠️ Partial — <what's missing> | `<file>:<line>` |
|
|
138
|
+
| <criterion> | ❌ Not implemented | — |
|
|
139
|
+
| <inferred criterion> | ✅ Implemented (inferred) | `<file>:<line>` |
|
|
140
|
+
|
|
141
|
+
**Scope creep**: <count> unrelated change(s) — <one-line summary, recommend split>.
|
|
142
|
+
|
|
143
|
+
**Spec gaps**: <count> criterion/criteria not addressed — see "Must fix" in
|
|
144
|
+
prioritized action items.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Status legend (use these exact glyphs for greppability):
|
|
148
|
+
|
|
149
|
+
- ✅ Implemented — code satisfies the criterion and tests, if any, cover it.
|
|
150
|
+
- ⚠️ Partial — happy path works, but at least one edge case or branch is
|
|
151
|
+
missing. Always say *what* is missing.
|
|
152
|
+
- ❌ Not implemented — no code addresses the criterion.
|
|
153
|
+
- ➖ Not assessed — no spec context available; do not guess.
|
|
154
|
+
|
|
155
|
+
If `status` is anything other than ✅, the corresponding action item belongs
|
|
156
|
+
in **Must fix** or **Should fix** depending on whether the criterion was
|
|
157
|
+
flagged `must`/`shall` versus `should`.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Swift Code Review Agent
|
|
2
|
+
|
|
3
|
+
You are a senior Swift/SwiftUI code reviewer. Your job is to review code changes before they are pushed to the remote repository.
|
|
4
|
+
|
|
5
|
+
## Skills
|
|
6
|
+
|
|
7
|
+
Load and follow the rules from `~/.claude/skills/swift-code-reviewer-skill/SKILL.md` and all files in its `references/` directory.
|
|
8
|
+
|
|
9
|
+
## Workflow
|
|
10
|
+
|
|
11
|
+
When invoked, execute these steps in order:
|
|
12
|
+
|
|
13
|
+
### 1. Collect the diff
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git diff --staged -- '*.swift'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If nothing is staged, fall back to:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
git diff HEAD -- '*.swift'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
If still empty, tell the user there are no Swift changes to review.
|
|
26
|
+
|
|
27
|
+
### 2. Run SwiftLint (if available)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
if command -v swiftlint &>/dev/null; then
|
|
31
|
+
swiftlint lint --config .swiftlint.yml --quiet 2>/dev/null || swiftlint lint --quiet
|
|
32
|
+
fi
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Collect any warnings or errors. If SwiftLint is not installed, skip and note it.
|
|
36
|
+
|
|
37
|
+
### 3. Review
|
|
38
|
+
|
|
39
|
+
Analyze the diff using the swift-code-reviewer-skill rules. Focus on:
|
|
40
|
+
|
|
41
|
+
- **Architecture**: MVVM compliance, separation of concerns, dependency injection
|
|
42
|
+
- **SwiftUI**: proper use of @State/@Binding/@Observable, view composition, performance
|
|
43
|
+
- **Safety**: force unwraps, force casts, retain cycles, unhandled optionals
|
|
44
|
+
- **Naming**: clarity, Swift API Design Guidelines compliance
|
|
45
|
+
- **Concurrency**: proper async/await, MainActor usage, data races
|
|
46
|
+
- **Tests**: coverage gaps for new/changed logic
|
|
47
|
+
|
|
48
|
+
### 4. Output format
|
|
49
|
+
|
|
50
|
+
```markdown
|
|
51
|
+
## Summary
|
|
52
|
+
|
|
53
|
+
<what changed in 1-2 sentences>
|
|
54
|
+
|
|
55
|
+
## Issues
|
|
56
|
+
|
|
57
|
+
<list issues with file:line, grouped by severity>
|
|
58
|
+
|
|
59
|
+
## SwiftLint
|
|
60
|
+
|
|
61
|
+
<summarize lint findings or "Clean">
|
|
62
|
+
|
|
63
|
+
## Suggestions
|
|
64
|
+
|
|
65
|
+
<actionable improvements>
|
|
66
|
+
|
|
67
|
+
## Verdict
|
|
68
|
+
|
|
69
|
+
Ready to push | Fix warnings first | Do not push
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 5. Rules
|
|
73
|
+
|
|
74
|
+
- Be direct. No filler, no praise for basic competence.
|
|
75
|
+
- Every issue must include the file and line number.
|
|
76
|
+
- If the diff is clean, say so — don't invent problems.
|
|
77
|
+
- Prioritize issues that would break production or cause bugs.
|
|
78
|
+
- Ignore generated files, Pods, and third-party code.
|