Skip to content
Merged
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
181 changes: 181 additions & 0 deletions .opencode/skills/feature-branch-pr/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
---
name: feature-branch-pr
description: Automate Git workflow: create feature branch from main/master, commit changes with auto-generated messages, and open PRs with auto-generated descriptions. Use when user wants to commit changes, create branches, or open PRs.
---

Automate the Git/GitHub workflow: create feature branches, commit changes with auto-generated messages, and open PRs with auto-generated descriptions.

## Process

### 1. Detect current branch

Run `git branch --show-current` to get the current branch name.

- If on `main` or `master` → proceed to step 2 (create feature branch)
- If on a feature branch → skip to step 3 (stage and commit)

**Completion criterion:** Current branch name captured and decision made.

### 2. Create feature branch (if on main/master)

1. Check for uncommitted changes: `git status --porcelain`
2. If changes exist, ask user: "You're on main/master with uncommitted changes. Create a feature branch first?"
3. Ask user: "What's the feature name?" (e.g., "add-login-page")
4. Create and switch to feature branch: `git checkout -b feature/<name>`
5. Display: "Created and switched to feature/<name>"

**Completion criterion:** On feature branch with name confirmed.

### 3. Stage changes

1. Run `git status` to check for changes
2. If no changes → display "No changes to commit" and stop
3. Stage changes: `git add .`
4. Confirm staging: `git diff --cached --stat`
5. If no staged changes → display "No changes to commit" and stop

**Completion criterion:** Changes staged and ready to commit.

### 4. Generate commit message

Analyze staged changes with `git diff --cached` to generate a commit message following conventional commits format:

**Type detection:**
- New files only → `feat`
- Modified files → Analyze changes:
- Documentation (*.md, *.txt) → `docs`
- Tests (*test*, *spec*) → `test`
- Config files → `chore`
- Code logic changes → `fix` or `feat` based on scope
- Code formatting/style → `style`
- Code restructuring → `refactor`
- Deleted files → `refactor` or `feat`

**Scope detection:**
- Use the common directory prefix of changed files
- If files span multiple directories, use the most prominent one
- Omit scope if changes are too diverse

**Description rules:**
- Imperative mood ("add" not "added" or "adds")
- Lowercase first letter
- No period at end
- Keep under 50 characters
- Focus on *what* changed, not *how*

**Example:** `feat(auth): add login page with OAuth support`

### 5. Commit changes

1. Run `git commit -m "<generated message>"`
2. Verify commit: `git log -1 --oneline`
3. Display: "Committed: <message>"

**Completion criterion:** Changes committed successfully.

### 6. Ask about PR

Ask user: "Do you want to open a PR? (yes/no)"

- If no → display "Done! Changes committed to feature/<name>" and stop
- If yes → proceed to step 7

**Completion criterion:** User decision received.

### 7. Detect base branch

1. Check if `main` exists: `git branch --list main`
2. Check if `master` exists: `git branch --list master`
3. If both exist, ask user which to use as base
4. If only one exists, use that one
5. If neither exists, ask user for base branch name

**Completion criterion:** Base branch identified.

### 8. Generate PR description

Analyze the feature branch changes to generate a PR description:

1. Get commit history: `git log <base-branch>..HEAD --oneline`
2. Get diff summary: `git diff <base-branch>...HEAD --stat`
3. Get full diff: `git diff <base-branch>...HEAD`

**Generate description with:**

```markdown
## Summary
[1-2 sentences describing overall purpose of changes]

## Changes
- [Bullet point for each significant change, derived from commits and diff]

## Testing
- [How to test these changes, if discernible from code]
- [Any test files added/modified]

## Related
[List of commits from feature branch]
```

**Completion criterion:** PR description generated.

### 9. Create PR

1. Check gh CLI is available: `gh --version`
2. Check authentication: `gh auth status`
3. If not authenticated → display instructions and stop
4. Check for existing PR: `gh pr list --head feature/<name>`
5. If PR exists → display existing PR URL and stop
6. Create PR:
```bash
gh pr create \
--base <base-branch> \
--head feature/<name> \
--title "<commit message>" \
--body "<generated description>"
```
7. Extract PR URL from output
8. Display: `🚀 your PR is opened take a look: <PR_URL>`

**Completion criterion:** PR created and URL displayed.

## Edge Cases

- **No changes to commit:** Stop with message "No changes to commit"
- **gh CLI missing:** Display "GitHub CLI (gh) is not installed. Install from https://cli.github.com/"
- **gh not authenticated:** Display "Run 'gh auth login' to authenticate with GitHub"
- **PR already exists:** Display "PR already exists: <URL>"
- **Empty diff after commits:** Don't create PR, display message
- **User cancels:** Gracefully stop at any prompt with "Operation cancelled"

## Examples

### Scenario 1: On main, create new feature
```
$ git branch --show-current
main

→ You're on main. What's the feature name?
user: add-dark-mode

→ Created and switched to feature/add-dark-mode
→ Changes staged: 3 files changed, 45 insertions(+), 12 deletions(-)
→ Committed: feat(ui): add dark mode toggle
→ Do you want to open a PR? (yes/no)
user: yes

→ 🚀 your PR is opened take a look: https://github.com/user/repo/pull/42
```

