Repository navigation
feat: add cycle support (issues --cycle, cycles) - #40
Closed
seraph-pixelperfect wants to merge 1 commit into
Closed
seraph-pixelperfect wants to merge 1 commit into
seraph-pixelperfect wants to merge 1 commit into
Conversation
This was referenced Aug 19, 2026
Collaborator
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.
Closes #21
Depends on #39
What
issues --cycle <current|number>— filter the issue list to a cycle.--cycle current→cycle: { isActive: { eq: true } }in theIssueFilter(composes with--team,--state,--label,--project,--search,--limit).--cycle <number>→cycle: { number: { eq: N } }, requires--team(loudVALIDATION_ERRORotherwise) because cycle numbers are only unique within a team — see Ambiguities.cycles [--team KEY]— new command listing recent cycles (number, date range, progress, plus status and team attribution). Default 10, most recently updated first, auto-paginated 50/batch likeprojects.Schema verification (mandatory check) — the issue's guess was right
Source: generated documents from
@linear/sdk90.0.0 (npm pack @linear/sdk, inspecteddist/index-zxW4m1xd.d.mts), cross-checked against the SDK's ownTeam_CyclesDocumentGraphQL string indist/index.mjs.Verified shapes:
IssueFilter.cycleInputMaybe<NullableCycleFilter>— "Filters that the issues cycle must satisfy"NullableCycleFilter.isActiveInputMaybe<BooleanComparator>— "Comparator for the filtering active cycle"BooleanComparator{ eq?: Boolean; neq?: Boolean }NullableCycleFilter.numberInputMaybe<NumberComparator>;NumberComparator = { eq?: Float, gt, gte, in, lt, lte, neq, nin }Cyclefields usednumber: Float,startsAt: DateTime,endsAt: DateTime,progress: Float(0–1, "(completed estimate points + 0.25 * in-progress estimate points) / total estimate points", 0 when unestimated)Team.cyclesargs (TeamCyclesArgs)filter: CycleFilter,first/after/before/last: Int/String,orderBy: PaginationOrderByQuery.cyclesCycleConnection— "All cycles accessible to the user", same args asTeam.cyclesDeviation call-out: none needed for the filter. The issue's guessed
cycle: { isActive: { eq: true } }is schema-valid as-is (it lives onNullableCycleFilter, reachable throughIssueFilter.cycle), so noisActiveDatehack or cycle-id resolution round trip is required. Numeric filtering usescycle: { number: { eq: N } }, also verified.One deliberate schema-driven choice:
PaginationOrderByonly offerscreatedAt | updatedAt(nonumberordering), socyclesorders byupdatedAt(recent-first), matching theissues/projectsconvention.Command-path choices:
cycles --team KEYresolves the team via the existingresolveTeamIdhelper, then queriesteam(id: $id) { cycles(first, after, orderBy: updatedAt) }exactly as the issue specifies. Unknown team key → loud error (Team "X" not found in your workspace).cycleswith no--teamqueries the rootcyclesconnection in a single request (instead of N per-team queries) and selectsteam { key }per node so mixed-team rows are attributed. Small deviation from the letter of the issue ("via theteam { cycles }connection"), kept for the team-scoped path; flagged for the reviewer.Demo (real output)
Before (base
feat/projects-command):After — both parse, dispatch, and reach the Linear API layer (mapped 401 with a fake key):
Offline loud validation (no network involved):
Rendered TOON (fetch stubbed at the transport; exactly the GraphQL this PR sends — note the issued filter):
Local gates (real output)
pnpm build— clean (tsc, no output)pnpm lint— clean (eslint ., no output)pnpm run build:skill -- --check—skills/linear-axi/SKILL.md is up to date.pnpm test:78 → 97 tests (19 new in
test/cycles.test.ts: root + teamcyclesdocument shape/ordering/variables, rendering, statuses, empty state, hints, loud-validation errors, auto-pagination, and allissues --cyclefilter compositions).pnpm run format:check— RED with exactly the 18 pre-existing files (verified identical set on the base branch;src/cli.ts,src/linear.ts,src/commands/issues.ts,skills/linear-axi/SKILL.mdwere already flagged before this PR). The two new files (src/commands/cycles.ts,test/cycles.test.ts) passpnpm exec prettier --check. No newly-flagged files.Ambiguities (flagged, decisions documented in
--helptoo)--cyclewithout--team— cycle numbers restart per team, so--cycle 42is ambiguous. Picked the AXI loud-validation route: hard error with both fixes suggested (--cycle 42 --team LIN, or--cycle current). Not silent all-teams matching.--cycle currentwithout--team— the filter is well-defined (isActive eq truematches issues in any team's active cycle), so it is allowed and documented as such; in multi-team workspaces the result mixes teams (rows still carryteam { key }). Reviewer: happy to require--teamhere too if preferred.Cycle.progressis a 0–1 float over estimate points (0 when nothing is estimated), rendered as an integer 0–100 percent, same asprojects.status(past/active/upcoming) is derived fromstartsAt/endsAt; Linear's ownCycle.isActiveadditionally excludes manually early-completed cycles, which dates alone can't see — close enough for a listing, and no extra field is fetched.cyclesuses the rootcyclesconnection (one request,team { key }per node) rather than Nteam { cycles }queries — see schema section.Stacked PR note
Stacked on #39 (stack: #37 → #38 → #39); base is
feat/projects-command, notmain. No CI checks appear —ci.ymlonly triggers on PRs targetingmain. CI runs when retargeted tomainafter the stack merges; local gates above are the evidence. Not retargeting.CI quota-block caveat: this org has seen spending-limit/billing rejections that look like check failures. If a check shows red after retarget, inspect
gh run view <run-id>/ the jobs API for a billing annotation, or emptystepswith ~3-4s duration — that is a quota block, not a code failure; rely on the local gates above rather than retrying.Identity note
Automated agent dispatch authenticated as
seraph-pixelperfect, submitted for human review — not self-approved. Merge is a human decision.