@jmcombs/pi-qwen-guard 0.0.0

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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/index.ts +77 -0
  4. package/package.json +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jeremy Combs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ <div align="center">
2
+ <img src="https://raw.githubusercontent.com/jmcombs/pi-extensions/main/assets/qwen-guard/preview.png" width="250" alt="pi-qwen-guard">
3
+ <br>
4
+ <a href="https://www.npmjs.com/package/@jmcombs/pi-qwen-guard"><img src="https://img.shields.io/npm/v/@jmcombs/pi-qwen-guard.svg" alt="npm version"></a>
5
+ <a href="https://www.npmjs.com/package/@jmcombs/pi-qwen-guard"><img src="https://img.shields.io/npm/dm/@jmcombs/pi-qwen-guard.svg" alt="npm downloads"></a>
6
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
7
+ <a href="https://github.com/jmcombs/pi-extensions/stargazers"><img src="https://img.shields.io/github/stars/jmcombs/pi-extensions?style=social" alt="GitHub stars"></a>
8
+ <a href="https://github.com/jmcombs/pi-extensions/issues"><img src="https://img.shields.io/github/issues/jmcombs/pi-extensions" alt="Open issues"></a>
9
+ <a href="https://github.com/sponsors/jmcombs"><img src="https://img.shields.io/badge/Sponsor-30363D?style=flat&logo=GitHub-Sponsors&logoColor=EA4AAA" alt="Sponsor"></a>
10
+ </div>
11
+
12
+ # @jmcombs/pi-qwen-guard
13
+
14
+ Automatically detects Qwen 3.6 (or any Qwen model via Ollama) and injects strict incremental-mode rules to prevent "error: terminated" and "Stream ended without finish_reason".
15
+
16
+ Just install and forget — works on every session.
17
+
18
+ ## Quick Start
19
+
20
+ ```bash
21
+ pi install @jmcombs/pi-qwen-guard
22
+ ```
23
+
24
+ The guard activates silently the moment you start a session with any Qwen model. No commands, no configuration, no secrets.
25
+
26
+ ## How It Works
27
+
28
+ On `session_start`:
29
+
30
+ - Inspects `ctx.model.id`.
31
+ - If it contains "qwen", sets an internal flag and shows a one-time success notification:
32
+ > 🛡️ pi-qwen-guard: Qwen3.6 incremental mode enabled
33
+
34
+ On every `before_agent_start` (i.e. before each agent turn):
35
+
36
+ - When the flag is set, appends a block of strict incremental-mode instructions to the system prompt.
37
+
38
+ The injected rules (abridged):
39
+
40
+ > CRITICAL QWEN3.6 / OLLAMA INCREMENTAL MODE (enforced every turn):
41
+ >
42
+ > - Never output more than ~70–80 lines of code in any single response.
43
+ > - Prefer the edit tool over write for any file that already exists.
44
+ > - Work in small logical chunks.
45
+ > - After completing a chunk, emit a progress signal that starts with exactly:
46
+ > `🛡️ pi-qwen-guard: ✅ Chunk complete. File is now X lines.`
47
+ > - You may then continue directly to the next chunk (no need to wait for user approval).
48
+
49
+ This forces the model to stay within Ollama's streaming limits and eliminates the two fatal errors.
50
+
51
+ The guard is a no-op for all non-Qwen models.
52
+
53
+ ## Development
54
+
55
+ This package lives in the [pi-extensions monorepo](https://github.com/jmcombs/pi-extensions).
56
+
57
+ ```bash
58
+ # From the repo root
59
+ npm ci
60
+ npm run check
61
+ ```
62
+
63
+ To try local changes against a real Pi session:
64
+
65
+ ```bash
66
+ pi -e ./packages/qwen-guard
67
+ ```
68
+
69
+ ## License
70
+
71
+ [MIT](./LICENSE) © Jeremy Combs
package/index.ts ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @jmcombs/pi-qwen-guard
3
+ *
4
+ * Automatically detects when a Qwen model (via Ollama) is active inside Pi and
5
+ * injects strict incremental-mode rules into the system prompt. The rules are
6
+ * only active while a Qwen model is selected and are removed when you switch
7
+ * to a different model.
8
+ *
9
+ * The guard provides:
10
+ * - Automatic activation notice when a Qwen model is selected
11
+ * - Strong behavioral constraints to reduce streaming terminations
12
+ * - Consistent `🛡️ pi-qwen-guard:` signaling (✅ for progress, ❌ for self-correction)
13
+ *
14
+ * Works with or without a separate plan-first / TODO workflow (though results
15
+ * are generally better when combined with one).
16
+ *
17
+ * See:
18
+ * - packages/qwen-guard/TESTING.md
19
+ * - CONTRIBUTING.md and TEMPLATE.md at the repo root
20
+ * - https://pi.dev/docs/extensions
21
+ */
22
+
23
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
24
+
25
+ const QWEN_INSTRUCTIONS = `
26
+ Qwen-safe incremental mode is now active.
27
+
28
+ Hard rules (these exist to prevent your responses from being killed by Ollama streaming limits):
29
+
30
+ - Never output more than ~60 lines of code or changes in a single response.
31
+ - After completing any meaningful piece of work (a function, a logical change, a file section, etc.), you must immediately output a single line that starts with exactly "🛡️ pi-qwen-guard: " followed by either ✅ (successful small chunk) or ❌ (you had to stop or self-correct because you were approaching limits).
32
+ - Prefer the edit tool over write for any existing file.
33
+ - Work in small, focused increments. Do not attempt large refactors, multiple files, or broad architectural changes in one response.
34
+ - You may continue to the next small chunk after signaling. You do **not** need explicit user approval after every signal.
35
+
36
+ For best results with Qwen models on Ollama, strongly consider using a plan-first workflow: create or maintain a TODO.md (or task_plan.md) with small atomic tasks, get the user's explicit approval on the plan before starting major work, and execute one task at a time while keeping the plan updated. This guard will still enforce the size limits and signaling even if you are not using a plan, but combining both is significantly more reliable and prevents streaming failures.
37
+
38
+ Violating the response size limit or failing to use the required signaling prefix will cause "Stream ended without finish_reason" or similar fatal errors.
39
+ `;
40
+
41
+ export default function (pi: ExtensionAPI): void {
42
+ let isQwenModel = false;
43
+
44
+ const updateQwenStatus = (modelId: string | undefined) => {
45
+ isQwenModel = (modelId?.toLowerCase() ?? "").includes("qwen");
46
+ };
47
+
48
+ pi.on("session_start", (_event, ctx) => {
49
+ updateQwenStatus(ctx.model?.id);
50
+
51
+ if (isQwenModel) {
52
+ ctx.ui.notify("🛡️ pi-qwen-guard: Qwen3.6 incremental mode enabled", "info");
53
+ }
54
+ });
55
+
56
+ pi.on("model_select", (event: unknown) => {
57
+ // event shape is typically { model: { id: string } }
58
+ const e = event as { model?: { id?: string }; id?: string };
59
+ const modelId = e.model?.id ?? e.id;
60
+ updateQwenStatus(modelId);
61
+ });
62
+
63
+ pi.on("before_agent_start", (event) => {
64
+ if (!isQwenModel) return;
65
+
66
+ const instructions = QWEN_INSTRUCTIONS.trim();
67
+
68
+ // Avoid appending multiple times if the prompt is rebuilt
69
+ if ((event.systemPrompt || "").includes("🛡️ pi-qwen-guard")) {
70
+ return;
71
+ }
72
+
73
+ return {
74
+ systemPrompt: (event.systemPrompt || "") + "\n\n" + instructions,
75
+ };
76
+ });
77
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@jmcombs/pi-qwen-guard",
3
+ "version": "0.0.0",
4
+ "description": "Auto-enables strict incremental mode for Qwen 3.6 (Ollama) to prevent 'terminated' and streaming errors.",
5
+ "homepage": "https://github.com/jmcombs/pi-extensions/tree/main/packages/qwen-guard",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/jmcombs/pi-extensions.git",
9
+ "directory": "packages/qwen-guard"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/jmcombs/pi-extensions/issues"
13
+ },
14
+ "license": "MIT",
15
+ "author": "Jeremy Combs",
16
+ "type": "module",
17
+ "main": "./index.ts",
18
+ "types": "./index.ts",
19
+ "files": [
20
+ "index.ts",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "keywords": [
25
+ "pi-package",
26
+ "pi-extension",
27
+ "qwen",
28
+ "ollama",
29
+ "guardrails"
30
+ ],
31
+ "pi": {
32
+ "extensions": [
33
+ "./index.ts"
34
+ ],
35
+ "image": "https://raw.githubusercontent.com/jmcombs/pi-extensions/main/assets/qwen-guard/preview.png"
36
+ },
37
+ "engines": {
38
+ "node": ">=22.0.0"
39
+ },
40
+ "peerDependencies": {
41
+ "@earendil-works/pi-coding-agent": "*",
42
+ "typebox": "*"
43
+ }
44
+ }