@psnext/slingcli 2.5.20260607-2 → 2.5.20260615-2

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 (205) hide show
  1. package/node_modules/@aws-sdk/core/dist-cjs/index.js +47 -2297
  2. package/node_modules/@aws-sdk/core/dist-es/index.js +3 -3
  3. package/node_modules/@aws-sdk/core/dist-types/index.d.ts +5 -5
  4. package/node_modules/@aws-sdk/core/dist-types/ts3.4/index.d.ts +5 -5
  5. package/node_modules/@aws-sdk/core/package.json +8 -8
  6. package/node_modules/@aws-sdk/credential-provider-env/package.json +4 -4
  7. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/package.json +7 -7
  8. package/node_modules/@aws-sdk/credential-provider-http/package.json +6 -5
  9. package/node_modules/@aws-sdk/credential-provider-ini/package.json +11 -11
  10. package/node_modules/@aws-sdk/credential-provider-login/package.json +5 -5
  11. package/node_modules/@aws-sdk/credential-provider-node/package.json +9 -9
  12. package/node_modules/@aws-sdk/credential-provider-process/package.json +4 -4
  13. package/node_modules/@aws-sdk/credential-provider-sso/dist-cjs/index.js +4 -1
  14. package/node_modules/@aws-sdk/credential-provider-sso/dist-es/resolveSSOCredentials.js +4 -1
  15. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/package.json +5 -5
  16. package/node_modules/@aws-sdk/credential-provider-sso/package.json +9 -7
  17. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +5 -5
  18. package/node_modules/@aws-sdk/eventstream-handler-node/package.json +3 -3
  19. package/node_modules/@aws-sdk/middleware-eventstream/package.json +3 -3
  20. package/node_modules/@aws-sdk/middleware-websocket/package.json +5 -5
  21. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
  22. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
  23. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
  24. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
  25. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
  26. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/package.json +7 -7
  27. package/node_modules/@aws-sdk/nested-clients/package.json +6 -6
  28. package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +7 -6
  29. package/node_modules/@aws-sdk/types/package.json +2 -2
  30. package/node_modules/@aws-sdk/util-locate-window/package.json +2 -2
  31. package/node_modules/@aws-sdk/xml-builder/package.json +2 -2
  32. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +8 -0
  33. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +1 -1
  34. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  35. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +6 -6
  36. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +906 -873
  37. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +25 -8
  38. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +19 -12
  39. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +1 -0
  40. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +8 -4
  41. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +2 -1
  42. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js +2 -1
  43. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +1 -0
  44. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +1 -1
  45. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +3 -2
  46. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  47. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js +3 -5
  48. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/project-trust.js +48 -0
  49. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/startup-ui.js +133 -0
  50. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +14 -3
  51. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-runtime.js +4 -1
  52. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.js +2 -2
  53. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +9 -2
  54. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/utils.js +1 -1
  55. package/node_modules/@earendil-works/pi-coding-agent/dist/core/experimental.js +4 -0
  56. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js +4 -4
  57. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js +34 -0
  58. package/node_modules/@earendil-works/pi-coding-agent/dist/core/index.js +1 -0
  59. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +5 -57
  60. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +41 -5
  61. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +31 -17
  62. package/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js +58 -0
  63. package/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js +24 -26
  64. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.js +0 -4
  65. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +121 -54
  66. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +11 -2
  67. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +26 -0
  68. package/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js +89 -17
  69. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +2 -2
  70. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +65 -76
  71. package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js +1 -118
  72. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/first-time-setup.js +103 -0
  73. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
  74. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +7 -1
  75. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +20 -0
  76. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tree-selector.js +87 -12
  77. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/trust-selector.js +23 -18
  78. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +104 -30
  79. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.js +19 -41
  80. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +72 -18
  81. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js +78 -0
  82. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/version-check.js +4 -27
  83. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/LICENSE +15 -0
  84. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/bin/semver.js +195 -0
  85. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/classes/comparator.js +143 -0
  86. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/classes/index.js +7 -0
  87. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/classes/range.js +557 -0
  88. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/classes/semver.js +333 -0
  89. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/clean.js +8 -0
  90. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/cmp.js +54 -0
  91. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/coerce.js +62 -0
  92. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/compare-build.js +9 -0
  93. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/compare-loose.js +5 -0
  94. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/compare.js +7 -0
  95. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/diff.js +60 -0
  96. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/eq.js +5 -0
  97. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/gt.js +5 -0
  98. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/gte.js +5 -0
  99. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/inc.js +21 -0
  100. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/lt.js +5 -0
  101. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/lte.js +5 -0
  102. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/major.js +5 -0
  103. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/minor.js +5 -0
  104. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/neq.js +5 -0
  105. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/parse.js +18 -0
  106. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/patch.js +5 -0
  107. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/prerelease.js +8 -0
  108. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/rcompare.js +5 -0
  109. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/rsort.js +5 -0
  110. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/satisfies.js +12 -0
  111. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/sort.js +5 -0
  112. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/truncate.js +48 -0
  113. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/functions/valid.js +8 -0
  114. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/index.js +93 -0
  115. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/internal/constants.js +37 -0
  116. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/internal/debug.js +11 -0
  117. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/internal/identifiers.js +29 -0
  118. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/internal/lrucache.js +42 -0
  119. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/internal/parse-options.js +17 -0
  120. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/internal/re.js +223 -0
  121. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/package.json +78 -0
  122. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/preload.js +4 -0
  123. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/range.bnf +17 -0
  124. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/gtr.js +6 -0
  125. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/intersects.js +9 -0
  126. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/ltr.js +6 -0
  127. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/max-satisfying.js +27 -0
  128. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/min-satisfying.js +26 -0
  129. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/min-version.js +63 -0
  130. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/outside.js +82 -0
  131. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/simplify.js +49 -0
  132. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/subset.js +249 -0
  133. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/to-comparators.js +10 -0
  134. package/node_modules/@earendil-works/pi-coding-agent/node_modules/semver/ranges/valid.js +13 -0
  135. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +25 -12
  136. package/node_modules/@earendil-works/pi-coding-agent/package.json +6 -8
  137. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +102 -43
  138. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +11 -1
  139. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js +2 -2
  140. package/node_modules/@earendil-works/pi-tui/dist/index.js +2 -0
  141. package/node_modules/@earendil-works/pi-tui/dist/terminal-colors.js +51 -0
  142. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +38 -77
  143. package/node_modules/@earendil-works/pi-tui/dist/tui.js +145 -21
  144. package/node_modules/@earendil-works/pi-tui/dist/utils.js +44 -16
  145. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  146. package/node_modules/@nodable/entities/package.json +4 -1
  147. package/node_modules/@nodable/entities/src/EntityDecoder.js +104 -3
  148. package/node_modules/@nodable/entities/src/index.d.ts +77 -0
  149. package/node_modules/@nodable/entities/src/index.js +1 -1
  150. package/node_modules/@smithy/core/dist-cjs/submodules/retry/index.browser.js +3 -3
  151. package/node_modules/@smithy/core/dist-cjs/submodules/retry/index.js +3 -3
  152. package/node_modules/@smithy/core/dist-es/submodules/retry/middleware-retry/retry-pre-sra-deprecated/StandardRetryStrategy.js +2 -2
  153. package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/DefaultRateLimiter.js +1 -1
  154. package/node_modules/@smithy/core/package.json +17 -17
  155. package/node_modules/@smithy/credential-provider-imds/package.json +6 -6
  156. package/node_modules/@smithy/fetch-http-handler/package.json +9 -9
  157. package/node_modules/@smithy/signature-v4/package.json +6 -6
  158. package/node_modules/@smithy/types/package.json +9 -9
  159. package/node_modules/@types/node/README.md +1 -1
  160. package/node_modules/@types/node/http2.d.ts +1 -0
  161. package/node_modules/@types/node/package.json +2 -2
  162. package/node_modules/anynum/LICENSE +21 -0
  163. package/node_modules/anynum/README.md +142 -0
  164. package/node_modules/anynum/anynum.js +135 -0
  165. package/node_modules/anynum/digitTable.js +116 -0
  166. package/node_modules/anynum/package.json +42 -0
  167. package/node_modules/protobufjs/dist/light/protobuf.js +161 -203
  168. package/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
  169. package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
  170. package/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
  171. package/node_modules/protobufjs/dist/minimal/protobuf.js +34 -77
  172. package/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
  173. package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
  174. package/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
  175. package/node_modules/protobufjs/dist/protobuf.js +181 -223
  176. package/node_modules/protobufjs/dist/protobuf.js.map +1 -1
  177. package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
  178. package/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
  179. package/node_modules/protobufjs/index.d.ts +0 -8
  180. package/node_modules/protobufjs/package.json +2 -3
  181. package/node_modules/protobufjs/src/converter.js +5 -4
  182. package/node_modules/protobufjs/src/decoder.js +1 -1
  183. package/node_modules/protobufjs/src/service.js +5 -7
  184. package/node_modules/protobufjs/src/type.js +3 -1
  185. package/node_modules/protobufjs/src/util/minimal.js +1 -4
  186. package/node_modules/protobufjs/src/verifier.js +1 -1
  187. package/node_modules/semver/classes/range.js +11 -2
  188. package/node_modules/semver/package.json +2 -2
  189. package/node_modules/strnum/CHANGELOG.md +8 -0
  190. package/node_modules/strnum/README.md +9 -0
  191. package/node_modules/strnum/package.json +4 -1
  192. package/node_modules/strnum/strnum.js +9 -1
  193. package/package.json +6 -6
  194. package/slingshot/index.js +260 -258
  195. package/node_modules/@protobufjs/inquire/CHANGELOG.md +0 -8
  196. package/node_modules/@protobufjs/inquire/LICENSE +0 -26
  197. package/node_modules/@protobufjs/inquire/README.md +0 -13
  198. package/node_modules/@protobufjs/inquire/index.d.ts +0 -10
  199. package/node_modules/@protobufjs/inquire/index.js +0 -38
  200. package/node_modules/@protobufjs/inquire/package.json +0 -21
  201. package/node_modules/@protobufjs/inquire/tests/data/array.js +0 -1
  202. package/node_modules/@protobufjs/inquire/tests/data/emptyArray.js +0 -1
  203. package/node_modules/@protobufjs/inquire/tests/data/emptyObject.js +0 -1
  204. package/node_modules/@protobufjs/inquire/tests/data/object.js +0 -1
  205. package/node_modules/@protobufjs/inquire/tests/index.js +0 -20
