pi-system-one 1.3.0 → 1.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-system-one",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "System One decisions for Pi. Plug in TypeSafe Jev, Reflex, or your own provider.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/commands.ts CHANGED
@@ -41,18 +41,33 @@ export function registerSystemOneCommands(
41
41
  requestedMode?: string,
42
42
  ): Promise<void> {
43
43
  const current = store.session ?? createSessionConfig();
44
+ const modes: ConfigMode[] = [
45
+ current.mode,
46
+ ...(["typesafe", "custom", "native"] as ConfigMode[]).filter(
47
+ (mode) => mode !== current.mode,
48
+ ),
49
+ ];
50
+ const descriptions: Record<ConfigMode, string> = {
51
+ typesafe: "TypeSafe Jev (HTTP, full Score)",
52
+ custom: "Custom System One endpoint",
53
+ native: "Pi classifier (Choice and Noul only)",
54
+ };
55
+ const options = modes.map(
56
+ (mode) =>
57
+ `${mode} — ${descriptions[mode]}${mode === current.mode ? " (current)" : ""}`,
58
+ );
44
59
  const modeRaw =
45
60
  requestedMode ??
46
- (await ctx.ui.input(
47
- `Provider mode (typesafe / custom / native; current: ${current.mode}):`,
48
- current.mode,
49
- ));
61
+ (await ctx.ui.select("System One provider mode", options));
50
62
  if (modeRaw === undefined) {
51
63
  ctx.ui.notify("Cancelled.", "info");
52
64
  return;
53
65
  }
54
- const mode = (modeRaw.trim().toLowerCase() || current.mode) as ConfigMode;
55
- if (!["typesafe", "custom", "native"].includes(mode)) {
66
+ const mode =
67
+ requestedMode === undefined
68
+ ? modes[options.indexOf(modeRaw)]
69
+ : (modeRaw.trim().toLowerCase() as ConfigMode);
70
+ if (!mode || !modes.includes(mode)) {
56
71
  ctx.ui.notify("Choose typesafe, custom, or native.", "error");
57
72
  return;
58
73
  }
@@ -17,6 +17,7 @@ interface Captured {
17
17
  handler: ((args: string, ctx: never) => Promise<void>) | undefined;
18
18
  completions: ((prefix: string) => { value: string }[]) | undefined;
19
19
  notices: string[];
20
+ selections: { title: string; options: string[] }[];
20
21
  }
21
22
 
22
23
  function harness() {
@@ -25,6 +26,7 @@ function harness() {
25
26
  handler: undefined,
26
27
  completions: undefined,
27
28
  notices: [],
29
+ selections: [],
28
30
  };
29
31
  const pi = {
30
32
  registerTool: (t: { name: string }) => {
@@ -53,6 +55,11 @@ function ctxFor(
53
55
  return {
54
56
  modelRegistry: { getApiKeyForProvider: async () => undefined },
55
57
  ui: {
58
+ select: async (title: string, options: string[]) => {
59
+ cap.selections.push({ title, options });
60
+ const choice = answers.shift();
61
+ return options.find((option) => option.startsWith(choice ?? "\0"));
62
+ },
56
63
  input: async (_prompt: string, initial?: string) => {
57
64
  defaults?.push(initial ?? "");
58
65
  return answers.shift();
@@ -65,6 +72,44 @@ function ctxFor(
65
72
  }
66
73
 
67
74
  describe("so command", () => {
75
+ it("shows selectable provider modes for /so and /so config", async () => {
76
+ const { pi, cap } = harness();
77
+ const store: SessionStore = { session: createSessionConfig({}) };
78
+ registerSystemOneCommands(pi, store);
79
+ await cap.handler?.("", ctxFor(["native"], cap));
80
+ assert.equal(store.session?.mode, "native");
81
+ await cap.handler?.("config", ctxFor(["typesafe"], cap));
82
+ assert.equal(store.session?.mode, "typesafe");
83
+ assert.equal(cap.selections.length, 2);
84
+ assert.equal(cap.selections[0].options.length, 3);
85
+ assert.match(cap.selections[0].options[0], /typesafe.*current/i);
86
+ assert.match(cap.selections[1].options[0], /native.*current/i);
87
+ });
88
+
89
+ it("cancels selection without changing mode or saved settings", async () => {
90
+ const dir = mkdtempSync(join(tmpdir(), "so-select-"));
91
+ try {
92
+ const path = join(dir, "pi-system-one.json");
93
+ const { pi, cap } = harness();
94
+ const store: SessionStore = { session: createSessionConfig({}) };
95
+ registerSystemOneCommands(pi, store, path);
96
+ await cap.handler?.("config", ctxFor([undefined], cap));
97
+ assert.equal(store.session?.mode, "typesafe");
98
+ assert.throws(() => readFileSync(path, "utf8"), /ENOENT/);
99
+ assert.match(cap.notices.at(-1) ?? "", /Cancelled/);
100
+ } finally {
101
+ rmSync(dir, { recursive: true, force: true });
102
+ }
103
+ });
104
+
105
+ it("direct mode command bypasses selection", async () => {
106
+ const { pi, cap } = harness();
107
+ const store: SessionStore = { session: createSessionConfig({}) };
108
+ registerSystemOneCommands(pi, store);
109
+ await cap.handler?.("config native", ctxFor([], cap));
110
+ assert.equal(store.session?.mode, "native");
111
+ assert.equal(cap.selections.length, 0);
112
+ });
68
113
  it("clears prior model on custom endpoint change with blank model", async () => {
69
114
  const dir = mkdtempSync(join(tmpdir(), "so-model-change-"));
70
115
  const originalFetch = globalThis.fetch;