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
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: CI

on:
push:
branches:
- main
- dev
pull_request:

jobs:
rust-checks:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

- name: Cache cargo registry and build artifacts
uses: Swatinem/rust-cache@v2

- name: cargo fmt --check
run: cargo fmt --check

- name: cargo clippy -- -W clippy::pedantic
run: cargo clippy -- -W clippy::pedantic

- name: cargo test
run: cargo test
71 changes: 39 additions & 32 deletions PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,49 @@

- Pending:

- AI/ML foundations: commit message suggestions MVP.
- [x] Inventory current commit flow and integration points.
- [x] Identify data sources for suggestions (staged diff, status, branch name).
- [x] Decide on rule-based heuristic engine (no ML for MVP).
- Phase 1: Core Infrastructure
- [x] Create `src/suggestions/` module (mod.rs, engine.rs, rules.rs, diff.rs, context.rs)
- [x] Define `CommitSuggestion` struct (type, scope, message, confidence)
- [x] Define `SuggestionEngine` trait for testability
- [x] Extend `AppSettings` with suggestion config (enabled, max_suggestions, max_length)
- [x] Extend `ChangesState` with suggestions list and selected index
- Phase 2: Suggestion Engine Implementation
- [x] Implement diff analysis: extract files, scopes, change types
- [x] Implement text normalization: strip binaries, limit size, redact secrets
- [x] Implement branch context: extract issue keys from branch names
- [x] Implement rule-based type detection (file extension → commit type)
- [x] Implement suggestion generation with ranking/deduplication
- Phase 3: UI Integration
- [x] Update `ChangesPage` to render suggestions panel
- [x] Add suggestion keybindings (1-3 to accept, or Tab+Enter pattern)
- [x] Wire suggestion generation to staged changes changes
- [x] Allow editing after accepting suggestion
- Phase 4: Configuration & Polish
- [x] Add settings page options for suggestions
- [x] Add fallback messaging when no suggestions available
- [x] Add performance caching
- Phase 5: Testing & Documentation
- [x] Add unit tests for rules, diff analysis, engine
- [x] Add integration test for suggestion flow
- [x] Update docs and CHANGELOG
- Docs: video tutorials.
- Docs: use case examples.
- Docs: architecture deep-dive.
- Stage: Pre-Usable Release (stabilization + onboarding)
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- Pending: is now an empty list item, and - Stage: Pre-Usable Release ... is a separate top-level bullet. If the stage is meant to be part of the Pending section, indent it under Pending:; otherwise consider removing the Pending: header to avoid an empty section.

Suggested change
- Stage: Pre-Usable Release (stabilization + onboarding)
- Stage: Pre-Usable Release (stabilization + onboarding)

Copilot uses AI. Check for mistakes.
- 1) Reliability and correctness (must pass before release)
- [x] Add CI pipeline for `cargo fmt --check`, `cargo clippy -- -W clippy::pedantic`, and `cargo test`.
- [x] Expand integration coverage for branch flows (create/switch/delete/track remote branches).
- [ ] Add integration tests for stash/cherry-pick conflict recovery paths.
- [ ] Add regression tests for commit suggestion acceptance and manual edit flow.
- [ ] Audit and remove panic-prone `unwrap()`/`expect()` usage in runtime UI paths.
- 2) UX hardening for daily use
- [ ] Add clear empty/loading/error states across all pages (dashboard, changes, branches, stash, merge).
- [ ] Standardize success/error notifications in status bar with actionable recovery hints.
- [ ] Add destructive action confirmations (drop stash, delete branch, hard reset-like actions if exposed).
- [ ] Improve keyboard discoverability with contextual key hints per page.
- 3) Configuration and persistence
- [ ] Finalize default keymap profile and document overrides.
- [ ] Validate settings/keybindings schema on startup with user-friendly diagnostics.
- [ ] Add migration handling for future `.forge/` data format changes.
- 4) Packaging and install readiness
- [ ] Add release build profile checks and size/perf sanity benchmark pass.
- [ ] Provide install paths for Linux/macOS (binary download + cargo install instructions).
- [ ] Add shell completion generation/install instructions if supported.
- 5) Documentation required for first-time usability
- [ ] Publish a 5-minute quickstart workflow in `README.md` (init, stage, commit, branch, sync).
- [ ] Add troubleshooting decision tree for common failures (auth, merge conflicts, detached HEAD, lock files).
- [ ] Document commit suggestion behavior, limits, and config knobs in wiki + README.
- [ ] Create short terminal GIF/video walkthroughs for core workflows.
- 6) Release gate (definition of "maybe usable")
- [ ] Run dogfood pass on at least 3 real repos for one week and log friction items.
- [ ] Close all P0/P1 bugs found in dogfood pass.
- [ ] Tag `v0.1.0-beta` once reliability + docs + onboarding checklist is complete.
- 7) Additional tasks from README/wiki audit
- [ ] Implement rebase workflows (start/continue/abort) with conflict routing into Merge view.
- [ ] Add tag management (list/create/delete lightweight + annotated tags).
- [ ] Expand async task coverage beyond remote ops to prevent UI blocking on long Git operations.
- [ ] Add bulk file operations in Changes view (stage all / unstage all shortcuts).
- [ ] Add E2E UI workflow tests (tab navigation, staging/commit, merge resolution, remote ops).
- [ ] Add optional theme customization support (beyond built-in themes) or document deferral explicitly.
- [ ] Complete docs improvements tracked in roadmap: video tutorials, use case examples, architecture deep-dive.
- [ ] Run docs consistency pass to remove stale statements (implemented vs not implemented/limitations mismatch).


