claude-jump 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.
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/bin/cc-scan.js +31 -0
- package/bin/cc-setup.js +5 -0
- package/bin/cc.js +57 -0
- package/lib/setup.js +123 -0
- package/lib/uninstall.js +33 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 achilledavid
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# claude-cc
|
|
2
|
+
|
|
3
|
+
Jump to any project (interactive menu, ranked by how often/recently you use it) and launch [Claude Code](https://claude.com/claude-code) in one command — instead of `cd`-ing there by hand every time.
|
|
4
|
+
|
|
5
|
+
Built on [`zoxide`](https://github.com/ajeetdsouza/zoxide) (recent/frequent directory tracking) and [`fzf`](https://github.com/junegunn/fzf) (fuzzy interactive menu).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
### Via npm / pnpm / bun (recommended)
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm i -g claude-jump
|
|
13
|
+
# or
|
|
14
|
+
pnpm add -g claude-jump
|
|
15
|
+
# or
|
|
16
|
+
bun add -g claude-jump
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This installs the `cc`, `cc-scan` and `cc-setup` commands globally, and runs a setup step that installs `fzf` and `zoxide` if missing (via Homebrew / apt / dnf / pacman) and adds the `zoxide` shell hook to your rc file (zsh or bash).
|
|
20
|
+
|
|
21
|
+
pnpm and bun block install scripts by default for security, so the setup step may not run automatically. If `cc` doesn't work after install, run the setup manually:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
cc-setup
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then restart your terminal, or run `source ~/.zshrc` (or `~/.bashrc`).
|
|
28
|
+
|
|
29
|
+
### Via curl (no Node.js required)
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
curl -fsSL https://raw.githubusercontent.com/achilledavid/claude-cc/main/install.sh | bash
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This installs `fzf` and `zoxide` if missing, and adds a `cc` shell function to your rc file (zsh or bash). Unlike the npm install, `cc` here is a shell function, not a binary — it can `cd` you into the project directly and keep you there after you quit Claude Code.
|
|
36
|
+
|
|
37
|
+
Want a different command name (e.g. if `cc` clashes with something on your system, like the C compiler)?
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
curl -fsSL https://raw.githubusercontent.com/achilledavid/claude-cc/main/install.sh | CC_COMMAND=ccd bash
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then restart your terminal, or run `source ~/.zshrc` (or `~/.bashrc`).
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
cc # interactive menu of your projects, launches Claude Code in the one you pick
|
|
49
|
+
cc myproject # jump straight to the best match for "myproject", no menu
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The project list starts empty and fills up naturally as you `cd` around (that's what `zoxide` tracks). To seed it immediately with your existing projects:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
cc-scan ~/Desktop ~/Documents # finds every git repo under these paths and adds it to the list
|
|
56
|
+
cc-scan # defaults to scanning the current directory
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Uninstall
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npm uninstall -g claude-jump
|
|
63
|
+
# or
|
|
64
|
+
pnpm remove -g claude-jump
|
|
65
|
+
# or
|
|
66
|
+
bun remove -g claude-jump
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This removes the `cc`, `cc-scan` and `cc-setup` commands and the `zoxide` hook block from your shell rc file. `fzf` and `zoxide` themselves are left installed since other tools may use them.
|
|
70
|
+
|
|
71
|
+
If you installed via curl instead:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
curl -fsSL https://raw.githubusercontent.com/achilledavid/claude-cc/main/uninstall.sh | bash
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## How it works
|
|
78
|
+
|
|
79
|
+
- `zoxide` maintains a frecency-ranked (frequency + recency) database of directories you visit.
|
|
80
|
+
- `cc` opens that list through `fzf` for a fuzzy interactive pick, then launches `claude` in that directory.
|
|
81
|
+
- `cc <name>` skips the menu and jumps to `zoxide`'s best match directly.
|
|
82
|
+
- **npm install**: `cc` is a real binary (`bin/cc.js`), so quitting Claude Code leaves you back where you started — it doesn't persist a `cd` in your shell.
|
|
83
|
+
- **curl install**: `cc` is a shell function sourced from `~/.claude-cc/cc.sh`, so it `cd`s your actual shell into the project and you stay there after quitting Claude Code.
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT
|
package/bin/cc-scan.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { spawnSync } = require('child_process');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
function main() {
|
|
8
|
+
const roots = process.argv.slice(2);
|
|
9
|
+
if (roots.length === 0) {
|
|
10
|
+
roots.push(process.cwd());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const find = spawnSync(
|
|
14
|
+
'find',
|
|
15
|
+
[...roots, '-maxdepth', '6', '-type', 'd', '-name', '.git', '-not', '-path', '*/node_modules/*'],
|
|
16
|
+
{ encoding: 'utf8' }
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const gitDirs = (find.stdout || '').split('\n').filter(Boolean);
|
|
20
|
+
let found = 0;
|
|
21
|
+
for (const gitDir of gitDirs) {
|
|
22
|
+
const result = spawnSync('zoxide', ['add', path.dirname(gitDir)]);
|
|
23
|
+
if (result.status === 0) {
|
|
24
|
+
found += 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log(`cc-scan: added ${found} project(s)`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main();
|
package/bin/cc-setup.js
ADDED
package/bin/cc.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { spawnSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
function commandExists(cmd) {
|
|
7
|
+
return spawnSync('which', [cmd]).status === 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function requireDeps(deps) {
|
|
11
|
+
const missing = deps.filter((d) => !commandExists(d));
|
|
12
|
+
if (missing.length > 0) {
|
|
13
|
+
console.error(`cc: missing ${missing.join(', ')}. Run: cc-setup`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function pickDirectMatch(args) {
|
|
19
|
+
const result = spawnSync('zoxide', ['query', '--', ...args], { encoding: 'utf8' });
|
|
20
|
+
const dir = (result.stdout || '').trim();
|
|
21
|
+
if (!dir) {
|
|
22
|
+
console.error(`cc: no match for '${args.join(' ')}'`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
return dir;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function pickInteractive() {
|
|
29
|
+
const list = spawnSync('zoxide', ['query', '-l'], { encoding: 'utf8' }).stdout || '';
|
|
30
|
+
requireDeps(['fzf']);
|
|
31
|
+
const fzf = spawnSync('fzf', ['--height=50%', '--reverse', '--prompt=Project > '], {
|
|
32
|
+
input: list,
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
stdio: ['pipe', 'pipe', 'inherit'],
|
|
35
|
+
});
|
|
36
|
+
return (fzf.stdout || '').trim();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function main() {
|
|
40
|
+
const args = process.argv.slice(2);
|
|
41
|
+
requireDeps(['zoxide']);
|
|
42
|
+
|
|
43
|
+
const dir = args.length > 0 ? pickDirectMatch(args) : pickInteractive();
|
|
44
|
+
if (!dir) {
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!commandExists('claude')) {
|
|
49
|
+
console.error("cc: 'claude' (Claude Code CLI) was not found in PATH.");
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const result = spawnSync('claude', [], { cwd: dir, stdio: 'inherit' });
|
|
54
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
main();
|
package/lib/setup.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { spawnSync } = require('child_process');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
const MARK_START = '# >>> claude-jump >>>';
|
|
10
|
+
const MARK_END = '# <<< claude-jump <<<';
|
|
11
|
+
|
|
12
|
+
function commandExists(cmd) {
|
|
13
|
+
return spawnSync('which', [cmd]).status === 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function installDeps() {
|
|
17
|
+
const missing = ['fzf', 'zoxide'].filter((cmd) => !commandExists(cmd));
|
|
18
|
+
if (missing.length === 0) {
|
|
19
|
+
console.log('==> fzf and zoxide already installed');
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(`==> Installing missing dependencies: ${missing.join(', ')}`);
|
|
24
|
+
const managers = [
|
|
25
|
+
{ cmd: 'brew', args: ['install', ...missing] },
|
|
26
|
+
{ cmd: 'apt-get', args: ['install', '-y', ...missing], needsUpdate: true },
|
|
27
|
+
{ cmd: 'dnf', args: ['install', '-y', ...missing] },
|
|
28
|
+
{ cmd: 'pacman', args: ['-S', '--noconfirm', ...missing] },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
for (const mgr of managers) {
|
|
32
|
+
if (commandExists(mgr.cmd)) {
|
|
33
|
+
if (mgr.needsUpdate) {
|
|
34
|
+
spawnSync('sudo', ['apt-get', 'update'], { stdio: 'inherit' });
|
|
35
|
+
}
|
|
36
|
+
const useSudo = mgr.cmd !== 'brew';
|
|
37
|
+
const result = spawnSync(useSudo ? 'sudo' : mgr.cmd, useSudo ? [mgr.cmd, ...mgr.args] : mgr.args, {
|
|
38
|
+
stdio: 'inherit',
|
|
39
|
+
});
|
|
40
|
+
return result.status === 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('!! Could not detect a supported package manager (brew/apt/dnf/pacman).');
|
|
45
|
+
console.log(' Please install manually, then re-run: cc-setup');
|
|
46
|
+
console.log(' fzf: https://github.com/junegunn/fzf#installation');
|
|
47
|
+
console.log(' zoxide: https://github.com/ajeetdsouza/zoxide#installation');
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function detectShellName() {
|
|
52
|
+
return path.basename(process.env.SHELL || 'bash');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function detectRcFile() {
|
|
56
|
+
const home = os.homedir();
|
|
57
|
+
switch (detectShellName()) {
|
|
58
|
+
case 'zsh':
|
|
59
|
+
return path.join(home, '.zshrc');
|
|
60
|
+
case 'bash':
|
|
61
|
+
return path.join(home, '.bashrc');
|
|
62
|
+
default:
|
|
63
|
+
return path.join(home, '.profile');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function addShellHook() {
|
|
68
|
+
const shellName = detectShellName();
|
|
69
|
+
const rcFile = detectRcFile();
|
|
70
|
+
|
|
71
|
+
let contents = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, 'utf8') : '';
|
|
72
|
+
|
|
73
|
+
const startIdx = contents.indexOf(MARK_START);
|
|
74
|
+
const endIdx = contents.indexOf(MARK_END);
|
|
75
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
76
|
+
contents = contents.slice(0, startIdx) + contents.slice(endIdx + MARK_END.length);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const block = `\n${MARK_START}\neval "$(zoxide init ${shellName})"\n${MARK_END}\n`;
|
|
80
|
+
fs.writeFileSync(rcFile, contents.replace(/\s*$/, '') + '\n' + block);
|
|
81
|
+
|
|
82
|
+
return rcFile;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function run() {
|
|
86
|
+
if (process.platform !== 'darwin' && process.platform !== 'linux') {
|
|
87
|
+
console.log('!! claude-jump only supports macOS and Linux.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const isGlobal = process.env.npm_config_global === 'true';
|
|
92
|
+
const forced = process.env.CLAUDE_JUMP_FORCE_SETUP === '1';
|
|
93
|
+
if (!isGlobal && !forced) {
|
|
94
|
+
console.log('==> claude-jump: skipping shell setup (not a global install).');
|
|
95
|
+
console.log(' Install globally instead, or run `cc-setup` by hand:');
|
|
96
|
+
console.log(' npm i -g claude-jump / pnpm add -g claude-jump / bun add -g claude-jump');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log('==> Setting up claude-jump');
|
|
101
|
+
installDeps();
|
|
102
|
+
|
|
103
|
+
if (!commandExists('claude')) {
|
|
104
|
+
console.log("!! Warning: 'claude' (Claude Code CLI) was not found in PATH.");
|
|
105
|
+
console.log(" claude-jump will still work, but 'cc' needs it to launch anything.");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const rcFile = addShellHook();
|
|
109
|
+
|
|
110
|
+
console.log('');
|
|
111
|
+
console.log(`==> Shell hook added to ${rcFile}`);
|
|
112
|
+
console.log('==> Restart your shell or run: source ' + rcFile);
|
|
113
|
+
console.log('==> Seed your recent projects: cc-scan ~/Desktop ~/Documents');
|
|
114
|
+
console.log('==> Use it:');
|
|
115
|
+
console.log(' cc interactive menu');
|
|
116
|
+
console.log(' cc <name> jump directly');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = { run, addShellHook, installDeps, detectRcFile, detectShellName };
|
|
120
|
+
|
|
121
|
+
if (require.main === module) {
|
|
122
|
+
run();
|
|
123
|
+
}
|
package/lib/uninstall.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { detectRcFile } = require('./setup');
|
|
6
|
+
|
|
7
|
+
const MARK_START = '# >>> claude-jump >>>';
|
|
8
|
+
const MARK_END = '# <<< claude-jump <<<';
|
|
9
|
+
|
|
10
|
+
function run() {
|
|
11
|
+
const rcFile = detectRcFile();
|
|
12
|
+
if (!fs.existsSync(rcFile)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const contents = fs.readFileSync(rcFile, 'utf8');
|
|
17
|
+
const startIdx = contents.indexOf(MARK_START);
|
|
18
|
+
const endIdx = contents.indexOf(MARK_END);
|
|
19
|
+
if (startIdx === -1 || endIdx === -1) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const updated = contents.slice(0, startIdx) + contents.slice(endIdx + MARK_END.length);
|
|
24
|
+
fs.writeFileSync(rcFile, updated);
|
|
25
|
+
console.log(`==> Removed claude-jump block from ${rcFile}`);
|
|
26
|
+
console.log('==> fzf and zoxide themselves were not uninstalled.');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { run };
|
|
30
|
+
|
|
31
|
+
if (require.main === module) {
|
|
32
|
+
run();
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-jump",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Jump to any project (interactive menu, ranked by frecency) and launch Claude Code in one command.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude",
|
|
7
|
+
"claude-code",
|
|
8
|
+
"cli",
|
|
9
|
+
"zoxide",
|
|
10
|
+
"fzf",
|
|
11
|
+
"productivity"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/achilledavid/claude-cc#readme",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/achilledavid/claude-cc.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Achille David",
|
|
20
|
+
"bin": {
|
|
21
|
+
"cc": "bin/cc.js",
|
|
22
|
+
"cc-scan": "bin/cc-scan.js",
|
|
23
|
+
"cc-setup": "bin/cc-setup.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"bin",
|
|
27
|
+
"lib"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"postinstall": "node lib/setup.js",
|
|
31
|
+
"preuninstall": "node lib/uninstall.js"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=16"
|
|
35
|
+
},
|
|
36
|
+
"os": [
|
|
37
|
+
"darwin",
|
|
38
|
+
"linux"
|
|
39
|
+
]
|
|
40
|
+
}
|