Skip to main content

42TUI

Refresh the Intra project graph

The project_data.json used by the local TypeScript graph tools is served by the Projects website, which uses a browser session. An API OAuth bearer token does not create that website session. To refresh the graph automatically:

  1. Install dependencies with npm install, then install the browser with npx playwright install chromium. On a machine with a compatible Chromium already installed, set BROWSER_EXECUTABLE instead.
  2. Copy .env.example to .env and set INTRA_LOGIN, INTRA_PASSWORD, CURSUS_ID, and CAMPUS_ID. Keep .env private; it is gitignored.
  3. Run bun fetch-graph-auto.ts. The first login checks Remember me if the 42 login page offers it. Later runs reuse the saved browser profile in .local/intra-graph-browser/. If the session expires, the script logs in again with the credentials in .env.

The output is graph.<cursus>.<campus>.json, which is gitignored. A failed login or non-JSON response leaves the previous graph file in place. For a scheduled server refresh, run the same command from the repository root with a private .env; do not run overlapping jobs against the same browser profile. If 42 asks for MFA or presents a challenge, the unattended login stops and needs a fresh interactive authentication. HEADLESS=false opens a browser for diagnosing a changed login flow.

42TUI is an offline-first Python TUI for managing projects, work time, and notes for École 42 students. Local project data remains in SQLite. Optional 42 account connection uses a centralized Bun API with PostgreSQL, Redis, browser-based OAuth, and device-bound sessions; the local app does not receive the user's 42 password or OAuth client secret. See the OAuth backend operations runbook to deploy that service.

Getting started

Production installation

Once the installer service and first PyPI release are live, install on Linux/macOS:

curl -fsSL https://tui.42wip.in/install.sh | sh

The installer uses uv, obtains Python >=3.12 when needed (including on Ubuntu 22.04 with system Python 3.10), and installs 42tui in an isolated user environment. Restart your terminal if needed, then launch 42tui. Optional Nerd Font installation is available with --font; you must select the font in your terminal settings.

Windows support is experimental; a PowerShell installer, exact-version installs, upgrades, uninstall and migration from pipx are described in the production deployment guide.

Development from this checkout

Python 3.12 or newer is required (including the textual-image dependency).

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
42tui

For editable pipx installation, use the repository's AGENTS.md instructions with an explicitly compatible Python interpreter.

The database is created under $XDG_DATA_HOME/42tui/42tui.sqlite3 (or ~/.local/share/42tui/42tui.sqlite3). Existing projects remain available locally without an account connection. Connect a 42 account in Settings to synchronize real projects from the centralized API. Choosing Save + Set Up Checkout can clone the development repository URL entered for a project. Uninstalling the application preserves this local data.

Project browsing

The catalogue uses official project IDs while SQLite keeps its own IDs for notes, timers and checkout folders. Shared API metadata, the cursus/campus web graph and account progression are stored separately. Choose the cursus and campus in Settings. Common Core views display old + shared or new + shared exclusively. When the available evidence cannot select a track, choose one in Settings. Empty quests do not establish the old track or rank zero. Track preferences are scoped to the account, cursus and campus.

My 42 projects shows account projects in the selected track and the known current and next Common Core ranks. Rank resolution is automatic and uses quest evidence first. When rank quests are absent, an active, unvalidated rank-0 project can infer rank 0 from personal progress (source: project). This requires fresh personal sources and a fresh graph for the selected cursus/campus, a resolved track, and no validated rank-0 project, higher-rank attempt, specialization attempt or missing classification. Empty history alone never implies rank 0. Saved manual ranks are ignored. Project ranks are inferred from graph rules, not official API levels. All projects shows every Common Core project in the selected track until a quest confirms Common Core completion, then shows specialization projects. An unknown personal rank still permits browsing the full Common Core catalogue. Registerable projects uses only the account's registration API response, including ID-only projects whose catalogue description is unavailable. Specializations groups branches within the selected track. Active and local projects keeps local work accessible when the track is unknown. Search, type filters and pagination apply to the selected view. In My 42 projects, choosing a specific type browses all matching projects in the selected track, including later ranks and specializations, even when your personal rank is unknown. Returning to All types restores the current/next rank view. Other views keep their section, registration or local-work limits. The browser starts in My 42 projects. Loaded projects are sorted by ascending rank, with exams first within each rank, then by name. Unranked projects appear last. Exams have a dedicated badge and accent border. Click a rank header or press Enter on it to collapse or expand the group. Group state is kept during the session for each account, cursus/campus and view. Rank headers show completed/total counts for loaded projects matching the filters. Headers and project names use status colors: green for completed, accent for active, amber for partial progress or available, red for failed, and muted for inactive. Project status unknown is displayed as INACTIVE.

