9 Commits

Author SHA1 Message Date
Dave
7e45a1fba0 Bump version to 0.3.1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:49:22 +00:00
Dave
ad348e813f Update Cargo.lock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:49:02 +00:00
Dave
de5dcceeaf story-kit: done 281_story_matrix_bot_announces_itself_when_it_comes_online 2026-03-18 11:48:19 +00:00
Dave
53fdcfec75 story-kit: merge 281_story_matrix_bot_announces_itself_when_it_comes_online 2026-03-18 11:48:15 +00:00
Dave
bad680cf24 story-kit: create 282_story_matrix_bot_ambient_mode_toggle_via_chat_command 2026-03-18 11:47:51 +00:00
Dave
a5e64ded83 fix: unused system_prompt variable warning
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:44:16 +00:00
Dave
77e368d354 fix: --system is not a valid Claude Code CLI flag
Removed the --system argument from the PTY runner — Claude Code CLI
doesn't support it. Bot name instruction is now prepended to the user
prompt instead of passed as a system prompt argument.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:42:39 +00:00
Dave
db92a78d2b story-kit: accept 276_story_detect_and_log_when_root_mcp_json_port_is_modified 2026-03-18 11:40:54 +00:00
Dave
420deebdb4 story-kit: done 276_story_detect_and_log_when_root_mcp_json_port_is_modified 2026-03-18 11:39:47 +00:00
8 changed files with 95 additions and 42 deletions

View File

@@ -1,28 +0,0 @@
---
name: "Long-running supervisor agent with periodic pipeline polling"
agent: coder-opus
---
# Story 280: Long-running supervisor agent with periodic pipeline polling
## User Story
As a project owner, I want a long-running supervisor agent (opus) that automatically monitors the pipeline, assigns agents, resolves stuck items, and handles routine operational tasks, so that I don't have to manually check status, kick agents, or babysit the pipeline in every conversation.
## Acceptance Criteria
- [ ] Server can start a persistent supervisor agent that stays alive across the session (not per-story)
- [ ] Server prods the supervisor periodically (default 30s, configurable in project.toml) with a pipeline status update
- [ ] Supervisor auto-assigns agents to unassigned items in current/qa/merge stages
- [ ] Supervisor detects stuck agents (no progress for configurable timeout) and restarts them
- [ ] Supervisor detects merge failures and sends stories back to current for rebase when appropriate
- [ ] Supervisor can be chatted with via Matrix (timmy relays to supervisor) or via the web UI
- [ ] Supervisor logs its decisions so the human can review what it did and why
- [ ] Polling interval is configurable in project.toml (e.g. supervisor_poll_interval_secs = 30)
- [ ] Supervisor logs persistent/recurring problems to `.story_kit/problems.md` with timestamp, description, and frequency — humans review this file periodically to create stories for systemic issues
## Out of Scope
- Supervisor accepting or merging stories to master (human job)
- Supervisor making architectural decisions
- Replacing the existing per-story agent spawning — supervisor coordinates on top of it

View File

@@ -0,0 +1,23 @@
---
name: "Matrix bot ambient mode toggle via chat command"
---
# Story 282: Matrix bot ambient mode toggle via chat command
## User Story
As a user chatting with Timmy in a Matrix room, I want to toggle between "addressed mode" (bot only responds when mentioned by name) and "ambient mode" (bot responds to all messages) via a chat command, so that I don't have to @-mention the bot on every message when I'm the only one around.
## Acceptance Criteria
- [ ] Matrix bot defaults to addressed mode — only forwards messages containing the bot's name to Claude
- [ ] Chat command "timmy ambient on" switches to ambient mode — bot forwards all room messages to Claude
- [ ] Chat command "timmy ambient off" switches back to addressed mode
- [ ] Mode persists until explicitly toggled (not across bot restarts)
- [ ] Bot confirms the mode switch with a short response in chat
- [ ] When other users join or are active, user can flip back to addressed mode to avoid noise
- [ ] Ambient mode applies per-room (not globally across all rooms)
## Out of Scope
- TBD

View File

@@ -0,0 +1,17 @@
---
name: "Detect and log when root .mcp.json port is modified"
---
# Story 276: Detect and log when root .mcp.json port is modified
## User Story
As a ..., I want ..., so that ...
## Acceptance Criteria
- [ ] TODO
## Out of Scope
- TBD

2
Cargo.lock generated
View File

@@ -3997,7 +3997,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "story-kit"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"async-stream",
"async-trait",

View File

@@ -1,6 +1,6 @@
[package]
name = "story-kit"
version = "0.3.0"
version = "0.3.1"
edition = "2024"
build = "build.rs"

View File

