@mem9/opencode 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -2,82 +2,40 @@
2
2
 
3
3
  Persistent memory for [OpenCode](https://opencode.ai).
4
4
 
5
- The package ships two plugin entrypoints:
5
+ This package has two entrypoints:
6
6
 
7
7
  - a server plugin for recall, auto-ingest, and memory tools
8
8
  - a TUI plugin for interactive setup inside OpenCode
9
9
 
10
10
  ## Quick Start
11
11
 
12
- ### 1. Install the server plugin
12
+ ### 1. Install mem9 once at user scope
13
13
 
14
- Install the server plugin in one scope only.
15
-
16
- User scope:
17
-
18
- File: `~/.config/opencode/opencode.json`
19
-
20
- ```json
21
- {
22
- "plugin": ["@mem9/opencode"]
23
- }
24
- ```
25
-
26
- Project scope:
27
-
28
- File: `<project>/.opencode/opencode.json`
29
-
30
- ```json
31
- {
32
- "plugin": ["@mem9/opencode"]
33
- }
34
- ```
35
-
36
- Recommended pattern:
37
-
38
- - install the server plugin once at user scope
39
- - keep project-specific behavior in `<project>/.opencode/mem9.json`
40
-
41
- `@mem9/opencode` resolves npm `latest`.
42
- If you publish a prerelease channel, install it with an explicit npm specifier instead:
43
-
44
- ```json
45
- {
46
- "plugin": ["@mem9/opencode@rc"]
47
- }
48
- ```
49
-
50
- You can also pin an exact prerelease version:
51
-
52
- ```json
53
- {
54
- "plugin": ["@mem9/opencode@0.1.0-rc.0"]
55
- }
14
+ ```bash
15
+ opencode plugin --global @mem9/opencode
56
16
  ```
57
17
 
58
- Avoid duplicate plugin loading, such as:
18
+ That adds `@mem9/opencode` to these files:
59
19
 
60
- - user scope plugin plus project scope plugin
61
- - npm plugin plus local file plugin
20
+ macOS/Linux:
62
21
 
63
- ### 2. Install the TUI plugin for `/mem9-setup`
22
+ - `~/.config/opencode/opencode.json`
23
+ - `~/.config/opencode/tui.json`
64
24
 
65
- File: `~/.config/opencode/tui.json`
25
+ Windows:
66
26
 
67
- ```json
68
- {
69
- "plugin": ["@mem9/opencode"]
70
- }
71
- ```
27
+ - `%APPDATA%\\opencode\\opencode.json`
28
+ - `%APPDATA%\\opencode\\tui.json`
72
29
 
73
- That enables the `/mem9-setup` command inside OpenCode.
30
+ mem9 works best with one global plugin install plus project `mem9.json` overrides.
31
+ OpenCode merges plugin lists across scopes, so one install keeps recall, ingest, and tools predictable.
74
32
 
75
- ### 3. Restart OpenCode and run `/mem9-setup`
33
+ ### 2. Restart OpenCode and run `/mem9-setup`
76
34
 
77
- `/mem9-setup` is the single entrypoint for both:
35
+ `/mem9-setup` is the main entrypoint for:
78
36
 
79
37
  - shared mem9 credentials
80
- - OpenCode user/project mem9 settings
38
+ - OpenCode mem9 settings
81
39
 
82
40
  When no usable profile exists, it shows two actions:
83
41
 
@@ -89,32 +47,45 @@ When usable profiles already exist, it shows four actions:
89
47
  - `Get a mem9 API key automatically`
90
48
  - `Add an existing mem9 API key`
91
49
  - `Use an existing mem9 profile in a scope`
92
- - `Configure user/project settings`
50
+ - `Adjust scope settings`
93
51
 
94
- Profile creation ends after the profile is saved. Scope configuration is a separate action.
52
+ The last two actions do different things:
95
53
 
96
- ## File Layout
54
+ - `Use an existing mem9 profile in a scope` changes which profile a user or project scope uses
55
+ - `Adjust scope settings` changes `debug`, `defaultTimeoutMs`, and `searchTimeoutMs` for a user or project scope
97
56
 
98
- OpenCode config directory:
57
+ ### 3. Add project overrides only when you need them
99
58
 
100
- - macOS/Linux: usually `~/.config/opencode`
101
- - Windows: usually `%APPDATA%\\opencode`
59
+ Keep the plugin install global.
60
+ Use `<project>/.opencode/mem9.json` when one repository needs a different profile, debug flag, or timeout.
102
61
 
103
- OpenCode state directory:
62
+ Example:
104
63
 
105
- - macOS/Linux: usually `~/.local/share/opencode`
106
- - Windows: usually `%LOCALAPPDATA%\\opencode`
64
+ ```json
65
+ {
66
+ "schemaVersion": 1,
67
+ "profileId": "default",
68
+ "debug": false,
69
+ "defaultTimeoutMs": 8000,
70
+ "searchTimeoutMs": 15000
71
+ }
72
+ ```
107
73
 
108
- mem9 uses these files:
74
+ ## Where mem9 stores data
109
75
 
110
- - server plugin list: `~/.config/opencode/opencode.json` or `<project>/.opencode/opencode.json`
111
- - TUI plugin list: `~/.config/opencode/tui.json`
112
- - shared credentials: `$MEM9_HOME/.credentials.json`
113
- - user mem9 config: `<OpenCode config dir>/mem9.json`
114
- - project mem9 config: `<project>/.opencode/mem9.json`
115
- - debug logs: `<OpenCode state dir>/plugins/mem9/log/YYYY-MM-DD.jsonl`
76
+ - Shared credentials:
77
+ macOS/Linux: `$HOME/.mem9/.credentials.json`
78
+ Windows: `%USERPROFILE%\\.mem9\\.credentials.json`
79
+ - Global mem9 config:
80
+ macOS/Linux: `~/.config/opencode/mem9.json`
81
+ Windows: `%APPDATA%\\opencode\\mem9.json`
82
+ - Project mem9 override:
83
+ all platforms: `<project>/.opencode/mem9.json`
84
+ - Debug logs:
85
+ macOS/Linux: `~/.local/share/opencode/plugins/mem9/log/YYYY-MM-DD.jsonl`
86
+ Windows: `%LOCALAPPDATA%\\opencode\\plugins\\mem9\\log\\YYYY-MM-DD.jsonl`
116
87
 
117
- `MEM9_HOME` defaults to `$HOME/.mem9`.
88
+ `MEM9_HOME` defaults to `$HOME/.mem9` on macOS/Linux and `%USERPROFILE%\\.mem9` on Windows.
118
89
 
119
90
  ## Credentials File
120
91
 
@@ -173,33 +144,33 @@ These environment variables still override disk config at runtime:
173
144
  - `MEM9_DEBUG`
174
145
  - `MEM9_HOME`
175
146
 
176
- Legacy compatibility remains:
147
+ Legacy compatibility:
177
148
 
178
149
  - `MEM9_TENANT_ID`
179
150
 
180
151
  `MEM9_TENANT_ID` is treated as the API key source for older setups.
181
152
 
182
- ## Local Development Install
153
+ ## Upgrading
183
154
 
184
- You can also point OpenCode at a local checkout.
155
+ OpenCode caches npm plugins by package specifier.
156
+ When config points at `@mem9/opencode`, OpenCode resolves it as `@mem9/opencode@latest`.
185
157
 
186
- Server plugin example:
158
+ Upgrade flow:
187
159
 
188
- ```json
189
- {
190
- "plugin": ["./.opencode/plugins/mem9/src/index.ts"]
191
- }
192
- ```
160
+ 1. Quit OpenCode.
161
+ 2. Delete the cached folder that matches the installed specifier.
162
+ macOS/Linux default: `~/.cache/opencode/packages/@mem9/opencode@latest`
163
+ Windows default: `%LOCALAPPDATA%\\opencode\\packages\\@mem9\\opencode@latest`
164
+ If you pinned an exact version such as `@mem9/opencode@0.1.1`, delete that exact folder name instead.
165
+ 3. Run:
193
166
 
194
- TUI plugin example:
195
-
196
- ```json
197
- {
198
- "plugin": ["./.opencode/plugins/mem9/src/tui/index.ts"]
199
- }
167
+ ```bash
168
+ opencode plugin --force --global @mem9/opencode
200
169
  ```
201
170
 
202
- Keep the same one-scope rule for the server plugin even when using local paths.
171
+ 4. Restart OpenCode.
172
+
173
+ For prerelease testing, install an explicit npm specifier such as `@mem9/opencode@rc` or an exact version.
203
174
 
204
175
  ## What the Plugin Does
205
176
 
@@ -211,7 +182,7 @@ The server plugin does three things:
211
182
 
212
183
  ### Hook Flow
213
184
 
214
- OpenCode integration currently uses four runtime hooks:
185
+ OpenCode integration uses four runtime hooks:
215
186
 
216
187
  | Hook | What mem9 does |
217
188
  | --- | --- |
@@ -253,16 +224,8 @@ The plugin registers these tools:
253
224
  ## Troubleshooting
254
225
 
255
226
  - `Setup pending` means the plugin could not find a usable runtime identity. Run `/mem9-setup`, add `MEM9_API_KEY`, or point the active `mem9.json` scope at a profile with a non-empty `apiKey`.
256
- - If `/mem9-setup` is missing, confirm `@mem9/opencode` is listed in `~/.config/opencode/tui.json`.
227
+ - If `/mem9-setup` is missing, confirm `@mem9/opencode` is listed in your global `tui.json`.
257
228
  - If recall or tools work in one project and not another, check whether the project has its own `.opencode/mem9.json` override.
258
229
  - If recall, auto-ingest, or debug logs appear to run twice, check for duplicate plugin registration across user scope, project scope, npm, or local file paths.
259
230
  - If the selected profile exists but has no `apiKey`, update that profile in `$MEM9_HOME/.credentials.json`.
260
231
  - If debug logging is enabled and no file appears, confirm OpenCode can write to its state directory.
261
-
262
- ## Local Verification
263
-
264
- ```bash
265
- pnpm test
266
- pnpm run typecheck
267
- pnpm run pack:check
268
- ```
package/package.json CHANGED
@@ -1,15 +1,12 @@
1
1
  {
2
2
  "name": "@mem9/opencode",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "OpenCode memory plugin — cloud-persistent AI agent memory with hybrid vector + keyword search via mem9",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
- "oc-plugin": [
8
- "server",
9
- "tui"
10
- ],
11
7
  "exports": {
12
8
  ".": "./src/index.ts",
9
+ "./server": "./src/index.ts",
13
10
  "./tui": "./src/tui/index.ts"
14
11
  },
15
12
  "license": "Apache-2.0",
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export { default } from "./server/index.js";
1
+ export { default } from "./server/index.ts";
@@ -5,7 +5,7 @@ import type {
5
5
  SearchResult,
6
6
  StoreResult,
7
7
  UpdateMemoryInput,
8
- } from "../shared/types.js";
8
+ } from "../shared/types.ts";
9
9
 
10
10
  export interface IngestMessage {
11
11
  role: string;
@@ -1,18 +1,18 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import type { PluginInput } from "@opencode-ai/plugin";
3
- import { parseCredentialsFile } from "../shared/credentials-store.js";
4
- import { DEFAULT_SCOPE_CONFIG } from "../shared/defaults.js";
3
+ import { parseCredentialsFile } from "../shared/credentials-store.ts";
4
+ import { DEFAULT_SCOPE_CONFIG } from "../shared/defaults.ts";
5
5
  import {
6
6
  resolveOpenCodeBasePaths,
7
7
  resolveMem9Home,
8
8
  resolveMem9Paths,
9
9
  type Mem9ResolvedPaths,
10
- } from "../shared/platform-paths.js";
10
+ } from "../shared/platform-paths.ts";
11
11
  import {
12
12
  DEFAULT_API_URL,
13
13
  type Mem9ConfigFile,
14
14
  type Mem9CredentialsFile,
15
- } from "../shared/types.js";
15
+ } from "../shared/types.ts";
16
16
 
