gitreviewpilot 0.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.
- package/LICENSE +22 -0
- package/README.md +234 -0
- package/dist/commands/ask.d.ts +3 -0
- package/dist/commands/ask.d.ts.map +1 -0
- package/dist/commands/ask.js +32 -0
- package/dist/commands/ask.js.map +1 -0
- package/dist/commands/changes.d.ts +3 -0
- package/dist/commands/changes.d.ts.map +1 -0
- package/dist/commands/changes.js +57 -0
- package/dist/commands/changes.js.map +1 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +86 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/pr.d.ts +3 -0
- package/dist/commands/pr.d.ts.map +1 -0
- package/dist/commands/pr.js +53 -0
- package/dist/commands/pr.js.map +1 -0
- package/dist/commands/review.d.ts +3 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +45 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/version.d.ts +3 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +16 -0
- package/dist/commands/version.js.map +1 -0
- package/dist/config/env.d.ts +3 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +20 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/gitreviewpilotConfig.d.ts +16 -0
- package/dist/config/gitreviewpilotConfig.d.ts.map +1 -0
- package/dist/config/gitreviewpilotConfig.js +78 -0
- package/dist/config/gitreviewpilotConfig.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/services/ai.service.d.ts +60 -0
- package/dist/services/ai.service.d.ts.map +1 -0
- package/dist/services/ai.service.js +396 -0
- package/dist/services/ai.service.js.map +1 -0
- package/dist/services/askService.d.ts +12 -0
- package/dist/services/askService.d.ts.map +1 -0
- package/dist/services/askService.js +68 -0
- package/dist/services/askService.js.map +1 -0
- package/dist/services/git.service.d.ts +12 -0
- package/dist/services/git.service.d.ts.map +1 -0
- package/dist/services/git.service.js +61 -0
- package/dist/services/git.service.js.map +1 -0
- package/dist/services/github.service.d.ts +46 -0
- package/dist/services/github.service.d.ts.map +1 -0
- package/dist/services/github.service.js +169 -0
- package/dist/services/github.service.js.map +1 -0
- package/dist/services/prReviewService.d.ts +29 -0
- package/dist/services/prReviewService.d.ts.map +1 -0
- package/dist/services/prReviewService.js +107 -0
- package/dist/services/prReviewService.js.map +1 -0
- package/dist/services/prService.d.ts +12 -0
- package/dist/services/prService.d.ts.map +1 -0
- package/dist/services/prService.js +8 -0
- package/dist/services/prService.js.map +1 -0
- package/dist/services/reviewErrors.d.ts +7 -0
- package/dist/services/reviewErrors.d.ts.map +1 -0
- package/dist/services/reviewErrors.js +13 -0
- package/dist/services/reviewErrors.js.map +1 -0
- package/dist/services/reviewService.d.ts +16 -0
- package/dist/services/reviewService.d.ts.map +1 -0
- package/dist/services/reviewService.js +109 -0
- package/dist/services/reviewService.js.map +1 -0
- package/dist/utils/cli.d.ts +16 -0
- package/dist/utils/cli.d.ts.map +1 -0
- package/dist/utils/cli.js +108 -0
- package/dist/utils/cli.js.map +1 -0
- package/dist/utils/diff.d.ts +21 -0
- package/dist/utils/diff.d.ts.map +1 -0
- package/dist/utils/diff.js +105 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/formatter.d.ts +11 -0
- package/dist/utils/formatter.d.ts.map +1 -0
- package/dist/utils/formatter.js +186 -0
- package/dist/utils/formatter.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +35 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/repoContext.d.ts +26 -0
- package/dist/utils/repoContext.d.ts.map +1 -0
- package/dist/utils/repoContext.js +136 -0
- package/dist/utils/repoContext.js.map +1 -0
- package/dist/utils/spinner.d.ts +3 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +5 -0
- package/dist/utils/spinner.js.map +1 -0
- package/dist/utils/structuredReview.d.ts +21 -0
- package/dist/utils/structuredReview.d.ts.map +1 -0
- package/dist/utils/structuredReview.js +59 -0
- package/dist/utils/structuredReview.js.map +1 -0
- package/gitreviewpilot.config.json +3 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# GitReviewPilot
|
|
2
|
+
|
|
3
|
+
**Fast, structured AI reviews** for your code and PRs — with a focused, token-friendly CLI.
|
|
4
|
+
|
|
5
|
+
Use it for:
|
|
6
|
+
|
|
7
|
+
- Local files (`gitreviewpilot review`)
|
|
8
|
+
- GitHub PR diffs (`gitreviewpilot pr`)
|
|
9
|
+
- Your local branch changes vs upstream (`gitreviewpilot changes`)
|
|
10
|
+
- Questions about your codebase (`gitreviewpilot ask`)
|
|
11
|
+
|
|
12
|
+
It minimizes diffs (keeps only changed lines) to reduce tokens and keep reviews focused.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
### From npm (recommended)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm i -g gitreviewpilot
|
|
20
|
+
gitreviewpilot --help
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or run without installing globally:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx gitreviewpilot --help
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gitreviewpilot review README.md
|
|
33
|
+
gitreviewpilot pr 123 --repo vercel/next.js
|
|
34
|
+
gitreviewpilot changes
|
|
35
|
+
gitreviewpilot ask "Where is config loaded?"
|
|
36
|
+
gitreviewpilot version
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## How to use
|
|
40
|
+
|
|
41
|
+
### Set env (required for AI)
|
|
42
|
+
|
|
43
|
+
Pick one LLM provider and set its key (details are in this README under **LLM Provider**). Example (OpenAI-compatible):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
export GITREVIEWPILOT_LLM_PROVIDER=openai-compatible
|
|
47
|
+
export GITREVIEWPILOT_LLM_BASE_URL=https://api.openai.com
|
|
48
|
+
export GITREVIEWPILOT_LLM_API_KEY=...
|
|
49
|
+
export GITREVIEWPILOT_LLM_MODEL=gpt-4o-mini
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
If you want `gitreviewpilot pr`, also set:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
export GITREVIEWPILOT_GITHUB_TOKEN=...
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Run commands
|
|
59
|
+
|
|
60
|
+
#### Review a file
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
gitreviewpilot review README.md
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### Review a GitHub PR
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
gitreviewpilot pr 123 --repo vercel/next.js
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### Review local branch changes vs upstream
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
gitreviewpilot changes
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Ask a question about the repo
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
gitreviewpilot ask "Where is config loaded?"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Print version
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
gitreviewpilot version
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Optional: run before every push
|
|
91
|
+
|
|
92
|
+
Inside a git repo:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
gitreviewpilot install
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
That installs a `pre-push` hook that runs `gitreviewpilot changes` before pushing.
|
|
99
|
+
|
|
100
|
+
## Commands
|
|
101
|
+
|
|
102
|
+
### `review <file>`
|
|
103
|
+
|
|
104
|
+
Review a local file and print a structured report.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
gitreviewpilot review src/index.ts
|
|
108
|
+
gitreviewpilot review src/index.ts --max-chars 8000
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `pr <number> --repo <owner/repo>`
|
|
112
|
+
|
|
113
|
+
Fetch the PR diff from GitHub, minimize it, then generate a structured AI review.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
gitreviewpilot pr 123 --repo vercel/next.js
|
|
117
|
+
gitreviewpilot pr 123 --repo owner/repo --max-chars 8000
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `changes`
|
|
121
|
+
|
|
122
|
+
Review your local changes vs the configured upstream branch.
|
|
123
|
+
|
|
124
|
+
Under the hood it runs:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
git diff --unified=0 @{u}...HEAD
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
gitreviewpilot changes
|
|
134
|
+
gitreviewpilot changes --max-chars 8000
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
If you don’t have an upstream branch set, configure one (example):
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git branch --set-upstream-to origin/main
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `ask "<question>"`
|
|
144
|
+
|
|
145
|
+
Ask a question about your local codebase (GitReviewPilot scans a limited subset of files for context).
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
gitreviewpilot ask "How does auth work?"
|
|
149
|
+
gitreviewpilot ask "Where is the database client created?"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### `install` (optional)
|
|
153
|
+
|
|
154
|
+
Installs a git `pre-push` hook that runs `gitreviewpilot changes` before pushing.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
gitreviewpilot install
|
|
158
|
+
gitreviewpilot install --force
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
This writes `.git/hooks/pre-push` to run:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
node ./dist/index.js changes
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Configuration
|
|
168
|
+
|
|
169
|
+
Optional: add `gitreviewpilot.config.json` at the project root (for example to customize `focus` areas for prompts).
|
|
170
|
+
|
|
171
|
+
### Logging
|
|
172
|
+
|
|
173
|
+
- `GITREVIEWPILOT_LOG_LEVEL`: `debug` | `info` | `warn` | `error` (default: `info`)
|
|
174
|
+
|
|
175
|
+
### GitHub (for `gitreviewpilot pr`)
|
|
176
|
+
|
|
177
|
+
- `GITREVIEWPILOT_GITHUB_TOKEN` (recommended) or `GITHUB_TOKEN`
|
|
178
|
+
|
|
179
|
+
### LLM Provider (required for AI)
|
|
180
|
+
|
|
181
|
+
GitReviewPilot supports multiple providers. Pick one.
|
|
182
|
+
|
|
183
|
+
#### OpenAI / OpenAI-compatible (default)
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
export GITREVIEWPILOT_LLM_PROVIDER=openai-compatible
|
|
187
|
+
export GITREVIEWPILOT_LLM_BASE_URL=https://api.openai.com
|
|
188
|
+
export GITREVIEWPILOT_LLM_API_KEY=...
|
|
189
|
+
export GITREVIEWPILOT_LLM_MODEL=gpt-4o-mini
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Back-compat env vars also work: `OPENAI_BASE_URL`, `OPENAI_API_KEY`, `OPENAI_MODEL`.
|
|
193
|
+
|
|
194
|
+
#### Gemini
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
export GITREVIEWPILOT_LLM_PROVIDER=gemini
|
|
198
|
+
export GITREVIEWPILOT_LLM_API_KEY=...
|
|
199
|
+
export GITREVIEWPILOT_LLM_MODEL=gemini-2.0-flash
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### Anthropic
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
export GITREVIEWPILOT_LLM_PROVIDER=anthropic
|
|
206
|
+
export GITREVIEWPILOT_LLM_API_KEY=...
|
|
207
|
+
export GITREVIEWPILOT_LLM_MODEL=claude-3-5-sonnet-latest
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Privacy & security notes
|
|
211
|
+
|
|
212
|
+
- GitReviewPilot sends the content it is reviewing (files/diffs and related context) to the configured LLM provider.
|
|
213
|
+
- **Do not** run it on proprietary code if you’re not allowed to share that code with your chosen provider.
|
|
214
|
+
- API keys/tokens are read from environment variables (for example `GITREVIEWPILOT_LLM_API_KEY`, `GITREVIEWPILOT_GITHUB_TOKEN`). Don’t commit them to git.
|
|
215
|
+
|
|
216
|
+
## Development (this repo)
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npm install
|
|
220
|
+
npm run build
|
|
221
|
+
node dist/index.js --help
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Optional (for a `gitreviewpilot` command in your shell while developing):
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npm link
|
|
228
|
+
gitreviewpilot --help
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Requirements
|
|
232
|
+
|
|
233
|
+
- Node.js `>= 20`
|
|
234
|
+
- Git (for `gitreviewpilot changes` and `gitreviewpilot install`)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4BzD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { createSpinner } from "../utils/spinner.js";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
import { askQuestion } from "../services/askService.js";
|
|
5
|
+
export function registerAskCommand(program) {
|
|
6
|
+
program
|
|
7
|
+
.command("ask")
|
|
8
|
+
.argument("<question>", "Question to ask about the codebase")
|
|
9
|
+
.description("Ask a question about the local codebase (reads a limited set of files for context).")
|
|
10
|
+
.addHelpText("after", "\nExamples:\n gitreviewpilot ask \"How does auth work?\"\n gitreviewpilot ask \"Where is the database client created?\"\n")
|
|
11
|
+
.action(async (question) => {
|
|
12
|
+
logger.info(`Question: ${chalk.bold(question)}`);
|
|
13
|
+
const spinner = createSpinner("Thinking...").start();
|
|
14
|
+
try {
|
|
15
|
+
const result = await askQuestion(question);
|
|
16
|
+
spinner.succeed(chalk.green("Answer ready"));
|
|
17
|
+
logger.info(result.answer.trim());
|
|
18
|
+
if (result.includedFiles.length) {
|
|
19
|
+
logger.info("");
|
|
20
|
+
logger.info(chalk.dim(`Context files (${result.includedFiles.length}):`));
|
|
21
|
+
for (const f of result.includedFiles)
|
|
22
|
+
logger.info(chalk.dim(`- ${f}`));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
spinner.fail(chalk.red("Ask failed"));
|
|
27
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=ask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.js","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAExD,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CAAC,YAAY,EAAE,oCAAoC,CAAC;SAC5D,WAAW,CAAC,qFAAqF,CAAC;SAClG,WAAW,CACV,OAAO,EACP,6HAA6H,CAC9H;SACA,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;QACjC,MAAM,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,aAAa,CAAC,aAAa,CAAC,CAAC,KAAK,EAAE,CAAC;QAErD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;YAC3C,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAClC,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;gBAChC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAChB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,aAAa,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;gBAC1E,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,aAAa;oBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC/D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../../src/commands/changes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoD7D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { createSpinner } from "../utils/spinner.js";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
import { unifiedDiffAgainstUpstream } from "../services/git.service.js";
|
|
5
|
+
import { parseUnifiedDiff, toMinimizedDiff, truncateToChars } from "../utils/diff.js";
|
|
6
|
+
import { analyze, buildPRReviewPrompt, LlmApiError } from "../services/ai.service.js";
|
|
7
|
+
import { formatAiReview } from "../utils/formatter.js";
|
|
8
|
+
import { parsePositiveIntOption } from "../utils/cli.js";
|
|
9
|
+
export function registerChangesCommand(program) {
|
|
10
|
+
program
|
|
11
|
+
.command("changes")
|
|
12
|
+
.description("Review local changes vs upstream (git diff @{u}...HEAD) using minimized diff.")
|
|
13
|
+
.option("--max-chars <n>", "Max characters of minimized diff to send (default: 12000)", parsePositiveIntOption)
|
|
14
|
+
.addHelpText("after", "\nExamples:\n gitreviewpilot changes\n gitreviewpilot changes --max-chars 8000\n\nNotes:\n - Requires an upstream branch (e.g. origin/main).\n - Uses `git diff --unified=0 @{u}...HEAD` under the hood.\n")
|
|
15
|
+
.action(async (opts) => {
|
|
16
|
+
const spinner = createSpinner("Collecting git diff and generating review...").start();
|
|
17
|
+
try {
|
|
18
|
+
const maxChars = opts.maxChars ?? 12_000;
|
|
19
|
+
const diff = await unifiedDiffAgainstUpstream();
|
|
20
|
+
const files = parseUnifiedDiff(diff);
|
|
21
|
+
const minimized = toMinimizedDiff(files).trim();
|
|
22
|
+
if (!minimized) {
|
|
23
|
+
spinner.fail(chalk.yellow("No textual changes detected"));
|
|
24
|
+
logger.info("No textual hunks to review in `git diff @{u}...HEAD`.");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const { text, truncated } = truncateToChars(minimized, maxChars);
|
|
28
|
+
const minimizedForAi = truncated
|
|
29
|
+
? `${text}\n\n[Note: diff was truncated to ${maxChars.toLocaleString()} characters for analysis.]`
|
|
30
|
+
: text;
|
|
31
|
+
const prompt = buildPRReviewPrompt(minimizedForAi);
|
|
32
|
+
const aiRaw = await analyze(prompt);
|
|
33
|
+
spinner.succeed(chalk.green("Review complete"));
|
|
34
|
+
const changedFiles = files
|
|
35
|
+
.map((f) => f.newPath ?? f.oldPath ?? "(unknown file)")
|
|
36
|
+
.filter((v, i, arr) => Boolean(v) && arr.indexOf(v) === i);
|
|
37
|
+
if (changedFiles.length) {
|
|
38
|
+
console.log(chalk.bold("Files changed:"));
|
|
39
|
+
for (const f of changedFiles)
|
|
40
|
+
console.log(`- ${f}`);
|
|
41
|
+
console.log("");
|
|
42
|
+
}
|
|
43
|
+
console.log(formatAiReview(aiRaw));
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
spinner.fail(chalk.red("Review failed"));
|
|
47
|
+
if (err instanceof LlmApiError && err.retryAfterMs !== undefined) {
|
|
48
|
+
logger.error(`${err.message} Retry after ${Math.ceil(err.retryAfterMs / 1000)}s.`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=changes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changes.js","sourceRoot":"","sources":["../../src/commands/changes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoDpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyC7D"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createSpinner } from "../utils/spinner.js";
|
|
5
|
+
import { CliError } from "../utils/cli.js";
|
|
6
|
+
import { ensureGitRepo } from "../services/git.service.js";
|
|
7
|
+
async function fileExists(p) {
|
|
8
|
+
try {
|
|
9
|
+
const st = await stat(p);
|
|
10
|
+
return st.isFile();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function hookScript() {
|
|
17
|
+
// A Node-based hook is the most portable approach across macOS/Linux/Windows git-bash.
|
|
18
|
+
// In "dev-only" usage (running from a cloned repo), we prefer `node ./dist/index.js changes`.
|
|
19
|
+
// If `dist/index.js` doesn't exist, we fall back to `npx gitreviewpilot changes` for published installs.
|
|
20
|
+
return [
|
|
21
|
+
"#!/usr/bin/env node",
|
|
22
|
+
"/* GitReviewPilot pre-push hook (generated). */",
|
|
23
|
+
"import { spawnSync } from 'node:child_process';",
|
|
24
|
+
"import { existsSync } from 'node:fs';",
|
|
25
|
+
"",
|
|
26
|
+
"const localEntry = './dist/index.js';",
|
|
27
|
+
"const useLocal = existsSync(localEntry);",
|
|
28
|
+
"",
|
|
29
|
+
"let r;",
|
|
30
|
+
"if (useLocal) {",
|
|
31
|
+
" r = spawnSync(process.execPath, [localEntry, 'changes'], { stdio: 'inherit' });",
|
|
32
|
+
"} else {",
|
|
33
|
+
" const cmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';",
|
|
34
|
+
" const args = ['-y', 'gitreviewpilot', 'changes'];",
|
|
35
|
+
" r = spawnSync(cmd, args, { stdio: 'inherit' });",
|
|
36
|
+
"}",
|
|
37
|
+
"process.exitCode = r.status ?? 1;",
|
|
38
|
+
""
|
|
39
|
+
].join("\n");
|
|
40
|
+
}
|
|
41
|
+
function hookLooksLikeOurs(existing) {
|
|
42
|
+
return (existing.includes("GitReviewPilot pre-push hook") ||
|
|
43
|
+
existing.includes("gitreviewpilot changes") ||
|
|
44
|
+
existing.includes("PRlens pre-push hook") ||
|
|
45
|
+
existing.includes("prlens changes"));
|
|
46
|
+
}
|
|
47
|
+
export function registerInstallCommand(program) {
|
|
48
|
+
program
|
|
49
|
+
.command("install")
|
|
50
|
+
.description("Install a git pre-push hook that runs `gitreviewpilot changes` before pushing.")
|
|
51
|
+
.option("--force", "Overwrite an existing pre-push hook")
|
|
52
|
+
.addHelpText("after", "\nExamples:\n gitreviewpilot install\n gitreviewpilot install --force\n\nWhat it does:\n - Writes `.git/hooks/pre-push` to run `node ./dist/index.js changes` when available.\n - Falls back to `npx -y gitreviewpilot changes` if `dist/` isn't present.\n")
|
|
53
|
+
.action(async (opts) => {
|
|
54
|
+
const spinner = createSpinner("Installing git pre-push hook...").start();
|
|
55
|
+
try {
|
|
56
|
+
await ensureGitRepo();
|
|
57
|
+
const hooksDir = path.join(process.cwd(), ".git", "hooks");
|
|
58
|
+
await mkdir(hooksDir, { recursive: true });
|
|
59
|
+
const hookPath = path.join(hooksDir, "pre-push");
|
|
60
|
+
if (!opts.force && (await fileExists(hookPath))) {
|
|
61
|
+
const existing = await readFile(hookPath, "utf8").catch(() => "");
|
|
62
|
+
const already = hookLooksLikeOurs(existing);
|
|
63
|
+
spinner.fail(chalk.yellow("Hook already exists"));
|
|
64
|
+
if (already) {
|
|
65
|
+
console.log(chalk.dim(`Existing hook already looks like GitReviewPilot. If you want to rewrite it, run: gitreviewpilot install --force`));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.log(chalk.dim(`A pre-push hook already exists at ${hookPath}. Use --force to overwrite.`));
|
|
69
|
+
}
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
await writeFile(hookPath, hookScript(), { encoding: "utf8" });
|
|
74
|
+
spinner.succeed(chalk.green("Installed pre-push hook"));
|
|
75
|
+
console.log(chalk.dim(`Hook path: ${hookPath}`));
|
|
76
|
+
console.log(chalk.dim("Next: run `gitreviewpilot changes` once to ensure your upstream branch is configured."));
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
spinner.fail(chalk.red("Install failed"));
|
|
80
|
+
if (err instanceof CliError)
|
|
81
|
+
throw err;
|
|
82
|
+
throw new CliError("HOOK_INSTALL_FAILED", err instanceof Error ? err.message : String(err), undefined, err);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../src/commands/pr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsDxD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { createSpinner } from "../utils/spinner.js";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
import { reviewPullRequest, PrReviewError } from "../services/prReviewService.js";
|
|
5
|
+
import { formatAiReview } from "../utils/formatter.js";
|
|
6
|
+
import { parsePositiveIntOption, parseRepoRefArg } from "../utils/cli.js";
|
|
7
|
+
export function registerPrCommand(program) {
|
|
8
|
+
program
|
|
9
|
+
.command("pr")
|
|
10
|
+
.argument("<prNumber>", "Pull request number to inspect")
|
|
11
|
+
.requiredOption("--repo <owner/repo>", "GitHub repository (e.g. vercel/next.js)", parseRepoRefArg)
|
|
12
|
+
.option("--max-chars <n>", "Max characters of minimized diff to send (default: 12000)", parsePositiveIntOption)
|
|
13
|
+
.description("Fetch PR diff from GitHub, extract only changed lines, and generate a structured AI review.")
|
|
14
|
+
.addHelpText("after", "\nExamples:\n gitreviewpilot pr 123 --repo vercel/next.js\n gitreviewpilot pr 123 --repo owner/repo --max-chars 8000\n\nTips:\n - Set `GITREVIEWPILOT_GITHUB_TOKEN` to avoid rate limits.\n")
|
|
15
|
+
.action(async (prNumberRaw, opts) => {
|
|
16
|
+
logger.info(`PR review requested for ${chalk.bold(opts.repo)}#${chalk.bold(prNumberRaw)}`);
|
|
17
|
+
const spinner = createSpinner("Fetching PR diff and generating review...").start();
|
|
18
|
+
try {
|
|
19
|
+
const result = await reviewPullRequest(opts.repo, prNumberRaw, opts.maxChars === undefined ? {} : { maxChars: opts.maxChars });
|
|
20
|
+
spinner.succeed(chalk.green("Review complete"));
|
|
21
|
+
const metaBits = [
|
|
22
|
+
chalk.bold("PR:"),
|
|
23
|
+
`${result.repo.owner}/${result.repo.repo}#${result.pr.number}`,
|
|
24
|
+
chalk.bold(result.pr.title),
|
|
25
|
+
chalk.gray(`(${result.pr.htmlUrl})`)
|
|
26
|
+
];
|
|
27
|
+
console.log(metaBits.join(" "));
|
|
28
|
+
console.log(chalk.gray(`Diff: ${result.diffChars.toLocaleString()} chars, minimized: ${result.minimizedDiffChars.toLocaleString()} chars` +
|
|
29
|
+
(result.truncated ? `, sent: ${result.maxChars.toLocaleString()} chars (truncated)` : "")));
|
|
30
|
+
if (result.changedFiles.length > 0) {
|
|
31
|
+
console.log(chalk.bold("Files changed:"));
|
|
32
|
+
for (const f of result.changedFiles)
|
|
33
|
+
console.log(`- ${f}`);
|
|
34
|
+
}
|
|
35
|
+
console.log("");
|
|
36
|
+
console.log(formatAiReview(result.aiRaw));
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
spinner.fail(chalk.red("PR review failed"));
|
|
40
|
+
if (err instanceof PrReviewError) {
|
|
41
|
+
logger.error(err.message);
|
|
42
|
+
if (process.env.GITREVIEWPILOT_LOG_LEVEL === "debug" && err.details) {
|
|
43
|
+
logger.debug(chalk.gray(err.details));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
48
|
+
}
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=pr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr.js","sourceRoot":"","sources":["../../src/commands/pr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuC5D"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { createSpinner } from "../utils/spinner.js";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
import { reviewFile } from "../services/reviewService.js";
|
|
5
|
+
import { formatAiReview } from "../utils/formatter.js";
|
|
6
|
+
import { ReviewError } from "../services/reviewErrors.js";
|
|
7
|
+
import { parsePositiveIntOption } from "../utils/cli.js";
|
|
8
|
+
export function registerReviewCommand(program) {
|
|
9
|
+
program
|
|
10
|
+
.command("review")
|
|
11
|
+
.argument("<file>", "File path to review")
|
|
12
|
+
.option("--max-chars <n>", "Max characters to send to the AI (default: 12000)", parsePositiveIntOption)
|
|
13
|
+
.description("Review a local file with AI and print a structured report.")
|
|
14
|
+
.addHelpText("after", "\nExamples:\n gitreviewpilot review README.md\n gitreviewpilot review src/index.ts --max-chars 8000\n")
|
|
15
|
+
.action(async (file, opts) => {
|
|
16
|
+
logger.info(`Review requested for ${chalk.bold(file)}`);
|
|
17
|
+
const spinner = createSpinner(`Reviewing ${file}...`).start();
|
|
18
|
+
try {
|
|
19
|
+
const result = await reviewFile(file, opts.maxChars === undefined ? {} : { maxChars: opts.maxChars });
|
|
20
|
+
spinner.succeed(chalk.green("Review complete"));
|
|
21
|
+
const headerBits = [
|
|
22
|
+
chalk.bold("File:"),
|
|
23
|
+
result.filePath,
|
|
24
|
+
chalk.gray(`(${result.bytesRead.toLocaleString()} bytes${result.truncated ? `, truncated to ${result.maxChars.toLocaleString()} chars` : ""})`)
|
|
25
|
+
];
|
|
26
|
+
console.log(headerBits.join(" "));
|
|
27
|
+
console.log("");
|
|
28
|
+
console.log(formatAiReview(result.aiRaw));
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
spinner.fail(chalk.red("Review failed"));
|
|
32
|
+
if (err instanceof ReviewError) {
|
|
33
|
+
logger.error(err.message);
|
|
34
|
+
if (process.env.GITREVIEWPILOT_LOG_LEVEL === "debug" && err.details) {
|
|
35
|
+
logger.debug(chalk.gray(err.details));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
40
|
+
}
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;SACzC,MAAM,CAAC,iBAAiB,EAAE,mDAAmD,EAAE,sBAAsB,CAAC;SACtG,WAAW,CAAC,4DAA4D,CAAC;SACzE,WAAW,CACV,OAAO,EACP,yGAAyG,CAC1G;SACA,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,IAA2B,EAAE,EAAE;QAC1D,MAAM,CAAC,IAAI,CAAC,wBAAwB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,aAAa,CAAC,aAAa,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;QAE9D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACtG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAEhD,MAAM,UAAU,GAAG;gBACjB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;gBACnB,MAAM,CAAC,QAAQ;gBACf,KAAK,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,EAAE,SAAS,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,MAAM,CAAC,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;aAChJ,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;YACzC,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;gBAC/B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,OAAO,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;oBACpE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YACjE,CAAC;YACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/commands/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB7D"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
export function registerVersionCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("version")
|
|
5
|
+
.description("Print the installed GitReviewPilot version.")
|
|
6
|
+
.addHelpText("after", "\nExamples:\n gitreviewpilot version\n gitreviewpilot -V\n")
|
|
7
|
+
.action(() => {
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const pkg = require("../../package.json");
|
|
10
|
+
const name = pkg.name ?? "gitreviewpilot";
|
|
11
|
+
const version = pkg.version ?? "0.0.0";
|
|
12
|
+
// Standard CLI output: `<name> <version>`
|
|
13
|
+
console.log(`${name} ${version}`);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/commands/version.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,6CAA6C,CAAC;SAC1D,WAAW,CACV,OAAO,EACP,8DAA8D,CAC/D;SACA,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,oBAAoB,CAAwC,CAAC;QACjF,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,gBAAgB,CAAC;QAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;QACvC,0CAA0C;QAC1C,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAKA,wBAAgB,OAAO,IAAI,IAAI,CAW9B;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvD"}
|