Skip to content

harshit-vibes/cf

Repository files navigation

cf - Codeforces CLI

A command-line tool and Go SDK for competitive programming with Codeforces integration.

Go Version License CI

Features

  • Codeforces API Client - Full-featured API client with caching and rate limiting
  • Web Scraper - Parse problem statements, samples, and metadata
  • Workspace Management - Organize problems locally with versioned schemas
  • Health Checks - Verify system configuration and connectivity
  • TUI Dashboard - Beautiful terminal UI (coming soon)
  • Cross-Platform - Works on Linux, macOS, and Windows

Installation

Using Go (requires Go 1.22+)

go install github.com/harshit-vibes/cf/cmd/cf@latest

Download Binary

Download pre-built binaries from the Releases page.

macOS (Apple Silicon)

curl -Lo cf https://github.com/harshit-vibes/cf/releases/latest/download/cf-darwin-arm64
chmod +x cf
sudo mv cf /usr/local/bin/

macOS (Intel)

curl -Lo cf https://github.com/harshit-vibes/cf/releases/latest/download/cf-darwin-amd64
chmod +x cf
sudo mv cf /usr/local/bin/

Linux (amd64)

curl -Lo cf https://github.com/harshit-vibes/cf/releases/latest/download/cf-linux-amd64
chmod +x cf
sudo mv cf /usr/local/bin/

Linux (arm64)

curl -Lo cf https://github.com/harshit-vibes/cf/releases/latest/download/cf-linux-arm64
chmod +x cf
sudo mv cf /usr/local/bin/

Build from Source

git clone https://github.com/harshit-vibes/cf.git
cd cf
make build
sudo mv build/cf /usr/local/bin/

Quick Start

# Initialize a workspace
cf init ~/codeforces-practice

# Check system health
cf health

# Parse a problem from Codeforces
cf problem parse 1 A

# View your profile
cf user info

# List recent contests
cf contest list

# Show your statistics
cf stats

CLI Commands

Core Commands

Command Description
cf init [path] Initialize a new workspace
cf health Check system health and configuration
cf version Show version information

Problem Commands (cf problem, cf p)

Command Description
cf problem parse <contest> <index> Parse a problem from Codeforces
cf problem list [--tag TAG] [--min-rating N] [--max-rating N] List problems with filters
cf problem fetch <contest> [index] Fetch problem(s) to workspace
# Parse problem A from contest 1
cf problem parse 1 A

# List DP problems rated 1200-1400
cf problem list --tag dp --min-rating 1200 --max-rating 1400

# Fetch all problems from contest 1234
cf problem fetch 1234

User Commands (cf user, cf u)

Command Description
cf user info [handle] Show user profile information
cf user submissions [handle] [--limit N] Show recent submissions
cf user rating [handle] Show rating history
# View your profile
cf user info

# View tourist's submissions
cf user submissions tourist --limit 20

# View your rating history
cf user rating

Contest Commands (cf contest, cf c)

Command Description
cf contest list [--gym] [--limit N] List contests
cf contest problems <contest_id> Show contest problems
# List upcoming contests
cf contest list --limit 10

# Show problems from contest 1234
cf contest problems 1234

Statistics (cf stats)

# View your practice statistics
cf stats

# View another user's stats
cf stats tourist

Configuration (cf config)

Command Description
cf config get [key] Show configuration value(s)
cf config set <key> <value> Set a configuration value
cf config path Show config file paths
# View all configuration
cf config get

# Set your CF handle
cf config set cf_handle your_handle

# Set difficulty range
cf config set difficulty.min 1000
cf config set difficulty.max 1600

Workspace Structure

After running cf init, your workspace looks like:

workspace/
├── workspace.yaml      # Workspace manifest
├── problems/           # Problem metadata and statements
├── submissions/        # Your solutions
└── stats/              # Progress tracking

Configuration

Config File

Configuration is stored in ~/.cf/config.yaml:

cf_handle: your_handle
cookie: "JSESSIONID=xxx; 39ce7=xxx; cf_clearance=xxx"
difficulty:
  min: 800
  max: 1400
