Skip to content

feat!: complete architecture redesign with GraphQL codegen, auth system, and developer tooling#45

Merged
iamfj merged 177 commits intoczottmann:nextfrom
iamfj:v2
Feb 13, 2026
Merged

feat!: complete architecture redesign with GraphQL codegen, auth system, and developer tooling#45
iamfj merged 177 commits intoczottmann:nextfrom
iamfj:v2

Conversation

@iamfj
Copy link
Collaborator

@iamfj iamfj commented Feb 13, 2026

Overview

Hey Carlo! 👋

This is a comprehensive contribution from my fork that redesigns the Linearis architecture from the ground up. The goal was to make the codebase more maintainable, type-safe, and extensible — while also adding several new features that the community has been requesting.

This PR represents ~18 merged PRs in my fork, covering architecture, features, tooling, and documentation. I've organized everything below so you can review it incrementally.

Stats: 189 files changed, ~21k insertions, ~38k deletions (net reduction of ~17k lines)


What Changed and Why

1. GraphQL Codegen Migration

PRs: #2, #3, #4, #5, #6, #7, #8

Why: The original codebase used manually maintained TypeScript type definitions and runtime GraphQL string loading. This was error-prone, hard to keep in sync with the Linear API schema, and lacked compile-time type safety.

What:

  • Set up graphql-codegen infrastructure with a codegen.config.ts
  • Migrated all queries and mutations to .graphql files under graphql/queries/ and graphql/mutations/
  • Replaced all manual type definitions (linear-types.d.ts) with auto-generated types
  • Every client.request<T>() call is now fully typed with codegen output
  • Removed the old src/queries/ runtime loader and manual types

2. Five-Layer Architecture Redesign

PR: #10

Why: The original utils/ directory mixed concerns — ID resolution, GraphQL operations, business logic, and CLI orchestration were all tangled together. This made it hard to test individual layers, reuse logic, or reason about data flow.

What: Introduced a strict five-layer architecture:

Layer Directory Responsibility Client
Client src/client/ API wrappers
Resolver src/resolvers/ Human ID → UUID LinearSdkClient
Service src/services/ Business logic & CRUD GraphQLClient
Command src/commands/ CLI orchestration Both via createContext()
Common src/common/ Shared utilities

Key design decisions:

  • ID resolution happens exactly once in the resolver layer — services only accept UUIDs
  • Strict client separation: resolvers use the SDK client, services use the GraphQL client
  • Commands are thin orchestrators — no business logic, just wire up resolvers → services → output
  • Created typed GraphQLClient and LinearSdkClient wrappers
  • Added shared error types (notFoundError, multipleMatchesError)
  • Comprehensive unit tests for every resolver and service with one-layer-deep mocking

3. CLI Naming Redesign & Two-Tier Usage System

PRs: #12, #14

Why: Since Linearis is designed for LLM agents, the usage/help system needs to be token-efficient. Traditional --help output is verbose and wastes tokens. Also, some command names were inconsistent (e.g., project-milestones vs milestones, embeds vs files).

What:

  • Renamed commands for consistency: project-milestonesmilestones, embedsfiles
  • Standardized option names across all commands
  • Built a two-tier usage system optimized for LLM token consumption:
    • Tier 1 (linearis usage): ~200 token overview of all domains
    • Tier 2 (linearis <domain> usage): ~300-500 token detailed reference per domain
  • Every command exports DomainMeta with structured metadata
  • USAGE.md is auto-generated on every build and shipped with the package

4. Authentication System

PR: #26

Why: The original auth only supported env vars and a plaintext token file. There was no interactive setup, no token validation, and no secure storage — making it hard for new users to get started.

What: Full linearis auth command group:

  • linearis auth login — interactive token setup with browser-based token page, encrypted local storage
  • linearis auth status — shows current auth method and token source
  • linearis auth logout — removes stored credentials
  • AES-256-GCM encrypted token storage at ~/.linearis/token
  • Token validation via GetViewer GraphQL query
  • Structured auth error detection and output
  • Backwards-compatible with existing env var and legacy plaintext file
  • Comprehensive unit tests for all auth flows

5. Issue Relations Support

PR: #30 — addresses czottmann/linearis#27