### Scenario 2: On feature branch, commit directly
```
$ git branch --show-current
feature/add-login

→ Changes staged: 2 files changed, 28 insertions(+), 5 deletions(-)
→ Committed: feat(auth): implement login form validation
→ Do you want to open a PR? (yes/no)
user: no

→ Done! Changes committed to feature/add-login
```
136 changes: 136 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# AGENTS.md

`[opencode] AGENTS.md loaded`

## Project Overview

TodoApp is a sample Flutter project built as a testing playground. It manages
checklists and tasks using sqflite for local persistence. Targets Android, iOS,
and macOS. Uses flutter_bloc (Cubit) for state management, get_it + injectable
for DI, auto_route for navigation, freezed for immutable models, and supports
English/Portuguese localization.

## Dev Environment Setup

1. Install Flutter 3.38.x (see `.github/actions/setup-flutter/action.yml` for
the pinned version)
2. `flutter pub get`
3. `dart run build_runner build --delete-conflicting-outputs`
4. `flutter gen-l10n`

Steps 3 and 4 must be re-run after any changes to annotated classes
(`*.freezed.dart`, router config, injectable config) or `.arb` localization
files.

## Project Architecture

### Layer-first layout

```
lib/
data/ — models (freezed), DAOs (sqflite), repository, share handler
domain/ — sort & summary helpers
ui/ — screens (features), shared components, widgets, l10n
util/ — DI (get_it + injectable), navigation provider
```

### Key patterns

- **State management:** flutter_bloc (Cubit) with freezed immutable states.
Cubits are `@Injectable()` and used via `BlocProvider`/`BlocBuilder`.
- **DI:** get_it + injectable with code-gen. Initialized at startup via
`GetItStartupHandlerWrapper`.
- **Navigation:** auto_route (generated router). Abstracted behind
`NavigatorProvider` interface for testability.
- **Data:** Abstract repository → Impl → DAO pattern. The Abstract + Impl
convention is also used for `NavigatorProvider`, `ShareMessageHandler`, and
`TaskListSortHelper`.

### Code generation

Generated files (`*.freezed.dart`, `*.config.dart`, `*.gr.dart`,
`app_localizations*.dart`) are gitignored. Generated on demand via
`build_runner`.

## Coding Conventions

### Naming

- Files: `snake_case` (enforced by `file_names` lint)
- Classes/widgets: `PascalCase`
- Methods/variables: `camelCase`
- Abstract interfaces: plain name (e.g. `TodoRepository`); implementation:
name + `Impl`

### Imports

- Always use package imports (`package:todoapp/...`), never relative (enforced
by `always_use_package_imports` and `avoid_relative_lib_imports`)
- Directives ordered as: `dart:` → `package:` → project (enforced by
`directives_ordering`)

### Style

- Single quotes (enforced)
- `const` constructors wherever possible (enforced)
- Lines max 80 chars (enforced by `lines_longer_than_80_chars`)
- Curly braces required on all flow control (enforced by
`curly_braces_in_flow_control_structures`)
- Always declare return types (enforced by `always_declare_return_types`)

### Architecture rules (eagle_eye)

- `data/model/*` must have zero external dependencies
- `*viewmodel.dart` must not depend on `*_screen.dart`
- `util/*_provider.dart` and `util/*_handler.dart` must have zero external
dependencies

## Testing

### Running tests

```
flutter test
```

Code generation (`build_runner` + `flutter gen-l10n`) must complete before
tests pass.

### Organization

Tests mirror `lib/` structure under `test/` (layer-first).

### Patterns

- **Domain/logic tests:** Pure Dart, no widget testing. Use
Arrange/Act/Assert with real implementations (no mocking framework).
- **ViewModel (Cubit) tests:** Use `FakeRepository` (in-memory) from
`test/test_utils/fakes/`. Test initial state, then state transitions after
method calls.
- **Widget tests:** Wrap with `WidgetsUtil.buildMaterialAppWidgetTest()`
(sets up MaterialApp + localization delegates, forced English locale). Use
`testWidgets()`, `pumpWidget()`, `find.byKey()`, `find.text()`.

### Test doubles

Hand-written fakes in `test/test_utils/fakes/` (no mockito):
`FakeRepository`, `FakeNavigatorProvider`, `FakeCallbacks`, `FakeStates`.

## PR & CI

### PR checks (`.github/workflows/pr.yml`)

Triggered on PRs to `main`. Runs on every PR:

1. `flutter gen-l10n` — generate localizations
2. `dart run eagle_eye:main` — check architecture violations
3. `flutter test` — run test suite
4. `dart analyze` — static analysis
5. `flutter build apk --debug` — verify build compiles

All steps must pass before merging.

### Release (`.github/workflows/release_flutter_app.yaml`)

Triggered on any tag push. Builds APK (uploaded to Firebase App Distribution)
and macOS `.app.zip` (attached to GitHub Release).