This guide covers best practices and patterns for writing scripts that work well with tome-cli.
- Basic Script Structure
- Multi-Language Support
- Help Text Guidelines
- Using Environment Variables
- Script Organization
- Best Practices
Every tome-cli script should follow this basic structure:
#!/usr/bin/env bash
# USAGE: script-name <required-arg> [optional-arg]
# Brief description of what the script does
#
# More detailed explanation that spans
# multiple lines until the first blank line.
# This entire block becomes the help text.
set -euo pipefail # Good practice for bash scripts
# Your script logic here
echo "Hello from script-name"- Shebang line (
#!/usr/bin/env bash): Tells the system how to execute your script - USAGE/SUMMARY comment: Defines the command syntax and generates help text
- Help text: Continues after USAGE/SUMMARY until first blank line
- Script logic: Your actual implementation
tome-cli supports both USAGE: and SUMMARY: keywords interchangeably:
USAGE:- Standard format (recommended)SUMMARY:- Legacy format from original tome (fully supported for backward compatibility)
Both work identically:
# Option 1: USAGE (recommended for new scripts)
# USAGE: my-script <arg>
# Description here
# Option 2: SUMMARY (legacy, but fully supported)
# SUMMARY: my-script <arg>
# Description hereBest Practice: Use USAGE: for new scripts. Use SUMMARY: if migrating from original tome or for consistency with existing scripts that use it.
tome-cli supports any language with a proper shebang. Here are examples:
#!/usr/bin/env bash
# USAGE: bash-example <name>
# Demonstrates a bash script
echo "Hello, $1!"#!/usr/bin/env python3
# USAGE: python-example <number>
# Demonstrates a Python script
#
# This script multiplies the input by 2
import sys
if len(sys.argv) < 2:
print("Error: number required", file=sys.stderr)
sys.exit(1)
number = int(sys.argv[1])
print(f"Result: {number * 2}")#!/usr/bin/env -S deno run --allow-all
// USAGE: typescript-example <message>
// Demonstrates a TypeScript/Deno script
const message = Deno.args[0] || "World";
console.log(`Hello, ${message}!`);#!/usr/bin/env ruby
# USAGE: ruby-example <count>
# Demonstrates a Ruby script
count = ARGV[0]&.to_i || 0
puts "Count: #{count}"#!/usr/bin/env node
// USAGE: node-example [options]
// Demonstrates a Node.js script
const args = process.argv.slice(2);
console.log('Arguments:', args);#!/usr/bin/env bash
# USAGE: deploy <environment> [--force]
# Deploy the application to the specified environment
#
# Arguments:
# environment - Target environment (development, staging, production)
#
# Options:
# --force - Skip confirmation prompts
#
# Examples:
# deploy staging
# deploy production --force- Clear usage syntax: Show required vs optional arguments
- Brief description: One-line summary of what it does
- Detailed explanation: Explain arguments and options
- Examples: Show real usage examples
- Stops at blank line: First blank line ends the help text
- Must use
USAGE:orSUMMARY:(case-sensitive, with colon) - Works with any comment style (
#for most languages,//for C-style) - Should appear in the first ~20 lines of the file
- Continues until the first blank comment line
- The USAGE/SUMMARY line itself is shown as the short help
- Subsequent lines become the detailed help
tome-cli automatically injects useful environment variables into your scripts:
| Variable | Description | Example |
|---|---|---|
TOME_ROOT |
Absolute path to your scripts directory | /home/user/my-scripts |
TOME_EXECUTABLE |
Name of the CLI command | tome-cli or kit |
{NAME}_ROOT |
Uppercase executable name + _ROOT | KIT_ROOT |
{NAME}_EXECUTABLE |
Uppercase executable name + _EXECUTABLE | KIT_EXECUTABLE |
#!/usr/bin/env bash
# USAGE: my-script [options]
# Script that uses shared libraries
# Load common functions from your scripts root
source "$TOME_ROOT/lib/common.sh"
# Now you can use functions from common.sh
common_function_name "$@"#!/usr/bin/env bash
# USAGE: orchestrate-tasks
# Runs multiple scripts in sequence
# Call other scripts via the same CLI
"$TOME_ROOT/path/to/setup-script"
"$TOME_ROOT/path/to/main-task"
"$TOME_ROOT/path/to/cleanup-script"#!/usr/bin/env bash
# USAGE: help-example
# Shows how to reference the CLI name
echo "Usage: $TOME_EXECUTABLE help-example [options]"
echo ""
echo "Run '$TOME_EXECUTABLE help help-example' for more information"#!/usr/bin/env bash
# USAGE: store-data <key> <value>
# Store data in a shared cache
CACHE_DIR="$TOME_ROOT/.cache"
mkdir -p "$CACHE_DIR"
echo "$2" > "$CACHE_DIR/$1"my-scripts/
├── .tomeignore # Files/patterns to ignore
├── lib/ # Shared libraries
│ └── common.sh
├── db/ # Database-related scripts
│ ├── backup
│ ├── restore
│ └── migrate
├── deploy/ # Deployment scripts
│ ├── staging
│ └── production
└── utils/ # Utility scripts
├── format-code
└── run-tests
Directories become command namespaces:
# Scripts in root: direct access
my-cli format-code
# Scripts in subdirectories: namespaced
my-cli db backup
my-cli db restore
my-cli deploy productionKeep shared libraries in a lib/ directory and exclude them from command listing:
# .tomeignore
lib/
*.sh # If you have a lib of .sh files that should be sourced, not executedThen source them in your scripts:
#!/usr/bin/env bash
source "$TOME_ROOT/lib/common.sh"Always remember to make your scripts executable:
chmod +x my-scriptOr for an entire directory:
chmod +x my-scripts/**/*#!/usr/bin/env bash
set -euo pipefail # Exit on error, undefined variables, pipe failures
# Validate required arguments
if [ $# -lt 1 ]; then
echo "Error: Missing required argument" >&2
exit 1
fiif [ ! -f "$config_file" ]; then
echo "Error: Config file not found: $config_file" >&2
echo "Run '$TOME_EXECUTABLE init' to create a config file" >&2
exit 1
fiConsider implementing these common flags:
case "${1:-}" in
-h|--help)
# Show help (or let tome-cli handle it)
$TOME_EXECUTABLE help "$(basename "$0")"
exit 0
;;
-v|--verbose)
VERBOSE=true
shift
;;
esacGood script names:
db-backup✓deploy-production✓format-yaml✓
Poor script names:
script1✗do-stuff✗temp✗
Each script should do one thing well. If a script is getting too large:
# Instead of one giant "deploy" script
deploy-prepare
deploy-build
deploy-upload
deploy-activate
# Or organize them
deploy/prepare
deploy/build
deploy/upload
deploy/activate# .gitignore for your scripts directory
.cache/
.env
*.log
node_modules/#!/usr/bin/env bash
# USAGE: complex-processing <input-file>
# Processes data with multiple transformations
# This script performs the following steps:
# 1. Validates input file format
# 2. Extracts relevant fields
# 3. Applies business logic transformations
# 4. Outputs results in JSON format
process_data() {
local input="$1"
# ... implementation
}#!/usr/bin/env bash
# USAGE: test-my-feature
# Tests the my-feature script
TOME_ROOT="${TOME_ROOT:-$(dirname "$0")/..}"
# Run your script with test inputs
result=$("$TOME_ROOT/my-feature" "test-input")
# Verify expected output
if [ "$result" = "expected-output" ]; then
echo "✓ Test passed"
exit 0
else
echo "✗ Test failed: got '$result', expected 'expected-output'" >&2
exit 1
fi# .tomeignore
# Ignore source files if you have compiled versions
*.ts
*.py
# Ignore test files
*_test
*.test.*
# Ignore hidden files
.*
# Ignore documentation
README.md
*.md
# Ignore specific directories
lib/
test/
.cache/#!/usr/bin/env bash
# USAGE: service <command> [options]
# Manage the service
#
# Commands:
# start - Start the service
# stop - Stop the service
# restart - Restart the service
# status - Check service status
set -euo pipefail
command="${1:-}"
shift || true
case "$command" in
start)
echo "Starting service..."
# start logic
;;
stop)
echo "Stopping service..."
# stop logic
;;
restart)
"$0" stop
"$0" start
;;
status)
echo "Checking service status..."
# status logic
;;
*)
echo "Error: Unknown command: $command" >&2
echo "Run '$TOME_EXECUTABLE help service' for usage" >&2
exit 1
;;
esac#!/usr/bin/env bash
# USAGE: configured-script [options]
# Script that reads from a config file
CONFIG_FILE="$TOME_ROOT/.config/my-config.json"
if [ ! -f "$CONFIG_FILE" ]; then
echo "Error: Config file not found: $CONFIG_FILE" >&2
echo "Run '$TOME_EXECUTABLE init-config' first" >&2
exit 1
fi
# Read config (example with jq for JSON)
api_key=$(jq -r '.api_key' "$CONFIG_FILE")
# Use the configuration
echo "Using API key: ${api_key:0:8}..."#!/usr/bin/env bash
# USAGE: long-running-task
# A task that shows progress
steps=("Initializing" "Processing data" "Validating results" "Cleaning up")
for i in "${!steps[@]}"; do
current=$((i + 1))
total=${#steps[@]}
echo "[$current/$total] ${steps[$i]}..."
# Do actual work here
sleep 1
done
echo "✓ Complete!"- Learn about adding completions to your scripts
- See working examples in examples/
- Read the migration guide if coming from tome v1/v2