pi-ui-extend 0.1.9 → 0.1.13

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 (121) hide show
  1. package/README.md +23 -2
  2. package/dist/app/app.d.ts +4 -0
  3. package/dist/app/app.js +76 -7
  4. package/dist/app/cli/install.d.ts +16 -0
  5. package/dist/app/cli/install.js +34 -7
  6. package/dist/app/cli/startup-info.js +5 -2
  7. package/dist/app/cli/update.d.ts +7 -0
  8. package/dist/app/cli/update.js +11 -3
  9. package/dist/app/commands/command-controller.js +4 -0
  10. package/dist/app/commands/command-host.d.ts +4 -0
  11. package/dist/app/commands/command-model-actions.d.ts +5 -0
  12. package/dist/app/commands/command-model-actions.js +104 -0
  13. package/dist/app/commands/command-navigation-actions.d.ts +6 -1
  14. package/dist/app/commands/command-navigation-actions.js +37 -14
  15. package/dist/app/commands/command-registry.d.ts +4 -0
  16. package/dist/app/commands/command-registry.js +32 -0
  17. package/dist/app/commands/command-session-actions.d.ts +1 -0
  18. package/dist/app/commands/command-session-actions.js +15 -5
  19. package/dist/app/commands/shell-command.d.ts +7 -0
  20. package/dist/app/commands/shell-command.js +12 -4
  21. package/dist/app/commands/shell-controller.d.ts +1 -0
  22. package/dist/app/commands/shell-controller.js +1 -1
  23. package/dist/app/constants.d.ts +1 -1
  24. package/dist/app/constants.js +1 -1
  25. package/dist/app/icons.d.ts +1 -0
  26. package/dist/app/icons.js +3 -1
  27. package/dist/app/input/autocomplete-controller.d.ts +52 -0
  28. package/dist/app/input/autocomplete-controller.js +352 -0
  29. package/dist/app/input/input-action-controller.d.ts +1 -0
  30. package/dist/app/input/input-action-controller.js +21 -0
  31. package/dist/app/input/input-controller.d.ts +1 -0
  32. package/dist/app/input/input-controller.js +2 -0
  33. package/dist/app/input/input-paste-handler.d.ts +1 -0
  34. package/dist/app/input/input-paste-handler.js +22 -18
  35. package/dist/app/input/prompt-enhancer-controller.d.ts +7 -1
  36. package/dist/app/input/prompt-enhancer-controller.js +12 -3
  37. package/dist/app/input/voice-controller.d.ts +51 -1
  38. package/dist/app/input/voice-controller.js +42 -19
  39. package/dist/app/model/model-usage-status.d.ts +9 -0
  40. package/dist/app/model/model-usage-status.js +124 -34
  41. package/dist/app/popup/popup-action-controller.js +1 -1
  42. package/dist/app/process.d.ts +17 -0
  43. package/dist/app/process.js +68 -0
  44. package/dist/app/rendering/conversation-entry-renderer.js +8 -6
  45. package/dist/app/rendering/conversation-tool-renderer.js +3 -2
  46. package/dist/app/rendering/editor-layout-renderer.d.ts +1 -0
  47. package/dist/app/rendering/editor-layout-renderer.js +11 -1
  48. package/dist/app/rendering/message-content.js +65 -7
  49. package/dist/app/rendering/render-controller.js +6 -1
  50. package/dist/app/rendering/render-text.d.ts +3 -0
  51. package/dist/app/rendering/render-text.js +51 -3
  52. package/dist/app/rendering/status-line-renderer.d.ts +5 -1
  53. package/dist/app/rendering/status-line-renderer.js +61 -25
  54. package/dist/app/rendering/toast-renderer.js +10 -13
  55. package/dist/app/rendering/tool-block-renderer.d.ts +1 -0
  56. package/dist/app/rendering/tool-block-renderer.js +16 -33
  57. package/dist/app/runtime.d.ts +6 -1
  58. package/dist/app/runtime.js +35 -2
  59. package/dist/app/screen/clipboard.d.ts +11 -2
  60. package/dist/app/screen/clipboard.js +29 -21
  61. package/dist/app/screen/file-link-opener.d.ts +8 -0
  62. package/dist/app/screen/file-link-opener.js +11 -3
  63. package/dist/app/screen/file-links.js +3 -3
  64. package/dist/app/screen/image-opener.d.ts +12 -0
  65. package/dist/app/screen/image-opener.js +13 -5
  66. package/dist/app/screen/mouse-controller.d.ts +5 -2
  67. package/dist/app/screen/mouse-controller.js +16 -1
  68. package/dist/app/screen/screen-styler.d.ts +4 -1
  69. package/dist/app/screen/screen-styler.js +3 -2
  70. package/dist/app/screen/status-controller.d.ts +3 -0
  71. package/dist/app/screen/status-controller.js +23 -8
  72. package/dist/app/session/queued-message-controller.d.ts +7 -1
  73. package/dist/app/session/queued-message-controller.js +36 -21
  74. package/dist/app/session/resume-session-loader.d.ts +15 -0
  75. package/dist/app/session/resume-session-loader.js +204 -0
  76. package/dist/app/session/session-event-controller.d.ts +5 -1
  77. package/dist/app/session/session-event-controller.js +72 -5
  78. package/dist/app/session/session-history.js +4 -3
  79. package/dist/app/session/session-lifecycle-controller.d.ts +5 -0
  80. package/dist/app/session/session-lifecycle-controller.js +9 -1
  81. package/dist/app/session/tabs-controller.d.ts +10 -1
  82. package/dist/app/session/tabs-controller.js +101 -5
  83. package/dist/app/terminal/nerd-font-controller.d.ts +16 -0
  84. package/dist/app/terminal/nerd-font-controller.js +30 -23
  85. package/dist/app/terminal/terminal-controller.d.ts +1 -0
  86. package/dist/app/terminal/terminal-controller.js +1 -0
  87. package/dist/app/types.d.ts +14 -0
  88. package/dist/app/workspace/workspace-actions-controller.d.ts +1 -1
  89. package/dist/app/workspace/workspace-actions-controller.js +3 -3
  90. package/dist/app/workspace/workspace-undo.d.ts +1 -1
  91. package/dist/app/workspace/workspace-undo.js +22 -20
  92. package/dist/config.d.ts +27 -0
  93. package/dist/config.js +174 -1
  94. package/dist/default-pix-config.js +39 -353
  95. package/dist/input-editor.d.ts +7 -1
  96. package/dist/input-editor.js +47 -6
  97. package/dist/markdown-format.d.ts +1 -0
  98. package/dist/markdown-format.js +26 -1
  99. package/dist/schemas/index.d.ts +5 -0
  100. package/dist/schemas/index.js +5 -0
  101. package/dist/schemas/pi-tools-suite-schema.d.ts +177 -0
  102. package/dist/schemas/pi-tools-suite-schema.js +218 -0
  103. package/dist/schemas/pix-schema.d.ts +65 -0
  104. package/dist/schemas/pix-schema.js +91 -0
  105. package/dist/terminal-width.js +73 -56
  106. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +3 -0
  107. package/external/pi-tools-suite/src/dcp/compression-blocks.ts +1 -0
  108. package/external/pi-tools-suite/src/dcp/prompts.ts +1 -0
  109. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +46 -195
  110. package/external/pi-tools-suite/src/lib/lsp.ts +2 -1
  111. package/external/pi-tools-suite/src/lsp/_shared/output.ts +8 -7
  112. package/external/pi-tools-suite/src/lsp/manager.ts +4 -4
  113. package/external/pi-tools-suite/src/repo-discovery/index.ts +49 -2
  114. package/external/pi-tools-suite/src/todo/index.ts +4 -2
  115. package/external/pi-tools-suite/src/todo/state/selectors.ts +4 -0
  116. package/external/pi-tools-suite/src/todo/todo.ts +2 -6
  117. package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +9 -1
  118. package/external/pi-tools-suite/src/tool-descriptions.ts +1 -1
  119. package/package.json +12 -3
  120. package/schemas/pi-tools-suite.json +881 -0
  121. package/schemas/pix.json +298 -0
