@adithya-naik/cmd-tracker 1.0.2 → 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.
- package/.github/workflows/ci.yml +19 -1
- package/.husky/pre-commit +1 -0
- package/bin/tracker.js +45 -45
- package/eslint.config.js +30 -0
- package/package.json +19 -4
- package/src/commands/clear.js +13 -13
- package/src/commands/export.js +16 -16
- package/src/commands/favorite.js +16 -16
- package/src/commands/hook.js +26 -26
- package/src/commands/init.js +43 -43
- package/src/commands/list.js +32 -32
- package/src/commands/save.js +2 -2
- package/src/commands/search.js +18 -18
- package/src/commands/stats.js +55 -55
- package/src/index.js +2 -2
- package/src/utils/categorizer.js +71 -71
- package/src/utils/hook.js +70 -69
- package/src/utils/storage.js +14 -14
- package/src/utils/validator.js +27 -27
package/src/utils/categorizer.js
CHANGED
|
@@ -26,157 +26,157 @@ const CATEGORIES = {
|
|
|
26
26
|
* Any command starting with "git" goes here
|
|
27
27
|
* Examples: git status, git push, git commit -m "msg"
|
|
28
28
|
*/
|
|
29
|
-
git: [
|
|
29
|
+
git: ['git'],
|
|
30
30
|
|
|
31
31
|
/*
|
|
32
32
|
* Any command starting with "npm" or "npx" goes here
|
|
33
33
|
* Examples: npm install, npm run dev, npx create-react-app
|
|
34
34
|
*/
|
|
35
|
-
npm: [
|
|
35
|
+
npm: ['npm', 'npx'],
|
|
36
36
|
|
|
37
37
|
/*
|
|
38
38
|
* Any command starting with "docker" goes here
|
|
39
39
|
* Examples: docker ps, docker build, docker-compose up
|
|
40
40
|
*/
|
|
41
|
-
docker: [
|
|
41
|
+
docker: ['docker', 'docker-compose'],
|
|
42
42
|
|
|
43
43
|
/*
|
|
44
44
|
* Common linux commands go here
|
|
45
45
|
* Examples: ls -la, cd projects, mkdir new-folder
|
|
46
46
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
linux: [
|
|
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'
|
|
57
|
+
],
|
|
58
58
|
|
|
59
59
|
/*
|
|
60
60
|
* Node.js related commands
|
|
61
61
|
* Examples: node index.js, nodemon server.js
|
|
62
62
|
*/
|
|
63
|
-
node: [
|
|
63
|
+
node: ['node', 'nodemon'],
|
|
64
64
|
|
|
65
65
|
/*
|
|
66
66
|
* Angular CLI commands
|
|
67
67
|
* Examples: ng new my-app, ng serve, ng generate component
|
|
68
68
|
*/
|
|
69
|
-
angular: [
|
|
69
|
+
angular: ['ng'],
|
|
70
70
|
|
|
71
71
|
/*
|
|
72
72
|
* Python commands
|
|
73
73
|
* Examples: python app.py, pip install flask
|
|
74
74
|
*/
|
|
75
|
-
python: [
|
|
75
|
+
python: ['python', 'python3', 'pip', 'pip3'],
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
go: ['go'],
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
/*
|
|
80
80
|
* Java ecosystem commands
|
|
81
81
|
* Examples: java Main, javac Main.java,
|
|
82
82
|
* mvn package, gradle build
|
|
83
83
|
*/
|
|
84
|
-
|
|
84
|
+
java: ['java', 'javac', 'mvn', 'gradle'],
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
/*
|
|
87
87
|
* Rust development commands
|
|
88
88
|
* Examples: cargo build, cargo run,
|
|
89
89
|
* cargo test, rustc main.rs
|
|
90
90
|
*/
|
|
91
|
-
|
|
91
|
+
rust: ['cargo', 'rustc'],
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
/*
|
|
94
94
|
* .NET commands
|
|
95
95
|
* Examples: dotnet run, dotnet build,
|
|
96
96
|
* dotnet new console
|
|
97
97
|
*/
|
|
98
|
-
|
|
98
|
+
dotnet: ['dotnet'],
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
/*
|
|
101
101
|
* Kubernetes tooling commands
|
|
102
102
|
* Examples: kubectl get pods,
|
|
103
103
|
* kubectl apply -f app.yaml,
|
|
104
104
|
* helm install my-app chart/
|
|
105
105
|
*/
|
|
106
|
-
|
|
106
|
+
kubernetes: ['kubectl', 'helm'],
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
/*
|
|
109
109
|
* Database CLI commands
|
|
110
110
|
* Examples: mysql -u root -p,
|
|
111
111
|
* psql mydb,
|
|
112
112
|
* mongosh,
|
|
113
113
|
* redis-cli
|
|
114
114
|
*/
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
database: [
|
|
116
|
+
'mysql',
|
|
117
|
+
'psql',
|
|
118
|
+
'sqlite3',
|
|
119
|
+
'mongosh',
|
|
120
|
+
'redis-cli'
|
|
121
|
+
],
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
/*
|
|
124
124
|
* Cloud provider CLI commands
|
|
125
125
|
* Examples: aws s3 ls,
|
|
126
126
|
* gcloud compute instances list,
|
|
127
127
|
* az login
|
|
128
128
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
cloud: [
|
|
130
|
+
'aws',
|
|
131
|
+
'gcloud',
|
|
132
|
+
'az'
|
|
133
|
+
],
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
/*
|
|
136
136
|
* Package manager commands
|
|
137
137
|
* Examples: yarn install,
|
|
138
138
|
* pnpm dev,
|
|
139
139
|
* brew install git,
|
|
140
140
|
* snap install code
|
|
141
141
|
*/
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
packagemanagers: [
|
|
143
|
+
'yarn',
|
|
144
|
+
'pnpm',
|
|
145
|
+
'brew',
|
|
146
|
+
'snap'
|
|
147
|
+
],
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
/*
|
|
150
150
|
* Testing framework commands
|
|
151
151
|
* Examples: jest,
|
|
152
152
|
* vitest run,
|
|
153
153
|
* playwright test,
|
|
154
154
|
* cypress open
|
|
155
155
|
*/
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
156
|
+
testing: [
|
|
157
|
+
'jest',
|
|
158
|
+
'vitest',
|
|
159
|
+
'playwright',
|
|
160
|
+
'cypress'
|
|
161
|
+
],
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
/*
|
|
164
164
|
* AI coding assistant CLI commands
|
|
165
165
|
* Examples: claude, gemini, codex,
|
|
166
166
|
* opencode, aider, q chat
|
|
167
167
|
*/
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
180
|
|
|
181
181
|
};
|
|
182
182
|
|
|
@@ -201,8 +201,8 @@ function categorize(command) {
|
|
|
201
201
|
* Safety check — if command is empty or not a string, return "others"
|
|
202
202
|
* This prevents crashes if something unexpected is passed in
|
|
203
203
|
*/
|
|
204
|
-
if (!command || typeof command !==
|
|
205
|
-
return
|
|
204
|
+
if (!command || typeof command !== 'string') {
|
|
205
|
+
return 'others';
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
/*
|
|
@@ -216,7 +216,7 @@ function categorize(command) {
|
|
|
216
216
|
* → split(" ") → ["git", "status"]
|
|
217
217
|
* → [0] → "git"
|
|
218
218
|
*/
|
|
219
|
-
const firstWord = command.trim().toLowerCase().split(
|
|
219
|
+
const firstWord = command.trim().toLowerCase().split(' ')[0];
|
|
220
220
|
|
|
221
221
|
/*
|
|
222
222
|
* Object.entries() converts our CATEGORIES object into an array like:
|
|
@@ -248,7 +248,7 @@ function categorize(command) {
|
|
|
248
248
|
* If no category matched — return "others"
|
|
249
249
|
* This is our catch-all category
|
|
250
250
|
*/
|
|
251
|
-
return
|
|
251
|
+
return 'others';
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
/*
|
package/src/utils/hook.js
CHANGED
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
* 4. User sources their config → automatic capture starts!
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
const fs = require(
|
|
21
|
-
const path = require(
|
|
22
|
-
const os = require(
|
|
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,15 +28,14 @@ const os = require("node: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,
|
|
38
|
-
zsh: path.join(HOME_DIR,
|
|
39
|
-
fish: path.join(HOME_DIR,
|
|
36
|
+
bash: path.join(HOME_DIR, '.bashrc'),
|
|
37
|
+
zsh: path.join(HOME_DIR, '.zshrc'),
|
|
38
|
+
fish: path.join(HOME_DIR, '.config/fish/config.fish'),
|
|
40
39
|
|
|
41
40
|
};
|
|
42
41
|
|
|
@@ -69,42 +68,42 @@ const SHELL_CONFIGS = {
|
|
|
69
68
|
* This is cleaner and works on all bash/zsh versions
|
|
70
69
|
*/
|
|
71
70
|
const HOOK_SCRIPT = [
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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:]]*//\')',
|
|
77
76
|
' if [ -n "$LAST_CMD" ]; then',
|
|
78
77
|
' tracker save "$LAST_CMD" > /dev/null 2>&1',
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
' fi',
|
|
79
|
+
'}',
|
|
80
|
+
'',
|
|
81
|
+
'# For zsh',
|
|
83
82
|
'if [ -n "$ZSH_VERSION" ]; then',
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
' autoload -U add-zsh-hook',
|
|
84
|
+
' add-zsh-hook precmd _cmd_tracker_hook',
|
|
85
|
+
'fi',
|
|
86
|
+
'',
|
|
87
|
+
'# For bash',
|
|
89
88
|
'if [ -n "$BASH_VERSION" ]; then',
|
|
90
89
|
' PROMPT_COMMAND="_cmd_tracker_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"',
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
].join(
|
|
90
|
+
'fi',
|
|
91
|
+
''
|
|
92
|
+
].join('\n');
|
|
94
93
|
|
|
95
94
|
|
|
96
95
|
const FISH_HOOK_SCRIPT = [
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
].join(
|
|
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');
|
|
108
107
|
|
|
109
108
|
|
|
110
109
|
/*
|
|
@@ -113,7 +112,7 @@ const FISH_HOOK_SCRIPT = [
|
|
|
113
112
|
* → Check if hook already exists (avoid duplicates)
|
|
114
113
|
* → Find and remove hook when user runs tracker unhook
|
|
115
114
|
*/
|
|
116
|
-
const HOOK_MARKER =
|
|
115
|
+
const HOOK_MARKER = '# cmd-tracker shell hook — auto saves every command you type';
|
|
117
116
|
|
|
118
117
|
/*
|
|
119
118
|
* detectShell() — detects which shell user is running
|
|
@@ -128,28 +127,30 @@ const HOOK_MARKER = "# cmd-tracker shell hook — auto saves every command you t
|
|
|
128
127
|
* @returns {string} — "bash", "zsh", "fish", or "unknown"
|
|
129
128
|
*/
|
|
130
129
|
function detectShell() {
|
|
131
|
-
|
|
130
|
+
const shell = process.env.SHELL || '';
|
|
132
131
|
|
|
133
|
-
|
|
132
|
+
if (process.env.FISH_VERSION) return 'fish';
|
|
134
133
|
|
|
135
|
-
|
|
134
|
+
if (shell.includes('fish')) return 'fish';
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
+
}
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
if (shell.includes('zsh')) return 'zsh';
|
|
148
|
+
if (shell.includes('bash')) return 'bash';
|
|
148
149
|
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
const comspec = process.env.ComSpec || '';
|
|
151
|
+
if (comspec.includes('cmd.exe')) return 'unknown';
|
|
151
152
|
|
|
152
|
-
|
|
153
|
+
return 'unknown';
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
/*
|
|
@@ -176,10 +177,10 @@ function installHook() {
|
|
|
176
177
|
* If shell is unknown — we can't install hook
|
|
177
178
|
* This happens on Windows CMD/PowerShell
|
|
178
179
|
*/
|
|
179
|
-
if (shell ===
|
|
180
|
+
if (shell === 'unknown') {
|
|
180
181
|
return {
|
|
181
182
|
success: false,
|
|
182
|
-
message:
|
|
183
|
+
message: '❌ Automatic hook not supported on Windows CMD/PowerShell\n💡 Use Git Bash or WSL for automatic capture'
|
|
183
184
|
};
|
|
184
185
|
}
|
|
185
186
|
|
|
@@ -190,7 +191,7 @@ function installHook() {
|
|
|
190
191
|
* Look for our unique marker in the config file
|
|
191
192
|
*/
|
|
192
193
|
if (fs.existsSync(configFile)) {
|
|
193
|
-
const content = fs.readFileSync(configFile,
|
|
194
|
+
const content = fs.readFileSync(configFile, 'utf-8');
|
|
194
195
|
|
|
195
196
|
if (content.includes(HOOK_MARKER)) {
|
|
196
197
|
return {
|
|
@@ -204,17 +205,17 @@ function installHook() {
|
|
|
204
205
|
* Append hook script to shell config file
|
|
205
206
|
* appendFileSync → adds to end without deleting existing content
|
|
206
207
|
*/
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
if (shell === 'fish') {
|
|
209
|
+
fs.mkdirSync(path.dirname(configFile), { recursive: true });
|
|
210
|
+
}
|
|
210
211
|
|
|
211
|
-
|
|
212
|
+
/*
|
|
212
213
|
* Choose correct hook
|
|
213
214
|
*/
|
|
214
|
-
|
|
215
|
-
shell ===
|
|
215
|
+
const hookScript =
|
|
216
|
+
shell === 'fish' ? FISH_HOOK_SCRIPT : HOOK_SCRIPT;
|
|
216
217
|
|
|
217
|
-
|
|
218
|
+
fs.appendFileSync(configFile, hookScript);
|
|
218
219
|
|
|
219
220
|
return {
|
|
220
221
|
success: true,
|
|
@@ -225,10 +226,10 @@ function installHook() {
|
|
|
225
226
|
|
|
226
227
|
} catch (error) {
|
|
227
228
|
|
|
228
|
-
if (error.code ===
|
|
229
|
+
if (error.code === 'EACCES') {
|
|
229
230
|
return {
|
|
230
231
|
success: false,
|
|
231
|
-
message:
|
|
232
|
+
message: '❌ Permission denied! Try with sudo/admin permissions'
|
|
232
233
|
};
|
|
233
234
|
}
|
|
234
235
|
|
|
@@ -253,11 +254,11 @@ function removeHook() {
|
|
|
253
254
|
if (!fs.existsSync(configFile)) {
|
|
254
255
|
return {
|
|
255
256
|
success: false,
|
|
256
|
-
message:
|
|
257
|
+
message: '❌ Shell config file not found'
|
|
257
258
|
};
|
|
258
259
|
}
|
|
259
260
|
|
|
260
|
-
const content = fs.readFileSync(configFile,
|
|
261
|
+
const content = fs.readFileSync(configFile, 'utf-8');
|
|
261
262
|
|
|
262
263
|
/*
|
|
263
264
|
* Check if hook exists
|
|
@@ -265,7 +266,7 @@ function removeHook() {
|
|
|
265
266
|
if (!content.includes(HOOK_MARKER)) {
|
|
266
267
|
return {
|
|
267
268
|
success: false,
|
|
268
|
-
message:
|
|
269
|
+
message: '⚠️ Hook not found in shell config'
|
|
269
270
|
};
|
|
270
271
|
}
|
|
271
272
|
|
|
@@ -274,7 +275,7 @@ function removeHook() {
|
|
|
274
275
|
* Split by marker → take everything before it
|
|
275
276
|
* Then trim trailing whitespace
|
|
276
277
|
*/
|
|
277
|
-
const newContent = content.split(HOOK_MARKER)[0].trimEnd() +
|
|
278
|
+
const newContent = content.split(HOOK_MARKER)[0].trimEnd() + '\n';
|
|
278
279
|
fs.writeFileSync(configFile, newContent);
|
|
279
280
|
|
|
280
281
|
return {
|
|
@@ -303,7 +304,7 @@ function isHookInstalled() {
|
|
|
303
304
|
|
|
304
305
|
if (!fs.existsSync(configFile)) return false;
|
|
305
306
|
|
|
306
|
-
const content = fs.readFileSync(configFile,
|
|
307
|
+
const content = fs.readFileSync(configFile, 'utf-8');
|
|
307
308
|
return content.includes(HOOK_MARKER);
|
|
308
309
|
|
|
309
310
|
} catch (error) {
|
package/src/utils/storage.js
CHANGED
|
@@ -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(
|
|
15
|
-
const path = require(
|
|
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(
|
|
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(),
|
|
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,
|
|
40
|
+
const COMMANDS_FILE = path.join(TRACKER_DIR, 'commands.json');
|
|
41
41
|
|
|
42
42
|
/*
|
|
43
43
|
* This is our default empty structure
|
|
@@ -63,7 +63,7 @@ function getDefaultStructure() {
|
|
|
63
63
|
kubernetes: [],
|
|
64
64
|
database: [],
|
|
65
65
|
cloud: [],
|
|
66
|
-
|
|
66
|
+
packagemanagers: [],
|
|
67
67
|
testing: [],
|
|
68
68
|
ai: [],
|
|
69
69
|
others: [],
|
|
@@ -92,7 +92,7 @@ function initStorage() {
|
|
|
92
92
|
* Like "mkdir -p" in linux
|
|
93
93
|
*/
|
|
94
94
|
fs.mkdirSync(TRACKER_DIR, { recursive: true });
|
|
95
|
-
console.log(
|
|
95
|
+
console.log('✅ Created .tracker folder');
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/*
|
|
@@ -109,7 +109,7 @@ function initStorage() {
|
|
|
109
109
|
COMMANDS_FILE,
|
|
110
110
|
JSON.stringify(getDefaultStructure(), null, 2)
|
|
111
111
|
);
|
|
112
|
-
console.log(
|
|
112
|
+
console.log('✅ Created .tracker/commands.json');
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -132,7 +132,7 @@ function readCommands() {
|
|
|
132
132
|
* fs.readFileSync() → reads the file content as text
|
|
133
133
|
* JSON.parse() → converts JSON text back to JavaScript object
|
|
134
134
|
*/
|
|
135
|
-
const fileContent = fs.readFileSync(COMMANDS_FILE,
|
|
135
|
+
const fileContent = fs.readFileSync(COMMANDS_FILE, 'utf-8');
|
|
136
136
|
const parsed = JSON.parse(fileContent);
|
|
137
137
|
return { ...getDefaultStructure(), ...parsed };
|
|
138
138
|
}
|
|
@@ -149,7 +149,7 @@ function saveCommand(command) {
|
|
|
149
149
|
* Safety check — don't save empty commands
|
|
150
150
|
*/
|
|
151
151
|
if (!command || !command.trim()) {
|
|
152
|
-
return { saved: false, reason:
|
|
152
|
+
return { saved: false, reason: 'empty command' };
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
/*
|
|
@@ -183,7 +183,7 @@ function saveCommand(command) {
|
|
|
183
183
|
);
|
|
184
184
|
|
|
185
185
|
if (isDuplicate) {
|
|
186
|
-
return { saved: false, reason:
|
|
186
|
+
return { saved: false, reason: 'duplicate', category };
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
/*
|
|
@@ -218,7 +218,7 @@ function saveCommand(command) {
|
|
|
218
218
|
function toggleFavorite(command) {
|
|
219
219
|
|
|
220
220
|
if (!command || !command.trim()) {
|
|
221
|
-
return { success: false, reason:
|
|
221
|
+
return { success: false, reason: 'empty command' };
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
const cleanCommand = command.trim();
|
|
@@ -245,14 +245,14 @@ function toggleFavorite(command) {
|
|
|
245
245
|
|
|
246
246
|
return {
|
|
247
247
|
success: true,
|
|
248
|
-
action: currentFav ?
|
|
248
|
+
action: currentFav ? 'removed' : 'added',
|
|
249
249
|
command: cleanCommand,
|
|
250
250
|
category
|
|
251
251
|
};
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
return { success: false, reason:
|
|
255
|
+
return { success: false, reason: 'command not found' };
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
/*
|