@adithya-naik/cmd-tracker 1.0.0 → 1.0.3

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.
@@ -1,19 +1,20 @@
1
1
  /*
2
2
  * categorizer.js
3
- *
3
+ *
4
4
  * This file has ONE job — look at a command and decide its category
5
5
  * Example:
6
6
  * "git status" → "git"
7
7
  * "npm install" → "npm"
8
8
  * "docker ps" → "docker"
9
9
  * "ls -la" → "linux"
10
+ * "go get <pkgURL>" → "go"
10
11
  * "ng new my-app" → "others"
11
12
  */
12
13
 
13
14
  /*
14
15
  * We define all our categories and their keywords here
15
16
  * This is called a CONFIGURATION OBJECT
16
- *
17
+ *
17
18
  * Why keep it here separately?
18
19
  * → Easy to add new categories later
19
20
  * → Easy to add new keywords to existing categories
@@ -25,75 +26,169 @@ const CATEGORIES = {
25
26
  * Any command starting with "git" goes here
26
27
  * Examples: git status, git push, git commit -m "msg"
27
28
  */
28
- git: ["git"],
29
+ git: ['git'],
29
30
 
30
31
  /*
31
32
  * Any command starting with "npm" or "npx" goes here
32
33
  * Examples: npm install, npm run dev, npx create-react-app
33
34
  */
34
- npm: ["npm", "npx"],
35
+ npm: ['npm', 'npx'],
35
36
 
36
37
  /*
37
38
  * Any command starting with "docker" goes here
38
39
  * Examples: docker ps, docker build, docker-compose up
39
40
  */
40
- docker: ["docker", "docker-compose"],
41
+ docker: ['docker', 'docker-compose'],
41
42
 
42
43
  /*
43
44
  * Common linux commands go here
44
45
  * Examples: ls -la, cd projects, mkdir new-folder
45
46
  */
46
47
  linux: [
47
- "ls",
48
- "cd",
49
- "pwd",
50
- "mkdir",
51
- "rm",
52
- "cp",
53
- "mv",
54
- "cat",
55
- "touch",
56
- "chmod",
57
- "chown",
58
- "grep",
59
- "find",
60
- "echo",
61
- "sudo",
62
- "apt",
63
- "curl",
64
- "wget",
65
- "nano",
66
- "vim"
48
+ 'ls', 'cd', 'pwd', 'mkdir', 'rmdir',
49
+ 'rm', 'cp', 'mv', 'cat', 'touch',
50
+ 'chmod', 'chown', 'grep', 'find',
51
+ 'echo', 'sudo', 'apt', 'apt-get',
52
+ 'curl', 'wget', 'nano', 'vim',
53
+ 'less', 'head', 'tail', 'ps',
54
+ 'top', 'kill', 'df', 'du',
55
+ 'tar', 'zip', 'unzip', 'ssh',
56
+ 'scp', 'rsync'
67
57
  ],
68
58
 
69
59
  /*
70
60
  * Node.js related commands
71
61
  * Examples: node index.js, nodemon server.js
72
62
  */
73
- node: ["node", "nodemon"],
63
+ node: ['node', 'nodemon'],
74
64
 
75
65
  /*
76
66
  * Angular CLI commands
77
67
  * Examples: ng new my-app, ng serve, ng generate component
78
68
  */
79
- angular: ["ng"],
69
+ angular: ['ng'],
80
70
 
81
71
  /*
82
72
  * Python commands
83
73
  * Examples: python app.py, pip install flask
84
74
  */
