@cynicalsally/cli 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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +332 -0
  3. package/dist/commands/login.d.ts +3 -0
  4. package/dist/commands/login.d.ts.map +1 -0
  5. package/dist/commands/login.js +54 -0
  6. package/dist/commands/login.js.map +1 -0
  7. package/dist/commands/logout.d.ts +3 -0
  8. package/dist/commands/logout.d.ts.map +1 -0
  9. package/dist/commands/logout.js +15 -0
  10. package/dist/commands/logout.js.map +1 -0
  11. package/dist/commands/mcp.d.ts +3 -0
  12. package/dist/commands/mcp.d.ts.map +1 -0
  13. package/dist/commands/mcp.js +47 -0
  14. package/dist/commands/mcp.js.map +1 -0
  15. package/dist/commands/results.d.ts +3 -0
  16. package/dist/commands/results.d.ts.map +1 -0
  17. package/dist/commands/results.js +60 -0
  18. package/dist/commands/results.js.map +1 -0
  19. package/dist/commands/roast.d.ts +3 -0
  20. package/dist/commands/roast.d.ts.map +1 -0
  21. package/dist/commands/roast.js +233 -0
  22. package/dist/commands/roast.js.map +1 -0
  23. package/dist/commands/tools.d.ts +8 -0
  24. package/dist/commands/tools.d.ts.map +1 -0
  25. package/dist/commands/tools.js +460 -0
  26. package/dist/commands/tools.js.map +1 -0
  27. package/dist/commands/upgrade.d.ts +3 -0
  28. package/dist/commands/upgrade.d.ts.map +1 -0
  29. package/dist/commands/upgrade.js +82 -0
  30. package/dist/commands/upgrade.js.map +1 -0
  31. package/dist/commands/usage.d.ts +3 -0
  32. package/dist/commands/usage.d.ts.map +1 -0
  33. package/dist/commands/usage.js +87 -0
  34. package/dist/commands/usage.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +60 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/mcp-server.d.ts +12 -0
  40. package/dist/mcp-server.d.ts.map +1 -0
  41. package/dist/mcp-server.js +266 -0
  42. package/dist/mcp-server.js.map +1 -0
  43. package/dist/utils/api.d.ts +108 -0
  44. package/dist/utils/api.d.ts.map +1 -0
  45. package/dist/utils/api.js +79 -0
  46. package/dist/utils/api.js.map +1 -0
  47. package/dist/utils/background.d.ts +9 -0
  48. package/dist/utils/background.d.ts.map +1 -0
  49. package/dist/utils/background.js +91 -0
  50. package/dist/utils/background.js.map +1 -0
  51. package/dist/utils/config.d.ts +13 -0
  52. package/dist/utils/config.d.ts.map +1 -0
  53. package/dist/utils/config.js +65 -0
  54. package/dist/utils/config.js.map +1 -0
  55. package/dist/utils/files.d.ts +14 -0
  56. package/dist/utils/files.d.ts.map +1 -0
  57. package/dist/utils/files.js +141 -0
  58. package/dist/utils/files.js.map +1 -0
  59. package/dist/utils/flavor.d.ts +48 -0
  60. package/dist/utils/flavor.d.ts.map +1 -0
  61. package/dist/utils/flavor.js +79 -0
  62. package/dist/utils/flavor.js.map +1 -0
  63. package/dist/utils/git.d.ts +29 -0
  64. package/dist/utils/git.d.ts.map +1 -0
  65. package/dist/utils/git.js +90 -0
  66. package/dist/utils/git.js.map +1 -0
  67. package/dist/utils/output.d.ts +5 -0
  68. package/dist/utils/output.d.ts.map +1 -0
  69. package/dist/utils/output.js +206 -0
  70. package/dist/utils/output.js.map +1 -0
  71. package/dist/utils/report.d.ts +12 -0
  72. package/dist/utils/report.d.ts.map +1 -0
  73. package/dist/utils/report.js +190 -0
  74. package/dist/utils/report.js.map +1 -0
  75. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Thomas Geelens
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,332 @@
1
+ <p align="center">
2
+ <img src="assets/banner.png" alt="Cynical Sally CLI" width="700" />
3
+ </p>
4
+
5
+ <h1 align="center">@cynicalsally/cli</h1>
6
+
7
+ <p align="center">
8
+ <strong>Brutally honest code reviews. Terminal + IDE.</strong><br/>
9
+ <em>Because "You're absolutely right" is probably absolutely wrong.</em>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://www.npmjs.com/package/@cynicalsally/cli"><img src="https://img.shields.io/npm/v/@cynicalsally/cli.svg" alt="npm version" /></a>
14
+ <a href="https://www.npmjs.com/package/@cynicalsally/cli"><img src="https://img.shields.io/npm/dm/@cynicalsally/cli.svg" alt="npm downloads" /></a>
15
+ <a href="https://github.com/w1ckedxt/cynicalsally-cli/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@cynicalsally/cli.svg" alt="license" /></a>
16
+ </p>
17
+
18
+ ---
19
+
20
+ Your AI pair programmer is lying to you. Sally isn't.
21
+
22
+ She's the senior engineer your code hoped it'd never meet. Scores from 0 to 10, real issues backed by evidence, and fixes you can actually use.
23
+
24
+ Works as a CLI tool and as an MCP server in [Claude Code](https://claude.ai/claude-code), [Cursor](https://cursor.com), and [Windsurf](https://windsurf.com).
25
+
26
+ <p align="center">
27
+ <img src="assets/SallyInTerminal.png" alt="Sally in your terminal" width="500" />
28
+ </p>
29
+ <p align="center">
30
+ <img src="assets/SallyRoast1.png" alt="Sally code review with score and issues" width="500" />
31
+ <img src="assets/SallyRoast2.png" alt="Sally actionable fixes and verdict" width="500" />
32
+ </p>
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ npm install -g @cynicalsally/cli
38
+ ```
39
+
40
+ Or run without installing:
41
+
42
+ ```bash
43
+ npx @cynicalsally/cli roast ./src/
44
+ ```
45
+
46
+ **Requirements:** Node.js 18+
47
+
48
+ ## Quick Start
49
+
50
+ ```bash
51
+ # Sally auto-detects what to review
52
+ sally roast
53
+ # → staged changes? reviews those
54
+ # → unstaged changes? reviews those
55
+ # → recent commit? reviews that
56
+ # → nothing? scans the directory
57
+
58
+ # Roast a file or directory
59
+ sally roast src/utils/auth.ts
60
+ sally roast ./src/
61
+
62
+ # Roast staged changes before you commit
63
+ sally roast --staged
64
+
65
+ # Compare your branch against main
66
+ sally roast --diff main
67
+
68
+ # Deep analysis with issues + actionable fixes
69
+ sally roast ./src/ -m full_truth
70
+
71
+ # Run deep analysis in the background (OS notification when done)
72
+ sally roast ./src/ -m full_truth --bg
73
+ ```
74
+
75
+ ## Roast Options
76
+
77
+ ```
78
+ sally roast [paths...] [options]
79
+
80
+ --staged Review only staged git changes
81
+ --diff <branch> Compare against another branch (e.g., main)
82
+ -m, --mode <mode> "quick" (default) or "full_truth" (deep dive)
83
+ --tone <tone> "cynical" (default), "neutral", or "professional"
84
+ --lang <lang> Response language code (default: "en")
85
+ --json Output raw JSON (for piping or scripting)
86
+ --fail-under <score> Exit code 1 if quality score is below threshold
87
+ --ci CI mode: compact output, exit codes
88
+ --bg Run Full Truth in background, get OS notification when done
89
+ ```
90
+
91
+ ---
92
+
93
+ <h2 align="center">Sally's Full Suite</h2>
94
+
95
+ <p align="center">
96
+ <em>6 CLI tools. Unlimited usage. The most honest code reviewer you'll ever work with, right in your terminal.</em>
97
+ </p>
98
+
99
+ <p align="center">
100
+ <img src="assets/full-suite.png" alt="Sally's Full Suite" width="600" />
101
+ </p>
102
+
103
+ ---
104
+
105
+ ### Explain
106
+
107
+ <img src="assets/tool-explain.png" alt="sally explain" width="280" align="right" />
108
+
109
+ Sally reads the spaghetti someone left in your codebase and translates it into plain English. Just the cold, clear truth of what it actually does.
110
+
111
+ ```bash
112
+ sally explain src/utils/auth.ts
113
+
114
+ # Pipe code directly
115
+ cat legacy-module.js | sally explain
116
+
117
+ # Explain the current directory
118
+ sally explain
119
+ ```
120
+
121
+ <br clear="right"/>
122
+
123
+ ---
124
+
125
+ ### Refactor
126
+
127
+ <img src="assets/tool-refactor.png" alt="sally refactor" width="280" align="right" />
128
+
129
+ Before and after, side by side. Sally explains why one of them is going to haunt your 3am on-call rotation.
130
+
131
+ ```bash
132
+ sally refactor src/components/Dashboard.tsx
133
+
134
+ # Refactor current directory
135
+ sally refactor
136
+ ```
137
+
138
+ <br clear="right"/>
139
+
140
+ ---
141
+
142
+ ### PR Review
143
+
144
+ <img src="assets/tool-pr-review.png" alt="sally review-pr" width="280" align="right" />
145
+
146
+ Sally reviews your PR like a senior engineer who has time, opinions, and absolutely no reason to be polite.
147
+
148
+ ```bash
149
+ # Review PR #42 (requires GitHub CLI)
150
+ sally review-pr 42
151
+
152
+ # Review current branch vs main
153
+ sally review-pr
154
+
155
+ # Pipe a diff
156
+ git diff main | sally review-pr
157
+ ```
158
+
159
+ <br clear="right"/>
160
+
161
+ ---
162
+
163
+ ### Brainstorm
164
+
165
+ <img src="assets/tool-brainstorm.png" alt="sally brainstorm" width="280" align="right" />
166
+
167
+ Pitch your architecture idea and Sally tells you the three ways it falls apart at scale. Cheaper than a post-mortem.
168
+
169
+ ```bash
170
+ sally brainstorm "Microservices for a 2-person team?"
171
+
172
+ # Brainstorm about the current project
173
+ sally brainstorm
174
+ ```
175
+
176
+ <br clear="right"/>
177
+
178
+ ---
179
+
180
+ ### Frontend Review
181
+
182
+ <img src="assets/tool-frontend.png" alt="sally frontend" width="280" align="right" />
183
+
184
+ Sally tells you why your component re-renders on every keystroke and why your z-index is load-bearing.
185
+
186
+ ```bash
187
+ sally frontend src/components/Header.tsx
188
+
189
+ # Review all frontend code in a directory
190
+ sally frontend ./src/
191
+ ```
192
+
193
+ <br clear="right"/>
194
+
195
+ ---
196
+
197
+ ### Marketing Review
198
+
199
+ <img src="assets/tool-marketing.png" alt="sally marketing" width="280" align="right" />
200
+
201
+ Run your copy by Sally before your customers do. They won't be this constructive about it.
202
+
203
+ ```bash
204
+ sally marketing "Ship faster with AI-powered code reviews"
205
+
206
+ # Review your README and landing page copy
207
+ sally marketing README.md
208
+ ```
209
+
210
+ <br clear="right"/>
211
+
212
+ ---
213
+
214
+ Every tool accepts **file paths**, **raw text**, or **piped stdin**. Each includes **1 free trial**, no account needed.
215
+
216
+ ## CI/CD Integration
217
+
218
+ Gate your pipeline on code quality:
219
+
220
+ ```yaml
221
+ # GitHub Actions
222
+ - name: Sally Code Review
223
+ run: npx @cynicalsally/cli roast ./src/ --fail-under=5 --ci
224
+ ```
225
+
226
+ `--ci` gives compact output with exit codes. `--fail-under` fails the build when the score drops below your threshold. Add `--json` for machine-readable output.
227
+
228
+ ## MCP Server
229
+
230
+ Sally works as an MCP server inside **Claude Code**, **Cursor**, and **Windsurf**.
231
+
232
+ ### Claude Code
233
+
234
+ ```bash
235
+ claude mcp add cynical-sally -- npx @cynicalsally/cli mcp
236
+ ```
237
+
238
+ ### Cursor
239
+
240
+ Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per project):
241
+
242
+ ```json
243
+ {
244
+ "mcpServers": {
245
+ "cynical-sally": {
246
+ "command": "npx",
247
+ "args": ["@cynicalsally/cli", "mcp"]
248
+ }
249
+ }
250
+ }
251
+ ```
252
+
253
+ ### Windsurf
254
+
255
+ Add to `~/.codeium/windsurf/mcp_config.json`:
256
+
257
+ ```json
258
+ {
259
+ "mcpServers": {
260
+ "cynical-sally": {
261
+ "command": "npx",
262
+ "args": ["@cynicalsally/cli", "mcp"]
263
+ }
264
+ }
265
+ }
266
+ ```
267
+
268
+ ### Available tools
269
+
270
+ | MCP Tool | What it does |
271
+ |----------|-------------|
272
+ | `sally_roast` | Code review with score, issues, and fixes |
273
+ | `sally_explain` | Explain code with Sally's personality |
274
+ | `sally_review_pr` | Review PR diffs |
275
+ | `sally_refactor` | Refactoring suggestions with before/after |
276
+ | `sally_brainstorm` | Feedback on ideas and approaches |
277
+ | `sally_frontend` | Frontend/UI code review |
278
+ | `sally_marketing` | Marketing copy review |
279
+ | `sally_usage` | Check quota and account status |
280
+
281
+ Run `sally mcp` in your terminal to see setup instructions.
282
+
283
+ ## All Commands
284
+
285
+ | Command | Description |
286
+ |---------|-------------|
287
+ | `sally roast [paths...]` | Review files, directories, or git changes |
288
+ | `sally explain [file]` | Explain what code actually does |
289
+ | `sally refactor [file]` | Refactoring with before/after code |
290
+ | `sally review-pr [pr]` | Review a PR diff |
291
+ | `sally brainstorm "idea"` | Feedback on ideas and approaches |
292
+ | `sally frontend [file]` | Frontend/UI code review |
293
+ | `sally marketing "copy"` | Marketing copy review |
294
+ | `sally login <email>` | Log in via magic link |
295
+ | `sally logout` | Clear stored session |
296
+ | `sally usage` | Check your quota and account status |
297
+ | `sally upgrade` | Upgrade to Sally's Full Suite |
298
+ | `sally results` | View background review results |
299
+ | `sally mcp` | MCP server setup instructions |
300
+
301
+ ## Free to Use
302
+
303
+ 90 free roasts per month, no account needed. Every premium tool includes a free trial.
304
+
305
+ ```bash
306
+ sally usage # Check your quota
307
+ sally upgrade # Unlock the Full Suite
308
+ ```
309
+
310
+ ## Privacy & Security
311
+
312
+ - Code is transmitted over HTTPS and processed in real-time
313
+ - **No source code is stored** on our servers. Ever.
314
+ - Analysis results are tied to an anonymous device ID
315
+ - Full Suite members can optionally link an email for account features
316
+ - Config stored locally at `~/.sally/config.json`
317
+
318
+ For full details: [cynicalsally.com/privacy](https://cynicalsally.com/privacy)
319
+
320
+ ## Contributing
321
+
322
+ Found a bug or have a feature idea? [Open an issue](https://github.com/w1ckedxt/cynicalsally-cli/issues). Sally promises to only judge your issue title a little.
323
+
324
+ ## License
325
+
326
+ [MIT](LICENSE)
327
+
328
+ ---
329
+
330
+ <p align="center">
331
+ <a href="https://cynicalsally.com">cynicalsally.com</a> · <a href="https://www.npmjs.com/package/@cynicalsally/cli">npm</a> · <a href="https://github.com/w1ckedxt/cynicalsally-cli/issues">issues</a>
332
+ </p>
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const loginCommand: Command;
3
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,YAAY,SAwDrB,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { saveEmail, getEmail } from "../utils/config.js";
5
+ import { requestMagicLink } from "../utils/api.js";
6
+ export const loginCommand = new Command("login")
7
+ .description("Log in with your email (magic link)")
8
+ .argument("[email]", "Your email address")
9
+ .action(async (email) => {
10
+ const existing = getEmail();
11
+ if (existing) {
12
+ console.log(chalk.yellow("\nI already know you as ") +
13
+ chalk.cyan(existing) +
14
+ chalk.yellow(".") +
15
+ "\nWant a fresh start? " +
16
+ chalk.cyan("sally logout") +
17
+ " first.\n");
18
+ return;
19
+ }
20
+ if (!email) {
21
+ console.log(chalk.magenta("\nWho are you?") +
22
+ "\n\n" +
23
+ chalk.gray(" sally login your@email.com") +
24
+ "\n\n" +
25
+ chalk.gray("Full Suite members get unlimited roasts. The rest of you get a taste.\n"));
26
+ return;
27
+ }
28
+ if (!email.includes("@") || !email.includes(".")) {
29
+ console.log(chalk.red("\nThat's not an email. I'm cynical, not blind.\n"));
30
+ process.exit(1);
31
+ }
32
+ const spinner = ora({ text: "Summoning a magic link... don't get too excited.", color: "magenta" }).start();
33
+ try {
34
+ const result = await requestMagicLink(email);
35
+ spinner.stop();
36
+ if (result.sent) {
37
+ console.log(chalk.green("\nCheck your inbox at ") +
38
+ chalk.cyan(email) +
39
+ chalk.green(".") +
40
+ "\n\n" +
41
+ chalk.gray("Click the link. I'll be here, judging your code.\n"));
42
+ // Save email locally — the magic link verify maps deviceId server-side
43
+ saveEmail(email);
44
+ }
45
+ else {
46
+ console.log(chalk.red(`\n${result.error || "Couldn't send that. Even magic has its limits."}\n`));
47
+ }
48
+ }
49
+ catch {
50
+ spinner.stop();
51
+ console.log(chalk.red("\nCan't reach the server. Your wifi is as reliable as your code.\n"));
52
+ }
53
+ });
54
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAe,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAqB,MAAM,iBAAiB,CAAC;AAEtE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,qCAAqC,CAAC;KAClD,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAC;KACzC,MAAM,CAAC,KAAK,EAAE,KAAc,EAAE,EAAE;IAC/B,MAAM,QAAQ,GAAG,QAAQ,EAAE,CAAC;IAC5B,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CAAC,0BAA0B,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;YACpB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;YACjB,wBAAwB;YACxB,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC;YAC1B,WAAW,CACd,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC7B,MAAM;YACN,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC;YAC1C,MAAM;YACN,KAAK,CAAC,IAAI,CAAC,yEAAyE,CAAC,CACxF,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC,CAAC;QAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,EAAE,IAAI,EAAE,kDAAkD,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IAE5G,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC7C,OAAO,CAAC,IAAI,EAAE,CAAC;QAEf,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC;gBACnC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;gBACjB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;gBAChB,MAAM;gBACN,KAAK,CAAC,IAAI,CAAC,oDAAoD,CAAC,CACnE,CAAC;YACF,uEAAuE;YACvE,SAAS,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,KAAK,IAAI,gDAAgD,IAAI,CAAC,CAAC,CAAC;QACpG,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const logoutCommand: Command;
3
+ //# sourceMappingURL=logout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,aAAa,SAWtB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import { clearSession, getEmail } from "../utils/config.js";
4
+ export const logoutCommand = new Command("logout")
5
+ .description("Log out and clear stored session")
6
+ .action(() => {
7
+ const existing = getEmail();
8
+ if (!existing) {
9
+ console.log(chalk.gray("\nYou weren't even logged in. Dramatic much?\n"));
10
+ return;
11
+ }
12
+ clearSession();
13
+ console.log(chalk.green("\nGone.") + chalk.gray(" I already forgot about you.\n"));
14
+ });
15
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,QAAQ,GAAG,QAAQ,EAAE,CAAC;IAC5B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC,CAAC;QAC1E,OAAO;IACT,CAAC;IAED,YAAY,EAAE,CAAC;IACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC,CAAC;AACrF,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const mcpCommand: Command;
3
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,UAAU,SAuDnB,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ export const mcpCommand = new Command("mcp")
4
+ .description("Start MCP server (stdio) for AI agents in Cursor, Claude Desktop, Windsurf")
5
+ .action(async () => {
6
+ if (process.stdout.isTTY) {
7
+ // Running in a terminal — show setup instructions instead of starting stdio server
8
+ const mcpConfig = JSON.stringify({
9
+ mcpServers: {
10
+ "cynical-sally": {
11
+ command: "npx",
12
+ args: ["@cynicalsally/cli", "mcp"],
13
+ },
14
+ },
15
+ }, null, 2);
16
+ console.log(chalk.magenta("\n MCP Server") +
17
+ chalk.gray(" — Sally as a tool in your IDE\n"));
18
+ // Claude Code
19
+ console.log(chalk.white.bold(" Claude Code"));
20
+ console.log(chalk.cyan(" claude mcp add cynical-sally -- npx @cynicalsally/cli mcp\n"));
21
+ // Cursor
22
+ console.log(chalk.white.bold(" Cursor"));
23
+ console.log(chalk.gray(" Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json (per project):\n"));
24
+ console.log(chalk.cyan(" " + mcpConfig.replace(/\n/g, "\n ")));
25
+ console.log();
26
+ // Windsurf
27
+ console.log(chalk.white.bold(" Windsurf"));
28
+ console.log(chalk.gray(" Add to ~/.codeium/windsurf/mcp_config.json:\n"));
29
+ console.log(chalk.cyan(" " + mcpConfig.replace(/\n/g, "\n ")));
30
+ console.log();
31
+ // Tools
32
+ console.log(chalk.white.bold(" Available tools:"));
33
+ console.log(chalk.gray(" sally_roast ") + chalk.white("Code review with score, issues, and fixes"));
34
+ console.log(chalk.gray(" sally_explain ") + chalk.white("Explain code snippets or files"));
35
+ console.log(chalk.gray(" sally_review_pr ") + chalk.white("Review PR diffs"));
36
+ console.log(chalk.gray(" sally_refactor ") + chalk.white("Refactoring with before/after code"));
37
+ console.log(chalk.gray(" sally_brainstorm ") + chalk.white("Feedback on ideas and approaches"));
38
+ console.log(chalk.gray(" sally_frontend ") + chalk.white("Frontend/UI code review"));
39
+ console.log(chalk.gray(" sally_marketing ") + chalk.white("Marketing copy review"));
40
+ console.log(chalk.gray(" sally_usage ") + chalk.white("Check quota and account status"));
41
+ console.log();
42
+ return;
43
+ }
44
+ // Non-TTY: start the actual MCP stdio server
45
+ await import("../mcp-server.js");
46
+ });
47
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const resultsCommand: Command;
3
+ //# sourceMappingURL=results.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/commands/results.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,cAAc,SAqDvB,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import { readdirSync, readFileSync, unlinkSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { homedir } from "node:os";
6
+ import { displayRoast } from "../utils/output.js";
7
+ const RESULTS_DIR = join(homedir(), ".sally", "results");
8
+ export const resultsCommand = new Command("results")
9
+ .description("View your latest Full Truth review (background jobs)")
10
+ .option("--list", "List all saved reviews")
11
+ .option("--clear", "Clear all saved reviews")
12
+ .action((options) => {
13
+ try {
14
+ const files = readdirSync(RESULTS_DIR)
15
+ .filter((f) => f.endsWith(".json"))
16
+ .sort()
17
+ .reverse();
18
+ if (files.length === 0) {
19
+ console.log(chalk.gray("\n No saved reviews. Run a Full Truth review first.\n"));
20
+ return;
21
+ }
22
+ if (options.clear) {
23
+ for (const f of files) {
24
+ unlinkSync(join(RESULTS_DIR, f));
25
+ }
26
+ console.log(chalk.green(`\n Cleared ${files.length} saved review(s).\n`));
27
+ return;
28
+ }
29
+ if (options.list) {
30
+ console.log(chalk.magenta.bold("\n Saved Reviews\n"));
31
+ for (const f of files) {
32
+ try {
33
+ const data = JSON.parse(readFileSync(join(RESULTS_DIR, f), "utf-8"));
34
+ const score = data.data?.score?.toFixed(1) ?? "?";
35
+ const source = data._source || "unknown";
36
+ const date = data._savedAt ? new Date(data._savedAt).toLocaleString() : f;
37
+ console.log(chalk.gray(` ${date}`) + chalk.white(` — ${score}/10`) + chalk.gray(` (${source})`));
38
+ }
39
+ catch {
40
+ console.log(chalk.gray(` ${f}`));
41
+ }
42
+ }
43
+ console.log();
44
+ return;
45
+ }
46
+ // Show most recent result
47
+ const latest = files[0];
48
+ const data = JSON.parse(readFileSync(join(RESULTS_DIR, latest), "utf-8"));
49
+ if (data.data && data.voice && data.meta) {
50
+ displayRoast(data);
51
+ }
52
+ else {
53
+ console.log(chalk.yellow("\n Latest review file is malformed.\n"));
54
+ }
55
+ }
56
+ catch {
57
+ console.log(chalk.gray("\n No saved reviews yet. Run ") + chalk.cyan("sally roast -m full_truth --bg") + chalk.gray(" first.\n"));
58
+ }
59
+ });
60
+ //# sourceMappingURL=results.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"results.js","sourceRoot":"","sources":["../../src/commands/results.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const roastCommand: Command;
3
+ //# sourceMappingURL=roast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roast.d.ts","sourceRoot":"","sources":["../../src/commands/roast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,eAAO,MAAM,YAAY,SAyOrB,CAAC"}