1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::*;
use carryctx::adapter::unit_of_work::UnitOfWork;
use carryctx::application;
use carryctx::application::runtime::InvocationContext;
use carryctx::error::{CarryCtxError, ExitCode};
use clap::Parser;
// ── Event ────────────────────────────────────────────────────────────────
#[derive(Parser, Debug)]
pub enum EventCommand {
/// List events matching the specified filters
List {
/// Filter by associated task ULID
#[arg(long)]
task: Option<String>,
/// Filter by associated agent ULID
#[arg(long)]
agent: Option<String>,
/// Filter by associated session ULID
#[arg(long)]
session: Option<String>,
/// Filter by event type (e.g., TaskTransition, SessionStarted)
#[arg(long)]
event_type: Option<String>,
/// Only show events after this timestamp or relative duration
#[arg(long)]
since: Option<String>,
/// Only show events before this timestamp or relative duration
#[arg(long)]
until: Option<String>,
/// Limit the number of returned events
#[arg(long)]
limit: Option<u64>,
},
/// Show full raw JSON details for a specific event ULID
Show { event_id: String },
}
#[derive(Parser, Debug)]
pub struct EventArgs {
/// Event subcommand to execute
#[command(subcommand)]
pub command: EventCommand,
}
// ═══════════════════════════════════════════════════════════════════════════
// Handler: event
// ═══════════════════════════════════════════════════════════════════════════
pub fn handle_event(
args: &EventArgs,
ctx: &InvocationContext,
is_json: bool,
) -> Result<ExitCode, ExitCode> {
let mut runtime = try_open_runtime(ctx)?;
let project_id = &runtime.config.project.id;
let conn = runtime.database.connection_mut();
match &args.command {
EventCommand::List {
task,
agent,
session,
event_type,
since,
until,
limit,
} => {
// Resolve agent reference (name or ULID) to ULID for filtering.
// The local --agent clashes with the global --agent (CARRYCTX_AGENT env),
// so resolve it here to avoid filtering by raw agent name.
// Resolve agent reference (name or ULID) to ULID for filtering.
let resolved_agent_id = agent.as_deref().and_then(|a| {
if a.is_empty() {
None
} else {
resolve_agent_id(project_id, a, conn).ok()
}
});
// Resolve task reference (display ID or ULID) to ULID for filtering.
let resolved_task_id = task.as_deref().and_then(|t| {
if t.is_empty() {
None
} else {
resolve_task_id(project_id, t, conn).ok()
}
});
let filter = EventFilter {
project_id: project_id.to_string(),
task_id: resolved_task_id,
agent_id: resolved_agent_id,
session_id: session.clone(),
event_type: event_type.clone(),
since: since.clone(),
until: until.clone(),
limit: *limit,
};
let repo = carryctx::adapter::sqlite_repos::SqliteEventRepository::new(conn);
let events = repo.list(&filter).map_err(|e| e.exit_code)?;
// Markdown format support
if ctx.format == carryctx::application::runtime::OutputFormat::Markdown {
let mut out = String::from("# Events\n\n");
out.push_str("| Type | Agent | Occurred At |\n");
out.push_str("|---|---|---|\n");
for e in &events {
let agent = e.actor_agent_id.as_deref().unwrap_or("-");
let agent_short = if agent.len() > 8 { &agent[..8] } else { agent };
out.push_str(&format!(
"| {} | {} | {} |\n",
e.event_type,
agent_short,
&e.occurred_at[..19]
));
}
if !ctx.quiet {
print!("{out}");
}
return Ok(ExitCode::Success);
}
let result = serde_json::json!({"events": events, "next_cursor": null});
render_and_print("event.list", Ok(result), is_json, ctx.quiet)
}
EventCommand::Show { event_id } => {
let tx = conn
.transaction()
.map_err(|e| CarryCtxError::database_error(format!("{e}")).exit_code)?;
let uow = UnitOfWork::new(tx);
let result = application::event::show_event(project_id, event_id, &uow);
render_and_print("event.show", result, is_json, ctx.quiet)
}
}
}