@gmickel/gno 1.25.1 → 1.26.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 (56) hide show
  1. package/README.md +10 -4
  2. package/assets/skill/SKILL.md +29 -17
  3. package/browser-extension/artifacts/{gno-browser-clipper-v1.25.1.zip → gno-browser-clipper-v1.26.0.zip} +0 -0
  4. package/browser-extension/artifacts/gno-browser-clipper-v1.26.0.zip.sha256 +1 -0
  5. package/browser-extension/dist/manifest.json +1 -1
  6. package/package.json +1 -1
  7. package/spec/cli.md +146 -4
  8. package/spec/db/schema.sql +1 -1
  9. package/spec/output-schemas/project-profile-apply.schema.json +209 -0
  10. package/spec/output-schemas/project-profile-command.schema.json +160 -0
  11. package/spec/output-schemas/query-diagnose.schema.json +7 -1
  12. package/spec/output-schemas/setup-profile-result.schema.json +87 -0
  13. package/spec/project-profile.schema.json +301 -0
  14. package/src/cli/commands/collection/add.ts +39 -45
  15. package/src/cli/commands/collection/remove.ts +28 -28
  16. package/src/cli/commands/collection/rename.ts +55 -73
  17. package/src/cli/commands/context/add.ts +37 -26
  18. package/src/cli/commands/context/rm.ts +47 -20
  19. package/src/cli/commands/init.ts +55 -125
  20. package/src/cli/commands/models/use.ts +43 -38
  21. package/src/cli/commands/profile-apply.ts +334 -0
  22. package/src/cli/commands/profile.ts +409 -0
  23. package/src/cli/commands/setup-activation.ts +205 -54
  24. package/src/cli/commands/setup-profile.ts +223 -0
  25. package/src/cli/commands/setup.ts +3 -0
  26. package/src/cli/program.ts +110 -9
  27. package/src/config/index.ts +3 -0
  28. package/src/config/project-profile.ts +367 -0
  29. package/src/config/saver.ts +16 -7
  30. package/src/config/types.ts +42 -0
  31. package/src/core/config-mutation.ts +138 -76
  32. package/src/core/config-write-lock.ts +89 -0
  33. package/src/core/context-identity.ts +16 -0
  34. package/src/core/context-resolver.ts +2 -12
  35. package/src/core/folder-setup-planning.ts +6 -21
  36. package/src/core/folder-setup.ts +30 -2
  37. package/src/core/path-rules.ts +53 -0
  38. package/src/core/project-affinity-surface.ts +102 -7
  39. package/src/core/project-profile-apply-state.ts +268 -0
  40. package/src/core/project-profile-apply-validation.ts +95 -0
  41. package/src/core/project-profile-apply.ts +408 -0
  42. package/src/core/project-profile-canonical.ts +71 -0
  43. package/src/core/project-profile-diff.ts +302 -0
  44. package/src/core/project-profile-discovery.ts +519 -0
  45. package/src/core/project-profile-file.ts +37 -0
  46. package/src/core/project-profile-parser.ts +98 -0
  47. package/src/core/project-profile.ts +490 -0
  48. package/src/ingestion/walker.ts +85 -44
  49. package/src/llm/cache.ts +21 -0
  50. package/src/serve/config-sync.ts +2 -2
  51. package/src/serve/resident-runtime.ts +1 -0
  52. package/src/serve/routes/api.ts +1 -0
  53. package/src/store/migrations/021-multi-context-identity.ts +37 -0
  54. package/src/store/migrations/index.ts +2 -0
  55. package/src/store/sqlite/adapter.ts +83 -0
  56. package/browser-extension/artifacts/gno-browser-clipper-v1.25.1.zip.sha256 +0 -1
@@ -3,43 +3,43 @@
3
3
  */
4
4
 
5
5
  import { removeCollection } from "../../../collection";
6
- import { loadConfig, saveConfig } from "../../../config";
6
+ import { applyConfigFileChange } from "../../../core/config-mutation";
7
7
  import { CliError } from "../../errors";
8
8
 
