opencode-ascii 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 db
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,132 @@
1
+ # opencode-ascii
2
+
3
+ An [OpenCode](https://opencode.ai) plugin that automatically substitutes unicode characters with ASCII equivalents in AI responses and file edits.
4
+
5
+ ## Why?
6
+
7
+ LLMs love to reach for typographic characters — em-dashes, curly quotes, arrows, emoji — that look great in a browser but cause friction in terminals, code, config files, and plain-text tooling. This plugin intercepts output at two points:
8
+
9
+ - **AI text responses** (`experimental.text.complete`) — rewrites text parts before they are stored.
10
+ - **File write/edit tool calls** (`tool.execute.before`) — rewrites `write`, `edit`, and `patch` tool arguments before execution.
11
+
12
+ ## Installation
13
+
14
+ Add the package to your `opencode.json`:
15
+
16
+ ```json
17
+ {
18
+ "$schema": "https://opencode.ai/config.json",
19
+ "plugin": ["opencode-ascii"]
20
+ }
21
+ ```
22
+
23
+ OpenCode will install the package automatically via Bun at startup.
24
+
25
+ ## Configuration
26
+
27
+ All four substitution categories are **enabled by default**. Disable any category by passing options:
28
+
29
+ ```json
30
+ {
31
+ "$schema": "https://opencode.ai/config.json",
32
+ "plugin": [
33
+ ["opencode-ascii", {
34
+ "punctuation": true,
35
+ "arrows": true,
36
+ "math": true,
37
+ "emojis": false
38
+ }]
39
+ ]
40
+ }
41
+ ```
42
+
43
+ | Option | Type | Default | Description |
44
+ |---------------|---------|---------|----------------------------------------------|
45
+ | `punctuation` | boolean | `true` | Em/en dashes, ellipsis, curly/smart quotes |
46
+ | `arrows` | boolean | `true` | `→` → `->`, `←` → `<-`, `⇒` → `=>`, etc. |
47
+ | `math` | boolean | `true` | `≠` → `!=`, `≤` → `<=`, `×` → `*`, etc. |
48
+ | `emojis` | boolean | `true` | Common emoji → short ASCII labels |
49
+
50
+ ## Substitution reference
51
+
52
+ ### Punctuation
53
+
54
+ | Unicode | Character | ASCII |
55
+ |---------|-----------|-------|
56
+ | U+2014 | — | `--` |
57
+ | U+2013 | – | `-` |
58
+ | U+2026 | … | `...` |
59
+ | U+201C | " | `"` |
60
+ | U+201D | " | `"` |
61
+ | U+2018 | ' | `'` |
62
+ | U+2019 | ' | `'` |
63
+ | U+00AB | « | `"` |
64
+ | U+00BB | » | `"` |
65
+ | U+2022 | • | `-` |
66
+
67
+ ### Arrows
68
+
69
+ | Unicode | Character | ASCII |
70
+ |---------|-----------|-------|
71
+ | U+2192 | → | `->` |
72
+ | U+2190 | ← | `<-` |
73
+ | U+2191 | ↑ | `^` |
74
+ | U+2193 | ↓ | `v` |
75
+ | U+21D2 | ⇒ | `=>` |
76
+ | U+21D0 | ⇐ | `<=` |
77
+ | U+21D4 | ⇔ | `<=>` |
78
+ | U+2194 | | `<->` |
79
+
80
+ ### Math operators
81
+
82
+ | Unicode | Character | ASCII |
83
+ |---------|-----------|-------|
84
+ | U+2260 | ≠ | `!=` |
85
+ | U+2264 | ≤ | `<=` |
86
+ | U+2265 | ≥ | `>=` |
87
+ | U+00D7 | × | `*` |
88
+ | U+00F7 | ÷ | `/` |
89
+ | U+00B1 | ± | `+/-` |
90
+ | U+2212 | − | `-` |
91
+ | U+221E | ∞ | `inf` |
92
+ | U+2248 | ≈ | `~=` |
93
+ | U+221A | √ | `sqrt` |
94
+
95
+ ### Emojis
96
+
97
+ | Unicode | Character | ASCII |
98
+ |---------|-----------|-------|
99
+ | U+2713 | ✓ | `[x]` |
100
+ | U+274C | | `[!]` |
101
+ | U+26A0 | | `[!]` |
102
+ | U+2139 | | `[i]` |
103
+ | U+2B50 | | `[*]` |
104
+ | U+1F525 | | `[fire]` |
105
+ | U+1F680 | | `[>>]` |
106
+ | U+1F41B | | `[bug]` |
107
+ | U+1F4DD | | `[note]` |
108
+ | U+1F512 | | `[lock]` |
109
+ | U+1F513 | | `[open]` |
110
+ | U+1F4C1 | | `[dir]` |
111
+ | U+1F4C4 | | `[file]` |
112
+ | U+1F44D | | `[+1]` |
113
+ | U+1F44E | | `[-1]` |
114
+
115
+ See [`src/substitutions.ts`](src/substitutions.ts) for the full list.
116
+
117
+ ## How it works
118
+
119
+ The plugin uses two hooks:
120
+
121
+ 1. **`experimental.text.complete`** — fired by OpenCode after each AI text part finishes streaming. The plugin rewrites `output.text` in place before it is persisted.
122
+
123
+ 2. **`tool.execute.before`** — fired before any tool executes. The plugin rewrites:
124
+ - `output.args.content` for the `write` tool
125
+ - `output.args.newString` for the `edit` tool (never `oldString` — it must match existing file content exactly)
126
+ - `output.args.patch` for the `patch` tool
127
+
128
+ Substitution uses a single compiled regex built from all active mappings, so there is no O(n) string-replace loop per character.
129
+
130
+ ## License
131
+
132
+ MIT
@@ -0,0 +1,24 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ import { type SubstitutionConfig } from "./substitutions.js";
3
+ /**
4
+ * Options accepted by AsciiPlugin.
5
+ *
6
+ * All categories default to `true` (enabled).
7
+ * Set a category to `false` to skip substitution for it.
8
+ *
9
+ * @example
10
+ * // opencode.json — disable emoji and math substitutions
11
+ * {
12
+ * "plugin": [["opencode-ascii", { "emojis": false, "math": false }]]
13
+ * }
14
+ */
15
+ export type AsciiPluginOptions = SubstitutionConfig;
16
+ /**
17
+ * AsciiPlugin — substitutes unicode characters with ASCII equivalents
18
+ * in AI responses and file write/edit operations.
19
+ *
20
+ * Covered hooks:
21
+ * - `experimental.text.complete` : rewrites completed AI text parts
22
+ * - `tool.execute.before` : rewrites `write` and `edit` tool arguments
23
+ */
24
+ export declare const AsciiPlugin: Plugin;
package/dist/index.js ADDED
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AsciiPlugin = void 0;
4
+ const substitutions_js_1 = require("./substitutions.js");
5
+ function resolveConfig(options) {
6
+ if (!options)
7
+ return {};
8
+ const config = {};
9
+ if (typeof options["punctuation"] === "boolean")
10
+ config.punctuation = options["punctuation"];
11
+ if (typeof options["arrows"] === "boolean")
12
+ config.arrows = options["arrows"];
13
+ if (typeof options["math"] === "boolean")
14
+ config.math = options["math"];
15
+ if (typeof options["emojis"] === "boolean")
16
+ config.emojis = options["emojis"];
17
+ return config;
18
+ }
19
+ /**
20
+ * AsciiPlugin — substitutes unicode characters with ASCII equivalents
21
+ * in AI responses and file write/edit operations.
22
+ *
23
+ * Covered hooks:
24
+ * - `experimental.text.complete` : rewrites completed AI text parts
25
+ * - `tool.execute.before` : rewrites `write` and `edit` tool arguments
26
+ */
27
+ const AsciiPlugin = async (_ctx, options) => {
28
+ const config = resolveConfig(options);
29
+ const substitutions = (0, substitutions_js_1.buildSubstitutions)(config);
30
+ if (substitutions.length === 0) {
31
+ // All categories disabled — nothing to do.
32
+ return {};
33
+ }
34
+ const map = new Map(substitutions);
35
+ // Reset regex lastIndex before reuse by always using a fresh call to
36
+ // buildRegex; the 'g' flag is stateful so we rebuild per call or use
37
+ // a factory. We build once and rely on String.prototype.replace resetting it.
38
+ const regex = (0, substitutions_js_1.buildRegex)(substitutions);
39
+ function substitute(text) {
40
+ // Reset the regex state (stateful with /g flag)
41
+ regex.lastIndex = 0;
42
+ return (0, substitutions_js_1.applySubstitutions)(text, regex, map);
43
+ }
44
+ return {
45
+ /**
46
+ * Rewrite completed AI text parts before they are stored.
47
+ * `experimental.text.complete` fires once per text part after the
48
+ * streaming is done, giving us `output.text` to modify in place.
49
+ */
50
+ "experimental.text.complete": async (_input, output) => {
51
+ if (typeof output.text === "string") {
52
+ output.text = substitute(output.text);
53
+ }
54
+ },
55
+ /**
56
+ * Rewrite file-writing tool arguments before execution.
57
+ *
58
+ * Tools handled:
59
+ * - `write` : `args.content`
60
+ * - `edit` : `args.newString` (NOT `oldString` — it must match existing file content)
61
+ * - `patch` : `args.patch` (unified diff content)
62
+ */
63
+ "tool.execute.before": async (input, output) => {
64
+ switch (input.tool) {
65
+ case "write": {
66
+ if (typeof output.args?.content === "string") {
67
+ output.args.content = substitute(output.args.content);
68
+ }
69
+ break;
70
+ }
71
+ case "edit": {
72
+ if (typeof output.args?.newString === "string") {
73
+ output.args.newString = substitute(output.args.newString);
74
+ }
75
+ break;
76
+ }
77
+ case "patch": {
78
+ if (typeof output.args?.patch === "string") {
79
+ output.args.patch = substitute(output.args.patch);
80
+ }
81
+ break;
82
+ }
83
+ }
84
+ },
85
+ };
86
+ };
87
+ exports.AsciiPlugin = AsciiPlugin;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Unicode → ASCII substitution mappings, organised by category.
3
+ * Each entry is a tuple of [unicode, ascii].
4
+ */
5
+ export type Category = "punctuation" | "arrows" | "math" | "emojis";
6
+ export declare const PUNCTUATION: Array<[string, string]>;
7
+ export declare const ARROWS: Array<[string, string]>;
8
+ export declare const MATH: Array<[string, string]>;
9
+ export declare const EMOJIS: Array<[string, string]>;
10
+ export type SubstitutionConfig = {
11
+ punctuation?: boolean;
12
+ arrows?: boolean;
13
+ math?: boolean;
14
+ emojis?: boolean;
15
+ };
16
+ /**
17
+ * Build a combined substitution map from enabled categories.
18
+ */
19
+ export declare function buildSubstitutions(config?: SubstitutionConfig): Array<[string, string]>;
20
+ /**
21
+ * Build a compiled RegExp that matches all active unicode characters at once.
22
+ * This is much faster than running replace() N times.
23
+ */
24
+ export declare function buildRegex(substitutions: Array<[string, string]>): RegExp;
25
+ /**
26
+ * Apply substitutions to a string using a pre-built map and regex.
27
+ */
28
+ export declare function applySubstitutions(text: string, regex: RegExp, map: Map<string, string>): string;
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ /**
3
+ * Unicode → ASCII substitution mappings, organised by category.
4
+ * Each entry is a tuple of [unicode, ascii].
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.EMOJIS = exports.MATH = exports.ARROWS = exports.PUNCTUATION = void 0;
8
+ exports.buildSubstitutions = buildSubstitutions;
9
+ exports.buildRegex = buildRegex;
10
+ exports.applySubstitutions = applySubstitutions;
11
+ exports.PUNCTUATION = [
12
+ // Dashes
13
+ ["\u2014", "--"], // em dash (—)
14
+ ["\u2013", "-"], // en dash (–)
15
+ ["\u2012", "-"], // figure dash (‒)
16
+ ["\u2015", "--"], // horizontal bar (―)
17
+ // Ellipsis
18
+ ["\u2026", "..."], // horizontal ellipsis (…)
19
+ // Curly / smart quotes — double
20
+ ["\u201C", '"'], // left double quotation mark (")
21
+ ["\u201D", '"'], // right double quotation mark (")
22
+ ["\u201E", '"'], // double low-9 quotation mark („)
23
+ ["\u201F", '"'], // double high-reversed-9 quotation mark (‟)
24
+ ["\u00AB", '"'], // left-pointing double angle quotation mark («)
25
+ ["\u00BB", '"'], // right-pointing double angle quotation mark (»)
26
+ // Curly / smart quotes — single
27
+ ["\u2018", "'"], // left single quotation mark (')
28
+ ["\u2019", "'"], // right single quotation mark (')
29
+ ["\u201A", "'"], // single low-9 quotation mark (‚)
30
+ ["\u201B", "'"], // single high-reversed-9 quotation mark (‛)
31
+ ["\u2039", "'"], // single left-pointing angle quotation mark (‹)
32
+ ["\u203A", "'"], // single right-pointing angle quotation mark (›)
33
+ // Miscellaneous punctuation
34
+ ["\u2022", "-"], // bullet (•)
35
+ ["\u2023", ">"], // triangular bullet (‣)
36
+ ["\u2043", "-"], // hyphen bullet (⁃)
37
+ ["\u00B7", "."], // middle dot (·)
38
+ ["\u2027", "."], // hyphenation point (‧)
39
+ ["\u2032", "'"], // prime (′)
40
+ ["\u2033", '"'], // double prime (″)
41
+ ];
42
+ exports.ARROWS = [
43
+ ["\u2192", "->"], // rightwards arrow (→)
44
+ ["\u2190", "<-"], // leftwards arrow (←)
45
+ ["\u2191", "^"], // upwards arrow (↑)
46
+ ["\u2193", "v"], // downwards arrow (↓)
47
+ ["\u21D2", "=>"], // rightwards double arrow (⇒)
48
+ ["\u21D0", "<="], // leftwards double arrow (⇐)
49
+ ["\u21D4", "<=>"], // left right double arrow (⇔)
50
+ ["\u2194", "<->"], // left right arrow (↔)
51
+ ["\u21A6", "->"], // rightwards arrow from bar (↦)
52
+ ["\u21A4", "<-"], // leftwards arrow from bar (↤)
53
+ ["\u21E8", "->"], // rightwards white arrow (⇨)
54
+ ["\u21E6", "<-"], // leftwards white arrow (⇦)
55
+ ["\u2B9E", "->"], // black rightwards arrowhead (⮞) – U+2B9E
56
+ ["\u27A1", "->"], // black rightwards arrow (➡)
57
+ ["\u2B05", "<-"], // leftwards black arrow (⬅)
58
+ ["\u2B06", "^"], // upwards black arrow (⬆)
59
+ ["\u2B07", "v"], // downwards black arrow (⬇)
60
+ ];
61
+ exports.MATH = [
62
+ ["\u2260", "!="], // not equal to (≠)
63
+ ["\u2264", "<="], // less-than or equal to (≤)
64
+ ["\u2265", ">="], // greater-than or equal to (≥)
65
+ ["\u00D7", "*"], // multiplication sign (×)
66
+ ["\u00F7", "/"], // division sign (÷)
67
+ ["\u00B1", "+/-"], // plus-minus sign (±)
68
+ ["\u2212", "-"], // minus sign (−)
69
+ ["\u221E", "inf"], // infinity (∞)
70
+ ["\u2248", "~="], // almost equal to (≈)
71
+ ["\u223C", "~"], // tilde operator (∼)
72
+ ["\u221A", "sqrt"], // square root (√)
73
+ ["\u2211", "sum"], // n-ary summation (∑)
74
+ ["\u220F", "prod"], // n-ary product (∏)
75
+ ["\u2202", "d"], // partial differential (∂)
76
+ ["\u2207", "nabla"], // nabla (∇)
77
+ ["\u2208", "in"], // element of (∈)
78
+ ["\u2209", "not in"], // not an element of (∉)
79
+ ["\u2229", "&"], // intersection (∩)
80
+ ["\u222A", "|"], // union (∪)
81
+ ["\u2282", "<="], // subset of (⊂)
82
+ ["\u2283", ">="], // superset of (⊃)
83
+ ["\u2227", "&&"], // logical and (∧)
84
+ ["\u2228", "||"], // logical or (∨)
85
+ ["\u00AC", "!"], // not sign (¬)
86
+ ["\u2234", ":."], // therefore (∴)
87
+ ["\u2235", ":'"], // because (∵)
88
+ ];
89
+ exports.EMOJIS = [
90
+ // Checkmarks / cross marks
91
+ ["\u2713", "[x]"], // check mark (✓)
92
+ ["\u2714", "[x]"], // heavy check mark (✔)
93
+ ["\u2611", "[x]"], // ballot box with check (☑)
94
+ ["\u2705", "[x]"], // white heavy check mark (✅)
95
+ ["\u2717", "[!]"], // ballot x (✗)
96
+ ["\u2718", "[!]"], // heavy ballot x (✘)
97
+ ["\u274C", "[!]"], // cross mark (❌)
98
+ ["\u274E", "[!]"], // cross mark button (❎)
99
+ // Warnings / alerts
100
+ ["\u26A0", "[!]"], // warning sign (⚠)
101
+ ["\u2757", "[!]"], // heavy exclamation mark ornament (❗)
102
+ ["\u2755", "[!]"], // white exclamation mark ornament (❕)
103
+ ["\u26A1", "[!]"], // high voltage sign (⚡)
104
+ // Info / ideas
105
+ ["\u2139", "[i]"], // information source (ℹ)
106
+ ["\u1F4A1", "[i]"], // light bulb (💡)
107
+ // Stars / rating
108
+ ["\u2B50", "[*]"], // white medium star (⭐)
109
+ ["\u1F31F", "[*]"], // glowing star (🌟)
110
+ ["\u2605", "[*]"], // black star (★)
111
+ ["\u2606", "[*]"], // white star (☆)
112
+ // Common emoji sequences
113
+ ["\u1F525", "[fire]"], // fire (🔥)
114
+ ["\u1F680", "[>>]"], // rocket (🚀)
115
+ ["\u1F41B", "[bug]"], // bug (🐛)
116
+ ["\u1F41E", "[bug]"], // lady beetle (🐞)
117
+ ["\u1F4DD", "[note]"], // memo (📝)
118
+ ["\u270F", "[note]"], // pencil (✏)
119
+ ["\u1F512", "[lock]"], // lock (🔒)
120
+ ["\u1F513", "[open]"], // open lock (🔓)
121
+ ["\u1F4C1", "[dir]"], // file folder (📁)
122
+ ["\u1F4C2", "[dir]"], // open file folder (📂)
123
+ ["\u1F4C4", "[file]"], // page facing up (📄)
124
+ ["\u1F4C3", "[file]"], // page with curl (📃)
125
+ ["\u1F44D", "[+1]"], // thumbs up (👍)
126
+ ["\u1F44E", "[-1]"], // thumbs down (👎)
127
+ ["\u1F4AC", "[comment]"], // speech balloon (💬)
128
+ ["\u1F4E6", "[pkg]"], // package (📦)
129
+ ["\u1F517", "[link]"], // link (🔗)
130
+ ["\u1F6A7", "[wip]"], // construction sign (🚧)
131
+ ["\u2699", "[config]"], // gear (⚙)
132
+ ["\u1F527", "[fix]"], // wrench (🔧)
133
+ ["\u1F5D1", "[del]"], // wastebasket (🗑)
134
+ ["\u1F310", "[web]"], // globe with meridians (🌐)
135
+ ["\u1F4BB", "[pc]"], // personal computer (💻)
136
+ ["\u1F4F1", "[phone]"], // mobile phone (📱)
137
+ ["\u1F4E7", "[email]"], // e-mail (📧)
138
+ ["\u1F4CA", "[chart]"], // bar chart (📊)
139
+ ["\u1F4C8", "[up]"], // chart with upwards trend (📈)
140
+ ["\u1F4C9", "[down]"], // chart with downwards trend (📉)
141
+ ["\u2764", "[<3]"], // heavy black heart (❤)
142
+ ["\u1F4AF", "[100]"], // hundred points (💯)
143
+ ["\u1F44B", "[wave]"], // waving hand (👋)
144
+ ["\u1F91D", "[deal]"], // handshake (🤝)
145
+ ["\u1F4AA", "[strong]"], // flexed biceps (💪)
146
+ ["\u1F914", "[?]"], // thinking face (🤔)
147
+ ["\u1F644", "[eye-roll]"], // face with rolling eyes (🙄)
148
+ ["\u1F4A5", "[boom]"], // collision (💥)
149
+ ["\u1F389", "[party]"], // party popper (🎉)
150
+ ["\u1F3C6", "[trophy]"], // trophy (🏆)
151
+ ["\u1F4B0", "[money]"], // money bag (💰)
152
+ ["\u231B", "[wait]"], // hourglass (⌛)
153
+ ["\u23F3", "[wait]"], // hourglass with flowing sand (⏳)
154
+ ["\u1F504", "[refresh]"], // counterclockwise arrows button (🔄)
155
+ ["\u2705", "[done]"], // white heavy check mark (✅) — already above, kept for completeness
156
+ ];
157
+ const DEFAULT_CONFIG = {
158
+ punctuation: true,
159
+ arrows: true,
160
+ math: true,
161
+ emojis: true,
162
+ };
163
+ /**
164
+ * Build a combined substitution map from enabled categories.
165
+ */
166
+ function buildSubstitutions(config = {}) {
167
+ const resolved = { ...DEFAULT_CONFIG, ...config };
168
+ const entries = [];
169
+ if (resolved.punctuation)
170
+ entries.push(...exports.PUNCTUATION);
171
+ if (resolved.arrows)
172
+ entries.push(...exports.ARROWS);
173
+ if (resolved.math)
174
+ entries.push(...exports.MATH);
175
+ if (resolved.emojis)
176
+ entries.push(...exports.EMOJIS);
177
+ return entries;
178
+ }
179
+ /**
180
+ * Build a compiled RegExp that matches all active unicode characters at once.
181
+ * This is much faster than running replace() N times.
182
+ */
183
+ function buildRegex(substitutions) {
184
+ const pattern = substitutions
185
+ .map(([ch]) => ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
186
+ .join("|");
187
+ return new RegExp(pattern, "gu");
188
+ }
189
+ /**
190
+ * Apply substitutions to a string using a pre-built map and regex.
191
+ */
192
+ function applySubstitutions(text, regex, map) {
193
+ return text.replace(regex, (match) => map.get(match) ?? match);
194
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "opencode-ascii",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode plugin that substitutes unicode characters with ASCII equivalents in AI responses and file edits",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "peerDependencies": {
15
+ "@opencode-ai/plugin": ">=1.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@opencode-ai/plugin": "latest",
19
+ "typescript": "^5"
20
+ },
21
+ "keywords": [
22
+ "opencode",
23
+ "plugin",
24
+ "ascii",
25
+ "unicode"
26
+ ],
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/d3vv3/opencode-ascii.git"
31
+ }
32
+ }