mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
M13.14: agent-interface audit (every command over MCP, control channel and exec --list; automation ids for every widget)
- command.describe / list_commands {schemas} give each command's params JSON Schema;
MCP describe_command, add_effect, list_effects and get_state (app.capabilities)
- effectcraft-cli exec --list [--schemas]
- headless UI audit: every panel kind's interactive egui widgets are covered by
automation ids (fixed: timeline navigator, label swatch, bar trim edges, group and
property name cells)
- command lookup is indexed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
5cf37921ef
commit
0d5f60c4b7
9 files changed
+493
-35
No files matched your search
@@ -4,6 +4,7 @@
|
||||
//! effectcraft-cli info project + engine summary
|
||||
//! effectcraft-cli commands [--filter TEXT] [--enabled] list engine commands
|
||||
//! effectcraft-cli exec <command-id> [--params JSON] run one command
|
||||
//! effectcraft-cli exec --list [--filter TEXT] [--schemas] list engine commands (as `commands`)
|
||||
//! effectcraft-cli run <id> <json> [<id> <json> …] run several commands in order
|
||||
//! effectcraft-cli props <comp> <layer> [--flat] [--time S] a layer's property tree (with paths)
|
||||
//! effectcraft-cli get <comp> <layer> <path> [--time S] read a property
|
||||
@@ -36,7 +37,7 @@ use serde_json::{Value, json};
|
||||
const USAGE: &str = "usage: effectcraft-cli <info|commands|exec|run|props|get|set|render-frame|render|script|mcp> [args] [--json]
|
||||
info project + engine summary
|
||||
commands [--filter TEXT] [--enabled] list engine commands
|
||||
exec <command-id> [--params JSON] run one engine command
|
||||
exec <command-id> [--params JSON] run one engine command (exec --list: list them, as `commands`)
|
||||
run <id> <json> [<id> <json> ...] run several commands in order
|
||||
props <comp> <layer> [--flat] [--time S] a layer's property tree with paths
|
||||
get <comp> <layer> <path> [--time S] read a property
|
||||
@@ -346,23 +347,8 @@ fn run(cmd: &str, args: &Args, json_out: bool) -> Result<(), Failure> {
|
||||
}
|
||||
}
|
||||
}
|
||||
"commands" => {
|
||||
let mut b = backend(args, false)?;
|
||||
let v = tool(&mut b, "list_commands", json!({"filter": args.opt("--filter"), "enabled_only": args.flag("--enabled")}))?;
|
||||
if json_out {
|
||||
emit(&v, true);
|
||||
} else {
|
||||
for c in v.as_array().into_iter().flatten() {
|
||||
println!(
|
||||
"{:<36} {:<36} {:<16} {}",
|
||||
c["id"].as_str().unwrap_or(""),
|
||||
c["label"].as_str().unwrap_or(""),
|
||||
c["shortcut"].as_str().unwrap_or(""),
|
||||
c["params"].as_str().unwrap_or("")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
"commands" => list_commands_cmd(args, json_out)?,
|
||||
"exec" if args.flag("--list") => list_commands_cmd(args, json_out)?,
|
||||
"script" => script_cmd(args, json_out)?,
|
||||
"exec" => {
|
||||
let Some(id) = args.pos.first().cloned() else { return usage_err("exec needs a command id") };
|
||||
@@ -605,6 +591,27 @@ fn render(args: &Args, json_out: bool) -> Result<(), Failure> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `commands` / `exec --list`: every engine command (id, label, shortcut, params doc; with
|
||||
/// `--schemas` also each command's params JSON Schema).
|
||||
fn list_commands_cmd(args: &Args, json_out: bool) -> Result<(), Failure> {
|
||||
let mut b = backend(args, false)?;
|
||||
let v = tool(&mut b, "list_commands", json!({"filter": args.opt("--filter"), "enabled_only": args.flag("--enabled"), "schemas": args.flag("--schemas")}))?;
|
||||
if json_out {
|
||||
emit(&v, true);
|
||||
} else {
|
||||
for c in v.as_array().into_iter().flatten() {
|
||||
println!(
|
||||
"{:<36} {:<36} {:<16} {}",
|
||||
c["id"].as_str().unwrap_or(""),
|
||||
c["label"].as_str().unwrap_or(""),
|
||||
c["shortcut"].as_str().unwrap_or(""),
|
||||
c["params"].as_str().unwrap_or("")
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `bench`: time one frame N times without the layer cache (per-layer and per-effect breakdown),
|
||||
/// and optionally `--play N` consecutive frames with and without the cache.
|
||||
fn bench_cmd(args: &Args) -> Result<(), Failure> {
|
||||
|
||||
@@ -44,6 +44,30 @@ fn info_and_commands() {
|
||||
assert!(ids.iter().all(|i| i.contains("layer.new") || !i.is_empty()));
|
||||
}
|
||||
|
||||
/// The agent-interface audit (CLI side): `exec --list` lists every registered engine command
|
||||
/// with its label, params doc and (with `--schemas`) params JSON Schema, and `exec` routes every
|
||||
/// id to its command (an unknown parameter is rejected by that command).
|
||||
#[test]
|
||||
fn exec_list_covers_every_engine_command() {
|
||||
let v = ok_json(&["exec", "--list", "--schemas", "--empty"]);
|
||||
let listed: std::collections::HashMap<&str, &Value> = v.as_array().unwrap().iter().map(|c| (c["id"].as_str().unwrap(), c)).collect();
|
||||
let specs = effectcraft_engine::command_specs();
|
||||
assert_eq!(listed.len(), specs.len());
|
||||
for spec in specs {
|
||||
let c = listed.get(spec.id).unwrap_or_else(|| panic!("`exec --list` misses {}", spec.id));
|
||||
assert_eq!(c["label"], spec.label);
|
||||
// Empty docs (`{}`) are dropped from the compact listing.
|
||||
assert!(c["params"] == spec.params || (spec.params == "{}" && c["params"].is_null()), "{}: {}", spec.id, c["params"]);
|
||||
assert_eq!(c["schema"]["type"], "object", "{}", spec.id);
|
||||
}
|
||||
// Routing: a few ids through a real process (all ids are checked in-process by the MCP audit).
|
||||
for id in ["comp.new", "layer.newText", "effect.apply", "file.saveAs", "render.frame"].into_iter().filter(|i| listed.contains_key(i)) {
|
||||
let (code, v) = run_json(&["exec", id, r#"{"__audit":1}"#, "--empty"]);
|
||||
assert_eq!(code, 1, "{id}: {v}");
|
||||
assert!(v["error"].as_str().unwrap().contains("unknown parameter"), "{id}: {v}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_set_get_with_saved_project() {
|
||||
let proj = tmp("edit.ecproj");
|
||||
|
||||
Reference in new issue
Block a user