quickclaude 1.0.8 → 1.1.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.
@@ -2,8 +2,8 @@ name: Publish to npm
2
2
 
3
3
  on:
4
4
  push:
5
- tags:
6
- - "v*"
5
+ branches:
6
+ - main
7
7
 
8
8
  jobs:
9
9
  publish:
@@ -12,11 +12,14 @@ jobs:
12
12
  contents: read
13
13
  id-token: write
14
14
  steps:
15
- - uses: actions/checkout@v4
16
- - uses: actions/setup-node@v4
15
+ - uses: actions/checkout@v5
16
+ - uses: actions/setup-node@v5
17
17
  with:
18
18
  node-version: 22
19
+ registry-url: https://registry.npmjs.org
19
20
  - run: npm install -g npm@latest
20
21
  - run: npm --version
21
22
  - run: npm ci
22
23
  - run: npm publish --provenance --access public
24
+ env:
25
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/CLAUDE.md ADDED
@@ -0,0 +1,27 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ quickclaude is a CLI launcher for Claude Code. It scans `~/.claude/projects/` for project directories, presents an interactive selection menu, and spawns `claude` in the chosen directory.
8
+
9
+ ## Commands
10
+
11
+ - **Run**: `node bin/quickclaude.js` or `npm start`
12
+ - **Install globally (for testing)**: `npm install -g .`
13
+ - **No build step or test suite** — this is a single-file Node.js CLI tool
14
+
15
+ ## Architecture
16
+
17
+ The entire tool lives in `bin/quickclaude.js` (ESM, `#!/usr/bin/env node`):
18
+
19
+ - **`resolvePath(encoded)`** — Converts Claude's encoded project directory names (e.g., `-Users-dongwoo-projects-my-app`) back to real filesystem paths. Uses greedy longest-match against the filesystem, trying both `-` and space as segment joiners. Handles Windows drive letters (`C--Users-...`).
20
+ - **`getProjects()`** — Reads `~/.claude/projects/`, resolves each subdirectory name via `resolvePath`, filters out worktree dirs and non-existent paths.
21
+ - **`main()`** — Uses `@clack/prompts` for the interactive select UI, then spawns `claude` with `stdio: "inherit"` in the selected directory.
22
+
23
+ ## Key Details
24
+
25
+ - Single dependency: `@clack/prompts` for terminal UI
26
+ - Published to npm as `quickclaude` (global install / npx)
27
+ - Requires Node.js 18+ and Claude Code installed
package/README.md CHANGED
@@ -29,6 +29,11 @@ Or run without installing:
29
29
  npx quickclaude
30
30
  ```
31
31
 
32
+ ## Update
33
+ ```bash
34
+ npm update -g quickclaude
35
+ ```
36
+
32
37
  ## How it works
33
38
 
34
39
  1. Scans `~/.claude/projects/` for Claude Code project directories
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import * as p from "@clack/prompts";
4
- import { readdirSync, existsSync } from "fs";
4
+ import { readdirSync, existsSync, statSync } from "fs";
5
5
  import { join, sep } from "path";
6
6
  import { homedir } from "os";
7
7
  import { execSync, spawn } from "child_process";
@@ -68,13 +68,33 @@ function getProjects() {
68
68
  if (p.path.includes(`claude${sep}worktrees`)) return false;
69
69
  return existsSync(p.path);
70
70
  })
71
- .sort((a, b) => a.path.localeCompare(b.path));
71
+ .map((p) => {
72
+ const projectDir = join(CLAUDE_PROJECTS_DIR, p.dirName);
73
+ const mtime = statSync(projectDir).mtimeMs;
74
+ return { ...p, mtime };
75
+ })
76
+ .sort((a, b) => b.mtime - a.mtime);
77
+ }
78
+
79
+ function timeAgo(mtimeMs) {
80
+ const seconds = Math.floor((Date.now() - mtimeMs) / 1000);
81
+ if (seconds < 60) return "just now";
82
+ const minutes = Math.floor(seconds / 60);
83
+ if (minutes < 60) return `${minutes}m ago`;
84
+ const hours = Math.floor(minutes / 60);
85
+ if (hours < 24) return `${hours}h ago`;
86
+ const days = Math.floor(hours / 24);
87
+ if (days < 7) return `${days}d ago`;
88
+ const weeks = Math.floor(days / 7);
89
+ if (weeks < 4) return `${weeks}w ago`;
90
+ const months = Math.floor(days / 30);
91
+ return `${months}mo ago`;
72
92
  }
73
93
 
74
- function getProjectLabel(path) {
94
+ function getProjectLabel(path, mtimeMs) {
75
95
  const home = homedir();
76
96
  const display = path.startsWith(home) ? "~" + path.slice(home.length) : path;
77
- return display;
97
+ return `${timeAgo(mtimeMs)} · ${display}`;
78
98
  }
79
99
 
80
100
  async function main() {
@@ -91,7 +111,7 @@ async function main() {
91
111
  message: "Select a project",
92
112
  options: projects.map((proj) => ({
93
113
  value: proj.path,
94
- label: getProjectLabel(proj.path),
114
+ label: getProjectLabel(proj.path, proj.mtime),
95
115
  })),
96
116
  });
97
117
 
@@ -100,10 +120,12 @@ async function main() {
100
120
  process.exit(0);
101
121
  }
102
122
 
123
+ const args = process.argv.slice(2);
124
+
103
125
  p.outro(`Launching Claude in ${selected}`);
104
126
 
105
127
  // claude 실행 (현재 터미널에서 interactive하게)
106
- const child = spawn("claude", [], {
128
+ const child = spawn("claude", args, {
107
129
  cwd: selected,
108
130
  stdio: "inherit",
109
131
  shell: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quickclaude",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Quickly launch Claude Code in your project directories",
5
5
  "type": "module",
6
6
  "bin": {