mastracode 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/README.md +60 -12
  3. package/dist/agents/instructions.d.ts.map +1 -1
  4. package/dist/agents/model.d.ts +4 -1
  5. package/dist/agents/model.d.ts.map +1 -1
  6. package/dist/auth/claude-max-warning.d.ts +9 -0
  7. package/dist/auth/claude-max-warning.d.ts.map +1 -0
  8. package/dist/{chunk-7TFV3VBB.cjs → chunk-C4X3C2DL.cjs} +1091 -607
  9. package/dist/chunk-C4X3C2DL.cjs.map +1 -0
  10. package/dist/{chunk-7K5VFY2N.cjs → chunk-K4WJUBEC.cjs} +151 -48
  11. package/dist/chunk-K4WJUBEC.cjs.map +1 -0
  12. package/dist/{chunk-HHX6BKLR.js → chunk-M5LKPQB4.js} +12 -11
  13. package/dist/chunk-M5LKPQB4.js.map +1 -0
  14. package/dist/{chunk-V4HZ2AVV.js → chunk-REVOTI2T.js} +143 -38
  15. package/dist/chunk-REVOTI2T.js.map +1 -0
  16. package/dist/{chunk-C6XKRHRK.cjs → chunk-S5ZLN7DR.cjs} +6 -2
  17. package/dist/chunk-S5ZLN7DR.cjs.map +1 -0
  18. package/dist/{chunk-VRZZSUQE.js → chunk-SM3QCOA7.js} +6 -3
  19. package/dist/chunk-SM3QCOA7.js.map +1 -0
  20. package/dist/{chunk-LYETHS2L.js → chunk-X3BGE7CL.js} +877 -394
  21. package/dist/chunk-X3BGE7CL.js.map +1 -0
  22. package/dist/{chunk-VZFPT5N7.cjs → chunk-Z4QRXVST.cjs} +62 -61
  23. package/dist/chunk-Z4QRXVST.cjs.map +1 -0
  24. package/dist/cli.cjs +20 -10
  25. package/dist/cli.cjs.map +1 -1
  26. package/dist/cli.js +14 -4
  27. package/dist/cli.js.map +1 -1
  28. package/dist/docs/SKILL.md +30 -0
  29. package/dist/docs/assets/SOURCE_MAP.json +11 -0
  30. package/dist/docs/references/docs-mastra-code-configuration.md +299 -0
  31. package/dist/docs/references/docs-mastra-code-customization.md +228 -0
  32. package/dist/docs/references/docs-mastra-code-modes.md +104 -0
  33. package/dist/docs/references/docs-mastra-code-overview.md +135 -0
  34. package/dist/docs/references/docs-mastra-code-tools.md +229 -0
  35. package/dist/docs/references/reference-mastra-code-createMastraCode.md +108 -0
  36. package/dist/index.cjs +2 -2
  37. package/dist/index.js +1 -1
  38. package/dist/onboarding/onboarding-inline.d.ts +5 -0
  39. package/dist/onboarding/onboarding-inline.d.ts.map +1 -1
  40. package/dist/onboarding/settings.d.ts +3 -0
  41. package/dist/onboarding/settings.d.ts.map +1 -1
  42. package/dist/providers/openai-codex.d.ts +5 -1
  43. package/dist/providers/openai-codex.d.ts.map +1 -1
  44. package/dist/schema.d.ts +7 -1
  45. package/dist/schema.d.ts.map +1 -1
  46. package/dist/storage-PO4VN5NF.cjs +24 -0
  47. package/dist/{storage-JFFX7LJJ.cjs.map → storage-PO4VN5NF.cjs.map} +1 -1
  48. package/dist/storage-PQOHJLBI.js +3 -0
  49. package/dist/{storage-52Y5MKTG.js.map → storage-PQOHJLBI.js.map} +1 -1
  50. package/dist/tui/claude-max-warning.d.ts +18 -0
  51. package/dist/tui/claude-max-warning.d.ts.map +1 -0
  52. package/dist/tui/command-dispatch.d.ts.map +1 -1
  53. package/dist/tui/commands/index.d.ts +1 -0
  54. package/dist/tui/commands/index.d.ts.map +1 -1
  55. package/dist/tui/commands/login.d.ts.map +1 -1
  56. package/dist/tui/commands/models-pack.d.ts.map +1 -1
  57. package/dist/tui/commands/settings.d.ts.map +1 -1
  58. package/dist/tui/commands/skills.d.ts.map +1 -1
  59. package/dist/tui/commands/theme.d.ts +6 -0
  60. package/dist/tui/commands/theme.d.ts.map +1 -0
  61. package/dist/tui/commands/think.d.ts +1 -1
  62. package/dist/tui/commands/think.d.ts.map +1 -1
  63. package/dist/tui/components/banner.d.ts.map +1 -1
  64. package/dist/tui/components/diff-output.d.ts.map +1 -1
  65. package/dist/tui/components/help-overlay.d.ts.map +1 -1
  66. package/dist/tui/components/obi-loader.d.ts.map +1 -1
  67. package/dist/tui/components/settings.d.ts +1 -0
  68. package/dist/tui/components/settings.d.ts.map +1 -1
  69. package/dist/tui/components/shell-output.d.ts.map +1 -1
  70. package/dist/tui/components/task-progress.d.ts.map +1 -1
  71. package/dist/tui/components/thinking-settings.d.ts +11 -22
  72. package/dist/tui/components/thinking-settings.d.ts.map +1 -1
  73. package/dist/tui/components/tool-execution-enhanced.d.ts.map +1 -1
  74. package/dist/tui/detect-theme.d.ts +17 -0
  75. package/dist/tui/detect-theme.d.ts.map +1 -0
  76. package/dist/tui/event-dispatch.d.ts.map +1 -1
  77. package/dist/tui/handlers/agent-lifecycle.d.ts.map +1 -1
  78. package/dist/tui/index.d.ts +2 -2
  79. package/dist/tui/index.d.ts.map +1 -1
  80. package/dist/tui/mastra-tui.d.ts +8 -0
  81. package/dist/tui/mastra-tui.d.ts.map +1 -1
  82. package/dist/tui/render-messages.d.ts.map +1 -1
  83. package/dist/tui/setup.d.ts.map +1 -1
  84. package/dist/tui/status-line.d.ts.map +1 -1
  85. package/dist/tui/theme.d.ts +33 -20
  86. package/dist/tui/theme.d.ts.map +1 -1
  87. package/dist/tui.cjs +27 -19
  88. package/dist/tui.js +2 -2
  89. package/dist/utils/project.d.ts +5 -0
  90. package/dist/utils/project.d.ts.map +1 -1
  91. package/dist/utils/slash-command-processor.d.ts.map +1 -1
  92. package/package.json +8 -8
  93. package/dist/chunk-7K5VFY2N.cjs.map +0 -1
  94. package/dist/chunk-7TFV3VBB.cjs.map +0 -1
  95. package/dist/chunk-C6XKRHRK.cjs.map +0 -1
  96. package/dist/chunk-HHX6BKLR.js.map +0 -1
  97. package/dist/chunk-LYETHS2L.js.map +0 -1
  98. package/dist/chunk-V4HZ2AVV.js.map +0 -1
  99. package/dist/chunk-VRZZSUQE.js.map +0 -1
  100. package/dist/chunk-VZFPT5N7.cjs.map +0 -1
  101. package/dist/storage-52Y5MKTG.js +0 -3
  102. package/dist/storage-JFFX7LJJ.cjs +0 -24
@@ -1,9 +1,9 @@
1
- import { mastra, bg, bold, fg, getMarkdownTheme, theme, getEditorTheme, loadSettings, getAvailableModePacks, getAvailableOmPacks, ONBOARDING_VERSION, saveSettings, ThreadLockError, parseSubagentMeta, tintHex, getSelectListTheme, getSettingsListTheme } from './chunk-V4HZ2AVV.js';
2
- import { getOAuthProviders, detectProject, getUserId, PROVIDER_DEFAULT_MODELS } from './chunk-VRZZSUQE.js';
1
+ import { theme, mastra, getMarkdownTheme, getEditorTheme, loadSettings, saveSettings, getAvailableModePacks, getAvailableOmPacks, ONBOARDING_VERSION, ThreadLockError, parseSubagentMeta, tintHex, getSelectListTheme, getThemeMode, applyThemeMode, getSettingsListTheme } from './chunk-REVOTI2T.js';
2
+ import { getOAuthProviders, detectProject, getUserId, getCurrentGitBranch, PROVIDER_DEFAULT_MODELS } from './chunk-SM3QCOA7.js';
3
3
  import { getToolCategory, TOOL_CATEGORIES } from './chunk-U5A7TFNT.js';
4
- import { Box, Text, Spacer, Input, Container, fuzzyFilter, getEditorKeybindings, Markdown, ProcessTerminal, TUI, Editor, matchesKey, CombinedAutocompleteProvider, SelectList, visibleWidth, SettingsList } from '@mariozechner/pi-tui';
4
+ import { Box, Text, Spacer, Input, Container, fuzzyFilter, getEditorKeybindings, Markdown, ProcessTerminal, TUI, Editor, matchesKey, CombinedAutocompleteProvider, SelectList, visibleWidth, SettingsList, isKeyRelease } from '@mariozechner/pi-tui';
5
5
  import chalk10 from 'chalk';
6
- import { exec, execSync } from 'child_process';
6
+ import { exec, execFileSync, execSync } from 'child_process';
7
7
  import fs2, { readFileSync, unlinkSync, promises } from 'fs';
8
8
  import * as path4 from 'path';
9
9
  import path4__default, { join } from 'path';
@@ -14,6 +14,9 @@ import { highlight } from 'cli-highlight';
14
14
  import { parse } from 'partial-json';
15
15
  import * as yaml from 'js-yaml';
16
16
 
17
+ // src/auth/claude-max-warning.ts
18
+ var ANTHROPIC_OAUTH_PROVIDER_ID = "anthropic";
19
+ var CLAUDE_MAX_OAUTH_WARNING_MESSAGE = "OAuth with a Claude Max plan is a grey area. Anthropic has reportedly banned users for using Claude max credentials outside of Claude Code, and it may violate Anthropic Terms of Service. Proceed at your own risk.";
17
20
  var OnboardingInlineComponent = class extends Container {
18
21
  tui;
19
22
  options;
@@ -73,6 +76,10 @@ var OnboardingInlineComponent = class extends Container {
73
76
  this.selectedOmPack = packs[0];
74
77
  }
75
78
  }
79
+ /** Update whether the user has any provider access (e.g. after a login). */
80
+ updateHasProviderAccess(hasAccess) {
81
+ this.options.hasProviderAccess = hasAccess;
82
+ }
76
83
  // ---------------------------------------------------------------------------
77
84
  // Rendering helpers
78
85
  // ---------------------------------------------------------------------------
