pi-must-have-extension 0.4.0 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.3] - 2026-03-04
4
+
5
+ ### Fixed
6
+ - Use GitHub-compatible video embed format (thumbnail image linking to user-attachments video URL)
7
+
8
+ ## [0.4.2] - 2026-03-04
9
+
10
+ ### Fixed
11
+ - Restored missing asset references in README (demo.mp4 video and pi-must-have-extension.png image)
12
+
13
+ ## [0.4.1] - 2026-03-04
14
+
15
+ ### Changed
16
+ - Rewrote README.md with professional documentation standards
17
+ - Added comprehensive feature documentation, configuration reference, and usage examples
18
+ - Improved legacy config path detection with centralized lookup
19
+ - Added migration source tracking to config loader feedback
20
+
3
21
  ## 0.4.0 - 2026-03-02
4
22
 
5
23
  - Renamed package identity from `pi-must-have-plugin` to `pi-must-have-extension`.
package/README.md CHANGED
@@ -1,111 +1,177 @@
1
1
  # pi-must-have-extension
2
2
 
3
- Normalize RFC 2119 language in Pi prompts by automatically rewriting lowercase modal terms (for example `must`, `should not`, `optional`) into uppercase normative forms (`MUST`, `SHOULD NOT`, `OPTIONAL`).
3
+ Normalize RFC 2119 language in Pi prompts by automatically rewriting lowercase modal terms (`must`, `should not`, `optional`) into uppercase normative forms (`MUST`, `SHOULD NOT`, `OPTIONAL`).
4
4
 
