xtrm-tools 0.7.12 → 0.7.14

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 (42) hide show
  1. package/.xtrm/config/hooks.json +10 -0
  2. package/.xtrm/hooks/specialists/specialists-memory-cache-sync.mjs +57 -0
  3. package/.xtrm/hooks/specialists-agent-guard.mjs +76 -0
  4. package/.xtrm/registry.json +509 -393
  5. package/.xtrm/skills/default/premortem/SKILL.md +218 -0
  6. package/.xtrm/skills/default/releasing/SKILL.md +94 -0
  7. package/.xtrm/skills/default/releasing/scripts/xt-reports.ts +18 -0
  8. package/.xtrm/skills/default/session-close-report/SKILL.md +85 -17
  9. package/.xtrm/skills/default/specialists-creator/SKILL.md +117 -42
  10. package/.xtrm/skills/default/specialists-creator/scripts/audit-spec-uniformity.mjs +86 -0
  11. package/.xtrm/skills/default/specialists-creator/scripts/scaffold-specialist.ts +223 -0
  12. package/.xtrm/skills/default/specialists-creator/scripts/validate-specialist.ts +1 -1
  13. package/.xtrm/skills/default/sync-docs/SKILL.md +88 -208
  14. package/.xtrm/skills/default/sync-docs/scripts/pre-context.sh +17 -0
  15. package/.xtrm/skills/default/update-specialists/SKILL.md +99 -201
  16. package/.xtrm/skills/default/update-xt/SKILL.md +34 -0
  17. package/.xtrm/skills/default/using-kpi/SKILL.md +150 -0
  18. package/.xtrm/skills/default/using-nodes/SKILL.md +18 -102
  19. package/.xtrm/skills/default/using-script-specialists/SKILL.md +208 -0
  20. package/.xtrm/skills/default/using-specialists/SKILL.md +13 -0
  21. package/.xtrm/skills/default/using-specialists-v2/SKILL.md +773 -0
  22. package/.xtrm/skills/default/using-specialists-v3/SKILL.md +284 -0
  23. package/.xtrm/skills/default/using-specialists-v3/evals/evals.json +89 -0
  24. package/CHANGELOG.md +17 -0
  25. package/README.md +5 -1
  26. package/cli/dist/index.cjs +3401 -627
  27. package/cli/dist/index.cjs.map +1 -1
  28. package/cli/package.json +1 -1
  29. package/package.json +3 -2
  30. package/packages/pi-extensions/.serena/project.yml +130 -0
  31. package/packages/pi-extensions/extensions/pi-serena-compact/index.ts +4 -12
  32. package/packages/pi-extensions/extensions/xtrm-loader/index.ts +0 -1
  33. package/packages/pi-extensions/extensions/xtrm-ui/index.ts +201 -36
  34. package/packages/pi-extensions/extensions/xtrm-ui/themes/pidex-dark-flattools.json +79 -0
  35. package/packages/pi-extensions/extensions/xtrm-ui/themes/pidex-dark.json +85 -0
  36. package/packages/pi-extensions/extensions/xtrm-ui/themes/pidex-light-flattools.json +79 -0
  37. package/packages/pi-extensions/extensions/xtrm-ui/themes/pidex-light.json +85 -0
  38. package/packages/pi-extensions/package.json +1 -1
  39. package/packages/pi-extensions/themes/xtrm-ui/pidex-dark-flattools.json +79 -0
  40. package/packages/pi-extensions/themes/xtrm-ui/pidex-dark.json +3 -3
  41. package/packages/pi-extensions/themes/xtrm-ui/pidex-light-flattools.json +79 -0
  42. package/scripts/patch-external-pi-tools.mjs +154 -0