Details show exams, web difficulty as XP (including zero), nullable duration, classification provenance, structured requirements, recommendations and raw unparsed rules. Drawing links are visual relationships, not prerequisites. Project details show Solo or Group when matching session metadata is available, including in cached views. Known group sizes appear as Group · 3 members; missing or conflicting session counts remain unspecified. Register submits directly through the API; 42 validates eligibility even when team mode is unknown. API registration requires the projects scope and fresh project history and catalog data. Registration refreshes status and prevents duplicate submissions; creating or joining a team is a separate step. The Intra project link remains available for the complete requirements.

The backend publishes validated API/browser snapshots through a separate ingestion worker. TUI reads never start a browser. Catalogue and branch caches are scoped by cursus, campus and snapshot; personal observations are isolated by account. Offline views show saved data with its collection time and availability. Registration is fresh for five minutes, progression/quests for fifteen minutes and catalogue for 24 hours. An unavailable source preserves its prior observation as stale.

The TUI downloads all catalogue pages for both tracks and the unclassified section of the selected cursus/campus into the existing SQLite database. Reopening the app reuses that complete local copy for 24 hours; searches and project filters run locally. Project details are saved on first selection and reused for the same snapshot, including offline. Account progress still refreshes separately at startup. Expired catalogues remain visible while a replacement downloads; failed or interrupted downloads preserve the previous complete snapshot. No database reset is needed for this cache.

The last known dashboard profile and goals are also saved locally. After a successful sync, reopening with the same device credentials restores the selected cursus, track, rank, Journey and account details before network refreshes complete. The profile snapshot is bound to the backend and credential fingerprints; tokens and private keys are never stored in this cache. Saved goals are marked stale while refreshing, and pending or failed observations preserve the last known display.

To check the cache against synthetic HTTP responses, run postman simulation run postman/simulations/catalog-cache.sim.yaml --port 4901 and, in another terminal, CATALOG_CACHE_SIMULATION_URL=http://127.0.0.1:4901/backend PYTHONPATH=src .venv/bin/python -m unittest tests.test_catalog_cache_simulation.

The new SQLite schema refuses older databases with an explicit reset instruction. Back up local notes and activity before intentionally deleting an old database; the application never resets it automatically.

Project testers

In Projects, use the Testers button beside the project name or press t to reveal and focus the tester controls. The Testers section offers Install Francinette, then Run Francinette, for libft, ft_printf and get_next_line (including their 42cursus- slugs). One image serves all three projects. Installation needs no project folder; running needs an existing local folder configured with e. Born2beroot has no bundled tester yet.

42TUI fetches a pinned commit of WaRtr0/francinette-image and builds its Dockerfile, without running its installer or editing shell configuration. Docker uses linux/amd64 or linux/arm64 on Linux and macOS with x86_64 (Intel/AMD) or ARM64 processors. Francinette prefers the Docker engine's native architecture, avoiding x86_64 emulation on ARM64. This also respects the CPU of a remote Docker engine. 32-bit x86 is unsupported. Docker Engine, Docker Desktop and other compatible local engines use the standard Docker CLI. The active context and DOCKER_* variables are respected and kept for the entire operation, including cleanup. A Linux engine is required. Before running, 42TUI checks the selected container architecture and reads/writes a temporary probe inside the project folder to verify local file sharing and permissions. On Linux, a regular daemon runs testers with your UID/GID; rootless Docker retains its mapped container root. Francinette uses a private writable copy of its code for logs and caches, so generated project files remain owned by your account. During each run, private Clang wrappers default debug information to DWARF 4, compatible with the upstream Ubuntu 22.04 image's Valgrind. Memory checks remain enabled; compiler arguments and explicit debug-format options are preserved. The wrappers and writable tester copy are removed when the run finishes. The upstream Dockerfile fetches dependencies at build time; pinning the wrapper repository does not freeze those dependencies. The temporary container mounts the project read/write at /project and runs Francinette from its writable copy, keeping /project as the working directory. Tester output appears in the terminal; press any key to return to 42TUI. Ctrl+C interrupts the operation and removes its temporary container. An exit status of zero means the tester finished; inspect its output for individual test results. Logs are saved under $XDG_DATA_HOME/42tui/testers/logs, or ~/.local/share/42tui/testers/logs. Failed operations offer Retry.

The small × button beside a tester removes its Docker images (all installed versions and CPU architectures) and stopped 42TUI test/probe containers. It also works without a configured project folder. Since the installation is shared, removing Francinette affects all three projects; use Install Francinette to restore it. Running containers, project files, other testers and Docker volumes are left in place. Docker can retain shared layers and build cache, so removing the images does not guarantee reclaiming their entire displayed size. See Docker's resource cleanup documentation.

