proj-track 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.
Files changed (58) hide show
  1. package/README.md +253 -0
  2. package/dist/commands/clear.d.ts +5 -0
  3. package/dist/commands/clear.d.ts.map +1 -0
  4. package/dist/commands/clear.js +15 -0
  5. package/dist/commands/clear.js.map +1 -0
  6. package/dist/commands/init.d.ts +6 -0
  7. package/dist/commands/init.d.ts.map +1 -0
  8. package/dist/commands/init.js +57 -0
  9. package/dist/commands/init.js.map +1 -0
  10. package/dist/commands/list.d.ts +5 -0
  11. package/dist/commands/list.d.ts.map +1 -0
  12. package/dist/commands/list.js +34 -0
  13. package/dist/commands/list.js.map +1 -0
  14. package/dist/commands/pause.d.ts +6 -0
  15. package/dist/commands/pause.d.ts.map +1 -0
  16. package/dist/commands/pause.js +24 -0
  17. package/dist/commands/pause.js.map +1 -0
  18. package/dist/commands/remove.d.ts +7 -0
  19. package/dist/commands/remove.d.ts.map +1 -0
  20. package/dist/commands/remove.js +37 -0
  21. package/dist/commands/remove.js.map +1 -0
  22. package/dist/commands/resume.d.ts +6 -0
  23. package/dist/commands/resume.d.ts.map +1 -0
  24. package/dist/commands/resume.js +24 -0
  25. package/dist/commands/resume.js.map +1 -0
  26. package/dist/commands/run.d.ts +5 -0
  27. package/dist/commands/run.d.ts.map +1 -0
  28. package/dist/commands/run.js +39 -0
  29. package/dist/commands/run.js.map +1 -0
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +78 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/logger.d.ts +8 -0
  35. package/dist/logger.d.ts.map +1 -0
  36. package/dist/logger.js +42 -0
  37. package/dist/logger.js.map +1 -0
  38. package/dist/utils/file-handler.d.ts +36 -0
  39. package/dist/utils/file-handler.d.ts.map +1 -0
  40. package/dist/utils/file-handler.js +97 -0
  41. package/dist/utils/file-handler.js.map +1 -0
  42. package/dist/utils/project-detector.d.ts +17 -0
  43. package/dist/utils/project-detector.d.ts.map +1 -0
  44. package/dist/utils/project-detector.js +30 -0
  45. package/dist/utils/project-detector.js.map +1 -0
  46. package/dist/utils/security-filter.d.ts +10 -0
  47. package/dist/utils/security-filter.d.ts.map +1 -0
  48. package/dist/utils/security-filter.js +79 -0
  49. package/dist/utils/security-filter.js.map +1 -0
  50. package/dist/utils/shell-installer.d.ts +16 -0
  51. package/dist/utils/shell-installer.d.ts.map +1 -0
  52. package/dist/utils/shell-installer.js +232 -0
  53. package/dist/utils/shell-installer.js.map +1 -0
  54. package/package.json +78 -0
  55. package/scripts/preuninstall.js +97 -0
  56. package/scripts/release.sh +59 -0
  57. package/src/hooks/bash-hook.sh +67 -0
  58. package/src/hooks/zsh-hook.sh +54 -0
