Skip to content

Harden extmgr package flows and migrate tooling to Biome#11

Merged
ayagmar merged 6 commits intomasterfrom
chore/biome-extmgr-fixes
Mar 28, 2026
Merged

Harden extmgr package flows and migrate tooling to Biome#11
ayagmar merged 6 commits intomasterfrom
chore/biome-extmgr-fixes

Conversation

@ayagmar
Copy link
Copy Markdown
Owner

@ayagmar ayagmar commented Mar 28, 2026

Summary

  • migrate lint/format tooling from ESLint + Prettier to Biome and update CI/docs
  • harden loader, reload, and local package identity handling in extmgr
  • make the Husky pre-commit hook portable by using the active Node/Corepack/pnpm toolchain
  • keep remaining scoped local installs visible after partial removals

Testing

  • pnpm run check

Summary by CodeRabbit

  • New Features

    • Adopted Biome for linting/formatting and added Biome config.
  • Configuration Changes

    • CI triggers now run on both main and master; pnpm setup simplified.
    • Package metadata updated (package manager declared); Node requirement relaxed to >=22.
    • Pre-commit checks tightened to fail fast when runtimes are missing.
  • Bug Fixes / UX

    • More robust local/global package identity and duplicate detection.
    • Tasks now can gracefully fall back when custom UI is unavailable.
    • Reload replaces restart in package/config flows.
  • Documentation

    • README and docs updated to reflect reload behavior and platform integration.
  • Tests

    • New and updated tests covering task loader, formatting, discovery, install/remove, and package config.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 491c82a5-159f-4230-8691-95b4b3c52834

📥 Commits

Reviewing files that changed from the base of the PR and between 61cc3c1 and 094f4db.

📒 Files selected for processing (7)
  • .github/workflows/cd.yml
  • .husky/pre-commit
  • README.md
  • src/packages/management.ts
  • src/utils/format.ts
  • test/format.test.ts
  • test/install-remove.test.ts
💤 Files with no reviewable changes (1)
  • .github/workflows/cd.yml
✅ Files skipped from review due to trivial changes (1)
  • test/format.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • README.md
  • .husky/pre-commit
  • src/utils/format.ts

📝 Walkthrough

Walkthrough

Replaces ESLint/Prettier with Biome; makes package identity normalization cwd-aware and canonicalizes local paths; refactors UnifiedItem into discriminated union; implements UI-loader fallback for tasks; propagates scope-aware identities across discovery/management/install flows; updates tests and CI/hooks accordingly.

Changes

