36 Commits
Author SHA1 Message Date
simonfrankenandClaude Sonnet 5.5 4ac29c04d0 Offer "quit and update" in the footer, check for updates on every start
The update check ran at most once a day and only printed a line after the
TUI exited. Now the registry is asked on every start (in the background,
3 s timeout, no cache) and the result reaches the TUI as a message.

With a newer release, the footer shows `q quit and update to vX` right of
help and quit, on a highlighted background (Theme.Highlight). `q` quits
and `main` installs the release once the terminal is restored, sharing
the code of `submarine update`; plain quit moves to `Q`, ctrl+c never
updates.

Drop LatestCached and CheckInterval. Add D27 and F58.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
v0.9.0
2026-10-23 18:43:17 +02:00
simonfrankenandClaude Sonnet 5.5 e8d366d88f Compare pins with a target branch and flag pins off it
The ref of a pin was the closest branch containing it, so a pin on a
feature branch looked like a perfect one, and the ref changed with the
branch checked out in the submodule. Split it in two:

- Pin.Target is the branch the pin should be on: `branch =` of
  .gitmodules (none if it does not exist locally), else the remote's
  default branch.
- Pin.Ref is shown: the target if the pin is on it, else the branch
  containing the pin with the fewest commits after it. A tie goes to the
  upstream of the checked-out branch, then to the first name; no branch is
  a favorite.

A pushed pin that is not on its target is `off target`, a new issue at the
new level Info (blue), counted in the header and below collapsed rows. The
spine shows the target's tip, labelled with its branch name, so the
distance from it is visible; a diverged one is the fork. The role words in
a spine label are styled separately and `you` is bold.

Add Status.TargetTip, TargetDrift and Bases.Target, Pin.Target and
Pin.Branches; drop RefSource.IsTarget. The detail pane's label column is 11
wide for the word `off target`.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
v0.8.0
2026-10-23 17:35:16 +02:00
simonfrankenandClaude Sonnet 5.5 f287d6009f Draw diverged commits on the spine as a fork
A tip, checkout or committed pin that has diverged from the pin gets its
own lane, which joins the main lane at a "base" node: the merge base of
the two, with its SHA and subject. Only one diverged commit is drawn as a
fork; with two, or without a merge base, the nodes stay in a line with
the "diverged" gap.

Add Status.Bases, filled by the git provider with `merge-base` for each
diverged commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 16:40:25 +02:00
simonfrankenandClaude Sonnet 5.5 4c1be3f9ab Show the pin, tip and checkout as a spine in the detail pane
Replace the Pinned/Checkout/Tip rows and the list of commits between pin
and tip with one line of history, newest on top: a node per commit with
its roles (pin, tip, you, staged, HEAD) and the number of commits between
two nodes. A staged pin shows the committed pin as its own node and notes
when it moves back. All three on one commit read "in sync".

