Skip to content

Make project view show the whole project, add a picker, and order project list like Linear - #290

Merged
schpet merged 3 commits into
mainfrom
project-view-rich
Sep 18, 2026
Merged

schpet merged 3 commits into
mainfrom
project-view-rich

Conversation

@schpetbot

Copy link
Copy Markdown
Collaborator

Three related changes to the project commands, one commit each.

project view shows what Linear's project page shows

project view rendered a project's 255-character description summary and nothing else of its content. The long-form body lives in a separate field, content, that the query never selected — so a project whose body was written with project create --content-file displayed none of it. The same was true of everything hanging off the project: resources, documents, attachments, milestones, labels, members, initiatives and dependencies were all reachable from the API and none of them were asked for.

The output is now one document shaped like issue view: title and meta line, summary, overview, then a section per relationship, omitting any section whose connection came back empty. Connections are requested at Linear's 250 page cap and keep their pageInfo, so --json carries the real connection contract and a section that could not be shown in full says so. Issues are paginated to exhaustion, because their count is displayed — it previously stopped at Linear's default first page of 50.

Three rendering bugs went with it:

  • the status line was written straight to stdout the moment stdout was a terminal, putting it above the title in the finished output;
  • Project.icon holds a Linear icon name such as Rocket and never an emoji (the API rejects emoji outright), so prefixing the title with it produced # Rocket Mobile launch;
  • ProjectMilestone.progress arrives as 0-100 while the identically named Project.progress is a 0-1 ratio, so scaling both alike displayed a quarter-done milestone as 2500%.

A project reference now resolves through resolveProjectId, so a name works wherever a UUID does, including with --web/--app, which previously put the raw name into the URL. Long output pages like issue view does (--no-pager to disable).

This also fixes an ambiguity found in review: Linear does not require project names to be unique, and getProjectIdByName silently returned whichever match came first. A name matching several projects is now rejected with both IDs. That affects every command taking a project by name, not just project view.

project view with no argument picks a project

Every way of naming a project required already knowing its UUID, slug or exact name, which for a read command is backwards. Bare project view now offers a searchable list scoped like project list — the configured team, or the whole workspace when no team is set. The list is fetched in full, since the prompt filters client-side and anything left unfetched would be undiscoverable, and each row carries status, team keys and slug because project names are not unique. The value behind each row is always the UUID.

Prompting only happens when someone is there to answer: piped, redirected, under CI, or with --json, it errors before any network call rather than hanging.

project list orders projects the way Linear does

The old sort used a hardcoded status ranking that put in-progress work first, then fell back to the project name. Linear orders by where the status sits in the project flow (backlog → planned → in progress → paused → completed → canceled), then by the position the workspace gives that status within its category, then by the manual order projects are dragged into. The CLI ignored both the workspace's status positions and the manual order, so two projects a user had deliberately arranged came back alphabetised.

The status ranking is now an exhaustive switch rather than a lookup reached through a cast, so a status type added to the schema fails the type check where someone has to decide where it belongs.

Worth a second opinion: this ordering is reconstructed from the schema (ProjectStatus.position is documented as ordering statuses "within its type group"; Project.sortOrder as the manual order used in list views) rather than observed in the app — no browser session was available to check it against linear.app directly. It is also a visible change to default output: backlog-first instead of in-progress-first. Happy to flip it if that is not what the app does.

Testing

Beyond the unit suite, each change was exercised against the live Linear API using throwaway projects populated with content, external links, documents, milestones, labels, members, an initiative, issues across three states, a status update, and a dependency in both directions — by UUID, slug and exact name, as --json, piped, under a pty, and with --web. All fixtures have been deleted.

The 2500% milestone bug and the --web error-prefix bug were both caught by that live pass, not by the tests.

Two pre-existing project list command snapshots remain ignore: true for a mock-server problem that predates this branch and breaks the rest of the file when enabled, so the new ordering rule is exercised directly instead.

