PageSourceSearch

https://clawless.rs/assets/js/42acf18b.108563c5.js

js clawless.rs collected 2026-10-08 05:20:18 UTC 12,071 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([["8873"],{3144(e,t,s){s.r(t),s.d(t,{metadata:()=>n,default:()=>h,frontMatter:()=>o,contentTitle:()=>i,toc:()=>a,assets:()=>c});var n=JSON.parse('{"id":"concepts/output","title":"Output","description":"Output is the mechanism Clawless uses for framework-controlled command output.","source":"@site/docs/concepts/output.md","sourceDirName":"concepts","slug":"/concepts/output","permalink":"/docs/concepts/output","draft":false,"unlisted":false,"editUrl":"https://https://github.com/aonyx-ai/clawless/tree/main/docs/docs/concepts/output.md","tags":[],"version":"current","sidebarPosition":3,"frontMatter":{"sidebar_position":3},"sidebar":"tutorialSidebar","previous":{"title":"Context","permalink":"/docs/concepts/context"},"next":{"title":"Rendering","permalink":"/docs/concepts/rendering"}}'),r=s(4848),d=s(8453);let o={sidebar_position:3},i="Output",c={},a=[{value:"What is output?",id:"what-is-output",level:2},{value:"How it works",id:"how-it-works",level:2},{value:"The three methods",id:"the-three-methods",level:2},{value:"<code>message</code>",id:"message",level:3},{value:"<code>detail</code>
1",id:"detail",level:3},{value:"<code>artifact</code>",id:"artifact",level:3},{value:"Behavior matrix",id:"behavior-matrix",level:2},{value:"Output macros",id:"output-macros",level:2},{value:"When to use each method",id:"when-to-use-each-method",level:2},{value:"What&#39;s next",id:"whats-next",level:2}];function l(e){let t={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,d.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(t.header,{children:(0,r.jsx)(t.h1,{id:"output",children:"Output"})}),"\n",(0,r.jsxs)(t.p,{children:["Output is the mechanism Clawless uses for framework-controlled command output.\nRather than printing directly to stdout, commands route all user-facing text\nthrough an ",(0,r.jsx)(t.a,{href:"https://docs.rs/clawless/latest/clawless/output/struct.Output.html",children:(0,r.jsx)(t.code,{children:"Output"})})," instance, which automatically provides\n",(0,r.jsx)(t.code,{children:"--quiet"}),", ",(0,r.jsx)(t.code,{children:"--verbose"}),", and ",(0,r.jsx)(t.code,{children:"--json"})," support."]}),"\n",(0,r.jsx)(t.h2,{id:"what-is-output",children:"What is output?"}),"\n",(0,r.jsxs)(t.p,{children:["Output is an abstraction between your command and the terminal. Instead of\ncalling ",(0,r.jsx)(t.code,{children:"println!"})," directly, you call methods on the ",(0,r.jsx)(t.a,{href:"https://docs.rs/clawless/latest/clawless/output/struct.Output.html",children:(0,r.jsx)(t.code,{children:"Output"})}),"\ntype that the framework provides through ",(0,r.jsx)(t.a,{href:"./context",children:(0,r.jsx)(t.code,{children:"Context"})}),". The framework\nthen decides where and whether to write each message based on the flags the\nuser passed."]}),"\n",(0,r.jsx)(t.p,{children:"This matters for commands that produce structured data, need verbosity control,\nor must work in scripting pipelines. Framework-controlled output lets your\ncommand focus on what to say while the framework handles how to say it."}),"\n",(0,r.jsx)(t.h2,{id:"how-it-works",children:"How it works"}),"\n",(0,r.jsxs)(t.p,{children:["Access the ",(0,r.jsx)(t.a,{href:"https://docs.rs/clawless/latest/clawless/output/struct.Output.html",children:(0,r.jsx)(t.code,{children:"Output"})})," instance through context:"]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-rust",children:'use clawless::prelude::*;\n\n/// Count words in a sentence\n#[command]\npub async fn count(args: CountArgs, context: Context) -> CommandResult {\n    let output = context.output();\n\n    output.detail(format!("input: {}", args.sentence));\n    output.message("counting words");\n    output.artifact(&WordCount { words: 42 });\n\n    Ok(())\n}\n'})}),"\n",(0,r.jsxs)(t.p,{children:["The framework adds ",(0,r.jsx)(t.code,{children:"--quiet"}),", ",(0,r.jsx)(t.code,{children:"--verbose"}),", and ",(0,r.jsx)(t.code,{children:"--json"})," flags to every command\nautomatically. Your command doesn't need to declare or parse them."]}),"\n",(0,r.jsx)(t.h2,{id:"the-three-methods",children:"The three methods"}),"\n",(0,r.jsx)(t.h3,{id:"message",children:(0,r.jsx)(t.code,{children:"message"})}),"\n",(0,r.jsxs)(t.p,{children:["Writes an informational message. This is the replacement for ",(0,r.jsx)(t.code,{children:"println!"})," in most\ncases:"]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-rust",children:'output.message("deploying to production");\noutput.message(format!("found {} items", count));\n'})}),"\n",(0,r.jsxs)(t.p,{children:["Messages from ",(0,r.jsx)(t.code,{children:"message"})," are suppressed when the user passes ",(0,r.jsx)(t.code,{children:"--quiet"}),"."]}),"\n",(0,r.jsx)(t.h3,{id:"detail",children:(0,r.jsx)(t.code,{children:"detail"})}),"\n",(0,r.jsxs)(t.p,{children:["Writes a message that only appears with ",(0,r.jsx)(t.code,{children:"--verbose"}),". Use this for additional\ndetail that is helpful when debugging but noisy in normal usage:"]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-rust",children:'output.detail(format!("loading config from {}", path.display()));\noutput.detail("retrying connection");\n'})}),"\n",(0,r.jsx)(t.h3,{id:"artifact",children:(0,r.jsx)(t.code,{children:"artifact"})}),"\n",(0,r.jsxs)(t.p,{children:["Writes the primary data a command produces. In text mode, the value is\nformatted via ",(0,r.jsx)(t.a,{href:"https://doc.rust-lang.org/std/fmt/trait.Display.html",children:(0,r.jsx)(t.code,{children:"Display"})}),". In JSON mode, it is serialized via\n",(0,r.jsx)(t.a,{href:"https://docs.rs/serde/latest/serde/trait.Serialize.html",children:(0,r.jsx)(t.code,{children:"Serialize"})}),":"]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-rust",children:'use serde::Serialize;\nuse std::fmt;\n\n#[derive(Serialize)]\nstruct WordCount {\n    words: usize,\n}\n\nimpl fmt::Display for WordCount {\n    fn fmt(&self, f: &mut fmt::Formatter<\'_>) -> fmt::Result {\n        write!(f, "{}", self.words)\n    }\n}\n\noutput.artifact(&WordCount { words: 42 });\n'})}),"\n",(0,r.jsx)(t.p,{children:"Artifacts are always written, regardless of verbosity. This ensures that machine\nconsumers always get the data they need."}),"\n",(0,r.jsx)(t.h2,{id:"behavior-matrix",children:"Behavior matrix"}),"\n",(0,r.jsx)(t.p,{children:"The interaction of method, verbosity, and mode:"}),"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",(0,r.jsxs)(t.table,{children:[(0,r.jsx)(t.thead,{children:(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.th,{children:"Method"}),(0,r.jsx)(t.th,{children:"Default"}),(0,r.jsx)(t.th,{children:(0,r.jsx)(t.code,{children:"--quiet"})}),(0,r.jsx)(t.th,{children:(0,r.jsx)(t.code,{children:"--verbose"})}),(0,r.jsx)(t.th,{children:(0,r.jsx)(t.code,{children:"--json"})})]})}),(0,r.jsxs)(t.tbody,{children:[(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"message"})}),(0,r.jsx)(t.td,{children:"stdout"}),(0,r.jsx)(t.td,{children:"suppressed"}),(0,r.jsx)(t.td,{children:"stdout"}),(0,r.jsx)(t.td,{children:"stderr"})]}),(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"detail"})}),(0,r.jsx)(t.td,{children:"suppressed"}),(0,r.jsx)(t.td,{children:"suppressed"}),(0,r.jsx)(t.td,{children:"stdout"}),(0,r.jsx)(t.td,{children:"suppressed"})]}),(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"artifact"})}),(0,r.jsx)(t.td,{children:"stdout"}),(0,r.jsx)(t.td,{children:"stdout"}),(0,r.jsx)(t.td,{children:"stdout"}),(0,r.jsx)(t.td,{children:"stdout (as JSON)"})]})]})]}),"\n",(0,r.jsxs)(t.p,{children:["In JSON mode, ",(0,r.jsx)(t.code,{children:"message"})," and ",(0,r.jsx)(t.code,{children:"detail"})," output is redirected to stderr so that\nstdout contains only machine-readable JSON from ",(0,r.jsx)(t.code,{children:"artifact"}),". This follows the\nsame convention as ",(0,r.jsx)(t.code,{children:"gh"}),", ",(0,r.jsx)(t.code,{children:"kubectl"}),", and ",(0,r.jsx)(t.code,{children:"jq"}),"."]}),"\n",(0,r.jsx)(t.h2,{id:"output-macros",children:"Output macros"}),"\n",(0,r.jsxs)(t.p,{children:["The ",(0,r.jsx)(t.code,{children:"message!"}),", ",(0,r.jsx)(t.code,{children:"detail!"}),", and ",(0,r.jsx)(t.code,{children:"artifact!"})," macros provide shorthand for the\ncorresponding method calls. Each macro resolves ",(0,r.jsx)(t.code,{children:"context"})," at the call site, so\nyou do not need to extract the ",(0,r.jsx)(t.a,{href:"https://docs.rs/clawless/latest/clawless/output/stru
1ct.Output.html",children:(0,r.jsx)(t.code,{children:"Output"})})," instance yourself."]}),"\n",(0,r.jsx)(t.p,{children:"Here is the count example rewritten with macros:"}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-rust",children:'use clawless::prelude::*;\n\n/// Count words in a sentence\n#[command]\npub async fn count(args: CountArgs, context: Context) -> CommandResult {\n    detail!("input: {}", args.sentence);\n    message!("counting words");\n    artifact!(WordCount { words: 42 });\n\n    Ok(())\n}\n'})}),"\n",(0,r.jsxs)(t.p,{children:["The macros expand to ",(0,r.jsx)(t.code,{children:"context.output().message(...)"}),", ",(0,r.jsx)(t.code,{children:"context.output().detail(...)"}),",\nand ",(0,r.jsx)(t.code,{children:"context.output().artifact(...)"})," respectively. They resolve ",(0,r.jsx)(t.code,{children:"context"})," from\nthe enclosing function scope \u2014 every ",(0,r.jsx)(t.code,{children:"#[command]"})," function receives a ",(0,r.jsx)(t.code,{children:"context"}),"\nparameter, so no extra plumbing is needed."]}),"\n",(0,r.jsx)(t.h2,{id:"when-to-use-each-method",children:"When to use each method"}),"\n",(0,r.jsxs)(t.p,{children:[(0,r.jsx)(t.strong,{children:(0,r.jsx)(t.code,{children:"message"})})," for informational messages that describe what the command is doing.\nStatus updates, progress notes, and confirmations belong here."]}),"\n",(0,r.jsxs)(t.p,{children:[(0,r.jsx)(t.strong,{children:(0,r.jsx)(t.code,{children:"detail"})})," for information that helps with debugging or understanding internals.\nRaw inputs, resolved paths, retry attempts, and timing information belong here."]}),"\n",(0,r.jsxs)(t.p,{children:[(0,r.jsx)(t.strong,{children:(0,r.jsx)(t.code,{children:"artifact"})})," for the primary output of the command. If your command produces\ndata that another program might consume, use ",(0,r.jsx)(t.code,{children:"artifact"}),". The value must implement\nboth ",(0,r.jsx)(t.a,{href:"https://doc.rust-lang.org/std/fmt/trait.Display.html",children:(0,r.jsx)(t.code,{children:"Display"})})," and ",(0,r.jsx)(t.a,{href:"https://docs.rs/serde/latest/serde/trait.Serialize.html",children:(0,r.jsx)(t.code,{children:"Serialize"})}),"."]}),"\n",(0,r.jsxs)(t.p,{children:["Most commands only need ",(0,r.jsx)(t.code,{children:"message"}),". Commands that produce structured data use\n",(0,r.jsx)(t.code,{children:"artifact"}),". Commands with complex internals add ",(0,r.jsx)(t.code,{children:"detail"})," for observability."]}),"\n",(0,r.jsx)(t.h2,{id:"whats-next",children:"What's next"}),"\n",(0,r.jsxs)(t.ul,{children:["\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.strong,{children:(0,r.jsx)(t.a,{href:"./rendering",children:"Rendering"})})," - How output events reach the terminal through\npresenters and projections"]}),"\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.strong,{children:(0,r.jsx)(t.a,{href:"./context",children:"Context"})})," - The Context system that provides output to\ncommands"]}),"\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.strong,{children:(0,r.jsx)(t.a,{href:"./cancellation",children:"Cancellation"})})," - Cooperative shutdown through\ncancellation tokens"]}),"\n"]})]})}function h(e={}){let{wrapper:t}={...(0,d.R)(),...e.components};return t?(0,r.jsx)(t,{...e,children:(0,r.jsx)(l,{...e})}):l(e)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.