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
20 changes: 20 additions & 0 deletions .claude/agents/senior-rust-engineer.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ model: sonnet

You are a senior Rust engineer with deep expertise in TUI development using ratatui and async programming with tokio. You are building **forge** — a terminal-native API client (Postman in the terminal). Read `SPEC.md` for the full spec and `CLAUDE.md` for project conventions before writing code.

---

## Pre-Implementation Reasoning (MANDATORY)

**Before writing any code**, run the reasoning skill checklist at `.claude/skills/reasoning/SKILL.md`.

Work through every section that applies to your task:

1. **Data Model** — new/changed structs, enum variants, serde defaults, exhaustive matches
2. **Lifecycle/Sync** — when created, modified, flushed, destroyed, loaded on restart
3. **Idempotency/Dedup** — what if triggered twice? search before push
4. **Inverse Operations** — every open/create needs a matching close/delete/save
5. **UI Completeness** — focus cycle, keybind hints, empty state, overflow/scroll
6. **State Coherence** — clamp indices after mutations, invalidate caches
7. **"Who Else Touches This?" Audit** — grep every symbol you change; categorize by create/read/update/delete/persist/display

Answer each relevant question (one-liner is enough) before touching a single source file. Unanswered questions = gaps that will become bugs.

---

## Core Principles

- **Correctness first**: safe Rust, no `unwrap()` in production paths — use `?` and proper error types
Expand Down
5 changes: 4 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
"Bash(echo No tokio-util in lock yet:*)",
"WebFetch(domain:docs.rs)",
"Bash(cd \"C:\\\\Users\\\\alber\\\\Desktop\\\\forge\" && cargo check 2>&1)",
"Bash(ls:*)"
"Bash(ls:*)",
"Bash(xargs grep:*)",
"Bash(cargo check:*)",
"Bash(cargo test:*)"
]
},
"enableAllProjectMcpServers": true,
Expand Down
134 changes: 134 additions & 0 deletions .claude/skills/reasoning/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
---
name: reasoning
description: >
Self-questioning checklist to run before implementing any feature.
Use to surface edge cases, lifecycle gaps, persistence requirements,
dedup needs, and UI completeness issues before writing code.
Invoke mentally (or literally write answers) before starting implementation.
---

# Feature Reasoning Checklist

Before writing code, work through these question groups out loud.
Answer each one briefly — a one-liner is enough. Unanswered questions = gaps in the plan.

---

## 1. Data Model Questions

*Trigger: adding or changing any struct field, enum variant, or type.*

- Does this new field need to be **persisted** (saved to disk / TOML)?
→ If yes: add `#[serde(default)]` so old files still load.
- Are there **struct literal initializations** (`Struct { field: val, ... }`) elsewhere
that will now fail to compile?
→ Search for every place the struct is constructed by name.
- Does the `Default` impl need updating?
- Do any `Clone`, `PartialEq`, or `Display` impls need to handle the new field?
- If I added an enum variant: are there `match` statements on this enum elsewhere?
→ Every exhaustive match must get a new arm.

---

## 2. Lifecycle / Sync Questions

*Trigger: any feature that creates, modifies, or destroys stateful data.*

- **When is this data created?** (startup, user action, event?)
- **When is it modified?** (every keystroke, on submit, on navigate-away?)
- **When should it be flushed/saved?** Never assume "it's saved automatically."
→ List every code path that changes it; each path needs a save call or a sync point.
- **When is it destroyed?** (tab close, workspace switch, app exit?)
→ Every destruction path must flush state first.
- What happens on **app restart**? Is the state loaded back correctly?
→ Test: create → close app → reopen → is state intact?

---

## 3. Idempotency / Dedup Questions

*Trigger: any feature that opens, creates, or adds something.*

- What happens if the user **triggers this action twice** in a row?
→ Should it be a no-op, an error, or create a second item?
- Is there an **existing open instance** that should be re-focused instead?
→ Search open_tabs / active list before pushing a new item.
- What if the **item already exists** with the same identity?
→ Define identity: is it by ID, by name, by position?

