"use strict";(globalThis.webpackChunkproject_public_docs=globalThis.webpackChunkproject_public_docs||[]).push([[530],{7367(e,n,i){i.r(n),i.d(n,{assets:()=>d,contentTitle:()=>o,default:()=>h,frontMatter:()=>t,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"reference/design","title":"CmdForge Design Document","description":"A lightweight personal tool builder for AI-powered CLI commands","source":"@site/docs/reference/design.md","sourceDirName":"reference","slug":"/reference/design","permalink":"/rob/CmdForge/reference/design","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":5,"frontMatter":{"sidebar_label":"Design Philosophy","sidebar_position":5,"format":"md"},"sidebar":"docs","previous":{"title":"Example Tools","permalink":"/rob/CmdForge/reference/examples"},"next":{"title":"Web UI Design","permalink":"/rob/CmdForge/reference/web-ui-spec"}}');var r=i(4848),l=i(8453);const t={sidebar_label:"Design Philosophy",sidebar_position:5,format:"md"},o="CmdForge Design Document",d={},c=[{value:"Overview",id:"overview",level:2},{value:"Core Concepts",id:"core-concepts",level:2},{value:"Tool = Directory + Config",id:"tool--directory--config",level:3},{value:"config.yaml Format",id:"configyaml-format",level:3},{value:"Step Types",id:"step-types",level:3},{value:"Variables",id:"variables",level:3},{value:"Output Variables",id:"output-variables",level:3},{value:"CLI Interface",id:"cli-interface",level:2},{value:"Running Tools",id:"running-tools",level:3},{value:"Input Handling",id:"input-handling",level:3},{value:"Universal Flags (all tools)",id:"universal-flags-all-tools",level:3},{value:"Managing Tools",id:"managing-tools",level:3},{value:"Tool Composition",id:"tool-composition",level:2},{value:"External Pipelines (Tool-to-Tool)",id:"external-pipelines-tool-to-tool",level:3},{value:"Internal Pipelines (Multi-Step)",id:"internal-pipelines-multi-step",level:3},{value:"What This Design Doesn't Include",id:"what-this-design-doesnt-include",level:2},{value:"Dependencies",id:"dependencies",level:2},{value:"Example Workflow",id:"example-workflow",level:2}];function a(e){const n={blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,l.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"cmdforge-design-document",children:"CmdForge Design Document"})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsx)(n.p,{children:"A lightweight personal tool builder for AI-powered CLI commands"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,r.jsx)(n.p,{children:"CmdForge lets you create custom AI-powered terminal commands. You define a tool once (name, steps, provider), then use it like any Linux command."}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Example:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# Create a summarizer tool, then use it:\nsum -i text.txt -o summary.txt --max 512\n"})}),"\n",(0,r.jsx)(n.h2,{id:"core-concepts",children:"Core Concepts"}),"\n",(0,r.jsx)(n.h3,{id:"tool--directory--config",children:"Tool = Directory + Config"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"~/.cmdforge/\n sum/\n config.yaml\n processed.py # Optional external code file\n reviewer/\n config.yaml\n translator/\n config.yaml\n"})}),"\n",(0,r.jsx)(n.h3,{id:"configyaml-format",children:"config.yaml Format"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",children:'name: sum\ndescription: "Summarize documents"\narguments:\n - flag: --max\n variable: max\n default: "500"\n description: "Maximum words in summary"\nsteps:\n - type: prompt\n prompt: |\n Summarize the following text in {max} words or less:\n\n {input}\n provider: claude\n output_var: response\noutput: "{response}"\n\n# Optional: declare dependencies for meta-tools\ndependencies:\n - official/summarize\n - official/translate@^1.0.0 # With version constraint\n'})}),"\n",(0,r.jsx)(n.h3,{id:"step-types",children:"Step Types"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Prompt Step"})," - Calls an AI provider:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",children:'- type: prompt\n prompt: "Your prompt template with {variables}"\n provider: claude\n output_var: response\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Code Step"})," - Runs Python code:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",children:"- type: code\n code: |\n processed = input.upper()\n count = len(processed.split())\n output_var: processed, count\n code_file: processed.py # Optional: external file storage\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Tool Step"})," - Calls another CmdForge tool (meta-tools):"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",children:'- type: tool\n tool: official/summarize # Tool reference (owner/name or just name for local)\n input: "{input}" # Input to pass (supports variable substitution)\n args: # Optional arguments\n max_words: "100"\n output_var: summary # Variable to store the tool\'s output\n provider: claude # Optional: override the called tool\'s provider\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Steps execute in order. Each step's ",(0,r.jsx)(n.code,{children:"output_var"})," becomes available to subsequent steps."]}),"\n",(0,r.jsx)(n.h3,{id:"variables",children:"Variables"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"{input}"})," - Always available, contains stdin or input file content (empty string if no input)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"{variable_name}"})," - From arguments (e.g., ",(0,r.jsx)(n.code,{children:"{max}"}),")"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"{output_var}"})," - From previous steps (e.g., ",(0,r.jsx)(n.code,{children:"{response}"}),", ",(0,r.jsx)(n.code,{children:"{processed}"}),")"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"output-variables",children:"Output Variables"}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"output_var"})," field specifies which Python variable(s) to capture from your code:"]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Single variable:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",children:"output_var: processed\n"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:"processed = input.upper() # This gets captured\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Multiple variables (comma-separated):"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",children:"output_var: processed, count, summary\n"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'processed = input.upper()\ncount = len(processed.split())\nsummary = f"Processed {count} words"\n# All three are captured and available as {processed}, {count}, {summary}\n'})}),"\n",(0,r.jsx)(n.h2,{id:"cli-interface",children:"CLI Interface"}),"\n",(0,r.jsx)(n.h3,{id:"running-tools",children:"Running Tools"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# Basic usage (wrapper script in ~/.local/bin)\nsum -i document.txt -o summary.txt\n\n# With custom args\nsum -i document.txt --max 200\n\n# Preview prompt without calling AI\nsum -i document.txt --dry-run\n\n# Test with mock (no API call)\nsum -i document.txt --provider mock\n\n# Read from stdin, write to stdout\ncat doc.txt | sum | less\n\n# Or via cmdforge run\ncmdforge run sum -i document.txt\n"})}),"\n",(0,r.jsx)(n.h3,{id:"input-handling",children:"Input Handling"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Scenario"}),(0,r.jsx)(n.th,{children:"Behavior"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Piped stdin"}),(0,r.jsxs)(n.td,{children:["Automatically read (",(0,r.jsx)(n.code,{children:"cat file.txt | mytool"}),")"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"-i file.txt"})}),(0,r.jsx)(n.td,{children:"Read from file"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"--stdin"})}),(0,r.jsx)(n.td,{children:"Interactive input (type then Ctrl+D)"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"No input"}),(0,r.jsx)(n.td,{children:"Empty string (useful for argument-only tools)"})]})]})]}),"\n",(0,r.jsx)(n.h3,{id:"universal-flags-all-tools",children:"Universal Flags (all tools)"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Flag"}),(0,r.jsx)(n.th,{children:"Short"}),(0,r.jsx)(n.th,{children:"Description"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"--input"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"-i"})}),(0,r.jsx)(n.td,{children:"Input file"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"--output"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"-o"})}),(0,r.jsx)(n.td,{children:"Output file (or stdout if omitted)"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"--stdin"})}),(0,r.jsx)(n.td,{}),(0,r.jsx)(n.td,{children:"Read input interactively (type then Ctrl+D)"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"--dry-run"})}),(0,r.jsx)(n.td,{}),(0,r.jsx)(n.td,{children:"Show prompt, don't call AI"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"--show-prompt"})}),(0,r.jsx)(n.td,{}),(0,r.jsx)(n.td,{children:"Call AI but also print prompt to stderr"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"--provider"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"-p"})}),(0,r.jsxs)(n.td,{children:["Override provider (e.g., ",(0,r.jsx)(n.code,{children:"--provider mock"}),")"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"--verbose"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"-v"})}),(0,r.jsx)(n.td,{children:"Show debug info"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"--help"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"-h"})}),(0,r.jsx)(n.td,{children:"Show help"})]})]})]}),"\n",(0,r.jsx)(n.h3,{id:"managing-tools",children:"Managing Tools"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"cmdforge list # List all tools\ncmdforge create sum # Create new tool (basic)\ncmdforge edit sum # Edit tool config in $EDITOR\ncmdforge delete sum # Delete tool\ncmdforge test sum # Test with mock provider\ncmdforge run sum # Run tool for real\ncmdforge refresh # Refresh all wrapper scripts\ncmdforge check sum # Check dependencies for meta-tools\ncmdforge ui # Launch interactive UI\n"})}),"\n",(0,r.jsx)(n.h2,{id:"tool-composition",children:"Tool Composition"}),"\n",(0,r.jsx)(n.p,{children:"CmdForge tools are designed to chain together like any Unix command."}),"\n",(0,r.jsx)(n.h3,{id:"external-pipelines-tool-to-tool",children:"External Pipelines (Tool-to-Tool)"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:'# Chain multiple CmdForge tools\ncat logs.txt | log-errors | summarize | translate --lang Japanese\n\n# Mix with standard Unix tools\ngit log --oneline | head -20 | changelog | tee CHANGELOG.md\n\n# Build complex workflows\ncat *.py | review-code --focus security | json-extract --fields "issue, severity, file" | json2csv\n'})}),"\n",(0,r.jsx)(n.p,{children:"Each tool reads stdin and writes stdout. No special integration needed."}),"\n",(0,r.jsx)(n.h3,{id:"internal-pipelines-multi-step",children:"Internal Pipelines (Multi-Step)"}),"\n",(0,r.jsx)(n.p,{children:"Within a single tool, chain steps for preprocessing/validation:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",children:"steps:\n - type: code # Preprocess\n code: |\n filtered = '\\n'.join(l for l in input.split('\\n') if 'ERROR' in l)\n output_var: filtered\n - type: prompt # AI processes filtered input\n prompt: \"Explain these errors: {filtered}\"\n provider: claude-haiku\n output_var: explanation\n - type: code # Post-process\n code: |\n result = f\"Found {len(filtered.split(chr(10)))} errors:\\n\\n{explanation}\"\n output_var: result\noutput: \"{result}\"\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"When to use which:"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"External pipelines"}),": Reusable tools, different providers per stage, standard Unix interop"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Internal pipelines"}),": Tightly coupled steps, shared context, validation of AI output"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"what-this-design-doesnt-include",children:"What This Design Doesn't Include"}),"\n",(0,r.jsx)(n.p,{children:"Intentionally omitted (not needed for personal use):"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Trust tiers / security levels"}),"\n",(0,r.jsx)(n.li,{children:"Cryptographic signing"}),"\n",(0,r.jsx)(n.li,{children:"Container isolation / sandboxing"}),"\n",(0,r.jsx)(n.li,{children:"Certification testing"}),"\n",(0,r.jsx)(n.li,{children:"Distribution packaging"}),"\n",(0,r.jsx)(n.li,{children:"PII redaction"}),"\n",(0,r.jsx)(n.li,{children:"Audit logging"}),"\n",(0,r.jsx)(n.li,{children:"Provider capability negotiation"}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Why?"})," This is a personal tool builder. You write the tools, you run the tools, you accept the responsibility. Just like any bash script you write."]}),"\n",(0,r.jsx)(n.h2,{id:"dependencies",children:"Dependencies"}),"\n",(0,r.jsx)(n.p,{children:"Required:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Python 3.10+"}),"\n",(0,r.jsx)(n.li,{children:"PyYAML"}),"\n",(0,r.jsx)(n.li,{children:"urwid (for TUI)"}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"Optional fallbacks:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"python3-newt/snack (simpler TUI)"}),"\n",(0,r.jsx)(n.li,{children:"dialog/whiptail (basic TUI)"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"example-workflow",children:"Example Workflow"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["Run ",(0,r.jsx)(n.code,{children:"cmdforge"})," to open UI"]}),"\n",(0,r.jsx)(n.li,{children:'Select "Create" to create a new tool'}),"\n",(0,r.jsx)(n.li,{children:"Fill in: name, description, output template"}),"\n",(0,r.jsxs)(n.li,{children:["Add arguments (e.g., ",(0,r.jsx)(n.code,{children:"--max"})," with default ",(0,r.jsx)(n.code,{children:"500"}),")"]}),"\n",(0,r.jsx)(n.li,{children:"Add a prompt step with your prompt template and provider"}),"\n",(0,r.jsx)(n.li,{children:'Click "Save"'}),"\n",(0,r.jsx)(n.li,{children:"Exit UI"}),"\n",(0,r.jsxs)(n.li,{children:["Run ",(0,r.jsx)(n.code,{children:"sum -i myfile.txt -o summary.txt"})]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"Done. No containers, no signing, no certification. Just a tool that works."})]})}function h(e={}){const{wrapper:n}={...(0,l.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(a,{...e})}):a(e)}},8453(e,n,i){i.d(n,{R:()=>t,x:()=>o});var s=i(6540);const r={},l=s.createContext(r);function t(e){const n=s.useContext(l);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:t(e.components),s.createElement(l.Provider,{value:n},e.children)}}}]);