@dotobokuri/fleet-cli 1.1.2 → 1.2.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.
package/dist/index.js CHANGED
@@ -24041,7 +24041,7 @@ async function executeOneShot(opts) {
24041
24041
  // ../../packages/fleet-infra/dist/index.js
24042
24042
  init_chunk_6K6JYAYY();
24043
24043
 
24044
- // ../../packages/fleet-infra/dist/chunk-UJJES4N6.js
24044
+ // ../../packages/fleet-infra/dist/chunk-JMJCE3TR.js
24045
24045
  init_chunk_EDU3Y4RB();
24046
24046
 
24047
24047
  // ../../packages/fleet-infra/dist/chunk-NRRUNHG6.js
@@ -24134,30 +24134,30 @@ function ensureSafeDirectoryIfSensitive(dir, sensitivity) {
24134
24134
  }
24135
24135
  }
24136
24136
 
24137
- // ../../packages/fleet-infra/dist/chunk-UJJES4N6.js
24137
+ // ../../packages/fleet-infra/dist/chunk-JMJCE3TR.js
24138
24138
  init_chunk_PZ5AY32C();
24139
24139
  import * as path8 from "path";
24140
- var preset_exports = {};
24141
- __export2(preset_exports, {
24142
- createEmptyPresetData: () => createEmptyPresetData,
24143
- createPresetService: () => createPresetService,
24144
- createPresetStore: () => createPresetStore,
24145
- sanitizePresetData: () => sanitizePresetData
24146
- });
24147
- var PRESET_VERSION = 1;
24148
- var PRESET_FILE_NAME = "presets.json";
24149
- var LOCK_DIR_NAME = "presets.json.lock";
24140
+ var global_options_exports = {};
24141
+ __export2(global_options_exports, {
24142
+ createEmptyGlobalOptionsData: () => createEmptyGlobalOptionsData,
24143
+ createGlobalOptionsService: () => createGlobalOptionsService,
24144
+ createGlobalOptionsStore: () => createGlobalOptionsStore,
24145
+ sanitizeGlobalOptionsData: () => sanitizeGlobalOptionsData
24146
+ });
24147
+ var GLOBAL_OPTIONS_VERSION = 1;
24148
+ var GLOBAL_OPTIONS_FILE_NAME = "settings.json";
24149
+ var LOCK_DIR_NAME = "settings.json.lock";
24150
24150
  var LOCK_OWNER_FILE_NAME = "owner";