Why: Community-requested feature for managing issue dependencies and relationships.

What:

  • New issue-relation-service with create, find, and delete operations
  • GraphQL mutations for issue relations
  • Relation flags on both issues create and issues update:
    linearis issues create "Task" --team ENG --blocks ENG-124
    linearis issues update ENG-123 --related ENG-200
    linearis issues update ENG-123 --duplicate ENG-50
  • Supports: --blocks, --blocked-by, --related, --duplicate
  • Unit tests for all relation operations

6. Developer Tooling

PRs: #29, #23, #9

What:

  • Biome for formatting and linting (replaces ad-hoc style)
  • Lefthook for git hooks (format + lint on pre-commit)
  • Commitlint for conventional commit enforcement
  • Updated CI workflow for clarity and structure
  • Added Claude code review and PR assistant workflows

7. Codebase Simplification

PR: #31

What: Final cleanup pass removing verbose JSDoc comments that restated what the code expressed, simplifying expressions, and removing unused interfaces. Net result: -634 lines with no behavioral changes.

8. Agent Instructions & Skills

PRs: #27, #28

What:

  • Comprehensive AGENTS.md (also serves as CLAUDE.md) documenting all architectural rules, patterns, and anti-patterns for AI-assisted development
  • 15 Claude Code agent skills for structured workflows (TDD, debugging, code review, PR creation, etc.)

Breaking Changes

  • Command renames: project-milestonesmilestones, embedsfiles
  • Option renames: Standardized across all commands (e.g., --issues-first--limit)
  • search subcommands merged into list with filter flags
  • Architecture: Complete reorganization of source files (relevant for contributors)

Upstream Issues Addressed

How to Review

I'd suggest reviewing in this order:

  1. Architecture overview: Read the updated AGENTS.md for the full picture
  2. GraphQL layer: graphql/ directory and codegen.config.ts
  3. Client layer: src/client/
  4. Common layer: src/common/
  5. Resolvers + Services: src/resolvers/ and src/services/
  6. Commands: src/commands/
  7. Tests: tests/unit/
  8. Auth system: src/commands/auth.ts, src/common/encryption.ts, src/common/token-storage.ts

All Fork PRs (chronological)

# PR Description
1 #2 Setup GraphQL codegen infrastructure
2 #3 Migrate GraphQL queries to separate files
3 #4 Migrate issues service to codegen types
4 #5 Fix command parameter types for codegen
5 #6 Migrate documents and attachments to codegen
6 #7 Migrate cycles and milestones to codegen
7 #8 Remove manual types, finalize codegen migration
8 #9 Add Claude Code GitHub workflow
9 #10 Five-layer architecture redesign
10 #12 CLI naming redesign + two-tier usage system
11 #14 Remove obsolete src/queries loaders
12 #23 Update CI workflow
13 #26 Authentication system with encrypted storage
14 #27 Claude Code agent skills
15 #28 AGENTS.md documentation
16 #29 Biome, lefthook, and commitlint tooling
17 #30 Issue relations support
18 #31 Codebase simplification

Happy to discuss any of this, break things into smaller pieces if preferred, or adjust the approach. Looking forward to your feedback! 🙏

iamfj and others added 30 commits February 4, 2026 10:07
Add GitHub Actions workflow for automated PR assistance using Claude.
This workflow provides intelligent PR reviews and suggestions.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add GitHub Actions workflow for automated code reviews using Claude.
This workflow analyzes code changes and provides detailed feedback.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add concurrency configuration to the Claude code review workflow to manage simultaneous runs and cancel in-progress jobs for pull requests.
Changes GetIssueById and GetIssueByIdentifier to use
CompleteIssueWithCommentsFields fragment instead of CompleteIssueFields,
restoring comment data that was inadvertently removed during the GraphQL
file migration.

This fixes a data regression where reading issues by ID or identifier
would no longer return comment data as expected.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Creates query loader modules in src/queries/ that read GraphQL operations
from .graphql files and export them as string constants. This bridges the
gap between the new .graphql file structure and existing service imports.

The loaders:
- Read .graphql files at runtime using Node.js fs module
- Extract individual operations with fragment dependencies
- Export query/mutation strings with the same names services expect
- Enable existing code to work without modification