---

## 4. Inverse / Symmetric Operations

*Trigger: any "open", "create", "start", or "enable" action.*

For every action X, list its inverses and verify each handles the new state:

| Action added | Inverses to check |
|---|---|
| open tab | close tab, switch tab, switch workspace, app exit |
| create item | delete item, rename item, duplicate item |
| add field | all constructors, all serialization round-trips |
| enable feature | disable feature, reset to default |

**Every entry point needs a matching exit point that cleans up / persists.**

---

## 5. UI Completeness Questions

*Trigger: any new visible element (widget, panel, row, popup, indicator).*

- Should this element be **keyboard-navigable**?
→ If yes: add a `Focus` variant, update `next()`/`prev()`, add visual indicator.
- Does it need a **direct shortcut key** (number, letter)?
- Does it need **keybinding hints** in a status bar or hint footer?
- What happens when the element is **empty** (zero items, blank state)?
→ Render a placeholder; don't panic on `list[0]`.
- What happens when the element is **out of screen bounds** (many items, small terminal)?
→ Clamp scroll_offset; ensure cursor stays visible.

---

## 6. Related State Coherence

*Trigger: any action that adds or removes items from a list, or changes active indices.*

- After this action, are all **indices still valid**?
→ active_tab_idx, cursor, scroll_offset — clamp them after mutations.
- Are there **other state fields** that reference the mutated data by index or ID?
→ Update or invalidate caches (e.g. highlighted_body, selected row).
- Does any **other component render** based on the data I changed?
→ Read render functions that touch the same state; ensure they handle new shape.

---

## 7. The "Who Else Touches This?" Audit

*Run this for every struct, field, or function you modify.*

1. Search the codebase for all uses of the symbol.
2. Categorize: create / read / update / delete / display / persist / test.
3. For each category: does my change break or require updating that site?

```
Symbol: CollectionRequest
create: CollectionRequest::new(), struct literal in sidebar_duplicate → needs url/body_raw
read: flatten_tree(), find_col_request_by_id() → fine, reads by field
update: update_col_request_state() → needs url/body_raw
persist: save_collection_meta() → handled by serde
open: handle_sidebar_enter() → needs to load url/body_raw back
close: close_active_tab() → needs to sync url/body_raw first
```

---

## Checklist (run before every implementation)

- [ ] Listed all struct literals for modified structs → all compile?
- [ ] Named every lifecycle stage (create / modify / destroy) and wired save/load
- [ ] Checked for dedup: what if this is triggered twice?
- [ ] Verified all inverses (close, delete, switch) handle the new state
- [ ] If new UI element: added to Focus cycle, visual indicator, hint bar
- [ ] Indices/cursors clamped after any list mutation
- [ ] "Who else touches this?" audit complete — no missed call sites
47 changes: 46 additions & 1 deletion PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