project view rendered a project's 255-character `description` summary and
nothing else of its content, so a project whose body was written with
`project create --content-file` displayed none of it. The long-form body lives
in a separate field, `content`, that the query never selected. The same was
true of everything hanging off the project: resources, documents, attachments,
milestones, labels, members, initiatives and dependencies were all reachable
from the API and none of them were asked for.

Select them, and render the result as one document in the shape issue view
uses: title and meta line, summary, overview, then a section per relationship,
omitting any section whose connection came back empty. Connections are
requested at Linear's 250 page cap and keep their pageInfo, so --json carries
the real connection contract and a section that could not be shown in full says
so rather than trailing off. Issues are the exception and are paginated to
exhaustion, because their count is displayed and so is the one number that
could be wrong -- it previously stopped at Linear's default first page of 50.

Three rendering bugs went with it. The status line was written straight to
stdout the moment stdout was a terminal, which put it above the title in the
finished output. `Project.icon` holds a Linear icon name such as `Rocket` and
never an emoji -- the API rejects emoji outright -- so prefixing the title with
it produced `# Rocket Mobile launch`; it moves to a labelled row. And
`ProjectMilestone.progress` arrives as 0-100 while the identically named
`Project.progress` is a 0-1 ratio, so scaling both alike displayed a
quarter-done milestone as 2500%.

Resolving the argument through resolveProjectId means a project name works
wherever a UUID does, including with --web and --app, which previously put the
raw name into the URL. Milestones, resources and documents are sorted by
sortOrder ascending, since Linear returns them descending but displays them the
other way round.
Every other way of naming a project requires already knowing its UUID, slug or
exact name, which for a read command is backwards: the usual reason to run
project view is to go and look at something you have not memorised the
identifier of. Running it bare was simply an error.

Make the argument optional and, when it is missing, offer a searchable list
built from the same scope project list uses -- the configured team, or the
whole workspace when no team is set. The list is fetched in full rather than
capped, because the prompt filters client-side and anything left unfetched
would be undiscoverable. Labels carry the status, team keys and slug beside the
name: project names are not unique, and the extra terms give the search
something to match on. The value behind each row is always the UUID, so what is
displayed can never change which project is opened.

Prompting is only ever right when someone is there to answer, so every other
case errors before any network call rather than hanging: piped, redirected, in
CI, or with --json, where prompt output would also land in the middle of the
JSON. An empty scope errors too instead of opening a prompt with nothing in it.
project list sorted by a hardcoded status ranking that put in-progress work
first, then fell back to the project name. Linear does neither. It orders by
where the status sits in the project flow -- backlog, planned, in progress,
paused, completed, canceled -- then by the position the workspace has given
that status within its category, then by the manual order projects are dragged
into. The CLI ignored the workspace's own status positions and the manual order
entirely, so two projects a user had deliberately arranged came back
alphabetised.

Select sortOrder and status.position and compare on those, with name and id
left to break exact ties so the listing is stable across runs. The status
ranking is an exhaustive switch rather than a lookup table reached through a
cast, so a status type added to the schema fails the type check here, where
someone has to decide where in the flow it belongs, instead of silently sorting
to the end. A sort key that arrives non-numeric is reported rather than allowed
to return NaN from the comparator and scramble the listing.

This is reconstructed from the schema rather than observed in the app: position
is documented as ordering statuses "within its type group" and sortOrder as the
manual order used in list views. No browser session was available to check it
against linear.app directly.

The ordering is not sorted server-side. The `sort:` argument on projects() is
marked [INTERNAL], and since every page is fetched before sorting anyway it
would add a dependency on an unsupported argument for no benefit.

The two command-level ordering snapshots remain ignored for a pre-existing mock
server problem that predates this change and breaks the rest of the file when
they are enabled, so the ordering rule is exercised directly instead.
@schpet
schpet merged commit 7486a81 into main Sep 18, 2026
15 checks passed
@schpet
schpet deleted the project-view-rich branch September 18, 2026 04:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants