greptile 2.3.0 → 3.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/package.json CHANGED
@@ -1,37 +1,68 @@
1
1
  {
2
2
  "name": "greptile",
3
- "version": "2.3.0",
4
- "description": "Bridge for Greptile code review 'Fix in Claude Code' and 'Fix in Codex' links",
3
+ "version": "3.0.0",
4
+ "type": "module",
5
+ "description": "Greptile code review from your terminal",
5
6
  "bin": {
6
- "greptile-fix": "bin/greptile-fix.js"
7
+ "greptile": "dist/greptile.js"
7
8
  },
8
9
  "scripts": {
9
- "postinstall": "bash postinstall.sh",
10
- "preuninstall": "bash preuninstall.sh"
10
+ "test": "bun test",
11
+ "build": "bun scripts/bundle.ts",
12
+ "dev": "bun run src/main.ts",
13
+ "lint": "oxlint --type-aware src --ignore-pattern '**/__tests__/**' --ignore-pattern '**/*.test.ts' --ignore-pattern '**/*.test.tsx'",
14
+ "format": "oxfmt src package.json tsconfig.json",
15
+ "prepack": "bun scripts/bundle.ts",
16
+ "prepublishOnly": "bun run typecheck && bun scripts/bundle.ts",
17
+ "typecheck": "tsc --noEmit"
18
+ },
19
+ "devDependencies": {
20
+ "@clack/prompts": "^1.4.0",
21
+ "@inkjs/ui": "^2.0.0",
22
+ "@pierre/theme": "^1.0.3",
23
+ "@types/bun": "^1.3.11",
24
+ "@types/react": "^19.2.14",
25
+ "citty": "^0.2.2",
26
+ "cli-truncate": "^6.0.0",
27
+ "ink": "^7.0.3",
28
+ "picocolors": "^1.1.1",
29
+ "react": "^19.2.6",
30
+ "shiki": "^4.1.0",
31
+ "string-width": "^8.2.1",
32
+ "strip-ansi": "^7.2.0",
33
+ "terminal-link": "^5.0.0",
34
+ "typescript": "^6.0.3",
35
+ "wrap-ansi": "^9.0.2",
36
+ "zod": "^3.25.76"
11
37
  },
12
38
  "files": [
13
- "greptile-fix",
14
- "bin/",
15
- "greptile-fix.applescript",
16
- "build-app.sh",
17
- "postinstall.sh",
18
- "preuninstall.sh",
19
- "health-server.js",
20
- "com.greptile.health.plist"
39
+ "dist/",
40
+ "README.md"
21
41
  ],
22
42
  "engines": {
23
- "node": ">=18.0.0"
43
+ "node": ">=22.0.0"
24
44
  },
25
- "os": [
26
- "darwin"
27
- ],
28
45
  "keywords": [
29
46
  "greptile",
30
47
  "code-review",
48
+ "cli",
31
49
  "claude-code",
32
- "codex",
33
- "ide"
50
+ "codex"
34
51
  ],
35
52
  "license": "MIT",
36
- "homepage": "https://greptile.com"
53
+ "homepage": "https://greptile.com",
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "git+https://github.com/greptileai/cli.git"
57
+ },
58
+ "bugs": {
59
+ "url": "https://github.com/greptileai/cli/issues"
60
+ },
61
+ "publishConfig": {
62
+ "access": "public"
63
+ },
64
+ "overrides": {
65
+ "react": "^19.2.6",
66
+ "react-dom": "^19.2.6"
67
+ }
37
68
  }
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
- const { execFileSync } = require('child_process')
3
- const path = require('path')
4
-
5
- const script = path.join(__dirname, '..', 'greptile-fix')
6
- try {
7
- execFileSync(script, process.argv.slice(2), { stdio: 'inherit' })
8
- } catch (e) {
9
- process.exit(e.status || 1)
10
- }
package/build-app.sh DELETED
@@ -1,81 +0,0 @@
1
- #!/bin/bash
2
- #
3
- # build-app.sh — Builds the Greptile Fix .app bundle from AppleScript.
4
- #
5
- # This compiles greptile-fix.applescript into a macOS .app bundle,
6
- # adds the greptile:// URL scheme to its Info.plist, and registers it
7
- # with Launch Services so macOS knows to open it for greptile:// URLs.
8
- #
9
- # Usage: ./build-app.sh [output_dir]
10
- # output_dir defaults to the current directory
11
-
12
- set -euo pipefail
13
-
14
- # Only run on macOS
15
- if [ "$(uname)" != "Darwin" ]; then
16
- echo "build-app.sh: Skipping (macOS only)"
17
- exit 0
18
- fi
19
-
20
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
21
- OUTPUT_DIR="${1:-$SCRIPT_DIR}"
22
- APP_NAME="Greptile Fix"
23
- APP_PATH="$OUTPUT_DIR/$APP_NAME.app"
24
-
25
- echo "Building $APP_NAME.app..."
26
-
27
- # Resolve the absolute path to greptile-fix at install time so the .app
28
- # works even when launched by macOS with a minimal PATH (e.g. nvm/fnm/volta users).
29
- GREPTILE_FIX_BIN="$(command -v greptile-fix 2>/dev/null || echo "")"
30
- if [ -z "$GREPTILE_FIX_BIN" ]; then
31
- # Fallback: it should be next to this script in bin/
32
- GREPTILE_FIX_BIN="$SCRIPT_DIR/bin/greptile-fix.js"
33
- fi
34
- GREPTILE_FIX_DIR="$(dirname "$GREPTILE_FIX_BIN")"
35
- echo "Resolved greptile-fix: $GREPTILE_FIX_BIN"
36
-
37
- # 1. Compile AppleScript into .app bundle — inject the resolved bin directory
38
- # into the PATH so it works regardless of node version manager.
39
- if [ -d "$APP_PATH" ]; then
40
- rm -rf "$APP_PATH"
41
- fi
42
-
43
- TEMP_SCRIPT="$(mktemp /tmp/greptile-fix-XXXXXX.applescript)"
44
- # Clean up temp file on exit (success or failure)
45
- trap 'rm -f "$TEMP_SCRIPT"' EXIT
46
-
47
- # Escape sed special characters in the directory path (& \ /)
48
- ESCAPED_DIR="$(printf '%s' "$GREPTILE_FIX_DIR" | sed 's/[&/\]/\\&/g')"
49
- sed "s|PATH=/opt/homebrew/bin:/usr/local/bin:|PATH=$ESCAPED_DIR:/opt/homebrew/bin:/usr/local/bin:|" \
50
- "$SCRIPT_DIR/greptile-fix.applescript" > "$TEMP_SCRIPT"
51
-
52
- osacompile -o "$APP_PATH" "$TEMP_SCRIPT"
53
-
54
- if [ ! -d "$APP_PATH" ]; then
55
- echo "Error: osacompile failed to create $APP_PATH"
56
- exit 1
57
- fi
58
-
59
- # 2. Add URL scheme to Info.plist
60
- PLIST="$APP_PATH/Contents/Info.plist"
61
-
62
- /usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes array" "$PLIST"
63
- /usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:0 dict" "$PLIST"
64
- /usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:0:CFBundleURLName string com.greptile.fix" "$PLIST"
65
- /usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:0:CFBundleURLSchemes array" "$PLIST"
66
- /usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string greptile" "$PLIST"
67
-
68
- # Set a bundle identifier
69
- /usr/libexec/PlistBuddy -c "Add :CFBundleIdentifier string com.greptile.fix" "$PLIST" 2>/dev/null || \
70
- /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.greptile.fix" "$PLIST"
71
-
72
- echo "URL scheme added to Info.plist"
73
-
74
- # 3. Register with Launch Services
75
- /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -R "$APP_PATH"
76
-
77
- echo "Registered with Launch Services"
78
- echo ""
79
- echo "Built: $APP_PATH"
80
- echo ""
81
- echo "To test: open 'greptile://claude-code?prompt=hello&repo=test/repo'"
@@ -1,23 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>Label</key>
6
- <string>com.greptile.health</string>
7
- <key>ProgramArguments</key>
8
- <array>
9
- <string>/usr/local/bin/node</string>
10
- <string>health-server.js</string>
11
- </array>
12
- <key>WorkingDirectory</key>
13
- <string>__PACKAGE_DIR__</string>
14
- <key>RunAtLoad</key>
15
- <true/>
16
- <key>KeepAlive</key>
17
- <true/>
18
- <key>StandardOutPath</key>
19
- <string>__HOME__/.cache/greptile/greptile-health.log</string>
20
- <key>StandardErrorPath</key>
21
- <string>__HOME__/.cache/greptile/greptile-health.log</string>
22
- </dict>
23
- </plist>
package/greptile-fix DELETED
@@ -1,238 +0,0 @@
1
- #!/bin/bash
2
- #
3
- # greptile-fix — Bridge script for "Fix in <IDE>" links.
4
- #
5
- # Receives greptile://<ide>?prompt=...&repo=... URLs from the macOS URL scheme handler,
6
- # looks up the local repo path, and opens the appropriate CLI in Terminal with the prompt.
7
- #
8
- # Supported URL paths:
9
- # greptile://claude-code?prompt=... → opens `claude` CLI
10
- # greptile://codex?prompt=... → opens `codex` CLI
11
- # greptile://fix?prompt=... → opens `claude` CLI (legacy)
12
- #
13
- # Repo path mappings are stored in ~/.greptile/repos.json.
14
- # On first use for a repo, a Finder dialog asks the user to select the local folder.
15
-
16
- set -euo pipefail
17
-
18
- # Verify python3 is available (used for URL decoding and JSON operations)
19
- if ! command -v python3 &>/dev/null; then
20
- echo "Error: python3 is required but not found in PATH." >&2
21
- osascript -e 'display dialog "Greptile Fix requires Python 3, which was not found on this system.\n\nPlease install it via: xcode-select --install" buttons {"OK"} default button "OK" with icon caution' 2>/dev/null || true
22
- exit 1
23
- fi
24
-
25
- CONFIG_DIR="$HOME/.greptile"
26
- REPOS_FILE="$CONFIG_DIR/repos.json"
27
- CACHE_DIR="$HOME/.cache/greptile"
28
- LOG_FILE="$CACHE_DIR/greptile-fix.log"
29
-
30
- log() {
31
- # Rotate log if it exceeds 1MB
32
- if [ -f "$LOG_FILE" ] && [ "$(wc -c < "$LOG_FILE" 2>/dev/null || echo 0)" -gt 1048576 ]; then
33
- mv -f "$LOG_FILE" "$LOG_FILE.old" 2>/dev/null || true
34
- fi
35
- echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
36
- }
37
-
38
- # URL-decode a string (percent-decode)
39
- urldecode() {
40
- local encoded="$1"
41
- # Use Python for reliable percent-decoding
42
- python3 -c "import urllib.parse, sys; print(urllib.parse.unquote(sys.argv[1]))" "$encoded"
43
- }
44
-
45
- # Parse query parameters from a URL
46
- parse_url_param() {
47
- local url="$1"
48
- local param_name="$2"
49
-
50
- # Extract everything after '?'
51
- local query_string="${url#*\?}"
52
- if [ "$query_string" = "$url" ]; then
53
- # No query string
54
- return
55
- fi
56
-
57
- # Split by '&' and find the parameter
58
- local IFS='&'
59
- for pair in $query_string; do
60
- local key="${pair%%=*}"
61
- local value="${pair#*=}"
62
- if [ "$key" = "$param_name" ]; then
63
- urldecode "$value"
64
- return
65
- fi
66
- done
67
- }
68
-
69
- # Ensure config and cache directories exist
70
- ensure_config_dir() {
71
- if [ ! -d "$CONFIG_DIR" ]; then
72
- mkdir -p "$CONFIG_DIR"
73
- fi
74
- if [ ! -d "$CACHE_DIR" ]; then
75
- mkdir -p "$CACHE_DIR"
76
- chmod 700 "$CACHE_DIR"
77
- fi
78
- if [ ! -f "$REPOS_FILE" ]; then
79
- echo '{}' > "$REPOS_FILE"
80
- fi
81
- }
82
-
83
- # Look up local path for a repo from repos.json
84
- lookup_repo_path() {
85
- local repo="$1"
86
- python3 -c "
87
- import json, sys, os
88
- repos_file = os.path.expanduser(sys.argv[2])
89
- try:
90
- with open(repos_file) as f:
91
- data = json.load(f)
92
- path = data.get(sys.argv[1], '')
93
- if path:
94
- print(path)
95
- except:
96
- pass
97
- " "$repo" "$REPOS_FILE"
98
- }
99
-
100
- # Save a repo path mapping
101
- save_repo_path() {
102
- local repo="$1"
103
- local path="$2"
104
- python3 -c "
105
- import json, sys, os
106
- repos_file = os.path.expanduser(sys.argv[3])
107
- try:
108
- with open(repos_file) as f:
109
- data = json.load(f)
110
- except:
111
- data = {}
112
- data[sys.argv[1]] = sys.argv[2]
113
- with open(repos_file, 'w') as f:
114
- json.dump(data, f, indent=2)
115
- " "$repo" "$path" "$REPOS_FILE"
116
- }
117
-
118
- # Ask user to select a folder via macOS Finder dialog
119
- ask_for_repo_folder() {
120
- local repo="$1"
121
- # Pass repo name safely via environment variable to avoid AppleScript injection
122
- GREPTILE_REPO_NAME="$repo" osascript -e '
123
- set repoName to system attribute "GREPTILE_REPO_NAME"
124
- set chosenFolder to choose folder with prompt ("Select the local folder for " & repoName) default location (path to home folder)
125
- return POSIX path of chosenFolder
126
- ' 2>/dev/null
127
- }
128
-
129
- # Create a self-contained runner script that opens the CLI with the prompt.
130
- # Prints the runner path to stdout so the caller (AppleScript) can execute it in Terminal.
131
- create_runner_script() {
132
- local repo_path="$1"
133
- local prompt="$2"
134
- local cli_cmd="$3"
135
-
136
- # Write args to a temp file — avoids all shell quoting issues.
137
- # Line 1: repo path, Line 2+: prompt (may be multiline)
138
- local argsfile
139
- argsfile=$(mktemp "$CACHE_DIR/greptile-fix-args-XXXXXX")
140
- printf '%s\n' "$repo_path" > "$argsfile"
141
- printf '%s' "$prompt" >> "$argsfile"
142
-
143
- # Write a runner script that reads args from the file, then cleans up
144
- local runner
145
- runner=$(mktemp "$CACHE_DIR/greptile-fix-run-XXXXXX")
146
- chmod +x "$runner"
147
-
148
- # Note: $argsfile and $cli_cmd are safe to interpolate (mktemp path and validated CLI name)
149
- cat > "$runner" <<RUNNER_EOF
150
- #!/bin/bash
151
- ARGSFILE="$argsfile"
152
- REPO_PATH=\$(head -1 "\$ARGSFILE")
153
- PROMPT=\$(tail -n +2 "\$ARGSFILE")
154
- rm -f "\$ARGSFILE"
155
- cd "\$REPO_PATH" || exit 1
156
- $cli_cmd "\$PROMPT"
157
- rm -f "\$0"
158
- RUNNER_EOF
159
-
160
- # Print the runner path — the AppleScript app will use this to open Terminal
161
- echo "$runner"
162
- }
163
-
164
- # --- Main ---
165
-
166
- log "greptile-fix invoked with: $*"
167
-
168
- if [ $# -lt 1 ]; then
169
- log "ERROR: No URL argument provided"
170
- echo "Usage: greptile-fix 'greptile://claude-code?prompt=...&repo=...'" >&2
171
- exit 1
172
- fi
173
-
174
- URL="$1"
175
-
176
- # Determine IDE from URL path (greptile://<path>?...)
177
- # Extract the path segment between :// and ?
178
- URL_PATH="${URL#*://}" # remove scheme prefix
179
- URL_PATH="${URL_PATH%%\?*}" # remove query string
180
-
181
- case "$URL_PATH" in
182
- codex) IDE="codex" ;;
183
- claude-code) IDE="claude" ;;
184
- *) IDE="claude" ;; # legacy "fix" path and fallback
185
- esac
186
-
187
- # Parse URL parameters
188
- PROMPT=$(parse_url_param "$URL" "prompt")
189
- REPO=$(parse_url_param "$URL" "repo")
190
-
191
- if [ -z "$PROMPT" ]; then
192
- log "ERROR: No prompt parameter in URL"
193
- osascript -e 'display dialog "Greptile Fix: No prompt found in the URL." buttons {"OK"} default button "OK" with icon caution'
194
- exit 1
195
- fi
196
-
197
- log "Prompt: ${PROMPT:0:100}..."
198
- log "Repo: $REPO"
199
-
200
- ensure_config_dir
201
-
202
- # Determine local repo path
203
- REPO_PATH=""
204
- if [ -n "$REPO" ]; then
205
- REPO_PATH=$(lookup_repo_path "$REPO")
206
-
207
- if [ -z "$REPO_PATH" ] || [ ! -d "$REPO_PATH" ]; then
208
- log "Repo path not found or invalid for $REPO, asking user..."
209
- REPO_PATH=$(ask_for_repo_folder "$REPO")
210
-
211
- if [ -z "$REPO_PATH" ]; then
212
- log "User cancelled folder selection"
213
- exit 0
214
- fi
215
-
216
- # Remove trailing slash if present
217
- REPO_PATH="${REPO_PATH%/}"
218
-
219
- save_repo_path "$REPO" "$REPO_PATH"
220
- log "Saved repo path: $REPO -> $REPO_PATH"
221
- fi
222
- fi
223
-
224
- # Fallback: if no repo was specified, use current directory
225
- if [ -z "$REPO_PATH" ]; then
226
- REPO_PATH="$HOME"
227
- log "No repo specified, using home directory"
228
- fi
229
-
230
- log "Creating runner script for: $REPO_PATH (IDE: $IDE)"
231
- RUNNER=$(create_runner_script "$REPO_PATH" "$PROMPT" "$IDE")
232
- log "Runner script: $RUNNER"
233
-
234
- # Print the runner path to stdout — the AppleScript app reads this
235
- # and uses it to open Terminal (which requires Automation permission
236
- # that only the .app bundle has).
237
- echo "$RUNNER"
238
- log "Done"
@@ -1,63 +0,0 @@
1
- -- greptile-fix.applescript
2
- -- URL scheme handler for greptile:// protocol.
3
- -- Compiled into a .app bundle that registers the URL scheme with macOS.
4
- --
5
- -- Flow:
6
- -- 1. macOS sends us the greptile:// URL
7
- -- 2. We call greptile-fix CLI to parse the URL, resolve the repo path, and create a runner script
8
- -- 3. greptile-fix prints the runner script path to stdout
9
- -- 4. We detect the user's terminal and execute the runner script in it
10
-
11
- on open location theURL
12
- try
13
- -- Ensure log directory exists
14
- do shell script "mkdir -p $HOME/.cache/greptile"
15
- -- Call greptile-fix to parse URL and create runner script; it prints the runner path to stdout
16
- set runnerPath to do shell script "PATH=/opt/homebrew/bin:/usr/local/bin:$PATH greptile-fix " & quoted form of theURL & " 2>> $HOME/.cache/greptile/greptile-fix.log"
17
-
18
- if runnerPath is not "" then
19
- -- Detect terminal by checking running processes via shell (no accessibility permissions needed).
20
- -- Check non-default terminals first; fall back to Terminal.app.
21
- set termApp to "Terminal"
22
- try
23
- set ps to do shell script "ps -eo comm= 2>/dev/null"
24
- if ps contains "/Ghostty.app/" then
25
- set termApp to "Ghostty"
26
- else if ps contains "/iTerm.app/" then
27
- set termApp to "iTerm"
28
- else if ps contains "/Warp.app/" then
29
- set termApp to "Warp"
30
- else if ps contains "/kitty.app/" then
31
- set termApp to "kitty"
32
- else if ps contains "/WezTerm.app/" then
33
- set termApp to "WezTerm"
34
- else if ps contains "/Alacritty.app/" then
35
- set termApp to "Alacritty"
36
- end if
37
- end try
38
-
39
- do shell script "echo 'Detected terminal: " & termApp & "' >> $HOME/.cache/greptile/greptile-fix.log"
40
-
41
- -- Terminal-specific launch commands for macOS.
42
- -- Ghostty/kitty/WezTerm/Alacritty close the window when the -e command exits,
43
- -- so we launch bash and source the runner script to keep the shell session alive
44
- -- after the IDE command (claude/codex/cursor) finishes.
45
- -- Unset CLAUDECODE so the new session doesn't think it's nested inside an existing one.
46
- set cleanEnv to "unset CLAUDECODE; source " & quoted form of runnerPath & " ; exec zsh"
47
- if termApp is "Ghostty" then
48
- do shell script "open -na Ghostty --args -e zsh -li -c " & quoted form of cleanEnv & " &>/dev/null &"
49
- else if termApp is "kitty" then
50
- do shell script "open -na kitty --args zsh -li -c " & quoted form of cleanEnv & " &>/dev/null &"
51
- else if termApp is "WezTerm" then
52
- do shell script "open -na WezTerm --args start -- zsh -li -c " & quoted form of cleanEnv & " &>/dev/null &"
53
- else if termApp is "Alacritty" then
54
- do shell script "open -na Alacritty --args -e zsh -li -c " & quoted form of cleanEnv & " &>/dev/null &"
55
- else
56
- -- Terminal.app, iTerm, and Warp natively execute scripts via "open -a"
57
- do shell script "open -a " & quoted form of termApp & " " & quoted form of runnerPath
58
- end if
59
- end if
60
- on error errMsg
61
- do shell script "echo '[ERROR] " & quoted form of errMsg & "' >> $HOME/.cache/greptile/greptile-fix.log"
62
- end try
63
- end open location