Loading...
Loading...
Generate AI-agent-first CLIs from any API (OpenAPI, GraphQL, or browser-sniffed) with SQLite sync, compound commands, and MCP servers
npx skill4agent add aradotso/devtools-skills cli-printing-press-generatorSkill by ara.so — Devtools Skills collection.
--compactstalehealthbottleneck<api>-pp-cli<api>-pp-mcp--compact--dry-rungo install github.com/mvanhorn/cli-printing-press/v4/cmd/printing-press@latestprinting-press --versiongit pullgit clone https://github.com/mvanhorn/cli-printing-press.git
cd cli-printing-press# Load skills directly from this repo
claude --plugin-dir .
# Or in a new git worktree (for parallel runs)
claude --plugin-dir . -w/printing-press <api-name>
/printing-press <url>
/printing-press <api-name> codex# Generate from API name (auto-discovers docs/specs)
/printing-press Notion
# Generate from website (browser-sniff traffic)
/printing-press https://postman.com/explore
# Use Codex for code generation (60% fewer Opus tokens)
/printing-press HubSpot codex
# Reprint existing CLI under latest machine
/printing-press-reprint notion/printing-press-polish <api-name>/printing-press-polish linear/printing-press-publish <api-name>/printing-press-publish superhuman/printing-press-amend
/printing-press-amend <api-name>printing-press# Research phase
printing-press research <api-name> --output ./output
# Generate full CLI
printing-press generate notion --output ~/clis/notion-pp-cli
# Verify generated CLI
printing-press verify ~/clis/notion-pp-cli
# Score quality
printing-press scorecard ~/clis/notion-pp-cli
# Dogfood test
printing-press dogfood ~/clis/notion-pp-cli~/printing-press/
├── .runstate/<scope>/runs/<run-id>/working/<api>-pp-cli/ # Active runs
├── library/<api>/ # Published CLIs
└── manuscripts/<api>/<run-id>/ # Archived runs
├── research/
├── proofs/
├── discovery/
└── pipeline/<scope>printing-press generate stripe --output /custom/path/stripe-cli# API authentication
export NOTION_API_KEY="secret_..."
export LINEAR_API_KEY="lin_api_..."
export GITHUB_TOKEN="ghp_..."
# SQLite store location (override default)
export NOTION_PP_STORE_PATH="/custom/notion.db"
# Refresh settings for auto data-source
export LINEAR_PP_REFRESH_TTL="5m"# Interactive auth setup (writes to ~/.config/<api>-pp-cli/config.yaml)
notion-pp-cli auth login
# Or set directly
export NOTION_API_KEY="secret_abc123"# Auto mode: refresh if TTL expired (default)
linear-pp-cli issues list --data-source auto
# Force live API call
linear-pp-cli issues list --data-source live
# Local-only (offline)
linear-pp-cli issues list --data-source local# Initial sync
notion-pp-cli sync
# Incremental sync
notion-pp-cli sync --incremental
# Full-text search (FTS5)
notion-pp-cli search "authentication flow"
# Direct SQL query
notion-pp-cli sql "SELECT title FROM pages WHERE updated_at > date('now', '-7 days')"# Auto-JSON when piped
linear-pp-cli issues list | jq '.[] | select(.priority == "urgent")'
# Compact mode (60-80% fewer tokens)
linear-pp-cli issues list --compact
# Dry-run (safe exploration)
linear-pp-cli issues create --title "Test" --dry-run
# Typed exit codes
linear-pp-cli issues get ISSUE-123
echo $? # 0=success, 2=not found, 3=auth, 4=validation, 5=server, 7=offline# Linear example
linear-pp-cli stale --threshold 7d # Issues blocked >7 days
linear-pp-cli health --team backend # Team velocity metrics
linear-pp-cli bottleneck # Most-blocking issues
# Discord example
discord-pp-cli knowledge --channel docs # Thread knowledge graph
discord-pp-cli stale-threads --days 30 # Unanswered >30 days
# Stripe example
stripe-pp-cli churn-signals # Failed charges + cancellations
stripe-pp-cli cohort-health --month 2026-04package main
import (
"context"
"fmt"
"os"
"os/exec"
"encoding/json"
)
// Call generated Linear CLI
func getBlockedIssues() ([]Issue, error) {
cmd := exec.Command("linear-pp-cli", "stale",
"--threshold", "7d",
"--output", "json")
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("CLI error: %w", err)
}
var issues []Issue
if err := json.Unmarshal(output, &issues); err != nil {
return nil, err
}
return issues, nil
}
// Use MCP server programmatically
func callMCPServer(ctx context.Context, method string, params map[string]interface{}) error {
// MCP servers expose stdio interface
cmd := exec.CommandContext(ctx, "linear-pp-mcp")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
if err := cmd.Start(); err != nil {
return err
}
req := map[string]interface{}{
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": 1,
}
json.NewEncoder(stdin).Encode(req)
var resp map[string]interface{}
json.NewDecoder(stdout).Decode(&resp)
return cmd.Wait()
}#!/bin/bash
# Deploy blocker detection script
# Sync latest data
notion-pp-cli sync --incremental
# Find stale blockers
BLOCKERS=$(notion-pp-cli sql "
SELECT b.id, b.title, i.title as blocked_issue
FROM issues i
JOIN issues b ON i.blocker_id = b.id
WHERE b.state = 'in_progress'
AND b.updated_at < date('now', '-7 days')
" --output json)
# Send to Slack if any found
if [ "$(echo "$BLOCKERS" | jq 'length')" -gt 0 ]; then
echo "$BLOCKERS" | jq -r '.[] | "⚠️ \(.blocked_issue) blocked by stale: \(.title)"' \
| slack-cli send --channel "#deploy-alerts"
fi// Add to cmd/custom/reconcile.go in generated CLI
package custom
import (
"github.com/spf13/cobra"
"github.com/mvanhorn/linear-pp-cli/internal/store"
)
func NewReconcileCmd(st *store.Store) *cobra.Command {
cmd := &cobra.Command{
Use: "reconcile",
Short: "Find issues in API but missing from local store",
RunE: func(cmd *cobra.Command, args []string) error {
// Query local store
localIDs := st.GetAllIssueIDs()
// Query live API
liveIDs := fetchLiveIssueIDs()
// Diff
missing := difference(liveIDs, localIDs)
for _, id := range missing {
fmt.Printf("Missing: %s\n", id)
}
return nil
},
}
return cmd
}# Check if API has publicly accessible docs
curl -I https://developers.notion.com
# Try explicit spec URL
/printing-press https://raw.githubusercontent.com/notion/openapi/main/spec.yaml
# Use browser-sniff mode for private APIs
/printing-press https://internal-tool.company.comtail -f ~/.printing-press/.runstate/<scope>/runs/<run-id>/logs/generation.log# Run polish to auto-fix
/printing-press-polish <api-name>
# Check specific failure
printing-press verify ~/printing-press/library/<api> --verbose# Verify credentials work manually
export API_KEY="your_key"
curl -H "Authorization: Bearer $API_KEY" https://api.service.com/test
# Check generated auth.go
cat ~/printing-press/library/<api>-pp-cli/internal/auth/auth.go# Use incremental sync with backoff
api-pp-cli sync --incremental --rate-limit 10/min
# Check cursor tracking
api-pp-cli sql "SELECT resource, cursor, updated_at FROM sync_cursors"# Rebuild FTS5 index
api-pp-cli sql "DELETE FROM pages_fts"
api-pp-cli sync --rebuild-index
# Verify FTS table
api-pp-cli sql "SELECT * FROM sqlite_master WHERE type='table' AND name LIKE '%_fts'"# Check if required tables exist
api-pp-cli sql ".schema" | grep -A 5 "CREATE TABLE"
# Run with debug output
api-pp-cli stale --threshold 7d --log-level debug# Test stdio interface directly
echo '{"jsonrpc":"2.0","method":"ping","id":1}' | linear-pp-mcp
# Check MCP manifest
cat ~/printing-press/library/linear-pp-mcp/mcp.json~/.config/claude-code/mcp-servers.json{
"linear": {
"command": "/path/to/linear-pp-mcp",
"env": {
"LINEAR_API_KEY": "${LINEAR_API_KEY}"
}
}
}# Enable batch mode
api-pp-cli sync --batch-size 1000 --workers 4
# Use partial sync
api-pp-cli sync --resources "issues,comments" --since "2026-01-01"# Analyze query plan
api-pp-cli sql "EXPLAIN QUERY PLAN SELECT * FROM pages_fts WHERE pages_fts MATCH 'search term'"
# Add covering index
api-pp-cli sql "CREATE INDEX idx_pages_updated ON pages(updated_at DESC)"--compact