@@ -189,6 +189,191 @@ arca_cli dev.deps
189189
190190## API Reference
191191
192+ ### Context Module (` Arca.Cli.Ctx ` )
193+
194+ The Context module provides a structured way to build command output.
195+
196+ #### Context Structure
197+
198+ ``` elixir
199+ %Arca .Cli .Ctx {
200+ command: atom (), # Command being executed
201+ args: map (), # Parsed arguments
202+ options: map (), # Command options
203+ output: list (), # Structured output items
204+ errors: list (), # Error messages
205+ status: atom (), # :ok | :error | :warning | :pending
206+ cargo: map (), # Command-specific data
207+ meta: map () # Style, format, and other metadata
208+ }
209+ ```
210+
211+ #### Core Functions
212+
213+ ** ` Ctx.new(command, settings) ` **
214+
215+ Creates a new context for a command.
216+
217+ ``` elixir
218+ ctx = Ctx .new (:my_command , settings)
219+ ```
220+
221+ ** ` Ctx.add_output(ctx, output_item) ` **
222+
223+ Adds an output item to the context.
224+
225+ ``` elixir
226+ ctx = Ctx .add_output (ctx, {:success , " Operation completed" })
227+ ```
228+
229+ ** ` Ctx.add_error(ctx, error_message) ` **
230+
231+ Adds an error message to the context.
232+
233+ ``` elixir
234+ ctx = Ctx .add_error (ctx, " File not found" )
235+ ```
236+
237+ ** ` Ctx.complete(ctx, status) ` **
238+
239+ Marks the context as complete with a final status.
240+
241+ ``` elixir
242+ ctx = Ctx .complete (ctx, :ok ) # or :error, :warning
243+ ```
244+
245+ ** ` Ctx.put_cargo(ctx, key, value) ` ** / ** ` Ctx.get_cargo(ctx, key) ` **
246+
247+ Stores and retrieves command-specific data.
248+
249+ ``` elixir
250+ ctx = Ctx .put_cargo (ctx, :user_count , 42 )
251+ count = Ctx .get_cargo (ctx, :user_count )
252+ ```
253+
254+ #### Output Item Types
255+
256+ ** Messages**
257+
258+ ``` elixir
259+ {:success , message} # Green checkmark in ANSI mode
260+ {:error , message} # Red X in ANSI mode
261+ {:warning , message} # Yellow warning symbol in ANSI mode
262+ {:info , message} # Cyan info symbol in ANSI mode
263+ {:text , content} # Plain text
264+ ```
265+
266+ ** Tables**
267+
268+ ``` elixir
269+ # With explicit headers (columns in header order)
270+ {:table , rows, headers: [" Name" , " Age" , " City" ]}
271+
272+ # Override column order
273+ {:table , rows,
274+ headers: [" Name" , " Age" , " City" ],
275+ column_order: [" City" , " Age" , " Name" ]
276+ }
277+
278+ # First row as headers (preserves row order)
279+ {:table , [headers_row | data_rows], has_headers: true }
280+
281+ # Alphabetical column order (default when no headers/column_order)
282+ {:table , rows, []}
283+
284+ # Descending alphabetical
285+ {:table , rows, column_order: :desc }
286+ ```
287+
288+ ** Table Options:**
289+
290+ - ` headers ` : List of column header names (also sets column order)
291+ - ` column_order ` : Explicit column ordering (list, ` :asc ` , ` :desc ` , or custom function)
292+ - ` has_headers ` : Boolean, treats first row as headers
293+ - ` border_style ` : ` :solid ` , ` :solid_rounded ` , etc.
294+ - ` divide_body_rows ` : Boolean, add lines between rows
295+ - ` padding_x ` : Integer, horizontal padding in cells
296+
297+ ** Lists**
298+
299+ ``` elixir
300+ # Simple list
301+ {:list , [" Item 1" , " Item 2" , " Item 3" ]}
302+
303+ # List with title
304+ {:list , items, title: " Available Options" }
305+
306+ # List with custom bullet color (ANSI mode)
307+ {:list , items, bullet_color: :green }
308+ ```
309+
310+ ** Interactive Elements (ANSI mode only)**
311+
312+ ``` elixir
313+ # Spinner
314+ {:spinner , " Loading data" , fn ->
315+ result = perform_operation ()
316+ {:ok , result}
317+ end }
318+
319+ # Progress indicator
320+ {:progress , " Processing files" , fn ->
321+ result = process_files ()
322+ {:ok , result}
323+ end }
324+ ```
325+
326+ ### Output Module (` Arca.Cli.Output ` )
327+
328+ The Output module handles rendering of contexts to different formats.
329+
330+ ** ` Output.render(ctx) ` **
331+
332+ Renders a context to a string using the appropriate style.
333+
334+ ``` elixir
335+ output = Arca .Cli .Output .render (ctx)
336+ IO .puts (output)
337+ ```
338+
339+ ** Style Detection**
340+
341+ The output style is automatically determined:
342+
343+ 1 . Plain style if ` MIX_ENV=test `
344+ 2 . Plain style if ` NO_COLOR=1 `
345+ 3 . Style from ` ARCA_STYLE ` environment variable (ansi, plain, json, dump)
346+ 4 . Plain style if not a TTY
347+ 5 . ANSI style otherwise (default for interactive terminals)
348+
349+ ### Renderers
350+
351+ ** AnsiRenderer** - Colored output with formatting
352+
353+ - Green checkmarks for success
354+ - Red X for errors
355+ - Yellow warnings
356+ - Cyan info messages
357+ - Rounded borders for tables
358+ - Colored bullet points for lists
359+
360+ ** PlainRenderer** - No ANSI codes
361+
362+ - Simple symbols (✓, ✗, ⚠)
363+ - Box-drawing characters for tables
364+ - Plain bullet points for lists
365+ - Suitable for tests and non-TTY environments
366+
367+ ** JsonRenderer** - JSON structured output
368+
369+ - Outputs context as JSON
370+ - Useful for programmatic consumption
371+
372+ ** DumpRenderer** - Raw data inspection
373+
374+ - Shows internal structure
375+ - Useful for debugging
376+
192377### Command Definition
193378
194379Commands are defined using the ` Arca.Cli.Command.BaseCommand ` module:
@@ -216,6 +401,58 @@ defmodule YourApp.Cli.Commands.GreetCommand do
216401end
217402```
218403
404+ ### Context-Based Command Definition
405+
406+ Commands can return structured output using the Context system:
407+
408+ ``` elixir
409+ defmodule YourApp .Cli .Commands .UserCommand do
410+ use Arca .Cli .Command .BaseCommand
411+ alias Arca .Cli .Ctx
412+
413+ config :user ,
414+ name: " user" ,
415+ about: " Display user information" ,
416+ args: [
417+ username: [
418+ value_name: " USERNAME" ,
419+ help: " Username to lookup" ,
420+ required: true ,
421+ parser: :string
422+ ]
423+ ]
424+
425+ @impl true
426+ def handle (args, settings, _optimus ) do
427+ username = args.args.username
428+
429+ Ctx .new (:user , settings)
430+ |> fetch_user_data (username)
431+ |> format_output ()
432+ |> Ctx .complete (:ok )
433+ end
434+
435+ defp fetch_user_data (ctx, username) do
436+ case Database .get_user (username) do
437+ {:ok , user} ->
438+ Ctx .put_cargo (ctx, :user , user)
439+
440+ {:error , reason} ->
441+ ctx
442+ |> Ctx .add_error (" User not found: #{ reason } " )
443+ |> Ctx .complete (:error )
444+ end
445+ end
446+
447+ defp format_output (%Ctx {status: :error } = ctx), do: ctx
448+ defp format_output (%Ctx {cargo: %{user: user}} = ctx) do
449+ ctx
450+ |> Ctx .add_output ({:success , " User found: #{ user.name } " })
451+ |> Ctx .add_output ({:table , [user], headers: [" id" , " name" , " email" , " created_at" ]})
452+ end
453+ end
454+ ```
455+
219456### Namespaced Command Definition
220457
221458Using the NamespaceCommandHelper for multiple related commands:
0 commit comments