Add Status.Drift, Committed and CommittedDrift and Status.Spine(); drop
Status.Between and Repo.FetchedAt. The fetch age moves from the header to
the detail pane (right-aligned in the name's line). The header summary is
right-aligned, ordered by importance and cut with an ellipsis if too narrow.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 16:34:14 +02:00
simonfrankenandClaude Opus 5.5 aeedfe96d4 Document restore and switch branch in the README, new screenshot
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 14:55:43 +02:00
simonfrankenandClaude Sonnet 5.5 01a9babe64 Add a release skill
Picks the next semver from the commits since the last tag, runs a release
check, tags and pushes, hands over to the developer for make release, then
creates the Gitea release with MCP.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
v0.7.0
2026-10-22 14:38:14 +02:00
simonfrankenandClaude Sonnet 5.5 648e1fd46c Support the mouse in the branch popup
A click on a branch switches to it, the wheel moves the selection, a click
outside closes the popup. The popup is hit-tested by its own position, as it
is drawn after zone.Scan.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 14:38:14 +02:00
simonfrankenandClaude Opus 5.5 2b6b781826 Add switch branch (b): stage the tip and restore to it
b opens a centered popup with a search line and the local and remote
branches of the selection. Enter stages the branch tip as the pin of a
submodule, restores it (and everything below) to it, and puts HEAD on the
branch; a remote branch gets a local tracking branch unless one exists
elsewhere. Works on the superproject too. Losses are asked like restore.
The backdrop behind the popup is dimmed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 14:18:49 +02:00
simonfrankenandClaude Opus 5.5 b9f6055759 Restore keeps staged pins; offer the committed pin for a staged one
Staged pins survive a restore at every depth and are the targets of the
submodules below. For a submodule with a staged pin, x asks whether to
restore to the staged or the committed pin, the latter unstaging it.
Staged pins of a repository that moves count as a loss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.6.0
2026-10-22 12:42:20 +02:00
simonfrankenandClaude Opus 5.5 7980d3356a Add restore (x): make a repository exactly what git records
Restores the selection and all submodules below it recursively, asking
only if work would be lost. May clone/fetch missing objects (D23).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:38:29 +02:00
simonfrankenandClaude Sonnet 5.5 a328d3cc3a Remove the unimplemented u and b keys
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 12:19:35 +02:00
simonfrankenandClaude Sonnet 5.5 a285a35c95 Fetch only on request, remove --no-fetch
Nothing is fetched on startup any more; the header and detail pane show
the last fetch and f fetches everything. Records the change as D22.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 12:13:01 +02:00
simonfrankenandClaude Sonnet 5.5 928e76154f Add problems-only view (!) and draft the write actions
`!` reduces the tree to submodules with issues (or unknown state) plus
their ancestors, reusing the filter's reduction. Adds docs/actions.md as
a draft spec for restore and stage pin.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 12:12:11 +02:00
simonfrankenandClaude Sonnet 5.5 72c060882b Make the superproject selectable via the header
The header stands for the root repository: it is selected on startup,
shows the root's own state word, and the detail pane shows its state
like a submodule's (HEAD vs. upstream, changes, unpushed, leftover
submodule clones as untracked repositories). Submodules are ignored in
the root's changes; their rows show them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 11:54:40 +02:00
simonfrankenandClaude Sonnet 5.5 f9838c0c99 Fix flaky TestWatch when a fetch is reported in two rounds
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
v0.5.0
2026-10-22 10:24:11 +02:00
simonfrankenandClaude Sonnet 5.5 651ab10f51 Use sub as the default command in the README
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 10:18:34 +02:00
simonfrankenandClaude Sonnet 5.5 e0f4ac823b Show staged pins in the tree
A staged pin was only visible in the detail pane, so the header's
"n pin staged" could not be traced to a row. The status column now shows
a blue "staged" instead of "in sync", a blue marker after an issue word,
and the marker on collapsed rows with a staged pin below. It is still not an
issue and not counted in +n.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 10:14:35 +02:00
simonfrankenandClaude Sonnet 5.5 83824e42c1 Add a README with a screenshot and examples
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 21:28:49 +02:00
simonfrankenandClaude Sonnet 5.5 f5fabfd093 Add / filter for the tree
A text input above the footer filters submodules by path (case-insensitive).
Matches keep their ancestors. A filtered-out selection moves to the next
visible submodule; esc closes the input and removes the filter.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
v0.4.0
2026-10-21 21:15:24 +02:00
simonfrankenandClaude Sonnet 5.5 8aa12774eb Resolve pins to the closest branch
A pin on a pushed feature branch showed as diverged from origin/main
because the .gitmodules branch won before any distance was compared.