85
- python: ["python", "python3", "pip", "pip3"],
75
+ python: ['python', 'python3', 'pip', 'pip3'],
76
+
77
+ go: ['go'],
78
+
79
+ /*
80
+ * Java ecosystem commands
81
+ * Examples: java Main, javac Main.java,
82
+ * mvn package, gradle build
83
+ */
84
+ java: ['java', 'javac', 'mvn', 'gradle'],
85
+
86
+ /*
87
+ * Rust development commands
88
+ * Examples: cargo build, cargo run,
89
+ * cargo test, rustc main.rs
90
+ */
91
+ rust: ['cargo', 'rustc'],
92
+
93
+ /*
94
+ * .NET commands
95
+ * Examples: dotnet run, dotnet build,
96
+ * dotnet new console
97
+ */
98
+ dotnet: ['dotnet'],
99
+
100
+ /*
101
+ * Kubernetes tooling commands
102
+ * Examples: kubectl get pods,
103
+ * kubectl apply -f app.yaml,
104
+ * helm install my-app chart/
105
+ */
106
+ kubernetes: ['kubectl', 'helm'],
107
+
108
+ /*
109
+ * Database CLI commands
110
+ * Examples: mysql -u root -p,
111
+ * psql mydb,
112
+ * mongosh,
113
+ * redis-cli
114
+ */
115
+ database: [
116
+ 'mysql',
117
+ 'psql',
118
+ 'sqlite3',
119
+ 'mongosh',
120
+ 'redis-cli'
121
+ ],
122
+
123
+ /*
124
+ * Cloud provider CLI commands
125
+ * Examples: aws s3 ls,
126
+ * gcloud compute instances list,
127
+ * az login
128
+ */
129
+ cloud: [
130
+ 'aws',
131
+ 'gcloud',
132
+ 'az'
133
+ ],
134
+
135
+ /*
136
+ * Package manager commands
137
+ * Examples: yarn install,
138
+ * pnpm dev,
139
+ * brew install git,
140
+ * snap install code
141
+ */
142
+ packagemanagers: [
143
+ 'yarn',
144
+ 'pnpm',
145
+ 'brew',
146
+ 'snap'
147
+ ],
148
+
149
+ /*
150
+ * Testing framework commands
151
+ * Examples: jest,
152
+ * vitest run,
153
+ * playwright test,
154
+ * cypress open
155
+ */
156
+ testing: [
157
+ 'jest',
158
+ 'vitest',
159
+ 'playwright',
160
+ 'cypress'
161
+ ],
162
+
163
+ /*
164
+ * AI coding assistant CLI commands
165
+ * Examples: claude, gemini, codex,
166
+ * opencode, aider, q chat
167
+ */
168
+ ai: [
169
+ 'claude',
170
+ 'gemini',
171
+ 'codex',
172
+ 'c',
173
+ 'opencode',
174
+ 'aider',
175
+ 'q',
176
+ 'warp',
177
+ 'ollama',
178
+ 'huggingface-cli'
179
+ ],
180
+
86
181
  };
87
182
 
88
183
  /*
89
184
  * categorize() — the main function of this file
90
- *
185
+ *
91
186
  * It takes a command string as input
92
187
  * It returns the category name as a string
93
- *
188
+ *
94
189
  * @param {string} command - the terminal command typed by user
95
190
  * @returns {string} - category name (git/npm/docker/linux/node/angular/python/others)
96
- *
191
+ *
97
192
  * Example:
98
193
  * categorize("git status") → "git"
99
194
  * categorize("npm install") → "npm"
@@ -106,22 +201,22 @@ function categorize(command) {
106
201
  * Safety check — if command is empty or not a string, return "others"
107
202
  * This prevents crashes if something unexpected is passed in
108
203
  */
