<div align="center">

# pi-must-have-extension

[![npm version](https://img.shields.io/npm/v/pi-must-have-extension?style=for-the-badge)](https://www.npmjs.com/package/pi-must-have-extension)
[![License](https://img.shields.io/github/license/MasuRii/pi-must-have-extension?style=for-the-badge)](LICENSE)
[![Platform](https://img.shields.io/badge/Platform-macOS%20%7C%20Linux%20%7C%20Windows-blue?style=for-the-badge)]()

[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Y8Y01PSSVR)

Normalize RFC 2119/8174 and BCP 14-adjacent language in Pi prompts by automatically rewriting lowercase modal terms (`must`, `should not`, `optional`) and common variants (`mustn't`, `ought not`, `not allowed`) into uppercase normative forms (`MUST`, `SHOULD NOT`, `OPTIONAL`, `MUST NOT`, `NOT ALLOWED`).
<img width="1360" height="752" alt="image" src="https://github.com/user-attachments/assets/d5aff97e-ac69-48d1-a071-c7f5460899e4" />

</div>

## Demo

[![Watch demo video](https://raw.githubusercontent.com/MasuRii/pi-must-have-extension/main/asset/pi-must-have-extension.png)](https://github.com/user-attachments/assets/22149125-8976-4d06-98cb-e7cfa180476d)

## Features

- **RFC 2119/8174 compliance** — Transforms modal keywords to standard uppercase notation
- **Expanded wording coverage** — Includes contractions and adjacent policy terms such as `mandatory`, `mustn't`, `ought not`, `not allowed`, `prohibited`, and `forbidden`
- **Intelligent matching** — Case-insensitive with longest-first phrase replacement
- **Word-boundary aware** — Does not replace keywords embedded inside larger words
- **Configurable replacements** — Customize or extend the default keyword mappings
- **Input filtering** — Leaves slash commands (`/`) and shell input (`!`) unchanged
- **Auto-configuration** — Creates a default config file when none exists
- **Legacy migration** — Automatically migrates configs from previous plugin versions
- **Debug mode** — Optional file-backed debug logging for config and replacement details without terminal output

## Installation

### Local Extension Folder

Copy this repository to one of the following locations:

```text
~/.pi/agent/extensions/pi-must-have-extension     # Global default (when PI_CODING_AGENT_DIR is unset)
.pi/extensions/pi-must-have-extension             # Project-specific
```

Pi will auto-discover the extension on startup.

### NPM Package

```bash
pi install npm:pi-must-have-extension
```

## Usage

Once installed, the extension works automatically. When you type prompts containing RFC 2119 keywords:

**Input:**
```text
The function must validate input and should log errors.
```

**Transformed to:**
```text
The function MUST validate input and SHOULD log errors.
```

### Skipped Input

The extension does not transform:

- Slash commands (e.g., `/help`, `/reload`)
- Shell commands (e.g., `!ls`, `!git status`)
- Empty input

## Configuration

The extension uses a JSONC configuration file (JSON with comments):

```text
Default global path: ~/.pi/agent/extensions/pi-must-have-extension/config.jsonc
Actual global path: $PI_CODING_AGENT_DIR/extensions/pi-must-have-extension/config.jsonc when PI_CODING_AGENT_DIR is set
```

### Default Configuration

```jsonc
{
  // Enable file-backed debug logging under debug/
  // "debug": true,

  "replacements": {
    "must": "MUST",
    "must not": "MUST NOT",
    "mustn't": "MUST NOT",
    "required": "REQUIRED",
    "mandatory": "MANDATORY",
    "shall": "SHALL",
    "shall not": "SHALL NOT",
    "shan't": "SHALL NOT",
    "should": "SHOULD",
    "should not": "SHOULD NOT",
    "shouldn't": "SHOULD NOT",
    "ought": "SHOULD",
    "ought not": "SHOULD NOT",
    "oughtn't": "SHOULD NOT",
    "recommended": "RECOMMENDED",
    "not recommended": "NOT RECOMMENDED",
    "may": "MAY",
    "permitted": "PERMITTED",
    "not permitted": "NOT PERMITTED",
    "allowed": "ALLOWED",
    "not allowed": "NOT ALLOWED",
    "optional": "OPTIONAL",
    "not optional": "NOT OPTIONAL",
    "prohibited": "PROHIBITED",
    "forbidden": "FORBIDDEN",
    "disallowed": "DISALLOWED"
  }
}
```

### Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `debug` | `boolean` | `false` | Enable file-backed debug logging under `debug/`; no debug output is printed to the terminal |
| `replacements` | `object` | RFC 2119 defaults | Key-value map of terms to replace |

### Custom Replacements

You can add custom replacement rules or modify existing ones:

```jsonc
{
  "replacements": {
    // Standard RFC 2119
    "must": "MUST",
    "should": "SHOULD",
    
    // Custom shortcuts
    "rfc!": "The key words in this document are to be interpreted as described in RFC 2119.\n\n",
    "always": "**ALWAYS**",
    "never": "**NEVER**"
  }
}
```

An advanced replacement sample is included at `config/replacements.custom-sample.jsonc`.

The expanded defaults preserve the official BCP 14 keywords from RFC 2119/RFC 8174 and add practical writing variants found in policy and instruction text. Official BCP 14 reference: https://www.rfc-editor.org/bcp/bcp14.txt.

### Legacy Config Paths

The extension supports migration from previous versions. Legacy configs are read from:

```text
Default global legacy paths (respect PI_CODING_AGENT_DIR for the Pi agent directory):
~/.pi/agent/extensions/pi-must-have-plugin/config.jsonc
~/.pi/agent/extensions/must-have-plugin/config.jsonc
~/.config/opencode/MUST-have-plugin.jsonc
```

On first run, legacy configs are automatically migrated to the new location.

## Technical Details

### How It Works

1. **Session start**: Ensures config exists (creates default or migrates legacy)
2. **Input event**: Intercepts user prompts before sending to the agent
3. **Pattern matching**: Uses regex with word boundaries and longest-match-first ordering
4. **Transformation**: Returns modified text while preserving images and other input data

### Project Structure

```text
├── index.ts                 # Pi extension entrypoint
├── src/
│   ├── index.ts             # Extension event wiring
│   ├── constants.ts         # Paths, defaults, and extension name
│   ├── types.ts             # TypeScript interfaces
│   ├── config/
│   │   ├── config-loader.ts # Config loading and migration
│   │   └── jsonc.ts         # JSONC parser (strips comments)
│   └── replacements/
│       └── replacement-engine.ts  # Core replacement logic
├── config/
│   ├── config.example.jsonc           # Starter template
│   └── replacements.custom-sample.jsonc  # Advanced samples
└── test/
    └── replacement-engine.test.ts     # Unit tests
```

### Development

```bash
npm install          # Install dependencies
npm run build        # Type-check with TypeScript
npm run test         # Run test suite
npm run check        # Build + test
```

## Origin

This extension is a Pi-harness adaptation of [ariane-emory/MUST-have-plugin](https://github.com/ariane-emory/MUST-have-plugin), converted into a modular TypeScript Pi extension.

## Related Pi Extensions

- [pi-permission-system](https://github.com/MasuRii/pi-permission-system) — Permission enforcement for tool and command access
- [pi-tool-display](https://github.com/MasuRii/pi-tool-display) — Compact tool rendering and diff visualization
- [pi-rtk-optimizer](https://github.com/MasuRii/pi-rtk-optimizer) — RTK command rewriting and output compaction
- [pi-multi-auth](https://github.com/MasuRii/pi-multi-auth) — Multi-provider credential management and quota-aware rotation

## License

[MIT](LICENSE)