17
17
  const EMPTY_CREDENTIALS: Mem9CredentialsFile = {
18
18
  schemaVersion: 1,
@@ -1,11 +1,11 @@
1
1
  import type { Hooks } from "@opencode-ai/plugin";
2
- import type { IngestMessage, MemoryBackend } from "./backend.js";
3
- import type { DebugLogger } from "./debug.js";
4
- import { selectMessagesForIngest } from "./ingest/select.js";
5
- import { submitMessagesForIngest } from "./ingest/submit.js";
6
- import { formatRecallBlock } from "./recall/format.js";
7
- import { buildRecallQuery } from "./recall/query.js";
8
- import type { SessionTranscriptLoader } from "./session-transcript.js";
2
+ import type { IngestMessage, MemoryBackend } from "./backend.ts";
3
+ import type { DebugLogger } from "./debug.ts";
4
+ import { selectMessagesForIngest } from "./ingest/select.ts";
5
+ import { submitMessagesForIngest } from "./ingest/submit.ts";
6
+ import { formatRecallBlock } from "./recall/format.ts";
7
+ import { buildRecallQuery } from "./recall/query.ts";
8
+ import type { SessionTranscriptLoader } from "./session-transcript.ts";
9
9
 
10
10
  const MAX_RECALL_RESULTS = 10;
11
11
  const MIN_RECALL_QUERY_LEN = 5;
@@ -1,16 +1,16 @@
1
1
  import type { Hooks, Plugin } from "@opencode-ai/plugin";
2
- import type { MemoryBackend } from "./backend.js";
2
+ import type { MemoryBackend } from "./backend.ts";
3
3
  import {
4
4
  resolveEffectiveConfig,
5
5
  resolvePluginIdentity,
6
- } from "./config.js";
7
- import { createDebugLogger } from "./debug.js";
8
- import { ServerBackend } from "./server-backend.js";
9
- import { buildPendingSetupHooks } from "./setup-flow.js";
10
- import { buildTools } from "./tools.js";
11
- import { buildHooks } from "./hooks.js";
12
- import { createSessionTranscriptLoader } from "./session-transcript.js";
13
- import { PLUGIN_ID } from "../shared/plugin-meta.js";
6
+ } from "./config.ts";
7
+ import { createDebugLogger } from "./debug.ts";
8
+ import { ServerBackend } from "./server-backend.ts";
9
+ import { buildPendingSetupHooks } from "./setup-flow.ts";
10
+ import { buildTools } from "./tools.ts";
11
+ import { buildHooks } from "./hooks.ts";
12
+ import { createSessionTranscriptLoader } from "./session-transcript.ts";
13
+ import { PLUGIN_ID } from "../shared/plugin-meta.ts";
14
14
 