@@ -1,7 +1,7 @@
1
- import { Container, getKeybindings, Input, Spacer, Text, TruncatedText, truncateToWidth, } from "@earendil-works/pi-tui";
1
+ import { Container, getKeybindings, Input, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "@earendil-works/pi-tui";
2
2
  import { theme } from "../theme/theme.js";
3
3
  import { DynamicBorder } from "./dynamic-border.js";
4
- import { keyHint, keyText } from "./keybinding-hints.js";
4
+ import { formatKeyText, keyHint } from "./keybinding-hints.js";
5
5
  class TreeList {
6
6
  flatNodes = [];
7
7
  filteredNodes = [];
@@ -956,6 +956,90 @@ class SearchLine {
956
956
  }
957
957
  handleInput(_keyData) { }
958
958
  }
959
+ /** Component that renders tree help as semantic rows with chunk-aware wrapping */
960
+ class TreeHelp {
961
+ invalidate() { }
962
+ render(width) {
963
+ const items = TREE_HELP_ITEMS.map(({ keys, label, labelFirst }) => {
964
+ const text = formatHelpKeys(keys);
965
+ if (!text)
966
+ return label;
967
+ return labelFirst ? `${label} ${text}` : `${text} ${label}`;
968
+ });
969
+ const availableWidth = Math.max(1, width);
970
+ const indent = " ";
971
+ const separator = " · ";
972
+ const lines = [];
973
+ let currentLine = "";
974
+ for (const item of items) {
975
+ const candidate = currentLine
976
+ ? `${currentLine}${separator}${item}`
977
+ : visibleWidth(`${indent}${item}`) <= availableWidth
978
+ ? `${indent}${item}`
979
+ : item;
980
+ if (!currentLine || visibleWidth(candidate) <= availableWidth) {
981
+ currentLine = candidate;
982
+ continue;
983
+ }
984
+ lines.push(...wrapTextWithAnsi(currentLine.trimEnd(), availableWidth));
985
+ currentLine = visibleWidth(`${indent}${item}`) <= availableWidth ? `${indent}${item}` : item;
986
+ }
987
+ if (currentLine) {
988
+ lines.push(...wrapTextWithAnsi(currentLine.trimEnd(), availableWidth));
989
+ }
990
+ return lines.map((line) => theme.fg("muted", line));
991
+ }
992
+ }
993
+ const TREE_HELP_ITEMS = [
994
+ { keys: ["tui.select.up", "tui.select.down"], label: "move" },
995
+ { keys: ["tui.editor.cursorLeft", "tui.editor.cursorRight"], label: "page" },
996
+ { keys: ["app.tree.foldOrUp", "app.tree.unfoldOrDown"], label: "branch" },
997
+ { keys: ["app.tree.editLabel"], label: "label" },
998
+ { keys: ["app.tree.toggleLabelTimestamp"], label: "label time" },
999
+ {
1000
+ keys: [
1001
+ "app.tree.filter.default",
1002
+ "app.tree.filter.noTools",
1003
+ "app.tree.filter.userOnly",
1004
+ "app.tree.filter.labeledOnly",
1005
+ "app.tree.filter.all",
1006
+ ],
1007
+ label: "filters",
1008
+ labelFirst: true,
1009
+ },
1010
+ { keys: ["app.tree.filter.cycleForward", "app.tree.filter.cycleBackward"], label: "cycle", labelFirst: true },
1011
+ ];
1012
+ function formatHelpKeys(keybindings) {
1013
+ const keys = [];
1014
+ for (const keybinding of keybindings) {
1015
+ const key = getKeybindings().getKeys(keybinding)[0];
1016
+ if (key !== undefined)
1017
+ keys.push(key);
1018
+ }
1019
+ if (keys.length === 0)
1020
+ return "";
1021
+ return formatKeyText(compactRawKeys(keys))
1022
+ .replace(/\bpageUp\b/g, "pgup")
1023
+ .replace(/\bpageDown\b/g, "pgdn")
1024
+ .replace(/\bup\b/g, "↑")
1025
+ .replace(/\bdown\b/g, "↓")
1026
+ .replace(/\bleft\b/g, "←")
1027
+ .replace(/\bright\b/g, "→");
1028
+ }
1029
+ function compactRawKeys(keys) {
1030
+ if (keys.length === 1)
1031
+ return keys[0];
1032
+ const parts = keys.map((key) => {
1033
+ const separatorIndex = key.lastIndexOf("+");
1034
+ return separatorIndex === -1
1035
+ ? { prefix: "", suffix: key }
1036
+ : { prefix: key.slice(0, separatorIndex + 1), suffix: key.slice(separatorIndex + 1) };
1037
+ });
1038
+ const prefix = parts[0].prefix;
1039
+ return prefix && parts.every((part) => part.prefix === prefix)
1040
+ ? `${prefix}${parts.map((part) => part.suffix).join("/")}`
1041
+ : keys.join("/");
1042
+ }
959
1043
  /** Label input component shown when editing a label */
960
1044
  class LabelInput {
961
1045
  input;
@@ -1037,16 +1121,7 @@ export class TreeSelectorComponent extends Container {
1037
1121
  this.addChild(new Spacer(1));
1038
1122
  this.addChild(new DynamicBorder());
1039
1123
  this.addChild(new Text(theme.bold(" Session Tree"), 1, 0));
1040
- const filterKeys = [
1041
- keyText("app.tree.filter.default"),
1042
- keyText("app.tree.filter.noTools"),
1043
- keyText("app.tree.filter.userOnly"),
1044
- keyText("app.tree.filter.labeledOnly"),
1045
- keyText("app.tree.filter.all"),
1046
- ].join("/");
1047
- const cycleKeys = `${keyText("app.tree.filter.cycleForward")}/${keyText("app.tree.filter.cycleBackward")}`;
1048
- const branchKeys = `${keyText("app.tree.foldOrUp")}/${keyText("app.tree.unfoldOrDown")}`;
1049
- this.addChild(new TruncatedText(theme.fg("muted", ` ↑/↓: move. ←/→: page. ${branchKeys}: fold/branch. ${keyText("app.tree.editLabel")}: label. ${filterKeys}: filters (${cycleKeys} cycle). ${keyText("app.tree.toggleLabelTimestamp")}: label time`), 0, 0));
1124
+ this.addChild(new TreeHelp());
1050
1125
  this.addChild(new SearchLine(this.treeList));
1051
1126
  this.addChild(new DynamicBorder());
1052
1127
  this.addChild(new Spacer(1));
@@ -1,30 +1,30 @@
1
1
  import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
2
+ import { getProjectTrustOptions, } from "../../../core/trust-manager.js";
2
3
  import { theme } from "../theme/theme.js";
3
4
  import { DynamicBorder } from "./dynamic-border.js";
4
5
  import { keyHint, rawKeyHint } from "./keybinding-hints.js";
5
- const TRUST_OPTIONS = [
6
- { label: "Trust", trusted: true },
7
- { label: "Do not trust", trusted: false },
8
- ];
9
- function formatDecision(decision) {
10
- if (decision === true) {
11
- return "trusted";
6
+ function formatDecision(trustPath, decision) {
7
+ if (decision === null) {
8
+ return "none";
12
9
  }
13
- if (decision === false) {
14
- return "untrusted";
10
+ const label = decision.decision ? "trusted" : "untrusted";
11
+ if (trustPath !== undefined && decision.path !== trustPath) {
12
+ return `${label} (inherited from ${decision.path})`;
15
13
  }
16
- return "none";
14
+ return `${label} (${decision.path})`;
17
15
  }
18
16
  export class TrustSelectorComponent extends Container {
19
17
  selectedIndex;
20
18
  listContainer;
19
+ trustOptions;
21
20
  savedDecision;
22
21
  onSelectCallback;
23
22
  onCancelCallback;
24
23
  constructor(options) {
25
24
  super();
26
25
  this.savedDecision = options.savedDecision;
27
- this.selectedIndex = Math.max(0, TRUST_OPTIONS.findIndex((option) => option.trusted === options.savedDecision));
26
+ this.trustOptions = getProjectTrustOptions(options.cwd);
27
+ this.selectedIndex = Math.max(0, this.trustOptions.findIndex((option) => this.isSavedOption(option)));
28
28
  this.onSelectCallback = options.onSelect;
29
29
  this.onCancelCallback = options.onCancel;
30
30
  this.addChild(new DynamicBorder());
@@ -32,7 +32,7 @@ export class TrustSelectorComponent extends Container {
32
32
  this.addChild(new Text(theme.fg("accent", theme.bold("Project trust")), 1, 0));
33
33
  this.addChild(new Text(theme.fg("muted", options.cwd), 1, 0));
34
34
  this.addChild(new Spacer(1));
35
- this.addChild(new Text(theme.fg("muted", `Saved decision: ${formatDecision(options.savedDecision)}`), 1, 0));
35
+ this.addChild(new Text(theme.fg("muted", `Saved decision: ${formatDecision(this.trustOptions[0]?.savedPath, options.savedDecision)}`), 1, 0));
36
36
  this.addChild(new Text(theme.fg("muted", `Current session: ${options.projectTrusted ? "trusted" : "untrusted"}`), 1, 0));
37
37
  this.addChild(new Spacer(1));
38
38
  this.listContainer = new Container();
@@ -47,15 +47,20 @@ export class TrustSelectorComponent extends Container {
47
47
  this.addChild(new DynamicBorder());
48
48
  this.updateList();
49
49
  }
50
+ isSavedOption(option) {
51
+ return (option.savedPath !== undefined &&
52
+ this.savedDecision?.decision === option.trusted &&
53
+ this.savedDecision.path === option.savedPath);
54
+ }
50
55
  updateList() {
51
56
  this.listContainer.clear();
52
- for (let i = 0; i < TRUST_OPTIONS.length; i++) {
53
- const option = TRUST_OPTIONS[i];
57
+ for (let i = 0; i < this.trustOptions.length; i++) {
58
+ const option = this.trustOptions[i];
54
59
  if (!option) {
55
60
  continue;
56
61
  }
57
62
  const isSelected = i === this.selectedIndex;
58
- const isCurrent = option.trusted === this.savedDecision;
63
+ const isCurrent = this.isSavedOption(option);
59
64
  const checkmark = isCurrent ? theme.fg("success", " ✓") : "";
60
65
  const prefix = isSelected ? theme.fg("accent", "→ ") : " ";
61
66
  const label = isSelected ? theme.fg("accent", option.label) : theme.fg("text", option.label);
@@ -69,13 +74,13 @@ export class TrustSelectorComponent extends Container {
69
74
  this.updateList();
70
75
  }
71
76
  else if (kb.matches(keyData, "tui.select.down") || keyData === "j") {
72
- this.selectedIndex = Math.min(TRUST_OPTIONS.length - 1, this.selectedIndex + 1);
77
+ this.selectedIndex = Math.min(this.trustOptions.length - 1, this.selectedIndex + 1);
73
78
  this.updateList();
74
79
  }
75
80
  else if (kb.matches(keyData, "tui.select.confirm") || keyData === "\n") {
76
- const selected = TRUST_OPTIONS[this.selectedIndex];
81
+ const selected = this.trustOptions[this.selectedIndex];
77
82
  if (selected) {
78
- this.onSelectCallback(selected.trusted);
83
+ this.onSelectCallback({ trusted: selected.trusted, updates: selected.updates });
79
84
  }
80
85
  }
81
86
  else if (kb.matches(keyData, "tui.select.cancel")) {
@@ -24,8 +24,8 @@ import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../cor
24
24
  import { SessionManager } from "../../core/session-manager.js";
25
25
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
26
26
  import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
27
- import { hasProjectTrustInputs, ProjectTrustStore } from "../../core/trust-manager.js";
28
- import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
27
+ import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
28
+ import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.js";
29
29
  import { copyToClipboard } from "../../utils/clipboard.js";
30
30
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
31
31
  import { parseGitUrl } from "../../utils/git.js";
@@ -63,7 +63,7 @@ import { TreeSelectorComponent } from "./components/tree-selector.js";
63
63
  import { TrustSelectorComponent } from "./components/trust-selector.js";
64
64
  import { UserMessageComponent } from "./components/user-message.js";
65
65
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
66
- import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, initTheme, onThemeChange, setRegisteredThemes, setTheme, setThemeInstance, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
66
+ import { detectTerminalBackgroundTheme, getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, initTheme, onThemeChange, setRegisteredThemes, setTheme, setThemeInstance, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
67
67
  function isExpandable(obj) {
68
68
  return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
69
69
  }
@@ -215,6 +215,7 @@ export class InteractiveMode {
215
215
  // Custom header from extension (undefined = use built-in header)
216
216
  customHeader = undefined;
217
217
  options;
218
+ autoTrustOnReloadCwd;
218
219
  // Convenience accessors
219
220
  get session() {
220
221
  return this.runtimeHost.session;
@@ -231,6 +232,7 @@ export class InteractiveMode {
231
232
  constructor(runtimeHost, options = {}) {
232
233
  this.runtimeHost = runtimeHost;
233
234
  this.options = options;
235
+ this.autoTrustOnReloadCwd = options.autoTrustOnReloadCwd;
234
236
  this.runtimeHost.setBeforeSessionInvalidate(() => {
235
237
  this.resetExtensionUI();
236
238
  });
@@ -266,6 +268,22 @@ export class InteractiveMode {
266
268
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
267
269
  initTheme(this.settingsManager.getTheme(), true);
268
270
  }
271
+ async detectThemeIfUnset() {
272
+ if (this.settingsManager.getTheme()) {
273
+ return;
274
+ }
275
+ const detection = await detectTerminalBackgroundTheme({ ui: this.ui, timeoutMs: 100 });
276
+ const result = setTheme(detection.theme, true);
277
+ if (!result.success) {
278
+ return;
279
+ }
280
+ if (detection.confidence === "high") {
281
+ this.settingsManager.setTheme(detection.theme);
282
+ await this.settingsManager.flush();
283
+ }
284
+ this.updateEditorBorderColor();
285
+ this.ui.requestRender();
286
+ }
269
287
  getAutocompleteSourceTag(sourceInfo) {
270
288
  if (!sourceInfo) {
271
289
  return undefined;
@@ -370,8 +388,13 @@ export class InteractiveMode {
370
388
  }
371
389
  setupAutocompleteProvider() {
372
390
  let provider = this.createBaseAutocompleteProvider();
391
+ const triggerCharacters = [];
373
392
  for (const wrapProvider of this.autocompleteProviderWrappers) {
374
393
  provider = wrapProvider(provider);
394
+ triggerCharacters.push(...(provider.triggerCharacters ?? []));
395
+ }
396
+ if (triggerCharacters.length > 0) {
397
+ provider.triggerCharacters = [...new Set(triggerCharacters)];
375
398
  }
376
399
  this.autocompleteProvider = provider;
377
400
  this.defaultEditor.setAutocompleteProvider(provider);
@@ -428,8 +451,23 @@ export class InteractiveMode {
428
451
  : "";
429
452
  console.log(theme.fg("dim", `Model scope: ${modelList}${cycleHint}`));
430
453
  }
431
- // Add header container as first child
454
+ // Add header container as first child. Populate it after detectThemeIfUnset.
432
455
  this.ui.addChild(this.headerContainer);
456
+ this.ui.addChild(this.chatContainer);
457
+ this.ui.addChild(this.pendingMessagesContainer);
458
+ this.ui.addChild(this.statusContainer);
459
+ this.renderWidgets(); // Initialize with default spacer
460
+ this.ui.addChild(this.widgetContainerAbove);
461
+ this.ui.addChild(this.editorContainer);
462
+ this.ui.addChild(this.widgetContainerBelow);
463
+ this.ui.addChild(this.footer);
464
+ this.ui.setFocus(this.editor);
465
+ this.setupKeyHandlers();
466
+ this.setupEditorSubmitHandler();
467
+ // Start the UI before initializing extensions so session_start handlers can use interactive dialogs
468
+ this.ui.start();
469
+ this.isInitialized = true;
470
+ await this.detectThemeIfUnset();
433
471
  // Add header with keybindings from config (unless silenced)
434
472
  if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
435
473
  const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
@@ -476,20 +514,7 @@ export class InteractiveMode {
476
514
  this.builtInHeader = new Text("", 0, 0);
477
515
  this.headerContainer.addChild(this.builtInHeader);
478
516
  }
479
- this.ui.addChild(this.chatContainer);
480
- this.ui.addChild(this.pendingMessagesContainer);
481
- this.ui.addChild(this.statusContainer);
482
- this.renderWidgets(); // Initialize with default spacer
483
- this.ui.addChild(this.widgetContainerAbove);
484
- this.ui.addChild(this.editorContainer);
485
- this.ui.addChild(this.widgetContainerBelow);
486
- this.ui.addChild(this.footer);
487
- this.ui.setFocus(this.editor);
488
- this.setupKeyHandlers();
489
- this.setupEditorSubmitHandler();
490
- // Start the UI before initializing extensions so session_start handlers can use interactive dialogs
491
- this.ui.start();
492
- this.isInitialized = true;
517
+ this.ui.requestRender();
493
518
  // Initialize extensions first so resources are shown before messages
494
519
  await this.rebindCurrentSession();
495
520
  // Render initial messages AFTER showing loaded resources
@@ -670,7 +695,7 @@ export class InteractiveMode {
670
695
  if (newEntries.length > 0) {
671
696
  this.settingsManager.setLastChangelogVersion(VERSION);
672
697
  this.reportInstallTelemetry(VERSION);
673
- return newEntries.map((e) => e.content).join("\n\n");
698
+ return newEntries.map((e) => normalizeChangelogLinks(e.content, e)).join("\n\n");
674
699
  }
675
700
  return undefined;
676
701
  }
@@ -1278,6 +1303,7 @@ export class InteractiveMode {
1278
1303
  modelRegistry: this.session.modelRegistry,
1279
1304
  model: this.session.model,
1280
1305
  isIdle: () => !this.session.isStreaming,
1306
+ isProjectTrusted: () => this.settingsManager.isProjectTrusted(),
1281
1307
  signal: this.session.agent.signal,
1282
1308
  abort: () => {
1283
1309
  this.restoreQueuedMessagesToEditor({ abort: true });
@@ -1557,6 +1583,20 @@ export class InteractiveMode {
1557
1583
  /**
1558
1584
  * Create the ExtensionUIContext for extensions.
1559
1585
  */
1586
+ createProjectTrustContext(cwd) {
1587
+ const ui = this.createExtensionUIContext();
1588
+ return {
1589
+ cwd,
1590
+ mode: "tui",
1591
+ hasUI: true,
1592
+ ui: {
1593
+ select: ui.select,
1594
+ confirm: ui.confirm,
1595
+ input: ui.input,
1596
+ notify: ui.notify,
1597
+ },
1598
+ };
1599
+ }
1560
1600
  createExtensionUIContext() {
1561
1601
  return {
1562
1602
  select: (title, options, opts) => this.showExtensionSelector(title, options, opts),
@@ -2645,13 +2685,13 @@ export class InteractiveMode {
2645
2685
  }
2646
2686
  }
2647
2687
  renderProjectTrustWarningIfNeeded() {
2648
- if (this.settingsManager.isProjectTrusted() || !hasProjectTrustInputs(this.sessionManager.getCwd())) {
2688
+ if (this.settingsManager.isProjectTrusted() || !hasTrustRequiringProjectResources(this.sessionManager.getCwd())) {
2649
2689
  return;
2650
2690
  }
2651
2691
  if (this.chatContainer.children.length > 0) {
2652
2692
  this.chatContainer.addChild(new Spacer(1));
2653
2693
  }
2654
- this.chatContainer.addChild(new Text(theme.fg("warning", "This project is not trusted. Project instructions (AGENTS.md/CLAUDE.md), .pi resources, and project packages are ignored. Use /trust to save a trust decision, then restart pi."), 1, 0));
2694
+ this.chatContainer.addChild(new Text(theme.fg("warning", "This project is not trusted. Project .pi resources and packages are ignored. Use /trust to save a trust decision, then restart pi."), 1, 0));
2655
2695
  }
2656
2696
  async getUserInput() {
2657
2697
  const queuedInput = this.pendingUserInputs.shift();
@@ -2697,7 +2737,9 @@ export class InteractiveMode {
2697
2737
  if (this.isShuttingDown)
2698
2738
  return;
2699
2739
  this.isShuttingDown = true;
2700
- this.unregisterSignalHandlers();
2740
+ // Keep signal handlers registered until terminal cleanup has completed.
2741
+ // `signal-exit` checks the listener list during the same SIGTERM/SIGHUP
2742
+ // dispatch and re-sends the signal if only its own listeners remain.
2701
2743
  if (options?.fromSignal) {
2702
2744
  // Signal-triggered shutdown (SIGTERM/SIGHUP). Emit extension cleanup
2703
2745
  // (session_shutdown) BEFORE touching the terminal. Extension teardown
@@ -3262,6 +3304,7 @@ export class InteractiveMode {
3262
3304
  doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
3263
3305
  treeFilterMode: this.settingsManager.getTreeFilterMode(),
3264
3306
  showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
3307
+ defaultProjectTrust: this.settingsManager.getDefaultProjectTrust(),
3265
3308
  editorPaddingX: this.settingsManager.getEditorPaddingX(),
3266
3309
  autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
3267
3310
  quietStartup: this.settingsManager.getQuietStartup(),
@@ -3354,6 +3397,9 @@ export class InteractiveMode {
3354
3397
  onQuietStartupChange: (enabled) => {
3355
3398
  this.settingsManager.setQuietStartup(enabled);
3356
3399
  },
3400
+ onDefaultProjectTrustChange: (defaultProjectTrust) => {
3401
+ this.settingsManager.setDefaultProjectTrust(defaultProjectTrust);
3402
+ },
3357
3403
  onDoubleEscapeActionChange: (action) => {
3358
3404
  this.settingsManager.setDoubleEscapeAction(action);
3359
3405
  },
@@ -3468,19 +3514,42 @@ export class InteractiveMode {
3468
3514
  // Ignore auth lookup failures for warning-only checks.
3469
3515
  }
3470
3516
  }
3517
+ maybeSaveImplicitProjectTrustAfterReload() {
3518
+ const cwd = this.sessionManager.getCwd();
3519
+ if (this.autoTrustOnReloadCwd !== cwd) {
3520
+ return false;
3521
+ }
3522
+ if (!this.settingsManager.isProjectTrusted() || !hasTrustRequiringProjectResources(cwd)) {
3523
+ return false;
3524
+ }
3525
+ const trustStore = new ProjectTrustStore(this.runtimeHost.services.agentDir);
3526
+ try {
3527
+ if (trustStore.get(cwd) !== null) {
3528
+ this.autoTrustOnReloadCwd = undefined;
3529
+ return false;
3530
+ }
3531
+ trustStore.set(cwd, true);
3532
+ this.autoTrustOnReloadCwd = undefined;
3533
+ return true;
3534
+ }
3535
+ catch (error) {
3536
+ this.showWarning(`Could not save project trust after reload: ${error instanceof Error ? error.message : String(error)}`);
3537
+ return false;
3538
+ }
3539
+ }
3471
3540
  showTrustSelector() {
3472
3541
  const cwd = this.sessionManager.getCwd();
3473
3542
  const trustStore = new ProjectTrustStore(this.runtimeHost.services.agentDir);
3474
- const savedDecision = trustStore.get(cwd);
3543
+ const savedDecision = trustStore.getEntry(cwd);
3475
3544
  this.showSelector((done) => {
3476
3545
  const selector = new TrustSelectorComponent({
3477
3546
  cwd,
3478
3547
  savedDecision,
3479
3548
  projectTrusted: this.settingsManager.isProjectTrusted(),
3480
- onSelect: (trusted) => {
3481
- trustStore.set(cwd, trusted);
3549
+ onSelect: (selection) => {
3550
+ trustStore.setMany(selection.updates);
3482
3551
  done();
3483
- this.showStatus(`Saved trust decision: ${trusted ? "trusted" : "untrusted"}. Restart pi for this to take effect.`);
3552
+ this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart pi for this to take effect.`);
3484
3553
  },
3485
3554
  onCancel: () => {
3486
3555
  done();
@@ -3768,6 +3837,7 @@ export class InteractiveMode {
3768
3837
  try {
3769
3838
  const result = await this.runtimeHost.switchSession(sessionPath, {
3770
3839
  withSession: options?.withSession,
3840
+ projectTrustContextFactory: (cwd) => this.createProjectTrustContext(cwd),
3771
3841
  });
3772
3842
  if (result.cancelled) {
3773
3843
  return result;
@@ -3786,6 +3856,7 @@ export class InteractiveMode {
3786
3856
  const result = await this.runtimeHost.switchSession(sessionPath, {
3787
3857
  cwdOverride: selectedCwd,
3788
3858
  withSession: options?.withSession,
3859
+ projectTrustContextFactory: (cwd) => this.createProjectTrustContext(cwd),
3789
3860
  });
3790
3861
  if (result.cancelled) {
3791
3862
  return result;
@@ -4184,11 +4255,14 @@ export class InteractiveMode {
4184
4255
  force: false,
4185
4256
  showDiagnosticsWhenQuiet: true,
4186
4257
  });
4258
+ const savedImplicitProjectTrust = this.maybeSaveImplicitProjectTrustAfterReload();
4187
4259
  const modelsJsonError = this.session.modelRegistry.getError();
4188
4260
  if (modelsJsonError) {
4189
4261
  this.showError(`models.json error: ${modelsJsonError}`);
4190
4262
  }
4191
- this.showStatus("Reloaded keybindings, extensions, skills, prompts, themes");
4263
+ this.showStatus(savedImplicitProjectTrust
4264
+ ? "Reloaded keybindings, extensions, skills, prompts, themes; saved project trust"
4265
+ : "Reloaded keybindings, extensions, skills, prompts, themes");
4192
4266
  }
4193
4267
  catch (error) {
4194
4268
  dismissReloadBox(previousEditor);
@@ -4443,7 +4517,7 @@ export class InteractiveMode {
4443
4517
  const changelogMarkdown = allEntries.length > 0
4444
4518
  ? allEntries
4445
4519
  .reverse()
4446
- .map((e) => e.content)
4520
+ .map((e) => normalizeChangelogLinks(e.content, e))
4447
4521
  .join("\n\n")
4448
4522
  : "No changelog entries found.";
4449
4523
  this.chatContainer.addChild(new Spacer(1));
@@ -4510,7 +4584,7 @@ export class InteractiveMode {
4510
4584
  **Navigation**
4511
4585
  | Key | Action |
4512
4586
  |-----|--------|
4513
- | \`${cursorUp}\` / \`${cursorDown}\` / \`${cursorLeft}\` / \`${cursorRight}\` | Move cursor / browse history (Up when empty) |
4587
+ | \`${cursorUp}\` / \`${cursorDown}\` / \`${cursorLeft}\` / \`${cursorRight}\` | Move cursor / browse history |
4514
4588
  | \`${cursorWordLeft}\` / \`${cursorWordRight}\` | Move by word |
4515
4589
  | \`${cursorLineStart}\` | Start of line |
4516
4590
  | \`${cursorLineEnd}\` | End of line |
@@ -4727,7 +4801,6 @@ export class InteractiveMode {
4727
4801
  }
4728
4802
  }
4729
4803
  stop() {
4730
- this.unregisterSignalHandlers();
4731
4804
  if (this.settingsManager.getShowTerminalProgress()) {
4732
4805
  this.ui.terminal.setProgress(false);
4733
4806
  }
@@ -4745,6 +4818,7 @@ export class InteractiveMode {
4745
4818
  this.ui.stop();
4746
4819
  this.isInitialized = false;
4747
4820
  }
4821
+ this.unregisterSignalHandlers();
4748
4822
  }
4749
4823
  }
4750
4824
  //# sourceMappingURL=interactive-mode.js.map
@@ -512,46 +512,7 @@ function getAnsiColorLuminance(index) {
512
512
  export function getThemeForRgbColor(rgb) {
513
513
  return getRgbColorLuminance(rgb) >= 0.5 ? "light" : "dark";
514
514
  }
515
- function parseOscHexChannel(channel) {
516
- if (!/^[0-9a-f]+$/i.test(channel)) {
517
- return undefined;
518
- }
519
- const max = 16 ** channel.length - 1;
520
- if (max <= 0) {
521
- return undefined;
522
- }
523
- return Math.round((parseInt(channel, 16) / max) * 255);
524
- }
525
- export function parseOsc11BackgroundColor(data) {
526
- const match = data.match(/^\x1b\]11;([^\x07\x1b]*)(?:\x07|\x1b\\)$/i);
527
- if (!match) {
528
- return undefined;
529
- }
530
- const value = match[1].trim();
531
- if (value.startsWith("#")) {
532
- const hex = value.slice(1);
533
- if (/^[0-9a-f]{6}$/i.test(hex)) {
534
- return hexToRgb(value);
535
- }
536
- if (/^[0-9a-f]{12}$/i.test(hex)) {
537
- const r = parseOscHexChannel(hex.slice(0, 4));
538
- const g = parseOscHexChannel(hex.slice(4, 8));
539
- const b = parseOscHexChannel(hex.slice(8, 12));
540
- return r !== undefined && g !== undefined && b !== undefined ? { r, g, b } : undefined;
541
- }
542
- return undefined;
543
- }
544
- const rgbValue = value.replace(/^rgba?:/i, "");
545
- const [red, green, blue] = rgbValue.split("/");
546
- if (red === undefined || green === undefined || blue === undefined) {
547
- return undefined;
548
- }
549
- const r = parseOscHexChannel(red);
550
- const g = parseOscHexChannel(green);
551
- const b = parseOscHexChannel(blue);
552
- return r !== undefined && g !== undefined && b !== undefined ? { r, g, b } : undefined;
553
- }
554
- export function detectTerminalBackground(options = {}) {
515
+ export function detectTerminalBackgroundFromEnv(options = {}) {
555
516
  const env = options.env ?? process.env;
556
517
  const colorfgbg = env.COLORFGBG || "";
557
518
  const bg = getColorFgBgBackgroundIndex(colorfgbg);
@@ -570,8 +531,25 @@ export function detectTerminalBackground(options = {}) {
570
531
  confidence: "low",
571
532
  };
572
533
  }
534
+ export async function detectTerminalBackgroundTheme({ ui, timeoutMs, env, }) {
535
+ try {
536
+ const rgb = await ui.queryTerminalBackgroundColor({ timeoutMs });
537
+ if (rgb) {
538
+ return {
539
+ theme: getThemeForRgbColor(rgb),
540
+ source: "terminal background",
541
+ detail: `OSC 11 background rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`,
542
+ confidence: "high",
543
+ };
544
+ }
545
+ }
546
+ catch {
547
+ // Fall back to environment-based detection when the terminal query fails.
548
+ }
549
+ return detectTerminalBackgroundFromEnv({ env });
550
+ }
573
551
  export function getDefaultTheme() {
574
- return detectTerminalBackground().theme;
552
+ return detectTerminalBackgroundFromEnv().theme;
575
553
  }
576
554
  // ============================================================================
577
555
  // Global Theme Instance