5
- ## Origin
6
-
7
- This extension originated from the OpenCode plugin project: [ariane-emory/MUST-have-plugin](https://github.com/ariane-emory/MUST-have-plugin).
8
-
9
- `pi-must-have-extension` is a Pi-harness adaptation of that original project, converted into a modular TypeScript Pi extension.
10
-
11
- ## Preview
5
+ ![pi-must-have-extension](asset/pi-must-have-extension.png)
12
6
 
13
- ![pi-must-have-extension overview](https://raw.githubusercontent.com/MasuRii/pi-must-have-extension/main/asset/pi-must-have-extension.png)
7
+ ## Demo
14
8
 
15
9
  [![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)
16
10
 
17
- > npmjs.com README rendering does not reliably support inline `<video>` playback. Use the thumbnail above to play the demo video.
18
-
19
- Direct links:
20
- - Demo video (GitHub attachment): https://github.com/user-attachments/assets/22149125-8976-4d06-98cb-e7cfa180476d
21
- - Demo video file: https://raw.githubusercontent.com/MasuRii/pi-must-have-extension/main/asset/demo.mp4
22
-
23
11
  ## Features
24
12
 
25
- - Rewrites configurable keywords during normal prompt input.
26
- - Case-insensitive matching with longest-first phrase replacement.
27
- - Word-boundary-aware matching (does not replace inside larger words).
28
- - Leaves slash commands and shell-prefixed input unchanged.
29
- - Auto-creates a default config when none exists.
30
- - Supports legacy config path migration warnings.
31
- - Optional debug notifications in Pi TUI with replacement count/details in console logs.
13
+ - **RFC 2119/8174 compliance** — Transforms modal keywords to standard uppercase notation
14
+ - **Intelligent matching** — Case-insensitive with longest-first phrase replacement
15
+ - **Word-boundary aware** Does not replace keywords embedded inside larger words
16
+ - **Configurable replacements** Customize or extend the default keyword mappings
17
+ - **Input filtering** Leaves slash commands (`/`) and shell input (`!`) unchanged
18
+ - **Auto-configuration** Creates a default config file when none exists
19
+ - **Legacy migration** Automatically migrates configs from previous plugin versions
20
+ - **Debug mode** — Optional TUI notifications showing replacement counts
32
21
 
33
22
  ## Installation
34
23
 
35
- ### Local extension folder
24
+ ### Local Extension Folder
36
25
 
37
- Copy this repository to:
26
+ Copy this repository to one of the following locations:
38
27
 
39
- - Global: `~/.pi/agent/extensions/pi-must-have-extension`
40
- - Project: `.pi/extensions/pi-must-have-extension`
28
+ ```text
29
+ ~/.pi/agent/extensions/pi-must-have-extension # Global (all projects)
30
+ .pi/extensions/pi-must-have-extension # Project-specific
31
+ ```
41
32
 
42
- Pi will auto-discover it.
33
+ Pi will auto-discover the extension on startup.
43
34
 
44
- ### NPM package
35
+ ### NPM Package
45
36
 
46
37
  ```bash
47
38
  pi install npm:pi-must-have-extension
48
39
  ```
49
40
 
50
- ## Configuration
41
+ ## Usage
51
42
 
52
- Runtime config path:
43
+ Once installed, the extension works automatically. When you type prompts containing RFC 2119 keywords:
53
44
 
45
+ **Input:**
54
46
  ```text
55
- ~/.pi/agent/extensions/pi-must-have-extension/config.jsonc
47
+ The function must validate input and should log errors.
48
+ ```
49
+
50
+ **Transformed to:**
51
+ ```text
52
+ The function MUST validate input and SHOULD log errors.
56
53
  ```
57
54
 
58
- Legacy fallback paths (read-only fallback):
55
+ ### Skipped Input
56
+
57
+ The extension does not transform:
58
+
59
+ - Slash commands (e.g., `/help`, `/reload`)
60
+ - Shell commands (e.g., `!ls`, `!git status`)
61
+ - Empty input
62
+
63
+ ## Configuration
64
+
65
+ The extension uses a JSONC configuration file (JSON with comments):
59
66
 
60
67
  ```text
61
- ~/.pi/agent/extensions/pi-must-have-plugin/config.jsonc
62
- ~/.pi/agent/extensions/must-have-plugin/config.jsonc
63
- ~/.config/opencode/MUST-have-plugin.jsonc
68
+ ~/.pi/agent/extensions/pi-must-have-extension/config.jsonc
64
69
  ```
65
70
 
66
- Example config template is included at `config/config.example.jsonc`.
71
+ ### Default Configuration
67
72
 
68
73
  ```jsonc
69
74
  {
70
- "debug": false,
75
+ // Enable debug notifications in the TUI
76
+ // "debug": true,
77
+
71
78
  "replacements": {
72
79
  "must": "MUST",
73
80
  "must not": "MUST NOT",
74
- "should": "SHOULD"
81
+ "required": "REQUIRED",
82
+ "shall": "SHALL",
83
+ "shall not": "SHALL NOT",
84
+ "should": "SHOULD",
85
+ "should not": "SHOULD NOT",
86
+ "recommended": "RECOMMENDED",
87
+ "not recommended": "NOT RECOMMENDED",
88
+ "may": "MAY",
89
+ "optional": "OPTIONAL"
75
90
  }
76
91
  }
77
92
  ```
78
93
 
79
- An advanced replacement sample adapted from the original project is also included at:
94
+ ### Configuration Options
95
+
96
+ | Option | Type | Default | Description |
97
+ |--------|------|---------|-------------|
98
+ | `debug` | `boolean` | `false` | Enable TUI notifications showing replacement counts |
99
+ | `replacements` | `object` | RFC 2119 defaults | Key-value map of terms to replace |
100
+
101
+ ### Custom Replacements
102
+
103
+ You can add custom replacement rules or modify existing ones:
104
+
105
+ ```jsonc
106
+ {
107
+ "replacements": {
108
+ // Standard RFC 2119
109
+ "must": "MUST",
110
+ "should": "SHOULD",
111
+
112
+ // Custom shortcuts
113
+ "rfc!": "The key words in this document are to be interpreted as described in RFC 2119.\n\n",
114
+ "always": "**ALWAYS**",
115
+ "never": "**NEVER**"
116
+ }
117
+ }
118
+ ```
119
+
120
+ An advanced replacement sample is included at `config/replacements.custom-sample.jsonc`.
121
+
122
+ ### Legacy Config Paths
123
+
124
+ The extension supports migration from previous versions. Legacy configs are read from:
80
125
 
81
126
  ```text
82
- config/replacements.custom-sample.jsonc
127
+ ~/.pi/agent/extensions/pi-must-have-plugin/config.jsonc
128
+ ~/.pi/agent/extensions/must-have-plugin/config.jsonc
129
+ ~/.config/opencode/MUST-have-plugin.jsonc
83
130
  ```
84
131
 
85
- You can copy selected entries from that sample into your runtime `config.jsonc`.
132
+ On first run, legacy configs are automatically migrated to the new location.
133
+
134
+ ## Technical Details
135
+
136
+ ### How It Works
137
+
138
+ 1. **Session start**: Ensures config exists (creates default or migrates legacy)
139
+ 2. **Input event**: Intercepts user prompts before sending to the agent
140
+ 3. **Pattern matching**: Uses regex with word boundaries and longest-match-first ordering
141
+ 4. **Transformation**: Returns modified text while preserving images and other input data
86
142
 
87
- ## Development
143
+ ### Project Structure
144
+
145
+ ```text
146
+ ├── index.ts # Pi extension entrypoint
147
+ ├── src/
148
+ │ ├── index.ts # Extension event wiring
149
+ │ ├── constants.ts # Paths, defaults, and extension name
150
+ │ ├── types.ts # TypeScript interfaces
151
+ │ ├── config/
152
+ │ │ ├── config-loader.ts # Config loading and migration
153
+ │ │ └── jsonc.ts # JSONC parser (strips comments)
154
+ │ └── replacements/
155
+ │ └── replacement-engine.ts # Core replacement logic
156
+ ├── config/
157
+ │ ├── config.example.jsonc # Starter template
158
+ │ └── replacements.custom-sample.jsonc # Advanced samples
159
+ └── test/
160
+ └── replacement-engine.test.ts # Unit tests
161
+ ```
162
+
163
+ ### Development
88
164
 
89
165
  ```bash
90
- npm install
91
- npm run build
92
- npm run lint
93
- npm run test
94
- npm run check
166
+ npm install # Install dependencies
167
+ npm run build # Type-check with TypeScript
168
+ npm run test # Run test suite
169
+ npm run check # Build + test
95
170
  ```
96
171
 
97
- ## Project Structure
98
-
99
- - `index.ts` - Pi auto-discovery entrypoint.
100
- - `src/index.ts` - extension event wiring.
101
- - `src/config/` - config loading and JSONC parsing.
102
- - `src/replacements/` - replacement and input-skip engine.
103
- - `src/constants.ts` - extension constants and defaults.
104
- - `src/types.ts` - shared types.
105
- - `test/` - Node test suite.
106
- - `config/config.example.jsonc` - starter config template.
107
- - `config/replacements.custom-sample.jsonc` - advanced custom replacement sample.
108
- - `asset/` - README media (overview image and demo video).
172
+ ## Origin
173
+
174
+ 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.
109
175
 
110
176
  ## License
111
177
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-must-have-extension",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
4
4
  "description": "RFC 2119 keyword normalizer extension for the Pi coding agent.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -52,24 +52,44 @@ function parseConfigFromPath(path: string): Omit<ConfigLoadResult, "source"> {
52
52
  };
53
53
  }
54
54
 
55
+ const LEGACY_CONFIG_PATHS: readonly string[] = [
56
+ LEGACY_PI_MUST_HAVE_PLUGIN_CONFIG_PATH,
57
+ LEGACY_MUST_HAVE_PLUGIN_CONFIG_PATH,
58
+ LEGACY_OPENCODE_CONFIG_PATH,
59
+ ];
60
+
61
+ function findLegacyConfigPath(): string | undefined {
62
+ for (const path of LEGACY_CONFIG_PATHS) {
63
+ if (existsSync(path)) {
64
+ return path;
65
+ }
66
+ }
67
+
68
+ return undefined;
69
+ }
70
+
55
71
  export function ensureConfigExists(): EnsureConfigResult {
56
- if (
57
- existsSync(CONFIG_PATH) ||
58
- existsSync(LEGACY_PI_MUST_HAVE_PLUGIN_CONFIG_PATH) ||
59
- existsSync(LEGACY_MUST_HAVE_PLUGIN_CONFIG_PATH) ||
60
- existsSync(LEGACY_OPENCODE_CONFIG_PATH)
61
- ) {
72
+ if (existsSync(CONFIG_PATH)) {
62
73
  return { created: false };
63
74
  }
64
75
 
76
+ const legacyPath = findLegacyConfigPath();
77
+
65
78
  try {
66
79
  mkdirSync(CONFIG_DIR, { recursive: true });
80
+
81
+ if (legacyPath) {
82
+ const legacyContent = readFileSync(legacyPath, "utf-8");
83
+ writeFileSync(CONFIG_PATH, legacyContent, "utf-8");
84
+ return { created: true, migratedFrom: legacyPath };
85
+ }
86
+
67
87
  writeFileSync(CONFIG_PATH, DEFAULT_CONFIG, "utf-8");
68
88
  return { created: true };
69
89
  } catch (error) {
70
90
  return {
71
91
  created: false,
72
- error: `Failed to create default config at ${CONFIG_PATH}: ${error instanceof Error ? error.message : String(error)}`,
92
+ error: `Failed to initialize config at ${CONFIG_PATH}: ${error instanceof Error ? error.message : String(error)}`,
73
93
  };
74
94
  }
75
95
  }
package/src/index.ts CHANGED
@@ -49,6 +49,12 @@ export default function mustHaveExtension(pi: ExtensionAPI): void {
49
49
  if (ensureResult.error) {
50
50
  warnOnce(ensureResult.error, ctx);
51
51
  }
52
+ if (ensureResult.migratedFrom) {
53
+ warnOnce(
54
+ `${EXTENSION_NAME}: migrated legacy config from ${ensureResult.migratedFrom} to ${CONFIG_PATH}.`,
55
+ ctx,
56
+ );
57
+ }
52
58
 
53
59
  const loaded = loadConfig();
54
60
  if (loaded.warning) {
package/src/types.ts CHANGED
@@ -11,6 +11,7 @@ export interface ConfigLoadResult {
11
11
 
12
12
  export interface EnsureConfigResult {
13
13
  created: boolean;
14
+ migratedFrom?: string;
14
15
  error?: string;
15
16
  }
16
17