pi-sage 0.2.9 → 0.2.11

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.
@@ -378,8 +378,10 @@ async function runSettingsWizard(ctx: ExtensionCommandContext): Promise<void> {
378
378
  ctx.ui.notify(`Saved Sage settings to ${target}`, "info");
379
379
  };
380
380
 
381
+ let rootSelectionIndex = 0;
382
+
381
383
  while (true) {
382
- const action = await selectScrollable(ctx, "Sage settings", [
384
+ const rootOptions = [
383
385
  `Enabled: ${onOff(draft.enabled)}`,
384
386
  `Autonomous mode: ${onOff(draft.autonomousEnabled)}`,
385
387
  `Explicit requests bypass soft limits: ${onOff(draft.explicitRequestAlwaysAllowed)}`,
@@ -401,13 +403,18 @@ async function runSettingsWizard(ctx: ExtensionCommandContext): Promise<void> {
401
403
  `Cost cap/session: ${draft.maxEstimatedCostPerSession ?? "(none)"}`,
402
404
  `Save scope: ${scope}`,
403
405
  "Test Sage call"
404
- ]);
406
+ ];
407
+
408
+ const action = await selectScrollable(ctx, "Sage settings", rootOptions, 10, rootSelectionIndex);
405
409
 
406
410
  if (!action) {
407
411
  ctx.ui.notify("Sage settings closed (changes are saved immediately)", "info");
408
412
  return;
409
413
  }
410
414
 
415
+ const selectedIndex = rootOptions.indexOf(action);
416
+ if (selectedIndex >= 0) rootSelectionIndex = selectedIndex;
417
+
411
418
  if (action === "Test Sage call") {
412
419
  await runSettingsTestCall(ctx, draft);
413
420
  continue;
@@ -721,7 +728,8 @@ async function selectScrollable(
721
728
  ctx: HasUIContext,
722
729
  title: string,
723
730
  options: string[],
724
- maxVisible = 10
731
+ maxVisible = 10,
732
+ initialSelectedIndex = 0
725
733
  ): Promise<string | undefined> {
726
734
  if (options.length === 0) return undefined;
727
735
 
@@ -744,6 +752,8 @@ async function selectScrollable(
744
752
  noMatch: (text) => theme.fg("warning", text)
745
753
  });
746
754
 
755
+ selectList.setSelectedIndex(initialSelectedIndex);
756
+
747
757
  selectList.onSelect = (item) => done(item.value);
748
758
  selectList.onCancel = () => done(undefined);
749
759
  container.addChild(selectList);
@@ -838,19 +848,89 @@ async function runSettingsTestCall(ctx: ExtensionContext, settings: SageSettings
838
848
  return;
839
849
  }
840
850
 
841
- try {
842
- const result = await runSageSingleShot({
843
- cwd: ctx.cwd,
844
- model: resolvedModel,
845
- reasoningLevel: settings.reasoningLevel,
846
- timeoutMs: Math.min(settings.timeoutMs, 45_000),
847
- question: "Reply with 'Sage test OK' and one short sentence.",
848
- toolPolicy: settings.toolPolicy
849
- });
851
+ type TestOutcome = { status: "ok" | "error" | "cancelled"; message: string };
852
+
853
+ const outcome = await ctx.ui.custom<TestOutcome | undefined>((tui, theme, _keybindings, done) => {
854
+ const controller = new AbortController();
855
+ const container = new Container();
856
+
857
+ container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
858
+ container.addChild(new Text(theme.fg("accent", "Sage test call"), 1, 0));
859
+ container.addChild(new Text(theme.fg("dim", `Model: ${resolvedModel}`), 1, 0));
860
+ container.addChild(new Text("", 0, 0));
861
+
862
+ const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
863
+ let spinnerIndex = 0;
864
+ const statusLine = new Text(theme.fg("muted", `${spinnerFrames[spinnerIndex]} Running test...`), 1, 0);
865
+ container.addChild(statusLine);
850
866
 
851
- ctx.ui.notify(`Sage test succeeded (${result.latencyMs}ms)`, "info");
852
- } catch (error) {
853
- const message = error instanceof Error ? error.message : "Unknown test failure";
854
- ctx.ui.notify(`Sage test failed: ${message}`, "error");
867
+ container.addChild(new Text("", 0, 0));
868
+ container.addChild(new Text(theme.fg("dim", "Esc to cancel"), 1, 0));
869
+ container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
870
+
871
+ let finished = false;
872
+ const finish = (result: TestOutcome): void => {
873
+ if (finished) return;
874
+ finished = true;
875
+ clearInterval(spinnerTimer);
876
+ done(result);
877
+ };
878
+
879
+ const spinnerTimer = setInterval(() => {
880
+ if (finished) return;
881
+ spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;
882
+ statusLine.setText(theme.fg("muted", `${spinnerFrames[spinnerIndex]} Running test...`));
883
+ tui.requestRender();
884
+ }, 120);
885
+
886
+ void (async () => {
887
+ try {
888
+ const result = await runSageSingleShot({
889
+ cwd: ctx.cwd,
890
+ model: resolvedModel,
891
+ reasoningLevel: settings.reasoningLevel,
892
+ timeoutMs: Math.min(settings.timeoutMs, 45_000),
893
+ question: "Reply with 'Sage test OK' and one short sentence.",
894
+ toolPolicy: settings.toolPolicy,
895
+ signal: controller.signal
896
+ });
897
+
898
+ finish({ status: "ok", message: `Sage test succeeded (${result.latencyMs}ms)` });
899
+ } catch (error) {
900
+ if (controller.signal.aborted) {
901
+ finish({ status: "cancelled", message: "Sage test cancelled" });
902
+ return;
903
+ }
904
+
905
+ const message = error instanceof Error ? error.message : "Unknown test failure";
906
+ finish({ status: "error", message: `Sage test failed: ${message}` });
907
+ }
908
+ })();
909
+
910
+ return {
911
+ render: (width: number) => container.render(width),
912
+ invalidate: () => container.invalidate(),
913
+ handleInput: (data: string) => {
914
+ if (data === "\u001b") {
915
+ controller.abort();
916
+ finish({ status: "cancelled", message: "Sage test cancelled" });
917
+ return;
918
+ }
919
+ }
920
+ };
921
+ });
922
+
923
+ if (!outcome) return;
924
+
925
+ if (outcome.status === "ok") {
926
+ ctx.ui.notify(outcome.message, "info");
927
+ return;
855
928
  }
929
+
930
+ if (outcome.status === "cancelled") {
931
+ ctx.ui.notify(outcome.message, "warning");
932
+ return;
933
+ }
934
+
935
+ ctx.ui.notify(outcome.message, "error");
856
936
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sage",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Interactive-only advisory Sage extension for Pi",