Add cf tutorial and graceful Ctrl+C handling

- Add Interactive Tool Picker tutorial to web docs
- Handle SIGINT signal for clean exit on Ctrl+C
- Add KeyboardInterrupt handling in main()
- Add tutorial to Getting Started section in TOC

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rob 2026-01-18 03:49:18 -04:00
parent f6bb863f60
commit def8baf344
2 changed files with 108 additions and 1 deletions

View File

@ -329,11 +329,24 @@ def main():
"""Entry point for cf command."""
global _ui_out
import subprocess
import signal
# Handle Ctrl+C gracefully
def handle_sigint(sig, frame):
# Restore cursor and exit cleanly
sys.stderr.write(SHOW_CURSOR)
sys.stderr.flush()
sys.exit(0)
signal.signal(signal.SIGINT, handle_sigint)
# Read piped input if stdin is not a tty
piped_input = None
if not sys.stdin.isatty():
piped_input = sys.stdin.read()
try:
piped_input = sys.stdin.read()
except KeyboardInterrupt:
sys.exit(0)
# Check if we have a terminal available
if not os.path.exists('/dev/tty'):
@ -346,6 +359,9 @@ def main():
try:
with TTYInput() as tty_input:
result = run_picker(tty_input)
except KeyboardInterrupt:
_write(SHOW_CURSOR)
sys.exit(0)
except OSError as e:
print(f"cf requires a terminal: {e}", file=sys.stderr)
sys.exit(1)

View File

@ -231,6 +231,96 @@ cat complex_algorithm.py | explain --level expert</code></pre>
],
},
"interactive-picker": {
"title": "Interactive Tool Picker",
"description": "Use the cf command to quickly find and run tools",
"parent": "getting-started",
"content": """
<p class="lead">The <code>cf</code> command lets you browse, filter, and run your tools interactively
without remembering exact names. It's especially powerful when chaining tools together.</p>
<h2 id="basic-usage">Basic Usage</h2>
<p>Just type <code>cf</code> to open the picker:</p>
<pre><code class="language-bash">cf</code></pre>
<p>You'll see an inline dropdown with all your tools. Start typing to filter:</p>
<pre><code class="language-text">> sum
summarize - Summarize text using AI
summary-bullets - Convert text to bullet points
assume-role - Assume an expert persona</code></pre>
<h2 id="controls">Controls</h2>
<table class="w-full my-4">
<thead class="bg-gray-100">
<tr><th class="px-4 py-2 text-left">Key</th><th class="px-4 py-2 text-left">Action</th></tr>
</thead>
<tbody>
<tr class="border-b"><td class="px-4 py-2">Type characters</td><td class="px-4 py-2">Fuzzy filter by name or description</td></tr>
<tr class="border-b"><td class="px-4 py-2"> / </td><td class="px-4 py-2">Move selection up/down</td></tr>
<tr class="border-b"><td class="px-4 py-2">Enter</td><td class="px-4 py-2">Run the selected tool</td></tr>
<tr class="border-b"><td class="px-4 py-2">Tab</td><td class="px-4 py-2">Configure arguments (if tool has )</td></tr>
<tr class="border-b"><td class="px-4 py-2">Esc / Ctrl+C</td><td class="px-4 py-2">Cancel and exit</td></tr>
<tr><td class="px-4 py-2">Backspace</td><td class="px-4 py-2">Delete filter characters</td></tr>
</tbody>
</table>
<h2 id="piping-input">Piping Input</h2>
<p>Pipe content into <code>cf</code> just like any other tool:</p>
<pre><code class="language-bash"># Select a tool to process a file
cat article.txt | cf
# Pipe from another command
curl -s https://api.example.com/data | cf</code></pre>
<p>The picker appears, you choose a tool, and the piped content flows through it.</p>
<h2 id="chaining">Chaining Pickers</h2>
<p>The real power comes from chaining multiple pickers to build pipelines interactively:</p>
<pre><code class="language-bash"># Interactive two-stage pipeline
cat document.txt | cf | cf
# Three-stage processing
cf | cf | cf</code></pre>
<p>Each <code>cf</code> in the chain shows the picker. Select a tool, see its output, then
select another tool to process that output. You're building pipelines on the fly.</p>
<div class="bg-cyan-50 border-l-4 border-cyan-500 p-4 my-4">
<p class="font-semibold text-cyan-800">Example: Research Workflow</p>
<p class="text-cyan-700">
<code>cat notes.txt | cf | cf | cf</code><br><br>
Pick <strong>summarize</strong> Pick <strong>extract-keywords</strong> Pick <strong>format-markdown</strong><br><br>
You just built a summarize extract format pipeline without typing a single tool name.
</p>
</div>
<h2 id="configuring-args">Configuring Arguments</h2>
<p>Tools with arguments show a icon. Press <strong>Tab</strong> instead of Enter to configure them:</p>
<pre><code class="language-text">summarize arguments:
--max-length: 200 # Maximum summary length
--format: paragraph
Tab:edit Enter:run Esc:back</code></pre>
<p>Use / to select an argument, Tab to edit it, Enter to run with your settings.</p>
<h2 id="tips">Tips</h2>
<ul>
<li><strong>Fuzzy matching</strong> - Type "smz" to match "summarize", or search by description</li>
<li><strong>Fast selection</strong> - If the tool you want is highlighted, just hit Enter</li>
<li><strong>Escape anytime</strong> - Ctrl+C or Esc exits cleanly without running anything</li>
<li><strong>Combine with scripts</strong> - Use <code>cf</code> in shell scripts for interactive steps</li>
</ul>
""",
"headings": [
("basic-usage", "Basic Usage"),
("controls", "Controls"),
("piping-input", "Piping Input"),
("chaining", "Chaining Pickers"),
("configuring-args", "Configuring Arguments"),
("tips", "Tips"),
],
},
"publishing": {
"title": "Publishing Tools",
"description": "Share your tools with the CmdForge community",
@ -4226,6 +4316,7 @@ def get_toc():
SimpleNamespace(slug="getting-started", title="Getting Started", children=[
SimpleNamespace(slug="installation", title="Installation"),
SimpleNamespace(slug="first-tool", title="Your First Tool"),
SimpleNamespace(slug="interactive-picker", title="Interactive Picker (cf)"),
SimpleNamespace(slug="visual-builder", title="Visual Builder"),
SimpleNamespace(slug="yaml-config", title="YAML Config"),
]),