cli: just gui runs the viewer in the background; gui-fg, gui-status, [viewer] open_at_start

The scaffolded justfile now detaches the viewer (log in books-data/gui.log)
and won't open a second one for the same books. AGENTS.md tells the agent
to check gui-status at session start and follow open_at_start
(ask/always/never) in wanderbooks.toml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DeBdcyb3YyRkcpicBZH5wh
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-12 19:38:33 -06:00
1 parent 024c9896f2
commit b38c9e39cc
2 files changed
+37 -4

No files matched your search

+33 -2
View File
@@ -60,9 +60,24 @@ codex:
codex-setup: codex-setup:
codex mcp add "@@CODEX_NAME@@" -- "@@LEDGER_MCP@@" --data-dir "{{justfile_directory()}}/books-data" codex mcp add "@@CODEX_NAME@@" -- "@@LEDGER_MCP@@" --data-dir "{{justfile_directory()}}/books-data"
# Open the live, read-only view of these books # Open the live, read-only view of these books (in the background; once per books dir)
gui: gui:
"@@LEDGER_GUI@@" --data-dir books-data --books-toml wanderbooks.toml #!/usr/bin/env bash
set -u
if just gui-status &>/dev/null; then echo "The viewer is already open."; exit 0; fi
log="{{justfile_directory()}}/books-data/gui.log"
nohup "@@LEDGER_GUI@@" --data-dir "{{justfile_directory()}}/books-data" \
--books-toml "{{justfile_directory()}}/wanderbooks.toml" >"$log" 2>&1 </dev/null &
sleep 1
if kill -0 $! 2>/dev/null; then echo "Viewer open (pid $!)."; else echo "The viewer didn't start:"; cat "$log"; exit 1; fi
# Open the viewer in the foreground (logs in the terminal; Ctrl-C closes it)
gui-fg:
"@@LEDGER_GUI@@" --data-dir "{{justfile_directory()}}/books-data" --books-toml "{{justfile_directory()}}/wanderbooks.toml"
# Is the viewer open for these books? Prints open/closed; exits 0 only if open
gui-status:
@pgrep -f -- "ledger-gui --data-dir {{justfile_directory()}}/books-data( |$)" >/dev/null && echo open || { echo closed; exit 1; }
# Commit the journal: the git history of this dir is the archive # Commit the journal: the git history of this dir is the archive
backup: backup:
@@ -169,6 +184,14 @@ Bank feeds aren't automatic here: import the bank's CSV export
them read access) and open it with `wanderbooks gui`, or `ledger-gui them read access) and open it with `wanderbooks gui`, or `ledger-gui
--data-dir books-data`. Nothing they do there can change the books. --data-dir books-data`. Nothing they do there can change the books.
## Session start
Run `just gui-status`. If the viewer isn't open, follow `[viewer]
open_at_start` in `wanderbooks.toml`: `"ask"` (the default) offers to open
it, `"always"` opens it with `just gui`, `"never"` leaves it. `just gui`
starts it in the background and does nothing if it's already open. If the
human tells you their preference, write it there.
## Ongoing ## Ongoing
- Every transaction: draft, preview, confirm with the human, post, attach - Every transaction: draft, preview, confirm with the human, post, attach
@@ -203,6 +226,12 @@ fiscal_year_start = "01-01"
data_dir = "books-data" data_dir = "books-data"
accounting_basis = "accrual" accounting_basis = "accrual"
receipt_threshold = "25.00" receipt_threshold = "25.00"
[viewer]
# When a session starts and the viewer isn't open (`just gui-status`), the
# agent: "ask" offers to open it, "always" opens it (`just gui`), "never"
# leaves it closed.
open_at_start = "ask"
"#; "#;
const GITIGNORE: &str = r#"# The SQLite projection is disposable — it rebuilds from journal.jsonl. const GITIGNORE: &str = r#"# The SQLite projection is disposable — it rebuilds from journal.jsonl.
@@ -210,6 +239,8 @@ books-data/wanderbooks.db
books-data/wanderbooks.db-* books-data/wanderbooks.db-*
# Generated exports can always be regenerated from the journal. # Generated exports can always be regenerated from the journal.
exports/ exports/
# The background viewer's log (`just gui`).
books-data/gui.log
"#; "#;
const START_PROMPT: &str = "Hi! These are brand-new books. Please set them up with me."; const START_PROMPT: &str = "Hi! These are brand-new books. Please set them up with me.";
+4 -2
View File
@@ -99,8 +99,8 @@ fn the_server_is_registered_for_this_dir() {
); );
// The justfile opens this dir's books in the GUI. // The justfile opens this dir's books in the GUI.
let just = fs::read_to_string(books.join("justfile")).unwrap(); let just = fs::read_to_string(books.join("justfile")).unwrap();
assert!(just.contains("ledger-gui\" --data-dir books-data")); assert!(just.contains("ledger-gui\" --data-dir \"{{justfile_directory()}}/books-data\""));
for recipe in ["agent:", "gui:", "backup:", "update:"] { for recipe in ["agent:", "gui:", "gui-fg:", "gui-status:", "backup:", "update:"] {
assert!(just.contains(recipe), "justfile has {recipe}"); assert!(just.contains(recipe), "justfile has {recipe}");
} }
assert_eq!( assert_eq!(
@@ -128,6 +128,8 @@ fn agents_md_carries_the_contract() {
"chart_template", "chart_template",
"to the cent", "to the cent",
"just gui", "just gui",
"just gui-status",
"open_at_start",
] { ] {
assert!(md.contains(phrase), "AGENTS.md mentions {phrase:?}"); assert!(md.contains(phrase), "AGENTS.md mentions {phrase:?}");
} }