@@ -150,7 +150,7 @@ fn run_pty_session(
user_message: &str,
cwd: &str,
resume_session_id: Option<&str>,
system_prompt: Option<&str>,
_system_prompt: Option<&str>,
cancelled: Arc<AtomicBool>,
token_tx: tokio::sync::mpsc::UnboundedSender<String>,
thinking_tx: tokio::sync::mpsc::UnboundedSender<String>,
@@ -189,10 +189,8 @@ fn run_pty_session(
// a tool requires user approval, instead of using PTY stdin/stdout.
cmd.arg("--permission-prompt-tool");
cmd.arg("mcp__story-kit__prompt_permission");
if let Some(sys) = system_prompt {
cmd.arg("--system");
cmd.arg(sys);
}
// Note: --system is not a valid Claude Code CLI flag. System-level
// instructions (like bot name) are prepended to the user prompt instead.
cmd.cwd(cwd);
// Keep TERM reasonable but disable color
cmd.env("NO_COLOR", "1");

View File

@@ -167,6 +167,18 @@ pub struct BotContext {
pub bot_name: String,
}
// ---------------------------------------------------------------------------
// Startup announcement
// ---------------------------------------------------------------------------
/// Format the startup greeting the bot sends to each room when it comes online.
///
/// Uses the bot's configured display name so the message reads naturally
/// (e.g. "Timmy is online.").
pub fn format_startup_announcement(bot_name: &str) -> String {
format!("{bot_name} is online.")
}
// ---------------------------------------------------------------------------
// Bot entry point
// ---------------------------------------------------------------------------
@@ -305,10 +317,11 @@ pub async fn run_bot(
target_room_ids
);
// Clone values needed by the notification listener before they are moved
// into BotContext.
// Clone values needed by the notification listener and startup announcement
// before they are moved into BotContext.
let notif_room_ids = target_room_ids.clone();
let notif_project_root = project_root.clone();
let announce_room_ids = target_room_ids.clone();
let persisted = load_history(&project_root);
slog!(
@@ -320,6 +333,7 @@ pub async fn run_bot(
.display_name
.clone()
.unwrap_or_else(|| "Assistant".to_string());
let announce_bot_name = bot_name.clone();
let ctx = BotContext {
bot_user_id,
@@ -351,6 +365,23 @@ pub async fn run_bot(
notif_project_root,
);
// Send a startup announcement to each configured room so users know the
// bot is online. This runs once per process start — the sync loop handles
// reconnects internally so this code is never reached again on a network
// blip or sync resumption.
let announce_msg = format_startup_announcement(&announce_bot_name);
slog!("[matrix-bot] Sending startup announcement: {announce_msg}");
for room_id in &announce_room_ids {
if let Some(room) = client.get_room(room_id) {
let content = RoomMessageEventContent::text_plain(announce_msg.clone());
if let Err(e) = room.send(content).await {
slog!("[matrix-bot] Failed to send startup announcement to {room_id}: {e}");
}
} else {
slog!("[matrix-bot] Room {room_id} not found in client state, skipping announcement");
}
}
slog!("[matrix-bot] Starting Matrix sync loop");
// This blocks until the connection is terminated or an error occurs.
@@ -748,11 +779,10 @@ async fn handle_message(
// The prompt is just the current message with sender attribution.
// Prior conversation context is carried by the Claude Code session.
let prompt = format_user_prompt(&sender, &user_message);
let bot_name = &ctx.bot_name;
let system_prompt = format!(
"Your name is {bot_name}. Refer to yourself as {bot_name}, not Claude."
let prompt = format!(
"[Your name is {bot_name}. Refer to yourself as {bot_name}, not Claude.]\n\n{}",
format_user_prompt(&sender, &user_message)
);
let provider = ClaudeCodeProvider::new();
@@ -792,7 +822,7 @@ async fn handle_message(
&prompt,
&project_root_str,
resume_session_id.as_deref(),
Some(&system_prompt),
None,
&mut cancel_rx,
move |token| {
let mut buf = buffer_for_callback.lock().unwrap();
@@ -1664,6 +1694,19 @@ mod tests {
assert!(is_permission_approval("\tyes\n"));
}
// -- format_startup_announcement ----------------------------------------
#[test]
fn startup_announcement_uses_bot_name() {
assert_eq!(format_startup_announcement("Timmy"), "Timmy is online.");
}
#[test]
fn startup_announcement_uses_configured_display_name_not_hardcoded() {
assert_eq!(format_startup_announcement("HAL"), "HAL is online.");
assert_eq!(format_startup_announcement("Assistant"), "Assistant is online.");
}
// -- bot_name / system prompt -------------------------------------------
#[test]