Package Exports
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (freelo-cli) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
freelo-cli
Command-line interface for Freelo.io — manage projects, tasklists, tasks, comments, time entries, and files without leaving the terminal.
Status: early access. Authentication, configuration, and an introspectable command surface are shipped. Resource commands (projects, tasklists, tasks, comments, time entries, files) are landing one spec at a time.
Install
npm install -g freelo-cli
# or
pnpm add -g freelo-cliRequires Node.js 20.11 or newer.
Use
freelo --version # prints the installed version
freelo --help # shows available commands (human-readable)
freelo --introspect # full command tree as JSON (agent-friendly)
freelo help <cmd> --output json # JSON help for a single command or subtreeThe CLI is agent-first: when stdout is not a TTY, every data-returning command emits a versioned JSON envelope ({ "schema": "freelo.<resource>.<op>/vN", "data": ... }). Pipe it into jq, capture it from a script, or hand it to your agent runner.
Use freelo auth login once to store credentials (env vars FREELO_API_KEY + FREELO_EMAIL work too — they take precedence and skip the keychain entirely, which is the recommended path for CI and agent workflows).
Commands
auth
freelo auth login— Store credentials for a Freelo profile and verify them.freelo auth logout— Remove stored credentials for a Freelo profile.freelo auth whoami— Show the currently authenticated user.
comments
freelo comments add [input]— Post a single comment to a task from --message, --from-file, --editor, or stdin (-). Note: when the target task has no prior comments, the API converts this into the task description; usefreelo tasks description setfor explicit description writes.freelo comments edit [id...]— Overwrite an existing comment's content. Single id or batch (positional..., --ids, or --stdin NDJSON). Content from --message, --from-file, --editor, or stdin (-). Endpoint is POST /comment/{id} (yaml :2634 — POST for historical reasons). freelo comments list— List comments across all projects, tasks, documents, files and links the caller can see.
config
freelo config get <key>— Get the current value and source of a configuration key. Read-only keys (e.g. 'apiKey') return '[redacted]'.freelo config list— List all configuration keys with current values and sources.freelo config profiles— List all configured profiles.freelo config resolve— Print the merged effective configuration with optional per-leaf source annotation.freelo config set <key> <value>— Set a configuration key. Writable keys: output, color, profile, apiBaseUrl, verbose. Read-only keys (email, apiKey) must be updated via 'freelo auth login'.freelo config unset <key>— Remove a configuration key from the user conf store. Read-only keys cannot be unset via 'config'; use 'freelo auth logout' for credentials.freelo config use <profile>— Switch the active profile. The profile must already exist (created via 'freelo auth login'). No network call is made — use auth whoami to verify credentials afterward.
files
freelo files download <uuid>— Download a file by UUID. Streams the binary body to a local path (atomic write) or to stdout. Refuses to overwrite an existing destination unless --force is set.freelo files list— List every directory, link, file, and document the caller can see (paginated). Filter by --project / --type. No --task filter — endpoint does not surface one.freelo files upload <path...>— Upload one or more local files to Freelo. Returns a UUID per file. Optionally attach the uploads to a task by posting a comment with embedded references (the only documented attach mechanism — yaml :3876).
help
freelo help [commandPath...]— Print the command tree as JSON (--output json) or as the same text as --help (default).
labels
freelo labels attach— Attach one or more labels to a project (fetch-or-create per name). One POST per --name. Defaults to private labels (caller-only); use --public for shared labels.freelo labels delete [id...]— Hard-delete one or more project labels GLOBALLY (across all projects). Destructive — requires --yes (non-TTY) or interactive confirmation (TTY). 404 treated as idempotent (already-deleted).freelo labels detach— Detach one or more labels from a project. Idempotent — detaching a label that is not attached is a successful no-op (404 → already_in_target_state: true).freelo labels list— List the caller's available project labels (their private labels plus public labels from accessible projects). No filters in v1 —--projectis deferred until the API surfaces an attachments map.freelo labels rename <id>— Rename, recolor, or toggle private/public on an existing project label. At least one change flag is required.
notifications
freelo notifications list— List notifications addressed to the calling user (paginated). Filter by --unread / --project / --type. Server-side ACL-filtered.freelo notifications read [id...]— Mark notifications as read. Server-side idempotent: re-marking an already-read notification is a safe 200. Supports positional... / --ids / --stdin / --all-unread. freelo notifications unread [id...]— Mark notifications as unread (re-surface). Server-side idempotent: re-marking is safe. Supports positional... / --ids / --stdin.
projects
freelo projects activate [id...]— Activate (unarchive or undelete) one or more projects. Idempotent on the server: already-active projects return 200 no-op. May fail with PlanExceededException when restoring exceeds the plan cap.freelo projects archive [id...]— Archive one or more projects. Idempotent on the server: re-archiving an already-archived project succeeds (200 no-op).freelo projects create— Create a new project.freelo projects create-from-template <template_id>— Create a new project by cloning a project template (state=3).freelo projects delete [id...]— Soft-delete one or more projects. Destructive — requires --yes (non-TTY) or interactive confirmation (TTY). Reversible via 'freelo projects activate'. 404-after-delete is treated as idempotent already-deleted.freelo projects invite— Invite existing users (--user) and/or external people (--email) to one or more projects (--project). Single bulk POST to /users/manage-workers; --user and --email are not mutex (the API body accepts both in one call). Unknown emails trigger user creation server-side.freelo projects list— List projects in the chosen scope. Default scope is 'owned'.freelo projects show <id>— Show one project's full detail, with optional side-cars.freelo projects workers list— List a project's workers (active members + owner + guests). Paginated; defaults to fetching all pages.freelo projects workers remove— Remove one or more workers from a project. Atomic — the entire request fails if any single user cannot be removed (server-side ACL pre-check). The project owner cannot be removed via this command.
reports
freelo reports delete [id...]— Delete one or more work reports. Destructive — requires --yes (non-TTY) or interactive confirmation (TTY). 404 (and 400 with "not found" body) treated as idempotent already-deleted; 400 with UserCannotDeleteWorkReport surfaces as a hard ACL error.freelo reports edit [id]— Edit an existing work report (minutes / note / date). At least one change flag is required. Single-mode (+ flags) or NDJSON batch via --stdin. freelo reports list— List work reports (time entries) across all projects, with optional task / project / worker / date filters.freelo reports log— Log a finalized work report on a task (bypasses the live-timer flow). Single-mode (--task / --minutes …) or NDJSON batch via --stdin.
subtasks
freelo subtasks add— Create a subtask (taskcheck) under a parent task (single or NDJSON batch via --stdin). Note: Freelo's API auto-falls-back from a smart subtask (full task with worker, due date, etc.) to a simple taskcheck (a checkbox row with only a name) when the parent's tasklist can't host smart ones. The response envelope'sdata.storage_formfield reflects which form was actually persisted;data.input_ignored[]lists fields you set that the server discarded on the simple path.freelo subtasks list— List subtasks (taskchecks) under one parent task, paginated.
task-labels
freelo task-labels attach— Attach one or more labels to a task. Each --name and --uuid becomes one entry in a single bulk POST. --hex applies to name-mode entries only (UUID-mode uses the existing label's color).freelo task-labels create— Bulk-create task-label definitions in the caller account. Server-side fetch-or-create onname(case-sensitive) — re-running with the same names is a safe no-op. The API does not report new vs. reused.freelo task-labels detach— Detach one or more labels from a task. --name without --hex removes ALL labels with that name (aggressive); add --hex to scope to a specific (name,color) pair. UUID-mode is precise — removes exactly that label. Server is idempotent — detaching a label not on the task is a successful no-op.
tasklists
freelo tasklists create— Create a new tasklist in a project.freelo tasklists create-from-template <template_id>— Copy a tasklist from a project template into a target project (or a new one).freelo tasklists list— List tasklists across all projects you can see, optionally filtered to one project.freelo tasklists show <id>— Show one tasklist's detail, with optional assignable-workers side-car.
tasks
freelo tasks create— Create a task in a tasklist (single or NDJSON batch via --stdin).freelo tasks create-from-template <template_id>— Copy a single task from a project template into a target project.freelo tasks delete [id...]— Soft-delete one or more tasks. Destructive — requires --yes (non-TTY) or interactive confirmation (TTY). 404-after-delete is treated as idempotent already-deleted.freelo tasks description get <id>— Print one task's rich-text description.freelo tasks description set <id> [input]— Replace one task's rich-text description from a file, an interactive editor, or stdin (upsert).freelo tasks edit <id>— Partially update a task: name, due date, worker, priority, and label diff.freelo tasks estimate clear <id>— Remove a task's time estimate. Without --user, clears the team-wide total. With --user, clears one user's per-user estimate. Destructive — requires --yes (non-TTY) or interactive confirmation (TTY). freelo tasks estimate set <id>— Set or update a task's time estimate (in minutes). Without --user, sets the team-wide total estimate. With --user, sets a per-user estimate (independent of the total). Server upserts on every call. freelo tasks find-relations— Bulk-fetch typed relations for many tasks at once (1–100 per call). Read-only — does NOT create relations. Tasks the caller cannot access are silently omitted from the response; diffdata.task_idsagainstdata.tasks[*].task_idto detect inaccessible ids.freelo tasks finish [id...]— Mark tasks as finished. Idempotent: tasks already finished aren't re-POSTed.freelo tasks list— List tasks across all projects (default) or scoped to one project + tasklist.freelo tasks move [id]— Move a task between tasklists (single id) or move many tasks via NDJSON --stdin batch.freelo tasks project add <id>— Add a task to one or more secondary projects (multi-project membership). Each --tasklistflag adds the task to the project that owns that tasklist; the server creates a child task there. Repeatable; one POST per --tasklist (deduplicated). freelo tasks project remove <id>— Remove a task from a single SECONDARY project. Removing the task entirely (incl. its primary project) requiresfreelo tasks delete <id>instead — Freelo returns 403 AclException on primary-project removal attempts. Destructive; requires --yes (non-TTY) or interactive confirmation (TTY).freelo tasks relations <id>— Show all typed relations on a single task (blocked_by, blocks, related_to, duplicate_of). Read-only. Empty array if the task has no relations. Relations to tasks the caller cannot access are silently filtered out by Freelo.freelo tasks remind clear <id>— Remove your personal reminder for a task. Destructive — requires --yes (non-TTY) or interactive confirmation (TTY).freelo tasks remind set <id>— Schedule (or overwrite) your personal reminder for a task. --at is required; reminders are per-user — they only ping the caller, not other workers on the task.freelo tasks reopen [id...]— Reopen finished tasks (move back to active). Idempotent: already-active tasks are skipped.freelo tasks share <id>— Get (or create) a public, unauthenticated URL for a task. Anyone holding the URL can view the task read-only. Idempotent on the wire — first call creates the link, subsequent calls return the same URL.freelo tasks show <id>— Show one task's full detail, with optional side-cars.freelo tasks unshare <id>— Revoke the task's public share URL. Destructive — invalidates any previously shared URL immediately. Requires --yes (non-TTY) or interactive confirmation (TTY).
time
freelo time edit— Edit the active time tracking session in flight (--task / --clear-task / --note). At least one flag is required. Returns 409 with a friendly hint when no session is running.freelo time start— Start a time tracking session (singleton per user). Both --task and --note are optional; omit --task for general work. A second start while one is already running returns 409 with a friendly hint.freelo time status— Print the caller's currently-running time tracking session, or { active: false } when no timer is running. Returns exit 0 in both cases.freelo time stop— Stop the active time tracking session and emit the resulting work report. Returns 409 with a friendly hint when no session is running.
The block above is generated from freelo --introspect by scripts/check-readme.mjs and verified in CI. Run pnpm fix:readme to refresh it after adding or changing a command.
The freelo help command (e.g. freelo help auth --output json) is intentionally not listed above — it's a meta-command that introspect excludes.
Develop
pnpm install
pnpm dev -- --version # run the entry directly via tsx
pnpm build # bundle to dist/freelo.js
pnpm test # vitest
pnpm lint && pnpm typecheck
pnpm check:readme # verify the autogen Commands block matches `freelo --introspect`
pnpm fix:readme # rewrite the autogen Commands block in placeConventional Commits are enforced by a commit-msg hook. Add a changeset (pnpm changeset) for any user-visible change.
Process
This repository uses an agentic SDLC. See .claude/docs/sdlc.md (interactive) and .claude/docs/autonomous-sdlc.md (autonomous via /auto).
License
MIT