Make project view show the whole project, add a picker, and order project list like Linear - #290
Merged
Merged
Conversation
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.
schpetbot
pushed a commit
that referenced
this pull request
Sep 23, 2026
#290 ordered `project list` by status-type flow rank, then status position, then sortOrder. That rule was reconstructed from the schema docs and never checked against the app, and it is wrong: with probe projects in five statuses, Linear's projects list shows every project strictly by sortOrder ascending, so a backlog project with a higher sortOrder sits after a canceled one rather than with the other backlog projects. Order by sortOrder, then name, then id. Drop the status-type ranking and the status.position key, and stop fetching status.position, which nothing else read. Keep the non-finite check on sortOrder so a null key errors instead of scrambling the list. The status-type ranking was also the only exhaustive check on ProjectStatusType here, so make the date column's switch exhaustive instead of letting an unknown type fall through to the backlog date. Two things the observation cannot settle: whether the app view had a customised grouping/ordering setting, and sortOrder vs prioritySortOrder, which were identical for every project observed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three related changes to the
projectcommands, one commit each.project viewshows what Linear's project page showsproject viewrendered a project's 255-characterdescriptionsummary 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 withproject create --content-filedisplayed 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 theirpageInfo, so--jsoncarries 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:
Project.iconholds a Linear icon name such asRocketand never an emoji (the API rejects emoji outright), so prefixing the title with it produced# Rocket Mobile launch;ProjectMilestone.progressarrives as 0-100 while the identically namedProject.progressis a 0-1 ratio, so scaling both alike displayed a quarter-done milestone as2500%.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 likeissue viewdoes (--no-pagerto disable).This also fixes an ambiguity found in review: Linear does not require project names to be unique, and
getProjectIdByNamesilently 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 justproject view.project viewwith no argument picks a projectEvery way of naming a project required already knowing its UUID, slug or exact name, which for a read command is backwards. Bare
project viewnow offers a searchable list scoped likeproject 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 listorders projects the way Linear doesThe 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
switchrather 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.positionis documented as ordering statuses "within its type group";Project.sortOrderas 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--weberror-prefix bug were both caught by that live pass, not by the tests.Two pre-existing
project listcommand snapshots remainignore: truefor 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.