Fixes TypeScript compilation errors where services imported from deleted
src/queries/*.ts files. Services now successfully import from the new
loader modules which dynamically load from graphql/queries/ and
graphql/mutations/ directories.

Files added:
- src/queries/issues.ts
- src/queries/documents.ts
- src/queries/attachments.ts
- src/queries/project-milestones.ts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updates AGENTS.md (CLAUDE.md) to reflect the new GraphQL architecture:

- Documents the dual structure of graphql/ directory (source .graphql files)
  and src/queries/ (runtime query loaders)
- Updates "Query Definitions" section to explain both components
- Rewrites "Adding GraphQL Queries" workflow to document the new process:
  1. Define operations in .graphql files
  2. Run npm run generate for codegen
  3. Query loaders automatically extract operations
- Changes references from src/queries/common.ts to graphql/queries/issues.graphql
- Explains the separation between human-written .graphql files and
  generated TypeScript types in src/gql/

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add type aliases for GraphQL query/mutation return types to improve
readability in method signatures.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Return raw codegen types directly instead of transforming to manual
types.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Return union type of raw codegen types instead of transforming.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Return raw codegen type directly.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Return raw codegen type directly.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Use QuerySearchIssuesArgs instead of full query type. Remove
transformation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Delete transformIssueData and doTransformIssueData - no longer needed
since services return raw codegen types.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Line 166 referenced undefined variable 'id' instead of 'input.id'.
This caused a ReferenceError when resolving non-UUID issue identifiers.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Line 572 referenced non-existent 'input.milestoneId' instead of
'input.projectMilestoneId'. This would show 'undefined' in error
messages when milestone resolution fails.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Lines 670-677 spread entire searchArgs object into GraphQL variables,
but SearchIssues query only accepts 'term' and 'first' parameters.
This caused GraphQL validation errors when extra properties like
'limit' were passed through.

Now destructures only 'term' from searchArgs and passes it explicitly
along with 'first' parameter.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Pass QuerySearchIssuesArgs fields directly instead of wrong type.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Ensure parameters match IssueUpdateInput type from codegen.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add type aliases, remove transformations, return raw GraphQL types.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add type aliases, remove transformations, return raw GraphQL types.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Define CycleListOptions and CycleReadOptions locally. Replace
LinearCycle with codegen type alias.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add missing option interfaces (MilestoneListOptions, MilestoneReadOptions,
MilestoneCreateOptions, MilestoneUpdateOptions) and replace
LinearProjectMilestone with ProjectMilestoneUpdateInput from codegen.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Delete linear-types.d.ts - all types now generated from GraphQL
schema via codegen.

- Add type aliases in linear-service.ts for LinearLabel, LinearComment,
  and CreateCommentArgs
- Replace LinearProject with inline type definition
- Fix bug in graphql-issues-service.ts: use input.projectMilestoneId
  instead of input.milestoneId
- Remove dead code for milestone fallback lookup

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
iamfj and others added 23 commits February 9, 2026 23:05
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cess

Biome's --unsafe flag incorrectly converted command.parent!.parent!
to optional chaining, causing TypeScript errors. Non-null assertions
are correct here since Commander.js always sets parent references.

Also disables noNonNullAssertion biome rule as it conflicts with
the established Commander.js patterns in this codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
build(tooling): add biome, lefthook, and commitlint
…s with relations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tring

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Moves resolveIssueId calls for relation targets to before the
createIssue call so invalid target identifiers fail fast without
leaving a partially created issue.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers the defensive null check branch on the issue query result.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deduplicate relation flag logic between issues create and update by
extracting resolveRelationTarget() and applyRelation() helpers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidate duplicated relation flag validation from create and update
handlers into a shared function using the RelationFlags interface.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(issues): add issue relations support (blocking/blocked by/related/duplicate)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidates duplicate regex loops in embed-parser, simplifies
URL parsing, and removes redundant documentation comments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extracts shared SOURCE_LABELS constant in auth, inlines arrow
functions, uses ternary for optional ID resolution, and removes
unused ErrorResponse interface.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: remove verbose JSDoc and simplify codebase
@iamfj iamfj self-assigned this Feb 13, 2026
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.

1 participant