109
- if (!command || typeof command !== "string") {
110
- return "others";
204
+ if (!command || typeof command !== 'string') {
205
+ return 'others';
111
206
  }
112
207
 
113
208
  /*
114
209
  * .trim() → removes spaces from start and end
115
210
  * .toLowerCase() → converts to lowercase so "Git Status" = "git status"
116
211
  * .split(" ")[0] → takes only the FIRST word
117
- *
212
+ *
118
213
  * Example:
119
214
  * " git status " → trim → "git status"
120
215
  * → toLowerCase → "git status"
121
216
  * → split(" ") → ["git", "status"]
122
217
  * → [0] → "git"
123
218
  */
124
- const firstWord = command.trim().toLowerCase().split(" ")[0];
219
+ const firstWord = command.trim().toLowerCase().split(' ')[0];
125
220
 
126
221
  /*
127
222
  * Object.entries() converts our CATEGORIES object into an array like:
@@ -130,14 +225,14 @@ function categorize(command) {
130
225
  * ["npm", ["npm", "npx"]],
131
226
  * ...
132
227
  * ]
133
- *
228
+ *
134
229
  * We loop through each [categoryName, keywords] pair
135
230
  */
136
231
  for (const [categoryName, keywords] of Object.entries(CATEGORIES)) {
137
232
 
138
233
  /*
139
234
  * .includes() checks if our firstWord exists in the keywords array
140
- *
235
+ *
141
236
  * Example:
142
237
  * categoryName = "npm"
143
238
  * keywords = ["npm", "npx"]
@@ -153,13 +248,13 @@ function categorize(command) {
153
248
  * If no category matched — return "others"
154
249
  * This is our catch-all category
155
250
  */
156
- return "others";
251
+ return 'others';
157
252
  }
158
253
 
159
254
  /*
160
255
  * module.exports → makes this function available to other files
161
256
  * Without this line — no other file can use categorize()
162
- *
257
+ *
163
258
  * This is how Node.js shares code between files
164
259
  */
165
- module.exports = { categorize };
260
+ module.exports = { categorize };
package/src/utils/hook.js CHANGED
@@ -8,18 +8,18 @@
8
8
  * Supported shells:
9
9
  * → bash → hooks into ~/.bashrc
10
10
  * → zsh → hooks into ~/.zshrc
11
- * → Both are most common shells on Mac/Linux
11
+ * → fish hooks into ~/.config/fish/config.fish
12
12
  *
13
13
  * How it works:
14
- * 1. Detect user's shell (bash or zsh)
14
+ * 1. Detect user's shell (bash, zsh, or fish)
15
15
  * 2. Add a hook script to their shell config file
16
16
  * 3. Hook script calls "tracker save <command>" after every command
17
17
  * 4. User sources their config → automatic capture starts!
18
18
  */
19
19
 
20
- const fs = require("fs");
21
- const path = require("path");
22
- const os = require("os");
20
+ const fs = require('node:fs');
21
+ const path = require('node:path');
22
+ const os = require('node:os');
23
23
 
24
24
  /*
25
25
  * os module — built into Node.js
@@ -28,14 +28,15 @@ const os = require("os");
28
28
  * Mac/Linux → /home/adithya
29
29
  */
30
30
  const HOME_DIR = os.homedir();
31
-
32
31
  /*
33
32
  * Shell config file paths
34
33
  * These are standard locations for shell config files
35
34
  */
36
35
  const SHELL_CONFIGS = {
37
- bash: path.join(HOME_DIR, ".bashrc"),
38
- zsh: path.join(HOME_DIR, ".zshrc")
36
+ bash: path.join(HOME_DIR, '.bashrc'),
37
+ zsh: path.join(HOME_DIR, '.zshrc'),
38
+ fish: path.join(HOME_DIR, '.config/fish/config.fish'),
39
+
39
40
  };
40
41
 
41
42
  /*
@@ -67,28 +68,43 @@ const SHELL_CONFIGS = {
67
68
  * This is cleaner and works on all bash/zsh versions
68
69
  */
69
70
  const HOOK_SCRIPT = [
70
- "",
71
- "# cmd-tracker shell hook — auto saves every command you type",
72
- "_cmd_tracker_hook() {",
73
- " local LAST_CMD",
74
- " LAST_CMD=$(history 1 | sed 's/^[[:space:]]*[0-9]*[[:space:]]*//')",
71
+ '',
72
+ '# cmd-tracker shell hook — auto saves every command you type',
73
+ '_cmd_tracker_hook() {',
74
+ ' local LAST_CMD',
75
+ ' LAST_CMD=$(history 1 | sed \'s/^[[:space:]]*[0-9]*[[:space:]]*//\')',
75
76
  ' if [ -n "$LAST_CMD" ]; then',
76
77
  ' tracker save "$LAST_CMD" > /dev/null 2>&1',
77
- " fi",
78
- "}",
79
- "",
80
- "# For zsh",
78
+ ' fi',
79
+ '}',
80
+ '',
81
+ '# For zsh',
81
82
  'if [ -n "$ZSH_VERSION" ]; then',
82
- " autoload -U add-zsh-hook",
83
- " add-zsh-hook precmd _cmd_tracker_hook",
84
- "fi",
85
- "",
86
- "# For bash",
83
+ ' autoload -U add-zsh-hook',
84
+ ' add-zsh-hook precmd _cmd_tracker_hook',
85
+ 'fi',
86
+ '',
87
+ '# For bash',
87
88
  'if [ -n "$BASH_VERSION" ]; then',
88
89
  ' PROMPT_COMMAND="_cmd_tracker_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"',
89
- "fi",
90
- ""
91
- ].join("\n");
90
+ 'fi',
91
+ ''
92
+ ].join('\n');
93
+
94
+
95
+ const FISH_HOOK_SCRIPT = [
96
+ '',
97
+ '# cmd-tracker shell hook — auto saves every command you type',
98
+ 'function __cmd_tracker_postexec --on-event fish_postexec',
99
+ ' set -l last_cmd (history --max=1)',
100
+ '',
101
+ ' if test -n "$last_cmd"',
102
+ ' tracker save "$last_cmd" >/dev/null 2>&1',
103
+ ' end',
104
+ 'end',
105
+ '',
106
+ ].join('\n');
107
+
92
108
 