@@ -89,6 +96,8 @@ var OnboardingInlineComponent = class extends Container {
89
96
  return this.renderWelcome();
90
97
  case "auth":
91
98
  return this.renderAuth();
99
+ case "claudeMaxWarning":
100
+ return this.renderClaudeMaxWarning();
92
101
  case "modePack":
93
102
  return this.renderModePack();
94
103
  case "omPack":
@@ -115,14 +124,14 @@ var OnboardingInlineComponent = class extends Container {
115
124
  // ---------------------------------------------------------------------------
116
125
  renderWelcome() {
117
126
  const box = this.makeBox();
118
- box.addChild(new Text(bold(fg("accent", "\u{1F44B} Welcome to Mastra Code")), 0, 0));
127
+ box.addChild(new Text(theme.bold(theme.fg("accent", "\u{1F44B} Welcome to Mastra Code")), 0, 0));
119
128
  box.addChild(new Spacer(1));
120
- box.addChild(new Text(fg("text", "Let's configure your models and preferences."), 0, 0));
129
+ box.addChild(new Text(theme.fg("text", "Let's configure your models and preferences."), 0, 0));
121
130
  box.addChild(new Text(chalk10.white("You can re-run this anytime with /setup."), 0, 0));
122
131
  box.addChild(new Spacer(1));
123
132
  const items = [
124
- { value: "continue", label: ` ${fg("success", "Continue")}` },
125
- { value: "skip", label: ` ${fg("dim", "Skip")}` }
133
+ { value: "continue", label: ` ${theme.fg("success", "Continue")}` },
134
+ { value: "skip", label: ` ${theme.fg("dim", "Skip")}` }
126
135
  ];
127
136
  this.selectList = new SelectList(items, items.length, getSelectListTheme());
128
137
  this.selectList.onSelect = (item) => {
@@ -147,25 +156,31 @@ var OnboardingInlineComponent = class extends Container {
147
156
  // ---------------------------------------------------------------------------
148
157
  renderAuth() {
149
158
  const box = this.makeBox();
150
- box.addChild(new Text(bold(fg("accent", "\u{1F511} Authentication")), 0, 0));
159
+ box.addChild(new Text(theme.bold(theme.fg("accent", "\u{1F511} Authentication")), 0, 0));
151
160
  box.addChild(new Spacer(1));
152
161
  const providers = this.options.authProviders;
153
162
  if (providers.length === 0) {
154
- box.addChild(new Text(fg("dim", "No OAuth providers available. Skipping."), 0, 0));
163
+ box.addChild(new Text(theme.fg("dim", "No OAuth providers available. Skipping."), 0, 0));
155
164
  setTimeout(() => this.renderStep("modePack"), 100);
156
165
  return;
157
166
  }
158
- box.addChild(new Text(fg("text", "Log in with an AI provider to use your subscription:"), 0, 0));
167
+ box.addChild(new Text(theme.fg("text", "Log in with an AI provider to use your subscription,"), 0, 0));
168
+ box.addChild(new Text(theme.fg("text", "or skip if you have API keys configured as environment variables."), 0, 0));
159
169
  box.addChild(new Spacer(1));
160
170
  const items = providers.map((p) => ({
161
171
  value: p.value,
162
- label: p.loggedIn ? ` ${p.label} ${fg("success", "\u2713 logged in")}` : ` ${p.label}`
172
+ label: p.loggedIn ? ` ${p.label} ${theme.fg("success", "\u2713 logged in")}` : ` ${p.label}`
163
173
  }));
164
- items.push({ value: "__skip", label: ` ${fg("dim", "Skip (configure later with /login)")}` });
174
+ items.push({
175
+ value: "__skip",
176
+ label: ` ${theme.fg("dim", "Skip (use API keys or configure later with /login)")}`
177
+ });
165
178
  this.selectList = new SelectList(items, Math.min(items.length, 8), getSelectListTheme());
166
179
  this.selectList.onSelect = (item) => {
167
180
  if (item.value === "__skip") {
168
181
  this.renderStep("modePack");
182
+ } else if (item.value === ANTHROPIC_OAUTH_PROVIDER_ID) {
183
+ this.renderStep("claudeMaxWarning");
169
184
  } else {
170
185
  this.loginRequested = true;
171
186
  this.loginProvider = item.value;
@@ -179,7 +194,39 @@ var OnboardingInlineComponent = class extends Container {
179
194
  };
180
195
  box.addChild(this.selectList);
181
196
  box.addChild(new Spacer(1));
182
- box.addChild(new Text(fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc skip"), 0, 0));
197
+ box.addChild(new Text(theme.fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc skip"), 0, 0));
198
+ }
199
+ // ---------------------------------------------------------------------------
200
+ // Step: Claude Max OAuth Warning
201
+ // ---------------------------------------------------------------------------
202
+ renderClaudeMaxWarning() {
203
+ const box = this.makeBox();
204
+ box.addChild(new Text(theme.bold(theme.fg("warning", "\u26A0 Claude Max OAuth Warning")), 0, 0));
205
+ box.addChild(new Spacer(1));
206
+ box.addChild(new Text(theme.fg("text", CLAUDE_MAX_OAUTH_WARNING_MESSAGE), 0, 0));
207
+ box.addChild(new Spacer(1));
208
+ const items = [
209
+ { value: "continue", label: ` ${theme.fg("success", "Continue")}` },
210
+ { value: "cancel", label: ` ${theme.fg("dim", "Cancel")}` }
211
+ ];
212
+ this.selectList = new SelectList(items, items.length, getSelectListTheme());
213
+ this.selectList.onSelect = (item) => {
214
+ if (item.value === "continue") {
215
+ this.loginRequested = true;
216
+ this.loginProvider = ANTHROPIC_OAUTH_PROVIDER_ID;
217
+ this.options.onLogin(ANTHROPIC_OAUTH_PROVIDER_ID, () => {
218
+ this.renderStep("modePack");
219
+ });
220
+ } else {
221
+ this.renderStep("auth");
222
+ }
223
+ };
224
+ this.selectList.onCancel = () => {
225
+ this.renderStep("auth");
226
+ };
227
+ box.addChild(this.selectList);
228
+ box.addChild(new Spacer(1));
229
+ box.addChild(new Text(theme.fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc go back"), 0, 0));
183
230
  }
184
231
  // ---------------------------------------------------------------------------
185
232
  // Step: Mode pack
@@ -188,32 +235,33 @@ var OnboardingInlineComponent = class extends Container {
188
235
  modePackDetail;
189
236
  renderModePack() {
190
237
  const packs = this.options.modePacks;
191
- const hasProviderPack = packs.some((p) => !p.id.startsWith("custom"));
192
- if (!hasProviderPack) {
238
+ if (!this.options.hasProviderAccess) {
193
239
  const box2 = this.makeBox();
194
- box2.addChild(new Text(bold(fg("error", "No model providers configured")), 0, 0));
240
+ box2.addChild(new Text(theme.bold(theme.fg("error", "No model providers configured")), 0, 0));
195
241
  box2.addChild(new Spacer(1));
196
- box2.addChild(new Text(fg("text", "To use Mastra Code you need at least one API key or OAuth login"), 0, 0));
197
- box2.addChild(new Text(fg("text", "for Anthropic, OpenAI, or another supported provider."), 0, 0));
242
+ box2.addChild(new Text(theme.fg("text", "To use Mastra Code you need at least one API key or OAuth login"), 0, 0));
243
+ box2.addChild(new Text(theme.fg("text", "for Anthropic, OpenAI, or another supported provider."), 0, 0));
198
244
  box2.addChild(new Spacer(1));
199
245
  box2.addChild(
200
- new Text(fg("dim", "See https://mastra.ai/docs/models for supported providers and API key env vars."), 0, 0)
246
+ new Text(theme.fg("dim", "See https://mastra.ai/models for supported providers and API key env vars."), 0, 0)
201
247
  );
202
248
  box2.addChild(new Spacer(1));
203
- box2.addChild(new Text(fg("dim", "Set an API key and restart, or run /login to authenticate via OAuth."), 0, 0));
249
+ box2.addChild(
250
+ new Text(theme.fg("dim", "Set an API key and restart, or run /login to authenticate via OAuth."), 0, 0)
251
+ );
204
252
  this._finished = true;
205
253
  setTimeout(() => process.exit(1), 3e3);
206
254
  return;
207
255
  }
208
256
  const box = this.makeBox();
209
- box.addChild(new Text(bold(fg("accent", "Model Packs")), 0, 0));
257
+ box.addChild(new Text(theme.bold(theme.fg("accent", "Model Packs")), 0, 0));
210
258
  box.addChild(new Spacer(1));
211
- box.addChild(new Text(fg("text", "Choose default models for each mode (build / plan / fast):"), 0, 0));
259
+ box.addChild(new Text(theme.fg("text", "Choose default models for each mode (build / plan / fast):"), 0, 0));
212
260
  box.addChild(new Spacer(1));
213
261
  const prevId = this.options.previous?.modePackId ?? null;
214
262
  const items = packs.map((p) => ({
215
263
  value: p.id,
216
- label: ` ${p.name} ${fg("dim", p.description)}${p.id === prevId ? fg("dim", " (current)") : ""}`
264
+ label: ` ${p.name} ${theme.fg("dim", p.description)}${p.id === prevId ? theme.fg("dim", " (current)") : ""}`
217
265
  }));
218
266
  this.selectList = new SelectList(items, items.length, getSelectListTheme());
219
267
  const prevIdx = prevId ? packs.findIndex((p) => p.id === prevId) : -1;
@@ -224,12 +272,12 @@ var OnboardingInlineComponent = class extends Container {
224
272
  this.runCustomPackFlow();
225
273
  } else {
226
274
  this.selectedModePack = pack;
227
- this.collapseStep(`Model pack \u2192 ${bold(this.selectedModePack.name)}`);
275
+ this.collapseStep(`Model pack \u2192 ${theme.bold(this.selectedModePack.name)}`);
228
276
  this.renderStep("omPack");
229
277
  }
230
278
  };
231
279
  this.selectList.onCancel = () => {
232
- this.collapseStep(`Model pack \u2192 ${bold(this.selectedModePack.name)} (default)`);
280
+ this.collapseStep(`Model pack \u2192 ${theme.bold(this.selectedModePack.name)} (default)`);
233
281
  this.renderStep("omPack");
234
282
  };
235
283
  this.selectList.onSelectionChange = (item) => {
@@ -242,18 +290,18 @@ var OnboardingInlineComponent = class extends Container {
242
290
  const initialId = prevIdx > 0 ? packs[prevIdx].id : packs[0].id;
243
291
  this.updateModePackDetail(packs, initialId);
244
292
  box.addChild(new Spacer(1));
245
- box.addChild(new Text(fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc use default"), 0, 0));
293
+ box.addChild(new Text(theme.fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc use default"), 0, 0));
246
294
  }
247
295
  updateModePackDetail(packs, highlightedId) {
248
296
  const pack = packs.find((p) => p.id === highlightedId);
249
297
  if (!pack || !this.modePackDetail) return;
250
298
  if (pack.id === "custom") {
251
- this.modePackDetail.setText(fg("dim", " You'll pick a model for each mode in the next steps."));
299
+ this.modePackDetail.setText(theme.fg("dim", " You'll pick a model for each mode in the next steps."));
252
300
  } else {
253
301
  const detail = [
254
- ` ${chalk10.hex(mastra.blue)("plan")} \u2192 ${fg("text", pack.models.plan)}`,
255
- ` ${chalk10.hex(mastra.purple)("build")} \u2192 ${fg("text", pack.models.build)}`,
256
- ` ${chalk10.hex(mastra.green)("fast")} \u2192 ${fg("text", pack.models.fast)}`
302
+ ` ${chalk10.hex(mastra.blue)("plan")} \u2192 ${theme.fg("text", pack.models.plan)}`,
303
+ ` ${chalk10.hex(mastra.purple)("build")} \u2192 ${theme.fg("text", pack.models.build)}`,
304
+ ` ${chalk10.hex(mastra.green)("fast")} \u2192 ${theme.fg("text", pack.models.fast)}`
257
305
  ].join("\n");
258
306
  this.modePackDetail.setText(detail);
259
307
  }
@@ -277,7 +325,7 @@ var OnboardingInlineComponent = class extends Container {
277
325
  if (!modelId) {
278
326
  const fallback = this.options.modePacks.find((p) => p.id !== "custom") ?? this.options.modePacks[0];
279
327
  this.selectedModePack = fallback;
280
- this.collapseStep(`Model pack \u2192 ${bold(this.selectedModePack.name)} (cancelled custom)`);
328
+ this.collapseStep(`Model pack \u2192 ${theme.bold(this.selectedModePack.name)} (cancelled custom)`);
281
329
  this.renderStep("omPack");
282
330
  this.tui.requestRender();
283
331
  return;
@@ -291,7 +339,7 @@ var OnboardingInlineComponent = class extends Container {
291
339
  models: { build: models.build, plan: models.plan, fast: models.fast }
292
340
  };
293
341
  this.collapseStep(
294
- `Model pack \u2192 ${bold("Custom")} ${chalk10.hex(mastra.blue)("plan")} ${models.plan} ${chalk10.hex(mastra.purple)("build")} ${models.build} ${chalk10.hex(mastra.green)("fast")} ${models.fast}`
342
+ `Model pack \u2192 ${theme.bold("Custom")} ${chalk10.hex(mastra.blue)("plan")} ${models.plan} ${chalk10.hex(mastra.purple)("build")} ${models.build} ${chalk10.hex(mastra.green)("fast")} ${models.fast}`
295
343
  );
296
344
  this.renderStep("omPack");
297
345
  this.tui.requestRender();
@@ -306,15 +354,15 @@ var OnboardingInlineComponent = class extends Container {
306
354
  return;
307
355
  }
308
356
  const box = this.makeBox();
309
- box.addChild(new Text(bold(fg("accent", "\u{1F9E0} Observational Memory")), 0, 0));
357
+ box.addChild(new Text(theme.bold(theme.fg("accent", "\u{1F9E0} Observational Memory")), 0, 0));
310
358
  box.addChild(new Spacer(1));
311
- box.addChild(new Text(fg("text", "Choose the model for observational memory:"), 0, 0));
312
- box.addChild(new Text(fg("dim", "https://mastra.ai/docs/memory/observational-memory"), 0, 0));
359
+ box.addChild(new Text(theme.fg("text", "Choose the model for observational memory:"), 0, 0));
360
+ box.addChild(new Text(theme.fg("dim", "https://mastra.ai/docs/memory/observational-memory"), 0, 0));
313
361
  box.addChild(new Spacer(1));
314
362
  const prevOmId = this.options.previous?.omPackId ?? null;
315
363
  const items = omPacks.map((p) => ({
316
364
  value: p.id,
317
- label: ` ${p.name} ${fg("dim", p.description)}${p.id === prevOmId ? fg("dim", " (current)") : ""}`
365
+ label: ` ${p.name} ${theme.fg("dim", p.description)}${p.id === prevOmId ? theme.fg("dim", " (current)") : ""}`
318
366
  }));
319
367
  this.selectList = new SelectList(items, items.length, getSelectListTheme());
320
368
  const prevOmIdx = prevOmId ? omPacks.findIndex((p) => p.id === prevOmId) : -1;
@@ -325,32 +373,32 @@ var OnboardingInlineComponent = class extends Container {
325
373
  this.runCustomOmFlow();
326
374
  } else {
327
375
  this.selectedOmPack = pack;
328
- this.collapseStep(`Observational memory \u2192 ${bold(this.selectedOmPack.name)}`);
376
+ this.collapseStep(`Observational memory \u2192 ${theme.bold(this.selectedOmPack.name)}`);
329
377
  this.renderStep("yolo");
330
378
  }
331
379
  };
332
380
  this.selectList.onCancel = () => {
333
- this.collapseStep(`Observational memory \u2192 ${bold(this.selectedOmPack.name)} (default)`);
381
+ this.collapseStep(`Observational memory \u2192 ${theme.bold(this.selectedOmPack.name)} (default)`);
334
382
  this.renderStep("yolo");
335
383
  };
336
384
  box.addChild(this.selectList);
337
385
  box.addChild(new Spacer(1));
338
- box.addChild(new Text(fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc use default"), 0, 0));
386
+ box.addChild(new Text(theme.fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc use default"), 0, 0));
339
387
  }
340
388
  async runCustomOmFlow() {
341
389
  this.selectList = void 0;
342
- this.collapseStep(`Observational memory \u2192 ${bold("Custom")}`);
390
+ this.collapseStep(`Observational memory \u2192 ${theme.bold("Custom")}`);
343
391
  const modelId = await this.options.onSelectModel("Select model for observational memory");
344
392
  if (modelId) {
345
393
  this.selectedOmPack = { id: "custom", name: "Custom", description: "User-selected model", modelId };
346
- this.collapseStep(`Observational memory \u2192 ${bold("Custom")} ${modelId}`);
394
+ this.collapseStep(`Observational memory \u2192 ${theme.bold("Custom")} ${modelId}`);
347
395
  } else {
348
396
  const fallback = this.options.omPacks.find((p) => p.id !== "custom");
349
397
  if (fallback) {
350
398
  this.selectedOmPack = fallback;
351
- this.collapseStep(`Observational memory \u2192 ${bold(fallback.name)} (cancelled custom)`);
399
+ this.collapseStep(`Observational memory \u2192 ${theme.bold(fallback.name)} (cancelled custom)`);
352
400
  } else {
353
- this.collapseStep(`Observational memory \u2192 ${bold("Custom")} (cancelled)`);
401
+ this.collapseStep(`Observational memory \u2192 ${theme.bold("Custom")} (cancelled)`);
354
402
  }
355
403
  }
356
404
  this.renderStep("yolo");
@@ -361,22 +409,22 @@ var OnboardingInlineComponent = class extends Container {
361
409
  // ---------------------------------------------------------------------------
362
410
  renderYolo() {
363
411
  const box = this.makeBox();
364
- box.addChild(new Text(bold(fg("accent", "\u26A1 Tool Approval")), 0, 0));
412
+ box.addChild(new Text(theme.bold(theme.fg("accent", "\u26A1 Tool Approval")), 0, 0));
365
413
  box.addChild(new Spacer(1));
366
- box.addChild(new Text(fg("text", "YOLO mode auto-approves all tool calls (edits, commands, etc)."), 0, 0));
367
- box.addChild(new Text(fg("text", "You can toggle this anytime with Ctrl+Y or /yolo."), 0, 0));
414
+ box.addChild(new Text(theme.fg("text", "YOLO mode auto-approves all tool calls (edits, commands, etc)."), 0, 0));
415
+ box.addChild(new Text(theme.fg("text", "You can toggle this anytime with Ctrl+Y or /yolo."), 0, 0));
368
416
  box.addChild(new Spacer(1));
369
417
  const prevYolo = this.options.previous?.yolo ?? null;
370
- const currentOn = prevYolo === true ? fg("dim", " (current)") : "";
371
- const currentOff = prevYolo === false ? fg("dim", " (current)") : "";
418
+ const currentOn = prevYolo === true ? theme.fg("dim", " (current)") : "";
419
+ const currentOff = prevYolo === false ? theme.fg("dim", " (current)") : "";
372
420
  const items = [
373
421
  {
374
422
  value: "on",
375
- label: ` ${fg("success", "Enable YOLO")} ${fg("dim", "(recommended \u2014 auto-approve everything)")}${currentOn}`
423
+ label: ` ${theme.fg("success", "Enable YOLO")} ${theme.fg("dim", "(recommended \u2014 auto-approve everything)")}${currentOn}`
376
424
  },
377
425
  {
378
426
  value: "off",
379
- label: ` ${fg("warning", "Disable YOLO")} ${fg("dim", "(ask before each tool call)")}${currentOff}`
427
+ label: ` ${theme.fg("warning", "Disable YOLO")} ${theme.fg("dim", "(ask before each tool call)")}${currentOff}`
380
428
  }
381
429
  ];
382
430
  this.selectList = new SelectList(items, items.length, getSelectListTheme());
@@ -384,16 +432,16 @@ var OnboardingInlineComponent = class extends Container {
384
432
  this.selectList.onSelect = (item) => {
385
433
  this.selectedYolo = item.value === "on";
386
434
  const label = this.selectedYolo ? "enabled" : "disabled";
387
- this.collapseStep(`YOLO mode \u2192 ${bold(label)}`);
435
+ this.collapseStep(`YOLO mode \u2192 ${theme.bold(label)}`);
388
436
  this.renderStep("done");
389
437
  };
390
438
  this.selectList.onCancel = () => {
391
- this.collapseStep(`YOLO mode \u2192 ${bold("enabled")} (default)`);
439
+ this.collapseStep(`YOLO mode \u2192 ${theme.bold("enabled")} (default)`);
392
440
  this.renderStep("done");
393
441
  };
394
442
  box.addChild(this.selectList);
395
443
  box.addChild(new Spacer(1));
396
- box.addChild(new Text(fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc use default"), 0, 0));
444
+ box.addChild(new Text(theme.fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc use default"), 0, 0));
397
445
  }
398
446
  // ---------------------------------------------------------------------------
399
447
  // Step: Done
@@ -401,21 +449,21 @@ var OnboardingInlineComponent = class extends Container {
401
449
  renderDone() {
402
450
  this._finished = true;
403
451
  const box = this.makeBox();
404
- box.addChild(new Text(bold(fg("success", "\u2713 Setup complete!")), 0, 0));
452
+ box.addChild(new Text(theme.bold(theme.fg("success", "\u2713 Setup complete!")), 0, 0));
405
453
  box.addChild(new Spacer(1));
406
454
  const lines = [
407
- `Model pack: ${bold(this.selectedModePack.name)}`,
455
+ `Model pack: ${theme.bold(this.selectedModePack.name)}`,
408
456
  ` ${chalk10.hex(mastra.blue)("plan")} \u2192 ${this.selectedModePack.models.plan}`,
409
457
  ` ${chalk10.hex(mastra.purple)("build")} \u2192 ${this.selectedModePack.models.build}`,
410
458
  ` ${chalk10.hex(mastra.green)("fast")} \u2192 ${this.selectedModePack.models.fast}`,
411
- `Observational memory: ${bold(this.selectedOmPack.name)}`,
412
- `YOLO mode: ${bold(this.selectedYolo ? "enabled" : "disabled")}`
459
+ `Observational memory: ${theme.bold(this.selectedOmPack.name)}`,
460
+ `YOLO mode: ${theme.bold(this.selectedYolo ? "enabled" : "disabled")}`
413
461
  ];
414
462
  for (const line of lines) {
415
- box.addChild(new Text(fg("text", line), 0, 0));
463
+ box.addChild(new Text(theme.fg("text", line), 0, 0));
416
464
  }
417
465
  box.addChild(new Spacer(1));
418
- box.addChild(new Text(fg("dim", "Type a message to start coding, or use /help for commands."), 0, 0));
466
+ box.addChild(new Text(theme.fg("dim", "Type a message to start coding, or use /help for commands."), 0, 0));
419
467
  this.options.onComplete({
420
468
  modePack: this.selectedModePack,
421
469
  omPack: this.selectedOmPack,
@@ -431,7 +479,7 @@ var OnboardingInlineComponent = class extends Container {
431
479
  if (!this.stepBox) return;
432
480
  this.stepBox.clear();
433
481
  this.stepBox.setBgFn((text) => theme.bg("toolSuccessBg", text));
434
- this.stepBox.addChild(new Text(`${fg("success", "\u2713")} ${fg("text", summary)}`, 0, 0));
482
+ this.stepBox.addChild(new Text(`${theme.fg("success", "\u2713")} ${theme.fg("text", summary)}`, 0, 0));
435
483
  this.selectList = void 0;
436
484
  }
437
485
  // ---------------------------------------------------------------------------
@@ -445,22 +493,178 @@ var OnboardingInlineComponent = class extends Container {
445
493
  }
446
494
  }
447
495
  };
496
+ var AskQuestionInlineComponent = class extends Container {
497
+ contentBox;
498
+ selectList;
499
+ input;
500
+ onSubmit;
501
+ onCancel;
502
+ formatResult;
503
+ isNegativeAnswer;
504
+ answered = false;
505
+ questionText;
506
+ _focused = false;
507
+ get focused() {
508
+ return this._focused;
509
+ }
510
+ set focused(value) {
511
+ this._focused = value;
512
+ if (!this.answered && this.input) {
513
+ this.input.focused = value;
514
+ }
515
+ }
516
+ constructor(options, _ui) {
517
+ super();
518
+ this.onSubmit = options.onSubmit;
519
+ this.onCancel = options.onCancel;
520
+ this.formatResult = options.formatResult;
521
+ this.isNegativeAnswer = options.isNegativeAnswer;
522
+ this.questionText = options.question;
523
+ this.addChild(new Spacer(1));
524
+ this.contentBox = new Box(1, 1, (text) => theme.bg("toolPendingBg", text));
525
+ this.addChild(this.contentBox);
526
+ this.contentBox.addChild(new Text(theme.bold(theme.fg("accent", "\u2753 Question")), 0, 0));
527
+ this.contentBox.addChild(new Spacer(1));
528
+ for (const line of options.question.split("\n")) {
529
+ this.contentBox.addChild(new Text(theme.fg("text", line), 0, 0));
530
+ }
531
+ this.contentBox.addChild(new Spacer(1));
532
+ if (options.options && options.options.length > 0) {
533
+ this.buildSelectMode(options.options);
534
+ } else {
535
+ this.buildInputMode();
536
+ }
537
+ }
538
+ buildSelectMode(opts) {
539
+ const items = opts.map((opt) => ({
540
+ value: opt.label,
541
+ label: opt.description ? ` ${opt.label} ${theme.fg("dim", opt.description)}` : ` ${opt.label}`
542
+ }));
543
+ this.selectList = new SelectList(items, Math.min(items.length, 8), getSelectListTheme());
544
+ this.selectList.onSelect = (item) => {
545
+ this.handleAnswer(item.value);
546
+ };
547
+ this.selectList.onCancel = () => {
548
+ this.handleCancel();
549
+ };
550
+ this.contentBox.addChild(this.selectList);
551
+ this.contentBox.addChild(new Spacer(1));
552
+ this.contentBox.addChild(new Text(theme.fg("dim", "\u2191\u2193 to navigate \xB7 Enter to select \xB7 Esc to skip"), 0, 0));
553
+ }
554
+ buildInputMode() {
555
+ this.input = new Input();
556
+ this.input.onSubmit = (value) => {
557
+ const trimmed = value.trim();
558
+ if (trimmed) {
559
+ this.handleAnswer(trimmed);
560
+ }
561
+ };
562
+ this.contentBox.addChild(this.input);
563
+ this.contentBox.addChild(new Spacer(1));
564
+ this.contentBox.addChild(new Text(theme.fg("dim", "Enter to submit \xB7 Esc to skip"), 0, 0));
565
+ }
566
+ handleAnswer(answer) {
567
+ if (this.answered) return;
568
+ this.answered = true;
569
+ const isNegative = this.isNegativeAnswer?.(answer) ?? false;
570
+ this.contentBox.clear();
571
+ this.contentBox.setBgFn((text) => theme.bg(isNegative ? "toolErrorBg" : "toolSuccessBg", text));
572
+ const resultText = this.formatResult ? this.formatResult(answer) : `${this.questionText} \u2192 ${answer}`;
573
+ const icon = isNegative ? theme.fg("error", "\u2717") : theme.fg("success", "\u2713");
574
+ this.contentBox.addChild(new Text(theme.fg("text", `${icon} ${resultText}`), 0, 0));
575
+ this.onSubmit(answer);
576
+ }
577
+ handleCancel() {
578
+ if (this.answered) return;
579
+ this.answered = true;
580
+ this.contentBox.clear();
581
+ this.contentBox.setBgFn((text) => theme.bg("toolErrorBg", text));
582
+ this.contentBox.addChild(
583
+ new Text(theme.fg("dim", `${theme.fg("error", "\u2717")} ${this.questionText} (cancelled)`), 0, 0)
584
+ );
585
+ this.onCancel();
586
+ }
587
+ handleInput(data) {
588
+ if (this.answered) return;
589
+ if (this.selectList) {
590
+ this.selectList.handleInput(data);
591
+ } else if (this.input) {
592
+ const kb = getEditorKeybindings();
593
+ if (kb.matches(data, "selectCancel")) {
594
+ this.handleCancel();
595
+ return;
596
+ }
597
+ this.input.handleInput(data);
598
+ }
599
+ }
600
+ };
601
+
602
+ // src/tui/claude-max-warning.ts
603
+ function showClaudeMaxOAuthWarning(state, mode) {
604
+ const options = mode === "login" ? [
605
+ { label: "Continue", description: "Proceed with Anthropic OAuth" },
606
+ { label: "Cancel", description: "Go back" }
607
+ ] : [
608
+ { label: "Continue", description: "Keep Anthropic OAuth credentials" },
609
+ { label: "Remove OAuth", description: "Log out from Anthropic" }
610
+ ];
611
+ return new Promise((resolve2) => {
612
+ const questionComponent = new AskQuestionInlineComponent(
613
+ {
614
+ question: CLAUDE_MAX_OAUTH_WARNING_MESSAGE,
615
+ options,
616
+ formatResult: (answer) => `Claude Max OAuth warning \u2192 ${answer}`,
617
+ isNegativeAnswer: (answer) => answer !== "Continue",
618
+ onSubmit: (answer) => {
619
+ state.activeInlineQuestion = void 0;
620
+ if (answer === "Continue") {
621
+ resolve2("continue");
622
+ } else if (answer === "Cancel") {
623
+ resolve2("cancel");
624
+ } else {
625
+ resolve2("remove");
626
+ }
627
+ },
628
+ onCancel: () => {
629
+ state.activeInlineQuestion = void 0;
630
+ resolve2("cancel");
631
+ }
632
+ },
633
+ state.ui
634
+ );
635
+ state.activeInlineQuestion = questionComponent;
636
+ state.chatContainer.addChild(questionComponent);
637
+ state.chatContainer.addChild(new Spacer(1));
638
+ state.ui.requestRender();
639
+ state.chatContainer.invalidate();
640
+ });
641
+ }
448
642
  async function processSlashCommand(command, args, workingDir) {
449
- let result = command.template;
450
- result = replaceArguments(result, args);
643
+ const { result: withArgs, shouldAppendRawArgs } = replaceArguments(command.template, args);
644
+ let result = withArgs;
451
645
  result = await replaceShellOutput(result, workingDir);
452
646
  result = await replaceFileReferences(result, workingDir);
647
+ if (shouldAppendRawArgs) {
648
+ result = result.trimEnd() + `
649
+
650
+ ARGUMENTS: ${args.join(" ")}`;
651
+ }
453
652
  return result;
454
653
  }
455
654
  function replaceArguments(template, args) {
456
655
  let result = template;
656
+ const hasArgumentsVar = /\$ARGUMENTS/.test(template);
657
+ const hasPositionalVar = /\$[1-9]\d*/.test(template);
457
658
  result = result.replace(/\$ARGUMENTS/g, args.join(" "));
458
659
  args.forEach((arg, index) => {
459
660
  const pattern = new RegExp(`\\$${index + 1}`, "g");
460
661
  result = result.replace(pattern, arg);
461
662
  });
462
- result = result.replace(/\$\d+/g, "");
463
- return result;
663
+ result = result.replace(/\$[1-9]\d*/g, "");
664
+ return {
665
+ result,
666
+ shouldAppendRawArgs: !hasArgumentsVar && !hasPositionalVar && args.length > 0
667
+ };
464
668
  }
465
669
  async function replaceShellOutput(template, workingDir) {
466
670
  const shellPattern = /!`([^`]+)`/g;
@@ -525,7 +729,8 @@ function getCommands(modes) {
525
729
  { key: "/mcp", description: "Show/reload MCP connections" },
526
730
  { key: "/login", description: "Login with OAuth provider" },
527
731
  { key: "/logout", description: "Logout from OAuth provider" },
528
- { key: "/setup", description: "Run the setup wizard" }
732
+ { key: "/setup", description: "Run the setup wizard" },
733
+ { key: "/theme", description: "Switch color theme (auto/dark/light)" }
529
734
  ];
530
735
  if (modes > 1) {
531
736
  cmds.push({ key: "/mode", description: "Switch or list modes" });
@@ -605,22 +810,129 @@ function handleYoloCommand(ctx) {
605
810
  ctx.harness.setState({ yolo: !current });
606
811
  ctx.showInfo(!current ? "YOLO mode ON \u2014 tools auto-approved" : "YOLO mode OFF \u2014 tools require approval");
607
812
  }
813
+ var BASE_THINKING_LEVELS = [
814
+ { id: "off", label: "Off", providerValue: "none", description: "Reasoning disabled" },
815
+ { id: "low", label: "Low", providerValue: "low", description: "Light reasoning" },
816
+ { id: "medium", label: "Medium", providerValue: "medium", description: "Balanced reasoning" },
817
+ { id: "high", label: "High", providerValue: "high", description: "Deep reasoning" },
818
+ { id: "xhigh", label: "Very High", providerValue: "xhigh", description: "Maximum reasoning depth" }
819
+ ];
820
+ function isOpenAIModel(modelId) {
821
+ return modelId.startsWith("openai/");
822
+ }
823
+ function getThinkingLevelsForModel(modelId) {
824
+ if (!isOpenAIModel(modelId)) {
825
+ return [...BASE_THINKING_LEVELS];
826
+ }
827
+ return BASE_THINKING_LEVELS.map((level) => ({
828
+ ...level,
829
+ label: level.providerValue
830
+ }));
831
+ }
832
+ var THINKING_LEVELS = getThinkingLevelsForModel("");
833
+ function getThinkingLevelForModel(modelId, levelId) {
834
+ return getThinkingLevelsForModel(modelId).find((level) => level.id === levelId) ?? getThinkingLevelsForModel(modelId)[0];
835
+ }
608
836
 
609
837
  // src/tui/commands/think.ts
610
- async function handleThinkCommand(ctx) {
838
+ function supportsThinking(modelId) {
839
+ return modelId.startsWith("openai/");
840
+ }
841
+ function getThinkingStatusLine(modelId, levelId) {
842
+ const level = getThinkingLevelForModel(modelId, levelId);
843
+ return `Thinking: ${level.label}`;
844
+ }
845
+ function getModelNote(ctx) {
846
+ const modelId = ctx.state.harness.getCurrentModelId() ?? "";
847
+ if (!modelId) return "No model selected.";
848
+ if (!supportsThinking(modelId)) {
849
+ return `Warning: current model (${modelId}) may not support reasoning effort. Setting will be saved but may not take effect.`;
850
+ }
851
+ return null;
852
+ }
853
+ async function handleThinkCommand(ctx, args = []) {
611
854
  const currentLevel = ctx.harness.getState()?.thinkingLevel ?? "off";
612
- const levels = [
613
- { label: "Off", id: "off" },
614
- { label: "Minimal", id: "minimal" },
615
- { label: "Low", id: "low" },
616
- { label: "Medium", id: "medium" },
617
- { label: "High", id: "high" }
618
- ];
619
- const currentIdx = levels.findIndex((l) => l.id === currentLevel);
620
- const nextIdx = (currentIdx + 1) % levels.length;
621
- const next = levels[nextIdx];
622
- await ctx.harness.setState({ thinkingLevel: next.id });
623
- ctx.showInfo(`Thinking: ${next.label}`);
855
+ const modelId = ctx.state.harness.getCurrentModelId() ?? "";
856
+ const thinkingLevels = getThinkingLevelsForModel(modelId);
857
+ const arg = args[0]?.toLowerCase();
858
+ if (arg === "status") {
859
+ ctx.showInfo(getThinkingStatusLine(modelId, currentLevel));
860
+ return;
861
+ }
862
+ if (arg) {
863
+ const selected = thinkingLevels.find((l) => l.id === arg);
864
+ if (!selected) {
865
+ ctx.showInfo(
866
+ `Invalid thinking level: ${arg}. Use one of: ${THINKING_LEVELS.map((l) => l.id).join(", ")} or 'status'.`
867
+ );
868
+ return;
869
+ }
870
+ const note = getModelNote(ctx);
871
+ await ctx.harness.setState({ thinkingLevel: selected.id });
872
+ ctx.showInfo(getThinkingStatusLine(modelId, selected.id) + (note ? ` (${note})` : ""));
873
+ return;
874
+ }
875
+ const items = thinkingLevels.map((l) => ({
876
+ value: l.id,
877
+ label: ` ${l.label} ${theme.fg("dim", l.description)}${l.id === currentLevel ? theme.fg("dim", " (current)") : ""}`
878
+ }));
879
+ const modelNote = getModelNote(ctx);
880
+ return new Promise((resolve2) => {
881
+ const container = new Box(1, 1);
882
+ container.addChild(new Text(theme.bold(theme.fg("accent", "Thinking Level")), 0, 0));
883
+ container.addChild(new Spacer(1));
884
+ if (modelNote) {
885
+ container.addChild(new Text(theme.fg("warning", modelNote), 0, 0));
886
+ container.addChild(new Spacer(1));
887
+ }
888
+ const selectList = new SelectList(items, items.length, getSelectListTheme());
889
+ selectList.onSelect = async (item) => {
890
+ ctx.state.activeInlineQuestion = void 0;
891
+ try {
892
+ await ctx.harness.setState({ thinkingLevel: item.value });
893
+ const selectedLabel = getThinkingLevelForModel(modelId, item.value).label;
894
+ collapseResult(
895
+ `Thinking \u2192 ${theme.bold(item.value === currentLevel ? `${selectedLabel} (unchanged)` : selectedLabel)}`
896
+ );
897
+ } catch {
898
+ collapseResult("cancelled");
899
+ } finally {
900
+ ctx.state.ui.requestRender();
901
+ resolve2();
902
+ }
903
+ };
904
+ selectList.onCancel = () => {
905
+ ctx.state.activeInlineQuestion = void 0;
906
+ collapseResult("cancelled");
907
+ ctx.state.ui.requestRender();
908
+ resolve2();
909
+ };
910
+ container.addChild(selectList);
911
+ container.addChild(new Spacer(1));
912
+ container.addChild(new Text(theme.fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc cancel"), 0, 0));
913
+ const currentIdx = thinkingLevels.findIndex((l) => l.id === currentLevel);
914
+ if (currentIdx > 0) selectList.setSelectedIndex(currentIdx);
915
+ const collapseResult = (result) => {
916
+ container.clear();
917
+ if (result === "cancelled") {
918
+ container.addChild(new Text(theme.fg("dim", `${theme.fg("error", "\u2717")} Thinking level (cancelled)`), 0, 0));
919
+ } else {
920
+ container.addChild(new Text(theme.fg("text", `${theme.fg("success", "\u2713")} ${result}`), 0, 0));
921
+ }
922
+ };
923
+ const inputShim = {
924
+ handleInput: (data) => {
925
+ if (isKeyRelease(data)) return;
926
+ selectList.handleInput(data);
927
+ }
928
+ };
929
+ ctx.state.activeInlineQuestion = inputShim;
930
+ ctx.state.chatContainer.addChild(new Spacer(1));
931
+ ctx.state.chatContainer.addChild(container);
932
+ ctx.state.chatContainer.addChild(new Spacer(1));
933
+ ctx.state.ui.requestRender();
934
+ ctx.state.chatContainer.invalidate();
935
+ });
624
936
  }
625
937
 
626
938
  // src/tui/commands/permissions.ts
@@ -854,7 +1166,15 @@ ${modeList}`);
854
1166
 
855
1167
  // src/tui/commands/skills.ts
856
1168
  async function handleSkillsCommand(ctx) {
857
- const workspace = ctx.getResolvedWorkspace();
1169
+ let workspace = ctx.getResolvedWorkspace();
1170
+ if (!workspace && ctx.harness.hasWorkspace()) {
1171
+ try {
1172
+ workspace = await ctx.harness.resolveWorkspace();
1173
+ } catch (error) {
1174
+ ctx.showError(`Failed to resolve workspace: ${error instanceof Error ? error.message : String(error)}`);
1175
+ return;
1176
+ }
1177
+ }
858
1178
  if (!workspace?.skills) {
859
1179
  ctx.showInfo(
860
1180
  "No skills configured.\n\nAdd skills to any of these locations:\n .mastracode/skills/ (project-local)\n .claude/skills/ (project-local)\n ~/.mastracode/skills/ (global)\n ~/.claude/skills/ (global)\n\nEach skill is a folder with a SKILL.md file.\nInstall skills: npx add-skill <github-url>"
@@ -933,12 +1253,13 @@ async function handleResourceCommand(ctx, args) {
933
1253
  state.ui.requestRender();
934
1254
  ctx.showInfo(sub === "reset" ? `Resource ID reset to: ${defaultId}` : `Switched to resource: ${newId}`);
935
1255
  }
936
- var addedColor = chalk10.hex("#5cb85c");
937
- var hunkHeaderColor = chalk10.hex("#61afef");
938
- var fileHeaderColor = chalk10.bold.hex("#c678dd");
939
- var removedColor = chalk10.hex(mastra.red);
940
- var metaColor = chalk10.hex(mastra.mainGray);
941
1256
  function colorizeDiffLine(line) {
1257
+ const t = theme.getTheme();
1258
+ const addedColor = chalk10.hex(t.success);
1259
+ const hunkHeaderColor = chalk10.hex(t.toolBorderPending);
1260
+ const fileHeaderColor = chalk10.bold.hex(t.accent);
1261
+ const removedColor = chalk10.hex(mastra.red);
1262
+ const metaColor = chalk10.hex(mastra.mainGray);
942
1263
  if (line.startsWith("+++") || line.startsWith("---")) {
943
1264
  return fileHeaderColor(line);
944
1265
  }
@@ -957,7 +1278,7 @@ function colorizeDiffLine(line) {
957
1278
  if (line.startsWith("index ") || line.startsWith("new file") || line.startsWith("deleted file") || line.startsWith("similarity") || line.startsWith("rename")) {
958
1279
  return metaColor(line);
959
1280
  }
960
- const statMatch = line.match(/^(.+\|.+?)(\++)([-]*)$/);
1281
+ const statMatch = line.match(/^(.+\|.+?)(\++)([- ]*)$/);
961
1282
  if (statMatch) {
962
1283
  return statMatch[1] + addedColor(statMatch[2]) + removedColor(statMatch[3]);
963
1284
  }
@@ -970,7 +1291,9 @@ var DiffOutputComponent = class extends Container {
970
1291
  constructor(command, diffOutput) {
971
1292
  super();
972
1293
  this.addChild(new Spacer(1));
973
- this.addChild(new Text(`${fg("success", "\u2713")} ${bold(fg("muted", "$"))} ${fg("text", command)}`, 1, 0));
1294
+ this.addChild(
1295
+ new Text(`${theme.fg("success", "\u2713")} ${theme.bold(theme.fg("muted", "$"))} ${theme.fg("text", command)}`, 1, 0)
1296
+ );
974
1297
  const output = diffOutput.trimEnd();
975
1298
  if (output) {
976
1299
  const lines = output.split("\n");
@@ -1045,117 +1368,12 @@ async function handleDiffCommand(ctx, filePath) {
1045
1368
  opCounts.set(op, (opCounts.get(op) || 0) + 1);
1046
1369
  }
1047
1370
  const ops = Array.from(opCounts.entries()).map(([op, count]) => count > 1 ? `${op}\xD7${count}` : op).join(", ");
1048
- lines.push(` ${fg("path", fp)} ${fg("muted", `(${ops})`)}`);
1371
+ lines.push(` ${theme.fg("path", fp)} ${theme.fg("muted", `(${ops})`)}`);
1049
1372
  }
1050
1373
  lines.push("");
1051
- lines.push(fg("muted", "Use /diff <path> to see the git diff for a specific file."));
1374
+ lines.push(theme.fg("muted", "Use /diff <path> to see the git diff for a specific file."));
1052
1375
  ctx.showInfo(lines.join("\n"));
1053
1376
  }
1054
- var AskQuestionInlineComponent = class extends Container {
1055
- contentBox;
1056
- selectList;
1057
- input;
1058
- onSubmit;
1059
- onCancel;
1060
- formatResult;
1061
- isNegativeAnswer;
1062
- answered = false;
1063
- questionText;
1064
- _focused = false;
1065
- get focused() {
1066
- return this._focused;
1067
- }
1068
- set focused(value) {
1069
- this._focused = value;
1070
- if (!this.answered && this.input) {
1071
- this.input.focused = value;
1072
- }
1073
- }
1074
- constructor(options, _ui) {
1075
- super();
1076
- this.onSubmit = options.onSubmit;
1077
- this.onCancel = options.onCancel;
1078
- this.formatResult = options.formatResult;
1079
- this.isNegativeAnswer = options.isNegativeAnswer;
1080
- this.questionText = options.question;
1081
- this.addChild(new Spacer(1));
1082
- this.contentBox = new Box(1, 1, (text) => theme.bg("toolPendingBg", text));
1083
- this.addChild(this.contentBox);
1084
- this.contentBox.addChild(new Text(theme.bold(theme.fg("accent", "\u2753 Question")), 0, 0));
1085
- this.contentBox.addChild(new Spacer(1));
1086
- for (const line of options.question.split("\n")) {
1087
- this.contentBox.addChild(new Text(theme.fg("text", line), 0, 0));
1088
- }
1089
- this.contentBox.addChild(new Spacer(1));
1090
- if (options.options && options.options.length > 0) {
1091
- this.buildSelectMode(options.options);
1092
- } else {
1093
- this.buildInputMode();
1094
- }
1095
- }
1096
- buildSelectMode(opts) {
1097
- const items = opts.map((opt) => ({
1098
- value: opt.label,
1099
- label: opt.description ? ` ${opt.label} ${theme.fg("dim", opt.description)}` : ` ${opt.label}`
1100
- }));
1101
- this.selectList = new SelectList(items, Math.min(items.length, 8), getSelectListTheme());
1102
- this.selectList.onSelect = (item) => {
1103
- this.handleAnswer(item.value);
1104
- };
1105
- this.selectList.onCancel = () => {
1106
- this.handleCancel();
1107
- };
1108
- this.contentBox.addChild(this.selectList);
1109
- this.contentBox.addChild(new Spacer(1));
1110
- this.contentBox.addChild(new Text(theme.fg("dim", "\u2191\u2193 to navigate \xB7 Enter to select \xB7 Esc to skip"), 0, 0));
1111
- }
1112
- buildInputMode() {
1113
- this.input = new Input();
1114
- this.input.onSubmit = (value) => {
1115
- const trimmed = value.trim();
1116
- if (trimmed) {
1117
- this.handleAnswer(trimmed);
1118
- }
1119
- };
1120
- this.contentBox.addChild(this.input);
1121
- this.contentBox.addChild(new Spacer(1));
1122
- this.contentBox.addChild(new Text(theme.fg("dim", "Enter to submit \xB7 Esc to skip"), 0, 0));
1123
- }
1124
- handleAnswer(answer) {
1125
- if (this.answered) return;
1126
- this.answered = true;
1127
- const isNegative = this.isNegativeAnswer?.(answer) ?? false;
1128
- this.contentBox.clear();
1129
- this.contentBox.setBgFn((text) => theme.bg(isNegative ? "toolErrorBg" : "toolSuccessBg", text));
1130
- const resultText = this.formatResult ? this.formatResult(answer) : `${this.questionText} \u2192 ${answer}`;
1131
- const icon = isNegative ? theme.fg("error", "\u2717") : theme.fg("success", "\u2713");
1132
- this.contentBox.addChild(new Text(theme.fg("text", `${icon} ${resultText}`), 0, 0));
1133
- this.onSubmit(answer);
1134
- }
1135
- handleCancel() {
1136
- if (this.answered) return;
1137
- this.answered = true;
1138
- this.contentBox.clear();
1139
- this.contentBox.setBgFn((text) => theme.bg("toolErrorBg", text));
1140
- this.contentBox.addChild(
1141
- new Text(theme.fg("dim", `${theme.fg("error", "\u2717")} ${this.questionText} (cancelled)`), 0, 0)
1142
- );
1143
- this.onCancel();
1144
- }
1145
- handleInput(data) {
1146
- if (this.answered) return;
1147
- if (this.selectList) {
1148
- this.selectList.handleInput(data);
1149
- } else if (this.input) {
1150
- const kb = getEditorKeybindings();
1151
- if (kb.matches(data, "selectCancel")) {
1152
- this.handleCancel();
1153
- return;
1154
- }
1155
- this.input.handleInput(data);
1156
- }
1157
- }
1158
- };
1159
1377
  var ThreadSelectorComponent = class extends Box {
1160
1378
  searchInput;
1161
1379
  listContainer;
@@ -1179,7 +1397,7 @@ var ThreadSelectorComponent = class extends Box {
1179
1397
  this.searchInput.focused = value;
1180
1398
  }
1181
1399
  constructor(options) {
1182
- super(2, 1, (text) => bg("overlayBg", text));
1400
+ super(2, 1, (text) => theme.bg("overlayBg", text));
1183
1401
  this.tui = options.tui;
1184
1402
  this.currentResourceId = options.currentResourceId;
1185
1403
  this.allThreads = this.sortThreads(options.threads, options.currentThreadId);
@@ -1206,9 +1424,9 @@ var ThreadSelectorComponent = class extends Box {
1206
1424
  this.tui.requestRender();
1207
1425
  }
1208
1426
  buildUI() {
1209
- this.addChild(new Text(bold(fg("accent", "Select Thread")), 0, 0));
1427
+ this.addChild(new Text(theme.bold(theme.fg("accent", "Select Thread")), 0, 0));
1210
1428
  this.addChild(new Spacer(1));
1211
- this.addChild(new Text(fg("muted", "Type to search \u2022 \u2191\u2193 navigate \u2022 Enter select \u2022 Esc cancel"), 0, 0));
1429
+ this.addChild(new Text(theme.fg("muted", "Type to search \u2022 \u2191\u2193 navigate \u2022 Enter select \u2022 Esc cancel"), 0, 0));
1212
1430
  this.addChild(new Spacer(1));
1213
1431
  this.searchInput = new Input();
1214
1432
  this.searchInput.onSubmit = () => {
@@ -1271,33 +1489,33 @@ var ThreadSelectorComponent = class extends Box {
1271
1489
  if (!thread) continue;
1272
1490
  const isSelected = i === this.selectedIndex;
1273
1491
  const isCurrent = thread.id === this.currentThreadId;
1274
- const checkmark = isCurrent ? fg("success", " \u2713") : "";
1492
+ const checkmark = isCurrent ? theme.fg("success", " \u2713") : "";
1275
1493
  const shortId = thread.id.slice(-6);
1276
1494
  const threadPath = thread.metadata?.projectPath;
1277
- const pathTag = threadPath ? fg("dim", ` [${threadPath.split("/").pop()}]`) : "";
1495
+ const pathTag = threadPath ? theme.fg("dim", ` [${threadPath.split("/").pop()}]`) : "";
1278
1496
  const displayId = `${thread.resourceId}/${shortId}`;
1279
- const timeAgo = fg("muted", ` (${this.formatTimeAgo(thread.updatedAt)})`);
1497
+ const timeAgo = theme.fg("muted", ` (${this.formatTimeAgo(thread.updatedAt)})`);
1280
1498
  const hasCustomTitle = thread.title && thread.title !== "New Thread";
1281
1499
  let line = "";
1282
1500
  if (isSelected) {
1283
- line = fg("accent", `\u2192 ${displayId}`) + pathTag + timeAgo + checkmark;
1501
+ line = theme.fg("accent", `\u2192 ${displayId}`) + pathTag + timeAgo + checkmark;
1284
1502
  } else {
1285
1503
  line = ` ${displayId}` + pathTag + timeAgo + checkmark;
1286
1504
  }
1287
1505
  this.listContainer.addChild(new Text(line, 0, 0));
1288
1506
  const preview = this.messagePreviews.get(thread.id);
1289
1507
  if (preview) {
1290
- this.listContainer.addChild(new Text(` ${fg("muted", `"${preview}"`)}`, 0, 0));
1508
+ this.listContainer.addChild(new Text(` ${theme.fg("muted", `"${preview}"`)}`, 0, 0));
1291
1509
  } else if (hasCustomTitle) {
1292
- this.listContainer.addChild(new Text(` ${fg("muted", `"${thread.title}"`)}`, 0, 0));
1510
+ this.listContainer.addChild(new Text(` ${theme.fg("muted", `"${thread.title}"`)}`, 0, 0));
1293
1511
  }
1294
1512
  }
1295
1513
  if (startIndex > 0 || endIndex < this.filteredThreads.length) {
1296
- const scrollInfo = fg("muted", `(${this.selectedIndex + 1}/${this.filteredThreads.length})`);
1514
+ const scrollInfo = theme.fg("muted", `(${this.selectedIndex + 1}/${this.filteredThreads.length})`);
1297
1515
  this.listContainer.addChild(new Text(scrollInfo, 0, 0));
1298
1516
  }
1299
1517
  if (this.filteredThreads.length === 0) {
1300
- this.listContainer.addChild(new Text(fg("muted", "No matching threads"), 0, 0));
1518
+ this.listContainer.addChild(new Text(theme.fg("muted", "No matching threads"), 0, 0));
1301
1519
  }
1302
1520
  }
1303
1521
  handleInput(keyData) {
@@ -1448,7 +1666,7 @@ async function handleThreadTagDirCommand(ctx) {
1448
1666
  const questionComponent = new AskQuestionInlineComponent(
1449
1667
  {
1450
1668
  question: `Tag this thread with directory "${dirName}"?
1451
- ${fg("dim", projectPath)}`,
1669
+ ${theme.fg("dim", projectPath)}`,
1452
1670
  options: [{ label: "Yes" }, { label: "No" }],
1453
1671
  formatResult: (answer) => answer === "Yes" ? `Tagged thread with: ${dirName}` : `Thread not tagged`,
1454
1672
  onSubmit: async (answer) => {
@@ -1618,7 +1836,7 @@ var ModelSelectorComponent = class extends Box {
1618
1836
  this.searchInput.focused = value;
1619
1837
  }
1620
1838
  constructor(options) {
1621
- super(2, 1, (text) => bg("overlayBg", text));
1839
+ super(2, 1, (text) => theme.bg("overlayBg", text));
1622
1840
  this.tui = options.tui;
1623
1841
  this.title = options.title ?? "Select Model";
1624
1842
  this.titleColor = options.titleColor;
@@ -1630,10 +1848,10 @@ var ModelSelectorComponent = class extends Box {
1630
1848
  this.buildUI();
1631
1849
  }
1632
1850
  buildUI() {
1633
- const titleText = this.titleColor ? chalk10.bgHex(this.titleColor).white.bold(` ${this.title} `) : bold(fg("accent", this.title));
1851
+ const titleText = this.titleColor ? chalk10.bgHex(this.titleColor).white.bold(` ${this.title} `) : theme.bold(theme.fg("accent", this.title));
1634
1852
  this.addChild(new Text(titleText, 0, 0));
1635
1853
  this.addChild(new Spacer(1));
1636
- this.addChild(new Text(fg("muted", "Type to search \u2022 \u2191\u2193 navigate \u2022 Enter select \u2022 Esc cancel"), 0, 0));
1854
+ this.addChild(new Text(theme.fg("muted", "Type to search \u2022 \u2191\u2193 navigate \u2022 Enter select \u2022 Esc cancel"), 0, 0));
1637
1855
  this.addChild(new Spacer(1));
1638
1856
  this.searchInput = new Input();
1639
1857
  this.searchInput.onSubmit = () => {
@@ -1696,7 +1914,7 @@ var ModelSelectorComponent = class extends Box {
1696
1914
  if (this.hasCustomItem && i === 0) {
1697
1915
  const query = this.searchInput.getValue().trim();
1698
1916
  const isSelected2 = this.selectedIndex === 0;
1699
- const line2 = isSelected2 ? fg("accent", "\u2192 ") + bold(fg("accent", `Use: ${query}`)) : " " + fg("muted", `Use: ${query}`);
1917
+ const line2 = isSelected2 ? theme.fg("accent", "\u2192 ") + theme.bold(theme.fg("accent", `Use: ${query}`)) : " " + theme.fg("muted", `Use: ${query}`);
1700
1918
  this.listContainer.addChild(new Text(line2, 0, 0));
1701
1919
  continue;
1702
1920
  }
@@ -1705,23 +1923,23 @@ var ModelSelectorComponent = class extends Box {
1705
1923
  if (!item) continue;
1706
1924
  const isSelected = i === this.selectedIndex;
1707
1925
  const isCurrent = item.id === this.currentModelId;
1708
- const checkmark = isCurrent ? fg("success", " \u2713") : "";
1709
- const noKeyIndicator = !item.hasApiKey ? fg("error", " \u2717") + fg("muted", item.apiKeyEnvVar ? ` (${item.apiKeyEnvVar})` : " (no key)") : "";
1926
+ const checkmark = isCurrent ? theme.fg("success", " \u2713") : "";
1927
+ const noKeyIndicator = !item.hasApiKey ? theme.fg("error", " \u2717") + theme.fg("muted", item.apiKeyEnvVar ? ` (${item.apiKeyEnvVar})` : " (no key)") : "";
1710
1928
  let line = "";
1711
1929
  if (isSelected) {
1712
- line = fg("accent", "\u2192 " + item.id) + checkmark + noKeyIndicator;
1930
+ line = theme.fg("accent", "\u2192 " + item.id) + checkmark + noKeyIndicator;
1713
1931
  } else {
1714
- const modelText = item.hasApiKey ? item.id : fg("muted", item.id);
1932
+ const modelText = item.hasApiKey ? item.id : theme.fg("muted", item.id);
1715
1933
  line = " " + modelText + checkmark + noKeyIndicator;
1716
1934
  }
1717
1935
  this.listContainer.addChild(new Text(line, 0, 0));
1718
1936
  }
1719
1937
  if (startIndex > 0 || endIndex < totalItems) {
1720
- const scrollInfo = fg("muted", `(${this.selectedIndex + 1}/${totalItems})`);
1938
+ const scrollInfo = theme.fg("muted", `(${this.selectedIndex + 1}/${totalItems})`);
1721
1939
  this.listContainer.addChild(new Text(scrollInfo, 0, 0));
1722
1940
  }
1723
1941
  if (totalItems === 0) {
1724
- this.listContainer.addChild(new Text(fg("muted", "No matching models"), 0, 0));
1942
+ this.listContainer.addChild(new Text(theme.fg("muted", "No matching models"), 0, 0));
1725
1943
  }
1726
1944
  }
1727
1945
  handleInput(keyData) {
@@ -1906,7 +2124,9 @@ async function handleModelsCommand(ctx) {
1906
2124
  }
1907
2125
  var GRADIENT_WIDTH = 30;
1908
2126
  var BASE_COLOR = [124, 58, 237];
1909
- var MIN_BRIGHTNESS = 0.45;
2127
+ function getMinBrightness() {
2128
+ return getThemeMode() === "dark" ? 0.45 : 0.55;
2129
+ }
1910
2130
  function hexToRgb(hex) {
1911
2131
  const h = hex.replace("#", "");
1912
2132
  return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
@@ -1926,7 +2146,8 @@ function applyGradientSweep(text, offset, color, fadeProgress = 0) {
1926
2146
  distance = 100 - distance;
1927
2147
  }
1928
2148
  const normalizedDistance = Math.min(distance / (GRADIENT_WIDTH / 2), 1);
1929
- const animBrightness = MIN_BRIGHTNESS + (1 - MIN_BRIGHTNESS) * (1 - normalizedDistance);
2149
+ const minBrightness = getMinBrightness();
2150
+ const animBrightness = minBrightness + (1 - minBrightness) * (1 - normalizedDistance);
1930
2151
  const brightness = animBrightness + (IDLE_BRIGHTNESS - animBrightness) * fadeProgress;
1931
2152
  const r = Math.floor(baseColor[0] * brightness);
1932
2153
  const g = Math.floor(baseColor[1] * brightness);
@@ -2063,7 +2284,7 @@ var OMProgressComponent = class extends Container {
2063
2284
  if (this.state.thresholdPercent > 0) {
2064
2285
  const percent = Math.round(this.state.thresholdPercent);
2065
2286
  const bar = this.renderProgressBar(percent, 10);
2066
- this.statusText.setText(fg("muted", `OM ${bar} ${percent}%`));
2287
+ this.statusText.setText(theme.fg("muted", `OM ${bar} ${percent}%`));
2067
2288
  } else {
2068
2289
  this.statusText.setText("");
2069
2290
  }
@@ -2126,7 +2347,7 @@ function formatObservationStatus(state, compact, labelStyler) {
2126
2347
  }
2127
2348
  const label = compact === "full" ? "messages" : "msg";
2128
2349
  const fraction = `${formatTokensValue(state.pendingTokens)}/${formatTokensThreshold(state.threshold)}`;
2129
- const buffered = compact !== "noBuffer" && state.buffered.observations.projectedMessageRemoval > 0 ? chalk10.hex("#555")(` \u2193${formatTokensThreshold(state.buffered.observations.projectedMessageRemoval)}`) : "";
2350
+ const buffered = compact !== "noBuffer" && state.buffered.observations.projectedMessageRemoval > 0 ? theme.fg("muted", ` \u2193${formatTokensThreshold(state.buffered.observations.projectedMessageRemoval)}`) : "";
2130
2351
  return styleLabel(`${label} `) + colorByPercent(fraction, percent) + buffered;
2131
2352
  }
2132
2353
  function formatReflectionStatus(state, compact, labelStyler) {
@@ -2140,7 +2361,7 @@ function formatReflectionStatus(state, compact, labelStyler) {
2140
2361
  }
2141
2362
  const fraction = `${formatTokensValue(state.observationTokens)}/${formatTokensThreshold(state.reflectionThreshold)}`;
2142
2363
  const savings = state.buffered.reflection.inputObservationTokens - state.buffered.reflection.observationTokens;
2143
- const buffered = compact !== "noBuffer" && state.buffered.reflection.status === "complete" ? chalk10.hex("#555")(` \u2193${formatTokensThreshold(savings)}`) : "";
2364
+ const buffered = compact !== "noBuffer" && state.buffered.reflection.status === "complete" ? theme.fg("muted", ` \u2193${formatTokensThreshold(savings)}`) : "";
2144
2365
  return label + colorByPercent(fraction, percent) + buffered;
2145
2366
  }
2146
2367
  function formatOMStatus(state) {
@@ -2150,6 +2371,16 @@ function formatOMStatus(state) {
2150
2371
  // src/tui/status-line.ts
2151
2372
  var OBSERVER_COLOR = mastra.orange;
2152
2373
  var REFLECTOR_COLOR = mastra.pink;
2374
+ function lighten(r, g, b, factor) {
2375
+ return [Math.floor(r + (255 - r) * factor), Math.floor(g + (255 - g) * factor), Math.floor(b + (255 - b) * factor)];
2376
+ }
2377
+ function adjustBadgeColor(r, g, b, modeColor) {
2378
+ if (getThemeMode() !== "light") return [r, g, b];
2379
+ if (modeColor === mastra.purple || modeColor === mastra.blue) {
2380
+ return lighten(r, g, b, 0.25);
2381
+ }
2382
+ return [r, g, b];
2383
+ }
2153
2384
  function updateStatusLine(state) {
2154
2385
  if (!state.statusLine) return;
2155
2386
  const termWidth = (process.stdout.columns || 80) - 1;
@@ -2180,25 +2411,26 @@ function updateStatusLine(state) {
2180
2411
  badgeBrightness = animBrightness + (0.9 - animBrightness) * fade;
2181
2412
  }
2182
2413
  }
2183
- const [mr, mg, mb] = [
2414
+ const [mr, mg, mb] = adjustBadgeColor(
2184
2415
  Math.floor(mcr * badgeBrightness),
2185
2416
  Math.floor(mcg * badgeBrightness),
2186
- Math.floor(mcb * badgeBrightness)
2187
- ];
2188
- modeBadge = chalk10.bgRgb(mr, mg, mb).hex(mastra.bg).bold(` ${badgeName.toLowerCase()} `);
2417
+ Math.floor(mcb * badgeBrightness),
2418
+ modeColor
2419
+ );
2420
+ modeBadge = chalk10.bgRgb(mr, mg, mb).hex("#000000").bold(` ${badgeName.toLowerCase()} `);
2189
2421
  modeBadgeWidth = badgeName.length + 2;
2190
2422
  } else if (badgeName) {
2191
- modeBadge = fg("dim", badgeName) + " ";
2423
+ modeBadge = theme.fg("dim", badgeName) + " ";
2192
2424
  modeBadgeWidth = badgeName.length + 1;
2193
2425
  }
2194
2426
  if (mainModeColor) {
2195
- const [br, bg2, bb] = [
2427
+ const [br, bg, bb] = [
2196
2428
  parseInt(mainModeColor.slice(1, 3), 16),
2197
2429
  parseInt(mainModeColor.slice(3, 5), 16),
2198
2430
  parseInt(mainModeColor.slice(5, 7), 16)
2199
2431
  ];
2200
2432
  const dim = 0.35;
2201
- state.editor.borderColor = (text) => chalk10.rgb(Math.floor(br * dim), Math.floor(bg2 * dim), Math.floor(bb * dim))(text);
2433
+ state.editor.borderColor = (text) => chalk10.rgb(Math.floor(br * dim), Math.floor(bg * dim), Math.floor(bb * dim))(text);
2202
2434
  }
2203
2435
  const fullModelId = showOMMode ? isObserving ? state.harness.getObserverModelId() : state.harness.getReflectorModelId() : state.harness.getFullModelId();
2204
2436
  const shortModelId = fullModelId.includes("/") ? fullModelId.slice(fullModelId.indexOf("/") + 1) : fullModelId;
@@ -2208,14 +2440,15 @@ function updateStatusLine(state) {
2208
2440
  if (homedir2 && displayPath.startsWith(homedir2)) {
2209
2441
  displayPath = "~" + displayPath.slice(homedir2.length);
2210
2442
  }
2211
- if (state.projectInfo.gitBranch) {
2212
- displayPath = `${displayPath} (${state.projectInfo.gitBranch})`;
2213
- }
2443
+ const branch = state.projectInfo.gitBranch;
2444
+ const dirFull = branch ? `${displayPath} (${branch})` : displayPath;
2445
+ const dirBranchOnly = branch || null;
2446
+ const dirBranchShort = branch && branch.length > 24 ? branch.slice(0, 12) + ".." + branch.slice(-8) : dirBranchOnly;
2214
2447
  const isYolo = state.harness.getState().yolo === true;
2215
2448
  const styleModelId = (id) => {
2216
2449
  if (!state.modelAuthStatus.hasAuth) {
2217
2450
  const envVar = state.modelAuthStatus.apiKeyEnvVar;
2218
- return fg("dim", id) + fg("error", " \u2717") + fg("muted", envVar ? ` (${envVar})` : " (no key)");
2451
+ return theme.fg("dim", id) + theme.fg("error", " \u2717") + theme.fg("muted", envVar ? ` (${envVar})` : " (no key)");
2219
2452
  }
2220
2453
  const tintBg = modeColor ? tintHex(modeColor, 0.15) : void 0;
2221
2454
  const padded = ` ${id} `;
@@ -2233,11 +2466,12 @@ function updateStatusLine(state) {
2233
2466
  }
2234
2467
  }
2235
2468
  if (modeColor) {
2236
- const [r, g, b] = [
2469
+ const [r, g, b] = adjustBadgeColor(
2237
2470
  parseInt(modeColor.slice(1, 3), 16),
2238
2471
  parseInt(modeColor.slice(3, 5), 16),
2239
- parseInt(modeColor.slice(5, 7), 16)
2240
- ];
2472
+ parseInt(modeColor.slice(5, 7), 16),
2473
+ modeColor
2474
+ );
2241
2475
  const dim = 0.8;
2242
2476
  const fgStyled = chalk10.rgb(Math.floor(r * dim), Math.floor(g * dim), Math.floor(b * dim)).bold(padded);
2243
2477
  return tintBg ? chalk10.bgHex(tintBg)(fgStyled) : fgStyled;
@@ -2262,16 +2496,17 @@ function updateStatusLine(state) {
2262
2496
  sBadgeBrightness = animBrightness + (0.9 - animBrightness) * fade;
2263
2497
  }
2264
2498
  }
2265
- const [sr, sg, sb] = [
2499
+ const [sr, sg, sb] = adjustBadgeColor(
2266
2500
  Math.floor(mcr * sBadgeBrightness),
2267
2501
  Math.floor(mcg * sBadgeBrightness),
2268
- Math.floor(mcb * sBadgeBrightness)
2269
- ];
2270
- shortModeBadge = chalk10.bgRgb(sr, sg, sb).hex(mastra.bg).bold(` ${shortName} `);
2502
+ Math.floor(mcb * sBadgeBrightness),
2503
+ modeColor
2504
+ );
2505
+ shortModeBadge = chalk10.bgRgb(sr, sg, sb).hex("#000000").bold(` ${shortName} `);
2271
2506
  shortModeBadgeWidth = shortName.length + 2;
2272
2507
  } else if (badgeName) {
2273
2508
  const shortName = badgeName.toLowerCase().charAt(0);
2274
- shortModeBadge = fg("dim", shortName) + " ";
2509
+ shortModeBadge = theme.fg("dim", shortName) + " ";
2275
2510
  shortModeBadgeWidth = shortName.length + 1;
2276
2511
  }
2277
2512
  const buildLine = (opts) => {
@@ -2312,16 +2547,18 @@ function updateStatusLine(state) {
2312
2547
  if (ref) {
2313
2548
  parts.push({ plain: ref, styled: ref });
2314
2549
  }
2315
- if (opts.showDir) {
2550
+ const dirText = opts.dir !== void 0 ? opts.dir : opts.showDir ? dirFull : null;
2551
+ if (dirText) {
2316
2552
  parts.push({
2317
- plain: displayPath,
2318
- styled: fg("dim", displayPath)
2553
+ plain: dirText,
2554
+ styled: theme.fg("dim", dirText)
2319
2555
  });
2320
2556
  }
2321
2557
  const totalPlain = useBadgeWidth + parts.reduce((sum, p, i) => sum + visibleWidth(p.plain) + (i > 0 ? SEP.length : 0), 0);
2322
2558
  if (totalPlain > termWidth) return null;
2323
2559
  let styledLine;
2324
- if (opts.showDir && parts.length >= 3) {
2560
+ const hasDir = !!dirText;
2561
+ if (hasDir && parts.length >= 3) {
2325
2562
  const leftPart = parts[0];
2326
2563
  const centerParts = parts.slice(1, -1);
2327
2564
  const dirPart = parts[parts.length - 1];
@@ -2333,7 +2570,7 @@ function updateStatusLine(state) {
2333
2570
  const gapLeft = Math.floor(freeSpace / 2);
2334
2571
  const gapRight = freeSpace - gapLeft;
2335
2572
  styledLine = useBadge + leftPart.styled + " ".repeat(Math.max(gapLeft, 1)) + centerParts.map((p) => p.styled).join(SEP) + " ".repeat(Math.max(gapRight, 1)) + dirPart.styled;
2336
- } else if (opts.showDir && parts.length === 2) {
2573
+ } else if (hasDir && parts.length === 2) {
2337
2574
  const mainStr = useBadge + parts[0].styled;
2338
2575
  const dirPart = parts[parts.length - 1];
2339
2576
  const gap = termWidth - totalPlain;
@@ -2344,23 +2581,25 @@ function updateStatusLine(state) {
2344
2581
  return { plain: "", styled: styledLine };
2345
2582
  };
2346
2583
  const result = (
2347
- // 1. Full badge + full model + long labels + fractions + buffer + dir
2348
- buildLine({ modelId: fullModelId, memCompact: "full", showDir: true }) ?? // 2. Drop directory
2349
- buildLine({ modelId: fullModelId, memCompact: "full", showDir: false }) ?? // 3. Drop provider + "claude-" prefix, keep full labels + fractions + buffer
2350
- buildLine({ modelId: tinyModelId, memCompact: "full", showDir: false }) ?? // 4. Short labels (msg/mem) + fractions + buffer
2351
- buildLine({ modelId: tinyModelId, showDir: false }) ?? // 5. Short badge + short labels + fractions + buffer
2352
- buildLine({ modelId: tinyModelId, showDir: false, badge: "short" }) ?? // 6. Short badge + fractions (drop buffer indicator)
2584
+ // 1. Full badge + full model + long labels + fractions + buffer + full dir
2585
+ buildLine({ modelId: fullModelId, memCompact: "full", showDir: false, dir: dirFull }) ?? // 2. Full badge + full model + branch only (drop path)
2586
+ buildLine({ modelId: fullModelId, memCompact: "full", showDir: false, dir: dirBranchOnly }) ?? // 3. Full badge + full model + abbreviated branch
2587
+ buildLine({ modelId: fullModelId, memCompact: "full", showDir: false, dir: dirBranchShort }) ?? // 4. Drop directory entirely
2588
+ buildLine({ modelId: fullModelId, memCompact: "full", showDir: false }) ?? // 5. Drop provider + "claude-" prefix, keep full labels + fractions + buffer
2589
+ buildLine({ modelId: tinyModelId, memCompact: "full", showDir: false }) ?? // 6. Short labels (msg/mem) + fractions + buffer
2590
+ buildLine({ modelId: tinyModelId, showDir: false }) ?? // 7. Short badge + short labels + fractions + buffer
2591
+ buildLine({ modelId: tinyModelId, showDir: false, badge: "short" }) ?? // 8. Short badge + fractions (drop buffer indicator)
2353
2592
  buildLine({
2354
2593
  modelId: tinyModelId,
2355
2594
  memCompact: "noBuffer",
2356
2595
  showDir: false,
2357
2596
  badge: "short"
2358
- }) ?? // 7. Full badge + percent only
2597
+ }) ?? // 9. Full badge + percent only
2359
2598
  buildLine({
2360
2599
  modelId: tinyModelId,
2361
2600
  memCompact: "percentOnly",
2362
2601
  showDir: false
2363
- }) ?? // 8. Short badge + percent only
2602
+ }) ?? // 10. Short badge + percent only
2364
2603
  buildLine({
2365
2604
  modelId: tinyModelId,
2366
2605
  memCompact: "percentOnly",
@@ -2471,16 +2710,21 @@ function applyPack(ctx, pack) {
2471
2710
  }
2472
2711
  s.models.subagentModels = {};
2473
2712
  saveSettings(s);
2713
+ const hasOpenAI = Object.values(pack.models).some((m) => m.startsWith("openai/"));
2714
+ const currentThinking = harness.getState()?.thinkingLevel ?? "off";
2715
+ if (hasOpenAI && currentThinking === "off") {
2716
+ harness.setState({ thinkingLevel: "low" });
2717
+ }
2474
2718
  updateStatusLine(ctx.state);
2475
2719
  }
2476
2720
  function getPackDetail(pack) {
2477
2721
  if (pack.id === "custom") {
2478
- return fg("dim", " You'll pick a model for each mode.");
2722
+ return theme.fg("dim", " You'll pick a model for each mode.");
2479
2723
  }
2480
2724
  return [
2481
- ` ${chalk10.hex(mastra.blue)("plan")} \u2192 ${fg("text", pack.models.plan)}`,
2482
- ` ${chalk10.hex(mastra.purple)("build")} \u2192 ${fg("text", pack.models.build)}`,
2483
- ` ${chalk10.hex(mastra.green)("fast")} \u2192 ${fg("text", pack.models.fast)}`
2725
+ ` ${chalk10.hex(mastra.blue)("plan")} \u2192 ${theme.fg("text", pack.models.plan)}`,
2726
+ ` ${chalk10.hex(mastra.purple)("build")} \u2192 ${theme.fg("text", pack.models.build)}`,
2727
+ ` ${chalk10.hex(mastra.green)("fast")} \u2192 ${theme.fg("text", pack.models.fast)}`
2484
2728
  ].join("\n");
2485
2729
  }
2486
2730
  async function handleModelsPackCommand(ctx) {
@@ -2508,11 +2752,11 @@ async function handleModelsPackCommand(ctx) {
2508
2752
  const currentPackId = settings.models.activeModelPackId;
2509
2753
  const items = packs.map((p) => ({
2510
2754
  value: p.id,
2511
- label: ` ${p.name} ${fg("dim", p.description)}${p.id === currentPackId ? fg("dim", " (current)") : ""}`
2755
+ label: ` ${p.name} ${theme.fg("dim", p.description)}${p.id === currentPackId ? theme.fg("dim", " (current)") : ""}`
2512
2756
  }));
2513
2757
  return new Promise((resolve2) => {
2514
2758
  const container = new Box(1, 1);
2515
- container.addChild(new Text(bold(fg("accent", "Switch model pack")), 0, 0));
2759
+ container.addChild(new Text(theme.bold(theme.fg("accent", "Switch model pack")), 0, 0));
2516
2760
  container.addChild(new Spacer(1));
2517
2761
  const selectList = new SelectList(items, items.length, getSelectListTheme());
2518
2762
  const detailText = new Text("", 0, 0);
@@ -2535,14 +2779,14 @@ async function handleModelsPackCommand(ctx) {
2535
2779
  const customPack = await runCustomFlow(ctx);
2536
2780
  if (customPack) {
2537
2781
  applyPack(ctx, customPack);
2538
- collapseResult(`Model pack \u2192 ${bold("Custom")}`);
2782
+ collapseResult(`Model pack \u2192 ${theme.bold("Custom")}`);
2539
2783
  ctx.showInfo("Switched to Custom pack");
2540
2784
  } else {
2541
2785
  collapseResult("cancelled");
2542
2786
  }
2543
2787
  } else {
2544
2788
  applyPack(ctx, pack);
2545
- collapseResult(`Model pack \u2192 ${bold(pack.name)}`);
2789
+ collapseResult(`Model pack \u2192 ${theme.bold(pack.name)}`);
2546
2790
  ctx.showInfo(`Switched to ${pack.name} pack`);
2547
2791
  }
2548
2792
  resolve2();
@@ -2559,7 +2803,7 @@ async function handleModelsPackCommand(ctx) {
2559
2803
  container.addChild(new Spacer(1));
2560
2804
  container.addChild(detailText);
2561
2805
  container.addChild(new Spacer(1));
2562
- container.addChild(new Text(fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc cancel"), 0, 0));
2806
+ container.addChild(new Text(theme.fg("dim", "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc cancel"), 0, 0));
2563
2807
  const currentIdx = packs.findIndex((p) => p.id === currentPackId);
2564
2808
  const initialIdx = currentIdx >= 0 ? currentIdx : 0;
2565
2809
  if (initialIdx > 0) selectList.setSelectedIndex(initialIdx);
@@ -2569,9 +2813,9 @@ async function handleModelsPackCommand(ctx) {
2569
2813
  const collapseResult = (result) => {
2570
2814
  container.clear();
2571
2815
  if (result === "cancelled") {
2572
- container.addChild(new Text(fg("dim", `${fg("error", "\u2717")} Model pack (cancelled)`), 0, 0));
2816
+ container.addChild(new Text(theme.fg("dim", `${theme.fg("error", "\u2717")} Model pack (cancelled)`), 0, 0));
2573
2817
  } else if (result) {
2574
- container.addChild(new Text(fg("text", `${fg("success", "\u2713")} ${result}`), 0, 0));
2818
+ container.addChild(new Text(theme.fg("text", `${theme.fg("success", "\u2713")} ${result}`), 0, 0));
2575
2819
  }
2576
2820
  };
2577
2821
  ctx.state.chatContainer.addChild(new Spacer(1));
@@ -2757,13 +3001,13 @@ var ThresholdSubmenu = class extends Container {
2757
3001
  super();
2758
3002
  this.onDone = onDone;
2759
3003
  this.onBack = onBack;
2760
- this.addChild(new Text(bold(fg("accent", title)), 0, 0));
3004
+ this.addChild(new Text(theme.bold(theme.fg("accent", title)), 0, 0));
2761
3005
  this.addChild(new Spacer(1));
2762
- this.addChild(new Text(fg("muted", " _k tokens (type a number, e.g. 30 for 30k):"), 0, 0));
3006
+ this.addChild(new Text(theme.fg("muted", " _k tokens (type a number, e.g. 30 for 30k):"), 0, 0));
2763
3007
  this.input = new Input();
2764
3008
  this.addChild(this.input);
2765
3009
  this.addChild(new Spacer(1));
2766
- this.addChild(new Text(fg("muted", " Or pick a preset:"), 0, 0));
3010
+ this.addChild(new Text(theme.fg("muted", " Or pick a preset:"), 0, 0));
2767
3011
  const items = presets.map((p) => ({
2768
3012
  value: String(p),
2769
3013
  label: ` ${formatTokens(p)} tokens`
@@ -2779,7 +3023,7 @@ var ThresholdSubmenu = class extends Container {
2779
3023
  this.selectList.onCancel = onBack;
2780
3024
  this.addChild(this.selectList);
2781
3025
  this.addChild(new Spacer(1));
2782
- this.addChild(new Text(fg("dim", " Enter to confirm \xB7 \u2193 for presets \xB7 Esc to go back"), 0, 0));
3026
+ this.addChild(new Text(theme.fg("dim", " Enter to confirm \xB7 \u2193 for presets \xB7 Esc to go back"), 0, 0));
2783
3027
  }
2784
3028
  handleInput(data) {
2785
3029
  if (this.inInputMode) {
@@ -2822,9 +3066,9 @@ var ModelSelectSubmenu = class extends Container {
2822
3066
  this.onSelect = onSelect;
2823
3067
  this.onCancel = onCancel;
2824
3068
  this.tui = tui;
2825
- this.addChild(new Text(bold(fg("accent", title)), 0, 0));
3069
+ this.addChild(new Text(theme.bold(theme.fg("accent", title)), 0, 0));
2826
3070
  this.addChild(new Spacer(1));
2827
- this.addChild(new Text(fg("muted", "Type to search \xB7 \u2191\u2193 navigate \xB7 Enter select \xB7 Esc back"), 0, 0));
3071
+ this.addChild(new Text(theme.fg("muted", "Type to search \xB7 \u2191\u2193 navigate \xB7 Enter select \xB7 Esc back"), 0, 0));
2828
3072
  this.addChild(new Spacer(1));
2829
3073
  this.searchInput = new Input();
2830
3074
  this.addChild(this.searchInput);
@@ -2852,15 +3096,15 @@ var ModelSelectSubmenu = class extends Container {
2852
3096
  const item = this.filteredModels[i];
2853
3097
  const isSelected = i === this.selectedIndex;
2854
3098
  const isCurrent = item.id === this.currentModelId;
2855
- const checkmark = isCurrent ? fg("success", " \u2713") : "";
2856
- const line = isSelected ? fg("accent", `\u2192 ${item.label}`) + checkmark : ` ${item.label}` + checkmark;
3099
+ const checkmark = isCurrent ? theme.fg("success", " \u2713") : "";
3100
+ const line = isSelected ? theme.fg("accent", `\u2192 ${item.label}`) + checkmark : ` ${item.label}` + checkmark;
2857
3101
  this.listContainer.addChild(new Text(line, 0, 0));
2858
3102
  }
2859
3103
  if (startIndex > 0 || endIndex < total) {
2860
- this.listContainer.addChild(new Text(fg("muted", `(${this.selectedIndex + 1}/${total})`), 0, 0));
3104
+ this.listContainer.addChild(new Text(theme.fg("muted", `(${this.selectedIndex + 1}/${total})`), 0, 0));
2861
3105
  }
2862
3106
  if (total === 0) {
2863
- this.listContainer.addChild(new Text(fg("muted", "No matching models"), 0, 0));
3107
+ this.listContainer.addChild(new Text(theme.fg("muted", "No matching models"), 0, 0));
2864
3108
  }
2865
3109
  }
2866
3110
  handleInput(data) {
@@ -2899,8 +3143,8 @@ var OMSettingsComponent = class extends Box {
2899
3143
  this._focused = value;
2900
3144
  }
2901
3145
  constructor(config, callbacks, models, tui) {
2902
- super(2, 1, (text) => bg("overlayBg", text));
2903
- this.addChild(new Text(bold(fg("accent", "Observational Memory Settings")), 0, 0));
3146
+ super(2, 1, (text) => theme.bg("overlayBg", text));
3147
+ this.addChild(new Text(theme.bold(theme.fg("accent", "Observational Memory Settings")), 0, 0));
2904
3148
  this.addChild(new Spacer(1));
2905
3149
  const items = [
2906
3150
  {
@@ -3096,15 +3340,15 @@ var StorageBackendSubmenu = class extends Container {
3096
3340
  this.phase = "connection";
3097
3341
  this.clear();
3098
3342
  if (this.pendingBackend === "pg") {
3099
- this.addChild(new Text(bold(fg("accent", "PostgreSQL Connection")), 0, 0));
3343
+ this.addChild(new Text(theme.bold(theme.fg("accent", "PostgreSQL Connection")), 0, 0));
3100
3344
  this.addChild(new Spacer(1));
3101
- this.addChild(new Text(fg("muted", "Enter a connection string:"), 0, 0));
3102
- this.addChild(new Text(fg("dim", "e.g. postgresql://user:pass@localhost:5432/mydb"), 0, 0));
3345
+ this.addChild(new Text(theme.fg("muted", "Enter a connection string:"), 0, 0));
3346
+ this.addChild(new Text(theme.fg("dim", "e.g. postgresql://user:pass@localhost:5432/mydb"), 0, 0));
3103
3347
  } else {
3104
- this.addChild(new Text(bold(fg("accent", "LibSQL Connection")), 0, 0));
3348
+ this.addChild(new Text(theme.bold(theme.fg("accent", "LibSQL Connection")), 0, 0));
3105
3349
  this.addChild(new Spacer(1));
3106
- this.addChild(new Text(fg("muted", "Enter a URL or leave empty for default local file:"), 0, 0));
3107
- this.addChild(new Text(fg("dim", "e.g. libsql://your-db.turso.io"), 0, 0));
3350
+ this.addChild(new Text(theme.fg("muted", "Enter a URL or leave empty for default local file:"), 0, 0));
3351
+ this.addChild(new Text(theme.fg("dim", "e.g. libsql://your-db.turso.io"), 0, 0));
3108
3352
  }
3109
3353
  this.addChild(new Spacer(1));
3110
3354
  this.input = new Input();
@@ -3114,7 +3358,7 @@ var StorageBackendSubmenu = class extends Container {
3114
3358
  }
3115
3359
  this.addChild(this.input);
3116
3360
  this.addChild(new Spacer(1));
3117
- this.addChild(new Text(fg("dim", "Enter to save \xB7 Esc to go back"), 0, 0));
3361
+ this.addChild(new Text(theme.fg("dim", "Enter to save \xB7 Esc to go back"), 0, 0));
3118
3362
  }
3119
3363
  handleInput(data) {
3120
3364
  if (this.phase === "select") {
@@ -3154,8 +3398,8 @@ var SettingsComponent = class extends Box {
3154
3398
  this._focused = value;
3155
3399
  }
3156
3400
  constructor(config, callbacks) {
3157
- super(2, 1, (text) => bg("overlayBg", text));
3158
- this.addChild(new Text(bold(fg("accent", "Settings")), 0, 0));
3401
+ super(2, 1, (text) => theme.bg("overlayBg", text));
3402
+ this.addChild(new Text(theme.bold(theme.fg("accent", "Settings")), 0, 0));
3159
3403
  this.addChild(new Spacer(1));
3160
3404
  const notificationModes = [
3161
3405
  { value: "off", label: "Off", desc: "No notifications" },
@@ -3163,13 +3407,11 @@ var SettingsComponent = class extends Box {
3163
3407
  { value: "system", label: "System", desc: "Native OS notification" },
3164
3408
  { value: "both", label: "Both", desc: "Bell + system notification" }
3165
3409
  ];
3166
- const thinkingLevels = [
3167
- { value: "off", label: "Off", desc: "No extended thinking" },
3168
- { value: "minimal", label: "Minimal", desc: "~1k budget tokens" },
3169
- { value: "low", label: "Low", desc: "~4k budget tokens" },
3170
- { value: "medium", label: "Medium", desc: "~10k budget tokens" },
3171
- { value: "high", label: "High", desc: "~32k budget tokens" }
3172
- ];
3410
+ const thinkingLevels = getThinkingLevelsForModel(config.currentModelId).map((level) => ({
3411
+ value: level.id,
3412
+ label: level.label,
3413
+ desc: level.description
3414
+ }));
3173
3415
  const getNotifLabel = (mode) => notificationModes.find((m) => m.value === mode)?.label ?? mode;
3174
3416
  const getThinkingLabel = (level) => thinkingLevels.find((l) => l.value === level)?.label ?? level;
3175
3417
  const items = [
@@ -3223,7 +3465,7 @@ var SettingsComponent = class extends Box {
3223
3465
  {
3224
3466
  id: "thinking",
3225
3467
  label: "Thinking level",
3226
- description: "Extended thinking budget for Anthropic models (currently disabled)",
3468
+ description: "Reasoning depth level",
3227
3469
  currentValue: getThinkingLabel(config.thinkingLevel),
3228
3470
  submenu: (_currentValue, done) => new SelectSubmenu(
3229
3471
  thinkingLevels.map((l) => ({
@@ -3313,6 +3555,7 @@ async function handleSettingsCommand(ctx) {
3313
3555
  notifications: state?.notifications ?? "off",
3314
3556
  yolo: state?.yolo === true,
3315
3557
  thinkingLevel: state?.thinkingLevel ?? "off",
3558
+ currentModelId: ctx.state.harness.getCurrentModelId() ?? "",
3316
3559
  escapeAsCancel: ctx.state.editor.escapeEnabled,
3317
3560
  storageBackend: globalSettings.storage.backend,
3318
3561
  pgConnectionString: globalSettings.storage.pg?.connectionString ?? "",
@@ -3367,12 +3610,12 @@ Storage backend changed to ${label}. Restarting is required.
3367
3610
  }
3368
3611
  var LoginDialogComponent = class extends Box {
3369
3612
  constructor(tui, providerId, onComplete) {
3370
- super(2, 1, (text) => bg("overlayBg", text));
3613
+ super(2, 1, (text) => theme.bg("overlayBg", text));
3371
3614
  this.onComplete = onComplete;
3372
3615
  this.tui = tui;
3373
3616
  const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
3374
3617
  const providerName = providerInfo?.name || providerId;
3375
- this.addChild(new Text(fg("warning", `Login to ${providerName}`)));
3618
+ this.addChild(new Text(theme.fg("warning", `Login to ${providerName}`)));
3376
3619
  this.addChild(new Spacer(1));
3377
3620
  this.contentContainer = new Container();
3378
3621
  this.addChild(this.contentContainer);
@@ -3423,13 +3666,13 @@ var LoginDialogComponent = class extends Box {
3423
3666
  */
3424
3667
  showAuth(url, instructions) {
3425
3668
  this.contentContainer.clear();
3426
- this.contentContainer.addChild(new Text(fg("accent", url)));
3669
+ this.contentContainer.addChild(new Text(theme.fg("accent", url)));
3427
3670
  const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
3428
3671
  const hyperlink = `\x1B]8;;${url}\x07${clickHint}\x1B]8;;\x07`;
3429
- this.contentContainer.addChild(new Text(fg("muted", hyperlink)));
3672
+ this.contentContainer.addChild(new Text(theme.fg("muted", hyperlink)));
3430
3673
  if (instructions) {
3431
3674
  this.contentContainer.addChild(new Spacer(1));
3432
- this.contentContainer.addChild(new Text(fg("warning", instructions)));
3675
+ this.contentContainer.addChild(new Text(theme.fg("warning", instructions)));
3433
3676
  }
3434
3677
  const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
3435
3678
  exec(`${openCmd} "${url}"`);
@@ -3440,12 +3683,12 @@ var LoginDialogComponent = class extends Box {
3440
3683
  */
3441
3684
  showPrompt(message, placeholder) {
3442
3685
  this.contentContainer.addChild(new Spacer(1));
3443
- this.contentContainer.addChild(new Text(fg("text", message)));
3686
+ this.contentContainer.addChild(new Text(theme.fg("text", message)));
3444
3687
  if (placeholder) {
3445
- this.contentContainer.addChild(new Text(fg("muted", `e.g., ${placeholder}`)));
3688
+ this.contentContainer.addChild(new Text(theme.fg("muted", `e.g., ${placeholder}`)));
3446
3689
  }
3447
3690
  this.contentContainer.addChild(this.input);
3448
- this.contentContainer.addChild(new Text(fg("muted", "(Escape to cancel, Enter to submit)")));
3691
+ this.contentContainer.addChild(new Text(theme.fg("muted", "(Escape to cancel, Enter to submit)")));
3449
3692
  this.input.setValue("");
3450
3693
  this.tui.requestRender();
3451
3694
  return new Promise((resolve2, reject) => {
@@ -3457,7 +3700,7 @@ var LoginDialogComponent = class extends Box {
3457
3700
  * Show progress message
3458
3701
  */
3459
3702
  showProgress(message) {
3460
- this.contentContainer.addChild(new Text(fg("muted", message)));
3703
+ this.contentContainer.addChild(new Text(theme.fg("muted", message)));
3461
3704
  this.tui.requestRender();
3462
3705
  }
3463
3706
  handleInput(data) {
@@ -3478,6 +3721,15 @@ async function performLogin(ctx, providerId) {
3478
3721
  ctx.showError("Auth storage not configured");
3479
3722
  return;
3480
3723
  }
3724
+ if (providerId === ANTHROPIC_OAUTH_PROVIDER_ID) {
3725
+ const warningResult = await showClaudeMaxOAuthWarning(ctx.state, "login");
3726
+ if (warningResult !== "continue") {
3727
+ return;
3728
+ }
3729
+ const settings = loadSettings();
3730
+ settings.onboarding.claudeMaxOAuthWarningAcknowledgedAt = (/* @__PURE__ */ new Date()).toISOString();
3731
+ saveSettings(settings);
3732
+ }
3481
3733
  return new Promise((resolve2) => {
3482
3734
  const dialog = new LoginDialogComponent(ctx.state.ui, providerId, (success, message) => {
3483
3735
  ctx.state.ui.hideOverlay();
@@ -3639,6 +3891,121 @@ Pay special attention to: ${focusArea}
3639
3891
  async function handleSetupCommand(ctx) {
3640
3892
  await ctx.showOnboarding();
3641
3893
  }
3894
+
3895
+ // src/tui/detect-theme.ts
3896
+ function queryTerminalBackground(timeoutMs = 200) {
3897
+ return new Promise((resolve2) => {
3898
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
3899
+ resolve2(null);
3900
+ return;
3901
+ }
3902
+ let settled = false;
3903
+ let buffer = "";
3904
+ let wasRaw;
3905
+ let wasResumed = false;
3906
+ let timer;
3907
+ const cleanup = () => {
3908
+ if (settled) return;
3909
+ settled = true;
3910
+ if (timer) {
3911
+ clearTimeout(timer);
3912
+ timer = void 0;
3913
+ }
3914
+ process.stdin.removeListener("data", onData);
3915
+ try {
3916
+ if (process.stdin.isTTY) {
3917
+ process.stdin.setRawMode(wasRaw);
3918
+ }
3919
+ } catch {
3920
+ }
3921
+ if (wasResumed) {
3922
+ process.stdin.pause();
3923
+ }
3924
+ };
3925
+ const onData = (data) => {
3926
+ buffer += data.toString();
3927
+ const match = buffer.match(/\x1b\]11;rgb:([0-9a-fA-F]+)\/([0-9a-fA-F]+)\/([0-9a-fA-F]+)/);
3928
+ if (match) {
3929
+ cleanup();
3930
+ const rHex = match[1];
3931
+ const gHex = match[2];
3932
+ const bHex = match[3];
3933
+ const normalize = (hex) => {
3934
+ const val = parseInt(hex, 16);
3935
+ return hex.length <= 2 ? val / 255 : val / 65535;
3936
+ };
3937
+ const r = normalize(rHex);
3938
+ const g = normalize(gHex);
3939
+ const b = normalize(bHex);
3940
+ const linearize = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
3941
+ const luma = 0.2126 * linearize(r) + 0.7152 * linearize(g) + 0.0722 * linearize(b);
3942
+ resolve2(luma >= 0.5 ? "light" : "dark");
3943
+ return;
3944
+ }
3945
+ };
3946
+ timer = setTimeout(() => {
3947
+ cleanup();
3948
+ resolve2(null);
3949
+ }, timeoutMs);
3950
+ if (timer.unref) timer.unref();
3951
+ try {
3952
+ wasRaw = process.stdin.isRaw ?? false;
3953
+ process.stdin.setRawMode(true);
3954
+ if (process.stdin.isPaused()) {
3955
+ process.stdin.resume();
3956
+ wasResumed = true;
3957
+ }
3958
+ process.stdin.on("data", onData);
3959
+ process.stdout.write("\x1B]11;?\x07");
3960
+ } catch {
3961
+ cleanup();
3962
+ resolve2(null);
3963
+ }
3964
+ });
3965
+ }
3966
+ function detectFromColorFgBg() {
3967
+ const colorFgBg = process.env.COLORFGBG;
3968
+ if (!colorFgBg) return null;
3969
+ const parts = colorFgBg.split(";");
3970
+ const bgPart = parts[parts.length - 1];
3971
+ if (bgPart === void 0) return null;
3972
+ const bgIndex = parseInt(bgPart, 10);
3973
+ if (isNaN(bgIndex)) return null;
3974
+ return bgIndex >= 7 ? "light" : "dark";
3975
+ }
3976
+ async function detectTerminalTheme() {
3977
+ const envTheme = process.env.MASTRA_THEME?.toLowerCase();
3978
+ if (envTheme === "light") return "light";
3979
+ if (envTheme === "dark") return "dark";
3980
+ const oscResult = await queryTerminalBackground(200);
3981
+ if (oscResult) return oscResult;
3982
+ const fgbgResult = detectFromColorFgBg();
3983
+ if (fgbgResult) return fgbgResult;
3984
+ return "dark";
3985
+ }
3986
+
3987
+ // src/tui/commands/theme.ts
3988
+ async function handleThemeCommand(ctx, args) {
3989
+ const arg = args[0]?.toLowerCase();
3990
+ if (!arg) {
3991
+ const mode = getThemeMode();
3992
+ const settings2 = loadSettings();
3993
+ const pref = settings2.preferences.theme ?? "auto";
3994
+ ctx.showInfo(`Theme: ${mode} (preference: ${pref})`);
3995
+ return;
3996
+ }
3997
+ if (arg !== "auto" && arg !== "dark" && arg !== "light") {
3998
+ ctx.showError("Usage: /theme [auto|dark|light]");
3999
+ return;
4000
+ }
4001
+ const settings = loadSettings();
4002
+ settings.preferences.theme = arg;
4003
+ saveSettings(settings);
4004
+ const resolved = arg === "auto" ? await detectTerminalTheme() : arg;
4005
+ applyThemeMode(resolved);
4006
+ ctx.showInfo(`Theme set to ${arg}${arg === "auto" ? ` (detected: ${resolved})` : ""}`);
4007
+ ctx.state.ui.requestRender();
4008
+ }
3642
4009
  var MAX_COLLAPSED_LINES = 3;
3643
4010
  var BORDER_COLOR = mastra.purple;
3644
4011
  var SlashCommandComponent = class extends Container {
@@ -3866,12 +4233,12 @@ function reasonToMessage(reason) {
3866
4233
  // src/tui/display.ts
3867
4234
  function showError(state, message) {
3868
4235
  state.chatContainer.addChild(new Spacer(1));
3869
- state.chatContainer.addChild(new Text(fg("error", `Error: ${message}`), 1, 0));
4236
+ state.chatContainer.addChild(new Text(theme.fg("error", `Error: ${message}`), 1, 0));
3870
4237
  state.ui.requestRender();
3871
4238
  }
3872
4239
  function showInfo(state, message) {
3873
4240
  state.chatContainer.addChild(new Spacer(1));
3874
- state.chatContainer.addChild(new Text(fg("muted", message), 1, 0));
4241
+ state.chatContainer.addChild(new Text(theme.fg("muted", message), 1, 0));
3875
4242
  state.ui.requestRender();
3876
4243
  }
3877
4244
  function showFormattedError(state, event) {
@@ -3881,9 +4248,9 @@ function showFormattedError(state, event) {
3881
4248
  const errorMessage = error.message || String(error);
3882
4249
  const isValidationError = errorMessage.toLowerCase().includes("validation failed") || errorMessage.toLowerCase().includes("required parameter") || errorMessage.includes("Required");
3883
4250
  if (isValidationError) {
3884
- state.chatContainer.addChild(new Text(fg("error", "Tool validation error - see details above"), 1, 0));
4251
+ state.chatContainer.addChild(new Text(theme.fg("error", "Tool validation error - see details above"), 1, 0));
3885
4252
  state.chatContainer.addChild(
3886
- new Text(fg("muted", " Check the tool execution box for specific parameter requirements"), 1, 0)
4253
+ new Text(theme.fg("muted", " Check the tool execution box for specific parameter requirements"), 1, 0)
3887
4254
  );
3888
4255
  } else {
3889
4256
  let errorText = `Error: ${parsed.message}`;
@@ -3891,12 +4258,12 @@ function showFormattedError(state, event) {
3891
4258
  const retryDelay = "retryDelay" in event ? event.retryDelay : parsed.retryDelay;
3892
4259
  if (retryable && retryDelay) {
3893
4260
  const seconds = Math.ceil(retryDelay / 1e3);
3894
- errorText += fg("muted", ` (retry in ${seconds}s)`);
4261
+ errorText += theme.fg("muted", ` (retry in ${seconds}s)`);
3895
4262
  }
3896
- state.chatContainer.addChild(new Text(fg("error", errorText), 1, 0));
4263
+ state.chatContainer.addChild(new Text(theme.fg("error", errorText), 1, 0));
3897
4264
  const hint = getErrorHint(parsed.type);
3898
4265
  if (hint) {
3899
- state.chatContainer.addChild(new Text(fg("muted", ` Hint: ${hint}`), 1, 0));
4266
+ state.chatContainer.addChild(new Text(theme.fg("muted", ` Hint: ${hint}`), 1, 0));
3900
4267
  }
3901
4268
  }
3902
4269
  state.ui.requestRender();
@@ -3971,7 +4338,7 @@ async function dispatchSlashCommand(input, state, buildCtx) {
3971
4338
  await handleOMCommand(buildCtx());
3972
4339
  return true;
3973
4340
  case "think":
3974
- await handleThinkCommand(buildCtx());
4341
+ await handleThinkCommand(buildCtx(), args);
3975
4342
  return true;
3976
4343
  case "permissions":
3977
4344
  await handlePermissionsCommand(buildCtx(), args);
@@ -4018,6 +4385,9 @@ async function dispatchSlashCommand(input, state, buildCtx) {
4018
4385
  case "setup":
4019
4386
  await handleSetupCommand(buildCtx());
4020
4387
  return true;
4388
+ case "theme":
4389
+ await handleThemeCommand(buildCtx(), args);
4390
+ return true;
4021
4391
  default: {
4022
4392
  const customCommand = state.customSlashCommands.find((cmd) => cmd.name === command);
4023
4393
  if (customCommand) {
@@ -4050,6 +4420,10 @@ ${processedContent.trim()}
4050
4420
  }
4051
4421
  function handleAgentStart(ctx) {
4052
4422
  const { state } = ctx;
4423
+ const freshBranch = getCurrentGitBranch(state.projectInfo.rootPath);
4424
+ if (freshBranch) {
4425
+ state.projectInfo.gitBranch = freshBranch;
4426
+ }
4053
4427
  if (!state.gradientAnimator) {
4054
4428
  state.gradientAnimator = new GradientAnimator(() => {
4055
4429
  ctx.updateStatusLine();
@@ -4062,6 +4436,10 @@ function handleAgentEnd(ctx) {
4062
4436
  if (state.gradientAnimator) {
4063
4437
  state.gradientAnimator.fadeOut();
4064
4438
  }
4439
+ const freshBranch = getCurrentGitBranch(state.projectInfo.rootPath);
4440
+ if (freshBranch) {
4441
+ state.projectInfo.gitBranch = freshBranch;
4442
+ }
4065
4443
  if (state.streamingComponent) {
4066
4444
  state.streamingComponent = void 0;
4067
4445
  state.streamingMessage = void 0;
@@ -4316,11 +4694,11 @@ function formatStackTrace(stack) {
4316
4694
  if (line.match(/^\s*at\s+/)) {
4317
4695
  return line.replace(
4318
4696
  /(\s+at\s+)([^(]+)(\s*\()([^)]+)(\))/,
4319
- (match, at, fn, open, loc, close) => `${fg("muted", at)}${fg("function", fn)}${fg("muted", open)}${fg("path", loc)}${fg("muted", close)}`
4697
+ (match, at, fn, open, loc, close) => `${theme.fg("muted", at)}${theme.fg("function", fn)}${theme.fg("muted", open)}${theme.fg("path", loc)}${theme.fg("muted", close)}`
4320
4698
  );
4321
4699
  }
4322
4700
  if (!line.trim() || line.includes("node_modules")) {
4323
- return fg("muted", line);
4701
+ return theme.fg("muted", line);
4324
4702
  }
4325
4703
  return line;
4326
4704
  });
@@ -4351,23 +4729,23 @@ var ErrorDisplayComponent = class extends Container {
4351
4729
  }
4352
4730
  build() {
4353
4731
  const info = parseErrorInfo(this.error);
4354
- const borderTop = new Text(fg("error", "\u250C\u2500 Error \u2500" + "\u2500".repeat(50) + "\u2510"), 0, 0);
4732
+ const borderTop = new Text(theme.fg("error", "\u250C\u2500 Error \u2500" + "\u2500".repeat(50) + "\u2510"), 0, 0);
4355
4733
  this.addChild(borderTop);
4356
4734
  const errorContainer = new Container();
4357
- const errorBg = (text) => bg("errorBg", text);
4735
+ const errorBg = (text) => theme.bg("errorBg", text);
4358
4736
  if (info.name && info.name !== "Error") {
4359
4737
  const typeLine = new Container();
4360
4738
  typeLine.addChild(new Text("\u2502 ", 0, 0));
4361
- typeLine.addChild(new Text(errorBg(` ${bold(fg("error", info.name))} `), 0, 0));
4739
+ typeLine.addChild(new Text(errorBg(` ${theme.bold(theme.fg("error", info.name))} `), 0, 0));
4362
4740
  errorContainer.addChild(typeLine);
4363
4741
  }
4364
4742
  const msgLine = new Container();
4365
4743
  msgLine.addChild(new Text("\u2502 ", 0, 0));
4366
- msgLine.addChild(new Text(bold(info.message), 0, 0));
4744
+ msgLine.addChild(new Text(theme.bold(info.message), 0, 0));
4367
4745
  errorContainer.addChild(msgLine);
4368
4746
  if (info.file && info.line) {
4369
4747
  const location = `${info.file}:${info.line}${info.column ? `:${info.column}` : ""}`;
4370
- errorContainer.addChild(new Text(fg("muted", ` at ${location}`), 0, 0));
4748
+ errorContainer.addChild(new Text(theme.fg("muted", ` at ${location}`), 0, 0));
4371
4749
  }
4372
4750
  this.addChild(errorContainer);
4373
4751
  if (this.options.showContext && info.context) {
@@ -4378,26 +4756,26 @@ var ErrorDisplayComponent = class extends Container {
4378
4756
  this.addChild(new Spacer(1));
4379
4757
  this.addChild(new CollapsibleStackTrace(info.stack, { expanded: this.options.expanded }, this.ui));
4380
4758
  }
4381
- const borderBottom = new Text(fg("error", "\u2514" + "\u2500".repeat(60) + "\u2518"), 0, 0);
4759
+ const borderBottom = new Text(theme.fg("error", "\u2514" + "\u2500".repeat(60) + "\u2518"), 0, 0);
4382
4760
  this.addChild(borderBottom);
4383
4761
  }
4384
4762
  createCodeContext(context, errorLine) {
4385
4763
  const container = new Container();
4386
4764
  const codeBlock = new Container();
4387
- codeBlock.addChild(new Text(fg("muted", "Code context:"), 0, 0));
4765
+ codeBlock.addChild(new Text(theme.fg("muted", "Code context:"), 0, 0));
4388
4766
  if (context.before) {
4389
4767
  context.before.forEach((line, i) => {
4390
4768
  const lineNum = errorLine ? errorLine - context.before.length + i : i + 1;
4391
- codeBlock.addChild(new Text(fg("muted", `${lineNum.toString().padStart(4)} \u2502 ${line}`), 0, 0));
4769
+ codeBlock.addChild(new Text(theme.fg("muted", `${lineNum.toString().padStart(4)} \u2502 ${line}`), 0, 0));
4392
4770
  });
4393
4771
  }
4394
4772
  if (context.line && errorLine) {
4395
- codeBlock.addChild(new Text(fg("error", `${errorLine.toString().padStart(4)} \u2502 ${context.line}`), 0, 0));
4773
+ codeBlock.addChild(new Text(theme.fg("error", `${errorLine.toString().padStart(4)} \u2502 ${context.line}`), 0, 0));
4396
4774
  }
4397
4775
  if (context.after) {
4398
4776
  context.after.forEach((line, i) => {
4399
4777
  const lineNum = errorLine ? errorLine + i + 1 : i + 1;
4400
- codeBlock.addChild(new Text(fg("muted", `${lineNum.toString().padStart(4)} \u2502 ${line}`), 0, 0));
4778
+ codeBlock.addChild(new Text(theme.fg("muted", `${lineNum.toString().padStart(4)} \u2502 ${line}`), 0, 0));
4401
4779
  });
4402
4780
  }
4403
4781
  container.addChild(codeBlock);
@@ -4959,17 +5337,18 @@ var ToolExecutionComponentEnhanced = class extends Container {
4959
5337
  const maxDiags = shouldCollapse ? COLLAPSED_DIAG_LINES : diagnostics.entries.length;
4960
5338
  const entriesToShow = diagnostics.entries.slice(0, maxDiags);
4961
5339
  for (const diag of entriesToShow) {
4962
- const color = diag.severity === "error" ? "#e06c75" : diag.severity === "warning" ? "#f59e0b" : "#71717a";
5340
+ const t = theme.getTheme();
5341
+ const color = diag.severity === "error" ? t.error : diag.severity === "warning" ? t.warning : t.muted;
4963
5342
  const icon = diag.severity === "error" ? "\u2717" : diag.severity === "warning" ? "\u26A0" : "\u2139";
4964
5343
  const location = diag.location ? chalk10.hex(color)(diag.location) + " " : "";
4965
- const line = ` ${chalk10.hex(color)(icon)} ${location}${chalk10.hex("#a1a1aa")(diag.message)}`;
5344
+ const line = ` ${chalk10.hex(color)(icon)} ${location}${theme.fg("thinkingText", diag.message)}`;
4966
5345
  this.contentBox.addChild(new Text(line, 0, 0));
4967
5346
  }
4968
5347
  if (shouldCollapse) {
4969
5348
  const remaining = diagnostics.entries.length - COLLAPSED_DIAG_LINES;
4970
5349
  this.contentBox.addChild(
4971
5350
  new Text(
4972
- chalk10.hex("#71717a")(` ... ${remaining} more diagnostic${remaining > 1 ? "s" : ""} (ctrl+e to expand)`),
5351
+ theme.fg("muted", ` ... ${remaining} more diagnostic${remaining > 1 ? "s" : ""} (ctrl+e to expand)`),
4973
5352
  0,
4974
5353
  0
4975
5354
  )
@@ -5016,20 +5395,20 @@ var ToolExecutionComponentEnhanced = class extends Container {
5016
5395
  const newLines = newStr.split("\n");
5017
5396
  const lines = [];
5018
5397
  let firstChangeIndex = -1;
5019
- const removedColor2 = chalk10.hex(mastra.red);
5020
- const addedColor2 = chalk10.hex("#5cb85c");
5398
+ const removedColor = chalk10.hex(mastra.red);
5399
+ const addedColor = chalk10.hex(theme.getTheme().success);
5021
5400
  const maxLines = Math.max(oldLines.length, newLines.length);
5022
5401
  for (let i = 0; i < maxLines; i++) {
5023
5402
  if (i >= oldLines.length) {
5024
5403
  if (firstChangeIndex === -1) firstChangeIndex = lines.length;
5025
- lines.push(addedColor2(newLines[i]));
5404
+ lines.push(addedColor(newLines[i]));
5026
5405
  } else if (i >= newLines.length) {
5027
5406
  if (firstChangeIndex === -1) firstChangeIndex = lines.length;
5028
- lines.push(removedColor2(oldLines[i]));
5407
+ lines.push(removedColor(oldLines[i]));
5029
5408
  } else if (oldLines[i] !== newLines[i]) {
5030
5409
  if (firstChangeIndex === -1) firstChangeIndex = lines.length;
5031
- lines.push(removedColor2(oldLines[i]));
5032
- lines.push(addedColor2(newLines[i]));
5410
+ lines.push(removedColor(oldLines[i]));
5411
+ lines.push(addedColor(newLines[i]));
5033
5412
  } else {
5034
5413
  lines.push(theme.fg("muted", oldLines[i]));
5035
5414
  }
@@ -5604,7 +5983,7 @@ function formatMarker(data) {
5604
5983
  switch (data.type) {
5605
5984
  case "om_observation_start": {
5606
5985
  const tokens = data.tokensToObserve > 0 ? ` ~${formatTokens2(data.tokensToObserve)} tokens` : "";
5607
- return fg("muted", ` \u{1F9E0} ${label} in progress${tokens}...`);
5986
+ return theme.fg("muted", ` \u{1F9E0} ${label} in progress${tokens}...`);
5608
5987
  }
5609
5988
  case "om_observation_end": {
5610
5989
  const observed = formatTokens2(data.tokensObserved);
@@ -5612,36 +5991,36 @@ function formatMarker(data) {
5612
5991
  const ratio = data.tokensObserved > 0 && data.observationTokens > 0 ? `${Math.round(data.tokensObserved / data.observationTokens)}x` : "";
5613
5992
  const duration = (data.durationMs / 1e3).toFixed(1);
5614
5993
  const ratioStr = ratio ? ` (${ratio} compression)` : "";
5615
- return fg("success", ` \u{1F9E0} Observed: ${observed} \u2192 ${compressed} tokens${ratioStr} in ${duration}s \u2713`);
5994
+ return theme.fg("success", ` \u{1F9E0} Observed: ${observed} \u2192 ${compressed} tokens${ratioStr} in ${duration}s \u2713`);
5616
5995
  }
5617
5996
  case "om_observation_failed": {
5618
5997
  const tokens = data.tokensAttempted ? ` (${formatTokens2(data.tokensAttempted)} tokens)` : "";
5619
- return fg("error", ` \u2717 ${label} failed${tokens}: ${data.error}`);
5998
+ return theme.fg("error", ` \u2717 ${label} failed${tokens}: ${data.error}`);
5620
5999
  }
5621
6000
  case "om_buffering_start": {
5622
6001
  const tokens = data.tokensToBuffer > 0 ? ` ~${formatTokens2(data.tokensToBuffer)} tokens` : "";
5623
- return fg("muted", ` \u27F3 Buffering ${label.toLowerCase()}${tokens}...`);
6002
+ return theme.fg("muted", ` \u27F3 Buffering ${label.toLowerCase()}${tokens}...`);
5624
6003
  }
5625
6004
  case "om_buffering_end": {
5626
6005
  const input = formatTokens2(data.tokensBuffered);
5627
6006
  const outputTokens = data.operationType === "observation" && data.observations ? Math.round(data.observations.length / 4) : data.bufferedTokens;
5628
6007
  const output = formatTokens2(outputTokens);
5629
6008
  const ratio = data.tokensBuffered > 0 && outputTokens > 0 ? ` (${Math.round(data.tokensBuffered / outputTokens)}x)` : "";
5630
- return fg("success", ` \u2713 Buffered ${label.toLowerCase()}: ${input} \u2192 ${output} tokens${ratio}`);
6009
+ return theme.fg("success", ` \u2713 Buffered ${label.toLowerCase()}: ${input} \u2192 ${output} tokens${ratio}`);
5631
6010
  }
5632
6011
  case "om_buffering_failed": {
5633
- return fg("error", ` \u2717 Buffering ${label.toLowerCase()} failed: ${data.error}`);
6012
+ return theme.fg("error", ` \u2717 Buffering ${label.toLowerCase()} failed: ${data.error}`);
5634
6013
  }
5635
6014
  case "om_activation": {
5636
6015
  const kind = data.operationType === "reflection" ? "reflection" : "observations";
5637
6016
  const msgTokens = formatTokens2(data.tokensActivated);
5638
6017
  const obsTokens = formatTokens2(data.observationTokens);
5639
- return fg("success", ` \u2713 Activated ${kind}: -${msgTokens} msg tokens, +${obsTokens} obs tokens`);
6018
+ return theme.fg("success", ` \u2713 Activated ${kind}: -${msgTokens} msg tokens, +${obsTokens} obs tokens`);
5640
6019
  }
5641
6020
  }
5642
6021
  }
5643
- var OBSERVER_COLOR2 = "#f59e0b";
5644
- var REFLECTOR_COLOR2 = "#ef4444";
6022
+ var OBSERVER_COLOR2 = mastra.orange;
6023
+ var REFLECTOR_COLOR2 = mastra.red;
5645
6024
  var COLLAPSED_LINES = 10;
5646
6025
  function formatTokens3(tokens) {
5647
6026
  if (tokens === 0) return "0";
@@ -5966,13 +6345,13 @@ var AskQuestionDialogComponent = class extends Box {
5966
6345
  if (this.input) this.input.focused = value;
5967
6346
  }
5968
6347
  constructor(options) {
5969
- super(2, 1, (text) => bg("overlayBg", text));
6348
+ super(2, 1, (text) => theme.bg("overlayBg", text));
5970
6349
  this.onSubmit = options.onSubmit;
5971
6350
  this.onCancel = options.onCancel;
5972
- this.addChild(new Text(bold(fg("accent", "Question")), 0, 0));
6351
+ this.addChild(new Text(theme.bold(theme.fg("accent", "Question")), 0, 0));
5973
6352
  this.addChild(new Spacer(1));
5974
6353
  for (const line of options.question.split("\n")) {
5975
- this.addChild(new Text(fg("text", line), 0, 0));
6354
+ this.addChild(new Text(theme.fg("text", line), 0, 0));
5976
6355
  }
5977
6356
  this.addChild(new Spacer(1));
5978
6357
  if (options.options && options.options.length > 0) {
@@ -5984,7 +6363,7 @@ var AskQuestionDialogComponent = class extends Box {
5984
6363
  buildSelectMode(opts) {
5985
6364
  const items = opts.map((opt) => ({
5986
6365
  value: opt.label,
5987
- label: opt.description ? ` ${opt.label} ${fg("dim", opt.description)}` : ` ${opt.label}`
6366
+ label: opt.description ? ` ${opt.label} ${theme.fg("dim", opt.description)}` : ` ${opt.label}`
5988
6367
  }));
5989
6368
  this.selectList = new SelectList(items, Math.min(items.length, 8), getSelectListTheme());
5990
6369
  this.selectList.onSelect = (item) => {
@@ -5993,7 +6372,7 @@ var AskQuestionDialogComponent = class extends Box {
5993
6372
  this.selectList.onCancel = this.onCancel;
5994
6373
  this.addChild(this.selectList);
5995
6374
  this.addChild(new Spacer(1));
5996
- this.addChild(new Text(fg("dim", " \u2191\u2193 to navigate \xB7 Enter to select \xB7 Esc to skip"), 0, 0));
6375
+ this.addChild(new Text(theme.fg("dim", " \u2191\u2193 to navigate \xB7 Enter to select \xB7 Esc to skip"), 0, 0));
5997
6376
  }
5998
6377
  buildInputMode() {
5999
6378
  this.input = new Input();
@@ -6005,7 +6384,7 @@ var AskQuestionDialogComponent = class extends Box {
6005
6384
  };
6006
6385
  this.addChild(this.input);
6007
6386
  this.addChild(new Spacer(1));
6008
- this.addChild(new Text(fg("dim", " Enter to submit \xB7 Esc to skip"), 0, 0));
6387
+ this.addChild(new Text(theme.fg("dim", " Enter to submit \xB7 Esc to skip"), 0, 0));
6009
6388
  }
6010
6389
  handleInput(data) {
6011
6390
  if (this.selectList) {
@@ -6255,7 +6634,7 @@ async function handleSandboxAccessRequest(ctx, questionId, requestedPath, reason
6255
6634
  const questionComponent = new AskQuestionInlineComponent(
6256
6635
  {
6257
6636
  question: `Grant sandbox access to "${requestedPath}"?
6258
- ${fg("dim", `Reason: ${reason}`)}`,
6637
+ ${theme.fg("dim", `Reason: ${reason}`)}`,
6259
6638
  options: [
6260
6639
  { label: "Yes", description: "Allow access to this directory" },
6261
6640
  { label: "No", description: "Deny access" }
@@ -6603,7 +6982,7 @@ var ToolApprovalDialogComponent = class extends Box {
6603
6982
  this._focused = value;
6604
6983
  }
6605
6984
  constructor(options) {
6606
- super(2, 1, (text) => bg("overlayBg", text));
6985
+ super(2, 1, (text) => theme.bg("overlayBg", text));
6607
6986
  this.toolName = options.toolName;
6608
6987
  this.args = options.args;
6609
6988
  this.categoryLabel = options.categoryLabel;
@@ -6611,28 +6990,28 @@ var ToolApprovalDialogComponent = class extends Box {
6611
6990
  this.buildUI();
6612
6991
  }
6613
6992
  buildUI() {
6614
- this.addChild(new Text(fg("warning", "\u26A0 Tool Approval Required"), 0, 0));
6993
+ this.addChild(new Text(theme.fg("warning", "\u26A0 Tool Approval Required"), 0, 0));
6615
6994
  this.addChild(new Spacer(1));
6616
- this.addChild(new Text(fg("accent", `Tool: `) + fg("text", this.toolName), 0, 0));
6995
+ this.addChild(new Text(theme.fg("accent", `Tool: `) + theme.fg("text", this.toolName), 0, 0));
6617
6996
  if (this.categoryLabel) {
6618
- this.addChild(new Text(fg("accent", `Category: `) + fg("text", this.categoryLabel), 0, 0));
6997
+ this.addChild(new Text(theme.fg("accent", `Category: `) + theme.fg("text", this.categoryLabel), 0, 0));
6619
6998
  }
6620
6999
  this.addChild(new Spacer(1));
6621
- this.addChild(new Text(fg("muted", "Arguments:"), 0, 0));
7000
+ this.addChild(new Text(theme.fg("muted", "Arguments:"), 0, 0));
6622
7001
  const argsText = this.formatArgs(this.args);
6623
7002
  for (const line of argsText.split("\n").slice(0, 10)) {
6624
- this.addChild(new Text(fg("text", " " + line), 0, 0));
7003
+ this.addChild(new Text(theme.fg("text", " " + line), 0, 0));
6625
7004
  }
6626
7005
  if (argsText.split("\n").length > 10) {
6627
- this.addChild(new Text(fg("muted", " ... (truncated)"), 0, 0));
7006
+ this.addChild(new Text(theme.fg("muted", " ... (truncated)"), 0, 0));
6628
7007
  }
6629
7008
  this.addChild(new Spacer(1));
6630
7009
  const categoryHint = this.categoryLabel ? `lways allow ${this.categoryLabel.toLowerCase()}` : "lways allow category";
6631
- const dim = chalk10.hex("#555");
6632
- const key = chalk10.white.bold;
7010
+ const dimColor = chalk10.hex(theme.getTheme().dim);
7011
+ const key = chalk10.hex(theme.getTheme().text).bold;
6633
7012
  this.addChild(
6634
7013
  new Text(
6635
- fg("accent", "Allow? ") + key("y") + dim("es ") + key("n") + dim("o ") + key("a") + dim(categoryHint + " ") + key("Y") + dim("olo"),
7014
+ theme.fg("accent", "Allow? ") + key("y") + dimColor("es ") + key("n") + dimColor("o ") + key("a") + dimColor(categoryHint + " ") + key("Y") + dimColor("olo"),
6636
7015
  0,
6637
7016
  0
6638
7017
  )
@@ -6950,6 +7329,10 @@ async function dispatchEvent(event, ectx, state) {
6950
7329
  ectx.showInfo(`Switched to thread: ${event.threadId}`);
6951
7330
  await ectx.renderExistingMessages();
6952
7331
  await state.harness.loadOMProgress();
7332
+ const freshBranch = getCurrentGitBranch(state.projectInfo.rootPath);
7333
+ if (freshBranch) {
7334
+ state.projectInfo.gitBranch = freshBranch;
7335
+ }
6953
7336
  const threadState = state.harness.getState();
6954
7337
  if (state.taskProgress) {
6955
7338
  state.taskProgress.updateTasks(threadState.tasks ?? []);
@@ -7116,7 +7499,7 @@ var UserMessageComponent = class extends Container {
7116
7499
 
7117
7500
  // src/tui/render-messages.ts
7118
7501
  function renderCompletedTasksInline(state, tasks, insertIndex = -1, collapsed = false) {
7119
- const headerText = bold(fg("accent", "Tasks")) + fg("dim", ` [${tasks.length}/${tasks.length} completed]`);
7502
+ const headerText = theme.bold(theme.fg("accent", "Tasks")) + theme.fg("dim", ` [${tasks.length}/${tasks.length} completed]`);
7120
7503
  const container = new Container();
7121
7504
  container.addChild(new Spacer(1));
7122
7505
  container.addChild(new Text(headerText, 0, 0));
@@ -7131,7 +7514,11 @@ function renderCompletedTasksInline(state, tasks, insertIndex = -1, collapsed =
7131
7514
  }
7132
7515
  if (remaining > 0) {
7133
7516
  container.addChild(
7134
- new Text(fg("dim", ` ... ${remaining} more completed task${remaining > 1 ? "s" : ""} (ctrl+e to expand)`), 0, 0)
7517
+ new Text(
7518
+ theme.fg("dim", ` ... ${remaining} more completed task${remaining > 1 ? "s" : ""} (ctrl+e to expand)`),
7519
+ 0,
7520
+ 0
7521
+ )
7135
7522
  );
7136
7523
  }
7137
7524
  if (insertIndex >= 0) {
@@ -7146,10 +7533,10 @@ function renderClearedTasksInline(state, clearedTasks, insertIndex = -1) {
7146
7533
  container.addChild(new Spacer(1));
7147
7534
  const count = clearedTasks.length;
7148
7535
  const label = count === 1 ? "Task" : "Tasks";
7149
- container.addChild(new Text(fg("accent", `${label} cleared`), 0, 0));
7536
+ container.addChild(new Text(theme.fg("accent", `${label} cleared`), 0, 0));
7150
7537
  for (const task of clearedTasks) {
7151
7538
  const icon = task.status === "completed" ? chalk10.hex(mastra.green)("\u2713") : chalk10.hex(mastra.darkGray)("\u25CB");
7152
- const text = chalk10.dim.strikethrough(task.content);
7539
+ const text = chalk10.hex(theme.getTheme().dim).strikethrough(task.content);
7153
7540
  container.addChild(new Text(` ${icon} ${text}`, 0, 0));
7154
7541
  }
7155
7542
  if (insertIndex >= 0) {
@@ -7511,15 +7898,15 @@ function colorLine(line) {
7511
7898
  function renderBanner(version, appName) {
7512
7899
  const name = appName;
7513
7900
  if (name !== "Mastra Code") {
7514
- return fg("accent", "\u25C6") + " " + bold(fg("accent", name)) + fg("dim", ` v${version}`);
7901
+ return theme.fg("accent", "\u25C6") + " " + theme.bold(theme.fg("accent", name)) + theme.fg("dim", ` v${version}`);
7515
7902
  }
7516
7903
  const cols = process.stdout.columns || 80;
7517
7904
  if (cols < 30) {
7518
- return fg("accent", "\u25C6") + " " + bold(fg("accent", "Mastra Code")) + fg("dim", ` v${version}`);
7905
+ return theme.fg("accent", "\u25C6") + " " + theme.bold(theme.fg("accent", "Mastra Code")) + theme.fg("dim", ` v${version}`);
7519
7906
  }
7520
7907
  const art = cols >= 50 ? FULL_ART : SHORT_ART;
7521
7908
  const coloredLines = art.map((line) => colorLine(line));
7522
- coloredLines.push(fg("dim", `v${version}`));
7909
+ coloredLines.push(theme.fg("dim", `v${version}`));
7523
7910
  return coloredLines.join("\n");
7524
7911
  }
7525
7912
  var TaskProgressComponent = class extends Container {
@@ -7546,7 +7933,7 @@ var TaskProgressComponent = class extends Container {
7546
7933
  const completed = this.tasks.filter((t) => t.status === "completed").length;
7547
7934
  const total = this.tasks.length;
7548
7935
  if (completed === total) return;
7549
- const headerText = " " + bold(fg("accent", "Tasks")) + fg("dim", ` [${completed}/${total} completed]`);
7936
+ const headerText = " " + theme.bold(theme.fg("accent", "Tasks")) + theme.fg("dim", ` [${completed}/${total} completed]`);
7550
7937
  this.addChild(new Spacer(1));
7551
7938
  this.addChild(new Text(headerText, 0, 0));
7552
7939
  for (const task of this.tasks) {
@@ -7557,18 +7944,18 @@ var TaskProgressComponent = class extends Container {
7557
7944
  const indent = " ";
7558
7945
  switch (task.status) {
7559
7946
  case "completed": {
7560
- const icon = chalk10.green("\u2713");
7561
- const text = chalk10.green.strikethrough(task.content);
7947
+ const icon = theme.fg("success", "\u2713");
7948
+ const text = chalk10.hex(theme.getTheme().success).strikethrough(task.content);
7562
7949
  return `${indent}${icon} ${text}`;
7563
7950
  }
7564
7951
  case "in_progress": {
7565
- const icon = chalk10.yellow("\u25B6");
7566
- const text = chalk10.yellow.bold(task.activeForm);
7952
+ const icon = theme.fg("warning", "\u25B6");
7953
+ const text = theme.bold(theme.fg("warning", task.activeForm));
7567
7954
  return `${indent}${icon} ${text}`;
7568
7955
  }
7569
7956
  case "pending": {
7570
- const icon = chalk10.dim("\u25CB");
7571
- const text = chalk10.dim(task.content);
7957
+ const icon = theme.fg("dim", "\u25CB");
7958
+ const text = theme.fg("dim", task.content);
7572
7959
  return `${indent}${icon} ${text}`;
7573
7960
  }
7574
7961
  }
@@ -7676,13 +8063,13 @@ function buildLayout(state, refreshModelAuthStatus) {
7676
8063
  state.projectInfo.gitBranch ? `Branch: ${state.projectInfo.gitBranch}` : null,
7677
8064
  state.projectInfo.isWorktree ? `Worktree of: ${state.projectInfo.mainRepoPath}` : null,
7678
8065
  `User: ${getUserId(state.projectInfo.rootPath)}`
7679
- ].filter(Boolean).map((line) => fg("muted", line)).join("\n");
7680
- const sep = fg("dim", " \xB7 ");
8066
+ ].filter(Boolean).map((line) => theme.fg("muted", line)).join("\n");
8067
+ const sep = theme.fg("dim", " \xB7 ");
7681
8068
  const hintParts = [];
7682
8069
  if (state.harness.listModes().length > 1) {
7683
- hintParts.push(`${fg("accent", "\u21E7+Tab")} ${fg("muted", "cycle modes")}`);
8070
+ hintParts.push(`${theme.fg("accent", "\u21E7+Tab")} ${theme.fg("muted", "cycle modes")}`);
7684
8071
  }
7685
- hintParts.push(`${fg("accent", "/help")} ${fg("muted", "info & shortcuts")}`);
8072
+ hintParts.push(`${theme.fg("accent", "/help")} ${theme.fg("muted", "info & shortcuts")}`);
7686
8073
  const instructions = ` ${hintParts.join(sep)}`;
7687
8074
  state.ui.addChild(new Spacer(1));
7688
8075
  state.ui.addChild(new Text(banner, 1, 0));
@@ -7704,6 +8091,17 @@ function buildLayout(state, refreshModelAuthStatus) {
7704
8091
  refreshModelAuthStatus();
7705
8092
  state.ui.setFocus(state.editor);
7706
8093
  }
8094
+ function detectFdPath() {
8095
+ const whichCmd = process.platform === "win32" ? "where" : "which";
8096
+ for (const bin of ["fd", "fdfind"]) {
8097
+ try {
8098
+ const resolved = execFileSync(whichCmd, [bin], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0];
8099
+ if (resolved) return resolved;
8100
+ } catch {
8101
+ }
8102
+ }
8103
+ return null;
8104
+ }
7707
8105
  function setupAutocomplete(state) {
7708
8106
  const slashCommands = [
7709
8107
  { name: "new", description: "Start a new thread" },
@@ -7712,7 +8110,7 @@ function setupAutocomplete(state) {
7712
8110
  { name: "models:pack", description: "Switch model pack" },
7713
8111
  { name: "subagents", description: "Configure subagent model defaults" },
7714
8112
  { name: "om", description: "Configure Observational Memory models" },
7715
- { name: "think", description: "Set thinking level (Anthropic)" },
8113
+ { name: "think", description: "Set thinking (off|low|medium|high|xhigh|status)" },
7716
8114
  { name: "login", description: "Login with OAuth provider" },
7717
8115
  { name: "skills", description: "List available skills" },
7718
8116
  { name: "cost", description: "Show token usage and estimated costs" },
@@ -7747,6 +8145,7 @@ function setupAutocomplete(state) {
7747
8145
  },
7748
8146
  { name: "review", description: "Review a GitHub pull request" },
7749
8147
  { name: "setup", description: "Re-run the setup wizard" },
8148
+ { name: "theme", description: "Switch color theme (auto/dark/light)" },
7750
8149
  { name: "exit", description: "Exit the TUI" },
7751
8150
  { name: "help", description: "Show available commands" }
7752
8151
  ];
@@ -7760,7 +8159,8 @@ function setupAutocomplete(state) {
7760
8159
  description: customCmd.description || `Custom: ${customCmd.name}`
7761
8160
  });
7762
8161
  }
7763
- state.autocompleteProvider = new CombinedAutocompleteProvider(slashCommands, process.cwd());
8162
+ const fdPath = detectFdPath();
8163
+ state.autocompleteProvider = new CombinedAutocompleteProvider(slashCommands, process.cwd(), fdPath);
7764
8164
  state.editor.setAutocompleteProvider(state.autocompleteProvider);
7765
8165
  }
7766
8166
  async function loadCustomSlashCommands(state) {
@@ -7862,16 +8262,22 @@ var ShellOutputComponent = class extends Container {
7862
8262
  this.addChild(new Spacer(1));
7863
8263
  const statusIcon = exitCode === 0 ? "\u2713" : "\u2717";
7864
8264
  const statusColor = exitCode === 0 ? "success" : "error";
7865
- this.addChild(new Text(`${fg(statusColor, statusIcon)} ${bold(fg("muted", "$"))} ${fg("text", command)}`, 1, 0));
8265
+ this.addChild(
8266
+ new Text(
8267
+ `${theme.fg(statusColor, statusIcon)} ${theme.bold(theme.fg("muted", "$"))} ${theme.fg("text", command)}`,
8268
+ 1,
8269
+ 0
8270
+ )
8271
+ );
7866
8272
  const output = (stdout + (stderr ? (stdout ? "\n" : "") + stderr : "")).trimEnd();
7867
8273
  if (output) {
7868
8274
  const lines = output.split("\n");
7869
8275
  for (const line of lines) {
7870
- this.addChild(new Text(fg("toolOutput", ` ${line}`), 0, 0));
8276
+ this.addChild(new Text(theme.fg("toolOutput", ` ${line}`), 0, 0));
7871
8277
  }
7872
8278
  }
7873
8279
  if (exitCode !== 0) {
7874
- this.addChild(new Text(fg("error", ` Exit code: ${exitCode}`), 0, 0));
8280
+ this.addChild(new Text(theme.fg("error", ` Exit code: ${exitCode}`), 0, 0));
7875
8281
  }
7876
8282
  }
7877
8283
  };
@@ -8260,6 +8666,10 @@ var MastraTUI = class _MastraTUI {
8260
8666
  showInfo(this.state, "No model selected. Use /models to select a model, or /login to authenticate.");
8261
8667
  continue;
8262
8668
  }
8669
+ const allowed = await this.runUserPromptHook(userInput);
8670
+ if (!allowed) {
8671
+ continue;
8672
+ }
8263
8673
  const images = this.state.pendingImages.length > 0 ? [...this.state.pendingImages] : void 0;
8264
8674
  this.state.pendingImages = [];
8265
8675
  addUserMessage(this.state, {
@@ -8334,6 +8744,7 @@ var MastraTUI = class _MastraTUI {
8334
8744
  updateTerminalTitle(this.state);
8335
8745
  await renderExistingMessages(this.state);
8336
8746
  await renderExistingTasks(this.state);
8747
+ await this.checkClaudeMaxOAuthWarning();
8337
8748
  if (this.state.pendingLockConflict) {
8338
8749
  this.showThreadLockPrompt(this.state.pendingLockConflict.threadTitle, this.state.pendingLockConflict.ownerPid);
8339
8750
  this.state.pendingLockConflict = null;
@@ -8358,6 +8769,44 @@ var MastraTUI = class _MastraTUI {
8358
8769
  }
8359
8770
  async handleEvent(event) {
8360
8771
  await dispatchEvent(event, this.getEventContext(), this.state);
8772
+ if (event.type === "agent_end") {
8773
+ const stopReason = event.reason === "aborted" ? "aborted" : event.reason === "error" ? "error" : "complete";
8774
+ await this.runStopHook(stopReason);
8775
+ }
8776
+ }
8777
+ showHookWarnings(event, warnings) {
8778
+ for (const warning of warnings) {
8779
+ showInfo(this.state, `[${event}] ${warning}`);
8780
+ }
8781
+ }
8782
+ async runStopHook(stopReason) {
8783
+ const hookMgr = this.state.hookManager;
8784
+ if (!hookMgr) return;
8785
+ try {
8786
+ const result = await hookMgr.runStop(void 0, stopReason);
8787
+ this.showHookWarnings("Stop", result.warnings);
8788
+ if (!result.allowed && result.blockReason) {
8789
+ showError(this.state, `Stop hook blocked: ${result.blockReason}`);
8790
+ }
8791
+ } catch (error) {
8792
+ showError(this.state, `Stop hook failed: ${error instanceof Error ? error.message : String(error)}`);
8793
+ }
8794
+ }
8795
+ async runUserPromptHook(userInput) {
8796
+ const hookMgr = this.state.hookManager;
8797
+ if (!hookMgr) return true;
8798
+ try {
8799
+ const result = await hookMgr.runUserPromptSubmit(userInput);
8800
+ this.showHookWarnings("UserPromptSubmit", result.warnings);
8801
+ if (!result.allowed) {
8802
+ showError(this.state, result.blockReason || "Blocked by UserPromptSubmit hook");
8803
+ return false;
8804
+ }
8805
+ return true;
8806
+ } catch (error) {
8807
+ showError(this.state, `UserPromptSubmit hook failed: ${error instanceof Error ? error.message : String(error)}`);
8808
+ return false;
8809
+ }
8361
8810
  }
8362
8811
  // ===========================================================================
8363
8812
  /**
@@ -8426,6 +8875,26 @@ var MastraTUI = class _MastraTUI {
8426
8875
  this.state.ui.requestRender();
8427
8876
  this.state.chatContainer.invalidate();
8428
8877
  }
8878
+ /**
8879
+ * One-time startup check: if the user has Anthropic OAuth credentials and
8880
+ * hasn't yet acknowledged the Claude Max ToS warning, show it now.
8881
+ */
8882
+ async checkClaudeMaxOAuthWarning() {
8883
+ const authStorage = this.state.authStorage;
8884
+ if (!authStorage || !authStorage.isLoggedIn(ANTHROPIC_OAUTH_PROVIDER_ID)) return;
8885
+ const settings = loadSettings();
8886
+ if (settings.onboarding.claudeMaxOAuthWarningAcknowledgedAt) return;
8887
+ const result = await showClaudeMaxOAuthWarning(this.state, "startup");
8888
+ if (result === "continue") {
8889
+ settings.onboarding.claudeMaxOAuthWarningAcknowledgedAt = (/* @__PURE__ */ new Date()).toISOString();
8890
+ saveSettings(settings);
8891
+ } else if (result === "remove") {
8892
+ authStorage.logout(ANTHROPIC_OAUTH_PROVIDER_ID);
8893
+ settings.onboarding.claudeMaxOAuthWarningAcknowledgedAt = (/* @__PURE__ */ new Date()).toISOString();
8894
+ saveSettings(settings);
8895
+ await this.refreshModelAuthStatus();
8896
+ }
8897
+ }
8429
8898
  /**
8430
8899
  * Get the workspace, preferring harness-owned workspace over the direct option.
8431
8900
  */
@@ -8519,7 +8988,7 @@ var MastraTUI = class _MastraTUI {
8519
8988
  signal: dialog.signal
8520
8989
  }).then(async () => {
8521
8990
  this.state.ui.hideOverlay();
8522
- const { PROVIDER_DEFAULT_MODELS: PROVIDER_DEFAULT_MODELS2 } = await import('./storage-52Y5MKTG.js');
8991
+ const { PROVIDER_DEFAULT_MODELS: PROVIDER_DEFAULT_MODELS2 } = await import('./storage-PQOHJLBI.js');
8523
8992
  const defaultModel = PROVIDER_DEFAULT_MODELS2[providerId];
8524
8993
  if (defaultModel) {
8525
8994
  await this.state.harness.switchModel({ modelId: defaultModel });
@@ -8564,6 +9033,7 @@ var MastraTUI = class _MastraTUI {
8564
9033
  };
8565
9034
  };
8566
9035
  const access = await buildAccess();
9036
+ const hasProviderAccess = Object.values(access).some(Boolean);
8567
9037
  const savedSettings = loadSettings();
8568
9038
  const modePacks = getAvailableModePacks(access, savedSettings.customModelPacks);
8569
9039
  const omPacks = getAvailableOmPacks(access);
@@ -8582,6 +9052,7 @@ var MastraTUI = class _MastraTUI {
8582
9052
  authProviders,
8583
9053
  modePacks,
8584
9054
  omPacks,
9055
+ hasProviderAccess,
8585
9056
  previous,
8586
9057
  onComplete: async (result) => {
8587
9058
  this.state.activeOnboarding = void 0;
@@ -8599,11 +9070,23 @@ var MastraTUI = class _MastraTUI {
8599
9070
  resolve2();
8600
9071
  },
8601
9072
  onLogin: (providerId, done) => {
9073
+ if (providerId === ANTHROPIC_OAUTH_PROVIDER_ID) {
9074
+ const s = loadSettings();
9075
+ s.onboarding.claudeMaxOAuthWarningAcknowledgedAt = (/* @__PURE__ */ new Date()).toISOString();
9076
+ saveSettings(s);
9077
+ }
8602
9078
  this.performLogin(providerId).then(async () => {
8603
- const updatedAccess = await buildAccess();
8604
- component.updateModePacks(getAvailableModePacks(updatedAccess, savedSettings.customModelPacks));
8605
- component.updateOmPacks(getAvailableOmPacks(updatedAccess));
8606
- done();
9079
+ try {
9080
+ const updatedAccess = await buildAccess();
9081
+ const updatedHasAccess = Object.values(updatedAccess).some(Boolean);
9082
+ component.updateModePacks(getAvailableModePacks(updatedAccess, savedSettings.customModelPacks));
9083
+ component.updateOmPacks(getAvailableOmPacks(updatedAccess));
9084
+ component.updateHasProviderAccess(updatedHasAccess);
9085
+ } catch (err) {
9086
+ console.error("Failed to refresh provider access after login:", err);
9087
+ } finally {
9088
+ done();
9089
+ }
8607
9090
  });
8608
9091
  },
8609
9092
  onSelectModel: async (title, modeColor) => {
@@ -8725,19 +9208,19 @@ var LoginSelectorComponent = class extends Box {
8725
9208
  onSelectCallback;
8726
9209
  onCancelCallback;
8727
9210
  constructor(mode, authSource, onSelect, onCancel) {
8728
- super(2, 1, (text) => bg("overlayBg", text));
9211
+ super(2, 1, (text) => theme.bg("overlayBg", text));
8729
9212
  this.mode = mode;
8730
9213
  this.authSource = authSource;
8731
9214
  this.onSelectCallback = onSelect;
8732
9215
  this.onCancelCallback = onCancel;
8733
9216
  this.loadProviders();
8734
9217
  const title = mode === "login" ? "Select provider to login:" : "Select provider to logout:";
8735
- this.addChild(new Text(fg("text", title)));
9218
+ this.addChild(new Text(theme.fg("text", title)));
8736
9219
  this.addChild(new Spacer(1));
8737
9220
  this.listContainer = new Container();
8738
9221
  this.addChild(this.listContainer);
8739
9222
  this.addChild(new Spacer(1));
8740
- this.addChild(new Text(fg("muted", "Press Enter to select, Escape to cancel")));
9223
+ this.addChild(new Text(theme.fg("muted", "Press Enter to select, Escape to cancel")));
8741
9224
  this.updateList();
8742
9225
  }
8743
9226
  loadProviders() {
@@ -8750,10 +9233,10 @@ var LoginSelectorComponent = class extends Box {
8750
9233
  if (!provider) continue;
8751
9234
  const isSelected = i === this.selectedIndex;
8752
9235
  const isLoggedIn = this.authSource.isLoggedIn(provider.id);
8753
- const statusIndicator = isLoggedIn ? fg("success", " \u2713 logged in") : "";
9236
+ const statusIndicator = isLoggedIn ? theme.fg("success", " \u2713 logged in") : "";
8754
9237
  let line = "";
8755
9238
  if (isSelected) {
8756
- line = fg("accent", "\u2192 " + provider.name) + statusIndicator;
9239
+ line = theme.fg("accent", "\u2192 " + provider.name) + statusIndicator;
8757
9240
  } else {
8758
9241
  line = " " + provider.name + statusIndicator;
8759
9242
  }
@@ -8761,7 +9244,7 @@ var LoginSelectorComponent = class extends Box {
8761
9244
  }
8762
9245
  if (this.allProviders.length === 0) {
8763
9246
  const message = this.mode === "login" ? "No OAuth providers available" : "No OAuth providers logged in. Use /login first.";
8764
- this.listContainer.addChild(new Text(fg("muted", message)));
9247
+ this.listContainer.addChild(new Text(theme.fg("muted", message)));
8765
9248
  }
8766
9249
  }
8767
9250
  handleInput(keyData) {
@@ -8783,6 +9266,6 @@ var LoginSelectorComponent = class extends Box {
8783
9266
  }
8784
9267
  };
8785
9268
 
8786
- export { AssistantMessageComponent, LoginDialogComponent, LoginSelectorComponent, MastraTUI, ModelSelectorComponent, OMProgressComponent, ToolExecutionComponentEnhanced, UserMessageComponent, createTUIState, formatOMStatus };
8787
- //# sourceMappingURL=chunk-LYETHS2L.js.map
8788
- //# sourceMappingURL=chunk-LYETHS2L.js.map
9269
+ export { AssistantMessageComponent, LoginDialogComponent, LoginSelectorComponent, MastraTUI, ModelSelectorComponent, OMProgressComponent, ToolExecutionComponentEnhanced, UserMessageComponent, createTUIState, detectTerminalTheme, formatOMStatus };
9270
+ //# sourceMappingURL=chunk-X3BGE7CL.js.map
9271
+ //# sourceMappingURL=chunk-X3BGE7CL.js.map