nolo-cli 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +728 -73
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -16077,12 +16077,14 @@ function buildLocalToolExecutors(args2) {
16077
16077
  })();
16078
16078
  const question = String(parsedArgs.question ?? "").trim();
16079
16079
  const choices = Array.isArray(parsedArgs.choices) ? parsedArgs.choices : [];
16080
+ const questions = Array.isArray(parsedArgs.questions) ? parsedArgs.questions : void 0;
16080
16081
  const blocking = parsedArgs.blocking !== false;
16081
- if (!question || choices.length === 0) {
16082
+ const hasQuestions = questions && questions.length > 0;
16083
+ if (!hasQuestions && (!question || choices.length === 0)) {
16082
16084
  return {
16083
16085
  content: JSON.stringify({
16084
16086
  error: "ui_ask_choice",
16085
- detail: "ui_ask_choice \u9700\u8981 question \u548C\u81F3\u5C11\u4E00\u4E2A choice\u3002"
16087
+ detail: "ui_ask_choice \u9700\u8981 question+choices \u6216 questions\u3002"
16086
16088
  }),
16087
16089
  metadata: { uiAskChoice: true, error: true }
16088
16090
  };
@@ -16090,10 +16092,28 @@ function buildLocalToolExecutors(args2) {
16090
16092
  if (args2.requestUserChoice) {
16091
16093
  try {
16092
16094
  const result = await args2.requestUserChoice({
16093
- question,
16094
- choices,
16095
- blocking
16095
+ question: hasQuestions ? questions[0].question : question,
16096
+ choices: hasQuestions ? questions[0].choices : choices,
16097
+ blocking,
16098
+ ...hasQuestions ? { questions } : {}
16096
16099
  });
16100
+ if (result.kind === "multi-submitted") {
16101
+ return {
16102
+ content: JSON.stringify({
16103
+ type: "ui_ask_choice",
16104
+ question,
16105
+ choices,
16106
+ blocking,
16107
+ ...hasQuestions ? { questions } : {},
16108
+ answers: result.answers,
16109
+ selected: {
16110
+ label: result.answers.map((a) => a.userMessage).join(", "),
16111
+ userMessage: result.userMessage
16112
+ }
16113
+ }),
16114
+ metadata: { uiAskChoice: true, resolved: true }
16115
+ };
16116
+ }
16097
16117
  if (result.kind === "selected") {
16098
16118
  return {
16099
16119
  content: JSON.stringify({
@@ -16101,6 +16121,7 @@ function buildLocalToolExecutors(args2) {
16101
16121
  question,
16102
16122
  choices,
16103
16123
  blocking,
16124
+ ...hasQuestions ? { questions } : {},
16104
16125
  selected: {
16105
16126
  label: result.label,
16106
16127
  userMessage: result.userMessage
@@ -16115,6 +16136,7 @@ function buildLocalToolExecutors(args2) {
16115
16136
  question,
16116
16137
  choices,
16117
16138
  blocking,
16139
+ ...hasQuestions ? { questions } : {},
16118
16140
  selected: { label: "", userMessage: "" },
16119
16141
  cancelled: true
16120
16142
  }),
@@ -16128,7 +16150,8 @@ function buildLocalToolExecutors(args2) {
16128
16150
  type: "ui_ask_choice",
16129
16151
  question,
16130
16152
  choices,
16131
- blocking
16153
+ blocking,
16154
+ ...hasQuestions ? { questions } : {}
16132
16155
  }),
16133
16156
  metadata: { uiAskChoice: true }
16134
16157
  };
@@ -37355,11 +37378,24 @@ var init_dialogUrl = __esm({
37355
37378
 
37356
37379
  // packages/ai/tools/uiAskChoiceTool.ts
37357
37380
  async function uiAskChoiceFunc(args2, _thunkApi) {
37381
+ const blocking = args2?.blocking !== false;
37382
+ if (Array.isArray(args2?.questions) && args2.questions.length > 0) {
37383
+ const firstQ = args2.questions[0];
37384
+ return {
37385
+ rawData: {
37386
+ type: "ui_ask_choice",
37387
+ question: firstQ?.question ?? "",
37388
+ choices: firstQ?.choices ?? [],
37389
+ blocking,
37390
+ questions: args2.questions
37391
+ },
37392
+ displayData: args2.questions.map((q) => q.question).join(" / ")
37393
+ };
37394
+ }
37358
37395
  const question = String(args2?.question ?? "").trim();
37359
37396
  const choices = Array.isArray(args2?.choices) ? args2.choices : [];
37360
- const blocking = args2?.blocking !== false;
37361
37397
  if (!question || choices.length === 0) {
37362
- throw new Error("ui_ask_choice \u9700\u8981 question \u548C\u81F3\u5C11\u4E00\u4E2A choice\u3002");
37398
+ throw new Error("ui_ask_choice \u9700\u8981 question+choices \u6216 questions\u3002");
37363
37399
  }
37364
37400
  return {
37365
37401
  rawData: {
@@ -37408,14 +37444,20 @@ var init_uiAskChoiceTool = __esm({
37408
37444
  "\u5728\u4EE3\u7801\u534F\u4F5C\u573A\u666F\u4E0B\u7684\u63A8\u8350\u7528\u6CD5\uFF1A",
37409
37445
  "- \u5982\u679C\u4F60\u5DF2\u7ECF\u5728\u672C\u8F6E assistant \u7684 content \u4E2D\u7ED9\u51FA\u4E86\u5206\u6790\u3001\u8BF4\u660E\u6216\u591A\u4E2A\u5907\u9009\u65B9\u6848\uFF0C\u53EF\u4EE5\u5728\u540C\u4E00\u8F6E\u6D88\u606F\u7684\u7ED3\u5C3E\u8C03\u7528\u672C\u5DE5\u5177\uFF0C\u8BF7\u7528\u6237\u9009\u62E9\u201C\u662F\u5426\u6839\u636E\u4E0A\u8FF0\u65B9\u6848\u5F00\u59CB\u5B9E\u9645\u4FEE\u6539\u4EE3\u7801\u201D\u6216\u201C\u4F18\u5148\u6267\u884C\u54EA\u4E00\u4E2A\u65B9\u6848/\u6B65\u9AA4\u201D\u3002",
37410
37446
  "- \u5728\u8FD9\u79CD\u60C5\u51B5\u4E0B\uFF0Cquestion \u5B57\u6BB5\u901A\u5E38\u662F\u4E00\u4E2A\u7B80\u77ED\u7684\u95EE\u9898\uFF08\u4F8B\u5982\uFF1A\u201C\u63A5\u4E0B\u6765\u4F60\u5E0C\u671B\u6211\u6309\u54EA\u4E2A\u65B9\u6848\u6765\u5177\u4F53\u4FEE\u6539\u4EE3\u7801\uFF1F\u201D\uFF09\uFF0C\u800C\u8BE6\u7EC6\u7684\u89E3\u91CA\u548C\u65B9\u6848\u63CF\u8FF0\u653E\u5728 content \u4E2D\u3002",
37411
- "- \u5982\u679C\u5F53\u524D\u5BF9\u8BDD\u53EA\u9700\u8981\u4E00\u4E2A\u7B80\u5355\u7684\u9009\u62E9\uFF0C\u800C\u4E0D\u9700\u8981\u989D\u5916\u89E3\u91CA\uFF0C\u4F60\u4E5F\u53EF\u4EE5\u4E0D\u8F93\u51FA\u989D\u5916\u7684 content\uFF0C\u800C\u53EA\u8C03\u7528\u672C\u5DE5\u5177\uFF0C\u7531 question \u5B57\u6BB5\u76F4\u63A5\u5411\u7528\u6237\u63D0\u95EE\u3002"
37447
+ "- \u5982\u679C\u5F53\u524D\u5BF9\u8BDD\u53EA\u9700\u8981\u4E00\u4E2A\u7B80\u5355\u7684\u9009\u62E9\uFF0C\u800C\u4E0D\u9700\u8981\u989D\u5916\u89E3\u91CA\uFF0C\u4F60\u4E5F\u53EF\u4EE5\u4E0D\u8F93\u51FA\u989D\u5916\u7684 content\uFF0C\u800C\u53EA\u8C03\u7528\u672C\u5DE5\u5177\uFF0C\u7531 question \u5B57\u6BB5\u76F4\u63A5\u5411\u7528\u6237\u63D0\u95EE\u3002",
37448
+ "",
37449
+ "\u591A\u95EE\u9898 & \u591A\u9009\u652F\u6301\uFF1A",
37450
+ "- \u5F53\u4F60\u9700\u8981\u4E00\u6B21\u95EE\u591A\u4E2A\u95EE\u9898\u65F6\uFF0C\u4F7F\u7528 questions \u6570\u7EC4\u4EE3\u66FF question+choices\u3002",
37451
+ "- \u6BCF\u4E2A question \u53EF\u4EE5\u8BBE\u7F6E multiSelect: true \u5141\u8BB8\u591A\u9009\u3002",
37452
+ "- \u6BCF\u4E2A question \u53EF\u4EE5\u8BBE\u7F6E allowOther: false \u9690\u85CF\u201C\u5176\u4ED6\u201D\u8F93\u5165\u6846\u3002",
37453
+ "- \u6BCF\u4E2A choice \u53EF\u4EE5\u52A0 detail \u5B57\u6BB5\u63D0\u4F9B\u66F4\u957F\u7684\u63CF\u8FF0\u3002"
37412
37454
  ].join("\n"),
37413
37455
  parameters: {
37414
37456
  type: "object",
37415
37457
  properties: {
37416
37458
  question: {
37417
37459
  type: "string",
37418
- description: "\u5C55\u793A\u7ED9\u7528\u6237\u7684\u95EE\u9898\u6587\u6848\uFF0C\u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u8981\u505A\u4EC0\u4E48\u9009\u62E9\u3002\u4F8B\u5982\uFF1A\u201C\u63A5\u4E0B\u6765\u4F60\u66F4\u5E0C\u671B\u6211\u5E2E\u4F60\u505A\u54EA\u4EF6\u4E8B\uFF1F\u201D\u3002\u4E0D\u8981\u5728\u540C\u4E00\u8F6E assistant \u666E\u901A\u6587\u672C\u91CC\u91CD\u590D\u8FD9\u53E5\u8BDD\u3002"
37460
+ description: "\u5C55\u793A\u7ED9\u7528\u6237\u7684\u95EE\u9898\u6587\u6848\uFF08\u5355\u95EE\u9898\u6A21\u5F0F\uFF09\u3002\u4E0E questions \u4E8C\u9009\u4E00\u3002"
37419
37461
  },
37420
37462
  choices: {
37421
37463
  type: "array",
@@ -37431,6 +37473,10 @@ var init_uiAskChoiceTool = __esm({
37431
37473
  type: "string",
37432
37474
  description: "\u663E\u793A\u7ED9\u7528\u6237\u770B\u7684\u6309\u94AE\u6587\u5B57\u3002\u4F8B\u5982\uFF1A\u201C\u751F\u6210\u672C\u5468\u5468\u62A5\u201D\u3002"
37433
37475
  },
37476
+ detail: {
37477
+ type: "string",
37478
+ description: "\u9009\u9879\u7684\u8865\u5145\u63CF\u8FF0\uFF0C\u663E\u793A\u5728 label \u4E0B\u65B9\u3002\u53EF\u9009\u3002"
37479
+ },
37434
37480
  userMessage: {
37435
37481
  type: "string",
37436
37482
  description: [
@@ -37443,6 +37489,56 @@ var init_uiAskChoiceTool = __esm({
37443
37489
  required: ["id", "label"]
37444
37490
  }
37445
37491
  },
37492
+ questions: {
37493
+ type: "array",
37494
+ description: "\u591A\u95EE\u9898\u6A21\u5F0F\uFF1A\u4E00\u6B21\u95EE\u591A\u4E2A\u95EE\u9898\uFF0C\u6BCF\u4E2A\u95EE\u9898\u72EC\u7ACB\u6E32\u67D3\u4E3A\u4E00\u4E2A tab\u3002\u4E0E question+choices \u4E8C\u9009\u4E00\u3002",
37495
+ items: {
37496
+ type: "object",
37497
+ properties: {
37498
+ id: {
37499
+ type: "string",
37500
+ description: "\u95EE\u9898\u6807\u8BC6\uFF0C\u7528\u4E8E\u7ED3\u679C\u5BF9\u5E94\u3002"
37501
+ },
37502
+ question: {
37503
+ type: "string",
37504
+ description: "\u95EE\u9898\u6587\u6848\u3002"
37505
+ },
37506
+ choices: {
37507
+ type: "array",
37508
+ description: "\u8BE5\u95EE\u9898\u7684\u5907\u9009\u9879\u3002",
37509
+ items: {
37510
+ type: "object",
37511
+ properties: {
37512
+ id: { type: "string" },
37513
+ label: { type: "string" },
37514
+ detail: {
37515
+ type: "string",
37516
+ description: "\u8865\u5145\u63CF\u8FF0\u3002"
37517
+ },
37518
+ userMessage: { type: "string" }
37519
+ },
37520
+ required: ["id", "label"]
37521
+ }
37522
+ },
37523
+ multiSelect: {
37524
+ type: "boolean",
37525
+ description: "\u5141\u8BB8\u591A\u9009\u3002\u9ED8\u8BA4 false\u3002",
37526
+ default: false
37527
+ },
37528
+ allowOther: {
37529
+ type: "boolean",
37530
+ description: "\u663E\u793A\u201C\u5176\u4ED6\u201D\u81EA\u7531\u8F93\u5165\u884C\u3002\u9ED8\u8BA4 true\u3002",
37531
+ default: true
37532
+ },
37533
+ required: {
37534
+ type: "boolean",
37535
+ description: "\u662F\u5426\u5FC5\u987B\u56DE\u7B54\u624D\u80FD\u63D0\u4EA4\u3002\u9ED8\u8BA4 true\u3002",
37536
+ default: true
37537
+ }
37538
+ },
37539
+ required: ["id", "question", "choices"]
37540
+ }
37541
+ },
37446
37542
  blocking: {
37447
37543
  type: "boolean",
37448
37544
  description: [
@@ -37452,7 +37548,7 @@ var init_uiAskChoiceTool = __esm({
37452
37548
  default: true
37453
37549
  }
37454
37550
  },
37455
- required: ["question", "choices"]
37551
+ required: []
37456
37552
  }
37457
37553
  };
37458
37554
  }
@@ -106909,13 +107005,15 @@ function renderDialogOverflow(text, colorEnabled = resolveCliColorEnabled()) {
106909
107005
  function renderDialogCommand(text, colorEnabled = resolveCliColorEnabled()) {
106910
107006
  return themeText(` ${text}`, "danger", colorEnabled);
106911
107007
  }
106912
- var DIALOG_CURSOR;
107008
+ var DIALOG_CURSOR, DIALOG_CHECKED, DIALOG_UNCHECKED;
106913
107009
  var init_dialogFrame = __esm({
106914
107010
  "packages/cli/tui/dialogFrame.ts"() {
106915
107011
  "use strict";
106916
107012
  init_terminalStyles();
106917
107013
  init_theme();
106918
107014
  DIALOG_CURSOR = "\u276F";
107015
+ DIALOG_CHECKED = "\u25C9";
107016
+ DIALOG_UNCHECKED = "\u25CB";
106919
107017
  }
106920
107018
  });
106921
107019
 
@@ -107257,6 +107355,496 @@ var init_confirmDialog = __esm({
107257
107355
  }
107258
107356
  });
107259
107357
 
107358
+ // packages/ai/tools/askChoiceState.ts
107359
+ function normalizeAskChoiceArgs(args2) {
107360
+ const blocking = args2.blocking !== false;
107361
+ if (Array.isArray(args2.questions) && args2.questions.length > 0) {
107362
+ return {
107363
+ questions: args2.questions.map(
107364
+ (q, i) => normalizeQuestion(q, i)
107365
+ ),
107366
+ blocking
107367
+ };
107368
+ }
107369
+ const question = String(args2.question ?? "").trim();
107370
+ const choices = Array.isArray(args2.choices) ? args2.choices : [];
107371
+ if (!question || choices.length === 0) {
107372
+ return { questions: [], blocking };
107373
+ }
107374
+ return {
107375
+ questions: [
107376
+ normalizeQuestion(
107377
+ { question, choices, multiSelect: false, allowOther: true, required: true },
107378
+ 0
107379
+ )
107380
+ ],
107381
+ blocking
107382
+ };
107383
+ }
107384
+ function normalizeQuestion(raw, index) {
107385
+ return {
107386
+ id: String(raw?.id ?? `q${index}`),
107387
+ question: String(raw?.question ?? "").trim(),
107388
+ choices: (Array.isArray(raw?.choices) ? raw.choices : []).map(
107389
+ (c, ci) => ({
107390
+ id: String(c?.id ?? `c${ci}`),
107391
+ label: String(c?.label ?? "").trim(),
107392
+ ...typeof c?.detail === "string" && c.detail.trim() ? { detail: c.detail.trim() } : {},
107393
+ ...typeof c?.userMessage === "string" && c.userMessage.trim() ? { userMessage: c.userMessage.trim() } : {}
107394
+ })
107395
+ ),
107396
+ multiSelect: raw?.multiSelect === true,
107397
+ allowOther: raw?.allowOther !== false,
107398
+ required: raw?.required !== false
107399
+ };
107400
+ }
107401
+ function createInitialAskChoiceState(questions) {
107402
+ return {
107403
+ questions,
107404
+ activeIndex: 0,
107405
+ questionStates: questions.map(() => ({
107406
+ cursorIndex: 0,
107407
+ selectedIds: [],
107408
+ pickedId: null,
107409
+ otherText: "",
107410
+ otherFocused: false
107411
+ })),
107412
+ phase: "active"
107413
+ };
107414
+ }
107415
+ function askChoiceReducer(state4, action3) {
107416
+ if (state4.phase !== "active") return state4;
107417
+ switch (action3.type) {
107418
+ case "CANCEL":
107419
+ return { ...state4, phase: "cancelled" };
107420
+ case "SUBMIT": {
107421
+ if (!canSubmit(state4)) return state4;
107422
+ return { ...state4, phase: "submitted" };
107423
+ }
107424
+ case "SWITCH_TAB": {
107425
+ const idx = clamp3(action3.index, 0, state4.questions.length - 1);
107426
+ return { ...state4, activeIndex: idx };
107427
+ }
107428
+ case "NEXT_TAB": {
107429
+ const idx = Math.min(state4.activeIndex + 1, state4.questions.length - 1);
107430
+ return { ...state4, activeIndex: idx };
107431
+ }
107432
+ case "PREV_TAB": {
107433
+ const idx = Math.max(state4.activeIndex - 1, 0);
107434
+ return { ...state4, activeIndex: idx };
107435
+ }
107436
+ case "MOVE_CURSOR": {
107437
+ const qs = state4.questionStates[state4.activeIndex];
107438
+ const q = state4.questions[state4.activeIndex];
107439
+ const maxIndex = q.allowOther ? q.choices.length : q.choices.length - 1;
107440
+ const next = clamp3(qs.cursorIndex + action3.delta, 0, maxIndex);
107441
+ const newQs = [...state4.questionStates];
107442
+ newQs[state4.activeIndex] = {
107443
+ ...qs,
107444
+ cursorIndex: next,
107445
+ otherFocused: false
107446
+ };
107447
+ return { ...state4, questionStates: newQs };
107448
+ }
107449
+ case "TOGGLE_AT_CURSOR": {
107450
+ const qs = state4.questionStates[state4.activeIndex];
107451
+ const q = state4.questions[state4.activeIndex];
107452
+ if (!q.multiSelect) return state4;
107453
+ const isOtherRow = qs.cursorIndex >= q.choices.length;
107454
+ if (isOtherRow) {
107455
+ const newQs2 = [...state4.questionStates];
107456
+ newQs2[state4.activeIndex] = { ...qs, otherFocused: !qs.otherFocused };
107457
+ return { ...state4, questionStates: newQs2 };
107458
+ }
107459
+ const choiceId = q.choices[qs.cursorIndex]?.id;
107460
+ if (!choiceId) return state4;
107461
+ const has = qs.selectedIds.includes(choiceId);
107462
+ const newSelected = has ? qs.selectedIds.filter((id) => id !== choiceId) : [...qs.selectedIds, choiceId];
107463
+ const newQs = [...state4.questionStates];
107464
+ newQs[state4.activeIndex] = { ...qs, selectedIds: newSelected };
107465
+ return { ...state4, questionStates: newQs };
107466
+ }
107467
+ case "SELECT_AT_CURSOR": {
107468
+ const qs = state4.questionStates[state4.activeIndex];
107469
+ const q = state4.questions[state4.activeIndex];
107470
+ const isOtherRow = qs.cursorIndex >= q.choices.length;
107471
+ if (isOtherRow) {
107472
+ const newQs2 = [...state4.questionStates];
107473
+ newQs2[state4.activeIndex] = { ...qs, otherFocused: true };
107474
+ return { ...state4, questionStates: newQs2 };
107475
+ }
107476
+ const choiceId = q.choices[qs.cursorIndex]?.id;
107477
+ if (!choiceId) return state4;
107478
+ if (q.multiSelect) {
107479
+ const has = qs.selectedIds.includes(choiceId);
107480
+ const newSelected = has ? qs.selectedIds.filter((id) => id !== choiceId) : [...qs.selectedIds, choiceId];
107481
+ const newQs2 = [...state4.questionStates];
107482
+ newQs2[state4.activeIndex] = { ...qs, selectedIds: newSelected };
107483
+ return { ...state4, questionStates: newQs2 };
107484
+ }
107485
+ const newQs = [...state4.questionStates];
107486
+ newQs[state4.activeIndex] = { ...qs, pickedId: choiceId, otherFocused: false };
107487
+ if (state4.questions.length === 1) {
107488
+ return {
107489
+ ...state4,
107490
+ questionStates: newQs,
107491
+ phase: "submitted"
107492
+ };
107493
+ }
107494
+ const nextTab = Math.min(
107495
+ state4.activeIndex + 1,
107496
+ state4.questions.length - 1
107497
+ );
107498
+ return {
107499
+ ...state4,
107500
+ questionStates: newQs,
107501
+ activeIndex: nextTab
107502
+ };
107503
+ }
107504
+ case "FOCUS_OTHER": {
107505
+ const qs = state4.questionStates[state4.activeIndex];
107506
+ const newQs = [...state4.questionStates];
107507
+ newQs[state4.activeIndex] = { ...qs, otherFocused: true };
107508
+ return { ...state4, questionStates: newQs };
107509
+ }
107510
+ case "BLUR_OTHER": {
107511
+ const qs = state4.questionStates[state4.activeIndex];
107512
+ const newQs = [...state4.questionStates];
107513
+ newQs[state4.activeIndex] = { ...qs, otherFocused: false };
107514
+ return { ...state4, questionStates: newQs };
107515
+ }
107516
+ case "SET_OTHER_TEXT": {
107517
+ const qs = state4.questionStates[state4.activeIndex];
107518
+ const newQs = [...state4.questionStates];
107519
+ newQs[state4.activeIndex] = { ...qs, otherText: action3.text };
107520
+ return { ...state4, questionStates: newQs };
107521
+ }
107522
+ default:
107523
+ return state4;
107524
+ }
107525
+ }
107526
+ function isQuestionAnswered(q, qs) {
107527
+ if (!q.required) return true;
107528
+ const hasSelection = q.multiSelect ? qs.selectedIds.length > 0 : qs.pickedId !== null;
107529
+ const hasOther = q.allowOther && qs.otherText.trim().length > 0;
107530
+ return hasSelection || hasOther;
107531
+ }
107532
+ function canSubmit(state4) {
107533
+ return state4.questions.every(
107534
+ (q, i) => isQuestionAnswered(q, state4.questionStates[i])
107535
+ );
107536
+ }
107537
+ function buildAskChoiceResult(state4) {
107538
+ if (state4.phase === "cancelled") return { kind: "cancelled" };
107539
+ if (state4.phase !== "submitted") return { kind: "cancelled" };
107540
+ const answers = state4.questions.map((q, i) => {
107541
+ const qs = state4.questionStates[i];
107542
+ const selectedIds = q.multiSelect ? qs.selectedIds : qs.pickedId ? [qs.pickedId] : [];
107543
+ const otherText = qs.otherText.trim();
107544
+ const parts = [];
107545
+ for (const id of selectedIds) {
107546
+ const choice = q.choices.find((c) => c.id === id);
107547
+ if (choice) {
107548
+ parts.push(choice.userMessage || choice.label);
107549
+ }
107550
+ }
107551
+ if (otherText) {
107552
+ parts.push(otherText);
107553
+ }
107554
+ return {
107555
+ questionId: q.id,
107556
+ selectedIds,
107557
+ otherText,
107558
+ userMessage: parts.join("\n")
107559
+ };
107560
+ });
107561
+ return { kind: "submitted", answers };
107562
+ }
107563
+ function clamp3(value, min, max) {
107564
+ return Math.max(min, Math.min(max, value));
107565
+ }
107566
+ var init_askChoiceState = __esm({
107567
+ "packages/ai/tools/askChoiceState.ts"() {
107568
+ "use strict";
107569
+ }
107570
+ });
107571
+
107572
+ // packages/cli/tui/askChoiceDialog.ts
107573
+ function renderTabBar(questions, activeIndex, colorEnabled) {
107574
+ const tabs = questions.map((q, i) => {
107575
+ const label = `Q${i + 1}`;
107576
+ if (i === activeIndex) {
107577
+ return colorEnabled ? `${themeColorSequence("accent")}\x1B[1m ${label} \x1B[0m` : `[${label}]`;
107578
+ }
107579
+ return colorEnabled ? themeText(` ${label} `, "muted", colorEnabled) : ` ${label} `;
107580
+ });
107581
+ const submitLabel = "Submit";
107582
+ return tabs.join(" ") + " " + (colorEnabled ? themeText(submitLabel, "chrome", colorEnabled) : submitLabel);
107583
+ }
107584
+ function renderFooter(colorEnabled) {
107585
+ const hints = "type answer \u21B5save tab switch s submit esc cancel";
107586
+ return colorEnabled ? themeText(` ${hints}`, "chrome", colorEnabled) : ` ${hints}`;
107587
+ }
107588
+ function renderAskChoiceFrame(state4) {
107589
+ const colorEnabled = resolveCliColorEnabled();
107590
+ const q = state4.questions[state4.activeIndex];
107591
+ const qs = state4.questionStates[state4.activeIndex];
107592
+ const lines = [];
107593
+ lines.push(renderDialogTitle("question"));
107594
+ lines.push("");
107595
+ if (state4.questions.length > 1) {
107596
+ lines.push(renderTabBar(state4.questions, state4.activeIndex, colorEnabled));
107597
+ lines.push("");
107598
+ }
107599
+ lines.push(
107600
+ colorEnabled ? `${themeColorSequence("accent")}? ${q.question}\x1B[0m` : `? ${q.question}`
107601
+ );
107602
+ if (q.multiSelect) {
107603
+ lines.push(
107604
+ colorEnabled ? themeText(" Space to toggle, Enter to confirm selection.", "muted", colorEnabled) : " Space to toggle, Enter to confirm selection."
107605
+ );
107606
+ } else {
107607
+ lines.push(
107608
+ colorEnabled ? themeText(" Type your answer, then press Enter to save.", "muted", colorEnabled) : " Type your answer, then press Enter to save."
107609
+ );
107610
+ }
107611
+ lines.push("");
107612
+ const totalRows = q.choices.length + (q.allowOther ? 1 : 0);
107613
+ const window2 = computeVisibleWindow({
107614
+ selectedIndex: qs.cursorIndex,
107615
+ total: totalRows
107616
+ });
107617
+ if (window2.start > 0) {
107618
+ lines.push(
107619
+ colorEnabled ? themeText(` \u2191 ${window2.start} more`, "chrome", colorEnabled) : ` \u2191 ${window2.start} more`
107620
+ );
107621
+ }
107622
+ for (let i = window2.start; i < window2.end; i++) {
107623
+ if (i < q.choices.length) {
107624
+ const choice = q.choices[i];
107625
+ const focused = qs.cursorIndex === i;
107626
+ const checkbox = q.multiSelect ? qs.selectedIds.includes(choice.id) ? DIALOG_CHECKED : DIALOG_UNCHECKED : qs.pickedId === choice.id ? DIALOG_CHECKED : void 0;
107627
+ lines.push(
107628
+ renderDialogRow({
107629
+ label: `[${i + 1}] ${choice.label}`,
107630
+ ...choice.detail ? { detail: choice.detail } : {},
107631
+ focused,
107632
+ ...checkbox ? { checkbox } : {}
107633
+ })
107634
+ );
107635
+ } else {
107636
+ const focused = qs.cursorIndex === i;
107637
+ const marker = focused ? DIALOG_CURSOR : " ";
107638
+ const otherContent = qs.otherFocused ? `${qs.otherText}\u2588` : qs.otherText || "";
107639
+ const row = `${marker} [${i + 1}] Other: ${otherContent}`;
107640
+ if (focused && colorEnabled) {
107641
+ lines.push(
107642
+ `\x1B[1m${themeColorSequence("accent")}${row}\x1B[0m`
107643
+ );
107644
+ } else {
107645
+ lines.push(row);
107646
+ }
107647
+ }
107648
+ }
107649
+ if (window2.end < totalRows) {
107650
+ lines.push(
107651
+ colorEnabled ? themeText(` \u2193 ${totalRows - window2.end} more`, "chrome", colorEnabled) : ` \u2193 ${totalRows - window2.end} more`
107652
+ );
107653
+ }
107654
+ lines.push("");
107655
+ lines.push(renderFooter(colorEnabled));
107656
+ return lines.join("\n");
107657
+ }
107658
+ async function runAskChoiceDialog(args2) {
107659
+ const { request } = args2;
107660
+ const normalized = normalizeAskChoiceArgs({
107661
+ question: request.question,
107662
+ choices: request.choices,
107663
+ questions: request.questions,
107664
+ blocking: request.blocking
107665
+ });
107666
+ if (normalized.questions.length === 0) {
107667
+ return { kind: "cancelled" };
107668
+ }
107669
+ const q0 = normalized.questions[0];
107670
+ if (normalized.questions.length === 1 && !q0.multiSelect && !q0.allowOther) {
107671
+ }
107672
+ let state4 = createInitialAskChoiceState(normalized.questions);
107673
+ const output2 = args2.output ?? process.stdout;
107674
+ const input2 = args2.input ?? process.stdin;
107675
+ const readKey = args2.readKey ?? createRawKeyReader(input2);
107676
+ const wasRaw = Boolean(input2.isTTY && input2.isRaw);
107677
+ let renderedLineCount = 0;
107678
+ const bottomAnchored = Boolean(args2.bottomAnchored && args2.bottomRow);
107679
+ const resolveBottomRow = () => Math.max(
107680
+ 1,
107681
+ typeof args2.bottomRow === "function" ? args2.bottomRow() : args2.bottomRow ?? 0
107682
+ );
107683
+ let lastBottomRow = 0;
107684
+ const paint = () => {
107685
+ const frame = renderAskChoiceFrame(state4);
107686
+ const lines = frame.split("\n");
107687
+ const lineCount = lines.length;
107688
+ const canPosition = outputIsTty(output2) && typeof output2.write === "function";
107689
+ if (bottomAnchored && canPosition) {
107690
+ const anchorRow = resolveBottomRow();
107691
+ clearAnchoredLines(
107692
+ output2,
107693
+ lastBottomRow > 0 ? lastBottomRow : anchorRow,
107694
+ renderedLineCount
107695
+ );
107696
+ for (let i = 0; i < lines.length; i++) {
107697
+ const row = anchorRow - (lines.length - 1 - i);
107698
+ if (row < 1) break;
107699
+ output2.write(`\x1B[${row};1H\x1B[2K${lines[i]}`);
107700
+ }
107701
+ lastBottomRow = anchorRow;
107702
+ renderedLineCount = lineCount;
107703
+ return;
107704
+ }
107705
+ if (canPosition) {
107706
+ for (let i = 0; i < renderedLineCount; i++) {
107707
+ output2.write("\x1B[1A\x1B[2K");
107708
+ }
107709
+ output2.write(`${frame}
107710
+ `);
107711
+ renderedLineCount = lineCount;
107712
+ return;
107713
+ }
107714
+ if (typeof output2.write === "function") {
107715
+ output2.write(`${frame}
107716
+ `);
107717
+ }
107718
+ renderedLineCount = lineCount;
107719
+ };
107720
+ const resizeTarget = output2;
107721
+ const onOutputResize = () => paint();
107722
+ if (input2.isTTY && !wasRaw) {
107723
+ input2.setRawMode?.(true);
107724
+ }
107725
+ if (bottomAnchored && outputIsTty(output2)) {
107726
+ resizeTarget.on?.("resize", onOutputResize);
107727
+ }
107728
+ paint();
107729
+ try {
107730
+ while (state4.phase === "active") {
107731
+ const sequence = await readKey();
107732
+ if (sequence == null) {
107733
+ state4 = askChoiceReducer(state4, { type: "CANCEL" });
107734
+ break;
107735
+ }
107736
+ let action3 = null;
107737
+ if (isCancel(sequence)) {
107738
+ action3 = { type: "CANCEL" };
107739
+ } else if (sequence === CSI_TAB) {
107740
+ action3 = { type: "NEXT_TAB" };
107741
+ } else if (sequence === CSI_SHIFT_TAB) {
107742
+ action3 = { type: "PREV_TAB" };
107743
+ } else if (isArrowUp(sequence)) {
107744
+ action3 = { type: "MOVE_CURSOR", delta: -1 };
107745
+ } else if (isArrowDown(sequence)) {
107746
+ action3 = { type: "MOVE_CURSOR", delta: 1 };
107747
+ } else if (sequence === CSI_SPACE) {
107748
+ action3 = { type: "TOGGLE_AT_CURSOR" };
107749
+ } else if (sequence === "s" && !state4.questionStates[state4.activeIndex].otherFocused && canSubmit(state4)) {
107750
+ action3 = { type: "SUBMIT" };
107751
+ } else if (isSubmit(sequence)) {
107752
+ const qs = state4.questionStates[state4.activeIndex];
107753
+ const q = state4.questions[state4.activeIndex];
107754
+ const isOtherRow = qs.cursorIndex >= q.choices.length;
107755
+ if (qs.otherFocused) {
107756
+ action3 = { type: "BLUR_OTHER" };
107757
+ } else if (isOtherRow && !qs.otherFocused) {
107758
+ action3 = { type: "FOCUS_OTHER" };
107759
+ } else {
107760
+ action3 = { type: "SELECT_AT_CURSOR" };
107761
+ }
107762
+ } else if (sequence === CSI_BACKSPACE || sequence === CSI_BACKSPACE_ALT) {
107763
+ const qs = state4.questionStates[state4.activeIndex];
107764
+ if (qs.otherFocused && qs.otherText.length > 0) {
107765
+ action3 = {
107766
+ type: "SET_OTHER_TEXT",
107767
+ text: qs.otherText.slice(0, -1)
107768
+ };
107769
+ }
107770
+ } else if (sequence.length === 1 && sequence.charCodeAt(0) >= 32) {
107771
+ const qs = state4.questionStates[state4.activeIndex];
107772
+ if (qs.otherFocused) {
107773
+ action3 = {
107774
+ type: "SET_OTHER_TEXT",
107775
+ text: qs.otherText + sequence
107776
+ };
107777
+ }
107778
+ }
107779
+ if (action3) {
107780
+ const prev = state4;
107781
+ state4 = askChoiceReducer(state4, action3);
107782
+ if (state4.phase !== "active") break;
107783
+ if (action3.type === "BLUR_OTHER" && state4.questions.length === 1 && canSubmit(state4)) {
107784
+ state4 = askChoiceReducer(state4, { type: "SUBMIT" });
107785
+ break;
107786
+ }
107787
+ paint();
107788
+ }
107789
+ }
107790
+ } finally {
107791
+ resizeTarget.off?.("resize", onOutputResize);
107792
+ readKey.dispose?.();
107793
+ if (input2.isTTY) {
107794
+ drainInputBuffer(input2);
107795
+ if (!wasRaw) input2.setRawMode?.(false);
107796
+ if (bottomAnchored) {
107797
+ clearAnchoredLines(
107798
+ output2,
107799
+ lastBottomRow > 0 ? lastBottomRow : resolveBottomRow(),
107800
+ renderedLineCount
107801
+ );
107802
+ } else {
107803
+ for (let i = 0; i < renderedLineCount; i++) {
107804
+ output2.write("\x1B[1A\x1B[2K");
107805
+ }
107806
+ }
107807
+ renderedLineCount = 0;
107808
+ }
107809
+ }
107810
+ const result = buildAskChoiceResult(state4);
107811
+ if (result.kind === "cancelled") {
107812
+ return { kind: "cancelled" };
107813
+ }
107814
+ if (result.answers.length === 1) {
107815
+ const a = result.answers[0];
107816
+ return {
107817
+ kind: "selected",
107818
+ userMessage: a.userMessage,
107819
+ label: a.selectedIds.map((id) => {
107820
+ const q = normalized.questions[0];
107821
+ return q.choices.find((c) => c.id === id)?.label ?? "";
107822
+ }).join(", ") || a.otherText || ""
107823
+ };
107824
+ }
107825
+ return {
107826
+ kind: "multi-submitted",
107827
+ answers: result.answers,
107828
+ userMessage: result.answers.map((a) => a.userMessage).filter(Boolean).join("\n\n")
107829
+ };
107830
+ }
107831
+ var CSI_TAB, CSI_SHIFT_TAB, CSI_BACKSPACE, CSI_BACKSPACE_ALT, CSI_SPACE;
107832
+ var init_askChoiceDialog = __esm({
107833
+ "packages/cli/tui/askChoiceDialog.ts"() {
107834
+ "use strict";
107835
+ init_askChoiceState();
107836
+ init_dialogFrame();
107837
+ init_selectDialog();
107838
+ init_terminalStyles();
107839
+ init_theme();
107840
+ CSI_TAB = " ";
107841
+ CSI_SHIFT_TAB = "\x1B[Z";
107842
+ CSI_BACKSPACE = "\x7F";
107843
+ CSI_BACKSPACE_ALT = "\b";
107844
+ CSI_SPACE = " ";
107845
+ }
107846
+ });
107847
+
107260
107848
  // packages/cli/tui/dialogHost.ts
107261
107849
  function resolveTtyRows(output2) {
107262
107850
  if (typeof output2 === "object" && output2 !== null && "rows" in output2 && typeof output2.rows === "number") {
@@ -107294,6 +107882,92 @@ var init_dialogHost = __esm({
107294
107882
  }
107295
107883
  });
107296
107884
 
107885
+ // packages/cli/tui/activityIndicator.ts
107886
+ function createActivityIndicator(deps) {
107887
+ const now = deps.now ?? (() => Date.now());
107888
+ const frameIntervalMs = deps.frameIntervalMs ?? ACTIVITY_FRAME_INTERVAL_MS;
107889
+ const fallbackDelayMs = deps.fallbackDelayMs ?? ACTIVITY_FALLBACK_DELAY_MS;
107890
+ const setIntervalFn = deps.setIntervalFn ?? ((cb, ms) => setInterval(cb, ms));
107891
+ const clearIntervalFn = deps.clearIntervalFn ?? ((handle) => clearInterval(handle));
107892
+ let explicitLabel = null;
107893
+ let explicitStartedAt = 0;
107894
+ let fallbackActive = false;
107895
+ let fallbackStartedAt = 0;
107896
+ let lastActivityAt = 0;
107897
+ let frameIndex = 0;
107898
+ let timer = null;
107899
+ const elapsedSecFrom = (startedAt) => startedAt > 0 ? Math.max(0, Math.floor((now() - startedAt) / 1e3)) : 0;
107900
+ const tick = () => {
107901
+ frameIndex += 1;
107902
+ if (explicitLabel === null && !fallbackActive && deps.isTurnActive() && lastActivityAt > 0 && now() - lastActivityAt >= fallbackDelayMs) {
107903
+ fallbackActive = true;
107904
+ fallbackStartedAt = now();
107905
+ }
107906
+ deps.onRepaint();
107907
+ };
107908
+ const ensureTimer = () => {
107909
+ if (timer === null) {
107910
+ timer = setIntervalFn(tick, frameIntervalMs);
107911
+ }
107912
+ };
107913
+ const report = (label) => {
107914
+ lastActivityAt = now();
107915
+ if (label !== null) {
107916
+ explicitLabel = label;
107917
+ if (explicitStartedAt === 0) explicitStartedAt = lastActivityAt;
107918
+ fallbackActive = false;
107919
+ fallbackStartedAt = 0;
107920
+ } else {
107921
+ explicitLabel = null;
107922
+ explicitStartedAt = 0;
107923
+ fallbackActive = false;
107924
+ fallbackStartedAt = 0;
107925
+ }
107926
+ ensureTimer();
107927
+ deps.onRepaint();
107928
+ };
107929
+ const getView = () => {
107930
+ if (!deps.isTurnActive()) return null;
107931
+ const frame = ACTIVITY_FRAMES[frameIndex % ACTIVITY_FRAMES.length];
107932
+ if (explicitLabel !== null) {
107933
+ return {
107934
+ frame,
107935
+ label: explicitLabel,
107936
+ elapsedSec: elapsedSecFrom(explicitStartedAt)
107937
+ };
107938
+ }
107939
+ if (fallbackActive) {
107940
+ return {
107941
+ frame,
107942
+ label: deps.fallbackLabel(),
107943
+ elapsedSec: elapsedSecFrom(fallbackStartedAt)
107944
+ };
107945
+ }
107946
+ return null;
107947
+ };
107948
+ const stop = () => {
107949
+ if (timer !== null) {
107950
+ clearIntervalFn(timer);
107951
+ timer = null;
107952
+ }
107953
+ explicitLabel = null;
107954
+ explicitStartedAt = 0;
107955
+ fallbackActive = false;
107956
+ fallbackStartedAt = 0;
107957
+ lastActivityAt = 0;
107958
+ };
107959
+ return { report, getView, stop };
107960
+ }
107961
+ var ACTIVITY_FRAMES, ACTIVITY_FRAME_INTERVAL_MS, ACTIVITY_FALLBACK_DELAY_MS;
107962
+ var init_activityIndicator = __esm({
107963
+ "packages/cli/tui/activityIndicator.ts"() {
107964
+ "use strict";
107965
+ ACTIVITY_FRAMES = ["\xB7", "~", "\u2248", "\u223F", "\u2248", "~"];
107966
+ ACTIVITY_FRAME_INTERVAL_MS = 150;
107967
+ ACTIVITY_FALLBACK_DELAY_MS = 1200;
107968
+ }
107969
+ });
107970
+
107297
107971
  // packages/ai/agent/utils/sortUtils.ts
107298
107972
  function sortAgentsFavoriteOwnedPublic(items) {
107299
107973
  const map = /* @__PURE__ */ new Map();
@@ -110678,44 +111352,21 @@ async function startTuiWorkspace(options) {
110678
111352
  let buffer = "";
110679
111353
  let cursorPos = 0;
110680
111354
  let activeTurnAbort = null;
110681
- let activityLabel = null;
110682
- let activityStartedAt = 0;
110683
- let activityFrameIndex = 0;
110684
- let activityTimer = null;
110685
- const stopActivity = () => {
110686
- if (activityTimer !== null) {
110687
- clearInterval(activityTimer);
110688
- activityTimer = null;
110689
- }
110690
- activityLabel = null;
110691
- activityStartedAt = 0;
110692
- };
110693
- const activityReporter = (label) => {
110694
- if (label !== null) {
110695
- activityLabel = label;
110696
- if (activityStartedAt === 0) {
110697
- activityStartedAt = Date.now();
110698
- }
110699
- if (activityTimer === null) {
110700
- activityTimer = setInterval(() => {
110701
- activityFrameIndex += 1;
110702
- if (fixedInput.active && !fixedInput.isPaused()) {
110703
- output2.write("\x1B[?2026h\x1B[?25l");
110704
- try {
110705
- fixedInput.repaint(buffer);
110706
- } finally {
110707
- output2.write("\x1B[?25h\x1B[?2026l");
110708
- }
110709
- }
110710
- }, 150);
110711
- }
110712
- } else {
110713
- stopActivity();
111355
+ const activityIndicator = createActivityIndicator({
111356
+ isTurnActive: () => activeTurnAbort !== null,
111357
+ fallbackLabel: () => `${state4.agentName} -> working`,
111358
+ onRepaint: () => {
110714
111359
  if (fixedInput.active && !fixedInput.isPaused()) {
110715
- fixedInput.repaint(buffer);
111360
+ output2.write("\x1B[?2026h\x1B[?25l");
111361
+ try {
111362
+ fixedInput.repaint(buffer);
111363
+ } finally {
111364
+ output2.write("\x1B[?25h\x1B[?2026l");
111365
+ }
110716
111366
  }
110717
111367
  }
110718
- };
111368
+ });
111369
+ const activityReporter = (label) => activityIndicator.report(label);
110719
111370
  let explicitAgentSwitch = false;
110720
111371
  let copyViewExitResolver = null;
110721
111372
  const history = createTurnHistory();
@@ -110814,25 +111465,15 @@ ${t("copyViewHint")}
110814
111465
  scheduleRender();
110815
111466
  }) : output2;
110816
111467
  const requestUserChoice = isInteractiveInput(input2) && dialogHost ? async (req) => {
110817
- const items = req.choices.map((c) => ({
110818
- label: c.label
110819
- }));
110820
111468
  try {
110821
- const pickResult = await dialogHost.run(
110822
- (anchor) => runSelectDialog({
110823
- items,
110824
- title: req.question,
111469
+ return await dialogHost.run(
111470
+ (anchor) => runAskChoiceDialog({
111471
+ request: req,
110825
111472
  input: input2,
110826
111473
  output: output2,
110827
111474
  ...anchor
110828
111475
  })
110829
111476
  );
110830
- if (pickResult.kind === "selected") {
110831
- const choice = req.choices[pickResult.index];
110832
- const userMessage = choice?.userMessage?.trim() || choice?.label || "";
110833
- return { kind: "selected", userMessage, label: choice?.label ?? "" };
110834
- }
110835
- return { kind: "cancelled" };
110836
111477
  } catch {
110837
111478
  return { kind: "cancelled" };
110838
111479
  }
@@ -110885,7 +111526,7 @@ ${t("copyViewHint")}
110885
111526
  }
110886
111527
  return { ok: !wasAborted, aborted: wasAborted };
110887
111528
  } finally {
110888
- stopActivity();
111529
+ activityIndicator.stop();
110889
111530
  activeTurnAbort = null;
110890
111531
  explicitAgentSwitch = false;
110891
111532
  }
@@ -111244,17 +111885,15 @@ ${err.message}` : ""}`
111244
111885
  return base;
111245
111886
  },
111246
111887
  getActivityLine: () => {
111247
- if (activityLabel === null) return null;
111888
+ const view = activityIndicator.getView();
111889
+ if (view === null) return null;
111248
111890
  const colorEnabled = resolveCliColorEnabled();
111249
- const FRAMES = ["\xB7", "~", "\u2248", "\u223F", "\u2248", "~"];
111250
- const frame = FRAMES[activityFrameIndex % FRAMES.length];
111251
- const elapsedSec = activityStartedAt > 0 ? Math.max(0, Math.floor((Date.now() - activityStartedAt) / 1e3)) : 0;
111252
- const elapsed = formatElapsedSeconds(elapsedSec);
111891
+ const elapsed = formatElapsedSeconds(view.elapsedSec);
111253
111892
  const stopHint = t("stopHint");
111254
111893
  if (!colorEnabled) {
111255
- return `${frame} ${activityLabel} (${elapsed}) \xB7 ${stopHint}`;
111894
+ return `${view.frame} ${view.label} (${elapsed}) \xB7 ${stopHint}`;
111256
111895
  }
111257
- return themeText(frame, "accent") + " " + themeText(activityLabel, "muted") + themeText(` (${elapsed})`, "chrome") + themeText(` \xB7 ${stopHint}`, "chrome");
111896
+ return themeText(view.frame, "accent") + " " + themeText(view.label, "muted") + themeText(` (${elapsed})`, "chrome") + themeText(` \xB7 ${stopHint}`, "chrome");
111258
111897
  },
111259
111898
  getQueueLines: () => {
111260
111899
  if (!chatQueueBinding || chatQueueBinding.queueLength() === 0) return [];
@@ -111343,12 +111982,27 @@ ${err.message}` : ""}`
111343
111982
  if (busyLock) {
111344
111983
  const submittedText2 = result.submit;
111345
111984
  const trimmedText = submittedText2.trim();
111346
- if (trimmedText === "/context" || trimmedText === "/ctx") {
111985
+ const busySlashCommand = trimmedText.split(/\s+/)[0]?.toLowerCase();
111986
+ const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/switch";
111987
+ if (isBusyLocalSlash) {
111988
+ const beforeAgentKey = state4.agentKey;
111347
111989
  const res = handleTuiInput(submittedText2, state4);
111348
- state4 = res.nextState;
111349
- if (res.output) {
111350
- output2.write(`${res.output}
111990
+ if (res.action) {
111991
+ output2.write(
111992
+ "Model picker isn't available while a reply is running. Use `/switch <name>` to switch now (takes effect on the next turn), or wait for the reply to finish.\n"
111993
+ );
111994
+ } else {
111995
+ state4 = res.nextState;
111996
+ let msg = res.output;
111997
+ if (busySlashCommand === "/switch" && res.nextState.agentKey !== beforeAgentKey) {
111998
+ const hint = "Note: the new model takes effect on the next turn. Switching models may consume more tokens because the conversation context is re-sent to the new model.";
111999
+ msg = msg ? `${msg}
112000
+ ${hint}` : hint;
112001
+ }
112002
+ if (msg) {
112003
+ output2.write(`${msg}
111351
112004
  `);
112005
+ }
111352
112006
  }
111353
112007
  buffer = "";
111354
112008
  cursorPos2 = 0;
@@ -111571,8 +112225,9 @@ var init_readlineWorkspace = __esm({
111571
112225
  init_updateCommands();
111572
112226
  init_processSpawn();
111573
112227
  init_confirmDialog();
111574
- init_selectDialog();
112228
+ init_askChoiceDialog();
111575
112229
  init_dialogHost();
112230
+ init_activityIndicator();
111576
112231
  init_agentPicker();
111577
112232
  init_agentCatalog();
111578
112233
  init_dialogPicker();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nolo-cli",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "description": "Agent-first terminal workspace for Nolo",
6
6
  "bin": {