greptile 2.4.0 → 3.0.1

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.4.0",
4
- "description": "Bridge for Greptile code review 'Fix in Claude Code' and 'Fix in Codex' links",
3
+ "version": "3.0.1",
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,243 +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
- ACK_ID=$(parse_url_param "$URL" "ack")
191
-
192
- if [ -z "$PROMPT" ]; then
193
- log "ERROR: No prompt parameter in URL"
194
- osascript -e 'display dialog "Greptile Fix: No prompt found in the URL." buttons {"OK"} default button "OK" with icon caution'
195
- exit 1
196
- fi
197
-
198
- log "Prompt: ${PROMPT:0:100}..."
199
- log "Repo: $REPO"
200
-
201
- ensure_config_dir
202
-
203
- # Determine local repo path
204
- REPO_PATH=""
205
- if [ -n "$REPO" ]; then
206
- REPO_PATH=$(lookup_repo_path "$REPO")
207
-
208
- if [ -z "$REPO_PATH" ] || [ ! -d "$REPO_PATH" ]; then
209
- log "Repo path not found or invalid for $REPO, asking user..."
210
- REPO_PATH=$(ask_for_repo_folder "$REPO")
211
-
212
- if [ -z "$REPO_PATH" ]; then
213
- log "User cancelled folder selection"
214
- exit 0
215
- fi
216
-
217
- # Remove trailing slash if present
218
- REPO_PATH="${REPO_PATH%/}"
219
-
220
- save_repo_path "$REPO" "$REPO_PATH"
221
- log "Saved repo path: $REPO -> $REPO_PATH"
222
- fi
223
- fi
224
-
225
- # Fallback: if no repo was specified, use current directory
226
- if [ -z "$REPO_PATH" ]; then
227
- REPO_PATH="$HOME"
228
- log "No repo specified, using home directory"
229
- fi
230
-
231
- log "Creating runner script for: $REPO_PATH (IDE: $IDE)"
232
- RUNNER=$(create_runner_script "$REPO_PATH" "$PROMPT" "$IDE")
233
- log "Runner script: $RUNNER"
234
-
235
- # Print the runner path to stdout — the AppleScript app reads this
236
- # and uses it to open Terminal (which requires Automation permission
237
- # that only the .app bundle has).
238
- # Line 1: runner path, Line 2 (optional): ACK ID for auto-redirect
239
- echo "$RUNNER"
240
- if [ -n "$ACK_ID" ]; then
241
- echo "$ACK_ID"
242
- fi
243
- log "Done"
@@ -1,78 +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
16
- -- Output: line 1 = runner path, line 2 (optional) = ACK ID for auto-redirect
17
- set fixOutput 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"
18
-
19
- set outputLines to paragraphs of fixOutput
20
- set runnerPath to item 1 of outputLines
21
- set ackId to ""
22
- if (count of outputLines) > 1 then
23
- set ackId to item 2 of outputLines
24
- end if
25
-
26
- if runnerPath is not "" then
27
- -- Detect terminal by checking running processes via shell (no accessibility permissions needed).
28
- -- Check non-default terminals first; fall back to Terminal.app.
29
- set termApp to "Terminal"
30
- try
31
- set ps to do shell script "ps -eo comm= 2>/dev/null"
32
- if ps contains "/Ghostty.app/" then
33
- set termApp to "Ghostty"
34
- else if ps contains "/iTerm.app/" then
35
- set termApp to "iTerm"
36
- else if ps contains "/Warp.app/" then
37
- set termApp to "Warp"
38
- else if ps contains "/kitty.app/" then
39
- set termApp to "kitty"
40
- else if ps contains "/WezTerm.app/" then
41
- set termApp to "WezTerm"
42
- else if ps contains "/Alacritty.app/" then
43
- set termApp to "Alacritty"
44
- end if
45
- end try
46
-
47
- do shell script "echo 'Detected terminal: " & termApp & "' >> $HOME/.cache/greptile/greptile-fix.log"
48
-
49
- -- Terminal-specific launch commands for macOS.
50
- -- Ghostty/kitty/WezTerm/Alacritty close the window when the -e command exits,
51
- -- so we launch bash and source the runner script to keep the shell session alive
52
- -- after the IDE command (claude/codex/cursor) finishes.
53
- -- Unset CLAUDECODE so the new session doesn't think it's nested inside an existing one.
54
- set cleanEnv to "unset CLAUDECODE; source " & quoted form of runnerPath & " ; exec zsh"
55
- if termApp is "Ghostty" then
56
- do shell script "open -na Ghostty --args -e zsh -li -c " & quoted form of cleanEnv & " &>/dev/null &"
57
- else if termApp is "kitty" then
58
- do shell script "open -na kitty --args zsh -li -c " & quoted form of cleanEnv & " &>/dev/null &"
59
- else if termApp is "WezTerm" then
60
- do shell script "open -na WezTerm --args start -- zsh -li -c " & quoted form of cleanEnv & " &>/dev/null &"
61
- else if termApp is "Alacritty" then
62
- do shell script "open -na Alacritty --args -e zsh -li -c " & quoted form of cleanEnv & " &>/dev/null &"
63
- else
64
- -- Terminal.app, iTerm, and Warp natively execute scripts via "open -a"
65
- do shell script "open -a " & quoted form of termApp & " " & quoted form of runnerPath
66
- end if
67
-
68
- -- Send ACK to health server so the web page can auto-redirect back to the PR
69
- if ackId is not "" then
70
- try
71
- do shell script "curl -s -X POST http://127.0.0.1:4747/ack/" & quoted form of ackId & " --max-time 2 &>/dev/null &"
72
- end try
73
- end if
74
- end if
75
- on error errMsg
76
- do shell script "echo '[ERROR] " & quoted form of errMsg & "' >> $HOME/.cache/greptile/greptile-fix.log"
77
- end try
78
- end open location