Commit Graph
66 Commits
Author SHA1 Message Date
Neil 4ed095d328 refactor(linear): group issue-context reads under concrete owners (#31686)
* refactor(linear): group issue-context owners and use direct imports

* test(linear): mock concrete current-context owner in WSL move suite
2026-10-21 16:58:22 -07:00
Brennan Benson 21e3d76576 feat(linear): edit and delete Linear comments from the CLI (#30003)
* feat(linear): edit and delete Linear comments from the CLI

Adds orca linear comment edit <commentId> and orca linear comment delete <commentId>
(alias rm). The comment id alone names the comment; Orca finds the connected workspace
that owns it unless --workspace is given. Both refresh a linked worktree's issue and
report unconfirmed writes with a read-back step. The orca-linear guide documents them.

* test(linear): use a checked linked worktree fixture

* fix(linear): bound and cancel comment actions
2026-10-17 13:14:48 -07:00
Neil f8089c787b fix(orcad): keep the integration credential key only in the owner-only key file (#29859)
* fix(orcad): keep the integration credential key only in the owner-only key file

Managed servers no longer create, read or wait on the macOS login Keychain or
a Secret Service keyring for their credential master key. A record an hourly
build pointed at the keyring is migrated once: the key is copied into the key
file when it reads back without UI within 5s, otherwise a new key file is
started and old credentials show the usual reconnect error. Removes the
locked-Keychain status fields, retry, the integrationCredentials.moveKeyToFile
RPC and the Settings notice.

* fix(orcad): serialize the keyring-record migration so concurrent starts agree on one key

* docs(orcad): note the migration lock

* fix(orcad): publish the migration lock with its PID and bound every wait

* refactor(orcad): reuse the file transaction lock for the keyring-record migration
2026-10-16 15:53:30 -07:00
Neil 839f553dee fix(orcad): explain a locked macOS Keychain to the user and recover Linear/Jira without a restart (#29608)
* fix(orcad): tell the user when a locked macOS Keychain hides Linear and Jira, and recover without a restart

A managed macOS host reached only over SSH keeps its login Keychain locked
after a reboot, so the server's credential key failed closed and the app
asked the user to reconnect a token that was fine. Exit 36 from
security find-generic-password (errSecInteractionNotAllowed) is now a
'locked' lookup: the server logs it once, retries the Keychain on each
credential use and before each Linear/Jira status read, and status carries
an optional credentialErrorReason: 'keychain_locked' that the app shows in
place of the generic text. Once unlocked, Settings offers a one-click move
of the same key into the owner-only key file.

* fix(orcad): keep retrying a locked Keychain after a timeout, fence the key move to the host that asked, report the lock only for credentials that need the key

* fix(settings): offer the file-storage switch to a paired browser's implicit server too

* fix(settings): route the Keychain notice and key move by the host that reported the status; sweep stale key-record staging; say the moved key is not machine-bound

* fix(orcad): sweep only this machine's key-record staging, so a shared home's other hosts keep theirs
2026-10-16 04:09:33 -07:00
Neil 3074852b7c fix(orcad): seal integration credentials on the headless server instead of storing them in plaintext (#29412)
* fix(orcad): seal integration credentials on the headless server instead of storing them in plaintext

The Node SecretStore always reported encryption unavailable, so every
credential writer on orcad fell back to plaintext. orcad now seals with
AES-256-GCM using a host-owned master key kept in the OS keyring (macOS
Keychain, Linux Secret Service) when reachable at first start, else in a
0600 profile file bound to the machine ID. A key that cannot load fails
closed instead of downgrading. Existing plaintext credentials are sealed
on startup without loss.

* docs: describe how the headless server seals credentials and its limits

* fix(orcad): unique credential staging files, no keyring items for temp profiles, isolate startup test

* fix(orcad): keep the credential key with the credentials, one record per machine

* fix(orcad): never re-key over an unreadable record, serialize first-start key creation, refuse an unrestricted key file

* fix(orcad): verify a key record's Windows ACL and wait out a concurrent first start's keyring write

* fix(orcad): keep a keyring claim and fail closed when its store result is uncertain

* fix(credentials): restrict the staged file's Windows ACL before it replaces a credential

* fix(orcad): a temporary profile never creates a key in, or re-seals, the real credential home

* ci: baseline the two single-file credential ACL restrictions

* fix(credentials): stage Windows credentials born restricted and verify the key record asynchronously; document rollback

* test(bitbucket): inject the write failure into the Windows staging writer too
2026-10-15 23:18:05 -07:00
NeilandOrcaWin 7909e72d2d ci: move PR analysis and Linux unit tests to Blacksmith (#29362)
* ci: report final PR checks outside the shared runner queue

* ci: fix inherited mobile test file size failure

* ci: move PR analysis and ARM test shards to Blacksmith

* Qualify x64 Blacksmith capacity and reject incompatible runner images early

* Keep extracted mobile test adapters within the test-only lint scope

* Identify the runner profile overriding shell fixture homes

* Preserve fixture HOME through the Blacksmith login profile

* Make cache invalidation fixtures observable within one filesystem clock tick

* Scope Blacksmith system-profile preparation to Linux runners

* Wait for observable ctime in daemon hash cache fixtures

* Use the mobile test lint scope already merged on main

* Stabilize metadata and timer fixtures on fast CI runners

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-15 21:07:49 -07:00
NeilandMinho Yi 5ae088a24b fix(integrations): notice Jira and Linear state changes from other processes (#28594)
Reload externally changed Jira and Linear connection metadata and credentials on the next read using bounded file-identity caches. Existing captured requests and retained result expiry policies remain intact.

Co-authored-by: Minho Yi <tisohjung@gmail.com>
2026-10-14 03:24:18 -07:00
Neilandkazu-42 f50b89b5f6 fix(linear): exclude label groups from assignable labels (#26861)
Co-authored-by: kazu-42 <91968271+kazu-42@users.noreply.github.com>
2026-10-10 00:26:36 -07:00
Neil 4aa361c435 Close the SDK test server before destroying its connections 2026-10-08 04:28:07 -07:00
Neil 5125f518cf Bound multi-reference lookup work and normalize by identity 2026-10-08 04:28:07 -07:00
Neil 5b8a982f8f Fix private Linear images in task descriptions (#25849)
* Fix private Linear images in task descriptions

* Strengthen Linear image signing validation and typed access
2026-10-06 02:49:22 -07:00
Neil 5fa290fa50 feat(secrets): warn in Settings when a credential is stored unencrypted (#24048)
* feat(secrets): warn in Settings when a credential is stored unencrypted

When no OS keyring is usable, the MiniMax stores write the credential as a
plaintext envelope and say so with a console.warn nobody reads. The users this
affects are exactly the ones who never see a main-process log, so in practice
they were told nothing (#21827).

Report it where the credential is managed instead. Each store gains a
protection reader, the status IPC carries it, and Settings renders a warning
next to the credential it applies to.

Keyed on the stored bytes, not isEncryptionAvailable(): a credential saved
before a keyring existed stays plaintext until it is saved again, so reporting
current capability would call it protected while the file says otherwise. The
readers parse the envelope kind without decrypting, so opening Settings cannot
provoke a keychain prompt.

The console.warn stays. It carries no secret material, and it is still the only
signal on a headless host with no Settings window.

* feat(secrets): extend the unsealed-credential warning to every affected store

The speech key, Linear tokens, Jira tokens and the Bitbucket credential have
the same plaintext fallback the MiniMax stores do, and the same console-only
warning nobody reads.

Add a shared `readCredentialFileProtection` for the four stores that write bare
ciphertext with no envelope, classifying with the same printable-UTF-8 test
`readStoredCredentialToken` already uses — so the reporter cannot drift into
disagreeing with the reader about the same bytes.

Linear and Jira report across every stored workspace/site rather than the
active one: sealing is a host-wide property, so a second workspace stored while
the keyring was missing is exposed even when the active one is sealed. Both
fields are optional, so an older remote host that omits them reads as unknown
rather than as sealed. Bitbucket reports null for env-supplied auth, where Orca
stores nothing and has no claim to make.

Also fixes the credential-connection test double, whose identity-function
`encryptString` wrote a readable token — faithful enough for a round-trip
assertion, but it made the suite assert that a sealed credential was exposed.

* chore(i18n): extract the unsealed-credential notice strings

CI's localization-extraction gate requires every translate() key to exist in
the primary catalog. Inserted in place rather than re-sorting the file, which
is not fully sorted and would have produced a 17k-line diff.

* test(web): pin the null protection fields on the desktop-only MiniMax bridge

The web bridge reports no protection because it stores nothing; the shape
assertions had to move with it.
2026-09-30 02:13:03 -07:00
Neil 17690e6b9a style: settle oxfmt 0.70 drift and stop formatting vendored licences (#23377)
The oxfmt 0.65 -> 0.70 bump landed without a repo-wide reformat, so 36 files
already in the tree no longer matched what the new version emits. Anyone running
`pnpm format` picked all of them up alongside their own change.

Also excludes `resources/licenses/**`: `oxfmt --write .` was rewriting the
vendored PCRE2 licence, turning its `*` redistribution bullets into `-`. Third
party licence text has to be reproduced verbatim, so formatting must not touch it.
2026-09-27 01:14:53 -07:00
OrcaWinandm4air 4085e1cf60 fix(memory): release stale session registries (#21734)
* fix(memory): bound session and lifecycle registries

* fix(memory): bound transient filesystem registries

* fix(memory): cap path and locale caches

* fix(memory): bound runtime recovery registries

* fix(memory): bound host mirror gap verdicts

* fix(memory): bound shell startup env cache

* fix(memory): bound gitlab host context cache

* fix(memory): release removed ssh generations

* fix(memory): expire cloud refresh replay guards

* fix(memory): release retired plugin generations

* fix(memory): bound plugin log key retention

* fix(memory): bound automation authority generations

* fix(memory): bound native chat enrichment cache

* fix(memory): bound web session tracking generations

* fix(memory): bound codex credential absence paths

* fix(memory): bound WSL canonical path cache

* fix(memory): bound sparse checkout cache

* fix(memory): bound shared directory cache

* fix(memory): bound advertised URL scan snapshots

* fix(memory): bound automation manager cache

* fix(memory): bound web session reorder intents

* fix(memory): bound web session focus intents

* fix(memory): bound web session handoffs

* fix(memory): bound automation dispatch tokens

* fix(memory): bound host mirror waiters

* fix(memory): bound retained session activity

* fix(memory): bound retained session activity

* fix(memory): bound web session close intents

* fix(memory): bound cloud session cache

* fix(memory): bound WSL home cache

* fix(memory): bound SSH capability cache

* fix(memory): bound trust grant cooldowns

* fix(memory): bound WSL auth drain state

* fix(memory): bound Linear workspace credential cache

* fix(memory): bound local Git capability cache

* fix(memory): bound WSL Git environment cache

* fix(memory): bound WSL Git environment cache

* fix(memory): bound WSL preflight cache

* fix(memory): keep hot cache entries warm

* fix(memory): preserve generation fences across eviction

* fix(memory): close remaining eviction fences

* fix(memory): align evicted upstream generations

* fix(memory): trim successful capability probes

* fix(auth): retain expired refresh replay evidence

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-09-20 14:41:50 -07:00
e373c89536 perf: cache update timestamps for Linear and Jira result sorting (#19473)
* perf: cache update timestamps for Linear and Jira result sorting

* perf(issues): build updatedAt key map without an intermediate tuple array

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: Neil <neil@stably.ai>
2026-09-08 19:44:13 -07:00
Neil 2aec442f42 Split Linear issue service by operation (#16770) 2026-08-27 17:04:48 -07:00
Neil 4371aaf722 refactor provider clients into domain modules (#16168) 2026-08-24 19:51:57 -07:00
Neil 838f5bfb75 fix(secrets): tell Linux users when their secrets are only obfuscated (#16033)
On Linux with no keyring, Electron falls back to the `basic_text` backend, which
"encrypts" with a hardcoded password. `isEncryptionAvailable()` returns true for
it, so Orca reported those secrets as sealed. They are not.

The obvious fix — returning false for basic_text — is wrong and would have been a
credential regression: `decryptWithStatus()` skips decryption entirely when
encryption is unavailable, so every already-stored secret would read back empty.
Sealing genuinely works on basic_text and must keep working.

So capability and trust are now separate questions. `isEncryptionAvailable()`
still answers "can this host seal and unseal", and `describeProtectionGap()`
(renamed from `describeUnavailable`) answers "is my data actually protected",
covering both no-sealing and weak-sealing.

That method had no production caller — the port documented a promise nothing
kept. `reportSecretProtectionGap()` now reads it at startup. A user-visible
surface is follow-up; this at least stops the silence.

Adds a bootstrap wiring guard over all nine host port installs. The no-op
defaults are correct for a renderer-less host and silently wrong for the desktop,
and a dropped or reordered install fails no existing test. Verified in both
directions: it fails when an install is removed, and when one moves after the
runtime is constructed.
2026-08-22 22:30:11 -07:00
Neil d07ce15cff refactor(host): route secret storage through a SecretStore port (#15916) 2026-08-22 16:38:00 -07:00
Brennan Benson 3fca1d1648 fix(linear): unbound list-issues by default, surface truncation, bind cursor workspace (#15824)
Fixes STA-5076.

list-issues capped at 50 by default and hard-clamped at 250, with hasMore buried
under result.meta and no stderr warning for --json, so a page that stopped early
read as a complete answer. Omitting --limit now walks Linear's pages until they
run out (meta.limit is null), and --limit <n> is the only cap, paging past
Linear's 250-per-request maximum to reach it. result.truncated sits next to
result.issues and is set only when a cap actually held results back; human output
prints "truncated: showing N".

The read still has to fit the CLI's 60s RPC budget, so a 20s wall-clock deadline
and a 200-page ceiling stop the walk early and report truncated with a
continuation cursor rather than failing the command.

Also:
- issued --cursor values bind the resolved workspace, so call -> nextCursor ->
  call works without --workspace; raw Linear cursors still need one and now carry
  nextSteps
- issued cursors whose payload smuggles back `all` or an empty workspace are
  rejected at decode, since either would widen the read past the bound workspace
- JSON issue rows carry priorityLabel (none/urgent/high/medium/low), matching
  orca linear priority set
- truncated and priorityLabel are optional on the wire, so a host that predates
  either is not read as "complete"; readers fall back to meta.hasMore
- the truncation line prints the rows actually rendered, so a remote result with
  no meta.returned cannot print "showing undefined"
2026-08-21 14:28:55 -07:00
Neil 83117f2860 refactor(integrations): split issue-tracker clients under the max-lines budget (#14704)
The GitLab, GitHub, Jira and Linear integration modules, their two IPC
registrars, and the shared GitHub project types each carried a file-level
`eslint-disable max-lines` and ran 351-614 counted lines against a 300-line
budget. AGENTS.md calls for splitting rather than suppressing, and
config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all
eight suppressions and prunes their entries (341 -> 333).

Pure move, no behavior change. Each client is cut along the seam it already
had: per-operation modules for the issue APIs (create / update / comment /
field options), and for Jira the request queue, site credential store,
authenticated request, and site identity. The two IPC registrars keep their own
handlers and delegate the rest to per-domain sub-registrars, so they remain
real entry points rather than re-export shims.

The IPC surface is proved intact rather than assumed: comparing (method,
channel) multisets between HEAD and the split gives 52 registrations across 52
distinct channels on both sides.

Provider-neutrality is preserved -- GitLab and GitHub keep separate, parallel
module layouts rather than being merged behind a shared abstraction.

Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(the one remaining failure is a pre-existing load flake in an untouched file,
green when re-run serially), no new runtime import cycles among 744 modules,
and no lint suppression added anywhere.
2026-08-15 18:17:20 -07:00
Neil 77f23b013f refactor(shared): drop the shared/types barrel and import from the real modules (#14447)
#14397 split `shared/types.ts` into 46 per-domain modules but kept the path as
a re-export barrel so the import sites did not have to change. This removes
the barrel: every consumer now imports from the module that actually declares
the type, and `src/shared/types.ts` is deleted.

Barrels hide where a type lives, make every consumer look like it depends on
the whole domain, and let an unrelated edit invalidate a module that ~2,000
files transitively import.

2,323 import declarations across 2,321 files. Rewritten mechanically: each
specifier was resolved to an absolute path via the TypeScript AST and
recomputed, rather than string-substituted, so alias forms (`@/../../shared/
types`) and per-specifier `type` modifiers survive.

Four cases the mechanical pass had to handle, each found by a gate rather than
by reading the diff:

- Modules inside `src/shared` import the barrel as `./types`, not
  `shared/types`. A pre-filter on the latter string skipped 176 of them and
  left imports dangling at a deleted file, which surfaced as confusing
  `Property 'x' is optional in type 'Repo' but required in Pick<Repo, ...>`
  errors rather than "module not found".
- The barrel RENAMED one type on the way through
  (`WorkspaceSource as WorkspaceCreateTelemetrySource`), so the original name
  in the owning module has to be re-aliased at each consumer.
- Three test files put `;(globalThis as ...)` on the line after the import.
  TypeScript parses that `;` as the import statement's terminator, so
  replacing through `statement.getEnd()` deletes it and breaks ASI. The
  rewrite now stops at the module specifier.
- A file that already imported directly from a module got a SECOND import
  from it, because the barrel re-exported those same names — which trips
  `import/no-duplicates` under `--deny-warnings`. A post-pass merges
  declarations sharing a specifier and type-only-ness; the `import type` plus
  `import` pair from one module is left alone, since that form is allowed.

Splitting one barrel import into several genuinely adds lines, which pushed
`terminal-layout-pty-ownership.ts` to 301 counted lines: its 107-character
import must wrap, and neither local type collapses onto one line (101 and 116
characters). Rather than contort a type declaration to fit a line budget,
`collectLeafIds` and `pruneLeaves` move to `terminal-pane-layout-tree.ts` —
they are pure structural operations on the layout tree and independent of PTY
ownership. `visible-worktrees.ts` similarly loses its own mini-barrel
re-export of `isDefaultBranchWorkspace`, with the four real consumers
repointed at the declaring module. No `max-lines` bypass added.

Verified: cold `tsc --noEmit` green on node, cli, and web (buildinfo deleted
first — these projects are `composite: true` and reuse stale caches); the full
`pnpm lint` green, not just bare oxlint — the narrower local check is what let
the duplicate imports reach CI; max-lines ratchet OK at 344.
2026-08-13 22:48:24 -07:00
Neil 583ab1601b refactor(shared): group worktree, github, and linear modules into folders (#14437)
`src/shared` is a flat directory of ~1,150 entries. The worktree, github, and
linear domains accounted for 71 of them, so finding the module you wanted meant
scanning a wall of same-prefixed filenames.

Move each domain into its own folder and drop the now-redundant prefix:

    src/shared/github-pr-types.ts    -> src/shared/github/pull-request-types.ts
    src/shared/worktree-id.ts        -> src/shared/worktree/id.ts
    src/shared/linear-links.ts       -> src/shared/linear/links.ts

This follows the existing `network/` and `new-workspace/` convention in the
same directory, which also drop the prefix inside the folder.

Whole clusters move, including tests. Foldering only part of a domain would be
worse than flat: a reader would have to check both `github/` and the flat
directory, and `github-auth-types.ts` / `github-project-types.ts` are type
modules that belong with the rest. No files with these prefixes remain flat.

Import specifiers were rewritten by resolving each one to an absolute path and
recomputing it, not by string substitution, so the `@/../../shared/...` alias
forms are handled correctly. 501 specifiers across 298 files.

Two things `tsc` cannot catch, handled explicitly:

- `github-project-types.ts` carries its own `max-lines` bypass, so its baseline
  entry is REPOINTED to the new path rather than pruned. Pruning would drop the
  bypass and then flag the new path as a fresh violation. Ratchet stays at 345.
- `mobile/` is outside `pnpm typecheck` and cannot be typechecked here
  (`mobile/node_modules` is empty). Instead every relative specifier in the repo
  was resolved against the filesystem: 174 unresolved before this change and 174
  after — identical, so nothing broke in mobile either.

The pinned `tests/e2e/.cross-version-checkouts` fixtures are deliberately NOT
rewritten; they are a snapshot of an older release and still reference the old
paths.

Verified: cold `tsc --noEmit` green on node, cli, and web (buildinfo deleted
first — these projects are `composite: true` and reuse stale caches).
2026-08-13 20:44:16 -07:00
Neil 991a3fe963 chore(lint): update oxlint to 1.77 and enable no-op cleanup rules (#13901)
Enable eleven oxlint rules that simplify code without changing behavior, and fix
every existing violation. Each candidate was gated on measured cost rather than
assumption, so rules that regressed runtime performance or type checking were
dropped instead of suppressed.

typescript/no-redundant-type-constituents is the largest addition: 113 sites, no
autofix. Dead constituents are deleted. Where the redundant literal existed to
document intent (`string | 'all'`), it is preserved as `(string & {})`, which
keeps the autocomplete hint the original code was reaching for instead of
flattening it away. The rule also caught a broken import —
remote-shared-control-retirement-probe.ts pulled RuntimeStatus from
src/shared/types, which does not export it, so the type silently degraded to
`any`; no tsconfig covers that file, so tsc never saw it.

oxlint stays at 1.77.0 rather than 1.78.0 because .npmrc sets
minimum-release-age=4320 and 1.78.0 is younger than that window.

Rules evaluated and rejected, with what disqualified each:
- prefer-string-raw: String.raw is a runtime call, not a literal (184x slower)
- prefer-string-replace-all: 26% slower
- text-encoding-identifier-case: ~5% slower, reproducible
- prefer-spread: [...str] is 110% slower than split('') and differs on surrogates
- no-implicit-coercion: `!!x` narrows types and `Boolean(x)` does not (22 tsc errors)
- prefer-arrow-callback: arrows are not constructible, breaking `new` on mocks
- object-shorthand: rewrites source text asserted by a tracked reliability gate
- switch-case-braces: pushes ten files past max-lines, which cannot be suppressed
- no-useless-switch-case: drops `case undefined:` that switch-exhaustiveness-check needs
- arrow-body-style: 115 violations have no fix, and it breaks max-lines
- newline-after-import: false-positives on the leading-semicolon ASI idiom

electron-vite-output-contract asserted on the literal
Object.prototype.hasOwnProperty.call text; retarget it to Object.hasOwn, which
rejects inherited keys identically.
2026-08-11 18:19:43 -07:00
Neil 934faaec76 refactor: deduplicate Linear team readers (#13417) 2026-08-09 18:54:22 -07:00
Neil 3200f7251c perf(startup): stop parsing qrcode and @linear/sdk at launch (#10788)
Both are reachable only from features most users never touch, but both were in
the main bundle's eager top-level require block.

@linear/sdk is the sharper case: linear-sdk.ts exists solely to load that ~2.6 MB
CJS bundle lazily, and a single value import in issue-relation-write.ts defeated
it for everyone. That file now imports the type and goes through the loader.

qrcode is only reachable from mobile pairing, and both call sites were already
async, so they take a dynamic import.
2026-07-26 20:31:58 -07:00
NeilandOrca aab112933e Revert "fix(memory): bound OOM-prone accumulators (#10179)" (#10255)
Co-authored-by: Orca <help@stably.ai>
2026-07-23 18:35:31 -07:00
Neil 8f40ddf328 fix(memory): bound OOM-prone accumulators (#10179) 2026-07-23 06:22:56 -07:00
Brennan Benson a10a2ba53c feat(linear): add MCP-style save issue (#9670)
* feat(linear): add MCP-style save issue

* fix(linear): harden save issue parity

* fix(linear): close save issue contract gaps

* docs(linear): bundle project discovery with save issue
2026-07-21 13:25:22 -07:00
Brennan Benson 87af1c8673 feat(linear): add complete issue relations (#9674)
* feat(linear): add complete issue relations

* fix(linear): harden relation reads and writes

* fix(linear): classify ambiguous relation writes
2026-07-21 13:21:19 -07:00
Brennan Benson 42a4f017b4 feat(linear): add MCP-compatible issue listing (#9672) 2026-07-21 13:16:44 -07:00
Brennan Benson be066fe8e9 feat(linear): expose issue activity history (#9667) 2026-07-21 13:13:05 -07:00
NeilandOrca 9b7d15362d perf(linear): lazy-load @linear/sdk off the launch parse path (#9698)
Co-authored-by: Orca <help@stably.ai>
2026-07-20 22:41:59 -07:00
Rod BoevandJinjing 877a74c193 feat(linear): use Linear branch names for worktrees (#8617)
* feat(linear): use Linear branch names for worktrees

* fix(linear): preserve branch overrides across composer resets

Normalize Linear branch metadata at the shared workspace-source boundary, restore it when repo changes preserve the issue, and clear it when another provider replaces or removes the link. Add regression coverage for each lifecycle transition.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-15 22:29:01 -07:00
Jinjing 6166f6c47a feat(linear): add server-side issue filters for status and other properties (#8021)
Add attribute filters (status, priority, assignee, labels) to the Linear
issues list, applied in GraphQL before pagination so hasMore matches the
filtered set. Thread filters through IPC/RPC/store cache with invalidation
on issue mutations, and mirror GitHub-style filter chrome on TaskPage.
2026-07-09 22:23:56 -07:00
Brennan Benson 7b7a21e3c7 Give agents access to inline Linear ticket screenshots and media (#7484) 2026-07-05 23:55:56 -07:00
NeilandOrca 885badba60 perf(linear): fetch issue comments in one request, not N+1 (#7497)
getIssueComments loaded the issue, then its comments, then awaited c.user
inside a for-loop. Accessing .user on the Linear SDK's Comment model lazily
issues a fresh user(id) GraphQL query, so a comment-heavy issue did issue +
comments + N sequential user round-trips — a visible multi-second stall on
open, burning the complexity-based rate limit and holding one of only 4 shared
Linear concurrency slots (acquire/release) for the whole N*latency window.

Replace with a single rawRequest that fetches each comment's author inline
(first: 50, matching the SDK default page the code already relied on), the same
pattern the rest of this file uses. createdAt is passed through as the ISO
string rawRequest already returns (no re-serialization), and null avatarUrl is
normalized to undefined — output shape is unchanged.

Test asserts one request regardless of comment count and correct author
mapping (present user, null avatar, absent user).

Co-authored-by: Orca <help@stably.ai>
2026-07-05 23:08:43 -07:00
NeilandOrca 46646d7ff1 chore(lint): upgrade oxlint to 1.71 + enable 7 new rules (autofixed backlog) (#6841)
* chore(lint): upgrade oxlint to 1.71 and enable 7 new rules

Upgrade oxlint 1.67.0 -> 1.71.0 (1.72 was blocked by the repo's 3-day
minimum-release-age supply-chain guard; nothing here needs it). The
bump is a no-op on the existing config.

Enable 3 error rules (backlog autofixed to zero in this commit) and
4 warn rules (surface signal without gating CI):

error (autofixed, behavior-preserving):
- unicorn/prefer-node-protocol        (~1531 sites: bare builtin -> node:)
- typescript/no-import-type-side-effects (~36: all-inline-type -> import type)
- unicorn/no-array-reverse            (19: copy-then-reverse -> toReversed)

warn (real signal, current fires are test-only/correct):
- unicorn/no-array-fill-with-reference-type  (aliasing footgun guard)
- typescript/no-unsafe-function-type         (bans bare Function type)
- unicorn/prefer-array-flat-map              (map().flat() -> flatMap())
- unicorn/prefer-regexp-test                 (.match() in bool ctx -> .test())

mobile/.oxlintrc.json extends root, so it inherits all 7; the autofix
ran from root and covered mobile/ too.

Verification (all green): oxlint 0 errors (root+mobile+aux configs),
oxfmt clean, typecheck (node+cli+web), vitest 22795 passed / 0 failed,
builds (electron-vite + web + cli) succeed. node: rewrites confirmed to
skip embedded SSH/CLI string payloads (AST-only); all toReversed sites
verified to operate on fresh copies or write-once locals.

* chore(lint): bump mobile oxlint to 1.71 so inherited rules parse

mobile/ is a standalone pnpm project pinning its own oxlint@1.67, which
lacks unicorn/no-array-fill-with-reference-type (needs >=1.70). Since
mobile/.oxlintrc.json extends the root config, mobile CI's 'cd mobile &&
oxlint' failed to parse the new rule. Bump mobile to match root (1.71).

Verified in mobile/: oxlint 0 errors, oxfmt --check clean, tsc --noEmit
pass, vitest 978 passed / 0 failed.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-06-29 22:38:29 -07:00
Jinwoo Hong 972078f2c4 Fix paste ownership, input bounds, and IPC validation
Supersedes #5745, #5746, and #5747.
2026-06-19 17:14:55 -07:00
Trevin ChowandJinjing 2b8d9a43de feat(linear): add project support to agent CLI (#5433)
* feat(linear): add project support to agent CLI

* fix(linear): resolve project names across search pages

* fix(linear): harden agent project support

* fix(linear): address project review feedback

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-06-15 18:24:53 -07:00
Brennan BensonandOrca e7906969cf Attach Linear issues from the worktree CLI (#5322)
Co-authored-by: Orca <help@stably.ai>
2026-06-13 15:20:08 -07:00
Brennan BensonandOrca 3a2d39cb37 Support full Linear task workflows from the CLI (#5323)
Co-authored-by: Orca <help@stably.ai>
2026-06-13 15:07:44 -07:00
Brennan BensonandOrca 74c961b09f Add Linear write commands for agents (#5165)
Co-authored-by: Orca <help@stably.ai>
2026-06-12 13:29:13 -07:00
buf0-bot[bot]andorca-bug-scan-bot 146da04af2 fix: address pr-bug-scan validated finding from #4683 (#5151)
Isolated CredentialDecryptionError per-item in Linear getClients (client.ts:518) and Jira getClients (client.ts:373) on the 'all' selection so one bad credential no longer collapses healthy workspaces

Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
2026-06-10 23:56:17 -07:00
Brennan BensonandOrca cdc0ca5e53 Add read-only orca linear CLI with trusted launch-prompt pointer (V1) (#5126)
Co-authored-by: Orca <help@stably.ai>
2026-06-10 20:20:50 -07:00
Brennan BensonandOrca ceae167427 Handle integration credential decrypt failures (#4683)
Co-authored-by: Orca <help@stably.ai>
2026-06-10 13:08:09 -07:00
Jinjing a67afb5a04 fix: address review findings (#4853) 2026-06-07 19:50:05 -07:00
Jinwoo HongandOrca 4f18c7e79d Allow Linear context issue lists to load more (#4512)
* Allow Linear context issue lists to load more

Co-authored-by: Orca <help@stably.ai>

* Simplify Linear load more footer copy

Co-authored-by: Orca <help@stably.ai>

* Page Linear issue reads past backend cap

Co-authored-by: Orca <help@stably.ai>

* Align Linear load more footer with GitHub pager

Co-authored-by: Orca <help@stably.ai>

* Use pager for Linear issue lists

Co-authored-by: Orca <help@stably.ai>

* Avoid phantom Linear issue pages

Co-authored-by: Orca <help@stably.ai>

* Fix local Linear issue pagination cap

Co-authored-by: Orca <help@stably.ai>

* Fix Linear pagination review issues

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-06-02 21:17:20 -04:00
Jinwoo HongandOrca 135c69d3e8 Add Linear issue load more (#4437)
Co-authored-by: Orca <help@stably.ai>
2026-06-01 23:35:47 -07:00
Jinjing ec14507457 fix: address review findings (#4452) 2026-06-01 23:34:39 -07:00