@adithya-naik/cmd-tracker 1.0.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.
@@ -0,0 +1,92 @@
1
+ /*
2
+ * export.js
3
+ * Now with proper error handling using validator.js
4
+ */
5
+
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+ const { readCommands } = require("../utils/storage");
9
+ const { isInitialized, showInitError } = require("../utils/validator");
10
+
11
+ function exportCommand(options) {
12
+
13
+ /*
14
+ * Check initialization first
15
+ */
16
+ if (!isInitialized()) {
17
+ showInitError();
18
+ return;
19
+ }
20
+
21
+ try {
22
+ const data = readCommands();
23
+
24
+ const total = Object.values(data).reduce(
25
+ (sum, commands) => sum + commands.length, 0
26
+ );
27
+
28
+ if (total === 0) {
29
+ console.log("\nšŸ“­ No commands to export yet!");
30
+ console.log("šŸ’” Use: tracker save \"your command\"\n");
31
+ return;
32
+ }
33
+
34
+ if (options.csv) {
35
+ exportAsCSV(data, total);
36
+ } else {
37
+ exportAsJSON(data, total);
38
+ }
39
+
40
+ } catch (error) {
41
+ console.log("\nāŒ Error exporting commands");
42
+ console.log("šŸ’” Try running tracker init again\n");
43
+ }
44
+ }
45
+
46
+ function exportAsJSON(data, total) {
47
+
48
+ try {
49
+ const exportData = {
50
+ exportedAt: new Date().toISOString(),
51
+ totalCommands: total,
52
+ commands: data
53
+ };
54
+
55
+ const filePath = path.join(process.cwd(), "tracker-export.json");
56
+ fs.writeFileSync(filePath, JSON.stringify(exportData, null, 2));
57
+
58
+ console.log(`\nāœ… Exported ${total} commands to tracker-export.json`);
59
+ console.log(`šŸ“ Location: ${filePath}\n`);
60
+
61
+ } catch (error) {
62
+ console.log("\nāŒ Error creating JSON file");
63
+ console.log("šŸ’” Check if you have write permissions in this folder\n");
64
+ }
65
+ }
66
+
67
+ function exportAsCSV(data, total) {
68
+
69
+ try {
70
+ let csvContent = "category,command,date\n";
71
+
72
+ for (const [category, commands] of Object.entries(data)) {
73
+ for (const item of commands) {
74
+ const date = new Date(item.time).toLocaleDateString();
75
+ csvContent += `${category},"${item.command}",${date}\n`;
76
+ }
77
+ }
78
+
79
+ const filePath = path.join(process.cwd(), "tracker-export.csv");
80
+ fs.writeFileSync(filePath, csvContent);
81
+
82
+ console.log(`\nāœ… Exported ${total} commands to tracker-export.csv`);
83
+ console.log(`šŸ“ Location: ${filePath}`);
84
+ console.log(`šŸ’” Open in Excel or Google Sheets for easy revision!\n`);
85
+
86
+ } catch (error) {
87
+ console.log("\nāŒ Error creating CSV file");
88
+ console.log("šŸ’” Check if you have write permissions in this folder\n");
89
+ }
90
+ }
91
+
92
+ module.exports = { exportCommand };
@@ -0,0 +1,95 @@
1
+ /*
2
+ * favorite.js
3
+ *
4
+ * Handles two commands:
5
+ * tracker favorite "git status" → toggles favorite on that command
6
+ * tracker favorites → lists all favorited commands
7
+ *
8
+ * Favorites are stored as { favorite: true } on each command object
9
+ * in commands.json
10
+ */
11
+
12
+ const { toggleFavorite, getFavorites } = require("../utils/storage");
13
+ const { isInitialized, showInitError } = require("../utils/validator");
14
+
15
+ /*
16
+ * favoriteCommand() — toggles favorite on a specific command
17
+ *
18
+ * @param {string} command — command to favorite/unfavorite
19
+ */
20
+ function favoriteCommand(command) {
21
+
22
+ if (!isInitialized()) {
23
+ showInitError();
24
+ return;
25
+ }
26
+
27
+ if (!command || !command.trim()) {
28
+ console.log("\nāŒ Please provide a command to favorite");
29
+ console.log("šŸ’” Usage: tracker favorite \"git status\"\n");
30
+ return;
31
+ }
32
+
33
+ try {
34
+ const result = toggleFavorite(command.trim());
35
+
36
+ if (!result.success) {
37
+ if (result.reason === "command not found") {
38
+ console.log(`\nāŒ Command not found: "${command}"`);
39
+ console.log("šŸ’” Run tracker list to see saved commands\n");
40
+ return;
41
+ }
42
+ console.log("\nāŒ Failed to update favorite\n");
43
+ return;
44
+ }
45
+
46
+ if (result.action === "added") {
47
+ console.log(`\n⭐ Added to favorites: ${command}`);
48
+ console.log(`šŸ“ Category: ${result.category}\n`);
49
+ } else {
50
+ console.log(`\nāœ… Removed from favorites: ${command}\n`);
51
+ }
52
+
53
+ } catch (error) {
54
+ console.log("\nāŒ Error updating favorite");
55
+ console.log("šŸ’” Try running tracker init again\n");
56
+ }
57
+ }
58
+
59
+ /*
60
+ * favoritesCommand() — lists all favorited commands
61
+ */
62
+ function favoritesCommand() {
63
+
64
+ if (!isInitialized()) {
65
+ showInitError();
66
+ return;
67
+ }
68
+
69
+ try {
70
+ const favorites = getFavorites();
71
+
72
+ if (favorites.length === 0) {
73
+ console.log("\n⭐ No favorites yet!");
74
+ console.log("šŸ’” Usage: tracker favorite \"git status\"\n");
75
+ return;
76
+ }
77
+
78
+ console.log("\n⭐ CMD-TRACKER — Your Favorites\n");
79
+ console.log("─".repeat(50));
80
+
81
+ favorites.forEach((item, index) => {
82
+ const date = new Date(item.time).toLocaleDateString();
83
+ console.log(`\n ${index + 1}. ${item.command}`);
84
+ console.log(` šŸ“ ${item.category} šŸ“… ${date}`);
85
+ });
86
+
87
+ console.log("\n" + "─".repeat(50));
88
+ console.log(`⭐ Total favorites: ${favorites.length}\n`);
89
+
90
+ } catch (error) {
91
+ console.log("\nāŒ Error reading favorites\n");
92
+ }
93
+ }
94
+
95
+ module.exports = { favoriteCommand, favoritesCommand };
@@ -0,0 +1,89 @@
1
+ /*
2
+ * hook.js (command)
3
+ *
4
+ * Handles "tracker hook" and "tracker unhook" commands
5
+ *
6
+ * tracker hook → installs shell hook for auto capture
7
+ * tracker unhook → removes shell hook
8
+ */
9
+
10
+ const {
11
+ installHook,
12
+ removeHook,
13
+ isHookInstalled,
14
+ detectShell
15
+ } = require("../utils/hook");
16
+
17
+ const { isInitialized, showInitError } = require("../utils/validator");
18
+
19
+ /*
20
+ * hookCommand() — runs when user types: tracker hook
21
+ * Installs the shell hook for automatic command capture
22
+ */
23
+ function hookCommand() {
24
+
25
+ /*
26
+ * Must run tracker init before tracker hook
27
+ */
28
+ if (!isInitialized()) {
29
+ showInitError();
30
+ return;
31
+ }
32
+
33
+ console.log("\nšŸŖ„ Setting up automatic command capture...\n");
34
+
35
+ /*
36
+ * Show which shell was detected
37
+ */
38
+ const shell = detectShell();
39
+ console.log(`šŸ” Detected shell: ${shell}`);
40
+
41
+ /*
42
+ * Check if already installed
43
+ */
44
+ if (isHookInstalled()) {
45
+ console.log("\nāš ļø Shell hook is already installed!");
46
+ console.log("šŸ’” Your commands are already being captured automatically");
47
+ console.log("šŸ’” Run: tracker unhook to disable\n");
48
+ return;
49
+ }
50
+
51
+ /*
52
+ * Install the hook
53
+ */
54
+ const result = installHook();
55
+ console.log("\n" + result.message);
56
+
57
+ if (result.success) {
58
+ console.log("\nšŸŽÆ Almost done! Run this command to activate:");
59
+ /*
60
+ * Always show Unix style path for source command
61
+ * Even on Windows — Git Bash uses Unix paths
62
+ * ~/.bashrc works on all systems
63
+ */
64
+ const sourceCmd = result.shell === "zsh" ? "~/.zshrc" : "~/.bashrc";
65
+ console.log(`\n source ${sourceCmd}\n`);
66
+ console.log("After that — every command you type will be saved automatically! šŸš€\n");
67
+ }
68
+ }
69
+
70
+ /*
71
+ * unhookCommand() — runs when user types: tracker unhook
72
+ * Removes the shell hook
73
+ */
74
+ function unhookCommand() {
75
+
76
+ console.log("\nšŸ”Œ Removing automatic command capture...\n");
77
+
78
+ const result = removeHook();
79
+ console.log(result.message);
80
+
81
+ if (result.success) {
82
+ console.log("\nšŸŽÆ Almost done! Run this to apply changes:");
83
+ const sourceCmd = result.shell === "zsh" ? "~/.zshrc" : "~/.bashrc";
84
+ console.log(`\n source ${sourceCmd}\n`);
85
+ console.log("After that — automatic capture will be disabled\n");
86
+ }
87
+ }
88
+
89
+ module.exports = { hookCommand, unhookCommand };
@@ -0,0 +1,153 @@
1
+ /*
2
+ * init.js
3
+ * Now with proper error handling
4
+ *
5
+ * Error cases handled:
6
+ * → Already initialized (runs tracker init twice)
7
+ * → No write permissions in folder
8
+ * → Any unexpected file system errors
9
+ */
10
+
11
+ const fs = require("fs");
12
+ const path = require("path");
13
+ const { initStorage } = require("../utils/storage");
14
+ const { isInitialized } = require("../utils/validator");
15
+
16
+ function initCommand() {
17
+
18
+ console.log("šŸš€ Initializing cmd-tracker in your project...\n");
19
+
20
+ try {
21
+
22
+ /*
23
+ * Check if already initialized
24
+ * If user runs tracker init twice — warn them
25
+ * but still continue in case files are corrupted
26
+ */
27
+ if (isInitialized()) {
28
+ console.log("āš ļø cmd-tracker is already initialized in this project!");
29
+ console.log("šŸ’” Your existing commands are safe");
30
+ console.log("šŸ’” Running init again will not delete your saved commands\n");
31
+ }
32
+
33
+ /*
34
+ * Initialize storage — creates .tracker folder
35
+ * and commands.json if they don't exist
36
+ */
37
+ initStorage();
38
+
39
+ /*
40
+ * Update .gitignore
41
+ */
42
+ updateGitignore();
43
+
44
+ console.log("\nāœ… cmd-tracker initialized successfully!");
45
+ console.log("šŸ“ Created .tracker/commands.json in your project");
46
+ console.log("\nšŸŽÆ You can now use:");
47
+ console.log(" tracker list → see all saved commands");
48
+ console.log(" tracker stats → see command statistics");
49
+ console.log(" tracker search → search your commands");
50
+ console.log(" tracker export → export your commands");
51
+ console.log("\nšŸ’” Start using your terminal normally");
52
+ console.log(" Commands will be saved automatically!\n");
53
+
54
+ } catch (error) {
55
+
56
+ /*
57
+ * Handle specific error types
58
+ *
59
+ * error.code === "EACCES" → permission denied
60
+ * This happens when user doesn't have write access
61
+ * to the current folder
62
+ */
63
+ if (error.code === "EACCES") {
64
+ console.error("āŒ Permission denied!");
65
+ console.error("šŸ’” Try running with admin permissions");
66
+ console.error("šŸ’” Or check folder write permissions\n");
67
+ return;
68
+ }
69
+
70
+ /*
71
+ * Handle no space left on disk
72
+ */
73
+ if (error.code === "ENOSPC") {
74
+ console.error("āŒ No space left on disk!");
75
+ console.error("šŸ’” Free up some disk space and try again\n");
76
+ return;
77
+ }
78
+
79
+ /*
80
+ * Any other unexpected error
81
+ */
82
+ console.error("āŒ Failed to initialize cmd-tracker");
83
+ console.error(`Error: ${error.message}\n`);
84
+ }
85
+ }
86
+
87
+ function updateGitignore() {
88
+
89
+ const gitignorePath = path.join(process.cwd(), ".gitignore");
90
+ /*
91
+ * Added tracker-export files to gitignore too
92
+ * Users should not push their exported files to GitHub
93
+ * These are personal revision files — local only
94
+ */
95
+ const trackerEntry = "\n# cmd-tracker personal data\n.tracker/\ntracker-export.json\ntracker-export.csv\n";
96
+
97
+ try {
98
+
99
+ if (fs.existsSync(gitignorePath)) {
100
+ const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
101
+
102
+ /*
103
+ * Check each entry separately
104
+ * So we can add missing entries without touching existing ones
105
+ */
106
+ let entriesToAdd = "";
107
+
108
+ if (!gitignoreContent.includes(".tracker/")) {
109
+ entriesToAdd += ".tracker/\n";
110
+ }
111
+
112
+ if (!gitignoreContent.includes("tracker-export.json")) {
113
+ entriesToAdd += "tracker-export.json\n";
114
+ }
115
+
116
+ if (!gitignoreContent.includes("tracker-export.csv")) {
117
+ entriesToAdd += "tracker-export.csv\n";
118
+ }
119
+
120
+ /*
121
+ * Only write if there's something new to add
122
+ */
123
+ if (entriesToAdd === "") {
124
+ console.log("āœ… .gitignore already up to date");
125
+ return;
126
+ }
127
+
128
+ fs.appendFileSync(
129
+ gitignorePath,
130
+ "\n# cmd-tracker personal data\n" + entriesToAdd
131
+ );
132
+ console.log("āœ… Updated .gitignore with cmd-tracker entries");
133
+
134
+ fs.appendFileSync(gitignorePath, trackerEntry);
135
+ console.log("āœ… Added .tracker/ to your .gitignore");
136
+
137
+ } else {
138
+ fs.writeFileSync(gitignorePath, trackerEntry);
139
+ console.log("āœ… Created .gitignore with .tracker/ entry");
140
+ }
141
+
142
+ } catch (error) {
143
+
144
+ /*
145
+ * .gitignore update failed — not critical
146
+ * tracker still works, just warn the user
147
+ */
148
+ console.log("āš ļø Could not update .gitignore automatically");
149
+ console.log("šŸ’” Manually add .tracker/ to your .gitignore\n");
150
+ }
151
+ }
152
+
153
+ module.exports = { initCommand };
@@ -0,0 +1,107 @@
1
+ /*
2
+ * list.js
3
+ *
4
+ * Handles "tracker list" and "tracker list <category>" commands
5
+ * Now with proper error handling using validator.js
6
+ */
7
+
8
+ const { readCommands } = require("../utils/storage");
9
+ const {
10
+ isInitialized,
11
+ showInitError,
12
+ isValidCategory,
13
+ showCategoryError
14
+ } = require("../utils/validator");
15
+
16
+ function listCommand(category) {
17
+
18
+ /*
19
+ * Check if tracker init was run first
20
+ * If not — show helpful error and stop
21
+ */
22
+ if (!isInitialized()) {
23
+ showInitError();
24
+ return;
25
+ }
26
+
27
+ /*
28
+ * If category provided — validate it
29
+ */
30
+ if (category) {
31
+ if (!isValidCategory(category)) {
32
+ showCategoryError(category);
33
+ return;
34
+ }
35
+
36
+ const data = readCommands();
37
+ const cat = category.toLowerCase();
38
+ displayCategory(cat, data[cat]);
39
+ return;
40
+ }
41
+
42
+ /*
43
+ * No category — show all commands
44
+ */
45
+ try {
46
+ const data = readCommands();
47
+ let totalCommands = 0;
48
+
49
+ console.log("\nšŸ“Ÿ CMD-TRACKER — Your Command History\n");
50
+ console.log("─".repeat(50));
51
+
52
+ for (const [cat, commands] of Object.entries(data)) {
53
+ if (commands.length === 0) continue;
54
+ displayCategory(cat, commands);
55
+ totalCommands += commands.length;
56
+ }
57
+
58
+ if (totalCommands === 0) {
59
+ console.log("\nšŸ“­ No commands saved yet!");
60
+ console.log("šŸ’” Use: tracker save \"your command\"");
61
+ console.log("šŸ’” Or run commands normally if shell hook is set up\n");
62
+ return;
63
+ }
64
+
65
+ console.log("─".repeat(50));
66
+ console.log(`\nāœ… Total: ${totalCommands} commands saved\n`);
67
+
68
+ } catch (error) {
69
+ /*
70
+ * Something went wrong reading the file
71
+ * Show clear error instead of crashing
72
+ */
73
+ console.log("\nāŒ Error reading commands");
74
+ console.log(`šŸ’” Try running tracker init again\n`);
75
+ }
76
+ }
77
+
78
+ function displayCategory(categoryName, commands) {
79
+
80
+ const icons = {
81
+ git: "šŸ”€",
82
+ npm: "šŸ“¦",
83
+ docker: "🐳",
84
+ linux: "🐧",
85
+ node: "🟢",
86
+ angular: "šŸ”“",
87
+ python: "šŸ",
88
+ others: "šŸ“Œ"
89
+ };
90
+
91
+ const icon = icons[categoryName] || "šŸ“Œ";
92
+
93
+ console.log(`\n${icon} ${categoryName.toUpperCase()} (${commands.length})`);
94
+ console.log("─".repeat(30));
95
+
96
+ if (commands.length === 0) {
97
+ console.log(" šŸ“­ No commands saved yet");
98
+ return;
99
+ }
100
+
101
+ commands.forEach((item, index) => {
102
+ const date = new Date(item.time).toLocaleDateString();
103
+ console.log(` ${String(index + 1).padStart(2, " ")}. ${item.command} (${date})`);
104
+ });
105
+ }
106
+
107
+ module.exports = { listCommand };
@@ -0,0 +1,63 @@
1
+ /*
2
+ * save.js
3
+ * Now with proper error handling
4
+ *
5
+ * Error cases handled:
6
+ * → tracker not initialized
7
+ * → empty command
8
+ * → file write failures
9
+ * → any unexpected errors
10
+ *
11
+ * Important: ALL errors are silent here
12
+ * save runs automatically in background
13
+ * We NEVER want it to interrupt user's workflow
14
+ */
15
+
16
+ const { saveCommand } = require("../utils/storage");
17
+ const { isInitialized } = require("../utils/validator");
18
+
19
+ function saveCommandAction(command) {
20
+
21
+ /*
22
+ * Safety check — empty command
23
+ * Return silently — no error message needed
24
+ */
25
+ if (!command || !command.trim()) {
26
+ return;
27
+ }
28
+
29
+ /*
30
+ * Check if tracker is initialized
31
+ *
32
+ * Unlike other commands — we fail SILENTLY here
33
+ * Because save runs automatically via shell hook
34
+ * Showing errors every time would be very annoying
35
+ * for users who haven't run tracker init yet
36
+ */
37
+ if (!isInitialized()) {
38
+ return;
39
+ }
40
+
41
+ try {
42
+
43
+ const result = saveCommand(command);
44
+
45
+ /*
46
+ * Only show output when command is newly saved
47
+ * Stay silent for duplicates
48
+ */
49
+ if (result.saved) {
50
+ console.log(`āœ… Saved [${result.category}]: ${command}`);
51
+ }
52
+
53
+ } catch (error) {
54
+
55
+ /*
56
+ * Fail completely silently
57
+ * Never interrupt user's terminal workflow
58
+ */
59
+ return;
60
+ }
61
+ }
62
+
63
+ module.exports = { saveCommandAction };
@@ -0,0 +1,97 @@
1
+ /*
2
+ * search.js
3
+ *
4
+ * Handles "tracker search <query>" command
5
+ *
6
+ * tracker search "git" → finds all commands containing "git"
7
+ * tracker search "install" → finds all commands containing "install"
8
+ *
9
+ * Searches across ALL categories at once
10
+ * Returns matching commands with their category
11
+ */
12
+
13
+ const { readCommands } = require("../utils/storage");
14
+ const {
15
+ isInitialized,
16
+ showInitError,
17
+ isValidQuery
18
+ } = require("../utils/validator");
19
+
20
+ /*
21
+ * searchCommand() — main function
22
+ *
23
+ * @param {string} query — what user is searching for
24
+ * comes from: tracker search "git status"
25
+ */
26
+ function searchCommand(query) {
27
+
28
+ /*
29
+ * Check initialization first
30
+ */
31
+ if (!isInitialized()) {
32
+ showInitError();
33
+ return;
34
+ }
35
+
36
+ /*
37
+ * Validate query
38
+ */
39
+ if (!isValidQuery(query)) {
40
+ console.log("\nāŒ Please provide a search query");
41
+ console.log("šŸ’” Usage: tracker search \"git\"\n");
42
+ return;
43
+ }
44
+
45
+ try {
46
+ const searchTerm = query.trim().toLowerCase();
47
+ const data = readCommands();
48
+ const results = [];
49
+
50
+ for (const [category, commands] of Object.entries(data)) {
51
+ for (const item of commands) {
52
+ if (item.command.toLowerCase().includes(searchTerm)) {
53
+ results.push({
54
+ command: item.command,
55
+ category: category,
56
+ time: item.time
57
+ });
58
+ }
59
+ }
60
+ }
61
+
62
+ if (results.length === 0) {
63
+ console.log(`\nšŸ” No commands found for: "${query}"\n`);
64
+ return;
65
+ }
66
+
67
+ console.log(`\nšŸ” Search results for: "${query}"`);
68
+ console.log("─".repeat(50));
69
+
70
+ results.forEach((item, index) => {
71
+
72
+ const date = new Date(item.time).toLocaleDateString();
73
+
74
+ /*
75
+ * Highlight the search term in results
76
+ * Replace matched part with uppercase version
77
+ * So user can easily spot what matched
78
+ */
79
+ const highlighted = item.command.replace(
80
+ new RegExp(searchTerm, "gi"),
81
+ (match) => `[${match.toUpperCase()}]`
82
+ );
83
+
84
+ console.log(`\n ${index + 1}. ${highlighted}`);
85
+ console.log(` šŸ“ Category: ${item.category} šŸ“… ${date}`);
86
+ });
87
+
88
+ console.log("\n" + "─".repeat(50));
89
+ console.log(`āœ… Found ${results.length} matching command(s)\n`);
90
+
91
+ } catch (error) {
92
+ console.log("\nāŒ Error searching commands");
93
+ console.log("šŸ’” Try running tracker init again\n");
94
+ }
95
+ }
96
+
97
+ module.exports = { searchCommand };