From 943c05b61cb64cc2561708dca4135082954ddea5 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Wed, 30 Sep 2026 21:52:33 +0200 Subject: [PATCH] Add rm, mv, promote, demote, refile, archive, lint, query to ork Client verbs for the new server capabilities, each a thin wrapper over the base 9P operations: the structural verbs write to /ctl, and query writes to /query and prints the matches. --- crates/ork-cli/src/main.rs | 106 +++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/crates/ork-cli/src/main.rs b/crates/ork-cli/src/main.rs index d24ca4c..93ae639 100644 --- a/crates/ork-cli/src/main.rs +++ b/crates/ork-cli/src/main.rs @@ -3,9 +3,9 @@ //! `ork` is only a 9P client: it connects to a running `ork-server` and //! operates on its namespace. All org-mode intelligence lives in the server. //! The base verbs (ls/read/write/rdwr) speak raw 9P; the friendly verbs -//! (docs/sections/todos/agenda/get/set/add/mkdoc/reload/tag/prop/todo/tree) -//! are thin compositions of those same 9P operations against the exposed -//! namespace. +//! (docs/sections/todos/agenda/get/set/add/mkdoc/reload/tag/prop/todo/tree/ +//! rm/mv/promote/demote/refile/archive/lint/query) are thin compositions of +//! those same 9P operations against the exposed namespace. //! //! Address resolution: `--addr` flag, then `$ORK_ADDR`, then //! `tcp://localhost:5640`. Accepts `tcp://host:port` or `unix:///path/to/sock`. @@ -172,6 +172,73 @@ enum Commands { /// Document name doc: String, }, + + /// Delete a section and its subtree + Rm { + /// Document name + doc: String, + /// Section id + id: String, + }, + + /// Reorder a section among its siblings + Mv { + /// Document name + doc: String, + /// Section id + id: String, + /// Direction: up or down + dir: String, + }, + + /// Decrease a section's outline level + Promote { + /// Document name + doc: String, + /// Section id + id: String, + }, + + /// Increase a section's outline level + Demote { + /// Document name + doc: String, + /// Section id + id: String, + }, + + /// Move a section's subtree to another document (or elsewhere) + Refile { + /// Source document name + doc: String, + /// Section id + id: String, + /// Target document name + to: String, + /// Place under this section id instead of at top level + #[arg(long, short)] + parent: Option, + }, + + /// Move a section's subtree to .org_archive + Archive { + /// Document name + doc: String, + /// Section id + id: String, + }, + + /// Validate documents (all, or one) + Lint { + /// Document name; omit to lint all + doc: Option, + }, + + /// Find sections matching a query (tag:X+todo:Y+priority:Z) + Query { + /// Match expression, e.g. "tag:work+todo:TODO" + expr: String, + }, } /// Section fields that may be written via `set`. @@ -499,6 +566,20 @@ fn print_tree( Ok(()) } +/// Send a control command and echo the server's response. +fn ctl(client: &mut Client, command: &str) -> io::Result<()> { + let resp = client.rdwr("/ctl", command.as_bytes())?; + io::stdout().write_all(&resp)?; + Ok(()) +} + +/// `query ` — write a match expression and print matching sections. +fn cmd_query(client: &mut Client, expr: &str) -> io::Result<()> { + let resp = client.rdwr("/query", expr.as_bytes())?; + io::stdout().write_all(&resp)?; + Ok(()) +} + fn run(cli: Cli) -> io::Result<()> { let addr = resolve_addr(cli.addr.as_deref()); let mut client = Client::connect(&addr) @@ -553,6 +634,25 @@ fn run(cli: Cli) -> io::Result<()> { cmd_todo(&mut client, &doc, &id, state.as_deref())? } Commands::Tree { doc } => cmd_tree(&mut client, &doc)?, + Commands::Rm { doc, id } => ctl(&mut client, &format!("rm {} {}", doc, id))?, + Commands::Mv { doc, id, dir } => { + ctl(&mut client, &format!("move {} {} {}", doc, id, dir))? + } + Commands::Promote { doc, id } => ctl(&mut client, &format!("promote {} {}", doc, id))?, + Commands::Demote { doc, id } => ctl(&mut client, &format!("demote {} {}", doc, id))?, + Commands::Refile { doc, id, to, parent } => { + let cmd = match parent { + Some(p) => format!("refile {} {} {} {}", doc, id, to, p), + None => format!("refile {} {} {}", doc, id, to), + }; + ctl(&mut client, &cmd)? + } + Commands::Archive { doc, id } => ctl(&mut client, &format!("archive {} {}", doc, id))?, + Commands::Lint { doc } => match doc { + Some(d) => ctl(&mut client, &format!("lint {}", d))?, + None => ctl(&mut client, "lint")?, + }, + Commands::Query { expr } => cmd_query(&mut client, &expr)?, } Ok(())