9
- export async function collectionRemove(name: string): Promise<void> {
10
- // Load config
11
- const configResult = await loadConfig();
12
- if (!configResult.ok) {
13
- throw new CliError(
14
- "RUNTIME",
15
- `Failed to load config: ${configResult.error.message}`
16
- );
17
- }
18
-
19
- // Remove collection using shared module
20
- const result = removeCollection(configResult.value, { name });
9
+ export async function collectionRemove(
10
+ name: string,
11
+ options: { configPath?: string } = {}
12
+ ): Promise<void> {
13
+ const mutation = await applyConfigFileChange(
14
+ { configPath: options.configPath },
15
+ (config) => {
16
+ const result = removeCollection(config, { name });
17
+ return result.ok
18
+ ? {
19
+ ok: true as const,
20
+ config: result.config,
21
+ value: result.collection,
22
+ }
23
+ : {
24
+ ok: false as const,
25
+ error: result.message,
26
+ code: result.code,
27
+ };
28
+ }
29
+ );
21
30
 
22
- if (!result.ok) {
31
+ if (!mutation.ok) {
23
32
  // Map collection error codes to CLI error codes
24
33
  const cliCode =
25
- result.code === "VALIDATION" ||
26
- result.code === "NOT_FOUND" ||
27
- result.code === "HAS_REFERENCES"
34
+ mutation.code === "VALIDATION" ||
35
+ mutation.code === "NOT_FOUND" ||
36
+ mutation.code === "HAS_REFERENCES"
28
37
  ? "VALIDATION"
29
38
  : "RUNTIME";
30
- throw new CliError(cliCode, result.message);
31
- }
32
-
33
- // Save config
34
- const saveResult = await saveConfig(result.config);
35
- if (!saveResult.ok) {
36
- throw new CliError(
37
- "RUNTIME",
38
- `Failed to save config: ${saveResult.error.message}`
39
- );
39
+ throw new CliError(cliCode, mutation.error);
40
40
  }
41
41
 
42
42
  process.stdout.write(
43
- `Collection "${result.collection.name}" removed successfully\n`
43
+ `Collection "${mutation.value?.name}" removed successfully\n`
44
44
  );
45
45
  }
@@ -2,90 +2,72 @@
2
2
  * gno collection rename - Rename a collection
3
3
  */
4
4
 
5
- import {
6
- CollectionSchema,
7
- getCollectionFromScope,
8
- loadConfig,
9
- saveConfig,
10
- } from "../../../config";
5
+ import { CollectionSchema, getCollectionFromScope } from "../../../config";
6
+ import { applyConfigFileChange } from "../../../core/config-mutation";
11
7
  import { CliError } from "../../errors";
12
8
 
13
9
  export async function collectionRename(
14
10
  oldName: string,
15
- newName: string
11
+ newName: string,
12
+ options: { configPath?: string } = {}
16
13
  ): Promise<void> {
17
14
  const oldCollectionName = oldName.toLowerCase();
18
15
  const newCollectionName = newName.toLowerCase();
19
16
 
20
- // Load config
21
- const result = await loadConfig();
22
- if (!result.ok) {
23
- throw new CliError(
24
- "RUNTIME",
25
- `Failed to load config: ${result.error.message}`
26
- );
27
- }
28
-
29
- const config = result.value;
30
-
31
- // Find old collection
32
- const collection = config.collections.find(
33
- (c) => c.name === oldCollectionName
34
- );
35
- if (!collection) {
36
- throw new CliError(
37
- "VALIDATION",
38
- `Collection "${oldCollectionName}" not found`
39
- );
40
- }
41
-
42
- // Check if new name already exists
43
- const existingNew = config.collections.find(
44
- (c) => c.name === newCollectionName
45
- );
46
- if (existingNew) {
47
- throw new CliError(
48
- "VALIDATION",
49
- `Collection "${newCollectionName}" already exists`
50
- );
51
- }
52
-
53
- // Validate new name
54
- const testCollection = { ...collection, name: newCollectionName };
55
- const validation = CollectionSchema.safeParse(testCollection);
56
- if (!validation.success) {
57
- throw new CliError(
58
- "VALIDATION",
59
- `Invalid collection name: ${validation.error.issues[0]?.message ?? "unknown error"}`
60
- );
61
- }
62
-
63
- // Rename collection
64
- collection.name = newCollectionName;
65
-
66
- // Update contexts that reference this collection
67
- for (const context of config.contexts) {
68
- const collFromScope = getCollectionFromScope(context.scopeKey);
69
- if (collFromScope === oldCollectionName) {
70
- // Update scope key
71
- if (context.scopeType === "collection") {
72
- context.scopeKey = `${newCollectionName}:`;
73
- } else if (context.scopeType === "prefix") {
74
- // Replace collection name in URI
75
- context.scopeKey = context.scopeKey.replace(
76
- `gno://${oldCollectionName}/`,
77
- `gno://${newCollectionName}/`
78
- );
17
+ const mutation = await applyConfigFileChange(
18
+ { configPath: options.configPath },
19
+ (config) => {
20
+ const collection = config.collections.find(
21
+ (item) => item.name === oldCollectionName
22
+ );
23
+ if (!collection) {
24
+ return {
25
+ ok: false as const,
26
+ error: `Collection "${oldCollectionName}" not found`,
27
+ code: "NOT_FOUND",
28
+ };
79
29
  }
30
+ if (config.collections.some((item) => item.name === newCollectionName)) {
31
+ return {
32
+ ok: false as const,
33
+ error: `Collection "${newCollectionName}" already exists`,
34
+ code: "DUPLICATE",
35
+ };
36
+ }
37
+ const validation = CollectionSchema.safeParse({
38
+ ...collection,
39
+ name: newCollectionName,
40
+ });
41
+ if (!validation.success) {
42
+ return {
43
+ ok: false as const,
44
+ error: `Invalid collection name: ${validation.error.issues[0]?.message ?? "unknown error"}`,
45
+ code: "VALIDATION",
46
+ };
47
+ }
48
+ collection.name = newCollectionName;
49
+ for (const context of config.contexts) {
50
+ if (getCollectionFromScope(context.scopeKey) !== oldCollectionName) {
51
+ continue;
52
+ }
53
+ if (context.scopeType === "collection") {
54
+ context.scopeKey = `${newCollectionName}:`;
55
+ } else if (context.scopeType === "prefix") {
56
+ context.scopeKey = context.scopeKey.replace(
57
+ `gno://${oldCollectionName}/`,
58
+ `gno://${newCollectionName}/`
59
+ );
60
+ }
61
+ }
62
+ return { ok: true as const, config };
80
63
  }
81
- }
82
-
83
- // Save config
84
- const saveResult = await saveConfig(config);
85
- if (!saveResult.ok) {
64
+ );
65
+ if (!mutation.ok) {
86
66
  throw new CliError(
87
- "RUNTIME",
88
- `Failed to save config: ${saveResult.error.message}`
67
+ ["NOT_FOUND", "DUPLICATE", "VALIDATION"].includes(mutation.code)
68
+ ? "VALIDATION"
69
+ : "RUNTIME",
70
+ mutation.error
89
71
  );
90
72
  }
91
73
 
@@ -6,7 +6,9 @@
6
6
  * @module src/cli/commands/context/add
7
7
  */
8
8
 
9
- import { loadConfig, parseScope, saveConfig } from "../../../config";
9
+ import { parseScope } from "../../../config";
10
+ import { applyConfigFileChange } from "../../../core/config-mutation";
11
+ import { normalizePersistedContextText } from "../../../core/context-identity";
10
12
 
11
13
  /**
12
14
  * Exit codes
@@ -21,7 +23,11 @@ const EXIT_VALIDATION = 1;
21
23
  * @param text - Context description text
22
24
  * @returns Exit code
23
25
  */
24
- export async function contextAdd(scope: string, text: string): Promise<number> {
26
+ export async function contextAdd(
27
+ scope: string,
28
+ text: string,
29
+ options: { configPath?: string } = {}
30
+ ): Promise<number> {
25
31
  // Parse scope
26
32
  const parsed = parseScope(scope);
27
33
  if (!parsed) {
@@ -32,33 +38,38 @@ export async function contextAdd(scope: string, text: string): Promise<number> {
32
38
  return EXIT_VALIDATION;
33
39
  }
34
40
 
35
- // Load config
36
- const configResult = await loadConfig();
37
- if (!configResult.ok) {
38
- console.error(`Error: ${configResult.error.message}`);
41
+ const normalizedText = normalizePersistedContextText(text);
42
+ if (!normalizedText) {
43
+ console.error("Error: Context text must not be empty");
39
44
  return EXIT_VALIDATION;
40
45
  }
41
46
 
42
- const config = configResult.value;
43
-
44
- // Check for duplicate scope
45
- const existing = config.contexts.find((ctx) => ctx.scopeKey === parsed.key);
46
- if (existing) {
47
- console.error(`Error: Context for scope "${scope}" already exists`);
48
- return EXIT_VALIDATION;
49
- }
50
-
51
- // Add context
52
- config.contexts.push({
53
- scopeType: parsed.type,
54
- scopeKey: parsed.key,
55
- text,
56
- });
57
-
58
- // Save config
59
- const saveResult = await saveConfig(config);
60
- if (!saveResult.ok) {
61
- console.error(`Error: ${saveResult.error.message}`);
47
+ const mutation = await applyConfigFileChange(
48
+ { configPath: options.configPath },
49
+ (config) => {
50
+ const duplicate = config.contexts.some(
51
+ (context) =>
52
+ context.scopeType === parsed.type &&
53
+ context.scopeKey === parsed.key &&
54
+ normalizePersistedContextText(context.text) === normalizedText
55
+ );
56
+ if (duplicate) {
57
+ return {
58
+ ok: false as const,
59
+ error: `Context for scope "${scope}" with that text already exists`,
60
+ code: "DUPLICATE",
61
+ };
62
+ }
63
+ config.contexts.push({
64
+ scopeType: parsed.type,
65
+ scopeKey: parsed.key,
66
+ text: normalizedText,
67
+ });
68
+ return { ok: true as const, config };
69
+ }
70
+ );
71
+ if (!mutation.ok) {
72
+ console.error(`Error: ${mutation.error}`);
62
73
  return EXIT_VALIDATION;
63
74
  }
64
75
 
@@ -6,7 +6,9 @@
6
6
  * @module src/cli/commands/context/rm
7
7
  */
8
8
 
9
- import { loadConfig, saveConfig } from "../../../config";
9
+ import { parseScope } from "../../../config";
10
+ import { applyConfigFileChange } from "../../../core/config-mutation";
11
+ import { normalizePersistedContextText } from "../../../core/context-identity";
10
12
 
11
13
  /**
12
14
  * Exit codes
@@ -20,30 +22,55 @@ const EXIT_VALIDATION = 1;
20
22
  * @param scope - Scope key to remove
21
23
  * @returns Exit code
22
24
  */
23
- export async function contextRm(scope: string): Promise<number> {
24
- // Load config
25
- const configResult = await loadConfig();
26
- if (!configResult.ok) {
27
- console.error(`Error: ${configResult.error.message}`);
25
+ export async function contextRm(
26
+ scope: string,
27
+ text?: string,
28
+ options: { configPath?: string } = {}
29
+ ): Promise<number> {
30
+ const parsed = parseScope(scope);
31
+ if (!parsed) {
32
+ console.error(`Error: Invalid scope format: ${scope}`);
28
33
  return EXIT_VALIDATION;
29
34
  }
30
-
31
- const config = configResult.value;
32
-
33
- // Find context
34
- const index = config.contexts.findIndex((ctx) => ctx.scopeKey === scope);
35
- if (index === -1) {
36
- console.error(`Error: Context for scope "${scope}" not found`);
35
+ const normalizedText =
36
+ text === undefined ? undefined : normalizePersistedContextText(text);
37
+ if (normalizedText === "") {
38
+ console.error("Error: Context text must not be empty");
37
39
  return EXIT_VALIDATION;
38
40
  }
39
41
 
40
- // Remove context
41
- config.contexts.splice(index, 1);
42
-
43
- // Save config
44
- const saveResult = await saveConfig(config);
45
- if (!saveResult.ok) {
46
- console.error(`Error: ${saveResult.error.message}`);
42
+ const mutation = await applyConfigFileChange(
43
+ { configPath: options.configPath },
44
+ (config) => {
45
+ const matches = config.contexts
46
+ .map((context, index) => ({ context, index }))
47
+ .filter(
48
+ ({ context }) =>
49
+ context.scopeType === parsed.type &&
50
+ context.scopeKey === parsed.key &&
51
+ (normalizedText === undefined ||
52
+ normalizePersistedContextText(context.text) === normalizedText)
53
+ );
54
+ if (matches.length === 0) {
55
+ return {
56
+ ok: false as const,
57
+ error: `Context for scope "${scope}" not found`,
58
+ code: "NOT_FOUND",
59
+ };
60
+ }
61
+ if (normalizedText === undefined && matches.length > 1) {
62
+ return {
63
+ ok: false as const,
64
+ error: `Multiple contexts exist for scope "${scope}"; pass the exact text to remove`,
65
+ code: "AMBIGUOUS",
66
+ };
67
+ }
68
+ config.contexts.splice(matches[0]!.index, 1);
69
+ return { ok: true as const, config };
70
+ }
71
+ );
72
+ if (!mutation.ok) {
73
+ console.error(`Error: ${mutation.error}`);
47
74
  return EXIT_VALIDATION;
48
75
  }
49
76
 
@@ -17,13 +17,12 @@ import {
17
17
  FTS_TOKENIZERS,
18
18
  type FtsTokenizer,
19
19
  getConfigPaths,
20
- isInitialized,
21
20
  isValidLanguageHint,
22
- loadConfigOrNull,
23
21
  pathExists,
24
- saveConfig,
25
22
  toAbsolutePath,
26
23
  } from "../../config";
24
+ import { applyConfigFileChange } from "../../core/config-mutation";
25
+ import { SqliteAdapter } from "../../store/sqlite/adapter";
27
26
 
28
27
  /** Pattern to replace invalid chars in collection names with hyphens */
29
28
  const INVALID_NAME_CHARS = /[^a-z0-9_-]/g;
@@ -70,166 +69,97 @@ export interface InitResult {
70
69
  error?: string;
71
70
  }
72
71
 
73
- /**
74
- * Handle case when already initialized.
75
- */
76
- async function handleAlreadyInitialized(
77
- options: InitOptions,
78
- paths: ReturnType<typeof getConfigPaths>
79
- ): Promise<InitResult> {
80
- // Ensure directories exist (may have been deleted by reset)
81
- await ensureDirectories();
82
-
83
- const config = await loadConfigOrNull(options.configPath);
84
- const dbPath = getIndexDbPath();
85
-
86
- if (!options.path) {
87
- return {
88
- success: true,
89
- alreadyInitialized: true,
90
- configPath: paths.configFile,
91
- dataDir: paths.dataDir,
92
- dbPath,
93
- };
94
- }
95
-
96
- if (!config) {
97
- return {
98
- success: false,
99
- configPath: paths.configFile,
100
- dataDir: paths.dataDir,
101
- dbPath,
102
- error: "Config exists but could not be loaded",
103
- };
104
- }
105
-
106
- const collectionResult = await addCollectionToConfig(config, options);
107
- if (!collectionResult.success) {
108
- return {
109
- success: false,
110
- configPath: paths.configFile,
111
- dataDir: paths.dataDir,
112
- dbPath,
113
- error: collectionResult.error,
114
- };
115
- }
116
-
117
- const saveResult = await saveConfig(config, options.configPath);
118
- if (!saveResult.ok) {
119
- return {
120
- success: false,
121
- configPath: paths.configFile,
122
- dataDir: paths.dataDir,
123
- dbPath,
124
- error: saveResult.error.message,
125
- };
126
- }
127
-
128
- return {
129
- success: true,
130
- alreadyInitialized: true,
131
- configPath: paths.configFile,
132
- dataDir: paths.dataDir,
133
- dbPath,
134
- collectionAdded: collectionResult.collectionName,
135
- };
136
- }
137
-
138
72
  /**
139
73
  * Execute gno init command.
140
74
  */
141
75
  export async function init(options: InitOptions = {}): Promise<InitResult> {
142
76
  const paths = getConfigPaths();
77
+ const configPath = toAbsolutePath(options.configPath ?? paths.configFile);
78
+ const dbPath = getIndexDbPath();
143
79
 
144
- // Check if already initialized
145
- const initialized = await isInitialized(options.configPath);
146
- if (initialized) {
147
- return handleAlreadyInitialized(options, paths);
148
- }
149
-
150
- // Create directories
151
80
  const dirResult = await ensureDirectories();
152
81
  if (!dirResult.ok) {
153
82
  return {
154
83
  success: false,
155
- configPath: paths.configFile,
84
+ configPath,
156
85
  dataDir: paths.dataDir,
157
- dbPath: getIndexDbPath(),
86
+ dbPath,
158
87
  error: dirResult.error.message,
159
88
  };
160
89
  }
161
90
 
162
- // Validate tokenizer option if provided
163
91
  if (options.tokenizer && !FTS_TOKENIZERS.includes(options.tokenizer)) {
164
92
  return {
165
93
  success: false,
166
- configPath: paths.configFile,
94
+ configPath,
167
95
  dataDir: paths.dataDir,
168
- dbPath: getIndexDbPath(),
96
+ dbPath,
169
97
  error: `Invalid tokenizer: ${options.tokenizer}. Valid: ${FTS_TOKENIZERS.join(", ")}`,
170
98
  };
171
99
  }
172
100
 
173
- // Create default config
174
- const config = createDefaultConfig();
175
-
176
- // Set tokenizer if provided
177
- if (options.tokenizer) {
178
- config.ftsTokenizer = options.tokenizer;
179
- }
180
-
181
- // Add collection if path provided
182
- let collectionName: string | undefined;
183
- if (options.path) {
184
- const collectionResult = await addCollectionToConfig(config, options);
185
- if (!collectionResult.success) {
101
+ const mutation = await applyConfigFileChange(
102
+ {
103
+ configPath,
104
+ createConfigIfMissing: createDefaultConfig,
105
+ },
106
+ async (config, state) => {
107
+ if (state.created && options.tokenizer) {
108
+ config.ftsTokenizer = options.tokenizer;
109
+ }
110
+ let collectionName: string | undefined;
111
+ if (options.path) {
112
+ const collectionResult = await addCollectionToConfig(config, options);
113
+ if (!collectionResult.success) {
114
+ return {
115
+ ok: false as const,
116
+ error: collectionResult.error,
117
+ code: "VALIDATION",
118
+ };
119
+ }
120
+ collectionName = collectionResult.collectionName;
121
+ }
186
122
  return {
187
- success: false,
188
- configPath: paths.configFile,
189
- dataDir: paths.dataDir,
190
- dbPath: getIndexDbPath(),
191
- error: collectionResult.error,
123
+ ok: true as const,
124
+ config,
125
+ skipSave: !state.created && !options.path,
126
+ value: {
127
+ alreadyInitialized: !state.created,
128
+ collectionName,
129
+ },
192
130
  };
193
131
  }
194
- collectionName = collectionResult.collectionName;
195
- }
196
-
197
- // Save config
198
- const saveResult = await saveConfig(config, options.configPath);
199
- if (!saveResult.ok) {
132
+ );
133
+ if (!mutation.ok) {
200
134
  return {
201
135
  success: false,
202
- configPath: paths.configFile,
136
+ configPath,
203
137
  dataDir: paths.dataDir,
204
- dbPath: getIndexDbPath(),
205
- error: saveResult.error.message,
138
+ dbPath,
139
+ error: mutation.error,
206
140
  };
207
141
  }
208
142
 
209
- // Create DB placeholder file only if it doesn't exist (don't truncate existing DB)
210
- const dbPath = getIndexDbPath();
211
- const dbFile = Bun.file(dbPath);
212
- const dbExists = await dbFile.exists();
213
- if (!dbExists) {
214
- try {
215
- await Bun.write(dbPath, "");
216
- } catch (error) {
217
- return {
218
- success: false,
219
- configPath: paths.configFile,
220
- dataDir: paths.dataDir,
221
- dbPath,
222
- error: `Failed to create database file: ${error instanceof Error ? error.message : String(error)}`,
223
- };
224
- }
143
+ const store = new SqliteAdapter();
144
+ const opened = await store.open(dbPath, mutation.config.ftsTokenizer);
145
+ if (!opened.ok) {
146
+ return {
147
+ success: false,
148
+ configPath,
149
+ dataDir: paths.dataDir,
150
+ dbPath,
151
+ error: `Failed to initialize database: ${opened.error.message}`,
152
+ };
225
153
  }
154
+ await store.close();
226
155
 
227
156
  return {
228
157
  success: true,
229
- configPath: paths.configFile,
158
+ alreadyInitialized: mutation.value?.alreadyInitialized || undefined,
159
+ configPath,
230
160
  dataDir: paths.dataDir,
231
161
  dbPath,
232
- collectionAdded: collectionName,
162
+ collectionAdded: mutation.value?.collectionName,
233
163
  };
234
164
  }
235
165