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