Cohort / File(s) Summary
Tooling & CI
/.github/workflows/ci.yml, /.github/workflows/cd.yml, package.json, .prettierrc, .prettierignore, biome.json, eslint.config.js
Switched from ESLint/Prettier to Biome (added biome.json, removed old configs), updated CI branch filters and pnpm setup usage, adjusted package scripts/devDeps, packageManager and engines.
Pre-commit hook
.husky/pre-commit
Made hook strict (set -eu); require node present; prefer corepack pnpm when available, fallback to pnpm; exit with 127 on missing prerequisites.
Docs
README.md, docs/index.html
Adjusted installation/reload guidance to prefer /reload when Pi is running; relaxed Node engine to >=22; updated marketing text.
Biome config
biome.json
Added linter/formatter settings, rules, overrides, and assist actions.
Types
src/types/index.ts
Replaced single UnifiedItem with discriminated union `LocalUnifiedItem
Package identity & normalization
src/utils/package-source.ts, src/packages/discovery.ts, src/packages/catalog.ts, src/utils/status.ts
Added cwd option to normalizePackageIdentity; canonicalize local sources (file://, ~/, relative resolution); callers updated to pass scope-aware cwd (project vs agent dir) and dedupe logic updated.
Package management & install flows
src/packages/install.ts, src/packages/management.ts, src/packages/extensions.ts, src/packages/catalog.ts
Propagated cwd into normalization, added fallbackWithoutLoader: true to loader configs, strengthened removal matching to consider multiple identity variants, removed unreachable branch.
Async UI task loader
src/ui/async-task.ts, test/async-task.test.ts
Introduced TaskSuccess wrapper and runTaskWithoutLoader; runTaskWithLoader now handles UI returning undefined and can fall back to non-UI execution; added tests covering fallback and error cases.
Unified UI & package-config
src/ui/unified.ts, src/ui/package-config.ts, src/ui/footer.ts, src/ui/help.ts
Changed staged-change semantics (delete staged entry when equal to original), tightened duplicate detection, removed hasToggleRows, switched reload prompting to confirmReload, updated help text from restart→reload.
UI imports & minor refactors
multiple src/* files
Widespread import hygiene changes to use import { type ... } or mixed imports and reorderings; minor defensive/format tweaks across utils (charAt, **, parse guards).
Tests & helpers
test/**/*, test/helpers/*
Added/updated tests for loader fallback, cwd-aware identity resolution, removal behavior, unified-item dedup rules; test harness now exposes ctx.reload() and reloadCount().

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Loader as runTaskWithLoader
    participant UI as ctx.ui.custom
    participant Task

    rect rgba(100, 200, 150, 0.5)
    Note over Client,Task: Custom UI available → returns success
    Client->>Loader: runTaskWithLoader(task, config)
    Loader->>UI: ctx.ui.custom(startedTask)
    UI->>Loader: { type: "ok", value: T }
    Loader->>Client: result.value
    end

    rect rgba(150, 100, 200, 0.5)
    Note over Client,Task: No custom UI → run without loader
    Client->>Loader: runTaskWithLoader(task, config)
    Loader->>Loader: !hasCustomUI => runTaskWithoutLoader(task)
    Loader->>Task: execute
    Task-->>Loader: result
    Loader->>Client: result
    end

    rect rgba(200, 150, 100, 0.5)
    Note over Client,Task: Custom UI degrades (returns undefined)
    Client->>Loader: runTaskWithLoader(task, config)
    Loader->>UI: ctx.ui.custom(startedTask)
    UI->>Loader: undefined
    alt config.fallbackWithoutLoader
    Loader->>Loader: runTaskWithoutLoader(task)
    Loader->>Task: execute
    Task-->>Loader: result
    Loader->>Client: result
    else no fallback
    Loader->>Client: undefined
    end
    end
Loading
sequenceDiagram
    participant Manager as Package Manager
    participant Source as normalizePackageIdentity
    participant Discovery
    participant Catalog

    rect rgba(100, 150, 200, 0.5)
    Note over Manager,Catalog: Scope-aware identity normalization
    Manager->>Source: normalizePackageIdentity(source, { cwd: ctx.cwd OR getAgentDir() })
    Source->>Source: expand file://, ~/, resolve ./ ../ relative to cwd
    Source-->>Manager: normalized identity
    Manager->>Discovery: isSourceInstalled / match installed identities
    Discovery->>Catalog: dedupeInstalledPackages(installed, cwd)
    Catalog-->>Discovery: deduped list
    Discovery-->>Manager: matched package(s)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

Poem

🐰 I hopped through types and Biome trees,

When loaders fade I still appease,
Paths now honor cwd and home,
Packages found where they call home,
A gentle reload — a joyful thump of glee.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: migrating tooling to Biome and hardening package flows, which aligns with the substantial changes across CI/config files and source code.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/biome-extmgr-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/packages/management.ts (1)

60-66: ⚠️ Potential issue | 🟠 Major

Normalize both sides of the update check the same way.

Line 60 switched the requested package identity to cwd-aware normalization, but Line 65 still normalizes update.source without that context. Relative local sources can now compare unequal here, causing updatePackageInternal() to incorrectly report “already up to date” and skip the actual update.

Suggested fix
-    const hasUpdate = updates.some(
-      (update) => normalizePackageIdentity(update.source) === updateIdentity
-    );
+    const hasUpdate = updates.some(
+      (update) => packageIdentity(update.source, { cwd: ctx.cwd }) === updateIdentity
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/packages/management.ts` around lines 60 - 66, The comparison is
inconsistent: updateIdentity is normalized with cwd-aware options but
update.source is not, causing relative/local sources to mismatch and making
updatePackageInternal() wrongly skip updates; fix by normalizing update.source
the same way (call normalizePackageIdentity(update.source, { cwd: ctx.cwd })
when computing hasUpdate) so both sides use identical cwd-aware normalization
before the comparison with updateIdentity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.husky/pre-commit:
- Around line 10-12: The pre-commit hook should not use exec to run "corepack
pnpm run check" because exec replaces the shell and prevents the fallback to
direct "pnpm run check" if corepack is broken; change the logic so you first
detect corepack (command -v corepack) and then validate it (for example by
running a harmless check like "corepack pnpm --version" or running "corepack
pnpm run check" in a conditional) and only exec into corepack when that
validation succeeds, otherwise fall back to running "pnpm run check" directly so
the script can recover when corepack exists but is not functional.

In `@package.json`:
- Line 55: The package.json currently pins pnpm via the "packageManager" field
which conflicts with the CI workflow that also pins a pnpm version causing
ERR_PNPM_BAD_PM_VERSION; choose a single source of truth by removing or
unpinning the "packageManager" entry in package.json (the "packageManager" key)
or alternatively remove the pnpm pin from the CI workflow so only one location
controls the pnpm version—update whichever source you keep to the desired pnpm
version and ensure the other no longer specifies pnpm to avoid mismatches.

In `@README.md`:
- Line 159: Update the README tip that currently reads "After saving package
extension config, reload pi to apply changes." to use consistent product casing
and command phrasing by changing "pi" to "Pi" and referencing the reload command
as "/reload" (i.e., "After saving package extension config, run /reload to apply
changes."), locating the line that contains the exact string "After saving
package extension config, reload pi to apply changes." and replacing it
accordingly.

In `@src/packages/management.ts`:
- Around line 347-354: The removal logic computes identity =
packageIdentity(source, { cwd: ctx.cwd }) but compares installed entries using
project cwd or getAgentDir(), causing mismatches for globally installed local
packages; update the removal check (the later block around where removal is
decided, currently lines ~401-405) to reproduce the same candidate normalization
as the initial installed filter: generate the same packageIdentity variations
(using { cwd: ctx.cwd } and, when appropriate, { resolvedPath, cwd:
getAgentDir() }) or re-run the same installed.filter comparison against identity
so the comparison uses identical scope roots; reference packageIdentity,
identity, installed, ctx.cwd and getAgentDir() when applying the fix.

In `@src/utils/format.ts`:
- Around line 6-9: The truncate function can return a string longer than
maxLength when maxLength <= 3; update truncate to first handle small maxLength
by returning text.slice(0, maxLength) when maxLength <= 3 (no ellipsis),
otherwise keep the existing behavior (slice to maxLength - 3 and append '...');
modify the truncate function accordingly so its output length never exceeds
maxLength.

---

Outside diff comments:
In `@src/packages/management.ts`:
- Around line 60-66: The comparison is inconsistent: updateIdentity is
normalized with cwd-aware options but update.source is not, causing
relative/local sources to mismatch and making updatePackageInternal() wrongly
skip updates; fix by normalizing update.source the same way (call
normalizePackageIdentity(update.source, { cwd: ctx.cwd }) when computing
hasUpdate) so both sides use identical cwd-aware normalization before the
comparison with updateIdentity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 40d59ec1-e0d2-4230-9c17-99b57fe9b988

📥 Commits

Reviewing files that changed from the base of the PR and between 0849cf8 and 8f157cf.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (60)
  • .github/workflows/ci.yml
  • .husky/pre-commit
  • .prettierignore
  • .prettierrc
  • README.md
  • biome.json
  • docs/index.html
  • eslint.config.js
  • package.json
  • src/commands/auto-update.ts
  • src/commands/cache.ts
  • src/commands/history.ts
  • src/commands/install.ts
  • src/commands/registry.ts
  • src/commands/types.ts
  • src/extensions/discovery.ts
  • src/index.ts
  • src/packages/catalog.ts
  • src/packages/discovery.ts
  • src/packages/extensions.ts
  • src/packages/install.ts
  • src/packages/management.ts
  • src/types/index.ts
  • src/ui/async-task.ts
  • src/ui/footer.ts
  • src/ui/help.ts
  • src/ui/package-config.ts
  • src/ui/remote.ts
  • src/ui/theme.ts
  • src/ui/unified.ts
  • src/utils/auto-update.ts
  • src/utils/cache.ts
  • src/utils/command.ts
  • src/utils/format.ts
  • src/utils/history.ts
  • src/utils/mode.ts
  • src/utils/notify.ts
  • src/utils/npm-exec.ts
  • src/utils/package-source.ts
  • src/utils/retry.ts
  • src/utils/settings.ts
  • src/utils/status.ts
  • src/utils/ui-helpers.ts
  • test/async-task.test.ts
  • test/auto-update.test.ts
  • test/cache-history.test.ts
  • test/command-orchestration.test.ts
  • test/command-tokenizer.test.ts
  • test/discovery-parser.test.ts
  • test/helpers/mocks.ts
  • test/helpers/package-catalog.ts
  • test/install-remove.test.ts
  • test/npm-exec.test.ts
  • test/package-config.test.ts
  • test/package-extensions.test.ts
  • test/remote-ui.test.ts
  • test/rpc-mode.test.ts
  • test/search-regression.test.ts
  • test/settings-list.test.ts
  • test/unified-items.test.ts
💤 Files with no reviewable changes (3)
  • .prettierrc
  • .prettierignore
  • eslint.config.js

},
"author": "ayagmar",
"license": "MIT",
"packageManager": "[email protected]",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make pnpm versioning single-sourced.

Line 55 adds a pinned packageManager, but CI is still pinning pnpm separately and the workflow is already failing with ERR_PNPM_BAD_PM_VERSION. As written, this PR blocks the pipeline; please let either package.json or the GitHub Action own the pnpm version, not both.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 55, The package.json currently pins pnpm via the
"packageManager" field which conflicts with the CI workflow that also pins a
pnpm version causing ERR_PNPM_BAD_PM_VERSION; choose a single source of truth by
removing or unpinning the "packageManager" entry in package.json (the
"packageManager" key) or alternatively remove the pnpm pin from the CI workflow
so only one location controls the pnpm version—update whichever source you keep
to the desired pnpm version and ensure the other no longer specifies pnpm to
avoid mismatches.

Validate corepack before execing the pre-commit check and let CD use the packageManager-pinned pnpm version.

Keep removal identity matching consistent across project and global relative local package sources, clamp truncate() for very small limits, and refresh the related docs/tests.
@ayagmar ayagmar merged commit 84edbf4 into master Mar 28, 2026
3 checks passed
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