diff --git a/src/cmdforge/cli/picker.py b/src/cmdforge/cli/picker.py index 5ec4182..7567dd9 100644 --- a/src/cmdforge/cli/picker.py +++ b/src/cmdforge/cli/picker.py @@ -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) diff --git a/src/cmdforge/web/docs_content.py b/src/cmdforge/web/docs_content.py index 0386ea0..822547f 100644 --- a/src/cmdforge/web/docs_content.py +++ b/src/cmdforge/web/docs_content.py @@ -231,6 +231,96 @@ cat complex_algorithm.py | explain --level expert ], }, + "interactive-picker": { + "title": "Interactive Tool Picker", + "description": "Use the cf command to quickly find and run tools", + "parent": "getting-started", + "content": """ +

The cf command lets you browse, filter, and run your tools interactively +without remembering exact names. It's especially powerful when chaining tools together.

+ +

Basic Usage

+

Just type cf to open the picker:

+
cf
+ +

You'll see an inline dropdown with all your tools. Start typing to filter:

+
> sum▌
+▸ summarize ⚙ - Summarize text using AI
+  summary-bullets - Convert text to bullet points
+  assume-role - Assume an expert persona
+ +

Controls

+ + + + + + + + + + + + +
KeyAction
Type charactersFuzzy filter by name or description
↑ / ↓Move selection up/down
EnterRun the selected tool
TabConfigure arguments (if tool has ⚙)
Esc / Ctrl+CCancel and exit
BackspaceDelete filter characters
+ +

Piping Input

+

Pipe content into cf just like any other tool:

+
# Select a tool to process a file
+cat article.txt | cf
+
+# Pipe from another command
+curl -s https://api.example.com/data | cf
+ +

The picker appears, you choose a tool, and the piped content flows through it.

+ +

Chaining Pickers

+

The real power comes from chaining multiple pickers to build pipelines interactively:

+
# Interactive two-stage pipeline
+cat document.txt | cf | cf
+
+# Three-stage processing
+cf | cf | cf
+ +

Each cf 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.

+ +
+

Example: Research Workflow

+

+ cat notes.txt | cf | cf | cf

+ Pick summarize → Pick extract-keywords → Pick format-markdown

+ You just built a summarize → extract → format pipeline without typing a single tool name. +

+
+ +

Configuring Arguments

+

Tools with arguments show a ⚙ icon. Press Tab instead of Enter to configure them:

+
summarize arguments:
+▸ --max-length: 200 # Maximum summary length
+  --format: paragraph
+Tab:edit Enter:run Esc:back
+ +

Use ↑/↓ to select an argument, Tab to edit it, Enter to run with your settings.

+ +

Tips

+ +""", + "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"), ]),