opencode-ascii 0.1.1 → 0.1.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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
- import { type SubstitutionConfig } from "./substitutions.js";
2
+ import { type SubstitutionConfig } from "./substitutions";
3
3
  /**
4
4
  * Options accepted by AsciiPlugin.
5
5
  *
@@ -19,6 +19,7 @@ export type AsciiPluginOptions = SubstitutionConfig;
19
19
  *
20
20
  * Covered hooks:
21
21
  * - `experimental.text.complete` : rewrites completed AI text parts
22
- * - `tool.execute.before` : rewrites `write` and `edit` tool arguments
22
+ * - `tool.execute.before` : rewrites `write`, `edit`, `multiedit`, and `apply_patch` tool arguments
23
23
  */
24
24
  export declare const AsciiPlugin: Plugin;
25
+ export default AsciiPlugin;
package/dist/index.js CHANGED
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AsciiPlugin = void 0;
4
- const substitutions_js_1 = require("./substitutions.js");
1
+ import { buildSubstitutions, buildRegex, applySubstitutions, } from "./substitutions";
5
2
  function resolveConfig(options) {
6
3
  if (!options)
7
4
  return {};
@@ -22,11 +19,11 @@ function resolveConfig(options) {
22
19
  *
23
20
  * Covered hooks:
24
21
  * - `experimental.text.complete` : rewrites completed AI text parts
25
- * - `tool.execute.before` : rewrites `write` and `edit` tool arguments
22
+ * - `tool.execute.before` : rewrites `write`, `edit`, `multiedit`, and `apply_patch` tool arguments
26
23
  */
27
- const AsciiPlugin = async (_ctx, options) => {
24
+ export const AsciiPlugin = async (_ctx, options) => {
28
25
  const config = resolveConfig(options);
29
- const substitutions = (0, substitutions_js_1.buildSubstitutions)(config);
26
+ const substitutions = buildSubstitutions(config);
30
27
  if (substitutions.length === 0) {
31
28
  // All categories disabled — nothing to do.
32
29
  return {};
@@ -35,11 +32,11 @@ const AsciiPlugin = async (_ctx, options) => {
35
32
  // Reset regex lastIndex before reuse by always using a fresh call to
36
33
  // buildRegex; the 'g' flag is stateful so we rebuild per call or use
37
34
  // a factory. We build once and rely on String.prototype.replace resetting it.
38
- const regex = (0, substitutions_js_1.buildRegex)(substitutions);
35
+ const regex = buildRegex(substitutions);
39
36
  function substitute(text) {
40
37
  // Reset the regex state (stateful with /g flag)
41
38
  regex.lastIndex = 0;
42
- return (0, substitutions_js_1.applySubstitutions)(text, regex, map);
39
+ return applySubstitutions(text, regex, map);
43
40
  }
44
41
  return {
45
42
  /**
@@ -56,9 +53,10 @@ const AsciiPlugin = async (_ctx, options) => {
56
53
  * Rewrite file-writing tool arguments before execution.
57
54
  *
58
55
  * 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)
56
+ * - `write` : `args.content`
57
+ * - `edit` : `args.newString` (NOT `oldString` -- it must match existing file content)
58
+ * - `multiedit` : each `args.edits[].newString`
59
+ * - `apply_patch` : `args.patchText` (unified diff content)
62
60
  */
63
61
  "tool.execute.before": async (input, output) => {
64
62
  switch (input.tool) {
@@ -74,9 +72,19 @@ const AsciiPlugin = async (_ctx, options) => {
74
72
  }
75
73
  break;
76
74
  }
77
- case "patch": {
78
- if (typeof output.args?.patch === "string") {
79
- output.args.patch = substitute(output.args.patch);
75
+ case "multiedit": {
76
+ if (Array.isArray(output.args?.edits)) {
77
+ for (const edit of output.args.edits) {
78
+ if (typeof edit?.newString === "string") {
79
+ edit.newString = substitute(edit.newString);
80
+ }
81
+ }
82
+ }
83
+ break;
84
+ }
85
+ case "apply_patch": {
86
+ if (typeof output.args?.patchText === "string") {
87
+ output.args.patchText = substitute(output.args.patchText);
80
88
  }
81
89
  break;
82
90
  }
@@ -84,4 +92,4 @@ const AsciiPlugin = async (_ctx, options) => {
84
92
  },
85
93
  };
86
94
  };
87
- exports.AsciiPlugin = AsciiPlugin;
95
+ export default AsciiPlugin;
@@ -1,14 +1,8 @@
1
- "use strict";
2
1
  /**
3
2
  * Unicode → ASCII substitution mappings, organised by category.
4
3
  * Each entry is a tuple of [unicode, ascii].
5
4
  */
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 = [
5
+ export const PUNCTUATION = [
12
6
  // Dashes
13
7
  ["\u2014", "--"], // em dash (—)
14
8
  ["\u2013", "-"], // en dash (–)
@@ -39,7 +33,7 @@ exports.PUNCTUATION = [
39
33
  ["\u2032", "'"], // prime (′)
40
34
  ["\u2033", '"'], // double prime (″)
41
35
  ];
42
- exports.ARROWS = [
36
+ export const ARROWS = [
43
37
  ["\u2192", "->"], // rightwards arrow (→)
44
38
  ["\u2190", "<-"], // leftwards arrow (←)
45
39
  ["\u2191", "^"], // upwards arrow (↑)
@@ -58,7 +52,7 @@ exports.ARROWS = [
58
52
  ["\u2B06", "^"], // upwards black arrow (⬆)
59
53
  ["\u2B07", "v"], // downwards black arrow (⬇)
60
54
  ];
61
- exports.MATH = [
55
+ export const MATH = [
62
56
  ["\u2260", "!="], // not equal to (≠)
63
57
  ["\u2264", "<="], // less-than or equal to (≤)
64
58
  ["\u2265", ">="], // greater-than or equal to (≥)
@@ -86,7 +80,7 @@ exports.MATH = [
86
80
  ["\u2234", ":."], // therefore (∴)
87
81
  ["\u2235", ":'"], // because (∵)
88
82
  ];
89
- exports.EMOJIS = [
83
+ export const EMOJIS = [
90
84
  // Checkmarks / cross marks
91
85
  ["\u2713", "[x]"], // check mark (✓)
92
86
  ["\u2714", "[x]"], // heavy check mark (✔)
@@ -102,57 +96,57 @@ exports.EMOJIS = [
102
96
  ["\u2755", "[!]"], // white exclamation mark ornament (❕)
103
97
  ["\u26A1", "[!]"], // high voltage sign (⚡)
104
98
  // Info / ideas
105
- ["\u2139", "[i]"], // information source ()
106
- ["\u1F4A1", "[i]"], // light bulb (💡)
99
+ ["\u2139", "[i]"], // information source ([i])
100
+ ["\u{1F4A1}", "[i]"], // light bulb (💡)
107
101
  // Stars / rating
108
- ["\u2B50", "[*]"], // white medium star ()
109
- ["\u1F31F", "[*]"], // glowing star (🌟)
110
- ["\u2605", "[*]"], // black star ()
111
- ["\u2606", "[*]"], // white star ()
102
+ ["\u2B50", "[*]"], // white medium star ([*])
103
+ ["\u{1F31F}", "[*]"], // glowing star (🌟)
104
+ ["\u2605", "[*]"], // black star ([*])
105
+ ["\u2606", "[*]"], // white star ([*])
112
106
  // 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
107
+ ["\u{1F525}", "[fire]"], // fire (🔥)
108
+ ["\u{1F680}", "[>>]"], // rocket (🚀)
109
+ ["\u{1F41B}", "[bug]"], // bug (🐛)
110
+ ["\u{1F41E}", "[bug]"], // lady beetle (🐞)
111
+ ["\u{1F4DD}", "[note]"], // memo (📝)
112
+ ["\u270F", "[note]"], // pencil ([note])
113
+ ["\u{1F512}", "[lock]"], // lock (🔒)
114
+ ["\u{1F513}", "[open]"], // open lock (🔓)
115
+ ["\u{1F4C1}", "[dir]"], // file folder (📁)
116
+ ["\u{1F4C2}", "[dir]"], // open file folder (📂)
117
+ ["\u{1F4C4}", "[file]"], // page facing up (📄)
118
+ ["\u{1F4C3}", "[file]"], // page with curl (📃)
119
+ ["\u{1F44D}", "[+1]"], // thumbs up (👍)
120
+ ["\u{1F44E}", "[-1]"], // thumbs down (👎)
121
+ ["\u{1F4AC}", "[comment]"], // speech balloon (💬)
122
+ ["\u{1F4E6}", "[pkg]"], // package (📦)
123
+ ["\u{1F517}", "[link]"], // link (🔗)
124
+ ["\u{1F6A7}", "[wip]"], // construction sign (🚧)
125
+ ["\u2699", "[config]"], // gear ([config])
126
+ ["\u{1F527}", "[fix]"], // wrench (🔧)
127
+ ["\u{1F5D1}", "[del]"], // wastebasket (🗑)
128
+ ["\u{1F310}", "[web]"], // globe with meridians (🌐)
129
+ ["\u{1F4BB}", "[pc]"], // personal computer (💻)
130
+ ["\u{1F4F1}", "[phone]"], // mobile phone (📱)
131
+ ["\u{1F4E7}", "[email]"], // e-mail (📧)
132
+ ["\u{1F4CA}", "[chart]"], // bar chart (📊)
133
+ ["\u{1F4C8}", "[up]"], // chart with upwards trend (📈)
134
+ ["\u{1F4C9}", "[down]"], // chart with downwards trend (📉)
135
+ ["\u2764", "[<3]"], // heavy black heart ([<3])
136
+ ["\u{1F4AF}", "[100]"], // hundred points (💯)
137
+ ["\u{1F44B}", "[wave]"], // waving hand (👋)
138
+ ["\u{1F91D}", "[deal]"], // handshake (🤝)
139
+ ["\u{1F4AA}", "[strong]"], // flexed biceps (💪)
140
+ ["\u{1F914}", "[?]"], // thinking face (🤔)
141
+ ["\u{1F644}", "[eye-roll]"], // face with rolling eyes (🙄)
142
+ ["\u{1F4A5}", "[boom]"], // collision (💥)
143
+ ["\u{1F389}", "[party]"], // party popper (🎉)
144
+ ["\u{1F3C6}", "[trophy]"], // trophy (🏆)
145
+ ["\u{1F4B0}", "[money]"], // money bag (💰)
146
+ ["\u231B", "[wait]"], // hourglass ([wait])
147
+ ["\u23F3", "[wait]"], // hourglass with flowing sand ([wait])
148
+ ["\u{1F504}", "[refresh]"], // counterclockwise arrows button (🔄)
149
+ ["\u2705", "[done]"], // white heavy check mark ([done]) -- already above, kept for completeness
156
150
  ];
157
151
  const DEFAULT_CONFIG = {
158
152
  punctuation: true,
@@ -163,24 +157,24 @@ const DEFAULT_CONFIG = {
163
157
  /**
164
158
  * Build a combined substitution map from enabled categories.
165
159
  */
166
- function buildSubstitutions(config = {}) {
160
+ export function buildSubstitutions(config = {}) {
167
161
  const resolved = { ...DEFAULT_CONFIG, ...config };
168
162
  const entries = [];
169
163
  if (resolved.punctuation)
170
- entries.push(...exports.PUNCTUATION);
164
+ entries.push(...PUNCTUATION);
171
165
  if (resolved.arrows)
172
- entries.push(...exports.ARROWS);
166
+ entries.push(...ARROWS);
173
167
  if (resolved.math)
174
- entries.push(...exports.MATH);
168
+ entries.push(...MATH);
175
169
  if (resolved.emojis)
176
- entries.push(...exports.EMOJIS);
170
+ entries.push(...EMOJIS);
177
171
  return entries;
178
172
  }
179
173
  /**
180
174
  * Build a compiled RegExp that matches all active unicode characters at once.
181
175
  * This is much faster than running replace() N times.
182
176
  */
183
- function buildRegex(substitutions) {
177
+ export function buildRegex(substitutions) {
184
178
  const pattern = substitutions
185
179
  .map(([ch]) => ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
186
180
  .join("|");
@@ -189,6 +183,6 @@ function buildRegex(substitutions) {
189
183
  /**
190
184
  * Apply substitutions to a string using a pre-built map and regex.
191
185
  */
192
- function applySubstitutions(text, regex, map) {
186
+ export function applySubstitutions(text, regex, map) {
193
187
  return text.replace(regex, (match) => map.get(match) ?? match);
194
188
  }
package/package.json CHANGED
@@ -1,14 +1,24 @@
1
1
  {
2
2
  "name": "opencode-ascii",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
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",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
7
15
  "files": [
8
16
  "dist"
9
17
  ],
10
18
  "scripts": {
11
19
  "build": "tsc",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
12
22
  "prepublishOnly": "npm run build"
13
23
  },
14
24
  "peerDependencies": {
@@ -16,7 +26,8 @@
16
26
  },
17
27
  "devDependencies": {
18
28
  "@opencode-ai/plugin": "latest",
19
- "typescript": "^5"
29
+ "typescript": "^5",
30
+ "vitest": "^4.1.2"
20
31
  },
21
32
  "keywords": [
22
33
  "opencode",