- apply: `check_extension_points_provided` (app nodes) — every EP visible on
the app's chain (its own + inherited) must have a provider: a same-apply
module, or one resolvable up-chain (override or default body). Else a clean
`pic plan` error instead of a runtime failure. Single-node only (the tree
path leans on the runtime backstop, like the dangling-import check).
- read-only surface: `extension_point_report` + `ExtensionPointInfo`;
`GET /{apps|groups}/{id}/extension-points` (AppRead / GroupScriptsRead);
`pic extension-points ls --app|--group` showing declared-vs-inherited + the
resolved provider (`app override` / `inherited default` / `unset`).
- `pic pull` round-trips an app's OWN declared EPs (filtered `declared_here`),
so pull→plan stays idempotent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
49 lines
1.7 KiB
Rust
49 lines
1.7 KiB
Rust
//! `pic extension-points ls --app|--group` — read-only view of a node's
|
|
//! §5.5 extension points. For an app, each EP visible on its chain is shown
|
|
//! with its resolved provider (`app override` / `inherited default` / `unset`);
|
|
//! for a group, its own declared names. Authoring is declarative only (the
|
|
//! manifest `extension_points = [...]`).
|
|
|
|
use anyhow::{bail, Result};
|
|
|
|
use crate::client::{Client, NodeKind};
|
|
use crate::config;
|
|
use crate::output::{OutputMode, Table};
|
|
|
|
pub async fn ls(app: Option<&str>, group: Option<&str>, mode: OutputMode) -> Result<()> {
|
|
let (kind, ident) = match (app, group) {
|
|
(Some(a), None) => (NodeKind::App, a),
|
|
(None, Some(g)) => (NodeKind::Group, g),
|
|
_ => bail!("`extension-points ls` requires exactly one of --app or --group"),
|
|
};
|
|
let creds = config::resolve()?;
|
|
let client = Client::from_creds(&creds)?;
|
|
let items = client.extension_points_list(kind, ident).await?;
|
|
|
|
match kind {
|
|
NodeKind::App => {
|
|
let mut table = Table::new(["name", "source", "provider"]);
|
|
for ep in &items {
|
|
table.row([
|
|
ep.name.clone(),
|
|
if ep.declared_here {
|
|
"declared".into()
|
|
} else {
|
|
"inherited".into()
|
|
},
|
|
ep.provider.clone().unwrap_or_else(|| "unset".into()),
|
|
]);
|
|
}
|
|
table.print(mode);
|
|
}
|
|
NodeKind::Group => {
|
|
let mut table = Table::new(["name"]);
|
|
for ep in &items {
|
|
table.row([ep.name.clone()]);
|
|
}
|
|
table.print(mode);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|