mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-11 12:03:31 +08:00
feat: add ecc2 automatic graph relations
This commit is contained in:
@@ -1262,6 +1262,7 @@ impl StateStore {
|
|||||||
alternatives: &[String],
|
alternatives: &[String],
|
||||||
reasoning: &str,
|
reasoning: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let session_entity = self.sync_context_graph_session(session_id)?;
|
||||||
let mut metadata = BTreeMap::new();
|
let mut metadata = BTreeMap::new();
|
||||||
metadata.insert(
|
metadata.insert(
|
||||||
"alternatives_count".to_string(),
|
"alternatives_count".to_string(),
|
||||||
@@ -1270,7 +1271,7 @@ impl StateStore {
|
|||||||
if !alternatives.is_empty() {
|
if !alternatives.is_empty() {
|
||||||
metadata.insert("alternatives".to_string(), alternatives.join(" | "));
|
metadata.insert("alternatives".to_string(), alternatives.join(" | "));
|
||||||
}
|
}
|
||||||
self.upsert_context_entity(
|
let decision_entity = self.upsert_context_entity(
|
||||||
Some(session_id),
|
Some(session_id),
|
||||||
"decision",
|
"decision",
|
||||||
decision,
|
decision,
|
||||||
@@ -1278,6 +1279,14 @@ impl StateStore {
|
|||||||
reasoning,
|
reasoning,
|
||||||
&metadata,
|
&metadata,
|
||||||
)?;
|
)?;
|
||||||
|
let relation_summary = format!("{} recorded this decision", session_entity.name);
|
||||||
|
self.upsert_context_relation(
|
||||||
|
Some(session_id),
|
||||||
|
session_entity.id,
|
||||||
|
decision_entity.id,
|
||||||
|
"decided",
|
||||||
|
&relation_summary,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1287,6 +1296,7 @@ impl StateStore {
|
|||||||
tool_name: &str,
|
tool_name: &str,
|
||||||
event: &PersistedFileEvent,
|
event: &PersistedFileEvent,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let session_entity = self.sync_context_graph_session(session_id)?;
|
||||||
let mut metadata = BTreeMap::new();
|
let mut metadata = BTreeMap::new();
|
||||||
metadata.insert(
|
metadata.insert(
|
||||||
"last_action".to_string(),
|
"last_action".to_string(),
|
||||||
@@ -1305,7 +1315,7 @@ impl StateStore {
|
|||||||
format!("Last activity: {action} via {tool_name}")
|
format!("Last activity: {action} via {tool_name}")
|
||||||
};
|
};
|
||||||
let name = context_graph_file_name(&event.path);
|
let name = context_graph_file_name(&event.path);
|
||||||
self.upsert_context_entity(
|
let file_entity = self.upsert_context_entity(
|
||||||
Some(session_id),
|
Some(session_id),
|
||||||
"file",
|
"file",
|
||||||
&name,
|
&name,
|
||||||
@@ -1313,9 +1323,57 @@ impl StateStore {
|
|||||||
&summary,
|
&summary,
|
||||||
&metadata,
|
&metadata,
|
||||||
)?;
|
)?;
|
||||||
|
self.upsert_context_relation(
|
||||||
|
Some(session_id),
|
||||||
|
session_entity.id,
|
||||||
|
file_entity.id,
|
||||||
|
action,
|
||||||
|
&summary,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sync_context_graph_session(&self, session_id: &str) -> Result<ContextGraphEntity> {
|
||||||
|
let session = self
|
||||||
|
.get_session(session_id)?
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Session not found for context graph sync: {session_id}"))?;
|
||||||
|
|
||||||
|
let mut metadata = BTreeMap::new();
|
||||||
|
metadata.insert("task".to_string(), session.task.clone());
|
||||||
|
metadata.insert("project".to_string(), session.project.clone());
|
||||||
|
metadata.insert("task_group".to_string(), session.task_group.clone());
|
||||||
|
metadata.insert("agent_type".to_string(), session.agent_type.clone());
|
||||||
|
metadata.insert("state".to_string(), session.state.to_string());
|
||||||
|
metadata.insert(
|
||||||
|
"working_dir".to_string(),
|
||||||
|
session.working_dir.display().to_string(),
|
||||||
|
);
|
||||||
|
if let Some(pid) = session.pid {
|
||||||
|
metadata.insert("pid".to_string(), pid.to_string());
|
||||||
|
}
|
||||||
|
if let Some(worktree) = &session.worktree {
|
||||||
|
metadata.insert(
|
||||||
|
"worktree_path".to_string(),
|
||||||
|
worktree.path.display().to_string(),
|
||||||
|
);
|
||||||
|
metadata.insert("worktree_branch".to_string(), worktree.branch.clone());
|
||||||
|
metadata.insert("base_branch".to_string(), worktree.base_branch.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let summary = format!(
|
||||||
|
"{} | {} | {} / {}",
|
||||||
|
session.state, session.agent_type, session.project, session.task_group
|
||||||
|
);
|
||||||
|
self.upsert_context_entity(
|
||||||
|
Some(&session.id),
|
||||||
|
"session",
|
||||||
|
&session.id,
|
||||||
|
None,
|
||||||
|
&summary,
|
||||||
|
&metadata,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn increment_tool_calls(&self, session_id: &str) -> Result<()> {
|
pub fn increment_tool_calls(&self, session_id: &str) -> Result<()> {
|
||||||
self.conn.execute(
|
self.conn.execute(
|
||||||
"UPDATE sessions
|
"UPDATE sessions
|
||||||
@@ -3832,6 +3890,20 @@ mod tests {
|
|||||||
.summary
|
.summary
|
||||||
.contains("SQLite keeps the graph queryable"));
|
.contains("SQLite keeps the graph queryable"));
|
||||||
|
|
||||||
|
let session_entities = db.list_context_entities(Some("session-1"), Some("session"), 10)?;
|
||||||
|
assert_eq!(session_entities.len(), 1);
|
||||||
|
assert_eq!(session_entities[0].name, "session-1");
|
||||||
|
assert_eq!(
|
||||||
|
session_entities[0].metadata.get("task"),
|
||||||
|
Some(&"context graph".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
let relations = db.list_context_relations(Some(session_entities[0].id), 10)?;
|
||||||
|
assert_eq!(relations.len(), 1);
|
||||||
|
assert_eq!(relations[0].relation_type, "decided");
|
||||||
|
assert_eq!(relations[0].to_entity_type, "decision");
|
||||||
|
assert_eq!(relations[0].to_entity_name, "Use sqlite for shared context");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3883,6 +3955,14 @@ mod tests {
|
|||||||
.summary
|
.summary
|
||||||
.contains("Last activity: modify via Edit"));
|
.contains("Last activity: modify via Edit"));
|
||||||
|
|
||||||
|
let session_entities = db.list_context_entities(Some("session-1"), Some("session"), 10)?;
|
||||||
|
assert_eq!(session_entities.len(), 1);
|
||||||
|
let relations = db.list_context_relations(Some(session_entities[0].id), 10)?;
|
||||||
|
assert_eq!(relations.len(), 1);
|
||||||
|
assert_eq!(relations[0].relation_type, "modify");
|
||||||
|
assert_eq!(relations[0].to_entity_type, "file");
|
||||||
|
assert_eq!(relations[0].to_entity_name, "config.ts");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3953,6 +4033,14 @@ mod tests {
|
|||||||
&& entity.name == "Backfill historical decision"));
|
&& entity.name == "Backfill historical decision"));
|
||||||
assert!(entities.iter().any(|entity| entity.entity_type == "file"
|
assert!(entities.iter().any(|entity| entity.entity_type == "file"
|
||||||
&& entity.path.as_deref() == Some("src/backfill.rs")));
|
&& entity.path.as_deref() == Some("src/backfill.rs")));
|
||||||
|
let session_entity = entities
|
||||||
|
.iter()
|
||||||
|
.find(|entity| entity.entity_type == "session" && entity.name == "session-1")
|
||||||
|
.expect("session entity should exist");
|
||||||
|
let relations = db.list_context_relations(Some(session_entity.id), 10)?;
|
||||||
|
assert_eq!(relations.len(), 2);
|
||||||
|
assert!(relations.iter().any(|relation| relation.relation_type == "decided"));
|
||||||
|
assert!(relations.iter().any(|relation| relation.relation_type == "modify"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10104,12 +10104,14 @@ diff --git a/src/lib.rs b/src/lib.rs\n\
|
|||||||
|
|
||||||
dashboard.toggle_context_graph_mode();
|
dashboard.toggle_context_graph_mode();
|
||||||
dashboard.toggle_search_scope();
|
dashboard.toggle_search_scope();
|
||||||
|
dashboard.cycle_graph_entity_filter();
|
||||||
dashboard.begin_search();
|
dashboard.begin_search();
|
||||||
for ch in "alpha.*".chars() {
|
for ch in "alpha.*".chars() {
|
||||||
dashboard.push_input_char(ch);
|
dashboard.push_input_char(ch);
|
||||||
}
|
}
|
||||||
dashboard.submit_search();
|
dashboard.submit_search();
|
||||||
|
|
||||||
|
assert_eq!(dashboard.graph_entity_filter, GraphEntityFilter::Decisions);
|
||||||
assert_eq!(dashboard.search_matches.len(), 2);
|
assert_eq!(dashboard.search_matches.len(), 2);
|
||||||
let first_session = dashboard.selected_session_id().map(str::to_string);
|
let first_session = dashboard.selected_session_id().map(str::to_string);
|
||||||
dashboard.next_search_match();
|
dashboard.next_search_match();
|
||||||
@@ -10121,6 +10123,40 @@ diff --git a/src/lib.rs b/src/lib.rs\n\
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn graph_sessions_filter_renders_auto_session_relations() -> Result<()> {
|
||||||
|
let session = sample_session(
|
||||||
|
"focus-12345678",
|
||||||
|
"planner",
|
||||||
|
SessionState::Running,
|
||||||
|
None,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
let mut dashboard = test_dashboard(vec![session.clone()], 0);
|
||||||
|
dashboard.db.insert_session(&session)?;
|
||||||
|
dashboard.db.insert_decision(
|
||||||
|
&session.id,
|
||||||
|
"Use graph relations",
|
||||||
|
&[],
|
||||||
|
"Edges make the context graph navigable",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
dashboard.toggle_context_graph_mode();
|
||||||
|
dashboard.cycle_graph_entity_filter();
|
||||||
|
dashboard.cycle_graph_entity_filter();
|
||||||
|
dashboard.cycle_graph_entity_filter();
|
||||||
|
dashboard.cycle_graph_entity_filter();
|
||||||
|
|
||||||
|
assert_eq!(dashboard.graph_entity_filter, GraphEntityFilter::Sessions);
|
||||||
|
assert_eq!(dashboard.output_title(), " Graph sessions ");
|
||||||
|
let rendered = dashboard.rendered_output_text(180, 30);
|
||||||
|
assert!(rendered.contains("focus-12345678"));
|
||||||
|
assert!(rendered.contains("summary running | planner |"));
|
||||||
|
assert!(rendered.contains("-> decided decision:Use graph relations"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn worktree_diff_columns_split_removed_and_added_lines() {
|
fn worktree_diff_columns_split_removed_and_added_lines() {
|
||||||
let patch = "\
|
let patch = "\
|
||||||
|
|||||||
Reference in New Issue
Block a user