daily_goal: 3
workspace_path: /path/to/workspace

Setting Your Handle

cf config set cf_handle your_codeforces_handle

Setting Up Cookie Authentication

The cookie is required for features like solution submission. Here's how to get it:

  1. Open Codeforces in your browser and log in
  2. Open Developer Tools (F12 or Cmd+Option+I)
  3. Go to the Network tab
  4. Refresh the page or navigate to any Codeforces page
  5. Click on any request to codeforces.com (e.g., the main document)
  6. Find the "Cookie" header in the Request Headers section
  7. Copy the entire cookie string - it should look like:
    JSESSIONID=24FF903C9002F539DCDE4C869C77C1DD; 39ce7=CFtzSSKd; cf_clearance=abc123...
    
  8. Set it in cf:
    cf config set cookie 'JSESSIONID=24FF903C9002F539DCDE4C869C77C1DD; 39ce7=CFtzSSKd; cf_clearance=...'

Note: Cookies expire periodically (especially cf_clearance). If you encounter authentication errors, repeat this process to get fresh cookies.

Configuration Options

Key Description Default
cf_handle Your Codeforces username (required)
cookie Browser cookie string for authenticated requests (optional)
difficulty.min Minimum problem difficulty for recommendations 800
difficulty.max Maximum problem difficulty for recommendations 1400
daily_goal Number of problems to solve per day 3
workspace_path Path to your workspace directory current directory

Using as a Go SDK

Installation

go get github.com/harshit-vibes/cf

Codeforces API Client

package main

import (
    "fmt"
    "github.com/harshit-vibes/cf/pkg/external/cfapi"
)

func main() {
    // Create client (no auth needed for public endpoints)
    client := cfapi.NewClient()

    // Get user info
    users, err := client.GetUserInfo([]string{"tourist"})
    if err != nil {
        panic(err)
    }
    fmt.Printf("Rating: %d\n", users[0].Rating)

    // Get problems
    problems, err := client.GetProblems([]string{"dp"})
    if err != nil {
        panic(err)
    }
    fmt.Printf("Found %d DP problems\n", len(problems))
}

Web Parser

package main

import (
    "fmt"
    "github.com/harshit-vibes/cf/pkg/external/cfweb"
)

func main() {
    parser := cfweb.NewParser()

    // Parse a problem
    problem, err := parser.ParseProblem(1, "A")
    if err != nil {
        panic(err)
    }

    fmt.Printf("Problem: %s\n", problem.Name)
    fmt.Printf("Rating: %d\n", problem.Rating)
    fmt.Printf("Samples: %d\n", len(problem.Samples))
}

Workspace Management

package main

import (
    "github.com/harshit-vibes/cf/pkg/internal/workspace"
)

func main() {
    // Open workspace
    ws := workspace.New("./my-workspace")

    // Initialize if needed
    if !ws.Exists() {
        ws.Init("My Practice", "my_cf_handle")
    }

    // List problems
    problems, _ := ws.ListProblems()
    for _, p := range problems {
        fmt.Printf("%s: %s\n", p.ID, p.Name)
    }
}

Development

Building

# Development build
make dev

# Production build
make build

# Cross-compile
make build-all

Testing

# Run tests
make test

# With coverage
make test-coverage

Project Structure

cf/
├── cmd/cf/              # CLI entry point
├── pkg/
│   ├── cmd/             # CLI commands
│   ├── tui/             # TUI components (coming soon)
│   ├── internal/
│   │   ├── config/      # Configuration
│   │   ├── health/      # Health checks
│   │   ├── workspace/   # Workspace management
│   │   ├── schema/      # Data schemas
│   │   └── errors/      # Error handling
│   └── external/
│       ├── cfapi/       # Codeforces API
│       ├── cfweb/       # Web scraping
│       └── health/      # External checks
├── Makefile
└── README.md

Roadmap

  • TUI Dashboard
  • Problem recommendations
  • Solution submission
  • Contest participation
  • Progress analytics

License

MIT

About

A beautiful terminal UI for practicing Codeforces problems

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors