cli: new books dirs get a SessionStart hook that checks the viewer
The viewer offer (open_at_start) lived only in AGENTS.md, so it depended on the agent remembering it on its first turn. The scaffold's .claude/settings.json now runs just gui-status at session start and hands the agent the result plus open_at_start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tpv1gBaepKjQiAVJqejGbA
This commit is contained in:
1 parent
b38c9e39cc
commit
799aee58ea
2 files changed
+22
-2
No files matched your search
@@ -15,7 +15,8 @@
|
|||||||
//! `.gitignore`, and `books-data/`.
|
//! `.gitignore`, and `books-data/`.
|
||||||
//! 3. Registers the MCP server for this directory only: `.mcp.json`, plus
|
//! 3. Registers the MCP server for this directory only: `.mcp.json`, plus
|
||||||
//! `.claude/settings.json` approving it, so Claude Code loads the tools
|
//! `.claude/settings.json` approving it, so Claude Code loads the tools
|
||||||
//! without `claude mcp add` or a prompt.
|
//! without `claude mcp add` or a prompt. The same settings carry a
|
||||||
|
//! SessionStart hook that checks whether the viewer is open.
|
||||||
//! 4. `git init`s the directory (its history is the archive) and commits.
|
//! 4. `git init`s the directory (its history is the archive) and commits.
|
||||||
//! 5. Starts `claude` in the directory with the onboarding prompt, unless
|
//! 5. Starts `claude` in the directory with the onboarding prompt, unless
|
||||||
//! `--no-start` or there's no terminal. With `--codex`, registers the
|
//! `--no-start` or there's no terminal. With `--codex`, registers the
|
||||||
@@ -363,11 +364,25 @@ fn mcp_json(ledger_mcp: &Path, books: &Path) -> String {
|
|||||||
|
|
||||||
/// `.claude/settings.json`: approve this directory's server, and its tools,
|
/// `.claude/settings.json`: approve this directory's server, and its tools,
|
||||||
/// so the first session starts working instead of asking. The server still
|
/// so the first session starts working instead of asking. The server still
|
||||||
/// previews every posting for the human to confirm.
|
/// previews every posting for the human to confirm. A SessionStart hook
|
||||||
|
/// checks the viewer and hands the agent `[viewer] open_at_start`, so the
|
||||||
|
/// offer to open it doesn't hang on the agent remembering AGENTS.md.
|
||||||
const CLAUDE_SETTINGS: &str = r#"{
|
const CLAUDE_SETTINGS: &str = r#"{
|
||||||
"enabledMcpjsonServers": ["wanderbooks"],
|
"enabledMcpjsonServers": ["wanderbooks"],
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": ["mcp__wanderbooks"]
|
"allow": ["mcp__wanderbooks"]
|
||||||
|
},
|
||||||
|
"hooks": {
|
||||||
|
"SessionStart": [
|
||||||
|
{
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "cd \"$CLAUDE_PROJECT_DIR\" && if just gui-status >/dev/null 2>&1; then echo 'wanderbooks viewer: open.'; else echo \"wanderbooks viewer: CLOSED. [viewer] open_at_start = $(sed -n 's/^open_at_start *= *//p' wanderbooks.toml). Act on this at the top of your first reply, before anything else: \\\"ask\\\" -> offer to open it (just gui); \\\"always\\\" -> run just gui; \\\"never\\\" -> say nothing.\"; fi"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|||||||
@@ -97,6 +97,11 @@ fn the_server_is_registered_for_this_dir() {
|
|||||||
settings["enabledMcpjsonServers"],
|
settings["enabledMcpjsonServers"],
|
||||||
serde_json::json!(["wanderbooks"])
|
serde_json::json!(["wanderbooks"])
|
||||||
);
|
);
|
||||||
|
// Every session starts by checking the viewer against open_at_start.
|
||||||
|
let hook = settings["hooks"]["SessionStart"][0]["hooks"][0]["command"]
|
||||||
|
.as_str()
|
||||||
|
.expect("SessionStart hook command");
|
||||||
|
assert!(hook.contains("just gui-status") && hook.contains("open_at_start"));
|
||||||
// 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 \"{{justfile_directory()}}/books-data\""));
|
assert!(just.contains("ledger-gui\" --data-dir \"{{justfile_directory()}}/books-data\""));
|
||||||
|
|||||||
Reference in new issue
Block a user