diff --git a/complete/src/fish.rs b/complete/src/fish.rs
index f86fc93..d6f0f66 100644
--- a/complete/src/fish.rs
+++ b/complete/src/fish.rs
@@ -1,19 +1,22 @@
 // For the full copyright and license information, please view the LICENSE
 // file that was distributed with this source code.
 
-use crate::{Command, ValueHint};
+use crate::{Command, Flag, ValueHint};
 
 /// Create completion script for `fish`
+///
+/// Short and long options are combined into single `complete` calls, even if
+/// they differ in whether they take arguments or not.
 pub fn render(c: &Command) -> String {
     let mut out = String::new();
     let name = &c.name;
     for arg in &c.args {
         let mut line = format!("complete -c {name}");
-        for short in &arg.short {
-            line.push_str(&format!(" -s {short}"));
+        for Flag { flag, .. } in &arg.short {
+            line.push_str(&format!(" -s {flag}"));
         }
-        for long in &arg.long {
-            line.push_str(&format!(" -l {long}"));
+        for Flag { flag, .. } in &arg.long {
+            line.push_str(&format!(" -l {flag}"));
         }
         line.push_str(&format!(" -d '{}'", arg.help));
         if let Some(value) = &arg.value {
@@ -42,15 +45,18 @@ fn render_value_hint(value: &ValueHint) -> String {
 #[cfg(test)]
 mod test {
     use super::render;
-    use crate::{Arg, Command, ValueHint};
+    use crate::{Arg, Command, Flag, Value, ValueHint};
 
     #[test]
     fn short() {
         let c = Command {
-            name: "test".into(),
+            name: "test",
             args: vec![Arg {
-                short: vec!["a".into()],
-                help: "some flag".into(),
+                short: vec![Flag {
+                    flag: "a",
+                    value: Value::No,
+                }],
+                help: "some flag",
                 ..Arg::default()
             }],
             ..Command::default()
@@ -61,10 +67,13 @@ mod test {
     #[test]
     fn long() {
         let c = Command {
-            name: "test".into(),
+            name: "test",
             args: vec![Arg {
-                long: vec!["all".into()],
-                help: "some flag".into(),
+                long: vec![Flag {
+                    flag: "all",
+                    value: Value::No,
+                }],
+                help: "some flag",
                 ..Arg::default()
             }],
             ..Command::default()
@@ -92,11 +101,14 @@ mod test {
         ];
         for (hint, expected) in args {
             let c = Command {
-                name: "test".into(),
+                name: "test",
                 args: vec![Arg {
-                    short: vec!["a".into()],
+                    short: vec![Flag {
+                        flag: "a",
+                        value: Value::No,
+                    }],
                     long: vec![],
-                    help: "some flag".into(),
+                    help: "some flag",
                     value: Some(hint),
                 }],
                 ..Command::default()
diff --git a/complete/src/lib.rs b/complete/src/lib.rs
index b703d64..14a3a47 100644
--- a/complete/src/lib.rs
+++ b/complete/src/lib.rs
@@ -1,28 +1,58 @@
 // For the full copyright and license information, please view the LICENSE
 // file that was distributed with this source code.
 
+//! Generation of completion and documentation
+//!
+//! All formats use the [`Command`] struct as input, which specifies all
+//! information needed. This struct is similar to some structs in the derive
+//! crate for uutils-args, but there are some key differences:
+//!
+//!  - This is meant to be more general.
+//!  - Some information is added (such as fields for the summary)
+//!  - We have [`ValueHint`] in this crate.
+//!  - Some information is removed because it is irrelevant for completion and documentation
+//!  - This struct is meant to exist at runtime of the program
+//!
 mod fish;
 mod man;
 mod md;
 mod zsh;
 
+/// A description of a CLI command
+///
+/// The completions and documentation will be generated based on this struct.
 #[derive(Default)]
-pub struct Command {
-    pub name: String,
-    pub summary: String,
-    pub version: String,
-    pub after_options: String,
-    pub args: Vec<Arg>,
+pub struct Command<'a> {
+    pub name: &'a str,
+    pub summary: &'a str,
+    pub version: &'a str,
+    pub after_options: &'a str,
+    pub args: Vec<Arg<'a>>,
 }
 
+/// Description of an argument
+///
+/// An argument may consist of several flags. In completions and documentation
+/// formats that support it, these flags will be grouped.
 #[derive(Default)]
-pub struct Arg {
-    pub short: Vec<String>,
-    pub long: Vec<String>,
-    pub help: String,
+pub struct Arg<'a> {
+    pub short: Vec<Flag<'a>>,
+    pub long: Vec<Flag<'a>>,
+    pub help: &'a str,
     pub value: Option<ValueHint>,
 }
 
+pub struct Flag<'a> {
+    pub flag: &'a str,
+    pub value: Value<'a>,
+}
+
+pub enum Value<'a> {
+    Required(&'a str),
+    Optional(&'a str),
+    No,
+}
+
 // Modelled after claps ValueHint
 pub enum ValueHint {
     Strings(Vec<String>),
diff --git a/complete/src/man.rs b/complete/src/man.rs
index 34d92aa..494f828 100644
--- a/complete/src/man.rs
+++ b/complete/src/man.rs
@@ -1,36 +1,61 @@
 // For the full copyright and license information, please view the LICENSE
 // file that was distributed with this source code.
 
-use crate::Command;
-use roff::{bold, roman, Roff};
+use crate::{Command, Flag, Value};
+use roff::{bold, italic, roman, Roff};
 
 pub fn render(c: &Command) -> String {
     let mut page = Roff::new();
     page.control("TH", [&c.name.to_uppercase(), "1"]);
     page.control("SH", ["NAME"]);
-    page.text([roman(&c.name)]);
+    page.text([roman(c.name)]);
     page.control("SH", ["DESCRIPTION"]);
-    page.text([roman(&c.summary)]);
+    page.text([roman(c.summary)]);
     page.control("SH", ["OPTIONS"]);
 
     for arg in &c.args {
         page.control("TP", []);
 
         let mut flags = Vec::new();
-        for l in &arg.long {
+        for Flag { flag, value } in &arg.long {
             if !flags.is_empty() {
                 flags.push(roman(", "));
             }
-            flags.push(bold(format!("--{l}")));
+            flags.push(bold(format!("--{flag}")));
+            match value {
+                Value::Required(name) => {
+                    flags.push(roman("="));
+                    flags.push(italic(*name));
+                }
+                Value::Optional(name) => {
+                    flags.push(roman("["));
+                    flags.push(roman("="));
+                    flags.push(italic(*name));
+                    flags.push(roman("]"));
+                }
+                Value::No => {}
+            }
         }
-        for s in &arg.short {
+        for Flag { flag, value } in &arg.short {
             if !flags.is_empty() {
                 flags.push(roman(", "));
             }
-            flags.push(bold(format!("-{s}")));
+            flags.push(bold(format!("-{flag}")));
+            match value {
+                Value::Required(name) => {
+                    flags.push(roman(" "));
+                    flags.push(italic(*name));
+                }
+                Value::Optional(name) => {
+                    flags.push(roman("["));
+                    flags.push(italic(*name));
+                    flags.push(roman("]"));
+                }
+                Value::No => {}
+            }
         }
         page.text(flags);
-        page.text([roman(&arg.help)]);
+        page.text([roman(arg.help)]);
     }
     page.render()
 }
diff --git a/complete/src/md.rs b/complete/src/md.rs
index 08f502f..1c2fa1b 100644
--- a/complete/src/md.rs
+++ b/complete/src/md.rs
@@ -1,18 +1,18 @@
 // For the full copyright and license information, please view the LICENSE
 // file that was distributed with this source code.
 
-use crate::Command;
+use crate::{Command, Flag, Value};
 
 /// Render command to a markdown file for mdbook
 pub fn render(c: &Command) -> String {
     let mut out = String::new();
     out.push_str(&title(c));
     out.push_str(&additional(c));
-    out.push_str(&c.summary);
+    out.push_str(c.summary);
     out.push_str("\n\n");
     out.push_str(&options(c));
     out.push_str("\n\n");
-    out.push_str(&c.after_options);
+    out.push_str(c.after_options);
     out.push('\n');
     out
 }
@@ -40,12 +40,22 @@ fn options(c: &Command) -> String {
 
         let mut flags = Vec::new();
 
-        for long in &arg.long {
-            flags.push(format!("<code>--{long}</code>"));
+        for Flag { flag, value } in &arg.long {
+            let value_str = match value {
+                Value::Required(name) => format!("={name}"),
+                Value::Optional(name) => format!("[={name}]"),
+                Value::No => String::new(),
+            };
+            flags.push(format!("<code>--{flag}{value_str}</code>"));
         }
 
-        for short in &arg.short {
-            flags.push(format!("<code>-{short}</code>"))
+        for Flag { flag, value } in &arg.short {
+            let value_str = match value {
+                Value::Required(name) => format!(" {name}"),
+                Value::Optional(name) => format!("[{name}]"),
+                Value::No => String::new(),
+            };
+            flags.push(format!("<code>-{flag}{value_str}</code>"));
         }
 
         out.push_str(&flags.join(", "));
diff --git a/complete/src/zsh.rs b/complete/src/zsh.rs
index 814c904..5087697 100644
--- a/complete/src/zsh.rs
+++ b/complete/src/zsh.rs
@@ -1,11 +1,11 @@
 // For the full copyright and license information, please view the LICENSE
 // file that was distributed with this source code.
 
-use crate::{Arg, Command};
+use crate::{Arg, Command, Flag};
 
 /// Create completion script for `zsh`
 pub fn render(c: &Command) -> String {
-    template(&c.name, &render_args(&c.args))
+    template(c.name, &render_args(&c.args))
 }
 
 fn render_args(args: &[Arg]) -> String {
@@ -13,11 +13,11 @@ fn render_args(args: &[Arg]) -> String {
     let indent = " ".repeat(8);
     for arg in args {
         let help = &arg.help;
-        for short in &arg.short {
-            out.push_str(&format!("{indent}'-{short}[{help}]' \\\n"));
+        for Flag { flag, .. } in &arg.short {
+            out.push_str(&format!("{indent}'-{flag}[{help}]' \\\n"));
         }
-        for long in &arg.long {
-            out.push_str(&format!("{indent}'--{long}[{help}]' \\\n"));
+        for Flag { flag, .. } in &arg.long {
+            out.push_str(&format!("{indent}'--{flag}[{help}]' \\\n"));
         }
     }
     out
diff --git a/derive/src/complete.rs b/derive/src/complete.rs
index 2d7b365..9e9ea96 100644
--- a/derive/src/complete.rs
+++ b/derive/src/complete.rs
@@ -3,7 +3,7 @@
 
 use crate::{
     argument::{ArgType, Argument},
-    flags::{Flag, Flags},
+    flags::{Flag, Flags, Value},
 };
 use proc_macro2::TokenStream;
 use quote::quote;
@@ -40,12 +40,33 @@ pub fn complete(args: &[Argument], file: &Option<String>) -> TokenStream {
 
         let short: Vec<_> = short
             .iter()
-            .map(|Flag { flag, .. }| quote!(String::from(#flag)))
+            .map(|Flag { flag, value }| {
+                let flag = flag.to_string();
+                let value = match value {
+                    Value::No => quote!(::uutils_args_complete::Value::No),
+                    Value::Optional(name) => quote!(::uutils_args_complete::Value::Optional(#name)),
+                    Value::Required(name) => quote!(::uutils_args_complete::Value::Optional(#name)),
+                };
+                quote!(::uutils_args_complete::Flag {
+                    flag: #flag,
+                    value: #value
+                })
+            })
             .collect();
 
         let long: Vec<_> = long
             .iter()
-            .map(|Flag { flag, .. }| quote!(String::from(#flag)))
+            .map(|Flag { flag, value }| {
+                let value = match value {
+                    Value::No => quote!(::uutils_args_complete::Value::No),
+                    Value::Optional(name) => quote!(::uutils_args_complete::Value::Optional(#name)),
+                    Value::Required(name) => quote!(::uutils_args_complete::Value::Optional(#name)),
+                };
+                quote!(::uutils_args_complete::Flag {
+                    flag: #flag,
+                    value: #value
+                })
+            })
             .collect();
 
         let hint = if let Some(ty) = field {
@@ -55,20 +76,20 @@ pub fn complete(args: &[Argument], file: &Option<String>) -> TokenStream {
         };
 
         arg_specs.push(quote!(
-            Arg {
+            ::uutils_args_complete::Arg {
                 short: vec![#(#short),*],
                 long: vec![#(#long),*],
-                help: String::from(#help),
+                help: #help,
                 value: #hint,
             }
         ))
     }
 
-    quote!(Command {
-        name: String::from(option_env!("CARGO_BIN_NAME").unwrap_or(env!("CARGO_PKG_NAME"))),
-        summary: String::from(#summary),
-        after_options: String::from(#after_options),
-        version: String::from(env!("CARGO_PKG_VERSION")),
+    quote!(::uutils_args_complete::Command {
+        name: option_env!("CARGO_BIN_NAME").unwrap_or(env!("CARGO_PKG_NAME")),
+        summary: #summary,
+        after_options: #after_options,
+        version: env!("CARGO_PKG_VERSION"),
         args: vec![#(#arg_specs),*]
     })
 }
diff --git a/derive/src/lib.rs b/derive/src/lib.rs
index e855127..cd8ccbf 100644
--- a/derive/src/lib.rs
+++ b/derive/src/lib.rs
@@ -107,8 +107,7 @@ pub fn arguments(input: TokenStream) -> TokenStream {
             }
 
             #[cfg(feature = "complete")]
-            fn complete() -> ::uutils_args_complete::Command {
-                use ::uutils_args_complete::{Command, Arg, ValueHint};
+            fn complete() -> ::uutils_args_complete::Command<'static> {
                 use ::uutils_args::Value;
                 #complete_command
             }
diff --git a/src/lib.rs b/src/lib.rs
index 22d124b..5ec42d2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -110,7 +110,7 @@ pub trait Arguments: Sized {
     }
 
     #[cfg(feature = "complete")]
-    fn complete() -> uutils_args_complete::Command;
+    fn complete() -> uutils_args_complete::Command<'static>;
 }
 
 /// An iterator over arguments.