feat: Add Biome linting, upgrade to Node 20, and add CI workflows - #3
Merged
Conversation
- Add @biomejs/biome 2.3.9 with 4-space indentation for all files - Upgrade from Node 18 to Node 20 (.nvmrc and package.json engines) - Hybrid approach: Biome for code/JSON, Prettier for markdown/YAML/CSS - Add separate lint.yml and test.yml CI workflows - Update lefthook for file-specific linting (biome + prettier) - Fix Node.js import protocols, eliminate unused imports/variables - Achieve 100% type safety (zero any usage) - Update format scripts for unified Biome/Prettier workflow Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
github-actions Bot
pushed a commit
that referenced
this pull request
Jan 22, 2026
# 1.0.0 (2026-01-22) ### Bug Fixes * add UTF-8 charset to OAuth callback HTML responses ([#8](#8)) ([fcb2d49](fcb2d49)) ### Features * Add Biome linting, upgrade to Node 20, and add CI workflows ([#3](#3)) ([57f802e](57f802e)) * add loading animations with global API proxy integration ([#7](#7)) ([bf53bfb](bf53bfb)) * Add OAuth authentication with dynamic client registration ([#6](#6)) ([2845ebd](2845ebd)), closes [#5](#5) * add semantic release automation for NPM publishing ([#10](#10)) ([c756324](c756324)) * Refactor login command to auth with status/logout subcommands ([#4](#4)) ([59e6493](59e6493))
|
🎉 This PR is included in version 1.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
4 tasks
lmjabreu
added a commit
that referenced
this pull request
May 27, 2026
The Twist v4 workspace_users/get endpoint returns both active and removed members, with a `removed: boolean` field that the CLI was ignoring. In workspace 1585 this meant `tw users` listed 554 entries when only 146 were actual members, and `resolveUserRefs` would happily match a removed user by name/email, leading to confusing HTTP 403 "User N is not part of workspace" errors from downstream mutations like `tw channel add`. Filter removed users in `getWorkspaceUsers` by default, and add an `--include-removed` flag on `tw users` for the rare audit case where you actually want to see them. When that flag is set, the human-readable output appends a red [removed] chip; `removed` is exposed in the curated JSON essentials so callers can distinguish the two states. Follow-up to side-discovery #3 in #244. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lmjabreu
added a commit
that referenced
this pull request
May 27, 2026
## Summary
`tw users` was listing users who had been **removed** from the
workspace. In `Doist` (ws:1585) this meant 554 entries returned for what
is actually a 146-member workspace, polluting `tw users [--json]` output
and silently breaking `resolveUserRefs` — any command that takes a
name/email user ref (`tw channel add`, `tw groups add-user`, etc.) could
resolve to a tombstoned user and then 403 from the backend with `User N
is not part of workspace`.
Root cause: Twist's v4 `workspace_users/get?id=<wsid>` endpoint returns
both active and removed members with a `removed: boolean` flag. The
SDK's `WorkspaceUserSchema` already exposes that field — the CLI just
wasn't reading it. The curated `--json` formatter also stripped it, so
the staleness was invisible.
Reproducer (before the fix):
```
$ tw users 1585 --json --full | jq '[.[] | select(.removed)] | length'
408
$ tw users 1585 --json | jq '. | map(select(.id == 2342 or .id == 2343))'
[
{ "id": 2342, "name": "Test User1", "userType": "GUEST" },
{ "id": 2343, "name": "Test User 2", "userType": "USER" }
]
$ # Both above are removed members; adding them to a channel returns 403
$ # "User 2342 is not part of workspace 1585"
```
After the fix:
```
$ tw users 1585 --json | jq '. | length'
146
$ tw users 1585 --json --include-removed | jq '. | length'
554
$ tw users 1585 --include-removed --search "Test User1"
id:2342 Test User1 <test1@doist.io> [GUEST] [removed]
```
## Changes
| Layer | File | Notes |
|---|---|---|
| API helper | `src/lib/api.ts` | `getWorkspaceUsers(workspaceId, {
includeRemoved? })` filters `removed === true` by default. One filter,
fixes `tw users`, `resolveUserRefs`
([src/lib/refs.ts](https://github.com/Doist/twist-cli/blob/lmjabreu/confident-lalande-769bec/src/lib/refs.ts)),
and `resolveNotifyIds`
([src/commands/thread/helpers.ts](https://github.com/Doist/twist-cli/blob/lmjabreu/confident-lalande-769bec/src/commands/thread/helpers.ts))
in one shot. |
| Command | `src/commands/user.ts` | New `--include-removed` flag. Human
output appends a red `[removed]` chip. |
| JSON | `src/lib/output.ts` | Added `removed` to
`USER_ESSENTIAL_FIELDS` so callers can distinguish the two states
without `--full`. |
| Tests | `src/commands/user.test.ts` | Two new tests: default omits
removed users, flag passes through and chip renders. |
`getUserById`-based callers (e.g. `tw groups view`) were already correct
because they only fetch ids that the group's `userIds` list references —
removed users aren't there.
## Why a flag, not always filter
Audit/recovery cases occasionally do want the historical roster (e.g.
"who used to be in here?"). `--include-removed` covers that without
making the common case noisy.
## Side discoveries
None. Scope was kept tight to the diagnosed bug.
## Checks
- [x] `npm run type-check`
- [x] `npm run lint:check`
- [x] `npm test` — 653/653 (2 new)
- [x] Manual end-to-end against workspace 1585 (Doist) — counts above
Follow-up to side-discovery #3 in #244.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
This PR modernizes the codebase with comprehensive linting, formatting, and CI/CD improvements. It adds Biome linting (matching todoist-ai configuration), upgrades to Node 20, and establishes robust CI pipelines for code quality assurance.
Key Changes
🎯 Biome Linting Setup
@biomejs/biome2.3.9 with exact todoist-ai configurationnpm run formathandles both tools seamlessly🚀 Node.js Upgrade
package.jsonengines and add.nvmrc✨ Type Safety Improvements
anyusage (100% type-safe codebase)node:imports)🛠 Git Hooks & Automation
*.{ts,js,json}files in pre-commit*.{md,yml,yaml,css}files in pre-commit🤖 CI/CD Pipeline
lint.yml- Type checking, linting, and formatting validationtest.yml- Build verification and test executionCode Quality Metrics
Before vs After
anyusage → 0anyusageVerification
Migration Details
Breaking Changes
Developer Experience
npm run formathandles everythingTesting
All functionality verified:
🤖 Generated with Claude Code