42TUI reuses working Docker installations. Otherwise it distinguishes a missing CLI, a stopped engine and denied access. Check Docker access again retries detection after setup. On macOS, Install Docker offers Homebrew when available, or downloads and opens the official installer for the host processor. Finish Docker Desktop setup yourself and start Docker before rechecking. On supported Ubuntu and Debian releases, the confirmation dialog lists the commands for Docker's official apt repository before any administrator command runs. Conflicting system packages must be resolved manually; 42TUI does not remove them. Other distributions link to the official installation instructions. For access problems, follow the Linux post-install steps, then sign out and back in; never run the entire application with sudo.

The bundled ft_tui/testers.toml is loaded at startup and extended by $XDG_CONFIG_HOME/42tui/testers.toml, falling back to ~/.config/42tui/testers.toml. Each user entry replaces the entire entry with the same id; multiple entries may target a project. Disable a bundled tester with:

[[testers]]
id = "francinette"
enabled = false

To add a tester, copy this template and replace the repository, full commit SHA, Docker build context and command with real values. The command is an argument array executed inside the container, without shell expansion. Repository URLs must use HTTPS, context paths must remain inside the repository, and platforms are linux and/or darwin:

[[testers]]
id = "my-tester"
name = "My tester"
projects = ["libft", "42cursus-ft_printf"]
platforms = ["linux", "darwin"]
repository = "https://github.com/your-account/your-tester.git"
ref = "0123456789abcdef0123456789abcdef01234567"
context = "."
command = ["/tester/run", "--verbose"]

Restart 42TUI to reload changes. Invalid user TOML or entries display an error and leave the complete bundled catalog usable. Image identities include the tester, commit, repository and context; availability is always checked against Docker. ARM64 images have a separate tag, so existing amd64 images are preserved. On ARM64, install the native image once from the tester controls after upgrading. Optional architectures = ["amd64", "arm64"] allows native execution for custom testers that support both CPUs. Existing custom entries default to ["amd64"]; a tester restricted to one CPU still requires emulation on other CPUs. When the engine's CPU is unavailable, the first configured architecture is used. See the verification report for tested environments and remaining platform checks.

Project checkout setup

In Projects, select a project and press e. Choose its local folder and optional development remote, then select Save + Set Up Checkout. 42TUI clones the development remote into a missing or empty folder, or initializes an empty local Git repository when no development remote is set. Existing repositories are preserved; non-empty non-Git folders and conflicting remote URLs are rejected.

When the checkout has commits, setup ensures local dev and master branches. A cloned checkout switches to dev, associated with origin/dev. master is reserved for Vogsphere, whose accepted submission branch is master; once the Intra API supplies its URL, 42TUI configures vogsphere/master. No branch is pushed automatically. Add a real development URL yourself if you want setup to clone it. For a new empty local repository, Git cannot store a master branch before the first commit; run setup again after creating that commit to create master.

In project details, Start timer records a manual work session in SQLite. A running timer survives closing and reopening 42TUI; starting a timer for another project stops the previous one. Subject checks run at the interval selected in Settings (six hours by default). 42TUI looks for subject.pdf at the project root, calculates its SHA-256, and asks the configured provider for a reference fingerprint. The backend queues subject work for the unified Intra worker, which uses the same authenticated browser profile as Graph and Pace. It discovers the subject link on the project page, prefers English, validates the PDF, and caches it with its SHA-256 in Redis. The HTTP handler never opens a browser or calls the restricted attachments endpoint. The TUI shows SUBJECT · QUEUED while waiting and checks for the result every 30 seconds. Missing or different PDFs are atomically installed as subject.pdf; failed downloads preserve the existing file. Configured folders work with or without a Git repository. Worker snapshots are fresh for six hours and retained for 24 hours. PDF contents are not extracted. Project setup adds *.pdf to .gitignore so a downloaded untracked subject isn't accidentally committed; PDFs already tracked by Git remain tracked until removed manually. Use Open subject to launch the PDF with the OS default application or TDF, selected in Settings. TDF must be installed separately and available on PATH.

Keyboard shortcuts

  • j / k: move down or up through projects
  • h / l: switch to the previous or next tab
  • e: set up or edit the selected project's local path and development remote
  • Home / End: select the first or last project
  • Ctrl+D / Ctrl+U: move down or up one page
  • F1–F5: help, projects, notes, Git Sync, and settings
  • q: quit