- [x] Round 1 — Core Request Engine
- [x] Round 2 — Environment Variables (partly - [more details here](SPEC.md#implementation-tasks-1))
- [ ] Round 3 — Collections & Workspaces
- [x] Round 3 — Collections & Workspaces
- [ ] Round 4 — Authentication
- [ ] Round 5 — Request Headers & Query Params (Done Partly)
- [ ] Round 6 — Request Body Editor (Done Partly)
Expand Down Expand Up @@ -124,6 +124,51 @@

---

## Round 3 — Collections & Workspaces ✓

### Files Implemented (13 new/major files)

| Layer | Files |
|---|---|
| State | `state/collection.rs`, `state/workspace.rs`, `state/app_state.rs` (major migration) |
| Storage | `storage/workspace.rs`, `storage/collection.rs` |
| UI | `ui/sidebar.rs` (full rewrite), `ui/request_tabs.rs`, `ui/naming_popup.rs`, `ui/confirm_delete.rs`, `ui/workspace_switcher.rs` |
| App Logic | `app.rs` (sidebar CRUD, tab management, workspace switching) |

### Architecture

- **AppState migration**: `state.request`/`state.response` → `state.workspace.open_tabs[active_tab_idx]`; accessed via `state.active_tab()` / `state.active_tab_mut()`
- **Environments moved**: `state.environments` → `state.workspace.environments`; `state.active_env_idx` → `state.workspace.active_environment_idx`
- **Storage path**: `%APPDATA%/forge/workspaces/<ws-name>/` (Windows) / XDG / macOS equivalents
- **Sidebar tree**: `SidebarNode` enum flattened to a list via `flatten_collections()`; collapsed node IDs tracked in `sidebar.collapsed_ids: HashSet<Uuid>`
- **Tabs**: `WorkspaceState.open_tabs: Vec<RequestTab>`; `active_tab_idx` tracks focus; tabs persist to `workspace.toml`

### Keybindings Added

| Key | Action |
|---|---|
| `Ctrl+W` | Workspace switcher popup |
| `Ctrl+n` (Sidebar) | New collection |
| `n` (Sidebar) | New request |
| `f` (Sidebar) | New folder |
| `r` (Sidebar) | Rename selected item |
| `d` (Sidebar) | Delete selected item |
| `D` (Sidebar) | Duplicate selected item |
| `h` / `l` (Sidebar) | Collapse / expand node |
| `/` (Sidebar) | Toggle search mode |
| `Alt+1–9` | Switch to tab N |
| `Alt+w` | Close active tab |
| `[` / `]` | Cycle open tabs (non-UrlBar focus) |

### Gotchas & Fixes

- Sidebar search runs inline at the footer row (repurposed hint row); `NamingState` carries the HTTP method for new requests so method persists through the naming popup flow
- `active_tab()` returns `Option<&RequestTab>` — all render functions must handle `None` gracefully
- Workspace save triggered on every tab/request mutation via `dirty` flag; debounced via the Tick event
- `flatten_collections()` recurses into folders and respects `collapsed_ids` to hide children

---

## Round 5 — Request Headers & Query Params (Done Partly)

### What's Implemented
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Every request, collection, environment, and workspace is a human-readable TOML f

- [x] **Round 1** - Core Request Engine (URL bar, HTTP executor, response viewer, syntax highlighting)
- [x] **Round 2** - Environment Variables (`{{variable}}` interpolation, env switcher, secret vars) (partly - [more details here](SPEC.md#implementation-tasks-1))
- [ ] **Round 3** - Collections & Workspaces (sidebar tree, tabs, file persistence)
- [x] **Round 3** - Collections & Workspaces (sidebar tree, tabs, file persistence)
- [ ] **Round 4** - Authentication (Basic, Bearer, API Key, OAuth 2.0, Digest)
- [ ] **Round 5** - Request Headers & Query Params (key-value editors, autocomplete, bidirectional URL sync)
- [ ] **Round 6** - Request Body Editor (JSON, Form, Multipart, GraphQL, Raw, Binary)
Expand Down
18 changes: 9 additions & 9 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -908,15 +908,15 @@ pub struct RequestTab {

### Implementation Tasks

- [ ] Implement sidebar tree widget with collapse/expand
- [ ] Implement collection CRUD (new, rename, delete, duplicate)
- [ ] Implement folder support inside collections
- [ ] Implement request tabs in main panel
- [ ] Implement workspace switcher popup
- [ ] Implement sidebar fuzzy search
- [ ] Implement file persistence for collections (TOML files)
- [ ] Implement auto-save on request modification
- [ ] Implement workspace-level CRUD
- [x] Implement sidebar tree widget with collapse/expand
- [x] Implement collection CRUD (new, rename, delete, duplicate)
- [x] Implement folder support inside collections
- [x] Implement request tabs in main panel
- [x] Implement workspace switcher popup
- [x] Implement sidebar fuzzy search
- [x] Implement file persistence for collections (TOML files)
- [x] Implement auto-save on request modification
- [x] Implement workspace-level CRUD
- [ ] Display unsaved indicator (`*`) on dirty requests/tabs

---
Expand Down
Loading