@@ -0,0 +1,232 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ const START_MARKER = '# --- proj-track hook start ---';
5
+ const END_MARKER = '# --- proj-track hook end ---';
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ /**
9
+ * Get the path to the hooks directory (works in both dev and installed contexts).
10
+ */
11
+ function getHooksDir() {
12
+ const packageRoot = path.resolve(__dirname, '..', '..');
13
+ const srcHooks = path.join(packageRoot, 'src', 'hooks');
14
+ if (fs.existsSync(srcHooks)) {
15
+ return srcHooks;
16
+ }
17
+ return path.join(packageRoot, 'hooks');
18
+ }
19
+ /**
20
+ * Detect which shell config files exist.
21
+ */
22
+ function getShellConfigs() {
23
+ const home = process.env.HOME || process.env.USERPROFILE || '';
24
+ const configs = [];
25
+ const bashrc = path.join(home, '.bashrc');
26
+ const zshrc = path.join(home, '.zshrc');
27
+ if (fs.existsSync(bashrc))
28
+ configs.push(bashrc);
29
+ if (fs.existsSync(zshrc))
30
+ configs.push(zshrc);
31
+ if (configs.length === 0) {
32
+ configs.push(bashrc);
33
+ }
34
+ return configs;
35
+ }
36
+ /**
37
+ * Read the appropriate shell hook file content.
38
+ */
39
+ function getHookContent(shell) {
40
+ const hooksDir = getHooksDir();
41
+ const hookFile = path.join(hooksDir, `${shell}-hook.sh`);
42
+ if (fs.existsSync(hookFile)) {
43
+ return fs.readFileSync(hookFile, 'utf-8');
44
+ }
45
+ return shell === 'zsh' ? getInlineZshHook() : getInlineBashHook();
46
+ }
47
+ function getInlineBashHook() {
48
+ return `${START_MARKER}
49
+ # AUTO-CAPTURE via PROMPT_COMMAND (NOT DEBUG trap - doesn't break arrow keys)
50
+
51
+ # Cleanup any previous installation from shell memory first
52
+ if declare -f __proj_track_capture >/dev/null 2>&1; then
53
+ PROMPT_COMMAND="\${PROMPT_COMMAND//__proj_track_capture;/}"
54
+ PROMPT_COMMAND="\${PROMPT_COMMAND//__proj_track_capture/}"
55
+ unset -f __proj_track_capture 2>/dev/null
56
+ fi
57
+
58
+ __proj_track_old_prompt="\${PROMPT_COMMAND:-}"
59
+
60
+ __proj_track_capture() {
61
+ if ! command -v proj-track-logger >/dev/null 2>&1; then
62
+ return
63
+ fi
64
+
65
+ local last_cmd
66
+ last_cmd=$(HISTTIMEFORMAT='' builtin history 1 | sed 's/^[ ]*[0-9]*[ ]*//')
67
+
68
+ [ -z "$last_cmd" ] && return
69
+
70
+ [[ "$last_cmd" =~ ^proj-track ]] && return
71
+ [[ "$last_cmd" =~ ^__vsc ]] && return
72
+ [[ "$last_cmd" =~ ^__proj ]] && return
73
+ [[ "$last_cmd" =~ ^__git ]] && return
74
+
75
+ if [ -d ".git" ] || [ -f "package.json" ] || [ -f ".proj-track.json" ]; then
76
+ [ -f ".proj-track-disabled" ] && return
77
+ [ -f ".proj-track.json.paused" ] && return
78
+ (proj-track-logger "$last_cmd" >/dev/null 2>&1 &)
79
+ disown -a 2>/dev/null
80
+ fi
81
+ }
82
+
83
+ if [ -n "$__proj_track_old_prompt" ]; then
84
+ PROMPT_COMMAND="__proj_track_capture; \${__proj_track_old_prompt}"
85
+ else
86
+ PROMPT_COMMAND='__proj_track_capture'
87
+ fi
88
+
89
+ alias thistory='proj-track list 2>/dev/null'
90
+ alias trun='proj-track run'
91
+ alias tclear='proj-track clear'
92
+ alias tinit='proj-track init'
93
+ alias tremove='proj-track remove'
94
+ alias tpause='proj-track pause'
95
+ alias tresume='proj-track resume'
96
+ ${END_MARKER}`;
97
+ }
98
+ function getInlineZshHook() {
99
+ return `${START_MARKER}
100
+ # AUTO-CAPTURE via preexec hook (Zsh native, no DEBUG trap)
101
+
102
+ if typeset -f __proj_track_preexec >/dev/null 2>&1; then
103
+ add-zsh-hook -d preexec __proj_track_preexec 2>/dev/null
104
+ unfunction __proj_track_preexec 2>/dev/null
105
+ fi
106
+
107
+ __proj_track_preexec() {
108
+ local cmd="$1"
109
+
110
+ if ! command -v proj-track-logger >/dev/null 2>&1; then
111
+ return
112
+ fi
113
+
114
+ [ -z "$cmd" ] && return
115
+
116
+ [[ "$cmd" =~ ^proj-track ]] && return
117
+ [[ "$cmd" =~ ^__vsc ]] && return
118
+ [[ "$cmd" =~ ^__proj ]] && return
119
+ [[ "$cmd" =~ ^__git ]] && return
120
+
121
+ if [ -d ".git" ] || [ -f "package.json" ] || [ -f ".proj-track.json" ]; then
122
+ [ -f ".proj-track-disabled" ] && return
123
+ [ -f ".proj-track.json.paused" ] && return
124
+ (proj-track-logger "$cmd" >/dev/null 2>&1 &)
125
+ disown 2>/dev/null
126
+ fi
127
+ }
128
+
129
+ autoload -Uz add-zsh-hook
130
+ add-zsh-hook preexec __proj_track_preexec
131
+
132
+ alias thistory='proj-track list 2>/dev/null'
133
+ alias trun='proj-track run'
134
+ alias tclear='proj-track clear'
135
+ alias tinit='proj-track init'
136
+ alias tremove='proj-track remove'
137
+ alias tpause='proj-track pause'
138
+ alias tresume='proj-track resume'
139
+ ${END_MARKER}`;
140
+ }
141
+ /**
142
+ * Check if the hook is already installed in a config file.
143
+ */
144
+ function isInstalled(configPath) {
145
+ if (!fs.existsSync(configPath))
146
+ return false;
147
+ const content = fs.readFileSync(configPath, 'utf-8');
148
+ return content.includes(START_MARKER);
149
+ }
150
+ /**
151
+ * Remove ALL proj-track related content from a config file.
152
+ * Uses line-by-line filtering to cleanly remove everything between
153
+ * start/end markers, including any orphaned partial blocks.
154
+ */
155
+ function cleanConfig(configPath) {
156
+ if (!fs.existsSync(configPath))
157
+ return;
158
+ const content = fs.readFileSync(configPath, 'utf-8');
159
+ // Nothing to clean
160
+ if (!content.includes('proj-track'))
161
+ return;
162
+ const lines = content.split('\n');
163
+ const cleaned = [];
164
+ let insideBlock = false;
165
+ for (const line of lines) {
166
+ // Detect start of any proj-track block
167
+ if (line.includes('# --- proj-track') && line.includes('start ---')) {
168
+ insideBlock = true;
169
+ continue;
170
+ }
171
+ // Detect end of any proj-track block
172
+ if (line.includes('# --- proj-track') && line.includes('end ---')) {
173
+ insideBlock = false;
174
+ continue;
175
+ }
176
+ // Skip lines inside a block
177
+ if (insideBlock)
178
+ continue;
179
+ cleaned.push(line);
180
+ }
181
+ // Remove trailing blank lines that were left behind
182
+ while (cleaned.length > 0 && cleaned[cleaned.length - 1].trim() === '') {
183
+ cleaned.pop();
184
+ }
185
+ // Ensure file ends with a newline
186
+ const newContent = cleaned.join('\n') + '\n';
187
+ fs.writeFileSync(configPath, newContent, 'utf-8');
188
+ }
189
+ /**
190
+ * Install the shell hook into .bashrc/.zshrc.
191
+ * Returns the list of files that were modified.
192
+ */
193
+ export function installShellFunction() {
194
+ const configs = getShellConfigs();
195
+ const modified = [];
196
+ for (const configPath of configs) {
197
+ // Clean any existing proj-track content first
198
+ cleanConfig(configPath);
199
+ const shell = configPath.endsWith('.zshrc') ? 'zsh' : 'bash';
200
+ const hookContent = getHookContent(shell);
201
+ fs.appendFileSync(configPath, `\n${hookContent}\n`, 'utf-8');
202
+ modified.push(configPath);
203
+ }
204
+ return modified;
205
+ }
206
+ /**
207
+ * Uninstall the shell hook from all config files.
208
+ * Cleanly removes ALL proj-track content — no leftovers.
209
+ * Returns the list of files that were modified.
210
+ */
211
+ export function uninstallShellFunction() {
212
+ const configs = getShellConfigs();
213
+ const modified = [];
214
+ for (const configPath of configs) {
215
+ if (!fs.existsSync(configPath))
216
+ continue;
217
+ const content = fs.readFileSync(configPath, 'utf-8');
218
+ if (!content.includes('proj-track'))
219
+ continue;
220
+ cleanConfig(configPath);
221
+ modified.push(configPath);
222
+ }
223
+ return modified;
224
+ }
225
+ /**
226
+ * Check if the shell hook is installed in any config file.
227
+ */
228
+ export function isShellFunctionInstalled() {
229
+ const configs = getShellConfigs();
230
+ return configs.some(c => isInstalled(c));
231
+ }
232
+ //# sourceMappingURL=shell-installer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-installer.js","sourceRoot":"","sources":["../../src/utils/shell-installer.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "proj-track",
3
+ "version": "1.0.0",
4
+ "description": "Auto-capture CLI command history per-project with zero terminal interference.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "proj-track": "dist/index.js",
9
+ "proj-track-logger": "dist/logger.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsc --watch",
14
+ "test": "NODE_OPTIONS='--experimental-vm-modules' jest",
15
+ "test:watch": "NODE_OPTIONS='--experimental-vm-modules' jest --watch",
16
+ "lint": "tsc --noEmit",
17
+ "prepublishOnly": "npm run build",
18
+ "preuninstall": "node scripts/preuninstall.js || true",
19
+ "docs:dev": "vitepress dev docs",
20
+ "docs:build": "vitepress build docs",
21
+ "docs:preview": "vitepress preview docs"
22
+ },
23
+ "keywords": [
24
+ "cli",
25
+ "command-history",
26
+ "project-tracker",
27
+ "terminal",
28
+ "developer-tools",
29
+ "shell-history",
30
+ "bash",
31
+ "zsh",
32
+ "auto-capture",
33
+ "prompt-command",
34
+ "devtools",
35
+ "command-logger",
36
+ "project-history",
37
+ "workflow",
38
+ "productivity",
39
+ "shell-hook",
40
+ "terminal-history",
41
+ "command-tracking",
42
+ "nodejs",
43
+ "typescript"
44
+ ],
45
+ "author": {
46
+ "name": "Ali Raza",
47
+ "url": "https://github.com/Ali-Raza-Arain"
48
+ },
49
+ "homepage": "https://Ali-Raza-Arain.github.io/proj-track/",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/Ali-Raza-Arain/proj-track.git"
53
+ },
54
+ "bugs": {
55
+ "url": "https://github.com/Ali-Raza-Arain/proj-track/issues"
56
+ },
57
+ "license": "MIT",
58
+ "dependencies": {
59
+ "chalk": "^5.3.0",
60
+ "commander": "^12.1.0"
61
+ },
62
+ "devDependencies": {
63
+ "@types/jest": "^30.0.0",
64
+ "@types/node": "^22.0.0",
65
+ "jest": "^30.3.0",
66
+ "ts-jest": "^29.4.6",
67
+ "typescript": "^5.6.0",
68
+ "vitepress": "^1.6.4"
69
+ },
70
+ "files": [
71
+ "dist",
72
+ "src/hooks",
73
+ "scripts"
74
+ ],
75
+ "engines": {
76
+ "node": ">=18.0.0"
77
+ }
78
+ }
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Pre-uninstall script for proj-track.
5
+ * Runs on `npm uninstall -g proj-track` (even with sudo).
6
+ *
7
+ * Handles the sudo case: when user runs `sudo npm uninstall -g`,
8
+ * HOME=/root but the real user's shell config is in /home/<user>.
9
+ * We use SUDO_USER to find the correct home directory.
10
+ */
11
+
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ import { execSync } from 'node:child_process';
15
+
16
+ /**
17
+ * Get the real user's home directory, even when running under sudo.
18
+ */
19
+ function getRealHome() {
20
+ const sudoUser = process.env.SUDO_USER;
21
+
22
+ if (sudoUser) {
23
+ // Running under sudo — resolve real user's home
24
+ try {
25
+ const home = execSync(`eval echo ~${sudoUser}`, { encoding: 'utf-8' }).trim();
26
+ if (home && fs.existsSync(home)) {
27
+ return home;
28
+ }
29
+ } catch {
30
+ // Fall through
31
+ }
32
+
33
+ // Fallback: common Linux home path
34
+ const linuxHome = `/home/${sudoUser}`;
35
+ if (fs.existsSync(linuxHome)) {
36
+ return linuxHome;
37
+ }
38
+ }
39
+
40
+ // Not sudo — use normal HOME
41
+ return process.env.HOME || process.env.USERPROFILE || '';
42
+ }
43
+
44
+ /**
45
+ * Remove all proj-track content from a shell config file.
46
+ */
47
+ function cleanConfig(configPath) {
48
+ if (!fs.existsSync(configPath)) return false;
49
+
50
+ const content = fs.readFileSync(configPath, 'utf-8');
51
+ if (!content.includes('proj-track')) return false;
52
+
53
+ const lines = content.split('\n');
54
+ const cleaned = [];
55
+ let insideBlock = false;
56
+
57
+ for (const line of lines) {
58
+ if (line.includes('# --- proj-track') && line.includes('start ---')) {
59
+ insideBlock = true;
60
+ continue;
61
+ }
62
+ if (line.includes('# --- proj-track') && line.includes('end ---')) {
63
+ insideBlock = false;
64
+ continue;
65
+ }
66
+ if (insideBlock) continue;
67
+ cleaned.push(line);
68
+ }
69
+
70
+ // Remove trailing blank lines
71
+ while (cleaned.length > 0 && cleaned[cleaned.length - 1].trim() === '') {
72
+ cleaned.pop();
73
+ }
74
+
75
+ fs.writeFileSync(configPath, cleaned.join('\n') + '\n', 'utf-8');
76
+ return true;
77
+ }
78
+
79
+ // --- Main ---
80
+ try {
81
+ const home = getRealHome();
82
+ if (!home) process.exit(0);
83
+
84
+ const configs = [
85
+ path.join(home, '.bashrc'),
86
+ path.join(home, '.zshrc'),
87
+ ];
88
+
89
+ for (const configPath of configs) {
90
+ if (cleanConfig(configPath)) {
91
+ console.log(`proj-track: cleaned ${configPath}`);
92
+ }
93
+ }
94
+ } catch {
95
+ // Silent exit — never block npm uninstall
96
+ process.exit(0);
97
+ }
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # ============================================================
5
+ # Release Script for proj-track
6
+ # Usage: ./scripts/release.sh [patch|minor|major]
7
+ # Default: patch
8
+ # ============================================================
9
+
10
+ BUMP="${1:-patch}"
11
+
12
+ echo "==> Running lint..."
13
+ npm run lint
14
+
15
+ echo "==> Running tests with coverage..."
16
+ npm test -- --coverage
17
+ echo ""
18
+
19
+ echo "==> Building package..."
20
+ npm run build
21
+
22
+ echo "==> Building docs..."
23
+ npm run docs:build
24
+
25
+ echo "==> Bumping version ($BUMP)..."
26
+ NEW_VERSION=$(npm version "$BUMP" --no-git-tag-version | tr -d 'v')
27
+ echo " New version: $NEW_VERSION"
28
+
29
+ echo "==> Publishing to npm..."
30
+ npm publish
31
+
32
+ echo "==> Staging changes..."
33
+ git add package.json package-lock.json
34
+
35
+ echo "==> Committing..."
36
+ git commit -m "$NEW_VERSION"
37
+
38
+ echo "==> Creating annotated tag..."
39
+ git tag -a "v$NEW_VERSION" -m "v$NEW_VERSION"
40
+
41
+ echo "==> Pushing commit + tag..."
42
+ git push origin main --follow-tags
43
+
44
+ echo "==> Creating GitHub Release..."
45
+ gh release create "v$NEW_VERSION" \
46
+ --title "v$NEW_VERSION" \
47
+ --generate-notes
48
+
49
+ echo ""
50
+ echo "============================================"
51
+ echo " Released v$NEW_VERSION successfully!"
52
+ echo "============================================"
53
+ echo ""
54
+ echo "Post-release checklist:"
55
+ echo " 1. Update CHANGELOG.md with release details"
56
+ echo " 2. Verify Codecov badge updated (https://codecov.io/gh/Ali-Raza-Arain/proj-track)"
57
+ echo " 3. Verify docs deployed (https://Ali-Raza-Arain.github.io/proj-track/)"
58
+ echo " 4. Update GitHub repo 'About' description if needed"
59
+ echo ""
@@ -0,0 +1,67 @@
1
+ # --- proj-track hook start ---
2
+ # AUTO-CAPTURE via PROMPT_COMMAND (NOT DEBUG trap - doesn't break arrow keys)
3
+
4
+ # Cleanup any previous installation from shell memory first
5
+ if declare -f __proj_track_capture >/dev/null 2>&1; then
6
+ # Remove old function from PROMPT_COMMAND
7
+ PROMPT_COMMAND="${PROMPT_COMMAND//__proj_track_capture;/}"
8
+ PROMPT_COMMAND="${PROMPT_COMMAND//__proj_track_capture/}"
9
+ unset -f __proj_track_capture 2>/dev/null
10
+ fi
11
+
12
+ # Store the original PROMPT_COMMAND so we don't clobber it
13
+ __proj_track_old_prompt="${PROMPT_COMMAND:-}"
14
+
15
+ __proj_track_capture() {
16
+ # Guard: if proj-track-logger is not installed, disable self
17
+ if ! command -v proj-track-logger >/dev/null 2>&1; then
18
+ return
19
+ fi
20
+
21
+ # Get the last command from bash history (strip leading number + spaces)
22
+ local last_cmd
23
+ last_cmd=$(HISTTIMEFORMAT='' builtin history 1 | sed 's/^[ ]*[0-9]*[ ]*//')
24
+
25
+ # Skip empty
26
+ [ -z "$last_cmd" ] && return
27
+
28
+ # Skip proj-track's own commands (prevent self-tracking)
29
+ [[ "$last_cmd" =~ ^proj-track ]] && return
30
+
31
+ # Skip VS Code internal commands
32
+ [[ "$last_cmd" =~ ^__vsc ]] && return
33
+ [[ "$last_cmd" =~ ^__proj ]] && return
34
+ [[ "$last_cmd" =~ ^__git ]] && return
35
+
36
+ # Check if in a project directory (.git, package.json, or .proj-track.json)
37
+ if [ -d ".git" ] || [ -f "package.json" ] || [ -f ".proj-track.json" ]; then
38
+ # Check if permanently disabled (proj-track remove)
39
+ [ -f ".proj-track-disabled" ] && return
40
+
41
+ # Check if paused
42
+ [ -f ".proj-track.json.paused" ] && return
43
+
44
+ # Run logger in SUBSHELL with full suppression (NO job control messages)
45
+ (proj-track-logger "$last_cmd" >/dev/null 2>&1 &)
46
+
47
+ # Disown to prevent "[1]+ Done" messages
48
+ disown -a 2>/dev/null
49
+ fi
50
+ }
51
+
52
+ # Chain with existing PROMPT_COMMAND (don't clobber git prompt, etc.)
53
+ if [ -n "$__proj_track_old_prompt" ]; then
54
+ PROMPT_COMMAND="__proj_track_capture; ${__proj_track_old_prompt}"
55
+ else
56
+ PROMPT_COMMAND='__proj_track_capture'
57
+ fi
58
+
59
+ # Convenience aliases (manual commands still available)
60
+ alias thistory='proj-track list 2>/dev/null'
61
+ alias trun='proj-track run'
62
+ alias tclear='proj-track clear'
63
+ alias tinit='proj-track init'
64
+ alias tremove='proj-track remove'
65
+ alias tpause='proj-track pause'
66
+ alias tresume='proj-track resume'
67
+ # --- proj-track hook end ---
@@ -0,0 +1,54 @@
1
+ # --- proj-track hook start ---
2
+ # AUTO-CAPTURE via preexec hook (Zsh native, no DEBUG trap)
3
+
4
+ # Cleanup any previous installation from shell memory first
5
+ if typeset -f __proj_track_preexec >/dev/null 2>&1; then
6
+ add-zsh-hook -d preexec __proj_track_preexec 2>/dev/null
7
+ unfunction __proj_track_preexec 2>/dev/null
8
+ fi
9
+
10
+ __proj_track_preexec() {
11
+ local cmd="$1"
12
+
13
+ # Guard: if proj-track-logger is not installed, disable self
14
+ if ! command -v proj-track-logger >/dev/null 2>&1; then
15
+ return
16
+ fi
17
+
18
+ # Skip empty
19
+ [ -z "$cmd" ] && return
20
+
21
+ # Skip proj-track's own commands
22
+ [[ "$cmd" =~ ^proj-track ]] && return
23
+
24
+ # Skip VS Code internal commands
25
+ [[ "$cmd" =~ ^__vsc ]] && return
26
+ [[ "$cmd" =~ ^__proj ]] && return
27
+ [[ "$cmd" =~ ^__git ]] && return
28
+
29
+ # Check if in a project directory
30
+ if [ -d ".git" ] || [ -f "package.json" ] || [ -f ".proj-track.json" ]; then
31
+ # Check if permanently disabled (proj-track remove)
32
+ [ -f ".proj-track-disabled" ] && return
33
+
34
+ # Check if paused
35
+ [ -f ".proj-track.json.paused" ] && return
36
+
37
+ # Silent execution in subshell
38
+ (proj-track-logger "$cmd" >/dev/null 2>&1 &)
39
+ disown 2>/dev/null
40
+ fi
41
+ }
42
+
43
+ autoload -Uz add-zsh-hook
44
+ add-zsh-hook preexec __proj_track_preexec
45
+
46
+ # Convenience aliases
47
+ alias thistory='proj-track list 2>/dev/null'
48
+ alias trun='proj-track run'
49
+ alias tclear='proj-track clear'
50
+ alias tinit='proj-track init'
51
+ alias tremove='proj-track remove'
52
+ alias tpause='proj-track pause'
53
+ alias tresume='proj-track resume'
54
+ # --- proj-track hook end ---