List vault intents. Optionally filtered by status. Pass `Some("all")` or `None` for all intents.
(
&self,
status_filter: Option<&str>,
)
| 454 | /// |
| 455 | /// Optionally filtered by status. Pass `Some("all")` or `None` for all intents. |
| 456 | pub fn vault_intent_list( |
| 457 | &self, |
| 458 | status_filter: Option<&str>, |
| 459 | ) -> Result<Vec<IntentInfo>, RepositoryError> { |
| 460 | let manifest = self.vault_manifest()?; |
| 461 | let mut intents: Vec<IntentInfo> = Vec::new(); |
| 462 | |
| 463 | for (id, summary) in &manifest.intents { |
| 464 | if let Some(filter) = status_filter { |
| 465 | if filter != "all" && summary.status != filter { |
| 466 | continue; |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | // Use the title stored in the manifest summary. |
| 471 | // Fall back to path-based lookup only for legacy entries without a title. |
| 472 | let title = if !summary.title.is_empty() { |
| 473 | summary.title.clone() |
| 474 | } else if !summary.vault_path.is_empty() { |
| 475 | self.vault_retrieve(&summary.vault_path)? |
| 476 | .and_then(|entry| { |
| 477 | let fm: serde_json::Map<String, serde_json::Value> = |
| 478 | serde_json::from_str(&entry.frontmatter_json).ok()?; |
| 479 | fm.get("title")?.as_str().map(String::from) |
| 480 | }) |
| 481 | .unwrap_or_else(|| id.clone()) |
| 482 | } else { |
| 483 | // Legacy entry with neither title nor vault_path — try scanning |
| 484 | self.find_intent_path(id)? |
| 485 | .and_then(|path| self.vault_retrieve(&path).ok().flatten()) |
| 486 | .and_then(|entry| { |
| 487 | let fm: serde_json::Map<String, serde_json::Value> = |
| 488 | serde_json::from_str(&entry.frontmatter_json).ok()?; |
| 489 | fm.get("title")?.as_str().map(String::from) |
| 490 | }) |
| 491 | .unwrap_or_else(|| id.clone()) |
| 492 | }; |
| 493 | |
| 494 | intents.push(IntentInfo { |
| 495 | id: id.clone(), |
| 496 | title, |
| 497 | status: summary.status.clone(), |
| 498 | priority: summary.priority.clone(), |
| 499 | assignee: summary.assignee.clone(), |
| 500 | goals: summary.goals, |
| 501 | blocked_by: summary.blocked_by.clone(), |
| 502 | }); |
| 503 | } |
| 504 | |
| 505 | // Sort by ID (which sorts by number within same prefix) |
| 506 | intents.sort_by(|a, b| a.id.cmp(&b.id)); |
| 507 | |
| 508 | Ok(intents) |
| 509 | } |
| 510 | |
| 511 | /// Show an intent's full content. |
| 512 | pub fn vault_intent_show(&self, intent_id: &str) -> Result<VaultEntry, RepositoryError> { |