Now the branch containing the pin with the fewest commits after it wins;
the .gitmodules branch, the upstream and the remote default branch only
break ties and are always compared. An unpushed pin compares just those
preferred refs by commits on either side.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
v0.3.0
2026-10-21 20:57:55 +02:00
simonfrankenandClaude Sonnet 5.5 5cb809428b Add c to copy the selected path, split key bar into groups
c copies the absolute directory of the selected submodule via OSC 52.
Keys are grouped into navigation (footer left), global (footer right)
and submodule (detail pane palette, footer if the pane can't show it).
Adds a key reference table to the UX guide.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 20:48:16 +02:00
simonfrankenandClaude Sonnet 5.5 90ea9cc241 Add shift+arrows for collapse/expand all
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
v0.2.0
2026-10-21 20:30:14 +02:00
simonfrankenandClaude Sonnet 5.5 e4340fb35e Show h/l, j/k and H/L in the key legend
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 20:29:47 +02:00
simonfrankenandClaude Sonnet 5.5 6cb7bd6967 Add H/L as aliases for collapse/expand all
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 20:27:27 +02:00
simonfrankenandClaude Sonnet 5.5 17151a578d Split hidden issue counts by level
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 20:21:00 +02:00
simonfrankenandClaude Sonnet 5.5 aec4ec6e3b Show failed loads as errors in the header
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 20:17:27 +02:00
simonfrankenandClaude Sonnet 5.5 e5fdc875af Count failed loads as danger in hidden issues
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 20:14:47 +02:00
simonfrankenandClaude Sonnet 5.5 af6d03a5b1 Mark untracked directories that are git repositories
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 20:12:13 +02:00
simonfrankenandClaude Sonnet 5.5 69d0f9b704 Don't count failed loads as loading; explain missing gitlink
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 20:09:59 +02:00
simonfrankenandClaude Sonnet 5.5 633de5aab1 Add install script, release Makefile and self-update
Releases are built locally (no runner) and uploaded to the Gitea generic
package registry. install.sh installs submarine plus a sub symlink;
`submarine update` replaces the binary after verifying its checksum.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
v0.1.0
2026-10-21 20:01:04 +02:00
Simon FrankenandClaude Opus 5.5 6e1236d495 Show a muted "in sync" for submodules without issues
An empty status cell could be read as missing data. The muted word
confirms the check ran without drawing attention. The word is defined
once in the theme and shared with the detail pane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:18:17 +02:00
Simon FrankenandClaude Opus 5.5 27f0c0b5d2 Fetch in the background and follow changes in the repositories
- On startup, after the local state is shown, fetch the superproject and
  every initialized submodule (all remotes, prune, at most 8 at once).
  Fetches never prompt; failures show git's message. --no-fetch skips it,
  f fetches again.
- The header shows "fetching n/m", then the fetch age and failures; the
  detail pane shows each submodule's last fetch and fetch errors.
- The git provider polls each repository's metadata (refs, HEAD, index,
  .gitmodules) every 2 s and reports changes. The app reloads only what
  changed, keeping old values, expansion and cursor; loads are
  single-flight with coalesced reruns. Refresh is driven by the
  repositories, so a fetch in another shell updates the view like ours.
- r reloads everything (worktree edits are not watched).
- Docs: F50-F53, N1/N8 rewritten, D18-D20 (D12 superseded).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:15:22 +02:00
Simon FrankenandClaude Opus 5.5 19c6f17eb2 Preview uncommitted changes in the detail pane
For a dirty submodule, list the changed files so the user can remember
what they were doing there:

  Changes   5 files · 2M 1A 1D 1R · +37 −12
            ──────────────────────────────────
            M  render.go  src/render/  +20 −4  3d ago
            ?  scratch.txt                     5m ago

- Status.Changes: files changed against HEAD (staged and unstaged
  combined) with kind, line counts and modification time; sorted by kind,
  then by size; capped at 200 with totals over all files.
- Git provider: parse `status --porcelain=v2 -z` and one
  `diff HEAD --numstat -z -M`, only for dirty submodules.
- Detail pane: flat list with a colored status letter per file, like IDE
  source-control views; counts and age in compact columns; up to 10 files,
  every kind keeps a line, the rest is summarized as "n more".
- Fixture gains libs/work with every kind of change; mock data extended.
- Docs: F24, change preview in the UX guide, D17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 15:55:46 +02:00
Simon FrankenandClaude Opus 5.5 64fe34b5b0 Add read-only git provider
Implement submodule.Provider with the git CLI so submarine reads real
repositories: `submarine [dir]`, with `--mock` for the built-in data.

- Tree reads .gitmodules recursively; Status derives presence, checkout,
  worktree, staged pin, pushed state and pin -> ref resolution from local
  git commands only (no network).
- Concurrent Status calls are bounded by runtime.NumCPU().
- scripts/make-fixture.sh builds a superproject covering every state; the
  integration tests run against it.
- theme.Ago shows "never" for a zero time.
- Docs updated; decisions D14-D16 added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 15:32:21 +02:00
Simon Franken c84f34ee2b ignore debug files 2026-10-21 15:10:58 +02:00
Simon FrankenandClaude Opus 5.5 2212b24fe1 Add submarine TUI prototype with mock data
Bubble Tea prototype for browsing repositories with many nested git
submodules: collapsible tree, pins shown as ref + distance, summarized
issue words with severity levels, detail pane, async loading via a
Provider interface backed by mock data. Includes docs/ with
requirements, state model, UX guide, architecture and decision log,
plus AGENTS.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 14:59:53 +02:00