# How Earendil π Detects Self-Updates for Yarn

> Learn how Earendil π detects Yarn self-updates. Discover the script path inspection and global add command with conditional package removal for seamless updates.

- Repository: [Earendil Works/pi](https://github.com/earendil-works/pi)
- Tags: how-to-guide
- Published: 2026-05-25

---

**TLDR:** Earendil π detects Yarn installations by inspecting the script path for `/yarn/` or `/.yarn/` segments, then generates a self-update command using `yarn global add --ignore-scripts` with conditional removal of the previous package version if the package name has changed.

Earendil π is a coding agent CLI that manages its own lifecycle across different package managers. When users install the tool globally via Yarn, the application must identify this environment and produce the correct upgrade sequence to avoid permission errors or script execution vulnerabilities. The detection and command generation logic lives in the configuration module, where specific string patterns in the installation path trigger Yarn-specific update behavior.

## Detecting Yarn Installations via Path Inspection

The `detectInstallMethod()` function in [`packages/coding-agent/src/config.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/config.ts) determines the package manager by analyzing the resolved path of the currently executing script. This approach avoids relying on environment variables that might not persist across shell sessions.

### The Yarn Path Signature

At lines 71-73 of [`config.ts`](https://github.com/earendil-works/pi/blob/main/config.ts), the function checks if the script path contains either `"/yarn/"` or `"/.yarn/"`. If either substring is present, the function returns `"yarn"` as the install method identifier.

```typescript
// packages/coding-agent/src/config.ts (lines 71-73)
if (scriptPath.includes("/yarn/") || scriptPath.includes("/.yarn/")) {
  return "yarn";
}

```

This detection works because Yarn installs global packages into directories that include these path segments, whether using Yarn Classic or Yarn Berry (Plug'n'Play) configurations.

## Building the Self-Update Command for Yarn

Once the install method is identified as `"yarn"`, the `getSelfUpdateCommandForMethod()` function constructs the appropriate shell commands. Located at lines 119-125 in [`packages/coding-agent/src/config.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/config.ts), this function handles both standard updates and package name migrations.

### The Command Construction Logic

The function uses helper utilities `makeSelfUpdateCommand()` and `makeSelfUpdateCommandStep()` to assemble the final command string. For Yarn installations, it produces a command that adds the new package globally while bypassing lifecycle scripts:

```typescript
// Yarn case (packages/coding-agent/src/config.ts lines 119-125)
return makeSelfUpdateCommand(
    makeSelfUpdateCommandStep("yarn", ["global", "add", "--ignore-scripts", updatePackageName]),
    updatePackageName === installedPackageName
        ? undefined
        : makeSelfUpdateCommandStep("yarn", ["global", "remove", installedPackageName]),
);

```

### Handling Package Name Changes

The logic includes a conditional removal step. When `updatePackageName` differs from `installedPackageName`—such as when migrating between npm scopes or package names—the function prepends a `yarn global remove` command for the old package. This prevents orphaned installations and ensures the CLI binary resolves to the new package location.

### Security Considerations with --ignore-scripts

The generated command explicitly includes the `--ignore-scripts` flag. This prevents post-install scripts from executing during the update process, mitigating supply chain attack vectors where malicious code might run with the user's privileges during the upgrade.

## Practical Examples of Yarn Self-Updates

When you run `pi self-update` on a Yarn-installed instance, the tool executes precisely the commands generated by the logic above.

Standard update (same package name):

```bash

# Detects Yarn installation, executes:

yarn global add --ignore-scripts pi-coding-agent

```

Update with package migration (different names):

```bash

# Detects Yarn installation and name change, executes:

yarn global remove old-scope/pi-coding-agent
yarn global add --ignore-scripts new-scope/pi-coding-agent

```

## Testing the Yarn Detection Logic

The Yarn self-update behavior is validated in [`packages/coding-agent/test/plan-mode-utils.test.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/test/plan-mode-utils.test.ts). These tests verify that the command builder correctly sequences removal before addition when the package identifiers differ, ensuring that the `$PATH` resolution always points to the latest version without conflicts.

## Summary

- **Path-based detection:** Earendil π identifies Yarn installations by checking for `"/yarn/"` or `"/.yarn/"` substrings in the script path via `detectInstallMethod()`.
- **Command generation:** The `getSelfUpdateCommandForMethod()` function in [`packages/coding-agent/src/config.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/config.ts) constructs the update commands.
- **Safe upgrades:** The generated command uses `yarn global add --ignore-scripts` to prevent arbitrary script execution during updates.
- **Migration support:** If the package name changes, the logic automatically prepends a `yarn global remove` command for the previous package.
- **Test coverage:** The behavior is verified in [`packages/coding-agent/test/plan-mode-utils.test.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/test/plan-mode-utils.test.ts).

## Frequently Asked Questions

### How does Earendil π determine if it was installed using Yarn?

Earendil π checks the resolved file path of the running script for the substrings `"/yarn/"` or `"/.yarn/"`. If either pattern is found in the path returned by `detectInstallMethod()`, the CLI registers the install method as `"yarn"` and uses Yarn-specific logic for subsequent self-update operations.

### What happens when the package name changes during a self-update?

When `updatePackageName` differs from `installedPackageName`, the `getSelfUpdateCommandForMethod()` function generates a two-step command. It first removes the old package using `yarn global remove`, then installs the new package with `yarn global add --ignore-scripts`. This ensures no binary conflicts occur in the global Yarn bin directory.

### Why does Earendil π use the --ignore-scripts flag for Yarn updates?

The `--ignore-scripts` flag is included in the Yarn global add command to prevent the execution of post-install lifecycle scripts during the update process. This security measure protects users from potentially malicious code that could otherwise run automatically when upgrading the package.

### Where is the Yarn self-update detection logic tested?

The Yarn-specific self-update logic is tested in [`packages/coding-agent/test/plan-mode-utils.test.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/test/plan-mode-utils.test.ts). These tests confirm that the command builder correctly orders removal and installation steps when package names differ, and that the `--ignore-scripts` flag is present in the generated command structure.