@@ -0,0 +1,85 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "pidex-dark",
4
+ "vars": {
5
+ "accentBlue": "#b8d3ff",
6
+ "accentCyan": "#b8d3ff",
7
+ "accentTeal": "#c7d2e0",
8
+ "successGreen": "#9fd59f",
9
+ "errorRed": "#ff9a9a",
10
+ "warningAmber": "#d2b48c",
11
+ "surface": "#000000",
12
+ "surfaceAlt": "#111111",
13
+ "surfaceMuted": "#1a1a1a",
14
+ "surfaceUser": "#1f1f1f",
15
+ "surfaceCustom": "#161616",
16
+ "gray": "#a7a7a7",
17
+ "dimGray": "#8a8a8a",
18
+ "borderGray": "#666666",
19
+ "borderBright": "#9a9a9a"
20
+ },
21
+ "colors": {
22
+ "accent": "accentBlue",
23
+ "border": "borderGray",
24
+ "borderAccent": "borderBright",
25
+ "borderMuted": "borderGray",
26
+ "success": "successGreen",
27
+ "error": "errorRed",
28
+ "warning": "warningAmber",
29
+ "muted": "gray",
30
+ "dim": "dimGray",
31
+ "text": "",
32
+ "thinkingText": "gray",
33
+
34
+ "selectedBg": "surfaceMuted",
35
+ "userMessageBg": "surfaceUser",
36
+ "userMessageText": "",
37
+ "customMessageBg": "surfaceCustom",
38
+ "customMessageText": "",
39
+ "customMessageLabel": "accentBlue",
40
+ "toolPendingBg": "surfaceMuted",
41
+ "toolSuccessBg": "surfaceMuted",
42
+ "toolErrorBg": "surfaceMuted",
43
+ "toolTitle": "",
44
+ "toolOutput": "gray",
45
+
46
+ "mdHeading": "warningAmber",
47
+ "mdLink": "accentBlue",
48
+ "mdLinkUrl": "dimGray",
49
+ "mdCode": "accentCyan",
50
+ "mdCodeBlock": "gray",
51
+ "mdCodeBlockBorder": "borderGray",
52
+ "mdQuote": "gray",
53
+ "mdQuoteBorder": "borderGray",
54
+ "mdHr": "borderGray",
55
+ "mdListBullet": "accentTeal",
56
+
57
+ "toolDiffAdded": "successGreen",
58
+ "toolDiffRemoved": "errorRed",
59
+ "toolDiffContext": "gray",
60
+
61
+ "syntaxComment": "#6b7280",
62
+ "syntaxKeyword": "#7aa2f7",
63
+ "syntaxFunction": "#c0caf5",
64
+ "syntaxVariable": "#a9b1d6",
65
+ "syntaxString": "#9ece6a",
66
+ "syntaxNumber": "#ff9e64",
67
+ "syntaxType": "#73daca",
68
+ "syntaxOperator": "#c0caf5",
69
+ "syntaxPunctuation": "#8f9bb3",
70
+
71
+ "thinkingOff": "borderGray",
72
+ "thinkingMinimal": "#707070",
73
+ "thinkingLow": "#7a7a7a",
74
+ "thinkingMedium": "#858585",
75
+ "thinkingHigh": "#8f8f8f",
76
+ "thinkingXhigh": "#999999",
77
+
78
+ "bashMode": "accentTeal"
79
+ },
80
+ "export": {
81
+ "pageBg": "#12161d",
82
+ "cardBg": "#171b22",
83
+ "infoBg": "#28230f"
84
+ }
85
+ }
@@ -0,0 +1,79 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "pidex-light-flattools",
4
+ "vars": {
5
+ "accentBlue": "#44546a",
6
+ "accentCyan": "#5f6b7a",
7
+ "accentTeal": "#5f6b7a",
8
+ "successGreen": "#587558",
9
+ "errorRed": "#a85f5f",
10
+ "warningAmber": "#8e7348",
11
+ "surface": "#ffffff",
12
+ "surfaceAlt": "#f1f1f1",
13
+ "surfaceMuted": "#e6e6e6",
14
+ "surfaceUser": "#f0f0f0",
15
+ "surfaceCustom": "#f5f5f5",
16
+ "gray": "#5f5f5f",
17
+ "dimGray": "#7a7a7a",
18
+ "borderGray": "#b9b9b9",
19
+ "borderBright": "#9f9f9f"
20
+ },
21
+ "colors": {
22
+ "accent": "accentBlue",
23
+ "border": "borderGray",
24
+ "borderAccent": "borderBright",
25
+ "borderMuted": "borderGray",
26
+ "success": "successGreen",
27
+ "error": "errorRed",
28
+ "warning": "warningAmber",
29
+ "muted": "gray",
30
+ "dim": "dimGray",
31
+ "text": "",
32
+ "thinkingText": "gray",
33
+ "selectedBg": "surfaceMuted",
34
+ "userMessageBg": "surfaceUser",
35
+ "userMessageText": "",
36
+ "customMessageBg": "surfaceCustom",
37
+ "customMessageText": "",
38
+ "customMessageLabel": "accentBlue",
39
+ "toolPendingBg": "",
40
+ "toolSuccessBg": "",
41
+ "toolErrorBg": "",
42
+ "toolTitle": "",
43
+ "toolOutput": "gray",
44
+ "mdHeading": "warningAmber",
45
+ "mdLink": "accentBlue",
46
+ "mdLinkUrl": "dimGray",
47
+ "mdCode": "accentCyan",
48
+ "mdCodeBlock": "gray",
49
+ "mdCodeBlockBorder": "borderGray",
50
+ "mdQuote": "gray",
51
+ "mdQuoteBorder": "borderGray",
52
+ "mdHr": "borderGray",
53
+ "mdListBullet": "accentTeal",
54
+ "toolDiffAdded": "successGreen",
55
+ "toolDiffRemoved": "errorRed",
56
+ "toolDiffContext": "gray",
57
+ "syntaxComment": "#6b7280",
58
+ "syntaxKeyword": "#3569c8",
59
+ "syntaxFunction": "#3b4351",
60
+ "syntaxVariable": "#364152",
61
+ "syntaxString": "#3f7d3d",
62
+ "syntaxNumber": "#b06500",
63
+ "syntaxType": "#2c7a7b",
64
+ "syntaxOperator": "#3b4351",
65
+ "syntaxPunctuation": "#55606f",
66
+ "thinkingOff": "borderGray",
67
+ "thinkingMinimal": "#9d9d9d",
68
+ "thinkingLow": "#979797",
69
+ "thinkingMedium": "#919191",
70
+ "thinkingHigh": "#8b8b8b",
71
+ "thinkingXhigh": "#858585",
72
+ "bashMode": "accentTeal"
73
+ },
74
+ "export": {
75
+ "pageBg": "#f5f7fa",
76
+ "cardBg": "#ffffff",
77
+ "infoBg": "#fff5e6"
78
+ }
79
+ }
@@ -0,0 +1,85 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "pidex-light",
4
+ "vars": {
5
+ "accentBlue": "#44546a",
6
+ "accentCyan": "#5f6b7a",
7
+ "accentTeal": "#5f6b7a",
8
+ "successGreen": "#587558",
9
+ "errorRed": "#a85f5f",
10
+ "warningAmber": "#8e7348",
11
+ "surface": "#ffffff",
12
+ "surfaceAlt": "#f1f1f1",
13
+ "surfaceMuted": "#e6e6e6",
14
+ "surfaceUser": "#f0f0f0",
15
+ "surfaceCustom": "#f5f5f5",
16
+ "gray": "#5f5f5f",
17
+ "dimGray": "#7a7a7a",
18
+ "borderGray": "#b9b9b9",
19
+ "borderBright": "#9f9f9f"
20
+ },
21
+ "colors": {
22
+ "accent": "accentBlue",
23
+ "border": "borderGray",
24
+ "borderAccent": "borderBright",
25
+ "borderMuted": "borderGray",
26
+ "success": "successGreen",
27
+ "error": "errorRed",
28
+ "warning": "warningAmber",
29
+ "muted": "gray",
30
+ "dim": "dimGray",
31
+ "text": "",
32
+ "thinkingText": "gray",
33
+
34
+ "selectedBg": "surfaceMuted",
35
+ "userMessageBg": "surfaceUser",
36
+ "userMessageText": "",
37
+ "customMessageBg": "surfaceCustom",
38
+ "customMessageText": "",
39
+ "customMessageLabel": "accentBlue",
40
+ "toolPendingBg": "surfaceAlt",
41
+ "toolSuccessBg": "surfaceAlt",
42
+ "toolErrorBg": "surfaceAlt",
43
+ "toolTitle": "",
44
+ "toolOutput": "gray",
45
+
46
+ "mdHeading": "warningAmber",
47
+ "mdLink": "accentBlue",
48
+ "mdLinkUrl": "dimGray",
49
+ "mdCode": "accentCyan",
50
+ "mdCodeBlock": "gray",
51
+ "mdCodeBlockBorder": "borderGray",
52
+ "mdQuote": "gray",
53
+ "mdQuoteBorder": "borderGray",
54
+ "mdHr": "borderGray",
55
+ "mdListBullet": "accentTeal",
56
+
57
+ "toolDiffAdded": "successGreen",
58
+ "toolDiffRemoved": "errorRed",
59
+ "toolDiffContext": "gray",
60
+
61
+ "syntaxComment": "#6b7280",
62
+ "syntaxKeyword": "#3569c8",
63
+ "syntaxFunction": "#3b4351",
64
+ "syntaxVariable": "#364152",
65
+ "syntaxString": "#3f7d3d",
66
+ "syntaxNumber": "#b06500",
67
+ "syntaxType": "#2c7a7b",
68
+ "syntaxOperator": "#3b4351",
69
+ "syntaxPunctuation": "#55606f",
70
+
71
+ "thinkingOff": "borderGray",
72
+ "thinkingMinimal": "#9d9d9d",
73
+ "thinkingLow": "#979797",
74
+ "thinkingMedium": "#919191",
75
+ "thinkingHigh": "#8b8b8b",
76
+ "thinkingXhigh": "#858585",
77
+
78
+ "bashMode": "accentTeal"
79
+ },
80
+ "export": {
81
+ "pageBg": "#f5f7fa",
82
+ "cardBg": "#ffffff",
83
+ "infoBg": "#fff5e6"
84
+ }
85
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaggerxtrm/pi-extensions",
3
- "version": "0.7.12",
3
+ "version": "0.7.14",
4
4
  "description": "Unified Pi extension entrypoint for xtrm-managed extensions",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -0,0 +1,79 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "pidex-dark-flattools",
