@learnaltitude/cli
v0.9.0
Published
Agent-neutral Altitude workshop client for Claude Code and Codex.
Readme
Altitude CLI
The Altitude CLI is the workshop client that connects supported coding agents to the Altitude service.
Install
npm install -g @learnaltitude/cliPair the CLI with your account at app.learnaltitude.com. Supported agents are Claude Code and Codex.
Connect, bind, and inspect a project
From a terminal, name the agent this computer will use and complete the browser device flow:
altitude connect --agent claude-code
altitude connect --agent codexUse one command, not both. Codex must meet the current compatibility floor documented on the
W2 card. Adapter-native
connect declares the detected version; the server refuses a parseable version below its floor
with server-authored remediation. The raw terminal command stamps the version as "unknown",
which deliberately remains pairable. Other JSON connection failures likewise print the server's
message; non-JSON failures retain the HTTP status line. Connecting stores a scoped bearer token
in the CLI data directory (by default, ~/.altitude/token.json) with owner-only permissions; it
does not activate hooks in every directory.
A directory declares WHICH journey it works. From the root of the project, pick from your journey list:
altitude bind --list # show your journeys and their statuses
altitude bind --journey <id> # bind this directory to one of them
altitude bind # binds directly when exactly one journey is bindableBinding writes a .altitude marker in that directory. Hooks discover the nearest marker while
walking upward, so the project and its subdirectories are active. Without a valid marker, hooks
remain dormant for capture, context, gates, and session registration. They still recover writes
left in the temporary spool, and a paired device's session-start and learner-turn hooks refresh
the account-level caches at most once per 10-minute attempt window so sandboxed CLI commands can
read them.
Binding during a running agent session takes effect on its next learner turn or tool lifecycle;
you do not need to restart the agent. That first bound lifecycle refreshes the journey before it
registers the adopted session, so the session is attributed to the current task when available.
Gate failures in a bound project still fail open. Several directories may bind the same journey
(git worktrees are the normal case); each directory holds exactly one journey, and its down-synced
working set is cached per journey in an owner-only file for offline sessions. An abandoned journey
stays visible in the list but cannot be bound; a paused journey can. When the refreshed or cached
journey list marks it paused, session start prints the server's paused notice when present and
keeps gates dormant until a later list refresh observes that you resumed it on Altitude (never
automatically from the terminal). Older responses without a notice stay silent. When the server
refuses a binding's journey (deleted, abandoned, or not entitled), the session prints the server's
explanation instead of silently substituting another project.
altitude diagnostics records these misses and stale bindings; the precise hook behavior is
defined in the workshop/server
protocol.
If the project is already bound to another journey, inspect the IDs before deliberately replacing
the marker with altitude bind --force.
Use altitude task --json --session <actual-session-id> for a network-first snapshot of the BOUND journey with local-cache
fallback; its source field says which was used, journey_status carries the journey's
last-known status, and journey_miss carries the server's refusal when the bound journey was not
served. After a failed live read, reason distinguishes a host-blocked network, an offline host,
rejected authorization, and a bad server response; transport carries the support-safe cause and
elapsed time. For session identity and recovery, see Versioned paid lessons.
Its full shape is the
taskEnvelopeSchema contract. altitude status is a local snapshot
that includes the nearest binding, the bound journey's cached working-set summaries, queued and
spooled event counts, and any host-published network-sandbox marker. altitude diagnostics
reports the primary log's path, existence, line count, malformed-line count, and valid records,
plus the spool path and its line count. Malformed lines do not prevent valid records from being
shown. Binding requires a connected device token; the command tells unconnected users to connect
and directs users without a bindable journey back to Altitude.
Queue, diagnostic, and flusher-wake writes refused because the CLI data directory is read-only are
preserved under the operating system's temporary directory. The next hook or flusher drain moves
them into the normal owner-only data directory. As a result, altitude emit still succeeds when a
supported host sandbox makes the home directory unwritable. Spool fallback is limited to
permission and read-only-filesystem failures; diagnostics and wake nudges remain best-effort.
The envelope also carries two update signals, both derived from the server's client_releases
answer on the settings down-sync (the server owns "what's current"; there is no npm registry
check). update_available is true when the CLI is behind the server's latest — its meaning is
unchanged for older plugin markdown. update_notices is the successor surface: zero, one, or two
server-authored lines (CLI and/or plugin), compared live against the installed versions on every
read. Hooks stamp routine notices once per session; urgent notices repeat within the
session. Task reads return current notices without consulting or updating those stamps. altitude task adds no version request of its own — a lesson never waits on one. Skills
update separately through the agent's plugin system.
Tutor flows can attach the learner's verbatim answer to its canonical concepts. The tutor does not grade the answer; grading and any resulting mastery update are server-side:
altitude emit quiz-moment --session <id> --question <q> --answer <a> \
--concepts <id,...>--concepts accepts comma-separated IDs or repeated flags. The CLI trims empty entries, removes
duplicates in first-seen order, and omits concept_ids when no non-empty ID is supplied.
Teaching preferences and check-ins
Change the bound journey's teaching preferences with one or more assignments:
altitude teaching set drills=1 step_size=line --session <id>
altitude teaching set check_density=every_run --jsonValid values are drills=0|1|2|3, step_size=line|function|feature, and
check_density=teach_runs|every_run. The server's
teaching read policy determines
how preferences apply to each lesson's learning level. The command requires a connected
device and discovers the same project binding as altitude task. It uses the device bearer
token to PATCH /api/journeys/{journeyId}/teaching with the usual HTTP timeout.
Success prints all three preferences; --json prints the server's
{ journey_id, teaching_knobs } response.
When network access fails or the server returns 5xx, the command queues a
teaching_knobs_set event with source: "cli" and reports that the change applies on the
next sync. An unwritable data directory uses the existing temporary spool. With --json,
this returns { status: "queued", journey_id, knobs, message }, not a confirmation that the
server applied the change. Server 4xx responses print the server message, exit 1, and are
neither retried nor queued. Invalid input also exits 1. These commands never exit 2.
If an earlier teaching update or accepted check-in for this journey is still in the queue
or temporary spool, the command exits 1 before PATCH. Run altitude flush --force, then
retry the command once syncing succeeds. This keeps an older claim from overwriting a
newer confirmed preference. Declined check-ins and other journeys do not delay the update.
Concurrent teaching set commands share an atomic directory lock in the temporary spool;
a second command exits 1 with a retry message until the first finishes its PATCH or queues
its fallback. The lock is released on both success and errors. After a forcibly terminated
process, stop any remaining teaching set commands before removing the empty
teaching-update.lock directory under the data directory's temporary spool. Locks are
never stolen on a timer, so a slow request cannot be overtaken.
Record a lesson-close proposal and the learner's explicit answer:
altitude emit teaching-checkin --session <id> --knob drills --value 1 \
--signal hints_heavy --answer yes \
--observation 'You needed three hints while writing the loop.'Signals are hints_heavy, drills_skipped, no_learner_lines, impatience, or floor.
--answer yes|no becomes the event's accepted boolean. Both answers queue a
teaching_checkin; the server owns applying accepted proposals and checking authorization.
This command makes no direct settings request. Both commands use the existing session
registry fallback when --session is omitted; pass the actual session ID when available.
Optional --task <uuid> --plan-revision <positive integer> flags are validated consistently
with other emitters, but the current shared check-in contract is journey-scoped and has no
fields to transmit that task/revision provenance. These are preference claims, not mastery evidence.
altitude task --json preserves the server's optional journey.teaching_knobs,
journey.current_task.due_review, and journey.current_task.method_checkin on both live and
cached reads. The client does not select review concepts or schedule check-ins.
Versioned paid lessons
Compatible client artifacts and release prerequisites are tracked in the learning-level rollout specification.
current_task.learning_requirements carries the server-authored instructions, level, lesson
mode, concept capabilities, and immutable plan revision. Paid executors retain that task and
revision for the whole lesson, including a completion queued after the journey changes.
Pass --task <id> --plan-revision <n> with both emit task-completed and emit quiz-moment
for every versioned task, including Beginner. Quiz text must preserve the actual question and
the learner's answer verbatim. These events are claims; server judgment owns capability credit.
learning_runtime contains server-authored update copy and compatible version minima. The CLI
compares its running version and the exact session marker’s hook-observed plugin version
against those minima even when reading a warm cache.
Use the exact reload command supplied by the current agent's Altitude hook. An omitted or
empty --session uses an unambiguous CLAUDE_CODE_SESSION_ID or CODEX_THREAD_ID;
if both are nonblank they must agree. An explicit ID must match the current host identity.
An ordinary terminal without either variable can provide an exact existing session ID.
Reads never select the newest session, borrow another session's plugin observation, or create
or modify session markers. The optional learning_runtime.session_id identifies an existing
plugin observation, not a replacement ID to invent.
The local task envelope distinguishes these runtime outcomes (the HTTP settings contract is unchanged):
supported: the lesson can proceed.update_required: a known installed version is below the server's compatibility minimum. Follow the update instructions and restart the agent after a plugin update.session_required: the CLI version or this session's plugin observation is unusable. Followrecovery_message. For a missing plugin observation, send the next normal learner prompt so the installed hook can refresh it, then reload using the hook's exact session command. Reopen the agent if hooks are not running. Do not invoke hooks manually, edit session records, or repeatedly update software to repair a missing observation.requirements_unavailable: an Intermediate task is missing its server-authored requirements. Reload online with the current session ID; contact [email protected] if they remain unavailable. Do not fabricate requirements or treat this as an update request. This outcome takes precedence over version checks.
Normal bound prompt hooks refresh existing plugin observations and clear stale observations
when the installed version is missing or blank. After recovery, require a fresh supported
result before teaching or reporting lesson completion. A bound cold cache cannot authorize
lesson evidence. An unsupported paid lesson
waits for a supported read and never silently adopts the free method; editor hooks remain fail
open. When the subscription is paused, bound paid journey lessons wait for renewed access;
standalone unbound free projects remain available. Intermediate does not mint automatic legacy gate credit. Its demonstrated evidence is
captured through the paid executor, with task and revision provenance.
Manual verification with a real Claude Code subscription and a local plugin checkout:
pnpm -F @learnaltitude/cli build
node packages/workshop-core/scripts/verify-learning-session.mjs /path/to/altitude-skillsThe harness uses a loopback fixture backend, an isolated scratch git repository, and the actual
CLI/plugin hooks. Enter learner messages one line at a time and /exit when finished. It removes
provider API-key environment variables and uses the installed agent's account. It retains the
scratch code, HTTP requests, emitted claims, and turn outputs in the printed temporary directory.
Fixture acceptance verifies transport and attribution, not real production grading or mastery.
Plugin marketplace installs and updates
Install the Altitude plugin exclusively through the marketplace built into your coding agent. For Claude Code, open a terminal and start Claude Code:
claudeThen run these commands inside that terminal session:
/plugin marketplace add jasonku09/altitude-skills
/plugin install altitude@altitudeWhen the installer asks for a scope, choose the user scope so the plugin is available across all
projects, including the new project folder created by your first lesson. If the install summary
asks you to run /reload-plugins, run it; newer Claude Code versions activate the plugin in place.
The VS Code built-in terminal works for this flow. The extension chat panel uses its separate
/plugins manager instead.
Start the lesson from a terminal with claude --permission-mode auto, then enter
/altitude:begin. Auto mode clears routine approval prompts while Claude Code's separate safety
checks screen every action and block risky ones; it is a safeguard, not a guarantee.
For Codex, update Codex to the latest version first, then run these commands in a terminal. Older Codex CLI releases do not recognize the plugin commands.
codex plugin marketplace add jasonku09/altitude-skills
codex plugin add altitude@altitudeOn the first Codex launch after installation, accept the one-time
Hooks need review -> Trust all and continue prompt. To start the first lesson, type $begin and
press Enter twice; the second press expands the command in Codex's composer. Skills update with the
agent's plugin system.
Neither half updates on its own by default. Claude Code enables marketplace auto-update only
for official Anthropic marketplaces; altitude is third-party, so its auto-update starts off.
Turn it on once — run /plugin in Claude Code, open Marketplaces → altitude, enable auto-update —
and the plugin then follows releases without further prompting.
The globally installed Altitude CLI never updates itself. To move it:
altitude updateThe command prints a one-line reminder that skills update with the agent, then asks the detected
global package manager to update @learnaltitude/cli. If detection or execution fails, it prints
the exact manual command instead.
Because nothing forces either update, the server enforces a minimum CLI version at the device
connect handshake. altitude connect is refused when the CLI does not report a readable version
or is older than the floor; the refusal names the update command and, for Claude Code, the
auto-update toggle. The floor is server-owned — raising it needs no client release.
Network disclosure
The distributed client uses the user's own agent subscription and no Altitude-held API key. The
CLI makes no third-party requests: the old anonymous registry.npmjs.org version check is gone.
Freshness now rides the existing authenticated settings down-sync — the request reports the
installed CLI and plugin versions as query parameters, and the response's client_releases block
carries the server's latest versions plus the notice copy. Lesson reads follow the
exact-session usage above, with network-first reads and cache fallback. Bound session-start hooks refresh the journey and journey list;
learner-turn hooks also refresh stale bound caches, and refresh immediately after delivering a
task completion that can change the snapshot. On paired devices, unbound session-start and
learner-turn hooks silently warm the account-level caches for sandboxed commands. Failed attempts
are rate-limited as well as successful reads: the defaults are 10 minutes for unbound warming and
60 seconds per bound journey, configurable with ALTITUDE_DOWNSYNC_TTL_MS and
ALTITUDE_BOUND_REFRESH_TTL_MS. connect performs only its device flow, while diagnostics,
status, and update-notice checks use cached down-sync data. Gates perform no version or network
work. Hook refresh and comparison failures remain silent and cannot change a hook's exit code.