@@ -1,46 +1,37 @@
1
1
  const TAB_WIDTH = 4;
2
2
  const ANSI_RESET = "\x1b[0m";
3
+ const EMOJI_PRESENTATION_REGEX = /\p{Emoji_Presentation}/u;
4
+ const EMOJI_REGEX = /\p{Emoji}/u;
5
+ const GRAPHEME_SEGMENTER = typeof Intl.Segmenter === "function" ? new Intl.Segmenter(undefined, { granularity: "grapheme" }) : undefined;
3
6
  export function expandTabs(text, tabWidth = TAB_WIDTH) {
4
7
  let result = "";
5
8
  let column = 0;
6
- for (let index = 0; index < text.length;) {
7
- const ansiLength = ansiSequenceLength(text, index);
8
- if (ansiLength > 0) {
9
- result += text.slice(index, index + ansiLength);
10
- index += ansiLength;
9
+ for (const cluster of displayClusters(text)) {
10
+ if (cluster.ansi) {
11
+ result += cluster.text;
11
12
  continue;
12
13
  }
13
- const codePoint = text.codePointAt(index) ?? 0;
14
- const char = String.fromCodePoint(codePoint);
15
- index += codePointLength(codePoint);
16
- if (char === "\n") {
17
- result += char;
14
+ if (cluster.text === "\n") {
15
+ result += cluster.text;
18
16
  column = 0;
19
17
  continue;
20
18
  }
21
- if (char === "\t") {
19
+ if (cluster.text === "\t") {
22
20
  const spaces = tabWidth - (column % tabWidth || tabWidth);
23
21
  const count = spaces === 0 ? tabWidth : spaces;
24
22
  result += " ".repeat(count);
25
23
  column += count;
26
24
  continue;
27
25
  }
28
- result += char;
29
- column += charDisplayWidth(char.codePointAt(0) ?? 0);
26
+ result += cluster.text;
27
+ column += cluster.width;
30
28
  }
31
29
  return result;
32
30
  }
33
31
  export function stringDisplayWidth(text) {
34
32
  let width = 0;
35
- for (let index = 0; index < text.length;) {
36
- const ansiLength = ansiSequenceLength(text, index);
37
- if (ansiLength > 0) {
38
- index += ansiLength;
39
- continue;
40
- }
41
- const codePoint = text.codePointAt(index) ?? 0;
42
- width += charDisplayWidth(codePoint);
43
- index += codePointLength(codePoint);
33
+ for (const cluster of displayClusters(text)) {
34
+ width += cluster.width;
44
35
  }
45
36
  return width;
46
37
  }
@@ -50,24 +41,18 @@ export function sliceByDisplayWidth(text, width) {
50
41
  let used = 0;
51
42
  let sawAnsi = false;
52
43
  let clipped = false;
53
- for (let index = 0; index < text.length;) {
54
- const ansiLength = ansiSequenceLength(text, index);
55
- if (ansiLength > 0) {
56
- result += text.slice(index, index + ansiLength);
44
+ for (const cluster of displayClusters(text)) {
45
+ if (cluster.ansi) {
46
+ result += cluster.text;
57
47
  sawAnsi = true;
58
- index += ansiLength;
59
48
  continue;
60
49
  }
61
- const codePoint = text.codePointAt(index) ?? 0;
62
- const char = String.fromCodePoint(codePoint);
63
- const charWidth = charDisplayWidth(codePoint);
64
- if (used + charWidth > safeWidth) {
50
+ if (used + cluster.width > safeWidth) {
65
51
  clipped = true;
66
52
  break;
67
53
  }
68
- result += char;
69
- used += charWidth;
70
- index += codePointLength(codePoint);
54
+ result += cluster.text;
55
+ used += cluster.width;
71
56
  }
72
57
  if (sawAnsi && clipped && !result.endsWith(ANSI_RESET))
73
58
  return `${result}${ANSI_RESET}`;
@@ -83,24 +68,18 @@ export function wrapDisplayLine(text, width) {
83
68
  const chunks = [];
84
69
  let chunk = "";
85
70
  let chunkWidth = 0;
86
- for (let index = 0; index < text.length;) {
87
- const ansiLength = ansiSequenceLength(text, index);
88
- if (ansiLength > 0) {
89
- chunk += text.slice(index, index + ansiLength);
90
- index += ansiLength;
71
+ for (const cluster of displayClusters(text)) {
72
+ if (cluster.ansi) {
73
+ chunk += cluster.text;
91
74
  continue;
92
75
  }
93
- const codePoint = text.codePointAt(index) ?? 0;
94
- const char = String.fromCodePoint(codePoint);
95
- const charWidth = charDisplayWidth(codePoint);
96
- if (chunk && chunkWidth + charWidth > safeWidth) {
76
+ if (chunk && chunkWidth + cluster.width > safeWidth) {
97
77
  chunks.push(chunk);
98
78
  chunk = "";
99
79
  chunkWidth = 0;
100
80
  }
101
- chunk += char;
102
- chunkWidth += charWidth;
103
- index += codePointLength(codePoint);
81
+ chunk += cluster.text;
82
+ chunkWidth += cluster.width;
104
83
  }
105
84
  chunks.push(chunk);
106
85
  return chunks;
@@ -146,23 +125,18 @@ function displayTokens(text) {
146
125
  const tokens = [];
147
126
  let current = "";
148
127
  let currentWhitespace;
149
- for (let index = 0; index < text.length;) {
150
- const ansiLength = ansiSequenceLength(text, index);
151
- if (ansiLength > 0) {
152
- current += text.slice(index, index + ansiLength);
153
- index += ansiLength;
128
+ for (const cluster of displayClusters(text)) {
129
+ if (cluster.ansi) {
130
+ current += cluster.text;
154
131
  continue;
155
132
  }
156
- const codePoint = text.codePointAt(index) ?? 0;
157
- const char = String.fromCodePoint(codePoint);
158
- const whitespace = /\s/u.test(char);
133
+ const whitespace = /^\s+$/u.test(cluster.text);
159
134
  if (current && currentWhitespace !== whitespace) {
160
135
  tokens.push({ text: current, whitespace: currentWhitespace ?? false });
161
136
  current = "";
162
137
  }
163
- current += char;
138
+ current += cluster.text;
164
139
  currentWhitespace = whitespace;
165
- index += codePointLength(codePoint);
166
140
  }
167
141
  if (current)
168
142
  tokens.push({ text: current, whitespace: currentWhitespace ?? false });
@@ -204,6 +178,49 @@ function ansiSequenceLength(text, index) {
204
178
  }
205
179
  return 2;
206
180
  }
181
+ function* displayClusters(text) {
182
+ for (let index = 0; index < text.length;) {
183
+ const ansiLength = ansiSequenceLength(text, index);
184
+ if (ansiLength > 0) {
185
+ const cluster = text.slice(index, index + ansiLength);
186
+ yield { text: cluster, width: 0, ansi: true };
187
+ index += ansiLength;
188
+ continue;
189
+ }
190
+ const nextAnsiIndex = text.indexOf("\x1b", index + 1);
191
+ const textEnd = nextAnsiIndex === -1 ? text.length : nextAnsiIndex;
192
+ const segment = text.slice(index, textEnd);
193
+ if (GRAPHEME_SEGMENTER) {
194
+ for (const { segment: cluster } of GRAPHEME_SEGMENTER.segment(segment)) {
195
+ yield { text: cluster, width: graphemeDisplayWidth(cluster), ansi: false };
196
+ }
197
+ index = textEnd;
198
+ continue;
199
+ }
200
+ while (index < textEnd) {
201
+ const codePoint = text.codePointAt(index) ?? 0;
202
+ const cluster = String.fromCodePoint(codePoint);
203
+ yield { text: cluster, width: graphemeDisplayWidth(cluster), ansi: false };
204
+ index += codePointLength(codePoint);
205
+ }
206
+ }
207
+ }
208
+ function graphemeDisplayWidth(text) {
209
+ if (!text)
210
+ return 0;
211
+ if (isEmojiGrapheme(text))
212
+ return 2;
213
+ let width = 0;
214
+ for (let index = 0; index < text.length;) {
215
+ const codePoint = text.codePointAt(index) ?? 0;
216
+ width += charDisplayWidth(codePoint);
217
+ index += codePointLength(codePoint);
218
+ }
219
+ return width;
220
+ }
221
+ function isEmojiGrapheme(text) {
222
+ return EMOJI_PRESENTATION_REGEX.test(text) || (EMOJI_REGEX.test(text) && (text.includes("\ufe0f") || text.includes("\u20e3")));
223
+ }
207
224
  function codePointLength(codePoint) {
208
225
  return codePoint > 0xffff ? 2 : 1;
209
226
  }
@@ -1,4 +1,7 @@
1
1
  {
2
+ // This sample is the value for `asyncSubagents` in ~/.config/pi/pi-tools-suite.jsonc.
3
+ // Full config schema: https://unpkg.com/pi-ui-extend/schemas/pi-tools-suite.json
4
+
2
5
  // Global async-subagents routing.
3
6
  // Rule of thumb:
4
7
  // - GLM: cheap/fast discovery, grep-style scanning, docs/test inventory.
@@ -86,6 +86,7 @@ export function formatCompressionIdDiagnostics(state: DcpState): string {
86
86
  `Current raw message IDs: ${compactList(rawIds)}.`,
87
87
  `Current active block IDs: ${compactList(blockIds)}.`,
88
88
  "Use only IDs from the latest visible context. If a raw range was already compressed, use the corresponding bN block ID instead of stale mNNN IDs.",
89
+ "Retry at most once with a safe closed range from those IDs, or skip compression if none is safe.",
89
90
  ].join("\n")
90
91
  }
91
92
 
@@ -157,6 +157,7 @@ Rules:
157
157
  - IDs must exist in the current conversation context.
158
158
  - \`startId\` must appear before \`endId\`.
159
159
  - Do not invent IDs. Use only IDs that are present in context.
160
+ - If \`compress\` fails with \`Unknown message ID\`, treat those IDs as stale; retry at most once using only the error's listed current raw IDs or eligible \`bN\` blocks, or skip if no closed range is safe.
160
161
 
161
162
  BATCHING
162
163
  When multiple independent ranges or individual messages are ready, include all of them in one \`compress\` call. Range entries must not overlap. Message entries should each summarize exactly one raw message.`
@@ -1,103 +1,37 @@
1
1
  // Default user config written when pi-tools-suite.jsonc is missing.
2
- export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = `{
3
- // Disable individual pi-tools-suite modules by uncommenting entries below.
4
- // Later project config in .pi/pi-tools-suite.jsonc can re-enable modules.
2
+ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
3
+ "$schema": "https://unpkg.com/pi-ui-extend/schemas/pi-tools-suite.json",
5
4
  "disabledModules": [
6
5
  // "ast-grep",
7
- // "async-subagents",
8
- // "lsp",
9
- // "repo-discovery",
10
- // "antigravity-auth",
11
- // "opencode-import",
12
- // "todo",
13
- // "model-tools",
14
- // "usage",
15
- // "web-search",
16
6
  // "dcp"
17
7
  ],
18
-
19
- // Renderer bundled terminal-bell notification settings.
20
- "terminalBell": {
21
- // Toggle terminal-bell attention signals across supported OSes:
22
- // terminal BEL, macOS notification sound, and system notifications
23
- // (macOS terminal-notifier/osascript or Linux notify-send when available).
24
- "sound": true
25
- },
26
-
27
- // Dynamic Context Pruning (DCP) module config.
28
- // The DCP module owns the compress tool and /dcp commands.
8
+ "terminalBell": { "sound": true },
29
9
  "dcp": {
30
10
  "enabled": true,
31
- "manualMode": {
32
- "enabled": false,
33
- "automaticStrategies": true
34
- },
35
- "compress": {
36
- "minContextPercent": 0.25,
37
- "nudgeFrequency": 1,
38
- "iterationNudgeThreshold": 8
39
- }
11
+ "manualMode": { "enabled": false, "automaticStrategies": true },
12
+ "compress": { "minContextPercent": 0.25, "nudgeFrequency": 1, "iterationNudgeThreshold": 8 }
40
13
  },
41
-
42
14
  "asyncSubagents": {
43
15
  "defaultType": "quick",
44
- "routing": {
45
- "enabled": true,
46
- "model": "zai/glm-4.5-air",
47
- "maxTaskChars": 1200,
48
- "maxTokens": 512,
49
- "maxRetries": 1,
50
- "temperature": 0,
51
- "timeoutMs": 12000,
52
- "debug": false
53
- },
16
+ "routing": { "enabled": true, "model": "zai/glm-4.5-air", "maxTaskChars": 1200, "maxTokens": 512, "maxRetries": 1, "temperature": 0, "timeoutMs": 12000, "debug": false },
54
17
  "presets": {
55
18
  "cheap": {
56
19
  "description": "Use cheap GLM/Gemini Flash models for text/code roles; keep vision on the enabled GPT vision model.",
57
20
  "types": {
58
- "quick": {
59
- "model": "zai/glm-4.5-air",
60
- "thinking": "off"
61
- },
62
- "scan": {
63
- "model": "zai/glm-4.5-air",
64
- "thinking": "off"
65
- },
66
- "research": {
67
- "model": "zai/glm-5-turbo",
68
- "thinking": "low"
69
- },
70
- "docs": {
71
- "model": "zai/glm-4.5-air",
72
- "thinking": "low"
73
- },
21
+ "quick": { "model": "zai/glm-4.5-air", "thinking": "off" },
22
+ "scan": { "model": "zai/glm-4.5-air", "thinking": "off" },
23
+ "research": { "model": "zai/glm-5-turbo", "thinking": "low" },
24
+ "docs": { "model": "zai/glm-4.5-air", "thinking": "low" },
74
25
  "frontend": {
75
26
  "model": "antigravity/gemini-3-flash-preview",
76
- "fallbackModels": [
77
- "zai/glm-5.1"
78
- ],
27
+ "fallbackModels": ["zai/glm-5.1"],
79
28
  "thinking": "medium"
80
29
  },
81
- "tests": {
82
- "model": "zai/glm-5-turbo",
83
- "thinking": "medium"
84
- },
85
- "review": {
86
- "model": "zai/glm-5.1",
87
- "thinking": "high"
88
- },
89
- "implement": {
90
- "model": "zai/glm-5.1",
91
- "thinking": "high"
92
- },
93
- "deep": {
94
- "model": "zai/glm-5.1",
95
- "thinking": "high"
96
- },
97
- "vision": {
98
- "model": "openai-codex/gpt-5.4-mini",
99
- "thinking": "off"
100
- }
30
+ "tests": { "model": "zai/glm-5-turbo", "thinking": "medium" },
31
+ "review": { "model": "zai/glm-5.1", "thinking": "high" },
32
+ "implement": { "model": "zai/glm-5.1", "thinking": "high" },
33
+ "deep": { "model": "zai/glm-5.1", "thinking": "high" },
34
+ "vision": { "model": "openai-codex/gpt-5.4-mini", "thinking": "off" }
101
35
  }
102
36
  },
103
37
  "gpt": {
@@ -105,71 +39,50 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = `{
105
39
  "types": {
106
40
  "quick": {
107
41
  "model": "openai-codex/gpt-5.3-codex-spark",
108
- "fallbackModels": [
109
- "zai/glm-4.5-air"
110
- ],
42
+ "fallbackModels": ["zai/glm-4.5-air"],
111
43
  "thinking": "off"
112
44
  },
113
45
  "scan": {
114
46
  "model": "openai-codex/gpt-5.3-codex-spark",
115
- "fallbackModels": [
116
- "zai/glm-4.5-air"
117
- ],
47
+ "fallbackModels": ["zai/glm-4.5-air"],
118
48
  "thinking": "off"
119
49
  },
120
50
  "research": {
121
51
  "model": "openai-codex/gpt-5.4-mini",
122
- "fallbackModels": [
123
- "zai/glm-5-turbo"
124
- ],
52
+ "fallbackModels": ["zai/glm-5-turbo"],
125
53
  "thinking": "low"
126
54
  },
127
55
  "docs": {
128
56
  "model": "openai-codex/gpt-5.3-codex-spark",
129
- "fallbackModels": [
130
- "zai/glm-4.5-air"
131
- ],
57
+ "fallbackModels": ["zai/glm-4.5-air"],
132
58
  "thinking": "low"
133
59
  },
134
60
  "frontend": {
135
61
  "model": "antigravity/gemini-3-flash-preview",
136
- "fallbackModels": [
137
- "zai/glm-5.1"
138
- ],
62
+ "fallbackModels": ["zai/glm-5.1"],
139
63
  "thinking": "medium"
140
64
  },
141
65
  "tests": {
142
66
  "model": "openai-codex/gpt-5.4-mini",
143
- "fallbackModels": [
144
- "zai/glm-5-turbo"
145
- ],
67
+ "fallbackModels": ["zai/glm-5-turbo"],
146
68
  "thinking": "medium"
147
69
  },
148
70
  "review": {
149
71
  "model": "openai-codex/gpt-5.5",
150
- "fallbackModels": [
151
- "zai/glm-5.1"
152
- ],
72
+ "fallbackModels": ["zai/glm-5.1"],
153
73
  "thinking": "high"
154
74
  },
155
75
  "implement": {
156
76
  "model": "openai-codex/gpt-5.5",
157
- "fallbackModels": [
158
- "zai/glm-5.1"
159
- ],
77
+ "fallbackModels": ["zai/glm-5.1"],
160
78
  "thinking": "high"
161
79
  },
162
80
  "deep": {
163
81
  "model": "openai-codex/gpt-5.5",
164
- "fallbackModels": [
165
- "zai/glm-5.1"
166
- ],
82
+ "fallbackModels": ["zai/glm-5.1"],
167
83
  "thinking": "high"
168
84
  },
169
- "vision": {
170
- "model": "openai-codex/gpt-5.4-mini",
171
- "thinking": "off"
172
- }
85
+ "vision": { "model": "openai-codex/gpt-5.4-mini", "thinking": "off" }
173
86
  }
174
87
  },
175
88
  "deep": {
@@ -177,77 +90,50 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = `{
177
90
  "types": {
178
91
  "quick": {
179
92
  "model": "openai-codex/gpt-5.4-mini",
180
- "fallbackModels": [
181
- "zai/glm-4.5-air"
182
- ],
93
+ "fallbackModels": ["zai/glm-4.5-air"],
183
94
  "thinking": "low"
184
95
  },
185
96
  "scan": {
186
97
  "model": "antigravity/gemini-3-flash-preview",
187
- "fallbackModels": [
188
- "openai-codex/gpt-5.3-codex-spark",
189
- "zai/glm-4.5-air"
190
- ],
98
+ "fallbackModels": ["openai-codex/gpt-5.3-codex-spark", "zai/glm-4.5-air"],
191
99
  "thinking": "off"
192
100
  },
193
101
  "research": {
194
102
  "model": "antigravity/gemini-3.1-pro-preview",
195
- "fallbackModels": [
196
- "openai-codex/gpt-5.4-mini",
197
- "zai/glm-5-turbo"
198
- ],
103
+ "fallbackModels": ["openai-codex/gpt-5.4-mini", "zai/glm-5-turbo"],
199
104
  "thinking": "medium"
200
105
  },
201
106
  "docs": {
202
107
  "model": "antigravity/gemini-2.5-flash",
203
- "fallbackModels": [
204
- "openai-codex/gpt-5.3-codex-spark",
205
- "zai/glm-4.5-air"
206
- ],
108
+ "fallbackModels": ["openai-codex/gpt-5.3-codex-spark", "zai/glm-4.5-air"],
207
109
  "thinking": "medium"
208
110
  },
209
111
  "frontend": {
210
112
  "model": "antigravity/gemini-3.1-pro-preview-customtools",
211
- "fallbackModels": [
212
- "zai/glm-5.1"
213
- ],
113
+ "fallbackModels": ["zai/glm-5.1"],
214
114
  "thinking": "low"
215
115
  },
216
116
  "tests": {
217
117
  "model": "antigravity/antigravity-claude-sonnet-4-6",
218
- "fallbackModels": [
219
- "openai-codex/gpt-5.4-mini",
220
- "zai/glm-5-turbo"
221
- ],
118
+ "fallbackModels": ["openai-codex/gpt-5.4-mini", "zai/glm-5-turbo"],
222
119
  "thinking": "high"
223
120
  },
224
121
  "review": {
225
122
  "model": "antigravity/antigravity-claude-sonnet-4-6",
226
- "fallbackModels": [
227
- "openai-codex/gpt-5.5",
228
- "zai/glm-5.1"
229
- ],
123
+ "fallbackModels": ["openai-codex/gpt-5.5", "zai/glm-5.1"],
230
124
  "thinking": "high"
231
125
  },
232
126
  "implement": {
233
127
  "model": "openai-codex/gpt-5.5",
234
- "fallbackModels": [
235
- "zai/glm-5.1"
236
- ],
128
+ "fallbackModels": ["zai/glm-5.1"],
237
129
  "thinking": "high"
238
130
  },
239
131
  "deep": {
240
132
  "model": "antigravity/antigravity-claude-opus-4-6-thinking",
241
- "fallbackModels": [
242
- "openai-codex/gpt-5.5",
243
- "zai/glm-5.1"
244
- ],
133
+ "fallbackModels": ["openai-codex/gpt-5.5", "zai/glm-5.1"],
245
134
  "thinking": "high"
246
135
  },
247
- "vision": {
248
- "model": "openai-codex/gpt-5.4-mini",
249
- "thinking": "off"
250
- }
136
+ "vision": { "model": "openai-codex/gpt-5.4-mini", "thinking": "off" }
251
137
  }
252
138
  }
253
139
  },
@@ -261,32 +147,19 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = `{
261
147
  "description": "Use for finding files, symbols, text, or inventory across a repo. Return paths/facts; do not judge code quality.",
262
148
  "model": "zai/glm-5-turbo",
263
149
  "thinking": "off",
264
- "tools": [
265
- "read",
266
- "grep"
267
- ]
150
+ "tools": ["read", "grep"]
268
151
  },
269
152
  "research": {
270
153
  "description": "Use for multi-file codebase research: read several files and explain how something works. No edits.",
271
154
  "model": "zai/glm-5-turbo",
272
155
  "thinking": "low",
273
- "tools": [
274
- "read",
275
- "grep"
276
- ]
277
- },
278
- "docs": {
279
- "description": "Use for documentation work: README/API docs review, docs gaps, changelog, migration notes, examples.",
280
- "model": "zai/glm-5-turbo",
281
- "thinking": "low"
156
+ "tools": ["read", "grep"]
282
157
  },
158
+ "docs": { "description": "Use for documentation work: README/API docs review, docs gaps, changelog, migration notes, examples.", "model": "zai/glm-5-turbo", "thinking": "low" },
283
159
  "frontend": {
284
160
  "description": "Use for frontend UI/UX visual work: styling, layout, typography, animation, responsive states, component polish, accessibility. Avoid backend/business logic unless needed for UI behavior.",
285
161
  "model": "antigravity/gemini-3-flash-preview",
286
- "fallbackModels": [
287
- "openai-codex/gpt-5.4-mini",
288
- "zai/glm-5.1"
289
- ],
162
+ "fallbackModels": ["openai-codex/gpt-5.4-mini", "zai/glm-5.1"],
290
163
  "thinking": "medium",
291
164
  "promptAppend": [
292
165
  "Act as a frontend UI/UX engineer for visual and product-facing work.",
@@ -299,38 +172,25 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = `{
299
172
  "description": "Use for tests: locate coverage, find gaps, run/check targeted test commands, diagnose failing tests.",
300
173
  "model": "zai/glm-5-turbo",
301
174
  "thinking": "medium",
302
- "tools": [
303
- "read",
304
- "grep",
305
- "bash"
306
- ]
175
+ "tools": ["read", "grep", "bash"]
307
176
  },
308
177
  "review": {
309
178
  "description": "Use for review/audit of existing code or changes: correctness, security, performance, maintainability, API risks, quality. Do not implement new code.",
310
179
  "model": "openai-codex/gpt-5.5",
311
- "fallbackModels": [
312
- "zai/glm-5.1"
313
- ],
180
+ "fallbackModels": ["zai/glm-5.1"],
314
181
  "thinking": "high",
315
- "tools": [
316
- "read",
317
- "grep"
318
- ]
182
+ "tools": ["read", "grep"]
319
183
  },
320
184
  "implement": {
321
185
  "description": "Use when the sub-agent should make or plan code changes for a feature, bug fix, or refactor.",
322
186
  "model": "openai-codex/gpt-5.5",
323
- "fallbackModels": [
324
- "zai/glm-5.1"
325
- ],
187
+ "fallbackModels": ["zai/glm-5.1"],
326
188
  "thinking": "high"
327
189
  },
328
190
  "deep": {
329
191
  "description": "Use for broad hard reasoning: architecture, system design, cross-module impact, complex tradeoffs.",
330
192
  "model": "openai-codex/gpt-5.5",
331
- "fallbackModels": [
332
- "zai/glm-5.1"
333
- ],
193
+ "fallbackModels": ["zai/glm-5.1"],
334
194
  "thinking": "high"
335
195
  },
336
196
  "vision": {
@@ -346,21 +206,12 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = `{
346
206
  }
347
207
  }
348
208
  },
349
-
350
- // Compact tool-renderer preview policy and name colors by tool name.
351
- // direction=head keeps the first lines; direction=tail keeps the last lines.
352
- // color accepts a Pi theme color name or a #rrggbb RGB literal. Tool entries
353
- // override default and can also set compactHidden.
354
209
  "toolRenderer": {},
355
-
356
210
  "promptCommands": {
357
211
  "commands": {
358
- "commit": {
359
- "prompt": "Commit all changes"
360
- }
212
+ "commit": { "prompt": "Commit all changes" }
361
213
  }
362
214
  },
363
-
364
215
  // LSP language servers. TypeScript/JavaScript is enabled by default;
365
216
  // uncomment additional server blocks after installing their binaries.
366
217
  "lsp": {
@@ -1,6 +1,7 @@
1
1
  import type { AgentToolResult, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import { existsSync } from "node:fs";
3
3
  import { toAbsolutePath } from "../lsp/_shared/paths.js";
4
+ import { LSP_DIAGNOSTIC_ICON } from "../lsp/_shared/output.js";
4
5
  import { getGlobalLspManager } from "../lsp/manager.js";
5
6
  import { getEventPaths, isMutationToolResult } from "../lsp/mutation-events.js";
6
7
 
@@ -52,7 +53,7 @@ export async function appendLspDiagnosticsToMutationResult<T extends LspEnrichab
52
53
  if (isMissingFileError(error)) return options.result;
53
54
  return {
54
55
  ...options.result,
55
- content: [...options.result.content, textContent(`LSP diagnostics:\n\n⚠️ lsp: ${(error as Error).message}`)],
56
+ content: [...options.result.content, textContent(`LSP diagnostics:\n\n${LSP_DIAGNOSTIC_ICON} lsp: ${(error as Error).message}`)],
56
57
  };
57
58
  }
58
59
  }