93
109
  /*
94
110
  * HOOK_MARKER — unique string we add to identify our hook
@@ -96,32 +112,45 @@ const HOOK_SCRIPT = [
96
112
  * → Check if hook already exists (avoid duplicates)
97
113
  * → Find and remove hook when user runs tracker unhook
98
114
  */
99
- const HOOK_MARKER = "# cmd-tracker shell hook — auto saves every command you type";
115
+ const HOOK_MARKER = '# cmd-tracker shell hook — auto saves every command you type';
100
116
 
101
117
  /*
102
118
  * detectShell() — detects which shell user is running
103
119
  *
104
- * process.env.SHELL → environment variable containing shell path
105
- * Example:
106
- * "/bin/bash" bash
107
- * "/bin/zsh" → zsh
120
+ * Priority:
121
+ * 1. process.env.FISH_VERSION — fish sets this automatically
122
+ * even when launched from another shell (parent $SHELL
123
+ * would still show bash/zsh)
124
+ * 2. process.env.SHELL — the standard shell path variable
125
+ * Examples: "/bin/bash" → bash, "/bin/zsh" → zsh
108
126
  *
109
- * @returns {string} — "bash", "zsh", or "unknown"
127
+ * @returns {string} — "bash", "zsh", "fish", or "unknown"
110
128
  */
111
129
  function detectShell() {
112
- const shell = process.env.SHELL || "";
130
+ const shell = process.env.SHELL || '';
131
+
132
+ if (process.env.FISH_VERSION) return 'fish';
113
133
 
114
- if (shell.includes("zsh")) return "zsh";
115
- if (shell.includes("bash")) return "bash";
134
+ if (shell.includes('fish')) return 'fish';
116
135
 
117
- /*
118
- * On Windows — check SHELL or ComSpec
119
- * Most Windows users use bash via Git Bash
120
- */
121
- const comspec = process.env.ComSpec || "";
122
- if (comspec.includes("cmd.exe")) return "unknown";
136
+ try {
137
+ const ppid = process.ppid;
138
+ const { execFileSync } = require('node:child_process');
139
+ const comm = execFileSync('/bin/ps', ['-o', 'comm=', '-p', String(ppid)], { encoding: 'utf-8' }).trim();
140
+ if (comm.includes('fish')) return 'fish';
141
+ if (comm.includes('zsh')) return 'zsh';
142
+ if (comm.includes('bash')) return 'bash';
143
+ } catch {
144
+ // ignore ps command errors
145
+ }
123
146
 
124
- return "bash"; // default to bash
147
+ if (shell.includes('zsh')) return 'zsh';
148
+ if (shell.includes('bash')) return 'bash';
149
+
150
+ const comspec = process.env.ComSpec || '';
151
+ if (comspec.includes('cmd.exe')) return 'unknown';
152
+
153
+ return 'unknown';
125
154
  }
126
155
 
127
156
  /*
@@ -148,10 +177,10 @@ function installHook() {
148
177
  * If shell is unknown — we can't install hook
149
178
  * This happens on Windows CMD/PowerShell
150
179
  */