4
+ "vars": {
5
+ "accentBlue": "#b8d3ff",
6
+ "accentCyan": "#b8d3ff",
7
+ "accentTeal": "#c7d2e0",
8
+ "successGreen": "#9fd59f",
9
+ "errorRed": "#ff9a9a",
10
+ "warningAmber": "#d2b48c",
11
+ "surface": "#000000",
12
+ "surfaceAlt": "#111111",
13
+ "surfaceMuted": "#1a1a1a",
14
+ "surfaceUser": "#1f1f1f",
15
+ "surfaceCustom": "#161616",
16
+ "gray": "#a7a7a7",
17
+ "dimGray": "#8a8a8a",
18
+ "borderGray": "#666666",
19
+ "borderBright": "#9a9a9a"
20
+ },
21
+ "colors": {
22
+ "accent": "accentBlue",
23
+ "border": "borderGray",
24
+ "borderAccent": "borderBright",
25
+ "borderMuted": "borderGray",
26
+ "success": "successGreen",
27
+ "error": "errorRed",
28
+ "warning": "warningAmber",
29
+ "muted": "gray",
30
+ "dim": "dimGray",
31
+ "text": "",
32
+ "thinkingText": "gray",
33
+ "selectedBg": "surfaceMuted",
34
+ "userMessageBg": "surfaceUser",
35
+ "userMessageText": "",
36
+ "customMessageBg": "surfaceCustom",
37
+ "customMessageText": "",
38
+ "customMessageLabel": "accentBlue",
39
+ "toolPendingBg": "",
40
+ "toolSuccessBg": "",
41
+ "toolErrorBg": "",
42
+ "toolTitle": "",
43
+ "toolOutput": "gray",
44
+ "mdHeading": "warningAmber",
45
+ "mdLink": "accentBlue",
46
+ "mdLinkUrl": "dimGray",
47
+ "mdCode": "accentCyan",
48
+ "mdCodeBlock": "gray",
49
+ "mdCodeBlockBorder": "borderGray",
50
+ "mdQuote": "gray",
51
+ "mdQuoteBorder": "borderGray",
52
+ "mdHr": "borderGray",
53
+ "mdListBullet": "accentTeal",
54
+ "toolDiffAdded": "successGreen",
55
+ "toolDiffRemoved": "errorRed",
56
+ "toolDiffContext": "gray",
57
+ "syntaxComment": "#6b7280",
58
+ "syntaxKeyword": "#7aa2f7",
59
+ "syntaxFunction": "#c0caf5",
60
+ "syntaxVariable": "#a9b1d6",
61
+ "syntaxString": "#9ece6a",
62
+ "syntaxNumber": "#ff9e64",
63
+ "syntaxType": "#73daca",
64
+ "syntaxOperator": "#c0caf5",
65
+ "syntaxPunctuation": "#8f9bb3",
66
+ "thinkingOff": "borderGray",
67
+ "thinkingMinimal": "#707070",
68
+ "thinkingLow": "#7a7a7a",
69
+ "thinkingMedium": "#858585",
70
+ "thinkingHigh": "#8f8f8f",
71
+ "thinkingXhigh": "#999999",
72
+ "bashMode": "accentTeal"
73
+ },
74
+ "export": {
75
+ "pageBg": "#12161d",
76
+ "cardBg": "#171b22",
77
+ "infoBg": "#28230f"
78
+ }
79
+ }
@@ -37,9 +37,9 @@
37
37
  "customMessageBg": "surfaceCustom",