In Settings, choose a favorite editor (Neovim, Vim, Visual Studio Code, or Zed) and a favorite Textual theme. From Projects, select a configured project and press o or use Open in editor to open its folder. Terminal editors temporarily take over the terminal; GUI editors open separately.

In Settings → Appearance, image rendering defaults to Automatic: 42TUI uses the Sixel or Kitty graphics backend selected by textual-image, with colored Unicode blocks as a fallback. You can force either protocol, select colored blocks, or turn images off. Forced protocols require support in the terminal and any multiplexer between it and 42TUI. Rendering is selected by protocol support, not CPU/GPU hardware.

Icons have independent Automatic, Nerd Font, Simple Unicode, and Text only modes, with a preview. Automatic mode uses terminal/environment hints, not a scan of installed fonts: those do not establish which font the terminal uses, especially over SSH. NERDFONT=1 or NERDFONT=0 overrides the automatic estimate. An explicit icon choice in Settings takes precedence. These preferences are saved locally and apply without restarting; font shape and pixel size remain controlled by the terminal.

42goto shell navigation

42goto changes the current shell directory to a project's configured local folder. Enable the shell function and project completion in your shell startup file:

# Bash: add to ~/.bashrc
eval "$(command 42goto --shell-init bash)"

# Zsh: add to ~/.zshrc
eval "$(command 42goto --shell-init zsh)"

Then run 42goto to jump to the currently active project, or 42goto <project> to open a project by its name or slug. Press Tab to complete configured project slugs. A fresh editor heartbeat defines the active project; when there is no fresh heartbeat, the most recently tracked project is used. If there is no activity history, a single configured in-progress project is used; otherwise 42goto asks you to specify the project. Project folders are read from the same local SQLite database as 42TUI. After updating a source checkout, reinstall the package to refresh the CLI entry points:

pip install -e .

Editor activity tracking

42TUI accepts local activity heartbeats from editor plugins, even when the TUI is closed. Configure the project folder in 42TUI first, then have an editor integration call the shared CLI command when the active file changes, is edited, or is saved:

42tui heartbeat --file /absolute/path/to/project/src/main.c --editor nvim --quiet
42tui heartbeat --file /absolute/path/to/project/src/main.c --editor vscode --write --quiet

The CLI finds the deepest configured project folder containing the file. It stores the project, editor, event time, and save flag in SQLite; it does not store the file path or read file contents. Elapsed time is attributed between recent heartbeats, with a 120-second idle cutoff and a shared timeline across editors to prevent double counting. Files outside configured project folders are ignored with an error. Editor-specific plugins can call this command, so each integration only needs to report its active file and editor ID; installing the app itself requires no network access. See integrations/ for the current VS Code, Zed, Vim, and Neovim adapters.

Roadmap

Implemented for the local MVP:

  • Local project catalog, dashboard, and SQLite persistence; centralized API project sync after OAuth connection
  • Profile settings, default projects folder, and per-project local path / development remote setup
  • Project navigation, Git hygiene, manual timer, PDF fingerprinting, and Markdown dev log
  • Local editor heartbeat ingestion and project-level coding time aggregation
  • Dashboard activity streaks, four-week heatmap, peak coding day/hour, and rolling validation pace against Black Hole deadlines
  • Editor integrations for VS Code, Zed, Vim, and Neovim
  • First-run onboarding for profile, projects folder, editor, theme, and language
  • English, French, and Spanish interface with JSON catalogs and English fallback

Available with the optional OAuth backend:

  • Browser-based 42 account pairing and profile/project synchronization, while preserving local SQLite data

Planned:

  • Fetch and display achievement stars for outstanding project evaluations
  • Automatically fetch the official Vogsphere remote from 42 Intra
  • Peer-Review Matcher: use the 42 API to show available defense slots, consider correction points and availability, notify about useful openings, and keep an evaluation history with grades and feedback
  • Cluster Map: simulate the campus's three clusters as a seat map, showing per-seat occupancy and available seats, plus the 42 profile and project information available for each seated student
  • Friends & Study Groups: use the 42 API to automatically sync friends' project enrollment and progress, with activity and completion feeds, Black Hole pace comparisons, and cluster presence

Source layout

Run the service regression tests from the project root after installing the package:

python -m unittest discover -s tests -v
src/ft_tui/
├── api/          # centralized API client and provider contract
├── config/       # local paths and settings
├── db/           # models and SQLite access
├── services/     # business logic independent from the UI
├── ui/           # Textual application and views
├── locales/      # English-fallback JSON catalogs (French and Spanish)
└── main.py       # CLI entry point

To add another interface language, add src/ft_tui/locales/<code>.json with English source messages as keys, then register its code and display name in ft_tui/i18n.py. Any untranslated message falls back to English.