151
- if (shell === "unknown") {
180
+ if (shell === 'unknown') {
152
181
  return {
153
182
  success: false,
154
- message: "❌ Automatic hook not supported on Windows CMD/PowerShell\n💡 Use Git Bash or WSL for automatic capture"
183
+ message: '❌ Automatic hook not supported on Windows CMD/PowerShell\n💡 Use Git Bash or WSL for automatic capture'
155
184
  };
156
185
  }
157
186
 
@@ -162,7 +191,7 @@ function installHook() {
162
191
  * Look for our unique marker in the config file
163
192
  */
164
193
  if (fs.existsSync(configFile)) {
165
- const content = fs.readFileSync(configFile, "utf-8");
194
+ const content = fs.readFileSync(configFile, 'utf-8');
166
195
 
167
196
  if (content.includes(HOOK_MARKER)) {
168
197
  return {
@@ -176,7 +205,17 @@ function installHook() {
176
205
  * Append hook script to shell config file
177
206
  * appendFileSync → adds to end without deleting existing content
178
207
  */
179
- fs.appendFileSync(configFile, HOOK_SCRIPT);
208
+ if (shell === 'fish') {
209
+ fs.mkdirSync(path.dirname(configFile), { recursive: true });
210
+ }
211
+
212
+ /*
213
+ * Choose correct hook
214
+ */
215
+ const hookScript =
216
+ shell === 'fish' ? FISH_HOOK_SCRIPT : HOOK_SCRIPT;
217
+
218
+ fs.appendFileSync(configFile, hookScript);
180
219
 
181
220
  return {
182
221
  success: true,
@@ -187,10 +226,10 @@ function installHook() {
187
226
 
188
227
  } catch (error) {
189
228
 
190
- if (error.code === "EACCES") {
229
+ if (error.code === 'EACCES') {
191
230
  return {
192
231
  success: false,
193
- message: "❌ Permission denied! Try with sudo/admin permissions"
232
+ message: '❌ Permission denied! Try with sudo/admin permissions'
194
233
  };
195
234
  }
196
235
 
@@ -215,11 +254,11 @@ function removeHook() {
215
254
  if (!fs.existsSync(configFile)) {
216
255
  return {
217
256
  success: false,
218
- message: "❌ Shell config file not found"
257
+ message: '❌ Shell config file not found'
219
258
  };
220
259
  }
221
260
 
222
- const content = fs.readFileSync(configFile, "utf-8");
261
+ const content = fs.readFileSync(configFile, 'utf-8');
223
262
 
224
263
  /*
225
264
  * Check if hook exists
@@ -227,7 +266,7 @@ function removeHook() {
227
266
  if (!content.includes(HOOK_MARKER)) {
228
267
  return {
229
268
  success: false,
230
- message: "⚠️ Hook not found in shell config"
269
+ message: '⚠️ Hook not found in shell config'
231
270
  };
232
271
  }
233
272
 
@@ -236,7 +275,7 @@ function removeHook() {
236
275
  * Split by marker → take everything before it
237
276
  * Then trim trailing whitespace
238
277
  */
239
- const newContent = content.split(HOOK_MARKER)[0].trimEnd() + "\n";
278
+ const newContent = content.split(HOOK_MARKER)[0].trimEnd() + '\n';
240
279
  fs.writeFileSync(configFile, newContent);
241
280
 
242
281
  return {
@@ -265,7 +304,7 @@ function isHookInstalled() {
265
304
 
266
305
  if (!fs.existsSync(configFile)) return false;
267
306
 
268
- const content = fs.readFileSync(configFile, "utf-8");
307
+ const content = fs.readFileSync(configFile, 'utf-8');
269
308
  return content.includes(HOOK_MARKER);
270
309
 
271
310
  } catch (error) {
@@ -278,4 +317,4 @@ module.exports = {
278
317
  removeHook,
279
318
  isHookInstalled,
280
319
  detectShell
281
- };
320
+ };
@@ -11,14 +11,14 @@
11
11
  * Mac/Linux: /home/user/project/.tracker/commands.json
12
12
  * path module handles this difference automatically
13
13
  */
14
- const fs = require("fs");
15
- const path = require("path");
14
+ const fs = require('fs');
15
+ const path = require('path');
16
16
 
17
17
  /*
18
18
  * Import our categorize function from categorizer.js
19
19
  * We need it here to know WHICH category to save the command in
20
20
  */
21
- const { categorize } = require("./categorizer");
21
+ const { categorize } = require('./categorizer');
22
22
 
23
23
  /*
24
24
  * process.cwd() → Current Working Directory
@@ -31,13 +31,13 @@ const { categorize } = require("./categorizer");
31
31
  * This is VERY important — we want to save commands in THEIR repo
32
32
  * not in our package folder
33
33
  */
34
- const TRACKER_DIR = path.join(process.cwd(), ".tracker");
34
+ const TRACKER_DIR = path.join(process.cwd(), '.tracker');
35
35
 
36
36
  /*
37
37
  * This is where all commands will be saved
38
38
  * .tracker/commands.json inside the user's repo
39
39
  */
40
- const COMMANDS_FILE = path.join(TRACKER_DIR, "commands.json");
40
+ const COMMANDS_FILE = path.join(TRACKER_DIR, 'commands.json');
41
41
 
42
42
  /*
43
43
  * This is our default empty structure
@@ -56,6 +56,16 @@ function getDefaultStructure() {
56
56
  node: [],
57
57
  angular: [],
58
58
  python: [],
59
+ go: [],
60
+ java: [],
61
+ rust: [],
62
+ dotnet: [],
63
+ kubernetes: [],
64
+ database: [],
65
+ cloud: [],
66
+ packagemanagers: [],
67
+ testing: [],
68
+ ai: [],
59
69
  others: [],
60
70
  };
61
71
  }
@@ -82,7 +92,7 @@ function initStorage() {
82
92
  * Like "mkdir -p" in linux
83
93
  */
84
94
  fs.mkdirSync(TRACKER_DIR, { recursive: true });
85
- console.log("✅ Created .tracker folder");
95
+ console.log('✅ Created .tracker folder');
86
96
  }
87
97
 
88
98
  /*
@@ -99,7 +109,7 @@ function initStorage() {
99
109
  COMMANDS_FILE,
100
110
  JSON.stringify(getDefaultStructure(), null, 2)
101
111
  );
102
- console.log("✅ Created .tracker/commands.json");
112
+ console.log('✅ Created .tracker/commands.json');
103
113
  }
104
114
  }
105
115
 
@@ -122,8 +132,9 @@ function readCommands() {
122
132
  * fs.readFileSync() → reads the file content as text
123
133
  * JSON.parse() → converts JSON text back to JavaScript object
124
134
  */
125
- const fileContent = fs.readFileSync(COMMANDS_FILE, "utf-8");
126
- return JSON.parse(fileContent);
135
+ const fileContent = fs.readFileSync(COMMANDS_FILE, 'utf-8');
136
+ const parsed = JSON.parse(fileContent);
137
+ return { ...getDefaultStructure(), ...parsed };
127
138
  }
128
139
 
129
140
  /*
@@ -138,7 +149,7 @@ function saveCommand(command) {
138
149
  * Safety check — don't save empty commands
139
150
  */
140
151
  if (!command || !command.trim()) {
141
- return { saved: false, reason: "empty command" };
152
+ return { saved: false, reason: 'empty command' };
142
153
  }
143
154
 
144
155
  /*
@@ -172,7 +183,7 @@ function saveCommand(command) {
172
183
  );
173
184
 
174
185
  if (isDuplicate) {
175
- return { saved: false, reason: "duplicate", category };
186
+ return { saved: false, reason: 'duplicate', category };
176
187
  }
177
188
 
178
189
  /*
@@ -207,7 +218,7 @@ function saveCommand(command) {
207
218
  function toggleFavorite(command) {
208
219
 
209
220
  if (!command || !command.trim()) {
210
- return { success: false, reason: "empty command" };
221
+ return { success: false, reason: 'empty command' };
211
222
  }
212
223
 
213
224
  const cleanCommand = command.trim();
@@ -234,14 +245,14 @@ function toggleFavorite(command) {
234
245
 
235
246
  return {
236
247
  success: true,
237
- action: currentFav ? "removed" : "added",
248
+ action: currentFav ? 'removed' : 'added',
238
249
  command: cleanCommand,
239
250
  category
240
251
  };
241
252
  }
242
253
  }
243
254
 
244
- return { success: false, reason: "command not found" };
255
+ return { success: false, reason: 'command not found' };
245
256
  }
246
257
 
247
258
  /*
@@ -283,4 +294,4 @@ module.exports = {
283
294
  getFavorites,
284
295
  TRACKER_DIR,
285
296
  COMMANDS_FILE,
286
- };
297
+ };