gyst-ai 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 +21 -0
- package/README.md +495 -0
- package/dist/chunk-2AZCJA4R.js +95 -0
- package/dist/chunk-6N6NGDKO.js +125 -0
- package/dist/chunk-6PVKVSSZ.js +114 -0
- package/dist/chunk-IPTMQZIA.js +148 -0
- package/dist/chunk-MJ3HNNN5.js +24 -0
- package/dist/cli/index.js +87 -0
- package/dist/config-4C6J75EX.js +175 -0
- package/dist/explain-QEEEINVH.js +124 -0
- package/dist/git-2Z36EDC3.js +130 -0
- package/dist/readme-FDEGZDMF.js +115 -0
- package/dist/review-3LWIN7DU.js +351 -0
- package/dist/roast-RLTJEMYM.js +153 -0
- package/dist/standup-UZ7Z6FTB.js +140 -0
- package/dist/wtf-PE2A26HR.js +83 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stanley Yang
|
|
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,495 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<br />
|
|
3
|
+
<img src="https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
|
|
4
|
+
<img src="https://img.shields.io/badge/Node.js-%3E%3D20-339933?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js" />
|
|
5
|
+
<img src="https://img.shields.io/badge/License-MIT-blue?style=for-the-badge" alt="MIT License" />
|
|
6
|
+
<img src="https://img.shields.io/badge/AI%20Powered-Claude%20%7C%20GPT%20%7C%20Gemini-blueviolet?style=for-the-badge" alt="AI Powered" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<pre align="center">
|
|
10
|
+
██████╗ ██╗ ██╗███████╗████████╗
|
|
11
|
+
██╔════╝ ╚██╗ ██╔╝██╔════╝╚══██╔══╝
|
|
12
|
+
██║ ███╗ ╚████╔╝ ███████╗ ██║
|
|
13
|
+
██║ ██║ ╚██╔╝ ╚════██║ ██║
|
|
14
|
+
╚██████╔╝ ██║ ███████║ ██║
|
|
15
|
+
╚═════╝ ╚═╝ ╚══════╝ ╚═╝
|
|
16
|
+
</pre>
|
|
17
|
+
|
|
18
|
+
<h3 align="center">Get your sh*t together.</h3>
|
|
19
|
+
<p align="center">
|
|
20
|
+
<strong>AI-powered developer toolkit for your terminal.</strong><br />
|
|
21
|
+
7 commands that save you hours every week.
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<a href="#installation">Install</a> ·
|
|
26
|
+
<a href="#commands">Commands</a> ·
|
|
27
|
+
<a href="#providers">Providers</a> ·
|
|
28
|
+
<a href="#configuration">Configuration</a> ·
|
|
29
|
+
<a href="#contributing">Contributing</a>
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## What is gyst?
|
|
35
|
+
|
|
36
|
+
**gyst** is an all-in-one AI developer CLI that lives in your terminal. It translates natural language to git commands, reviews your code with auto-fix, roasts bad code, explains cryptic errors, generates standups from git history, writes READMEs, and explains codebases — all powered by the AI provider of your choice.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Translate English to git
|
|
40
|
+
$ gyst git "undo my last commit but keep the changes"
|
|
41
|
+
🔧 git reset --soft HEAD~1
|
|
42
|
+
|
|
43
|
+
# AI code review with one-click fixes
|
|
44
|
+
$ gyst review src/ --fix
|
|
45
|
+
🔴 SQL Injection in auth.ts:42 → auto-fixed ✓
|
|
46
|
+
|
|
47
|
+
# Get roasted
|
|
48
|
+
$ gyst roast index.js --severity brutal
|
|
49
|
+
💀 Using var in 2026? This code is a time capsule.
|
|
50
|
+
|
|
51
|
+
# Explain any error
|
|
52
|
+
$ npm run build 2>&1 | gyst wtf
|
|
53
|
+
💡 Your PostCSS config references a plugin that isn't installed...
|
|
54
|
+
|
|
55
|
+
# Generate standup from git history
|
|
56
|
+
$ gyst standup --format slack
|
|
57
|
+
📋 *What I did:* Implemented auth flow, fixed 3 bugs...
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# npm
|
|
64
|
+
npm install -g gyst-ai
|
|
65
|
+
|
|
66
|
+
# pnpm
|
|
67
|
+
pnpm add -g gyst-ai
|
|
68
|
+
|
|
69
|
+
# yarn
|
|
70
|
+
yarn global add gyst-ai
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Requirements:** Node.js >= 20
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# 1. Set your API key (pick any provider)
|
|
79
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
80
|
+
# or
|
|
81
|
+
gyst config set key anthropic sk-ant-...
|
|
82
|
+
|
|
83
|
+
# 2. Start using it
|
|
84
|
+
gyst git "show me what changed this week"
|
|
85
|
+
gyst review src/
|
|
86
|
+
gyst roast app.js
|
|
87
|
+
gyst wtf "ECONNREFUSED 127.0.0.1:5432"
|
|
88
|
+
gyst standup
|
|
89
|
+
gyst readme --dry-run
|
|
90
|
+
gyst explain src/core/
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Commands
|
|
94
|
+
|
|
95
|
+
### `gyst git` — Natural Language Git
|
|
96
|
+
|
|
97
|
+
Translates plain English instructions into the exact git command you need. Understands your repo context (current branch, status, remotes, stash) and warns you before running anything destructive.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
$ gyst git "squash the last 3 commits"
|
|
101
|
+
┌─ 🔧 Git Command ───────────────────────┐
|
|
102
|
+
│ git rebase -i HEAD~3 │
|
|
103
|
+
│ Interactively squash the last 3 commits │
|
|
104
|
+
└─────────────────────────────────────────┘
|
|
105
|
+
Run this command? (Y/n):
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Features:**
|
|
109
|
+
- Reads your repo state (branch, status, log, remotes, stash count)
|
|
110
|
+
- Warns about destructive commands (requires typing `yes` instead of `Y`)
|
|
111
|
+
- Shows alternative commands when applicable
|
|
112
|
+
- Strips markdown fences from AI response for reliable parsing
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
### `gyst review` — AI Code Review with Auto-Fix
|
|
117
|
+
|
|
118
|
+
Professional code review that finds real bugs, security issues, and anti-patterns — then provides exact code replacements you can auto-apply.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Review a file
|
|
122
|
+
$ gyst review src/api/auth.ts
|
|
123
|
+
|
|
124
|
+
# Review an entire directory
|
|
125
|
+
$ gyst review src/
|
|
126
|
+
|
|
127
|
+
# Auto-apply all fixes
|
|
128
|
+
$ gyst review src/ --fix
|
|
129
|
+
|
|
130
|
+
# Only show critical and warning severity
|
|
131
|
+
$ gyst review src/ --severity warning
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Features:**
|
|
135
|
+
- 4 severity levels: `critical`, `warning`, `suggestion`, `nitpick`
|
|
136
|
+
- Exact `currentCode` → `suggestedCode` replacements for auto-fix
|
|
137
|
+
- Fixes applied in reverse line order to preserve positions
|
|
138
|
+
- Confirms before writing files, suggests `git diff` after
|
|
139
|
+
- `--json` output for CI integration
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### `gyst roast` — Code Roasting
|
|
144
|
+
|
|
145
|
+
Get a brutally honest (and funny) review of your code. Three severity levels from gentle encouragement to full Gordon Ramsay.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
$ gyst roast src/utils.ts --severity brutal
|
|
149
|
+
|
|
150
|
+
💀 Roast mode: BRUTAL
|
|
151
|
+
|
|
152
|
+
🔥 Code Roast: utils.ts
|
|
153
|
+
|
|
154
|
+
Line 1: `var express = require('express')` — Using var in 2026?
|
|
155
|
+
This code is a time capsule. Someone call the archaeology department.
|
|
156
|
+
|
|
157
|
+
📊 Overall: 3/10
|
|
158
|
+
🎤 Final Verdict: This code doesn't just have bugs — it IS the bug.
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Severity levels:**
|
|
162
|
+
- `gentle` — Constructive with light humor
|
|
163
|
+
- `medium` — Comedy roast meets code review (default)
|
|
164
|
+
- `brutal` — Full Gordon Ramsay, no mercy
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### `gyst wtf` — Error Explanation
|
|
169
|
+
|
|
170
|
+
Explain any error message and get copy-paste-ready fix commands. Supports piping directly from your build tools.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Pass an error directly
|
|
174
|
+
$ gyst wtf "ENOENT: no such file or directory, open './config.json'"
|
|
175
|
+
|
|
176
|
+
# Pipe from any command
|
|
177
|
+
$ npm run build 2>&1 | gyst wtf
|
|
178
|
+
$ cargo build 2>&1 | gyst wtf
|
|
179
|
+
$ python app.py 2>&1 | gyst wtf
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Features:**
|
|
183
|
+
- Detects your project type for framework-specific advice
|
|
184
|
+
- Streaming output — see the explanation as it generates
|
|
185
|
+
- Supports stdin piping (`!process.stdin.isTTY` detection)
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### `gyst standup` — Standup Generation
|
|
190
|
+
|
|
191
|
+
Generate standup updates from your git history. Supports multiple output formats.
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Default: beautiful terminal output
|
|
195
|
+
$ gyst standup
|
|
196
|
+
|
|
197
|
+
# Slack-formatted for pasting
|
|
198
|
+
$ gyst standup --format slack
|
|
199
|
+
|
|
200
|
+
# JSON for automation
|
|
201
|
+
$ gyst standup --format json
|
|
202
|
+
|
|
203
|
+
# Look back 3 days
|
|
204
|
+
$ gyst standup --days 3
|
|
205
|
+
|
|
206
|
+
# Custom time range
|
|
207
|
+
$ gyst standup --since "last monday"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Output includes:**
|
|
211
|
+
- What you did (grouped by logical work items)
|
|
212
|
+
- What's next (inferred from patterns)
|
|
213
|
+
- Blockers (if any)
|
|
214
|
+
- Stats: commits, files changed, insertions, deletions
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### `gyst readme` — README Generation
|
|
219
|
+
|
|
220
|
+
Analyzes your entire codebase and generates a comprehensive README.
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# Generate (saves as README.generated.md if README.md exists)
|
|
224
|
+
$ gyst readme
|
|
225
|
+
|
|
226
|
+
# Preview without writing
|
|
227
|
+
$ gyst readme --dry-run
|
|
228
|
+
|
|
229
|
+
# Different styles
|
|
230
|
+
$ gyst readme --style minimal # Just the essentials
|
|
231
|
+
$ gyst readme --style detailed # Full documentation (default)
|
|
232
|
+
$ gyst readme --style startup # Pitch-style, sells the project
|
|
233
|
+
|
|
234
|
+
# Custom output path
|
|
235
|
+
$ gyst readme --output DOCS.md
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### `gyst explain` — Code Explanation
|
|
241
|
+
|
|
242
|
+
Explain what code does in plain English. Works with single files or entire directories.
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Explain a file
|
|
246
|
+
$ gyst explain src/core/ai.ts
|
|
247
|
+
|
|
248
|
+
# Explain a directory (reads up to 8 files)
|
|
249
|
+
$ gyst explain src/core/
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Covers:**
|
|
253
|
+
- Purpose and problem solved
|
|
254
|
+
- Key logic and data flow
|
|
255
|
+
- Design patterns recognized
|
|
256
|
+
- Dependencies and gotchas
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
### `gyst config` — Configuration
|
|
261
|
+
|
|
262
|
+
Manage API keys, default models, and preferences.
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# Show current config
|
|
266
|
+
$ gyst config show
|
|
267
|
+
|
|
268
|
+
# Set API keys
|
|
269
|
+
$ gyst config set key anthropic sk-ant-api03-...
|
|
270
|
+
$ gyst config set key openai sk-...
|
|
271
|
+
$ gyst config set key google AIza...
|
|
272
|
+
$ gyst config set key groq gsk-...
|
|
273
|
+
|
|
274
|
+
# Set default model
|
|
275
|
+
$ gyst config set model gpt-4o
|
|
276
|
+
$ gyst config set model claude-opus
|
|
277
|
+
|
|
278
|
+
# Set preferences
|
|
279
|
+
$ gyst config set preference roastSeverity brutal
|
|
280
|
+
$ gyst config set preference standupDays 3
|
|
281
|
+
|
|
282
|
+
# Reset everything
|
|
283
|
+
$ gyst config reset
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Providers
|
|
287
|
+
|
|
288
|
+
gyst supports 6 AI providers out of the box. Set your preferred provider's API key and go.
|
|
289
|
+
|
|
290
|
+
| Provider | Models | Env Variable | Best For |
|
|
291
|
+
|----------|--------|-------------|----------|
|
|
292
|
+
| **Anthropic** | `claude-sonnet`, `claude-haiku`, `claude-opus` | `ANTHROPIC_API_KEY` | Best overall quality |
|
|
293
|
+
| **OpenAI** | `gpt-4o`, `gpt-4o-mini`, `gpt-4.1`, `gpt-4.1-mini` | `OPENAI_API_KEY` | Fastest responses |
|
|
294
|
+
| **Google** | `gemini-pro`, `gemini-flash` | `GOOGLE_GENERATIVE_AI_API_KEY` | Free tier available |
|
|
295
|
+
| **Groq** | `llama3` (Llama 3.1 70B) | `GROQ_API_KEY` | Fastest open model |
|
|
296
|
+
| **DeepSeek** | `deepseek` | `DEEPSEEK_API_KEY` | Budget-friendly |
|
|
297
|
+
| **Ollama** | `local:<model-name>` | — | Fully offline / private |
|
|
298
|
+
|
|
299
|
+
### Using a Specific Provider
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
# Use a specific model
|
|
303
|
+
$ gyst git "status" --model gpt-4o
|
|
304
|
+
|
|
305
|
+
# Use local Ollama
|
|
306
|
+
$ gyst git "status" --local
|
|
307
|
+
|
|
308
|
+
# Override provider
|
|
309
|
+
$ gyst git "status" --provider openai
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Provider Key Resolution
|
|
313
|
+
|
|
314
|
+
1. Environment variable (e.g., `ANTHROPIC_API_KEY`)
|
|
315
|
+
2. Config file (`~/.gyst/config.json` via `gyst config set key`)
|
|
316
|
+
3. Helpful error message pointing to `gyst config set key`
|
|
317
|
+
|
|
318
|
+
## Configuration
|
|
319
|
+
|
|
320
|
+
Config is stored at `~/.gyst/config.json`. You can edit it directly or use `gyst config set`.
|
|
321
|
+
|
|
322
|
+
```json
|
|
323
|
+
{
|
|
324
|
+
"defaultModel": "claude-sonnet",
|
|
325
|
+
"defaultProvider": "anthropic",
|
|
326
|
+
"keys": {
|
|
327
|
+
"anthropic": "sk-ant-..."
|
|
328
|
+
},
|
|
329
|
+
"ollamaBaseUrl": "http://localhost:11434",
|
|
330
|
+
"preferences": {
|
|
331
|
+
"roastSeverity": "medium",
|
|
332
|
+
"standupDays": 1,
|
|
333
|
+
"gitAutoExecute": false
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Global Flags
|
|
339
|
+
|
|
340
|
+
These flags work with every command:
|
|
341
|
+
|
|
342
|
+
| Flag | Description |
|
|
343
|
+
|------|-------------|
|
|
344
|
+
| `-m, --model <model>` | AI model to use (e.g., `claude-sonnet`, `gpt-4o`, `local:llama3.2`) |
|
|
345
|
+
| `-p, --provider <provider>` | Override provider (`anthropic`, `openai`, `google`, `groq`, `ollama`, `deepseek`) |
|
|
346
|
+
| `--local` | Use local Ollama model |
|
|
347
|
+
| `--temperature <n>` | Override temperature (0-1) |
|
|
348
|
+
| `--max-tokens <n>` | Override max output tokens |
|
|
349
|
+
| `--json` | Output as JSON (where supported) |
|
|
350
|
+
| `--no-color` | Disable colored output |
|
|
351
|
+
| `-v, --verbose` | Verbose output |
|
|
352
|
+
|
|
353
|
+
## Project Architecture
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
src/
|
|
357
|
+
├── cli/
|
|
358
|
+
│ ├── index.ts # Commander.js setup, all commands
|
|
359
|
+
│ └── commands/ # One file per command
|
|
360
|
+
│ ├── git.ts # Natural language → git
|
|
361
|
+
│ ├── review.ts # Code review + auto-fix
|
|
362
|
+
│ ├── roast.ts # Code roasting
|
|
363
|
+
│ ├── wtf.ts # Error explanation
|
|
364
|
+
│ ├── standup.ts # Standup generation
|
|
365
|
+
│ ├── readme.ts # README generation
|
|
366
|
+
│ ├── explain.ts # Code explanation
|
|
367
|
+
│ └── config.ts # Config management
|
|
368
|
+
├── core/
|
|
369
|
+
│ ├── types.ts # All TypeScript interfaces
|
|
370
|
+
│ ├── ai.ts # Unified AI client (Vercel AI SDK)
|
|
371
|
+
│ ├── models.ts # Model registry + aliases
|
|
372
|
+
│ ├── context.ts # Project context detection
|
|
373
|
+
│ └── git-context.ts # Git repo state gathering
|
|
374
|
+
├── prompts/ # System prompts for each command
|
|
375
|
+
├── ui/
|
|
376
|
+
│ ├── theme.ts # Chalk color theme
|
|
377
|
+
│ ├── spinner.ts # Ora spinner wrapper
|
|
378
|
+
│ └── box.ts # Boxen box wrapper
|
|
379
|
+
├── utils/
|
|
380
|
+
│ ├── exec.ts # Shell execution
|
|
381
|
+
│ ├── detect-project.ts # Framework detection
|
|
382
|
+
│ └── file-tree.ts # Directory tree generator
|
|
383
|
+
└── storage/
|
|
384
|
+
└── config.ts # ~/.gyst/config.json management
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Tech Stack
|
|
388
|
+
|
|
389
|
+
- **Runtime:** Node.js >= 20, TypeScript (strict mode)
|
|
390
|
+
- **Build:** tsup (ESM output, Node 20 target)
|
|
391
|
+
- **CLI Framework:** Commander.js
|
|
392
|
+
- **Terminal UI:** chalk 5 + boxen 8 + ora 8
|
|
393
|
+
- **AI:** [Vercel AI SDK](https://sdk.vercel.ai/) (`ai` + provider packages)
|
|
394
|
+
- **Testing:** Vitest with v8 coverage (182 tests, 96%+ coverage)
|
|
395
|
+
- **Linting:** Biome
|
|
396
|
+
|
|
397
|
+
## Development
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
# Clone
|
|
401
|
+
git clone https://github.com/stanleycyang/gyst.git
|
|
402
|
+
cd gyst
|
|
403
|
+
|
|
404
|
+
# Install dependencies
|
|
405
|
+
pnpm install
|
|
406
|
+
|
|
407
|
+
# Build
|
|
408
|
+
pnpm build
|
|
409
|
+
|
|
410
|
+
# Run locally
|
|
411
|
+
node dist/cli/index.js --help
|
|
412
|
+
|
|
413
|
+
# Development (watch mode)
|
|
414
|
+
pnpm dev
|
|
415
|
+
|
|
416
|
+
# Run tests
|
|
417
|
+
pnpm test
|
|
418
|
+
|
|
419
|
+
# Run tests with coverage
|
|
420
|
+
pnpm vitest run --coverage
|
|
421
|
+
|
|
422
|
+
# Lint
|
|
423
|
+
pnpm lint
|
|
424
|
+
|
|
425
|
+
# Type check
|
|
426
|
+
pnpm typecheck
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Landing Page
|
|
430
|
+
|
|
431
|
+
The landing page lives in `website/` and is a Next.js 16 app with Tailwind CSS.
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
cd website
|
|
435
|
+
pnpm install
|
|
436
|
+
pnpm dev # http://localhost:3000
|
|
437
|
+
pnpm build # Production build
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Testing
|
|
441
|
+
|
|
442
|
+
182 tests across 26 test suites with 96%+ statement coverage.
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
Test Files 26 passed (26)
|
|
446
|
+
Tests 182 passed (182)
|
|
447
|
+
|
|
448
|
+
% Coverage report from v8
|
|
449
|
+
All files | 96.07% Stmts | 86.37% Branch | 100% Funcs | 96.07% Lines
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Tests cover:
|
|
453
|
+
- All 7 system prompts
|
|
454
|
+
- All 8 CLI commands (with mocked AI calls)
|
|
455
|
+
- Core modules: model resolution, AI client, project context, git context
|
|
456
|
+
- Utils: shell exec, project detection, file tree generation
|
|
457
|
+
- Storage: config read/write/update, API key management
|
|
458
|
+
- UI: theme, spinner, box rendering
|
|
459
|
+
- Error handling and edge cases
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
# Run all tests
|
|
463
|
+
pnpm test
|
|
464
|
+
|
|
465
|
+
# Run with coverage report
|
|
466
|
+
pnpm vitest run --coverage
|
|
467
|
+
|
|
468
|
+
# Run specific test file
|
|
469
|
+
pnpm vitest run tests/core/models.test.ts
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## FAQ
|
|
473
|
+
|
|
474
|
+
**Q: Which AI provider should I use?**
|
|
475
|
+
A: Claude Sonnet (default) offers the best balance of quality and speed. GPT-4o is faster. Gemini has a generous free tier. Llama via Groq is the fastest open model. For privacy, use Ollama locally.
|
|
476
|
+
|
|
477
|
+
**Q: Does it send my code to the cloud?**
|
|
478
|
+
A: Yes, unless you use `--local` with Ollama. Your code is sent to whichever AI provider you configure. Use Ollama for fully offline, private operation.
|
|
479
|
+
|
|
480
|
+
**Q: Can I use it in CI/CD?**
|
|
481
|
+
A: Yes. Use `--json` for machine-readable output and set your API key via environment variable. Example: `gyst review src/ --severity warning --json`
|
|
482
|
+
|
|
483
|
+
**Q: How does `--fix` work?**
|
|
484
|
+
A: The AI returns exact `currentCode` → `suggestedCode` pairs. gyst does a `String.replace(currentCode, suggestedCode)` on your source files. It always confirms before writing and suggests `git diff` after.
|
|
485
|
+
|
|
486
|
+
## License
|
|
487
|
+
|
|
488
|
+
MIT
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
<p align="center">
|
|
493
|
+
<strong>Built with TypeScript, Vercel AI SDK, and way too much caffeine.</strong><br />
|
|
494
|
+
<sub>If gyst saved you time, give it a star ⭐</sub>
|
|
495
|
+
</p>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/exec.ts
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
async function exec(command, options = {}) {
|
|
6
|
+
const execOpts = {
|
|
7
|
+
cwd: options.cwd || process.cwd(),
|
|
8
|
+
encoding: "utf-8",
|
|
9
|
+
stdio: options.inheritStdio ? "inherit" : "pipe",
|
|
10
|
+
maxBuffer: 10 * 1024 * 1024
|
|
11
|
+
// 10MB
|
|
12
|
+
};
|
|
13
|
+
const result = execSync(command, execOpts);
|
|
14
|
+
return (result || "").toString();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/core/git-context.ts
|
|
18
|
+
async function getGitContext() {
|
|
19
|
+
const isRepo = await exec("git rev-parse --is-inside-work-tree").then(() => true).catch(() => false);
|
|
20
|
+
if (!isRepo) {
|
|
21
|
+
return {
|
|
22
|
+
isRepo: false,
|
|
23
|
+
currentBranch: "",
|
|
24
|
+
status: "",
|
|
25
|
+
recentLog: "",
|
|
26
|
+
stagedFiles: [],
|
|
27
|
+
modifiedFiles: [],
|
|
28
|
+
untrackedFiles: [],
|
|
29
|
+
remotes: [],
|
|
30
|
+
stashCount: 0,
|
|
31
|
+
hasUncommittedChanges: false,
|
|
32
|
+
lastCommitMessage: "",
|
|
33
|
+
branchList: []
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const [branch, status, log, remotes, stashList, lastCommit, branches] = await Promise.all([
|
|
37
|
+
exec("git branch --show-current").catch(() => "HEAD"),
|
|
38
|
+
exec("git status --short").catch(() => ""),
|
|
39
|
+
exec("git log --oneline -20 --no-decorate").catch(() => ""),
|
|
40
|
+
exec("git remote -v").catch(() => ""),
|
|
41
|
+
exec("git stash list").catch(() => ""),
|
|
42
|
+
exec("git log -1 --format=%s").catch(() => ""),
|
|
43
|
+
exec('git branch --list --format="%(refname:short)"').catch(() => "")
|
|
44
|
+
]);
|
|
45
|
+
const statusLines = status.trim().split("\n").filter(Boolean);
|
|
46
|
+
return {
|
|
47
|
+
isRepo: true,
|
|
48
|
+
currentBranch: branch.trim(),
|
|
49
|
+
status: status.trim(),
|
|
50
|
+
recentLog: log.trim(),
|
|
51
|
+
stagedFiles: statusLines.filter((l) => /^[MADRC]/.test(l)).map((l) => l.slice(3)),
|
|
52
|
+
modifiedFiles: statusLines.filter((l) => /^.M/.test(l)).map((l) => l.slice(3)),
|
|
53
|
+
untrackedFiles: statusLines.filter((l) => l.startsWith("??")).map((l) => l.slice(3)),
|
|
54
|
+
remotes: remotes.trim().split("\n").filter(Boolean),
|
|
55
|
+
stashCount: stashList.trim() ? stashList.trim().split("\n").length : 0,
|
|
56
|
+
hasUncommittedChanges: statusLines.length > 0,
|
|
57
|
+
lastCommitMessage: lastCommit.trim(),
|
|
58
|
+
branchList: branches.trim().split("\n").filter(Boolean).map((b) => b.replace(/"/g, ""))
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async function getDetailedGitLog(since) {
|
|
62
|
+
try {
|
|
63
|
+
return await exec(
|
|
64
|
+
`git log --since="${since}" --format="%h %s (%an, %ar)" --stat --no-merges`
|
|
65
|
+
);
|
|
66
|
+
} catch {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function getGitStats(since) {
|
|
71
|
+
try {
|
|
72
|
+
const commitCount = await exec(
|
|
73
|
+
`git rev-list --count --since="${since}" HEAD`
|
|
74
|
+
);
|
|
75
|
+
const diffStat = await exec(`git diff --stat --since="${since}" HEAD`);
|
|
76
|
+
const summaryMatch = diffStat.match(
|
|
77
|
+
/(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?/
|
|
78
|
+
);
|
|
79
|
+
return {
|
|
80
|
+
commits: parseInt(commitCount.trim(), 10) || 0,
|
|
81
|
+
filesChanged: summaryMatch ? parseInt(summaryMatch[1], 10) || 0 : 0,
|
|
82
|
+
insertions: summaryMatch ? parseInt(summaryMatch[2], 10) || 0 : 0,
|
|
83
|
+
deletions: summaryMatch ? parseInt(summaryMatch[3], 10) || 0 : 0
|
|
84
|
+
};
|
|
85
|
+
} catch {
|
|
86
|
+
return { commits: 0, filesChanged: 0, insertions: 0, deletions: 0 };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
exec,
|
|
92
|
+
getGitContext,
|
|
93
|
+
getDetailedGitLog,
|
|
94
|
+
getGitStats
|
|
95
|
+
};
|