- Completed:

- AI/ML foundations: commit message suggestions MVP.
- Testing: expand integration tests and add UI workflow coverage.
- Repo health UX: surface recovery actions inline for common Git failures.
- Remote branches: tracking + switch/manage remote-only branches.
Expand Down
126 changes: 126 additions & 0 deletions tests/git_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,132 @@ fn fixture_helpers_create_branch() {
assert!(!branch.is_head());
}

#[test]
fn create_branch_appears_in_list_branches() {
let fixture = RepoFixture::new().expect("fixture init failed");
fixture
.commit_file("foo.txt", "hello", "initial")
.expect("commit failed");

let client = GitClient::discover(fixture.path()).expect("discover failed");
client
.create_branch("feature")
.expect("create branch failed");

let branches = client
.list_branches(true, false)
.expect("list branches failed");
assert!(branches.iter().any(|(name, _)| name == "feature"));
}

#[test]
fn checkout_branch_switches_head_branch() {
let fixture = RepoFixture::new().expect("fixture init failed");
fixture
.commit_file("foo.txt", "hello", "initial")
.expect("commit failed");

let client = GitClient::discover(fixture.path()).expect("discover failed");
client
.create_branch("feature")
.expect("create branch failed");
client.checkout_branch("feature").expect("checkout failed");

let current = client.head_branch().expect("head branch missing");
assert_eq!(current, "feature");
}

#[test]
fn delete_branch_removes_local_branch() {
let fixture = RepoFixture::new().expect("fixture init failed");
fixture
.commit_file("foo.txt", "hello", "initial")
.expect("commit failed");

let client = GitClient::discover(fixture.path()).expect("discover failed");
client
.create_branch("feature")
.expect("create branch failed");
client
.delete_branch("feature")
.expect("delete branch failed");

assert!(
fixture
.repo()
.find_branch("feature", BranchType::Local)
.is_err(),
"feature branch should not exist after deletion"
);
}

#[test]
fn list_branches_with_upstream_shows_tracked_remote_branch() {
let fixture = RepoFixture::new().expect("fixture init failed");
fixture
.commit_file("foo.txt", "hello", "initial")
.expect("commit failed");

let client = GitClient::discover(fixture.path()).expect("discover failed");
client
.create_branch("feature/local")
.expect("create branch failed");

fixture
.repo()
.remote("origin", "https://example.invalid/forge.git")
.expect("create remote failed");

let head_oid = fixture
.repo()
.head()
.expect("head missing")
.target()
.expect("head target missing");

fixture
.repo()
.reference(
"refs/remotes/origin/feature/local",
head_oid,
true,
"create remote tracking ref",
)
.expect("create remote tracking ref failed");

let mut local_branch = fixture
.repo()
.find_branch("feature/local", BranchType::Local)
.expect("local branch missing");
local_branch
.set_upstream(Some("origin/feature/local"))
.expect("set upstream failed");

let upstream = client
.get_upstream_branch("feature/local")
.expect("get upstream failed");
assert_eq!(upstream.as_deref(), Some("origin/feature/local"));

let branches = client
.list_branches_with_upstream(true, true)
.expect("list branches with upstream failed");

let local = branches
.iter()
.find(|(name, _is_current, is_remote, _upstream)| name == "feature/local" && !*is_remote)
.expect("local feature branch missing");
assert_eq!(local.3.as_deref(), Some("origin/feature/local"));

assert!(
branches
.iter()
.any(|(name, _is_current, is_remote, _upstream)| {
name == "origin/feature/local" && *is_remote
}),
"remote tracking branch should be listed"
);
}

#[test]
fn stash_apply_keeps_entry_and_restores_changes() {
let fixture = RepoFixture::new().expect("fixture init failed");
Expand Down
Loading