15
15
  function buildPluginHooksAndTools(
16
16
  backend: MemoryBackend,
@@ -1,4 +1,4 @@
1
- import type { IngestMessage } from "../backend.js";
1
+ import type { IngestMessage } from "../backend.ts";
2
2
 
3
3
  const RELEVANT_MEMORIES_BLOCK_RE = /<relevant-memories>[\s\S]*?(<\/relevant-memories>|$)/g;
4
4
  const MAX_INGEST_MESSAGES = 12;
@@ -1,6 +1,6 @@
1
- import type { IngestInput, IngestResult, MemoryBackend } from "../backend.js";
2
- import type { DebugLogger } from "../debug.js";
3
- import { selectMessagesForIngest } from "./select.js";
1
+ import type { IngestInput, IngestResult, MemoryBackend } from "../backend.ts";
2
+ import type { DebugLogger } from "../debug.ts";
3
+ import { selectMessagesForIngest } from "./select.ts";
4
4
 
5
5
  export interface SubmitIngestOptions {
6
6
  backend: MemoryBackend;
@@ -1,4 +1,4 @@
1
- import type { Memory } from "../../shared/types.js";
1
+ import type { Memory } from "../../shared/types.ts";
2
2
 
3
3
  const MAX_CONTENT_LEN = 500;
4
4
  const MAX_TAGS = 3;
@@ -2,7 +2,7 @@ import type {
2
2
  IngestInput,
3
3
  IngestResult,
4
4
  MemoryBackend,
5
- } from "./backend.js";
5
+ } from "./backend.ts";
6
6
  import type {
7
7
  Memory,
8
8
  StoreResult,
@@ -10,8 +10,8 @@ import type {
10
10
  CreateMemoryInput,
11
11
  UpdateMemoryInput,
12
12
  SearchInput,
13
- } from "../shared/types.js";
14
- import { DEFAULT_SCOPE_CONFIG } from "../shared/defaults.js";
13
+ } from "../shared/types.ts";
14
+ import { DEFAULT_SCOPE_CONFIG } from "../shared/defaults.ts";
15
15
 
16
16
  function isRecord(value: unknown): value is Record<string, unknown> {
17
17
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1,5 +1,5 @@
1
1
  import type { PluginInput } from "@opencode-ai/plugin";
2
- import type { IngestMessage } from "./backend.js";
2
+ import type { IngestMessage } from "./backend.ts";
3
3
 
4
4
  const SESSION_TRANSCRIPT_FETCH_LIMIT = 24;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import type { Hooks, PluginInput } from "@opencode-ai/plugin";
2
- import type { EffectiveConfig } from "./config.js";
2
+ import type { EffectiveConfig } from "./config.ts";
3
3
 
4
4
  export function buildPendingSetupHooks(
5
5
  _input: PluginInput,
@@ -1,10 +1,10 @@
1
1
  import { tool } from "@opencode-ai/plugin";
2
- import type { MemoryBackend } from "./backend.js";
2
+ import type { MemoryBackend } from "./backend.ts";
3
3
  import type {
4
4
  CreateMemoryInput,
5
5
  UpdateMemoryInput,
6
6
  SearchInput,
7
- } from "../shared/types.js";
7
+ } from "../shared/types.ts";
8
8
 
9
9
  /**
10
10
  * Build the 5 memory tools for OpenCode.
@@ -1,4 +1,4 @@
1
- import type { Mem9CredentialsFile, Mem9Profile } from "./types.js";
1
+ import type { Mem9CredentialsFile, Mem9Profile } from "./types.ts";
2
2
 
3
3
  function isRecord(value: unknown): value is Record<string, unknown> {
4
4
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1,4 +1,4 @@
1
- import type { Mem9ConfigFile } from "./types.js";
1
+ import type { Mem9ConfigFile } from "./types.ts";
2
2
 
3
3
  export const DEFAULT_SCOPE_CONFIG: Required<
4
4
  Pick<
@@ -1,14 +1,14 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import type { Mem9ResolvedPaths } from "./platform-paths.js";
4
- import { parseCredentialsFile, stringifyCredentialsFile } from "./credentials-store.js";
5
- import { DEFAULT_SCOPE_CONFIG } from "./defaults.js";
3
+ import type { Mem9ResolvedPaths } from "./platform-paths.ts";
4
+ import { parseCredentialsFile, stringifyCredentialsFile } from "./credentials-store.ts";
5
+ import { DEFAULT_SCOPE_CONFIG } from "./defaults.ts";
6
6
  import {
7
7
  DEFAULT_API_URL,
8
8
  type Mem9ConfigFile,
9
9
  type Mem9CredentialsFile,
10
10
  type Mem9Profile,
11
- } from "./types.js";
11
+ } from "./types.ts";
12
12
 
13
13
  export type SetupScope = "user" | "project";
14
14
 
@@ -17,6 +17,7 @@ export interface SetupProfileSummary {
17
17
  label: string;
18
18
  baseUrl: string;
19
19
  hasApiKey: boolean;
20
+ apiKeyPreview: string;
20
21
  }
21
22
 
22
23
  export interface ScopeConfigState {
@@ -112,6 +113,23 @@ function hasApiKey(profile: unknown): boolean {
112
113
  return Boolean(normalizeOptionalString(profile.apiKey));
113
114
  }
114
115
 
116
+ function summarizeApiKeyPreview(apiKey: string): string {
117
+ const normalized = apiKey.trim();
118
+ if (normalized.length === 0) {
119
+ return "";
120
+ }
121
+
122
+ if (normalized.length <= 4) {
123
+ return `${normalized.slice(0, 1)}...`;
124
+ }
125
+
126
+ if (normalized.length <= 8) {
127
+ return `${normalized.slice(0, 2)}...${normalized.slice(-2)}`;
128
+ }
129
+
130
+ return `${normalized.slice(0, 4)}...${normalized.slice(-4)}`;
131
+ }
132
+
115
133
  function normalizeProfileRecord(
116
134
  profileId: string,
117
135
  profile: Mem9Profile | undefined,
@@ -188,6 +206,7 @@ function buildProfiles(
188
206
  label: normalized.label,
189
207
  baseUrl: normalized.baseUrl,
190
208
  hasApiKey: normalized.apiKey.trim().length > 0,
209
+ apiKeyPreview: summarizeApiKeyPreview(normalized.apiKey),
191
210
  };
192
211
  })
193
212
  .sort(sortProfileSummaries);
package/src/tui/index.ts CHANGED
@@ -8,8 +8,8 @@ import {
8
8
  resolveMem9Home,
9
9
  resolveMem9Paths,
10
10
  type Mem9ResolvedPaths,
11
- } from "../shared/platform-paths.js";
12
- import { PLUGIN_ID } from "../shared/plugin-meta.js";
11
+ } from "../shared/platform-paths.ts";
12
+ import { PLUGIN_ID } from "../shared/plugin-meta.ts";
13
13
  import {
14
14
  loadSetupState,
15
15
  provisionApiKey,
@@ -19,7 +19,7 @@ import {
19
19
  type SetupProfileSummary,
20
20
  type SetupScope,
21
21
  type SetupState,
22
- } from "../shared/setup-files.js";
22
+ } from "../shared/setup-files.ts";
23
23
 
24
24
  type SetupAction =
25
25
  | "auto-api-key"
@@ -27,7 +27,7 @@ type SetupAction =
27
27
  | "use-profile-in-scope"
28
28
  | "configure-scope";
29
29
 
30
- type ScopeFlowMode = "profile-only" | "full-config";
30
+ type ScopeFlowMode = "profile-only" | "settings-only";
31
31
 
32
32
  interface ProfileDraft {
33
33
  profileId: string;
@@ -50,6 +50,17 @@ export interface SetupActionOption {
50
50
  description: string;
51
51
  }
52
52
 
53
+ export interface SetupProfileOption {
54
+ title: string;
55
+ value: string;
56
+ description: string;
57
+ disabled: boolean;
58
+ }
59
+
60
+ interface SetupProfileOptionState {
61
+ currentProfileId?: string;
62
+ }
63
+
53
64
  function getProjectDir(api: TuiPluginApi): string {
54
65
  const worktree = api.state.path.worktree.trim();
55
66
  if (worktree.length > 0) {
@@ -155,12 +166,12 @@ export function buildSetupActionOptions(
155
166
  {
156
167
  title: "Use an existing mem9 profile in a scope",
157
168
  value: "use-profile-in-scope",
158
- description: "Choose a saved profile and apply it to user or project settings.",
169
+ description: "Choose which saved profile user or project settings should use.",
159
170
  },
160
171
  {
161
- title: "Configure user/project settings",
172
+ title: "Adjust scope settings",
162
173
  value: "configure-scope",
163
- description: "Change scope profile, debug logging, and request timeouts.",
174
+ description: "Change debug logging, request timeouts, and other mem9 settings for a user or project scope.",
164
175
  },
165
176
  );
166
177
  }
@@ -168,6 +179,30 @@ export function buildSetupActionOptions(
168
179
  return options;
169
180
  }
170
181
 
182
+ function formatProfileTitle(profile: SetupProfileSummary): string {
183
+ return profile.label === profile.profileId
184
+ ? profile.label
185
+ : `${profile.label} (${profile.profileId})`;
186
+ }
187
+
188
+ export function buildScopeProfileOptions(
189
+ state: Pick<SetupState, "profiles">,
190
+ optionState: SetupProfileOptionState = {},
191
+ ): SetupProfileOption[] {
192
+ return state.profiles.map((profile) => ({
193
+ title: formatProfileTitle(profile),
194
+ value: profile.profileId,
195
+ description: [
196
+ profile.apiKeyPreview || "API key missing",
197
+ profile.baseUrl,
198
+ profile.profileId === optionState.currentProfileId ? "Current in this scope" : undefined,
199
+ ]
200
+ .filter((value): value is string => Boolean(value))
201
+ .join(" | "),
202
+ disabled: !profile.hasApiKey,
203
+ }));
204
+ }
205
+
171
206
  function parsePositiveInteger(value: string, field: string): number | null {
172
207
  const trimmed = value.trim();
173
208
  if (trimmed.length === 0) {
@@ -279,7 +314,7 @@ function showActionDialog(
279
314
  return;
280
315
  }
281
316
 
282
- showScopeDialog(api, paths, state, "full-config");
317
+ showScopeDialog(api, paths, state, "settings-only");
283
318
  },
284
319
  }),
285
320
  );
@@ -459,7 +494,12 @@ function showScopeDialog(
459
494
  ],
460
495
  onSelect: (option) => {
461
496
  const draft = createScopeDraft(state, option.value);
462
- showScopeProfileDialog(api, paths, state, mode, draft);
497
+ if (mode === "profile-only") {
498
+ showScopeProfileDialog(api, paths, state, draft);
499
+ return;
500
+ }
501
+
502
+ showScopeDebugDialog(api, paths, state, draft);
463
503
  },
464
504
  }),
465
505
  );
@@ -469,27 +509,18 @@ function showScopeProfileDialog(
469
509
  api: TuiPluginApi,
470
510
  paths: Mem9ResolvedPaths,
471
511
  state: SetupState,
472
- mode: ScopeFlowMode,
473
512
  draft: ScopeDraft,
474
513
  ): void {
475
514
  api.ui.dialog.replace(() =>
476
515
  api.ui.DialogSelect<string>({
477
516
  title: "Which mem9 profile should this scope use?",
478
517
  current: draft.profileId,
479
- options: state.profiles.map((profile) => ({
480
- title: `${profile.label} (${profile.profileId})`,
481
- value: profile.profileId,
482
- description: `${profile.baseUrl} • ${profile.hasApiKey ? "API key configured" : "API key missing"}`,
483
- disabled: !profile.hasApiKey,
484
- })),
518
+ options: buildScopeProfileOptions(state, {
519
+ currentProfileId: draft.profileId,
520
+ }),
485
521
  onSelect: (option) => {
486
522
  draft.profileId = option.value;
487
- if (mode === "profile-only") {
488
- void submitScopeConfigDraft(api, paths, draft);
489
- return;
490
- }
491
-
492
- showScopeDebugDialog(api, paths, state, draft);
523
+ void submitScopeConfigDraft(api, paths, draft);
493
524
  },
494
525
  }),
495
526
  );
@@ -550,7 +581,7 @@ function showDefaultTimeoutDialog(
550
581
  showSearchTimeoutDialog(api, paths, state, draft);
551
582
  },
552
583
  onCancel: () => {
553
- showScopeProfileDialog(api, paths, state, "full-config", draft);
584
+ showScopeDebugDialog(api, paths, state, draft);
554
585
  },
555
586
  }),
556
587
  );