Project attempts and evaluations

For a connected account, select a project and open Attempts & evaluations from Project Details. The dedicated screen selects the current team and lets you browse previous attempts, members, scores, repository and lock/closure dates. Expand peer corrections to read comments and available feedback; expand automated evaluations to read their output. Scheduled, overdue/unfilled and completed corrections have separate labels. Missing marks remain distinct from zero.

Use arrow keys to select an attempt, Tab/Enter to expand an evaluation, r to refresh, i to open Intra, and Escape to return to the selected project. The layout stacks on terminals narrower than 90 columns. Activity, evaluation details, automated output and embedded team results are cached by account for up to 30 days. Reopening the screen reuses these observations; press Refresh to fetch changes from Intra. Cached sources show their collection time separately; unavailable fields and hidden participants stay explicit. Team management, booking and feedback submission remain planned in TODO.md.

Both the updated backend and TUI are required. Live 42 response parity remains unverified. See the HTTP simulation instructions for local integration verification and the sandbox limitation.

Friends and rankings

Connect your 42 account and select a cursus in Settings, then open Friends. Add friends by their 42 login to see their level, ongoing projects and recent cluster location. Open a friend for project history or “Compare with me” to find shared projects and opportunities to help each other. Rankings compares cursus working curriculum ranks among your friends and yourself, with equal places for tied ranks. Published old/new cursus project mappings determine the estimate; level remains visible.

Search and filters cover shared projects, campus membership and active cluster sessions. Enter opens a profile; Escape returns to the list. Narrow terminals show one pane at a time. Saved progression remains available offline, while cluster presence always requires a recent successful lookup. Refresh runs in the background while the tab is open; progression caches refresh every 15 minutes and presence every minute. The updated backend must be deployed before using online Friends. Campus rankings and historical growth remain follow-up work in TODO.md.

Search in Friends and Projects accepts abbreviations and small typos, ignores case, accents and separators, and supports multiple words in any order. For example, pswp or puhs swap finds push_swap. Friends search also matches display names, campuses and ongoing projects; rankings retain their original positions when filtered. Project search behaves consistently offline and in catalog results. Deploy the updated backend to enable fuzzy catalog queries.

The Add friend field searches a shared student directory stored in the backend. Type at least two characters, press Down to browse matches and Enter to choose a login, then select Add friend. It matches names, abbreviations and small typos, excludes existing friends and your account, and works across campuses. Results show when the index is building or stale. Selecting Add verifies the numeric user ID and handles renamed logins. Exact-login entry remains available. Deploy the new migration and run the directory worker to populate and refresh the index; typing never scans the 42 API.

Clubs

Open Clubs to create an invite-only campus group, add registered 42TUI students, and recruit players with Babyfoot, Smash or custom templates. Members join or waitlist; organizers start ready activities manually. Club managers and configured campus representatives can publish announcements.

The global DND button (Ctrl+Shift+D) suspends Clubs notifications. Each club has its own bell; added members and the campus group start muted. Invitations appear at the top right without taking focus (Ctrl+Shift+N focuses them). Activities and announcements remain browsable while muted. Spontaneous popups require fresh campus presence on an unlocked workstation.

Apply the Clubs database migration and restart both backend and TUI before use. See the MVP guide for deployment, current limitations and verification, and the roadmap for later phases.

Metadata

Release files for ft-tui 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ft-tui 0.2.2
File Size Uploaded Provenance
ft_tui-0.2.2.tar.gz 287.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for ft-tui 0.2.2
File Interpreter ABI Platform
ft_tui-0.2.2-py3-none-any.whl Python 3 none any Details

Total release size: 624.6 kB

Release files / ft_tui-0.2.2.tar.gz

Download URL ft_tui-0.2.2.tar.gz
Size 287.7 kB
Tags Source
SHA-256 checksum
How to use checksums
4473d5c56d91466189af1dc26d8f4f986d0362442377b6fbdea4d589e6d78f23
BLAKE2b-256 checksum
How to use checksums
78525612cd91d58668ba0aea4e48d2a6e47ba2934ba1157eb27c785998cd5524
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Release files / ft_tui-0.2.2-py3-none-any.whl

Download URL ft_tui-0.2.2-py3-none-any.whl
Size 336.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aa1561f1995057dad60b1a782ef3d7dfcdc4cb4cc251a5bba0c4f15f2ae43e45
BLAKE2b-256 checksum
How to use checksums
5272f1dbe34986bcf33ab10eb9837d5d884167b6820247d847ace5463e8cc77a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Security for ft-tui 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ft_tui-0.2.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page