24151
- var TEMP_FILE_PREFIX = `.tmp-${PRESET_FILE_NAME}-`;
24152
- function createPresetStore(deps = {}) {
24151
+ var TEMP_FILE_PREFIX = `.tmp-${GLOBAL_OPTIONS_FILE_NAME}-`;
24152
+ function createGlobalOptionsStore(deps = {}) {
24153
24153
  const dataDir2 = deps.dataDir ?? getFleetDataDir();
24154
- const presetPath = path8.join(dataDir2, PRESET_FILE_NAME);
24154
+ const optionsPath = path8.join(dataDir2, GLOBAL_OPTIONS_FILE_NAME);
24155
24155
  const lockDir = path8.join(dataDir2, LOCK_DIR_NAME);
24156
24156
  const store2 = createDurableJsonStore({
24157
- filePath: presetPath,
24157
+ filePath: optionsPath,
24158
24158
  lockDir,
24159
24159
  lockOwnerFileName: LOCK_OWNER_FILE_NAME,
24160
- sanitize: (value) => sanitizePresetData(value).data,
24160
+ sanitize: (value) => sanitizeGlobalOptionsData(value).data,
24161
24161
  sensitivity: "sensitive",
24162
24162
  timeoutMs: deps.timeoutMs,
24163
24163
  staleLockMs: deps.staleLockMs,
@@ -24165,113 +24165,50 @@ function createPresetStore(deps = {}) {
24165
24165
  now: deps.now
24166
24166
  });
24167
24167
  return {
24168
- path: presetPath,
24168
+ path: optionsPath,
24169
24169
  load: () => store2.load(),
24170
- save: (data) => store2.save(sanitizePresetData(data).data),
24171
- update: (mutate) => store2.update((current) => sanitizePresetData(mutate(current)).data)
24170
+ save: (data) => store2.save(sanitizeGlobalOptionsData(data).data),
24171
+ update: (mutate) => store2.update((current) => sanitizeGlobalOptionsData(mutate(current)).data)
24172
24172
  };
24173
24173
  }
24174
- function createEmptyPresetData() {
24175
- return { version: PRESET_VERSION, byCli: {} };
24174
+ function createEmptyGlobalOptionsData() {
24175
+ return {
24176
+ version: GLOBAL_OPTIONS_VERSION
24177
+ };
24176
24178
  }
24177
- function sanitizePresetData(value) {
24179
+ function sanitizeGlobalOptionsData(value) {
24178
24180
  if (!isRecord(value)) {
24179
- return { data: createEmptyPresetData(), changed: true };
24180
- }
24181
- let changed = value.version !== PRESET_VERSION;
24182
- const defaultCliId = typeof value.defaultCliId === "string" && value.defaultCliId.length > 0 ? value.defaultCliId : void 0;
24183
- if ("defaultCliId" in value && defaultCliId === void 0) {
24184
- changed = true;
24185
- }
24186
- const byCli = /* @__PURE__ */ Object.create(null);
24187
- if (isRecord(value.byCli)) {
24188
- for (const [cliId, rawPreset] of Object.entries(value.byCli)) {
24189
- if (!isSafeDictionaryKey(cliId)) {
24190
- changed = true;
24191
- continue;
24192
- }
24193
- const preset = sanitizeCliPreset(rawPreset);
24194
- if (preset.changed) {
24195
- changed = true;
24196
- }
24197
- if (Object.keys(preset.data).length > 0) {
24198
- byCli[cliId] = preset.data;
24199
- }
24200
- }
24201
- } else if ("byCli" in value) {
24202
- changed = true;
24181
+ return { data: createEmptyGlobalOptionsData(), changed: true };
24203
24182
  }
24204
- return { data: { version: PRESET_VERSION, ...defaultCliId ? { defaultCliId } : {}, byCli }, changed };
24205
- }
24206
- function sanitizeCliPreset(value) {
24207
- if (!isRecord(value)) {
24208
- return { data: {}, changed: true };
24183
+ if (value.version !== GLOBAL_OPTIONS_VERSION) {
24184
+ return { data: createEmptyGlobalOptionsData(), changed: true };
24209
24185
  }
24210
24186
  const data = {
24211
- ...typeof value.model === "string" && value.model.length > 0 ? { model: value.model } : {},
24187
+ version: GLOBAL_OPTIONS_VERSION,
24212
24188
  ...typeof value.native === "boolean" ? { native: value.native } : {},
24213
24189
  ...typeof value.replaceSystemPrompt === "boolean" ? { replaceSystemPrompt: value.replaceSystemPrompt } : {},
24214
- ...typeof value.enableMetaphor === "boolean" ? { enableMetaphor: value.enableMetaphor } : {},
24215
- ...typeof value.cursorSync === "boolean" ? { cursorSync: value.cursorSync } : {}
24190
+ ...typeof value.enableMetaphor === "boolean" ? { enableMetaphor: value.enableMetaphor } : {}
24216
24191
  };
24217
- const changed = Object.keys(value).some((key) => !(key in data)) || "model" in value && data.model === void 0 || "native" in value && data.native === void 0 || "replaceSystemPrompt" in value && data.replaceSystemPrompt === void 0 || "enableMetaphor" in value && data.enableMetaphor === void 0 || "cursorSync" in value && data.cursorSync === void 0;
24192
+ const allowedKeys = /* @__PURE__ */ new Set(["version", "native", "replaceSystemPrompt", "enableMetaphor"]);
24193
+ const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "native" in value && typeof value.native !== "boolean" || "replaceSystemPrompt" in value && typeof value.replaceSystemPrompt !== "boolean" || "enableMetaphor" in value && typeof value.enableMetaphor !== "boolean";
24218
24194
  return { data, changed };
24219
24195
  }
24220
- function isSafeDictionaryKey(key) {
24221
- return key.length > 0 && key !== "__proto__" && key !== "prototype" && key !== "constructor";
24222
- }
24223
24196
  function isRecord(value) {
24224
24197
  return typeof value === "object" && value !== null && !Array.isArray(value);
24225
24198
  }
24226
- function createPresetService(deps = {}) {
24227
- const store2 = deps.store ?? createPresetStore({ dataDir: deps.dataDir });
24199
+ function createGlobalOptionsService(deps = {}) {
24200
+ const store2 = deps.store ?? createGlobalOptionsStore({ dataDir: deps.dataDir });
24228
24201
  return {
24229
24202
  load: () => store2.load(),
24230
- resolveCliPreset: (cliId) => store2.load().byCli[cliId] ?? {},
24231
- saveCliPreset: (cliId, values) => updatePreset(store2, { cliId, values }),
24232
- saveDefaultCliId: (cliId) => updatePreset(store2, { defaultCliId: cliId ?? null }),
24233
- resetCliPreset: (cliId) => updatePreset(store2, { cliId, values: null }),
24234
- update: (mutation) => updatePreset(store2, mutation)
24235
- };
24236
- }
24237
- function updatePreset(store2, mutation) {
24238
- return store2.update((current) => applyPresetMutation(current, mutation));
24239
- }
24240
- function applyPresetMutation(current, mutation) {
24241
- const byCli = /* @__PURE__ */ Object.create(null);
24242
- for (const [cliId, preset] of Object.entries(current.byCli)) {
24243
- if (isSafeDictionaryKey2(cliId)) {
24244
- byCli[cliId] = preset;
24245
- }
24246
- }
24247
- if (mutation.cliId !== void 0) {
24248
- if (!isSafeDictionaryKey2(mutation.cliId)) {
24249
- return { version: 1, ...current.defaultCliId ? { defaultCliId: current.defaultCliId } : {}, byCli };
24250
- }
24251
- if (mutation.values === null) {
24252
- delete byCli[mutation.cliId];
24253
- } else if (mutation.values !== void 0) {
24254
- byCli[mutation.cliId] = prunePreset(mutation.values);
24255
- }
24256
- }
24257
- const nextDefaultCliId = mutation.defaultCliId === null ? void 0 : mutation.defaultCliId ?? current.defaultCliId;
24258
- return {
24259
- version: 1,
24260
- ...nextDefaultCliId ? { defaultCliId: nextDefaultCliId } : {},
24261
- byCli
24262
- };
24263
- }
24264
- function prunePreset(preset) {
24265
- return {
24266
- ...preset.model !== void 0 ? { model: preset.model } : {},
24267
- ...preset.native !== void 0 ? { native: preset.native } : {},
24268
- ...preset.replaceSystemPrompt !== void 0 ? { replaceSystemPrompt: preset.replaceSystemPrompt } : {},
24269
- ...preset.enableMetaphor !== void 0 ? { enableMetaphor: preset.enableMetaphor } : {},
24270
- ...preset.cursorSync !== void 0 ? { cursorSync: preset.cursorSync } : {}
24203
+ save: (data) => {
24204
+ store2.save(data);
24205
+ return store2.load();
24206
+ },
24207
+ update: (mutate) => updateGlobalOptions(store2, mutate)
24271
24208
  };
24272
24209
  }
24273
- function isSafeDictionaryKey2(key) {
24274
- return key.length > 0 && key !== "__proto__" && key !== "prototype" && key !== "constructor";
24210
+ function updateGlobalOptions(store2, mutate) {
24211
+ return store2.update(mutate);
24275
24212
  }
24276
24213
 
24277
24214
  // ../../packages/fleet-infra/dist/index.js
@@ -24302,7 +24239,7 @@ __export2(fs_store_exports, {
24302
24239
  // ../../packages/fleet-infra/dist/index.js
24303
24240
  init_chunk_PZ5AY32C();
24304
24241
  function createInfraServices(deps = {}) {
24305
- const presetService = createPresetService();
24242
+ const globalOptionsService = createGlobalOptionsService();
24306
24243
  const authService = createAuthService({ authPath: deps.authPath ?? DEFAULT_AUTH_PATH });
24307
24244
  return {
24308
24245
  agent: agent_exports,
@@ -24310,9 +24247,9 @@ function createInfraServices(deps = {}) {
24310
24247
  authService,
24311
24248
  dataDir,
24312
24249
  fsStore: fs_store_exports,
24313
- preset: preset_exports,
24250
+ globalOptions: global_options_exports,
24314
24251
  executorPortRuntime,
24315
- presetService
24252
+ globalOptionsService
24316
24253
  };
24317
24254
  }
24318
24255
 
@@ -31835,6 +31772,7 @@ async function runTaskForceBackend(registry3, cliType, carrierId, requestKey, re
31835
31772
  type: "track:begin",
31836
31773
  jobId,
31837
31774
  trackId,
31775
+ startedAt: execStartedAt,
31838
31776
  requestPreview: request.trim().split(/\r?\n/, 1)[0]
31839
31777
  });
31840
31778
  try {
@@ -31884,7 +31822,7 @@ async function runTaskForceBackend(registry3, cliType, carrierId, requestKey, re
31884
31822
  return buildTaskForceResult(cliType, result);
31885
31823
  } catch (error51) {
31886
31824
  const message = error51 instanceof Error ? error51.message : String(error51);
31887
- emitStreamEvent(registry3, { type: "track:finalized", jobId, trackId, status: "err", error: message });
31825
+ emitStreamEvent(registry3, { type: "track:finalized", jobId, trackId, status: "err", finishedAt: Date.now(), error: message });
31888
31826
  throw error51;
31889
31827
  }
31890
31828
  }
@@ -31951,6 +31889,7 @@ function emitTrackFinalized(registry3, jobId, trackId, result) {
31951
31889
  jobId,
31952
31890
  trackId,
31953
31891
  status: toTrackFinalStatus(status),
31892
+ finishedAt: Date.now(),
31954
31893
  error: status === "aborted" ? "aborted" : result.error,
31955
31894
  fallbackText: sanitizeChunk(result.responseText),
31956
31895
  fallbackThought: sanitizeChunk(result.thoughtText)
@@ -32171,6 +32110,7 @@ async function runSingleCarrier(opts) {
32171
32110
  type: "track:begin",
32172
32111
  jobId: opts.jobId,
32173
32112
  trackId: opts.carrierId,
32113
+ startedAt: execStartedAt,
32174
32114
  requestPreview: opts.request.trim().split(/\r?\n/, 1)[0]
32175
32115
  });
32176
32116
  try {
@@ -32220,6 +32160,7 @@ async function runSingleCarrier(opts) {
32220
32160
  jobId: opts.jobId,
32221
32161
  trackId: opts.carrierId,
32222
32162
  status: toTrackFinalStatus(finalStatus),
32163
+ finishedAt: Date.now(),
32223
32164
  sessionId,
32224
32165
  fallbackText: sanitizeChunk(execResult.responseText),
32225
32166
  fallbackThought: sanitizeChunk(execResult.thoughtText),
@@ -32242,6 +32183,7 @@ async function runSingleCarrier(opts) {
32242
32183
  jobId: opts.jobId,
32243
32184
  trackId: opts.carrierId,
32244
32185
  status: "err",
32186
+ finishedAt: Date.now(),
32245
32187
  error: message
32246
32188
  });
32247
32189
  throw error51;
@@ -32255,7 +32197,8 @@ function emitJobRegistered(registry3, jobId, carrierId, sortieKey, label, starte
32255
32197
  displayCli: carrierId,
32256
32198
  displayName: resolveCarrierDisplayName(registry3, carrierId),
32257
32199
  kind: "carrier",
32258
- runId
32200
+ runId,
32201
+ startedAt
32259
32202
  }];
32260
32203
  emitStreamEvent(registry3, {
32261
32204
  type: "job:registered",
@@ -33976,7 +33919,7 @@ Upon re-evaluation downgrade, halt the current phase and re-apply the appropriat
33976
33919
 
33977
33920
  The two gates are orthogonal: an anchored objective with speculative evidence still fails the Context Confidence gate, and conversely, complete evidence on a drifting objective still fails the Mission Anchor self-check. Apply both independently — never collapse one into the other.
33978
33921
 
33979
- ### Admiral's role
33922
+ ### Admiral's role (yours — the host agent, 제독; not the user)
33980
33923
 
33981
33924
  Evaluate confidence honestly and apply the gate strictly. Do not flatten partial evidence into a confident-sounding label. Surfacing ${"`"}partial${"`"} or ${"`"}speculative${"`"} is a feature, not a failure — it triggers the correct workflow (re-entry) and prevents low-quality plans from contaminating execution.`
33982
33925
  };
@@ -34002,7 +33945,7 @@ Any phase produces output (from a Carrier, from the Admiral's own analysis, or f
34002
33945
  ### Depth limit
34003
33946
  Deep Dive verification depth is capped at **2 iterations**. If after 2 rounds of audit + follow-up verification a claim remains unconfirmed, mark it as ${"``"}[Unverified — depth limit reached]${"``"} and report it to the Admiral of the Navy (대원수). Do not continue iterating — the cost of further verification outweighs the risk of surfaced uncertainty.
34004
33947
 
34005
- ### Admiral's role
33948
+ ### Admiral's role (yours — the host agent, 제독; not the user)
34006
33949
  Your role throughout Deep Dive is **coordination, not investigation**. Route, synthesize, and report — do not spend effort on direct deep analysis. Do **not** flatten uncertainty into confident-sounding summaries — preserve and surface ambiguity honestly.`
34007
33950
  };
34008
33951
  var MISSION_ANCHOR = {
@@ -34025,7 +33968,7 @@ Apply this Standing Order before entering Phase 1 and at every phase boundary in
34025
33968
  4. **Drift Recovery** — If the self-check is ${"``"}partial — adjust${"``"} or ${"``"}drift — halt${"``"}, do not enter the next phase. Return to the original user request, re-derive the anchor, and continue only after the objective is clear.
34026
33969
  5. **Compact Mode** — For trivial single-phase tasks with no more than 3 changed lines, state the Objective once and omit per-phase Anchor Recall lines. Multi-phase tasks never use this exemption.
34027
33970
 
34028
- ### Admiral's role
33971
+ ### Admiral's role (yours — the host agent, 제독; not the user)
34029
33972
  Your role is to preserve objective alignment, not to expand scope. Use the anchor to decide what to investigate, delegate, implement, review, document, skip, or halt.`
34030
33973
  };
34031
33974
  var RESULT_INTEGRITY = {
@@ -34078,6 +34021,7 @@ var FLEET_ROLE_PROMPT = String.raw`
34078
34021
  You are the host agent coordinating the Agent Harness Fleet for the user.
34079
34022
 
34080
34023
  # Action Guidelines
34024
+ - The user is the Admiral of the Navy (대원수). Address and refer to the user only as 대원수 — never as 제독, which is your own title (the host agent's). This rule holds whether or not the tone overlay is active.
34081
34025
  - Treat the ${"`"}<fleet section=\"protocol\">${"`"} and ${"`"}<fleet section=\"standing-orders\">${"`"} blocks as the binding operational doctrine for every task. The Fleet Action Protocol's phases and all Standing Orders apply unconditionally — they override any default behavior in conflict.
34082
34026
  - Fleet MCP surfaces (${"`"}fleet-carriers${"`"}, ${"`"}fleet-wiki${"`"}) and their tools may be lazy-loaded; never declare a Fleet tool unavailable without first inspecting these surfaces.
34083
34027
  - When delegating to a Carrier, state which Carrier in your reply to the user.
@@ -34086,8 +34030,10 @@ You are the host agent coordinating the Agent Harness Fleet for the user.
34086
34030
  `;
34087
34031
  var FLEET_PERSONA_PROMPT = String.raw`
34088
34032
  # Persona
34089
- You are the Admiral (제독) commanding this Fleet.
34090
- Your ultimate superior is the Admiral of the Navy (대원수), the supreme commander above the entire formation.
34033
+ You are the Admiral (제독) commanding this Fleet — this title denotes YOURSELF ALONE, used only in the first person.
34034
+ The user you serve is your ultimate superior, the Admiral of the Navy (대원수), the supreme commander above the entire formation.
34035
+ ALWAYS address and refer to the user as 대원수 — never as 제독. ALWAYS reserve 제독 for yourself — never apply it to the user.
34036
+ "제독" = you (the host agent); "대원수" = the user. These are two distinct roles; never collapse them onto one title.
34091
34037
  When operating under grand-fleet, intermediate strategic dispatch arrives through the Admiralty's Fleet Admiral (사령관) chain of command.
34092
34038
  You command your own Captains (함장들) of Carriers within this workspace.
34093
34039
  `;
@@ -36039,6 +35985,41 @@ function dim(text, colorEnabled) {
36039
35985
  return paint(ANSI_DIM, text, colorEnabled);
36040
35986
  }
36041
35987
 
35988
+ // src/mission-control/layout.ts
35989
+ function maxVisibleWidth(texts) {
35990
+ let maxWidth = 0;
35991
+ for (const text of texts) {
35992
+ maxWidth = Math.max(maxWidth, visibleWidth(text));
35993
+ }
35994
+ return maxWidth;
35995
+ }
35996
+ function padEndVisible(text, width) {
35997
+ return `${text}${" ".repeat(Math.max(0, width - visibleWidth(text)))}`;
35998
+ }
35999
+ function computeBlockLeftPad(blockWidth, innerWidth) {
36000
+ return Math.max(0, Math.floor((Math.max(0, innerWidth) - Math.max(0, blockWidth)) / 2));
36001
+ }
36002
+ function renderChoiceBlock(options2) {
36003
+ const labelWidth = maxVisibleWidth(options2.rows.map((row) => row.label));
36004
+ const rowTexts = options2.rows.map((row) => formatChoiceRow(row, labelWidth));
36005
+ const blockWidth = maxVisibleWidth(rowTexts);
36006
+ const leftPad = computeBlockLeftPad(blockWidth, options2.innerWidth);
36007
+ const rowWidth = Math.max(0, options2.innerWidth - leftPad);
36008
+ return rowTexts.map((row) => `${" ".repeat(leftPad)}${truncateToWidth(row, rowWidth)}`);
36009
+ }
36010
+ function renderKeyValueBlock(options2) {
36011
+ const keyWidth = maxVisibleWidth(options2.rows.map((row) => row.key));
36012
+ const rowTexts = options2.rows.map((row) => `${padEndVisible(row.key, keyWidth)}: ${row.value}`);
36013
+ const blockWidth = maxVisibleWidth(rowTexts);
36014
+ const leftPad = computeBlockLeftPad(blockWidth, options2.innerWidth);
36015
+ const rowWidth = Math.max(0, options2.innerWidth - leftPad);
36016
+ return rowTexts.map((row) => `${" ".repeat(leftPad)}${truncateToWidth(row, rowWidth)}`);
36017
+ }
36018
+ function formatChoiceRow(row, labelWidth) {
36019
+ const label = padEndVisible(row.label, labelWidth);
36020
+ return row.trailing === void 0 ? `${row.marker} ${label}` : `${row.marker} ${label} ${row.trailing}`;
36021
+ }
36022
+
36042
36023
  // src/mission-control/welcome.ts
36043
36024
  var FLEET_BANNER = ASCII_FLEET_BANNER;
36044
36025
  var BANNER_VISIBLE_WIDTH = visibleWidth(FLEET_BANNER[0] ?? "");
@@ -36115,6 +36096,7 @@ var MISSION_CONTROL_THEME = {
36115
36096
  fg: (name, text) => MISSION_CONTROL_THEME[name](text),
36116
36097
  muted: (text) => paint2("\x1B[38;2;160;150;180m", text),
36117
36098
  reset: (text) => `${text}${ANSI_RESET3}`,
36099
+ section: (text) => paint2(FLEET_COMMAND, text),
36118
36100
  success: (text) => paint2("\x1B[38;2;80;200;160m", text),
36119
36101
  warning: (text) => paint2("\x1B[38;2;255;200;100m", text)
36120
36102
  };
@@ -36129,8 +36111,8 @@ var STYLE = {
36129
36111
  function renderMissionControl(width, options2) {
36130
36112
  const innerWidth = Math.max(0, width);
36131
36113
  const banner = buildFleetBanner(innerWidth, options2.bannerPhase ?? 0);
36132
- const choiceWidth = computeChoiceWidth(options2.cliOptions);
36133
- const choiceLeftPad = Math.max(0, Math.floor((innerWidth - choiceWidth) / 2));
36114
+ const choiceWidth = CHOICE_INDENT + maxVisibleWidth(options2.cliOptions.map((option3) => option3.label));
36115
+ const choiceLeftPad = computeBlockLeftPad(choiceWidth, innerWidth);
36134
36116
  const lines = [""];
36135
36117
  if (banner.length > 0) {
36136
36118
  for (const line of banner) {
@@ -36174,10 +36156,9 @@ function renderChoiceLine(options2) {
36174
36156
  const marker = options2.selected ? STYLE.accent(SELECTED_MARKER) : STYLE.dim(IDLE_MARKER);
36175
36157
  const number4 = STYLE.muted(`${options2.index + 1}.`);
36176
36158
  const label = colorizeProvider(options2.entry.id, options2.entry.label);
36177
- const chips = options2.entry.optionChips && options2.entry.optionChips.length > 0 ? STYLE.dim(` [${options2.entry.optionChips.join(" \xB7 ")}]`) : "";
36178
36159
  const prefix = `${" ".repeat(options2.leftPad)}${marker} ${number4} `;
36179
36160
  const remaining = Math.max(0, options2.innerWidth - visibleWidth(prefix));
36180
- return `${prefix}${truncateToWidth(`${label}${chips}`, remaining)}`;
36161
+ return `${prefix}${truncateToWidth(label, remaining)}`;
36181
36162
  }
36182
36163
  function renderCountsLine(counts, release2, innerWidth) {
36183
36164
  const segments = [];
@@ -36241,16 +36222,6 @@ function colorizeProvider(cliId, text) {
36241
36222
  const color = getCarrierAnsi(cliId);
36242
36223
  return color ? `${color}${text}${ANSI_RESET3}` : text;
36243
36224
  }
36244
- function computeChoiceWidth(cliOptions) {
36245
- let maxLabelWidth = 0;
36246
- for (const option3 of cliOptions) {
36247
- const width = visibleWidth(option3.label);
36248
- if (width > maxLabelWidth) {
36249
- maxLabelWidth = width;
36250
- }
36251
- }
36252
- return CHOICE_INDENT + maxLabelWidth;
36253
- }
36254
36225
  function paint2(code, text) {
36255
36226
  return paint(code, text, true);
36256
36227
  }
@@ -36336,6 +36307,8 @@ function handleCarrierStatusOverlayInput(data, inputState, controller) {
36336
36307
  var ANSI_RESET4 = "\x1B[0m";
36337
36308
  var ANIM_INTERVAL_MS = 100;
36338
36309
  var BREATHING_CYCLE_FRAMES = 10;
36310
+ var TOKEN_COUNTUP_EASING_FACTOR = 0.25;
36311
+ var TOKEN_COUNTUP_MIN_STEP = 8;
36339
36312
  var PANEL_DIM_COLOR = "\x1B[38;2;160;150;180m";
36340
36313
  var TASKFORCE_BADGE_RGB = [100, 180, 255];
36341
36314
  var TASKFORCE_BADGE_COLOR2 = `\x1B[38;2;${TASKFORCE_BADGE_RGB[0]};${TASKFORCE_BADGE_RGB[1]};${TASKFORCE_BADGE_RGB[2]}m`;
@@ -36346,8 +36319,6 @@ var DEFAULT_BODY_H = 6;
36346
36319
  // src/mission-control/carrier-roster/renderer.ts
36347
36320
  var ANSI_RESET5 = "\x1B[0m";
36348
36321
  var INDENT = " ";
36349
- var SLOT_WIDTH = 4;
36350
- var NAME_WIDTH = 12;
36351
36322
  var MIN_CELL_WIDTH = 40;
36352
36323
  var ROSTER_ACTIONS_ID = "__roster_actions__";
36353
36324
  var CARRIER_ACTION_LABELS = [
@@ -36372,6 +36343,7 @@ function renderCarrierStatusOverlay(width, model, deps) {
36372
36343
  }
36373
36344
  for (let gi = 0; gi < model.viewModel.groupedEntries.length; gi++) {
36374
36345
  const group = model.viewModel.groupedEntries[gi];
36346
+ const entryMetrics = resolveEntryLineMetrics(group.entries);
36375
36347
  body.push(toCellLine(`${group.color}\u25C7${ANSI_RESET5} ${group.color}${group.header}${ANSI_RESET5}`), { kind: "blank" });
36376
36348
  for (const entry of group.entries) {
36377
36349
  const isSelected = entry.carrierId === model.viewModel.selectedCarrierId;
@@ -36379,7 +36351,7 @@ function renderCarrierStatusOverlay(width, model, deps) {
36379
36351
  kind: "cell",
36380
36352
  line: {
36381
36353
  bg: isSelected ? getEntryBgColorForEntry(entry) : void 0,
36382
- text: withIndent(renderEntryLine(entry, isSelected, deps))
36354
+ text: withIndent(renderEntryLine(entry, entryMetrics, isSelected, deps))
36383
36355
  }
36384
36356
  });
36385
36357
  if (isSelected && shouldRenderEntryEditor(model.state, entry.carrierId)) {
@@ -36556,20 +36528,19 @@ function toIndentedCellLine(text) {
36556
36528
  function withIndent(text) {
36557
36529
  return `${INDENT}${text}`;
36558
36530
  }
36559
- function padEndVisible(text, width) {
36560
- return `${text}${" ".repeat(Math.max(0, width - visibleWidth(text)))}`;
36561
- }
36562
36531
  function getDetailInnerWidth(width) {
36563
36532
  return Math.max(20, width - 8);
36564
36533
  }
36565
36534
  function buildDetailRows(entry, innerWidth, deps) {
36566
36535
  const provider = deps.getAvailableModels(entry.cliType);
36567
36536
  const modelLabel = getModelLabel(provider, entry.model);
36568
- const labelWidth = 8;
36569
- const valueWidth = Math.max(10, innerWidth - 10 - labelWidth);
36537
+ const labels = ["model", "cli", "role", "desc"];
36538
+ const labelWidth = maxVisibleWidth(labels);
36539
+ const prefix = " ";
36540
+ const valueWidth = Math.max(10, innerWidth - visibleWidth(prefix) - labelWidth - 1);
36570
36541
  const lines = [];
36571
36542
  const detailLine = (label, value) => {
36572
- lines.push(` ${deps.theme.dim(label.padEnd(labelWidth, " "))} ${value}`);
36543
+ lines.push(`${prefix}${deps.theme.dim(padEndVisible(label, labelWidth))} ${value}`);
36573
36544
  };
36574
36545
  detailLine("model", modelLabel);
36575
36546
  detailLine("cli", getCliDisplayName(entry.cliType));
@@ -36677,14 +36648,19 @@ function getSelectedEntry(model) {
36677
36648
  const selectedCarrierId = model.viewModel.selectedCarrierId;
36678
36649
  return selectedCarrierId ? model.viewModel.flatEntries.find((entry) => entry.carrierId === selectedCarrierId) ?? null : null;
36679
36650
  }
36680
- function renderEntryLine(entry, isSelected, deps) {
36651
+ function resolveEntryLineMetrics(entries) {
36652
+ return {
36653
+ displayNameWidth: maxVisibleWidth(entries.map((entry) => entry.displayName)),
36654
+ slotWidth: maxVisibleWidth(entries.map((entry) => `#${entry.slot}`))
36655
+ };
36656
+ }
36657
+ function renderEntryLine(entry, metrics, isSelected, deps) {
36681
36658
  const dim3 = deps.theme.dim;
36682
36659
  const slotStr = `#${entry.slot}`;
36683
- const slotPad = " ".repeat(Math.max(0, SLOT_WIDTH - slotStr.length));
36684
- const namePad = " ".repeat(Math.max(0, NAME_WIDTH - visibleWidth(entry.displayName)));
36685
36660
  const nameColor = getEntryColor(entry);
36686
36661
  const selectedPrefix = isSelected ? `${nameColor}\u25B8${ANSI_RESET5}` : " ";
36687
- const coloredName = `${nameColor}${entry.displayName}${ANSI_RESET5}`;
36662
+ const slotCell = padEndVisible(dim3(slotStr), metrics.slotWidth);
36663
+ const coloredName = padEndVisible(`${nameColor}${entry.displayName}${ANSI_RESET5}`, metrics.displayNameWidth);
36688
36664
  const modelLabel = getModelLabel(deps.getAvailableModels(entry.cliType), entry.model);
36689
36665
  const modelStr = entry.isDefault ? dim3(modelLabel) : modelLabel;
36690
36666
  const effortSupported = deps.getModelEffortLevels(entry.cliType, entry.model).length > 0;
@@ -36692,7 +36668,7 @@ function renderEntryLine(entry, isSelected, deps) {
36692
36668
  const roleStr = entry.role ? dim3(` (${entry.role})`) : "";
36693
36669
  const taskForceTag = !entry.subagentMode && entry.taskForceBackendCount >= 2 ? ` ${TASKFORCE_BADGE_COLOR2}[TF:${entry.taskForceBackendCount}]${ANSI_RESET5}` : "";
36694
36670
  const subagentTag = entry.subagentMode ? ` ${getSubagentSignatureColor()}[SA]${ANSI_RESET5}` : "";
36695
- return `${selectedPrefix} ${dim3(slotStr)}${slotPad}${coloredName}${namePad}${modelStr}${effortStr}${roleStr}${subagentTag}${taskForceTag}`;
36671
+ return `${selectedPrefix} ${slotCell} ${coloredName} ${modelStr}${effortStr}${roleStr}${subagentTag}${taskForceTag}`;
36696
36672
  }
36697
36673
  function renderRosterActionsRow(isSelected, deps) {
36698
36674
  const marker = isSelected ? `${deps.theme.accent("\u25B8")}` : " ";
@@ -37609,19 +37585,20 @@ var RosterTaskForcePanelSurface = class {
37609
37585
  return clampOverlayRows(maxRows, this.buildBackendEntries().length + editRows + TASKFORCE_EXTRA_BODY_ROWS);
37610
37586
  }
37611
37587
  render(width) {
37588
+ const entries = this.buildBackendEntries();
37589
+ const entryMetrics = this.resolveEntryLineMetrics(entries);
37612
37590
  const body = [
37613
37591
  { kind: "center", text: this.options.theme.dim("Carrier Roster / TaskForce") },
37614
37592
  { kind: "center", text: this.options.theme.accent(`TaskForce - ${this.options.carrierDisplayName}`) },
37615
37593
  { kind: "blank" }
37616
37594
  ];
37617
- const entries = this.buildBackendEntries();
37618
37595
  for (let i = 0; i < entries.length; i++) {
37619
37596
  const entry = entries[i];
37620
37597
  const isSelected = i === this.selectedIndex;
37621
37598
  body.push({
37622
37599
  kind: "cell",
37623
37600
  line: {
37624
- text: withIndent2(this.renderEntryLine(entry, isSelected))
37601
+ text: withIndent2(this.renderEntryLine(entry, entryMetrics, isSelected))
37625
37602
  }
37626
37603
  });
37627
37604
  if (isSelected && this.mode === "model") {
@@ -37834,15 +37811,26 @@ var RosterTaskForcePanelSurface = class {
37834
37811
  this.feedbackMessage = null;
37835
37812
  this.options.requestRender();
37836
37813
  }
37837
- renderEntryLine(entry, isSelected) {
37814
+ resolveEntryLineMetrics(entries) {
37815
+ return {
37816
+ detailWidth: maxVisibleWidth(entries.map((entry) => this.formatEntryDetail(entry))),
37817
+ displayNameWidth: maxVisibleWidth(entries.map((entry) => entry.displayName))
37818
+ };
37819
+ }
37820
+ renderEntryLine(entry, metrics, isSelected) {
37838
37821
  const selectedPrefix = isSelected ? `${entry.color}\u25B8${ANSI_RESET6}` : " ";
37822
+ const nameCell = padEndVisible(`${entry.color}${entry.displayName}${ANSI_RESET6}`, metrics.displayNameWidth);
37823
+ const detailCell = padEndVisible(this.formatEntryDetail(entry), metrics.detailWidth);
37824
+ const configTag = entry.isCustom ? ` ${ANSI_ACCENT}(custom)${ANSI_RESET6}` : ` ${ANSI_DIM3}(origin)${ANSI_RESET6}`;
37825
+ return ` ${selectedPrefix} ${nameCell} ${detailCell}${configTag}`;
37826
+ }
37827
+ formatEntryDetail(entry) {
37839
37828
  const provider = this.getAvailableModels(entry.cliType);
37840
37829
  const modelName = provider.models.find((model) => model.modelId === entry.model)?.name ?? entry.model;
37841
37830
  const modelStr = entry.isCustom ? modelName : this.options.theme.dim(modelName);
37842
37831
  const effortSupported = this.getEffortLevels(entry.cliType, entry.model).length > 0;
37843
37832
  const effortStr = effortSupported && entry.effort ? ` ${this.options.theme.dim("\xB7")} ${entry.isCustom ? entry.effort : this.options.theme.dim(entry.effort)}` : "";
37844
- const configTag = entry.isCustom ? ` ${ANSI_ACCENT}(custom)${ANSI_RESET6}` : ` ${ANSI_DIM3}(origin)${ANSI_RESET6}`;
37845
- return ` ${selectedPrefix} ${entry.color}${entry.displayName}${ANSI_RESET6} ${modelStr}${effortStr}${configTag}`;
37833
+ return `${modelStr}${effortStr}`;
37846
37834
  }
37847
37835
  getBackendConfig(cliType) {
37848
37836
  const snapshot = readCarriersSnapshot();
@@ -37978,7 +37966,7 @@ function resolveCellWidth2(lines) {
37978
37966
  return Math.max(MIN_CELL_WIDTH2, ...lineWidths);
37979
37967
  }
37980
37968
  function renderCellLine2(line, cellWidth, width) {
37981
- const padded = padEndVisible2(truncateToWidth(line.text, cellWidth), cellWidth);
37969
+ const padded = padEndVisible(truncateToWidth(line.text, cellWidth), cellWidth);
37982
37970
  return centerText(applyLineBg2(padded, line.bg), width);
37983
37971
  }
37984
37972
  function toCellLine2(text) {
@@ -37993,9 +37981,6 @@ function toIndentedCellLine2(text) {
37993
37981
  function withIndent2(text) {
37994
37982
  return `${INDENT2}${text}`;
37995
37983
  }
37996
- function padEndVisible2(text, width) {
37997
- return `${text}${" ".repeat(Math.max(0, width - visibleWidth(text)))}`;
37998
- }
37999
37984
  function applyLineBg2(line, bg) {
38000
37985
  if (!bg) return line;
38001
37986
  return `${bg}${line.replaceAll(ANSI_RESET6, `${ANSI_RESET6}${bg}`)}${ANSI_RESET6}`;
@@ -38130,25 +38115,31 @@ function createAboutPanel(deps) {
38130
38115
  render({ width }) {
38131
38116
  const release2 = deps.getRelease();
38132
38117
  const counts = deps.counts;
38118
+ const releaseRows = [
38119
+ { key: "Version", value: MISSION_CONTROL_THEME.accent(release2?.version ?? "(local)") },
38120
+ { key: "Channel", value: MISSION_CONTROL_THEME.accent(release2?.channel ?? "local") }
38121
+ ];
38122
+ const infoRows = [
38123
+ { key: "Carriers", value: MISSION_CONTROL_THEME.accent(String(counts?.carriers ?? 0)) },
38124
+ { key: "Wiki entries", value: MISSION_CONTROL_THEME.accent(String(counts?.wikiEntries ?? 0)) },
38125
+ { key: "Queued patches", value: MISSION_CONTROL_THEME.accent(String(counts?.queuedPatches ?? 0)) },
38126
+ { key: "Docs", value: MISSION_CONTROL_THEME.accent("(configured later)") },
38127
+ { key: "Node", value: MISSION_CONTROL_THEME.accent(process.version) }
38128
+ ];
38133
38129
  return [
38134
38130
  centerText(MISSION_CONTROL_THEME.dim(renderBreadcrumbs(deps.stack.breadcrumbs())), width),
38135
38131
  centerText(MISSION_CONTROL_THEME.accent("Fleet"), width),
38136
- centerText(formatKeyValue("Version", release2?.version ?? "(local)"), width),
38137
- centerText(formatKeyValue("Channel", release2?.channel ?? "local"), width),
38132
+ ...renderInfoRows(releaseRows, width),
38138
38133
  "",
38139
- centerText(formatKeyValue("Carriers", String(counts?.carriers ?? 0)), width),
38140
- centerText(formatKeyValue("Wiki entries", String(counts?.wikiEntries ?? 0)), width),
38141
- centerText(formatKeyValue("Queued patches", String(counts?.queuedPatches ?? 0)), width),
38142
- centerText(formatKeyValue("Docs", "(configured later)"), width),
38143
- centerText(formatKeyValue("Node", process.version), width),
38134
+ ...renderInfoRows(infoRows, width),
38144
38135
  "",
38145
38136
  centerText(MISSION_CONTROL_THEME.dim("Esc back"), width)
38146
38137
  ];
38147
38138
  }
38148
38139
  };
38149
38140
  }
38150
- function formatKeyValue(key, value) {
38151
- return `${key}: ${MISSION_CONTROL_THEME.accent(value)}`;
38141
+ function renderInfoRows(rows, width) {
38142
+ return renderKeyValueBlock({ innerWidth: width, rows });
38152
38143
  }
38153
38144
 
38154
38145
  // src/mission-control/menu/action-list-panel.ts
@@ -38197,7 +38188,7 @@ function createActionListPanel(options2) {
38197
38188
  ...breadcrumbLines,
38198
38189
  centerText(MISSION_CONTROL_THEME.accent(options2.title), width),
38199
38190
  "",
38200
- ...actions.map((item, index) => centerText(formatActionRow(item, index === selected), width)),
38191
+ ...renderActionRows(actions, selected, width),
38201
38192
  "",
38202
38193
  ...statusLines.map((line) => centerText(line, width)),
38203
38194
  ...statusLines.length === 0 ? [] : [""],
@@ -38222,11 +38213,15 @@ function clampSelected(selected, length) {
38222
38213
  if (length <= 0) return 0;
38223
38214
  return Math.min(selected, length - 1);
38224
38215
  }
38216
+ function renderActionRows(actions, selected, width) {
38217
+ const rows = actions.map((item, index) => formatActionRow(item, index === selected));
38218
+ return renderChoiceBlock({ innerWidth: width, rows });
38219
+ }
38225
38220
  function formatActionRow(item, selected) {
38226
38221
  const marker = selected ? MISSION_CONTROL_THEME.accent(item.marker ?? "\u25B8") : MISSION_CONTROL_THEME.dim(" ");
38227
- const detail = item.detail === void 0 ? "" : MISSION_CONTROL_THEME.dim(` ${item.detail}`);
38222
+ const trailing = item.detail === void 0 ? void 0 : MISSION_CONTROL_THEME.dim(item.detail);
38228
38223
  const label = selected ? MISSION_CONTROL_THEME.bg("selected", MISSION_CONTROL_THEME.accent(item.label)) : item.label;
38229
- return `${marker} ${label}${detail}`;
38224
+ return { label, marker, trailing };
38230
38225
  }
38231
38226
  function move(index, length, delta) {
38232
38227
  return length === 0 ? 0 : (index + delta + length) % length;
@@ -38348,7 +38343,7 @@ function createAuthPanel(deps) {
38348
38343
  centerText(MISSION_CONTROL_THEME.dim(renderBreadcrumbs(deps.stack.breadcrumbs())), width),
38349
38344
  centerText(MISSION_CONTROL_THEME.accent("Authentication"), width),
38350
38345
  "",
38351
- ...rows.map((row, index) => centerText(formatProviderRow(row, index === selected), width)),
38346
+ ...renderProviderRows(rows, selected, width),
38352
38347
  "",
38353
38348
  centerText(MISSION_CONTROL_THEME.dim("Enter actions Esc back"), width)
38354
38349
  ];
@@ -38460,11 +38455,17 @@ function createProviderRows() {
38460
38455
  status: "Checking"
38461
38456
  })).sort((left, right) => left.label.localeCompare(right.label));
38462
38457
  }
38458
+ function renderProviderRows(rows, selected, width) {
38459
+ return renderChoiceBlock({
38460
+ innerWidth: width,
38461
+ rows: rows.map((row, index) => formatProviderRow(row, index === selected))
38462
+ });
38463
+ }
38463
38464
  function formatProviderRow(row, selected) {
38464
38465
  const marker = selected ? MISSION_CONTROL_THEME.accent("\u25B8") : MISSION_CONTROL_THEME.dim(" ");
38465
38466
  const label = selected ? MISSION_CONTROL_THEME.bg("selected", MISSION_CONTROL_THEME.accent(row.label)) : row.label;
38466
38467
  const status = row.configured ? MISSION_CONTROL_THEME.success(row.status) : MISSION_CONTROL_THEME.warning(row.status);
38467
- return `${marker} ${label} ${status}`;
38468
+ return { label, marker, trailing: status };
38468
38469
  }
38469
38470
  function formatCliLabel(cliId) {
38470
38471
  return cliId.split("-").map((part) => part.length > 0 ? `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}` : part).join(" ");
@@ -38479,15 +38480,13 @@ function formatError2(error51) {
38479
38480
  // src/mission-control/menu/diagnostics-panel.ts
38480
38481
  init_data_dir();
38481
38482
  import * as os6 from "os";
38482
- import * as path12 from "path";
38483
- var ROOT_ROWS = ["Data Dir", "Reset Preset To Defaults", "System Info"];
38483
+ var ROOT_ROWS = ["Data Dir", "System Info"];
38484
38484
  var CONTROL_CHARS2 = /[\u0000-\u001f\u007f]/g;
38485
38485
  var LINE_BREAK_CHARS = /[\r\n]+/g;
38486
38486
  function createDiagnosticsPanel(deps) {
38487
38487
  let selected = 0;
38488
38488
  let view = "root";
38489
38489
  let scroll = 0;
38490
- let message = "";
38491
38490
  return {
38492
38491
  id: "fleet-menu:diagnostics",
38493
38492
  title: "Diagnostics",
@@ -38529,19 +38528,15 @@ function createDiagnosticsPanel(deps) {
38529
38528
  if (view === "system") {
38530
38529
  return renderSystemInfo(width);
38531
38530
  }
38532
- const lines = [
38531
+ return [
38533
38532
  "",
38534
38533
  centerText(MISSION_CONTROL_THEME.dim(renderBreadcrumbs(deps.stack.breadcrumbs())), width),
38535
38534
  centerText(MISSION_CONTROL_THEME.accent("Diagnostics"), width),
38536
38535
  "",
38537
- ...ROOT_ROWS.map((row, index) => centerText(formatRootRow(row, index === selected), width)),
38536
+ ...renderRootRows(selected, width),
38538
38537
  "",
38539
38538
  centerText(MISSION_CONTROL_THEME.dim("Enter open Esc back"), width)
38540
38539
  ];
38541
- if (message.length > 0) {
38542
- lines.push(centerText(message, width));
38543
- }
38544
- return lines;
38545
38540
  }
38546
38541
  };
38547
38542
  function openSelected() {
@@ -38554,80 +38549,55 @@ function createDiagnosticsPanel(deps) {
38554
38549
  view = "system";
38555
38550
  return;
38556
38551
  }
38557
- deps.stack.push(createActionListPanel({
38558
- id: "diagnostics:reset-preset",
38559
- title: "Reset Preset To Defaults",
38560
- statusLines: () => [MISSION_CONTROL_THEME.warning("All CLI presets will be reset to defaults. Continue?")],
38561
- onBack: () => {
38562
- deps.stack.pop();
38563
- },
38564
- actions: [
38565
- {
38566
- id: "confirm",
38567
- label: "Confirm",
38568
- run: () => {
38569
- resetPresets();
38570
- deps.stack.pop();
38571
- }
38572
- },
38573
- {
38574
- id: "cancel",
38575
- label: "Cancel",
38576
- run: () => {
38577
- deps.stack.pop();
38578
- }
38579
- }
38580
- ]
38581
- }));
38582
38552
  }
38583
38553
  function renderDataDir(width) {
38584
38554
  const dataDir2 = safeValue(getFleetDataDir);
38585
38555
  const cleanDataDir = sanitizeTerminalText(dataDir2);
38556
+ const rows = [
38557
+ { key: "Root", value: MISSION_CONTROL_THEME.accent(cleanDataDir) }
38558
+ ];
38586
38559
  return [
38587
38560
  "",
38588
38561
  centerText(MISSION_CONTROL_THEME.dim(`${renderBreadcrumbs(deps.stack.breadcrumbs())} / Data Dir`), width),
38589
38562
  centerText(MISSION_CONTROL_THEME.accent("Data Dir"), width),
38590
38563
  "",
38591
- centerText(formatKeyValue2("Root", cleanDataDir), width),
38592
- centerText(formatKeyValue2("Presets", sanitizeTerminalText(path12.join(dataDir2, "presets.json"))), width),
38564
+ ...renderInfoRows2(rows, width),
38593
38565
  "",
38594
38566
  centerText(MISSION_CONTROL_THEME.dim("Esc back"), width)
38595
38567
  ];
38596
38568
  }
38597
38569
  function renderSystemInfo(width) {
38570
+ const rows = [
38571
+ { key: "Node", value: MISSION_CONTROL_THEME.accent(process.version) },
38572
+ { key: "OS", value: MISSION_CONTROL_THEME.accent(`${os6.platform()} ${os6.release()} ${os6.arch()}`) },
38573
+ { key: "Shell", value: MISSION_CONTROL_THEME.accent(sanitizeTerminalText(deps.env.SHELL ?? "(unknown)")) },
38574
+ { key: "Terminal", value: MISSION_CONTROL_THEME.accent(sanitizeTerminalText(deps.env.TERM ?? "(unknown)")) },
38575
+ { key: "CWD", value: MISSION_CONTROL_THEME.accent(sanitizeTerminalText(deps.cwd)) }
38576
+ ];
38598
38577
  return [
38599
38578
  "",
38600
38579
  centerText(MISSION_CONTROL_THEME.dim(`${renderBreadcrumbs(deps.stack.breadcrumbs())} / System Info`), width),
38601
38580
  centerText(MISSION_CONTROL_THEME.accent("System Info"), width),
38602
38581
  "",
38603
- centerText(formatKeyValue2("Node", process.version), width),
38604
- centerText(formatKeyValue2("OS", `${os6.platform()} ${os6.release()} ${os6.arch()}`), width),
38605
- centerText(formatKeyValue2("Shell", sanitizeTerminalText(deps.env.SHELL ?? "(unknown)")), width),
38606
- centerText(formatKeyValue2("Terminal", sanitizeTerminalText(deps.env.TERM ?? "(unknown)")), width),
38607
- centerText(formatKeyValue2("CWD", sanitizeTerminalText(deps.cwd)), width),
38582
+ ...renderInfoRows2(rows, width),
38608
38583
  "",
38609
38584
  centerText(MISSION_CONTROL_THEME.dim("Esc back"), width)
38610
38585
  ];
38611
38586
  }
38612
- function resetPresets() {
38613
- const preset = deps.presetService?.load();
38614
- if (preset !== void 0) {
38615
- for (const cliId of Object.keys(preset.byCli)) {
38616
- deps.presetService?.resetCliPreset(cliId);
38617
- }
38618
- deps.presetService?.saveDefaultCliId(void 0);
38619
- }
38620
- deps.onPresetReset?.();
38621
- message = MISSION_CONTROL_THEME.success("Preset defaults restored.");
38622
- }
38623
38587
  }
38624
- function formatKeyValue2(key, value) {
38625
- return `${key}: ${MISSION_CONTROL_THEME.accent(value)}`;
38588
+ function renderInfoRows2(rows, width) {
38589
+ return renderKeyValueBlock({ innerWidth: width, rows });
38590
+ }
38591
+ function renderRootRows(selected, width) {
38592
+ return renderChoiceBlock({
38593
+ innerWidth: width,
38594
+ rows: ROOT_ROWS.map((row, index) => formatRootRow(row, index === selected))
38595
+ });
38626
38596
  }
38627
38597
  function formatRootRow(row, selected) {
38628
38598
  const marker = selected ? MISSION_CONTROL_THEME.accent("\u25B8") : MISSION_CONTROL_THEME.dim(" ");
38629
38599
  const label = selected ? MISSION_CONTROL_THEME.bg("selected", MISSION_CONTROL_THEME.accent(row)) : row;
38630
- return `${marker} ${label}`;
38600
+ return { label, marker };
38631
38601
  }
38632
38602
  function safeValue(read) {
38633
38603
  try {
@@ -38711,6 +38681,165 @@ function isEscIntermediate2(code) {
38711
38681
  return code >= 32 && code <= 47;
38712
38682
  }
38713
38683
 
38684
+ // src/mission-control/menu/sectioned-list-panel.ts
38685
+ function createSectionedListPanel(options2) {
38686
+ let selected = firstSelectableIndex(resolveRows(options2.rows));
38687
+ return {
38688
+ id: options2.id,
38689
+ title: options2.title,
38690
+ handleInput(data) {
38691
+ const rows = resolveRows(options2.rows);
38692
+ selected = clampSelectable(rows, selected);
38693
+ if (isUp(data)) {
38694
+ selected = move4(rows, selected, -1);
38695
+ return true;
38696
+ }
38697
+ if (isDown(data)) {
38698
+ selected = move4(rows, selected, 1);
38699
+ return true;
38700
+ }
38701
+ const row = rows[selected];
38702
+ if (isEnter(data) && isSelectable(row)) {
38703
+ runSelected(row);
38704
+ return true;
38705
+ }
38706
+ if (isRight(data) && row?.kind === "launch" && row.openModelOverride !== void 0) {
38707
+ row.openModelOverride();
38708
+ return true;
38709
+ }
38710
+ return false;
38711
+ },
38712
+ getFocusLine({ width }) {
38713
+ const rows = resolveRows(options2.rows);
38714
+ selected = clampSelectable(rows, selected);
38715
+ if (!isSelectable(rows[selected])) return void 0;
38716
+ return getRowFocusLine(width, options2, selected);
38717
+ },
38718
+ render({ width }) {
38719
+ const rows = resolveRows(options2.rows);
38720
+ selected = clampSelectable(rows, selected);
38721
+ const breadcrumbs = options2.breadcrumbs?.() ?? [];
38722
+ const statusLines = options2.statusLines?.() ?? [];
38723
+ const footerHint = MISSION_CONTROL_THEME.dim(options2.footer ?? "\u2191\u2193 select Enter open");
38724
+ const breadcrumbLines = breadcrumbs.length <= 1 ? [] : [
38725
+ "",
38726
+ centerText(MISSION_CONTROL_THEME.dim(renderBreadcrumbs(breadcrumbs)), width)
38727
+ ];
38728
+ return [
38729
+ ...breadcrumbLines,
38730
+ centerText(MISSION_CONTROL_THEME.accent(options2.title), width),
38731
+ "",
38732
+ ...renderRows(rows, selected, width),
38733
+ "",
38734
+ ...statusLines.map((line) => centerText(line, width)),
38735
+ ...statusLines.length === 0 ? [] : [""],
38736
+ centerText(footerHint, width)
38737
+ ];
38738
+ }
38739
+ };
38740
+ }
38741
+ function clampSelectable(rows, selected) {
38742
+ if (isSelectable(rows[selected])) {
38743
+ return selected;
38744
+ }
38745
+ return firstSelectableIndex(rows);
38746
+ }
38747
+ function firstSelectableIndex(rows) {
38748
+ const index = rows.findIndex(isSelectable);
38749
+ return index === -1 ? 0 : index;
38750
+ }
38751
+ function getRowFocusLine(width, options2, selected) {
38752
+ const breadcrumbs = options2.breadcrumbs?.() ?? [];
38753
+ const breadcrumbLines = breadcrumbs.length <= 1 ? [] : [
38754
+ "",
38755
+ centerText(MISSION_CONTROL_THEME.dim(renderBreadcrumbs(breadcrumbs)), width)
38756
+ ];
38757
+ const displayIndex = buildDisplayRows(resolveRows(options2.rows), selected).findIndex((row) => row.kind === "choice" && row.sourceIndex === selected);
38758
+ return breadcrumbLines.length + 2 + Math.max(0, displayIndex);
38759
+ }
38760
+ function isRight(data) {
38761
+ return data === "\x1B[C" || data === "\x1BOC";
38762
+ }
38763
+ function isSelectable(row) {
38764
+ return row !== void 0 && row.kind !== "header";
38765
+ }
38766
+ function move4(rows, index, delta) {
38767
+ const selectableCount = rows.filter(isSelectable).length;
38768
+ if (selectableCount === 0) return 0;
38769
+ let next = index;
38770
+ do {
38771
+ next = (next + delta + rows.length) % rows.length;
38772
+ } while (!isSelectable(rows[next]));
38773
+ return next;
38774
+ }
38775
+ function renderRows(rows, selected, width) {
38776
+ const displayRows = buildDisplayRows(rows, selected);
38777
+ const choiceRows = displayRows.filter((row) => row.kind === "choice");
38778
+ const renderedChoiceRows = renderChoiceBlock({
38779
+ innerWidth: width,
38780
+ rows: choiceRows.map((row) => row.choice)
38781
+ });
38782
+ let choiceIndex = 0;
38783
+ return displayRows.map((row) => {
38784
+ if (row.kind === "blank") return "";
38785
+ return renderedChoiceRows[choiceIndex++] ?? "";
38786
+ });
38787
+ }
38788
+ function resolveRows(rows) {
38789
+ return typeof rows === "function" ? rows() : rows;
38790
+ }
38791
+ function runSelected(row) {
38792
+ if (row.kind === "launch") {
38793
+ row.launch();
38794
+ return;
38795
+ }
38796
+ if (row.kind === "toggle") {
38797
+ row.toggle();
38798
+ return;
38799
+ }
38800
+ row.navigate();
38801
+ }
38802
+ function formatRow(row, selected) {
38803
+ if (row.kind === "header") {
38804
+ return {
38805
+ label: MISSION_CONTROL_THEME.section(row.label),
38806
+ marker: MISSION_CONTROL_THEME.dim(" ")
38807
+ };
38808
+ }
38809
+ const marker = selected ? ` ${MISSION_CONTROL_THEME.accent("\u25B8")}` : MISSION_CONTROL_THEME.dim(" ");
38810
+ const label = selected ? MISSION_CONTROL_THEME.bg("selected", MISSION_CONTROL_THEME.accent(row.label)) : row.label;
38811
+ const trailing = getTrailing(row);
38812
+ return { label, marker, trailing };
38813
+ }
38814
+ function buildDisplayRows(rows, selected) {
38815
+ const displayRows = [];
38816
+ let seenHeader = false;
38817
+ for (const [index, row] of rows.entries()) {
38818
+ if (row.kind === "header" && seenHeader) {
38819
+ displayRows.push({ kind: "blank" });
38820
+ }
38821
+ if (row.kind === "header") {
38822
+ seenHeader = true;
38823
+ }
38824
+ displayRows.push({
38825
+ choice: formatRow(row, index === selected),
38826
+ kind: "choice",
38827
+ sourceIndex: index
38828
+ });
38829
+ }
38830
+ return displayRows;
38831
+ }
38832
+ function getTrailing(row) {
38833
+ if (row.kind === "launch") {
38834
+ const parts = [row.detail, row.trailing].filter((part) => part !== void 0 && part.length > 0);
38835
+ return parts.length === 0 ? void 0 : MISSION_CONTROL_THEME.dim(parts.join(" "));
38836
+ }
38837
+ if (row.kind === "toggle") {
38838
+ return MISSION_CONTROL_THEME.dim(row.value);
38839
+ }
38840
+ return row.detail === void 0 ? void 0 : MISSION_CONTROL_THEME.dim(row.detail);
38841
+ }
38842
+
38714
38843
  // ../fleet-wiki-ui/dist/cli.mjs
38715
38844
  import { spawnSync, spawn as spawn3 } from "child_process";
38716
38845
  import { existsSync as existsSync5 } from "fs";
@@ -39569,13 +39698,16 @@ function createWikiPanel(deps) {
39569
39698
  },
39570
39699
  render({ width }) {
39571
39700
  const status = wiki.getStatus();
39701
+ const infoRows = [
39702
+ { key: "Port", value: MISSION_CONTROL_THEME.accent(String(wiki.getPort())) }
39703
+ ];
39572
39704
  return [
39573
39705
  "",
39574
39706
  centerText(MISSION_CONTROL_THEME.dim(renderBreadcrumbs(deps.stack.breadcrumbs())), width),
39575
39707
  centerText(MISSION_CONTROL_THEME.accent("Wiki Server"), width),
39576
39708
  "",
39577
39709
  centerText(formatStatus(status), width),
39578
- centerText(formatKeyValue3("Port", String(wiki.getPort())), width),
39710
+ ...renderInfoRows3(infoRows, width),
39579
39711
  "",
39580
39712
  centerText(MISSION_CONTROL_THEME.dim("External fleet wiki processes are not managed here."), width),
39581
39713
  "",
@@ -39666,8 +39798,8 @@ function formatStatus(status) {
39666
39798
  function formatActionsRow() {
39667
39799
  return `${MISSION_CONTROL_THEME.accent("\u25B8")} ${MISSION_CONTROL_THEME.bg("selected", MISSION_CONTROL_THEME.accent("Actions"))}`;
39668
39800
  }
39669
- function formatKeyValue3(key, value) {
39670
- return `${key}: ${MISSION_CONTROL_THEME.accent(value)}`;
39801
+ function renderInfoRows3(rows, width) {
39802
+ return renderKeyValueBlock({ innerWidth: width, rows });
39671
39803
  }
39672
39804
  function formatError3(error51) {
39673
39805
  return error51 instanceof Error && error51.message.length > 0 ? error51.message : "Wiki server failed.";
@@ -39681,10 +39813,9 @@ var EMPTY_MOUSE_PROTOCOL_STATE = {
39681
39813
  };
39682
39814
  var DEFAULT_SHIMMER_INTERVAL_MS = 100;
39683
39815
  var DEFAULT_SHIMMER_PHASE_STEP = 0.15;
39684
- var LAUNCHER_ITEMS = ["Start", "Configure Carriers", "Options", "System Menu", "Exit Fleet"];
39685
39816
  function createMissionControlController(options2) {
39686
- const cliOptions = options2.cliOptions.length > 0 ? [...options2.cliOptions] : [{ id: options2.defaultCliId, label: options2.defaultCliId }];
39687
- let selectedCliId = cliOptions.some((entry) => entry.id === options2.defaultCliId) ? options2.defaultCliId : cliOptions[0]?.id ?? options2.defaultCliId;
39817
+ const cliOptions = options2.cliOptions.length > 0 ? [...options2.cliOptions] : [{ id: options2.initialCliId, label: options2.initialCliId }];
39818
+ let selectedCliId = cliOptions.some((entry) => entry.id === options2.initialCliId) ? options2.initialCliId : cliOptions[0]?.id ?? options2.initialCliId;
39688
39819
  let state = "idle";
39689
39820
  let lastExit;
39690
39821
  let active;
@@ -39883,59 +40014,55 @@ function createMissionControlController(options2) {
39883
40014
  }
39884
40015
  function openLauncherRoot() {
39885
40016
  let stack;
39886
- const root = createLauncherRoot({
40017
+ const root = createMissionRootPanel({
40018
+ cliOptions,
39887
40019
  getStack: () => stack,
39888
- onRenderRequest: options2.onRenderRequest,
39889
- openItem: (index) => {
39890
- if (index === 0) {
39891
- stack.push(createStartPanel({
39892
- cliOptions,
39893
- launchSelected,
39894
- onRenderRequest: options2.onRenderRequest,
39895
- selectedCliId: () => selectedCliId,
39896
- sessionOptions: options2.sessionOptions,
39897
- setSelectedCliId: (cliId) => {
39898
- selectedCliId = cliId;
39899
- options2.sessionOptions?.selectCli(cliId);
39900
- options2.onRenderRequest();
39901
- },
39902
- stack
39903
- }));
39904
- return;
39905
- }
39906
- if (index === 1) {
39907
- openCarrierRoster();
39908
- return;
39909
- }
39910
- if (index === 2) {
39911
- stack.push(createOptionsPanel({
39912
- onRenderRequest: options2.onRenderRequest,
39913
- sessionOptions: options2.sessionOptions
39914
- }));
39915
- return;
39916
- }
39917
- if (index === 3) {
39918
- stack.push(createSystemMenuPanel({
39919
- authService: options2.authService,
39920
- counts: options2.loadedCounts,
39921
- cwd: options2.invocationCwd ?? process.cwd(),
39922
- env: options2.env ?? process.env,
39923
- getRelease: () => release2,
39924
- getStack: () => stack,
39925
- onPresetReset: () => {
39926
- options2.sessionOptions?.resetOverrides();
39927
- },
39928
- onRenderRequest: options2.onRenderRequest,
39929
- presetService: options2.presetService,
39930
- wikiController: options2.wikiController
39931
- }));
39932
- return;
39933
- }
39934
- options2.onExitFleet();
40020
+ launchCli: (entry) => {
40021
+ selectedCliId = entry.id;
40022
+ options2.sessionOptions?.selectCli(entry.id);
40023
+ void launchSelected();
39935
40024
  },
39936
40025
  loadedCounts: options2.loadedCounts,
40026
+ onRenderRequest: options2.onRenderRequest,
40027
+ openCarrierRoster,
40028
+ openModelOverride: (entry) => {
40029
+ selectedCliId = entry.id;
40030
+ options2.sessionOptions?.selectCli(entry.id);
40031
+ openModelOverride({
40032
+ entry,
40033
+ onRenderRequest: options2.onRenderRequest,
40034
+ sessionOptions: options2.sessionOptions,
40035
+ stack
40036
+ });
40037
+ },
40038
+ openSystemMenu: () => {
40039
+ stack.push(createSystemMenuPanel({
40040
+ authService: options2.authService,
40041
+ counts: options2.loadedCounts,
40042
+ cwd: options2.invocationCwd ?? process.cwd(),
40043
+ env: options2.env ?? process.env,
40044
+ getRelease: () => release2,
40045
+ getStack: () => stack,
40046
+ onRenderRequest: options2.onRenderRequest,
40047
+ wikiController: options2.wikiController
40048
+ }));
40049
+ },
39937
40050
  onExitFleet: options2.onExitFleet,
39938
- release: () => release2
40051
+ release: () => release2,
40052
+ selectedCliId: () => selectedCliId,
40053
+ sessionOptions: options2.sessionOptions,
40054
+ toggleEnableMetaphor: () => {
40055
+ options2.sessionOptions?.toggleEnableMetaphor();
40056
+ options2.onRenderRequest();
40057
+ },
40058
+ toggleNative: () => {
40059
+ options2.sessionOptions?.toggleNative();
40060
+ options2.onRenderRequest();
40061
+ },
40062
+ toggleReplaceSystemPrompt: () => {
40063
+ options2.sessionOptions?.toggleReplaceSystemPrompt();
40064
+ options2.onRenderRequest();
40065
+ }
39939
40066
  });
39940
40067
  stack = createPanelStack({
39941
40068
  root,
@@ -40006,79 +40133,85 @@ function createMissionControlController(options2) {
40006
40133
  function clearDefaultShimmerInterval(timer) {
40007
40134
  clearInterval(timer);
40008
40135
  }
40009
- function createLauncherRoot(options2) {
40010
- return createActionListPanel({
40136
+ function createMissionRootPanel(options2) {
40137
+ return createSectionedListPanel({
40011
40138
  id: "mission-control:launcher-root",
40012
40139
  title: "Mission Control",
40013
40140
  breadcrumbs: () => options2.getStack().breadcrumbs(),
40014
- footer: "\u2191\u2193 select Enter open",
40015
- statusLines: () => formatLauncherStatusLines(options2.loadedCounts, options2.release()),
40016
- actions: LAUNCHER_ITEMS.map((label, index) => ({
40017
- id: label.toLowerCase().replaceAll(" ", "-"),
40018
- label,
40019
- run: () => {
40020
- if (label === "Exit Fleet") {
40021
- options2.onExitFleet();
40022
- return;
40023
- }
40024
- options2.openItem(index);
40025
- options2.onRenderRequest();
40026
- }
40027
- }))
40141
+ footer: "\u2191\u2193 select Enter launch/open \u2192 model",
40142
+ statusLines: () => [
40143
+ ...formatLauncherStatusLines(options2.loadedCounts, options2.release()),
40144
+ ...options2.sessionOptions?.getStatusLines?.().map((line) => MISSION_CONTROL_THEME.error(line)) ?? []
40145
+ ],
40146
+ rows: () => createMissionRootRows(options2)
40028
40147
  });
40029
40148
  }
40030
- function createStartPanel(options2) {
40031
- let selected = Math.max(0, options2.cliOptions.findIndex((entry) => entry.id === options2.selectedCliId()));
40032
- return {
40033
- id: "mission-control:start",
40034
- title: "Start",
40035
- handleInput(data) {
40036
- selected = clampIndex(selected, options2.cliOptions.length);
40037
- if (isUp(data)) {
40038
- selected = moveIndex(selected, options2.cliOptions.length, -1);
40039
- return true;
40040
- }
40041
- if (isDown(data)) {
40042
- selected = moveIndex(selected, options2.cliOptions.length, 1);
40043
- return true;
40044
- }
40045
- const entry = options2.cliOptions[selected];
40046
- if (entry === void 0) {
40047
- return false;
40048
- }
40049
- if (isEnter(data)) {
40050
- options2.setSelectedCliId(entry.id);
40051
- void options2.launchSelected();
40052
- return true;
40053
- }
40054
- if (isRight(data) && options2.sessionOptions !== void 0) {
40055
- options2.setSelectedCliId(entry.id);
40056
- openStartModelOverride(options2, entry);
40057
- return true;
40149
+ function createMissionRootRows(options2) {
40150
+ const values = options2.sessionOptions?.getResolved().values;
40151
+ const model = options2.sessionOptions?.getDraft().model;
40152
+ return [
40153
+ { kind: "header", label: "LAUNCH" },
40154
+ ...options2.cliOptions.map((entry) => ({
40155
+ kind: "launch",
40156
+ id: `launch:${entry.id}`,
40157
+ label: entry.label,
40158
+ detail: entry.id === options2.selectedCliId() ? "selected" : void 0,
40159
+ trailing: model === void 0 ? void 0 : `model ${model}`,
40160
+ launch: () => options2.launchCli(entry),
40161
+ openModelOverride: () => options2.openModelOverride(entry)
40162
+ })),
40163
+ { kind: "header", label: "OPTION" },
40164
+ {
40165
+ kind: "toggle",
40166
+ id: "option:mode",
40167
+ label: "Mode",
40168
+ value: values?.native ? "Native" : "Fleet prompt",
40169
+ toggle: options2.toggleNative
40170
+ },
40171
+ {
40172
+ kind: "toggle",
40173
+ id: "option:system-prompt",
40174
+ label: "System prompt",
40175
+ value: formatSystemPromptOption(options2.sessionOptions),
40176
+ toggle: options2.toggleReplaceSystemPrompt
40177
+ },
40178
+ {
40179
+ kind: "toggle",
40180
+ id: "option:metaphor",
40181
+ label: "Metaphor",
40182
+ value: values?.enableMetaphor ? "Enabled" : "Off",
40183
+ toggle: options2.toggleEnableMetaphor
40184
+ },
40185
+ { kind: "header", label: "SYSTEM" },
40186
+ {
40187
+ kind: "navigate",
40188
+ id: "system:carrier-roster",
40189
+ label: "Carrier Roster",
40190
+ navigate: () => {
40191
+ options2.openCarrierRoster();
40192
+ options2.onRenderRequest();
40058
40193
  }
40059
- return false;
40060
40194
  },
40061
- getFocusLine() {
40062
- selected = clampIndex(selected, options2.cliOptions.length);
40063
- return options2.cliOptions.length === 0 ? void 0 : 4 + selected;
40195
+ {
40196
+ kind: "navigate",
40197
+ id: "system:system-menu",
40198
+ label: "System Menu",
40199
+ navigate: () => {
40200
+ options2.openSystemMenu();
40201
+ options2.onRenderRequest();
40202
+ }
40064
40203
  },
40065
- render({ width }) {
40066
- selected = clampIndex(selected, options2.cliOptions.length);
40067
- return [
40068
- "",
40069
- centerText(MISSION_CONTROL_THEME.dim(renderBreadcrumbs(options2.stack.breadcrumbs())), width),
40070
- centerText(MISSION_CONTROL_THEME.accent("Start"), width),
40071
- "",
40072
- ...options2.cliOptions.map((entry, index) => centerText(formatStartCliRow(entry, index === selected, options2.sessionOptions), width)),
40073
- "",
40074
- centerText(MISSION_CONTROL_THEME.dim("\u2191\u2193 select Enter launch \u2192 model Esc back"), width)
40075
- ];
40204
+ {
40205
+ kind: "navigate",
40206
+ id: "system:exit",
40207
+ label: "Exit",
40208
+ navigate: options2.onExitFleet
40076
40209
  }
40077
- };
40210
+ ];
40078
40211
  }
40079
- function openStartModelOverride(options2, entry) {
40212
+ function openModelOverride(options2) {
40080
40213
  options2.stack.push(createInputModal({
40081
- title: `${entry.label} Model Override`,
40214
+ title: `${options2.entry.label} Model Override`,
40082
40215
  message: "Set model override for the next launch.",
40083
40216
  mode: "text",
40084
40217
  initialValue: options2.sessionOptions?.getDraft().model ?? "",
@@ -40093,98 +40226,6 @@ function openStartModelOverride(options2, entry) {
40093
40226
  }
40094
40227
  }));
40095
40228
  }
40096
- function formatStartCliRow(entry, selected, sessionOptions) {
40097
- const marker = selected ? MISSION_CONTROL_THEME.accent("\u25B8") : MISSION_CONTROL_THEME.dim(" ");
40098
- const label = selected ? MISSION_CONTROL_THEME.bg("selected", MISSION_CONTROL_THEME.accent(entry.label)) : entry.label;
40099
- const model = sessionOptions?.getDraft().model;
40100
- const detail = model === void 0 ? "" : MISSION_CONTROL_THEME.dim(` model ${model}`);
40101
- return `${marker} ${label}${detail}`;
40102
- }
40103
- function isRight(data) {
40104
- return data === "\x1B[C" || data === "\x1BOC";
40105
- }
40106
- function clampIndex(index, length) {
40107
- if (length <= 0) {
40108
- return 0;
40109
- }
40110
- return Math.min(index, length - 1);
40111
- }
40112
- function moveIndex(index, length, delta) {
40113
- return length === 0 ? 0 : (index + delta + length) % length;
40114
- }
40115
- function createOptionsPanel(options2) {
40116
- let saveError = "";
40117
- return createActionListPanel({
40118
- id: "mission-control:options",
40119
- title: "Options",
40120
- footer: "\u2191\u2193 select Enter apply Esc back",
40121
- statusLines: () => [
40122
- ...options2.sessionOptions === void 0 ? [MISSION_CONTROL_THEME.dim("Session options are unavailable.")] : [],
40123
- ...saveError.length > 0 ? [MISSION_CONTROL_THEME.error(`Save failed: ${saveError}`)] : []
40124
- ],
40125
- actions: () => [
40126
- {
40127
- id: "mode",
40128
- label: "Mode",
40129
- detail: options2.sessionOptions?.getResolved().values.native ? "Native" : "Fleet prompt",
40130
- run: () => {
40131
- options2.sessionOptions?.toggleNative();
40132
- options2.onRenderRequest();
40133
- }
40134
- },
40135
- {
40136
- id: "system-prompt",
40137
- label: "System prompt",
40138
- detail: formatSystemPromptOption(options2.sessionOptions),
40139
- run: () => {
40140
- cycleSystemPromptRuntime(options2.sessionOptions);
40141
- options2.onRenderRequest();
40142
- }
40143
- },
40144
- {
40145
- id: "metaphor",
40146
- label: "Metaphor",
40147
- detail: options2.sessionOptions?.getResolved().values.enableMetaphor ? "Enabled" : "Off",
40148
- run: () => {
40149
- options2.sessionOptions?.toggleEnableMetaphor();
40150
- options2.onRenderRequest();
40151
- }
40152
- },
40153
- {
40154
- id: "cursor-sync",
40155
- label: "Cursor sync",
40156
- detail: options2.sessionOptions?.getResolved().values.cursorSync ? "Enabled" : "Off",
40157
- run: () => {
40158
- options2.sessionOptions?.toggleCursorSync();
40159
- options2.onRenderRequest();
40160
- }
40161
- },
40162
- {
40163
- id: "save-defaults",
40164
- label: "Save defaults",
40165
- run: () => {
40166
- saveError = "";
40167
- options2.onRenderRequest();
40168
- void options2.sessionOptions?.saveDraft().then(() => {
40169
- saveError = "";
40170
- options2.onRenderRequest();
40171
- }).catch((error51) => {
40172
- saveError = formatSaveError(error51);
40173
- options2.onRenderRequest();
40174
- });
40175
- }
40176
- },
40177
- {
40178
- id: "reset-overrides",
40179
- label: "Reset overrides",
40180
- run: () => {
40181
- options2.sessionOptions?.resetOverrides();
40182
- options2.onRenderRequest();
40183
- }
40184
- }
40185
- ]
40186
- });
40187
- }
40188
40229
  function createSystemMenuPanel(options2) {
40189
40230
  return createActionListPanel({
40190
40231
  id: "mission-control:system-menu",
@@ -40225,9 +40266,7 @@ function createSystemMenuPanel(options2) {
40225
40266
  stack.push(createDiagnosticsPanel({
40226
40267
  cwd: options2.cwd,
40227
40268
  env: options2.env,
40228
- onPresetReset: options2.onPresetReset,
40229
40269
  onRenderRequest: options2.onRenderRequest,
40230
- presetService: options2.presetService,
40231
40270
  stack
40232
40271
  }));
40233
40272
  }
@@ -40276,24 +40315,6 @@ function formatSystemPromptOption(sessionOptions) {
40276
40315
  }
40277
40316
  return values.replaceSystemPrompt ? "Replace" : "Append";
40278
40317
  }
40279
- function cycleSystemPromptRuntime(sessionOptions) {
40280
- const draft = sessionOptions?.getDraft();
40281
- if (draft === void 0) {
40282
- return;
40283
- }
40284
- if (draft.native) {
40285
- sessionOptions?.toggleNative();
40286
- if (draft.replaceSystemPrompt) {
40287
- sessionOptions?.toggleReplaceSystemPrompt();
40288
- }
40289
- return;
40290
- }
40291
- if (!draft.replaceSystemPrompt) {
40292
- sessionOptions?.toggleReplaceSystemPrompt();
40293
- return;
40294
- }
40295
- sessionOptions?.toggleNative();
40296
- }
40297
40318
  function isFailedExit(event) {
40298
40319
  return event.exitCode !== void 0 && event.exitCode !== 0 || event.signal !== void 0 && event.signal !== 0;
40299
40320
  }
@@ -40333,20 +40354,14 @@ function getMissionControlPanelFocusLine(component, width, rendered, panelLines)
40333
40354
  if (panelFocusLine === void 0) return void 0;
40334
40355
  return rendered.length - panelLines.length + panelFocusLine;
40335
40356
  }
40336
- function formatSaveError(error51) {
40337
- if (error51 instanceof Error && error51.message.length > 0) {
40338
- return error51.message;
40339
- }
40340
- return "Failed to save Fleet options.";
40341
- }
40342
40357
 
40343
40358
  // src/mission-control/loaded-counts.ts
40344
40359
  import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
40345
- import { join as join11 } from "path";
40360
+ import { join as join10 } from "path";
40346
40361
 
40347
40362
  // ../../packages/fleet-wiki/dist/index.js
40348
40363
  import path62 from "path";
40349
- import path13 from "path";
40364
+ import path12 from "path";
40350
40365
  import path52 from "path";
40351
40366
  import crypto4 from "crypto";
40352
40367
  import { mkdir as mkdir4, readdir as readdir2, readFile as readFile3, rename, rm as rm2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
@@ -40370,7 +40385,7 @@ import crypto22 from "crypto";
40370
40385
  import { readdir as readdir3 } from "fs/promises";
40371
40386
  import path122 from "path";
40372
40387
  import path112 from "path";
40373
- import path132 from "path";
40388
+ import path13 from "path";
40374
40389
  import { readFile as readFile42, stat as stat22 } from "fs/promises";
40375
40390
  import path14 from "path";
40376
40391
  import path15 from "path";
@@ -40422,7 +40437,7 @@ function extractLegacyMarkdownWikiLinks(body, wikiDir, basePath) {
40422
40437
  }
40423
40438
  function resolveLegacyWikiTarget(target, wikiDir, basePath) {
40424
40439
  if (!target || target.startsWith("#")) return null;
40425
- if (path13.isAbsolute(target)) return null;
40440
+ if (path12.isAbsolute(target)) return null;
40426
40441
  if (/^[a-z][a-z0-9+.-]*:/i.test(target)) return null;
40427
40442
  const withoutFragment = target.split("#", 1)[0]?.split("?", 1)[0] ?? "";
40428
40443
  if (!withoutFragment.endsWith(".md")) return null;
@@ -40432,12 +40447,12 @@ function resolveLegacyWikiTarget(target, wikiDir, basePath) {
40432
40447
  } catch {
40433
40448
  return null;
40434
40449
  }
40435
- const resolved = path13.resolve(path13.dirname(basePath), decodedTarget);
40436
- const relative = path13.relative(wikiDir, resolved);
40437
- if (!relative || relative.startsWith("..") || path13.isAbsolute(relative)) return null;
40450
+ const resolved = path12.resolve(path12.dirname(basePath), decodedTarget);
40451
+ const relative = path12.relative(wikiDir, resolved);
40452
+ if (!relative || relative.startsWith("..") || path12.isAbsolute(relative)) return null;
40438
40453
  return {
40439
40454
  target,
40440
- entryId: path13.basename(relative, ".md")
40455
+ entryId: path12.basename(relative, ".md")
40441
40456
  };
40442
40457
  }
40443
40458
  var KNOWLEDGE_ROOT_DIRNAME2 = ".fleet/knowledge";
@@ -44279,7 +44294,7 @@ async function planIngest(input, paths, now) {
44279
44294
  const target = `wiki/${input.id}.md`;
44280
44295
  const proposer = input.proposer ?? "tool:wiki_ingest";
44281
44296
  const currentEntry = await readWikiEntry(input.id, paths);
44282
- const currentMarkdown = currentEntry ? await readPatchFile(path132.join(paths.wikiDir, `${input.id}.md`)) : void 0;
44297
+ const currentMarkdown = currentEntry ? await readPatchFile(path13.join(paths.wikiDir, `${input.id}.md`)) : void 0;
44283
44298
  const currentHash = currentMarkdown ? computeContentHash(currentMarkdown) : void 0;
44284
44299
  const rawSource = buildRawSourceEntry(input, now);
44285
44300
  await ensureWorkspaceSchema(paths);
@@ -46394,7 +46409,7 @@ function countQueuedPatches(queueDir) {
46394
46409
  if (!entry.isDirectory() || entry.name.startsWith("_")) {
46395
46410
  continue;
46396
46411
  }
46397
- const patchPath = join11(queueDir, entry.name, QUEUE_PATCH_FILENAME);
46412
+ const patchPath = join10(queueDir, entry.name, QUEUE_PATCH_FILENAME);
46398
46413
  if (existsSync6(patchPath) && statSync2(patchPath).isFile()) {
46399
46414
  count += 1;
46400
46415
  }
@@ -46410,7 +46425,7 @@ function countMarkdownFilesRecursively(dir, isWikiRoot) {
46410
46425
  return 0;
46411
46426
  }
46412
46427
  for (const entry of entries) {
46413
- const entryPath = join11(dir, entry.name);
46428
+ const entryPath = join10(dir, entry.name);
46414
46429
  if (entry.isDirectory()) {
46415
46430
  count += countMarkdownFilesRecursively(entryPath, false);
46416
46431
  continue;
@@ -46503,10 +46518,13 @@ function buildPanelTrackViewModel(track, runs, maxTrackBlocks = DEFAULT_MAX_TRAC
46503
46518
  blocks: blockTail,
46504
46519
  displayCli: track.displayCli,
46505
46520
  displayName: track.displayName,
46521
+ displayedTokenCount: run?.displayedTokenCount ?? stats.estimatedTokenCount,
46506
46522
  estimatedTokenCount: stats.estimatedTokenCount,
46523
+ finishedAt: track.finishedAt,
46507
46524
  isComplete: status === "done" || status === "err",
46508
46525
  kind: track.kind,
46509
46526
  runId: run?.runId ?? track.runId,
46527
+ startedAt: track.startedAt,
46510
46528
  status,
46511
46529
  streamKey: track.streamKey,
46512
46530
  subtitle: track.subtitle,
@@ -46706,20 +46724,21 @@ function appendWidgetJobSummary(carrierRuntime, lines, width, jobs2, frame, them
46706
46724
  width
46707
46725
  ));
46708
46726
  if (shouldInlineSingleTrack(job)) continue;
46709
- appendTrackRows(carrierRuntime, lines, width, job, jobColor, frame, theme);
46727
+ appendTrackRows(carrierRuntime, lines, width, job, jobColor, frame, theme, now);
46710
46728
  }
46711
46729
  }
46712
46730
  }
46713
- function appendTrackRows(carrierRuntime, lines, width, job, jobColor, frame, theme) {
46731
+ function appendTrackRows(carrierRuntime, lines, width, job, jobColor, frame, theme, now) {
46714
46732
  for (let trackIndex = 0; trackIndex < job.tracks.length && lines.length < MAX_WIDGET_LINES; trackIndex++) {
46715
46733
  const track = job.tracks[trackIndex];
46716
46734
  if (!track) continue;
46717
46735
  const trackColor = resolveBackendRowColor(carrierRuntime, track.displayCli, job.ownerCarrierId) || jobColor;
46718
46736
  const icon = trackStatusIcon(track, frame, trackColor);
46737
+ const elapsed = widgetTrackElapsed(track, job, now);
46719
46738
  const stats = widgetTrackStats(track);
46720
46739
  const inline = !track.isComplete ? trackInlineBlock(track) : "";
46721
46740
  lines.push(truncateToWidth(
46722
- `${STREAM_PREFIX} ${border(theme, "\u2514\u2500")} ${icon} ${trackColor}${trackDisplayName(track)}${ANSI_RESET4}${stats}${inline}`,
46741
+ `${STREAM_PREFIX} ${border(theme, "\u2514\u2500")} ${icon} ${trackColor}${trackDisplayName(track)}${ANSI_RESET4}${elapsed}${stats}${inline}`,
46723
46742
  width
46724
46743
  ));
46725
46744
  }
@@ -46783,20 +46802,12 @@ function formatExitWarning() {
46783
46802
  return `${PANEL_DIM_COLOR}${EXIT_WARNING_TEXT}${ANSI_RESET4}`;
46784
46803
  }
46785
46804
  function formatCarrierTile(carrier, frame) {
46786
- const icon = carrierStatusIcon(carrier, frame, carrier.color);
46787
46805
  const hasActiveJob = carrier.activeJobCount > 0;
46788
46806
  const suffix = carrierBadges(carrier);
46789
- const prefix = `${icon} `;
46790
46807
  if (hasActiveJob) {
46791
- return `${prefix}${waveText(carrier.displayName, carrier.rgb, frame)}${suffix}${ANSI_RESET4}`;
46792
- }
46793
- return `${prefix}${carrier.color}${carrier.displayName}${suffix}${ANSI_RESET4}`;
46794
- }
46795
- function carrierStatusIcon(carrier, frame, color) {
46796
- if (carrier.activeJobCount > 0) {
46797
- return activeBreathingIcon(frame, color);
46808
+ return `${waveText(carrier.displayName, carrier.rgb, frame)}${suffix}${ANSI_RESET4}`;
46798
46809
  }
46799
- return color ? `${color}\u25CB${ANSI_RESET4}` : "\u25CB";
46810
+ return `${carrier.color}${carrier.displayName}${suffix}${ANSI_RESET4}`;
46800
46811
  }
46801
46812
  function carrierBadges(carrier) {
46802
46813
  if (carrier.subagentMode) {
@@ -46956,16 +46967,20 @@ function isHudEscIntermediate(code) {
46956
46967
  return code >= 32 && code <= 47;
46957
46968
  }
46958
46969
  function widgetTrackStats(track) {
46959
- const label = formatTokenEstimate(track.estimatedTokenCount);
46970
+ const label = formatTokenEstimate(track.displayedTokenCount);
46960
46971
  return label ? ` ${PANEL_DIM_COLOR}${label}${ANSI_RESET4}` : "";
46961
46972
  }
46962
46973
  function widgetJobElapsed(job, now) {
46963
46974
  return ` ${PANEL_DIM_COLOR}${formatElapsedDuration((job.finishedAt ?? now) - job.startedAt)}${ANSI_RESET4}`;
46964
46975
  }
46976
+ function widgetTrackElapsed(track, job, now) {
46977
+ if (track.startedAt === void 0) return "";
46978
+ return ` ${PANEL_DIM_COLOR}${formatElapsedDuration((track.finishedAt ?? job.finishedAt ?? now) - track.startedAt)}${ANSI_RESET4}`;
46979
+ }
46965
46980
  function activeBreathingIcon(frame, color) {
46966
46981
  const cycleFrame = (frame % BREATHING_CYCLE_FRAMES + BREATHING_CYCLE_FRAMES) % BREATHING_CYCLE_FRAMES;
46967
46982
  const eased = (Math.sin(cycleFrame / BREATHING_CYCLE_FRAMES * Math.PI * 2 - Math.PI / 2) + 1) / 2;
46968
- const icon = eased >= 0.5 ? "\u25CF" : "\u25CB";
46983
+ const icon = eased >= 0.5 ? "\u25CF" : " ";
46969
46984
  return color ? `${color}${icon}${ANSI_RESET4}` : icon;
46970
46985
  }
46971
46986
  function formatTokenEstimate(tokenCount) {
@@ -47268,6 +47283,7 @@ function createJobBarState(options2) {
47268
47283
  if (state.animTimer) return;
47269
47284
  state.animTimer = setInterval(() => {
47270
47285
  state.frame++;
47286
+ advanceDisplayedTokenCounts(state);
47271
47287
  getJobBarStateBindings().onRenderRequest();
47272
47288
  stopPanelAnimTimerIfIdle();
47273
47289
  }, ANIM_INTERVAL_MS);
@@ -47307,8 +47323,10 @@ function createJobBarState(options2) {
47307
47323
  function beginTrack(event) {
47308
47324
  const track = getTrack(event.jobId, event.trackId);
47309
47325
  if (!track) return;
47326
+ const startedAt = event.startedAt;
47310
47327
  const run = ensureRun(canonicalRunKey(track), track.displayCli, "conn");
47311
47328
  run.requestPreview = event.requestPreview ?? run.requestPreview;
47329
+ track.startedAt = startedAt ?? track.startedAt;
47312
47330
  track.status = "conn";
47313
47331
  syncCarrierActivityStatus(track.displayCli);
47314
47332
  }
@@ -47349,8 +47367,10 @@ function createJobBarState(options2) {
47349
47367
  function finalizeTrack(event) {
47350
47368
  const track = getTrack(event.jobId, event.trackId);
47351
47369
  if (!track) return;
47370
+ const finishedAt = event.finishedAt;
47352
47371
  const run = resolveRunForTrack(track) ?? ensureRun(canonicalRunKey(track), track.displayCli, toColStatus(event.status));
47353
47372
  track.status = toColStatus(event.status);
47373
+ track.finishedAt = finishedAt ?? Date.now();
47354
47374
  run.status = track.status;
47355
47375
  if (event.sessionId !== void 0) run.sessionId = event.sessionId;
47356
47376
  if (event.error !== void 0) run.error = event.error;
@@ -47365,6 +47385,7 @@ function createJobBarState(options2) {
47365
47385
  displayName: input.displayName,
47366
47386
  kind: input.kind,
47367
47387
  runId: input.runId,
47388
+ startedAt: input.startedAt,
47368
47389
  status: "wait",
47369
47390
  streamKey: input.streamKey,
47370
47391
  subtitle: input.subtitle,
@@ -47390,6 +47411,7 @@ function createJobBarState(options2) {
47390
47411
  return {
47391
47412
  blocks: [],
47392
47413
  cli,
47414
+ displayedTokenCount: 0,
47393
47415
  runId,
47394
47416
  status,
47395
47417
  text: "",
@@ -47418,6 +47440,31 @@ function createJobBarState(options2) {
47418
47440
  run.thinking = run.blocks.filter((block) => block.type === "thought").map((block) => block.text).join("");
47419
47441
  run.toolCalls = run.blocks.filter((block) => block.type === "tool").map((block) => ({ status: block.status, title: block.title }));
47420
47442
  }
47443
+ function advanceDisplayedTokenCounts(state) {
47444
+ for (const run of state.runs.values()) {
47445
+ const target = estimateTokenCount(run.blocks);
47446
+ if (run.displayedTokenCount >= target) {
47447
+ run.displayedTokenCount = target;
47448
+ continue;
47449
+ }
47450
+ const delta = target - run.displayedTokenCount;
47451
+ const easedStep = Math.ceil(delta * TOKEN_COUNTUP_EASING_FACTOR);
47452
+ run.displayedTokenCount = Math.min(target, run.displayedTokenCount + Math.max(TOKEN_COUNTUP_MIN_STEP, easedStep));
47453
+ }
47454
+ }
47455
+ function estimateTokenCount(blocks) {
47456
+ let charCount = 0;
47457
+ for (const block of blocks) {
47458
+ if (block.type === "tool") {
47459
+ charCount += block.title.length;
47460
+ if (block.status) charCount += block.status.length;
47461
+ charCount += block.detailChars ?? 0;
47462
+ continue;
47463
+ }
47464
+ charCount += block.text.length;
47465
+ }
47466
+ return charCount === 0 ? 0 : Math.max(1, Math.round(charCount / 4));
47467
+ }
47421
47468
  function prependFallbackBlocks(run, fallbackText, fallbackThought) {
47422
47469
  if (fallbackText && !run.text.trim()) {
47423
47470
  run.blocks.unshift({ text: fallbackText, type: "text" });
@@ -47620,91 +47667,45 @@ var TRUE_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
47620
47667
  var FALSE_VALUES = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
47621
47668
  function resolveSessionOptions(input) {
47622
47669
  const envCliId = input.parseCliId(input.env.FLEET_AGENT_CLI);
47623
- const presetDefaultCliId = parsePresetCliId(input.parseCliId, input.preset.defaultCliId);
47624
- const cliId = input.cliIdOverride ?? envCliId ?? presetDefaultCliId ?? input.defaults.cliId;
47625
- const cliIdSource = sourceOf(
47626
- input.cliIdOverride !== void 0,
47627
- envCliId !== void 0,
47628
- presetDefaultCliId !== void 0
47629
- );
47630
- const cliPreset = input.preset.byCli[cliId] ?? {};
47670
+ const cliId = input.cliIdOverride ?? envCliId ?? input.defaults.cliId;
47671
+ const cliIdSource = input.cliIdOverride !== void 0 ? "arg" : envCliId !== void 0 ? "env" : "default";
47672
+ const native = chooseBooleanWithoutArg({
47673
+ env: parseBooleanEnv(input.env.FLEET_NATIVE),
47674
+ globalOptions: input.globalOptions.native,
47675
+ fallback: input.defaults.native
47676
+ });
47677
+ const replaceSystemPrompt = chooseBooleanWithoutArg({
47678
+ env: parseBooleanEnv(input.env.FLEET_REPLACE_SYSTEM_PROMPT),
47679
+ globalOptions: input.globalOptions.replaceSystemPrompt,
47680
+ fallback: input.defaults.replaceSystemPrompt
47681
+ });
47682
+ const enableMetaphor = chooseBooleanWithoutArg({
47683
+ env: parseBooleanEnv(input.env.FLEET_ENABLE_METAPHOR),
47684
+ globalOptions: input.globalOptions.enableMetaphor,
47685
+ fallback: input.defaults.enableMetaphor
47686
+ });
47631
47687
  return {
47632
47688
  values: {
47633
47689
  cliId,
47634
- model: chooseString({
47635
- arg: void 0,
47636
- env: void 0,
47637
- preset: cliPreset.model,
47638
- fallback: input.defaults.model
47639
- }).value,
47640
- native: chooseBooleanWithoutArg({
47641
- env: parseBooleanEnv(input.env.FLEET_NATIVE),
47642
- preset: cliPreset.native,
47643
- fallback: input.defaults.native
47644
- }).value,
47645
- replaceSystemPrompt: chooseBooleanWithoutArg({
47646
- env: parseBooleanEnv(input.env.FLEET_REPLACE_SYSTEM_PROMPT),
47647
- preset: cliPreset.replaceSystemPrompt,
47648
- fallback: input.defaults.replaceSystemPrompt
47649
- }).value,
47650
- enableMetaphor: chooseBooleanWithoutArg({
47651
- env: parseBooleanEnv(input.env.FLEET_ENABLE_METAPHOR),
47652
- preset: cliPreset.enableMetaphor,
47653
- fallback: input.defaults.enableMetaphor
47654
- }).value,
47655
- cursorSync: chooseBoolean({
47656
- arg: input.argv.argvOverrides.cursorSync ? input.argv.cursorSync : void 0,
47657
- env: parseCursorSyncEnv(input.env.FLEET_CURSOR_SYNC),
47658
- preset: cliPreset.cursorSync,
47659
- fallback: input.defaults.cursorSync
47660
- }).value
47690
+ model: input.defaults.model,
47691
+ native: native.value,
47692
+ replaceSystemPrompt: replaceSystemPrompt.value,
47693
+ enableMetaphor: enableMetaphor.value
47661
47694
  },
47662
47695
  sources: {
47663
47696
  cliId: cliIdSource,
47664
- model: chooseString({ arg: void 0, env: void 0, preset: cliPreset.model, fallback: input.defaults.model }).source,
47665
- native: chooseBooleanWithoutArg({ env: parseBooleanEnv(input.env.FLEET_NATIVE), preset: cliPreset.native, fallback: input.defaults.native }).source,
47666
- replaceSystemPrompt: chooseBooleanWithoutArg({ env: parseBooleanEnv(input.env.FLEET_REPLACE_SYSTEM_PROMPT), preset: cliPreset.replaceSystemPrompt, fallback: input.defaults.replaceSystemPrompt }).source,
47667
- enableMetaphor: chooseBooleanWithoutArg({ env: parseBooleanEnv(input.env.FLEET_ENABLE_METAPHOR), preset: cliPreset.enableMetaphor, fallback: input.defaults.enableMetaphor }).source,
47668
- cursorSync: chooseBoolean({ arg: input.argv.argvOverrides.cursorSync ? input.argv.cursorSync : void 0, env: parseCursorSyncEnv(input.env.FLEET_CURSOR_SYNC), preset: cliPreset.cursorSync, fallback: input.defaults.cursorSync }).source
47697
+ model: "default",
47698
+ native: native.source,
47699
+ replaceSystemPrompt: replaceSystemPrompt.source,
47700
+ enableMetaphor: enableMetaphor.source
47669
47701
  }
47670
47702
  };
47671
47703
  }
47672
- function toPresetFragment(options2) {
47673
- return {
47674
- ...options2.model !== void 0 ? { model: options2.model } : {},
47675
- native: options2.native,
47676
- replaceSystemPrompt: options2.replaceSystemPrompt,
47677
- enableMetaphor: options2.enableMetaphor,
47678
- cursorSync: options2.cursorSync
47679
- };
47680
- }
47681
- function chooseBoolean(options2) {
47682
- if (options2.arg !== void 0) return { value: options2.arg, source: "arg" };
47683
- if (options2.env !== void 0) return { value: options2.env, source: "env" };
47684
- if (options2.preset !== void 0) return { value: options2.preset, source: "preset" };
47685
- return { value: options2.fallback, source: "default" };
47686
- }
47687
47704
  function chooseBooleanWithoutArg(options2) {
47688
47705
  if (options2.env !== void 0) return { value: options2.env, source: "env" };
47689
- if (options2.preset !== void 0) return { value: options2.preset, source: "preset" };
47706
+ if (options2.globalOptions !== void 0) return { value: options2.globalOptions, source: "global-options" };
47690
47707
  return { value: options2.fallback, source: "default" };
47691
47708
  }
47692
- function chooseString(options2) {
47693
- if (options2.arg !== void 0) return { value: options2.arg, source: "arg" };
47694
- if (options2.env !== void 0) return { value: options2.env, source: "env" };
47695
- if (options2.preset !== void 0) return { value: options2.preset, source: "preset" };
47696
- return { value: options2.fallback, source: "default" };
47697
- }
47698
- function sourceOf(hasArg, hasEnv, hasPreset) {
47699
- if (hasArg) return "arg";
47700
- if (hasEnv) return "env";
47701
- if (hasPreset) return "preset";
47702
- return "default";
47703
- }
47704
- function parseCursorSyncEnv(value) {
47705
- if (value === void 0) return void 0;
47706
- return parseBooleanEnv(value) ?? true;
47707
- }
47708
47709
  function parseBooleanEnv(value) {
47709
47710
  if (value === void 0) return void 0;
47710
47711
  const normalized = value.trim().toLowerCase();
@@ -47712,23 +47713,18 @@ function parseBooleanEnv(value) {
47712
47713
  if (FALSE_VALUES.has(normalized)) return false;
47713
47714
  return void 0;
47714
47715
  }
47715
- function parsePresetCliId(parseCliId, value) {
47716
- try {
47717
- return parseCliId(value);
47718
- } catch {
47719
- return void 0;
47720
- }
47721
- }
47722
47716
 
47723
47717
  // src/mission-control/options/runtime.ts
47724
47718
  function createSessionOptionsRuntime(options2) {
47725
- let preset = options2.presetService.load();
47719
+ let globalOptions = options2.globalOptionsService.load();
47726
47720
  let resolved = resolve3();
47727
47721
  let sessionFields = /* @__PURE__ */ new Set();
47728
47722
  let draft = { ...resolved.values };
47723
+ let statusLines = [];
47729
47724
  return {
47730
47725
  getResolved: () => toDraftResolved(),
47731
47726
  getDraft: () => draft,
47727
+ getStatusLines: () => statusLines,
47732
47728
  selectCli: (cliId) => {
47733
47729
  const previousDraft = draft;
47734
47730
  const nextResolved = resolveForCli(cliId);
@@ -47740,42 +47736,12 @@ function createSessionOptionsRuntime(options2) {
47740
47736
  };
47741
47737
  markSession("cliId");
47742
47738
  },
47743
- toggleNative: () => {
47744
- draft = { ...draft, native: !draft.native };
47745
- markSession("native");
47746
- },
47747
- toggleReplaceSystemPrompt: () => {
47748
- draft = { ...draft, replaceSystemPrompt: !draft.replaceSystemPrompt };
47749
- markSession("replaceSystemPrompt");
47750
- },
47751
- toggleEnableMetaphor: () => {
47752
- draft = { ...draft, enableMetaphor: !draft.enableMetaphor };
47753
- markSession("enableMetaphor");
47754
- },
47755
- toggleCursorSync: () => {
47756
- draft = { ...draft, cursorSync: !draft.cursorSync };
47757
- markSession("cursorSync");
47758
- },
47739
+ toggleNative: () => updateBoolean("native", !draft.native),
47740
+ toggleReplaceSystemPrompt: () => updateBoolean("replaceSystemPrompt", !draft.replaceSystemPrompt),
47741
+ toggleEnableMetaphor: () => updateBoolean("enableMetaphor", !draft.enableMetaphor),
47759
47742
  setModel: (model) => {
47760
47743
  draft = { ...draft, model: model && model.length > 0 ? model : void 0 };
47761
47744
  markSession("model");
47762
- },
47763
- saveDraft: async () => {
47764
- preset = options2.presetService.update({
47765
- defaultCliId: draft.cliId,
47766
- cliId: draft.cliId,
47767
- values: toPresetFragment(draft)
47768
- });
47769
- resolved = resolve3();
47770
- sessionFields = /* @__PURE__ */ new Set();
47771
- draft = { ...resolved.values };
47772
- return resolved;
47773
- },
47774
- resetOverrides: () => {
47775
- preset = options2.presetService.load();
47776
- resolved = resolveWithoutArg();
47777
- sessionFields = /* @__PURE__ */ new Set();
47778
- draft = { ...resolved.values };
47779
47745
  }
47780
47746
  };
47781
47747
  function resolve3() {
@@ -47783,22 +47749,8 @@ function createSessionOptionsRuntime(options2) {
47783
47749
  argv: options2.argv,
47784
47750
  defaults: options2.defaults,
47785
47751
  env: options2.env,
47786
- parseCliId: options2.parseCliId,
47787
- preset
47788
- });
47789
- }
47790
- function resolveWithoutArg() {
47791
- return resolveSessionOptions({
47792
- argv: {
47793
- ...options2.argv,
47794
- argvOverrides: {
47795
- cursorSync: false
47796
- }
47797
- },
47798
- defaults: options2.defaults,
47799
- env: options2.env,
47800
- parseCliId: options2.parseCliId,
47801
- preset
47752
+ globalOptions,
47753
+ parseCliId: options2.parseCliId
47802
47754
  });
47803
47755
  }
47804
47756
  function resolveForCli(cliId) {
@@ -47810,13 +47762,23 @@ function createSessionOptionsRuntime(options2) {
47810
47762
  cliId
47811
47763
  },
47812
47764
  env: options2.env,
47813
- parseCliId: options2.parseCliId,
47814
- preset
47765
+ globalOptions,
47766
+ parseCliId: options2.parseCliId
47815
47767
  });
47816
47768
  }
47817
47769
  function markSession(field) {
47818
47770
  sessionFields = new Set(sessionFields).add(field);
47819
47771
  }
47772
+ function persistGlobalOptions(field, value) {
47773
+ statusLines = [];
47774
+ void Promise.resolve().then(() => {
47775
+ globalOptions = options2.globalOptionsService.update((current) => ({ ...current, [field]: value }));
47776
+ }).catch((error51) => {
47777
+ statusLines = [`Save failed: ${formatSaveError(error51)}`];
47778
+ }).finally(() => {
47779
+ options2.onStatusChange?.();
47780
+ });
47781
+ }
47820
47782
  function toDraftResolved() {
47821
47783
  return {
47822
47784
  sources: {
@@ -47826,6 +47788,19 @@ function createSessionOptionsRuntime(options2) {
47826
47788
  values: draft
47827
47789
  };
47828
47790
  }
47791
+ function updateBoolean(field, value) {
47792
+ globalOptions = { ...globalOptions, [field]: value };
47793
+ resolved = resolve3();
47794
+ draft = { ...draft, [field]: value };
47795
+ markSession(field);
47796
+ persistGlobalOptions(field, value);
47797
+ }
47798
+ }
47799
+ function formatSaveError(error51) {
47800
+ if (error51 instanceof Error && error51.message.length > 0) {
47801
+ return error51.message;
47802
+ }
47803
+ return "Failed to save Fleet options.";
47829
47804
  }
47830
47805
 
47831
47806
  // src/runtime/runtime.ts
@@ -47995,7 +47970,7 @@ var DEFAULT_HOST_KEYBINDINGS = [
47995
47970
  function createMissionControlProfileConfig(options2) {
47996
47971
  return {
47997
47972
  cliOptions: getAgentCliMetadata(),
47998
- defaultCliId: resolveAgentCliId(options2.env),
47973
+ initialCliId: resolveAgentCliId(options2.env),
47999
47974
  resolveProfile: (selectedCliId, launchOptions) => resolveAgentCliProfile(options2.env, options2.invocationCwd, { cliId: selectedCliId, model: launchOptions?.model })
48000
47975
  };
48001
47976
  }
@@ -48008,14 +47983,17 @@ async function runApp(options2 = {}) {
48008
47983
  argv: argvOptions,
48009
47984
  defaults: {
48010
47985
  cliId: getDefaultAgentCliId(),
48011
- cursorSync: true,
48012
47986
  enableMetaphor: false,
48013
47987
  native: false,
48014
47988
  replaceSystemPrompt: true
48015
47989
  },
48016
47990
  env: process.env,
48017
- parseCliId: parseAgentCliId,
48018
- presetService: runtime.infraServices.presetService
47991
+ globalOptionsService: runtime.infraServices.globalOptionsService,
47992
+ onStatusChange: () => {
47993
+ ptyManager?.requestResize("programmatic");
47994
+ scheduleRender();
47995
+ },
47996
+ parseCliId: parseAgentCliId
48019
47997
  });
48020
47998
  const initialSessionOptions = sessionOptionsRuntime.getResolved().values;
48021
47999
  const cliId = initialSessionOptions.cliId;
@@ -48036,7 +48014,6 @@ async function runApp(options2 = {}) {
48036
48014
  env: process.env,
48037
48015
  invocationCwd
48038
48016
  });
48039
- const optionChips = createMissionControlOptionChips(sessionOptionsRuntime.getResolved());
48040
48017
  const wikiController = createWikiProcessController({
48041
48018
  cwd: invocationCwd,
48042
48019
  onChange: () => {
@@ -48047,11 +48024,8 @@ async function runApp(options2 = {}) {
48047
48024
  const release2 = readFleetCliRelease();
48048
48025
  const missionControl = createMissionControlController({
48049
48026
  ...missionControlProfileConfig,
48050
- defaultCliId: cliId,
48051
- cliOptions: missionControlProfileConfig.cliOptions.map((entry) => ({
48052
- ...entry,
48053
- optionChips: entry.id === cliId ? optionChips : []
48054
- })),
48027
+ initialCliId: cliId,
48028
+ cliOptions: missionControlProfileConfig.cliOptions,
48055
48029
  authService: runtime.infraServices.authService,
48056
48030
  carrierRuntime: runtime.carrierRuntime,
48057
48031
  createPtyHost: (profile) => createPtyHost({ profile }),
@@ -48071,7 +48045,6 @@ async function runApp(options2 = {}) {
48071
48045
  },
48072
48046
  env: process.env,
48073
48047
  invocationCwd,
48074
- presetService: runtime.infraServices.presetService,
48075
48048
  release: release2,
48076
48049
  sessionOptions: sessionOptionsRuntime,
48077
48050
  wikiController
@@ -48199,7 +48172,7 @@ async function runApp(options2 = {}) {
48199
48172
  writeDedicated: (data) => missionControl.ptyHost.write(data)
48200
48173
  });
48201
48174
  syncCursorPolicy = createCursorPolicySync({
48202
- cursorSync: true,
48175
+ cursorSync: argvOptions.cursorSync,
48203
48176
  cursorSyncExplicitlyEnabled: argvOptions.cursorSyncExplicitlyEnabled,
48204
48177
  fleetPty: missionBridge.ptyApi,
48205
48178
  getActiveAgentProfileId: () => missionControl.getActiveProfile()?.id,
@@ -48209,14 +48182,6 @@ async function runApp(options2 = {}) {
48209
48182
  ptyView: missionControl.ptyView,
48210
48183
  ui
48211
48184
  });
48212
- const staticCursorPolicySync = syncCursorPolicy;
48213
- syncCursorPolicy = () => {
48214
- if (!sessionOptionsRuntime.getDraft().cursorSync) {
48215
- ui.setCursorAnchorTarget(void 0);
48216
- return;
48217
- }
48218
- staticCursorPolicySync();
48219
- };
48220
48185
  ui.setChildren([missionControl.component, missionBridge.component]);
48221
48186
  syncCursorPolicy();
48222
48187
  missionBridge.start();
@@ -48233,17 +48198,6 @@ async function runApp(options2 = {}) {
48233
48198
  process.stdout.write(KITTY_ENABLE);
48234
48199
  disposeInputStream = attachInputStream(ui);
48235
48200
  }
48236
- function createMissionControlOptionChips(resolved) {
48237
- const { sources, values } = resolved;
48238
- const star = (source) => source === "arg" ? "*" : "";
48239
- return [
48240
- values.native ? `Native${star(sources.native)}` : `Fleet prompt${star(sources.native)}`,
48241
- values.replaceSystemPrompt ? `Replace${star(sources.replaceSystemPrompt)}` : `Append${star(sources.replaceSystemPrompt)}`,
48242
- values.enableMetaphor ? `Metaphor${star(sources.enableMetaphor)}` : void 0,
48243
- values.model ? values.model : void 0,
48244
- values.cursorSync ? void 0 : `Cursor off${star(sources.cursorSync)}`
48245
- ].filter((chip) => chip !== void 0);
48246
- }
48247
48201
  function createRunAppArgOptions(options2) {
48248
48202
  return {
48249
48203
  argvOverrides: {
@@ -48307,7 +48261,7 @@ var HELP_BANNER_INDENT2 = " ";
48307
48261
  var HELP_HINT = "Run 'fleet --help' for usage.";
48308
48262
  var TRUE_VALUES2 = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
48309
48263
  function parseFleetCliOptions(argv2, env = process.env) {
48310
- const cursorSyncEnv = parseCursorSyncEnv2(env.FLEET_CURSOR_SYNC);
48264
+ const cursorSyncEnv = parseCursorSyncEnv(env.FLEET_CURSOR_SYNC);
48311
48265
  let cursorSync = cursorSyncEnv.value;
48312
48266
  let cursorSyncExplicitlyEnabled = cursorSyncEnv.explicitlyEnabled;
48313
48267
  let help = false;
@@ -48368,7 +48322,7 @@ function formatUnknownFleetOption(option3) {
48368
48322
  return `Unknown fleet option: ${option3}
48369
48323
  ${HELP_HINT}`;
48370
48324
  }
48371
- function parseCursorSyncEnv2(value) {
48325
+ function parseCursorSyncEnv(value) {
48372
48326
  if (value === void 0) {
48373
48327
  return { explicitlyEnabled: false, value: true };
48374
48328
  }