38
38
  "customMessageText": "",
39
39
  "customMessageLabel": "accentBlue",
40
- "toolPendingBg": "surfaceAlt",
41
- "toolSuccessBg": "surfaceAlt",
42
- "toolErrorBg": "surfaceAlt",
40
+ "toolPendingBg": "surfaceMuted",
41
+ "toolSuccessBg": "surfaceMuted",
42
+ "toolErrorBg": "surfaceMuted",
43
43
  "toolTitle": "",
44
44
  "toolOutput": "gray",
45
45
 
@@ -0,0 +1,79 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "pidex-light-flattools",
4
+ "vars": {
5
+ "accentBlue": "#44546a",
6
+ "accentCyan": "#5f6b7a",
7
+ "accentTeal": "#5f6b7a",
8
+ "successGreen": "#587558",
9
+ "errorRed": "#a85f5f",
10
+ "warningAmber": "#8e7348",
11
+ "surface": "#ffffff",
12
+ "surfaceAlt": "#f1f1f1",
13
+ "surfaceMuted": "#e6e6e6",
14
+ "surfaceUser": "#f0f0f0",
15
+ "surfaceCustom": "#f5f5f5",
16
+ "gray": "#5f5f5f",
17
+ "dimGray": "#7a7a7a",
18
+ "borderGray": "#b9b9b9",
19
+ "borderBright": "#9f9f9f"
20
+ },
21
+ "colors": {
22
+ "accent": "accentBlue",
23
+ "border": "borderGray",
24
+ "borderAccent": "borderBright",
25
+ "borderMuted": "borderGray",
26
+ "success": "successGreen",
27
+ "error": "errorRed",
28
+ "warning": "warningAmber",
29
+ "muted": "gray",
30
+ "dim": "dimGray",
31
+ "text": "",
32
+ "thinkingText": "gray",
33
+ "selectedBg": "surfaceMuted",
34
+ "userMessageBg": "surfaceUser",
35
+ "userMessageText": "",
36
+ "customMessageBg": "surfaceCustom",
37
+ "customMessageText": "",
38
+ "customMessageLabel": "accentBlue",
39
+ "toolPendingBg": "",
40
+ "toolSuccessBg": "",
41
+ "toolErrorBg": "",
42
+ "toolTitle": "",
43
+ "toolOutput": "gray",
44
+ "mdHeading": "warningAmber",
45
+ "mdLink": "accentBlue",
46
+ "mdLinkUrl": "dimGray",
47
+ "mdCode": "accentCyan",
48
+ "mdCodeBlock": "gray",
49
+ "mdCodeBlockBorder": "borderGray",
50
+ "mdQuote": "gray",
51
+ "mdQuoteBorder": "borderGray",
52
+ "mdHr": "borderGray",
53
+ "mdListBullet": "accentTeal",
54
+ "toolDiffAdded": "successGreen",
55
+ "toolDiffRemoved": "errorRed",
56
+ "toolDiffContext": "gray",
57
+ "syntaxComment": "#6b7280",
58
+ "syntaxKeyword": "#3569c8",
59
+ "syntaxFunction": "#3b4351",
60
+ "syntaxVariable": "#364152",
61
+ "syntaxString": "#3f7d3d",
62
+ "syntaxNumber": "#b06500",
63
+ "syntaxType": "#2c7a7b",
64
+ "syntaxOperator": "#3b4351",
65
+ "syntaxPunctuation": "#55606f",
66
+ "thinkingOff": "borderGray",
67
+ "thinkingMinimal": "#9d9d9d",
68
+ "thinkingLow": "#979797",
69
+ "thinkingMedium": "#919191",
70
+ "thinkingHigh": "#8b8b8b",
71
+ "thinkingXhigh": "#858585",
72
+ "bashMode": "accentTeal"
73
+ },
74
+ "export": {
75
+ "pageBg": "#f5f7fa",
76
+ "cardBg": "#ffffff",
77
+ "infoBg": "#fff5e6"
78
+ }
79
+ }
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { homedir } from 'node:os';
5
+ import { spawnSync } from 'node:child_process';
6
+
7
+ function patchFile(filePath, transforms) {
8
+ if (!fs.existsSync(filePath)) return;
9
+ let text = fs.readFileSync(filePath, 'utf8');
10
+ const original = text;
11
+ for (const t of transforms) text = t(text);
12
+ if (text !== original) fs.writeFileSync(filePath, text, 'utf8');
13
+ }
14
+
15
+ function injectOnce(text, marker, insert) {
16
+ if (text.includes(insert)) return text;
17
+ if (!text.includes(marker)) return text;
18
+ return text.replace(marker, insert + marker);
19
+ }
20
+
21
+ function patchSerena(baseDir) {
22
+ const indexPath = path.join(baseDir, 'pi-serena-tools', 'index.ts');
23
+ const responsesPath = path.join(baseDir, 'pi-serena-tools', 'serenaResponses.ts');
24
+
25
+ patchFile(indexPath, [
26
+ (s) => s.includes('from "@mariozechner/pi-tui"') ? s : s.replace(
27
+ 'import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";\n',
28
+ 'import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";\nimport { Text } from "@mariozechner/pi-tui";\n',
29
+ ),
30
+ (s) => injectOnce(
31
+ s,
32
+ ' registerSerenaTools({',
33
+ ' const originalRegisterTool = pi.registerTool.bind(pi);\n' +
34
+ ' (pi).registerTool = (tool) => {\n' +
35
+ ' originalRegisterTool({\n' +
36
+ ' ...tool,\n' +
37
+ ' renderShell: "self",\n' +
38
+ ' renderCall: tool.renderCall ?? ((_args, _theme) => new Text("", 0, 0)),\n' +
39
+ ' renderResult: tool.renderResult ?? ((result, state, theme) => {\n' +
40
+ ' if (state?.isPartial) return new Text(theme.fg("muted", "…"), 0, 0);\n' +
41
+ ' const first = (result?.content ?? []).find((c) => c?.type === "text")?.text ?? "";\n' +
42
+ ' const line = String(first).split("\\n")[0] || "ok";\n' +
43
+ ' return new Text(theme.fg("toolOutput", line), 0, 0);\n' +
44
+ ' }),\n' +
45
+ ' });\n' +
46
+ ' };\n\n'
47
+ ),
48
+ ]);
49
+
50
+ patchFile(responsesPath, [
51
+ (s) => s.includes('import fs from "node:fs";') ? s : `import fs from "node:fs";\nimport path from "node:path";\n${s}`,
52
+ (s) => injectOnce(
53
+ s,
54
+ 'export const createWithCommonHandling = (deps: {',
55
+ 'function isExternalCompactEnabled() {\n' +
56
+ ' try {\n' +
57
+ ' const p = path.join(process.env.HOME ?? "", ".pi", "agent", "settings.json");\n' +
58
+ ' const cfg = JSON.parse(fs.readFileSync(p, "utf8"));\n' +
59
+ ' return cfg?.xtrmExternalCompact !== false;\n' +
60
+ ' } catch {\n' +
61
+ ' return true;\n' +
62
+ ' }\n' +
63
+ '}\n\n'
64
+ ),
65
+ (s) => s.replace(
66
+ ' const text = await deps.callSerena(toolName, args, timeoutMs);\n return wrapResult(text);',
67
+ ' const text = await deps.callSerena(toolName, args, timeoutMs);\n if (!isExternalCompactEnabled()) return wrapResult(text);\n const lines = text ? text.split("\\n").length : 0;\n return { content: [{ type: "text", text: `• serena ${toolName} · ${lines} lines` }] };',
68
+ ),
69
+ ]);
70
+ }
71
+
72
+ function patchGitnexus(baseDir) {
73
+ const indexPath = path.join(baseDir, 'pi-gitnexus', 'src', 'index.ts');
74
+
75
+ patchFile(indexPath, [
76
+ (s) => s.includes("from '@mariozechner/pi-tui'") ? s : s.replace(
77
+ "import type { ExtensionAPI, ExtensionContext } from '@mariozechner/pi-coding-agent';\n",
78
+ "import type { ExtensionAPI, ExtensionContext } from '@mariozechner/pi-coding-agent';\nimport { Text } from '@mariozechner/pi-tui';\nimport fs from 'node:fs';\nimport path from 'node:path';\n",
79
+ ),
80
+ (s) => injectOnce(
81
+ s,
82
+ "const SEARCH_TOOLS = new Set(['grep', 'find', 'bash', 'read', 'read_many']);",
83
+ "\nfunction isExternalCompactEnabled() {\n" +
84
+ " try {\n" +
85
+ " const p = path.join(process.env.HOME ?? '', '.pi', 'agent', 'settings.json');\n" +
86
+ " const cfg = JSON.parse(fs.readFileSync(p, 'utf8'));\n" +
87
+ " return cfg?.xtrmExternalCompact !== false;\n" +
88
+ " } catch {\n" +
89
+ " return true;\n" +
90
+ " }\n" +
91
+ "}\n"
92
+ ),
93
+ (s) => injectOnce(
94
+ s,
95
+ ' registerTools(pi);',
96
+ ' const originalRegisterTool = pi.registerTool.bind(pi);\n' +
97
+ ' (pi).registerTool = (tool) => {\n' +
98
+ ' originalRegisterTool({\n' +
99
+ ' ...tool,\n' +
100
+ ' renderShell: "self",\n' +
101
+ ' renderCall: tool.renderCall ?? ((_args, _theme) => new Text("", 0, 0)),\n' +
102
+ ' renderResult: tool.renderResult ?? ((result, state, theme) => {\n' +
103
+ ' if (state?.isPartial) return new Text(theme.fg("muted", "…"), 0, 0);\n' +
104
+ ' const first = (result?.content ?? []).find((c) => c?.type === "text")?.text ?? "";\n' +
105
+ ' const line = String(first).split("\\n")[0] || "ok";\n' +
106
+ ' return new Text(theme.fg("toolOutput", line), 0, 0);\n' +
107
+ ' }),\n' +
108
+ ' });\n' +
109
+ ' };\n\n'
110
+ ),
111
+ (s) => s.replace(
112
+ "if (!event.isError && event.toolName.startsWith('gitnexus_')) {",
113
+ "if (!event.isError && event.toolName.startsWith('gitnexus_') && isExternalCompactEnabled()) {",
114
+ ),
115
+ ]);
116
+ }
117
+
118
+ function collectNodeModuleRoots() {
119
+ const roots = new Set();
120
+
121
+ // NVM layout (current primary setup)
122
+ const nvmBase = path.join(homedir(), '.nvm', 'versions', 'node');
123
+ if (fs.existsSync(nvmBase)) {
124
+ for (const v of fs.readdirSync(nvmBase).filter((x) => x.startsWith('v'))) {
125
+ const modules = path.join(nvmBase, v, 'lib', 'node_modules');
126
+ if (fs.existsSync(modules)) roots.add(modules);
127
+ }
128
+ }
129
+
130
+ // Generic npm/pnpm global roots
131
+ for (const cmd of [
132
+ ['npm', ['root', '-g']],
133
+ ['pnpm', ['root', '-g']],
134
+ ]) {
135
+ const [bin, args] = cmd;
136
+ const out = spawnSync(bin, args, { encoding: 'utf8' });
137
+ if (out.status === 0) {
138
+ const root = (out.stdout || '').trim();
139
+ if (root && fs.existsSync(root)) roots.add(root);
140
+ }
141
+ }
142
+
143
+ return [...roots];
144
+ }
145
+
146
+ function main() {
147
+ const roots = collectNodeModuleRoots();
148
+ for (const modules of roots) {
149
+ patchSerena(modules);
150
+ patchGitnexus(modules);
151
+ }
152
+ }
153
+
154
+ main();