wdyt 0.1.0 → 0.1.3

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,6 +1,6 @@
1
1
  {
2
2
  "name": "wdyt",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "Code review context builder for LLMs - what do you think?",
6
6
  "license": "MIT",
@@ -16,12 +16,12 @@
16
16
  ],
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "git+https://github.com/Bewinxed/reepoproompt.git"
19
+ "url": "git+https://github.com/Bewinxed/wdyt.git"
20
20
  },
21
21
  "bugs": {
22
- "url": "https://github.com/Bewinxed/reepoproompt/issues"
22
+ "url": "https://github.com/Bewinxed/wdyt/issues"
23
23
  },
24
- "homepage": "https://github.com/Bewinxed/reepoproompt#readme",
24
+ "homepage": "https://github.com/Bewinxed/wdyt#readme",
25
25
  "keywords": [
26
26
  "code-review",
27
27
  "llm",
package/src/cli.ts CHANGED
@@ -15,6 +15,7 @@
15
15
 
16
16
  import { defineCommand, runMain } from "citty";
17
17
  import type { CLIFlags } from "./types";
18
+ import pkg from "../package.json";
18
19
  import { windowsCommand } from "./commands/windows";
19
20
  import { builderCommand } from "./commands/builder";
20
21
  import {
@@ -179,7 +180,7 @@ if (args[0] === "init") {
179
180
  const main = defineCommand({
180
181
  meta: {
181
182
  name: "wdyt",
182
- version: "0.1.0",
183
+ version: pkg.version,
183
184
  description: "Code review context builder for LLMs",
184
185
  },
185
186
  args: {
@@ -18,9 +18,10 @@
18
18
  * }
19
19
  */
20
20
 
21
- import { existsSync, readFileSync, mkdirSync } from "fs";
21
+ import { mkdirSync } from "fs";
22
22
  import { join, dirname, basename } from "path";
23
23
  import { homedir } from "os";
24
+ import { $ } from "bun";
24
25
  import { getTab, getWindow } from "../state";
25
26
 
26
27
  /**
@@ -74,14 +75,79 @@ function generateUUID(): string {
74
75
  }
75
76
 
76
77
  /**
77
- * Read file content safely
78
+ * Check if claude CLI is available
78
79
  */
79
- function readFileSafe(path: string): { success: boolean; content?: string; error?: string } {
80
+ async function claudeCliAvailable(): Promise<boolean> {
80
81
  try {
81
- if (!existsSync(path)) {
82
+ await $`which claude`.quiet();
83
+ return true;
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Run a review using Claude CLI
91
+ * Returns the review output including verdict
92
+ */
93
+ async function runClaudeReview(contextPath: string, prompt: string): Promise<string> {
94
+ // Read the context file content first
95
+ const contextFile = Bun.file(contextPath);
96
+ const contextContent = await contextFile.text();
97
+
98
+ const reviewPrompt = `You are reviewing code changes. Analyze the following context and provide a thorough review.
99
+
100
+ Review instructions:
101
+ ${prompt}
102
+
103
+ <context>
104
+ ${contextContent}
105
+ </context>
106
+
107
+ Analyze the code for:
108
+ - Correctness - Logic errors, bugs, spec compliance
109
+ - Security - Injection risks, auth gaps, data exposure
110
+ - Simplicity - Over-engineering, unnecessary complexity
111
+ - Edge cases - Failure modes, boundary conditions
112
+
113
+ Provide findings organized by severity (Critical > Major > Minor).
114
+
115
+ REQUIRED: End your review with exactly one verdict tag:
116
+ <verdict>SHIP</verdict> - Code is production-ready
117
+ <verdict>NEEDS_WORK</verdict> - Issues must be fixed first
118
+ <verdict>MAJOR_RETHINK</verdict> - Fundamental problems require redesign`;
119
+
120
+ // Write prompt to temp file to avoid shell escaping issues
121
+ const tempPromptPath = join(getChatsDir(), `review-prompt-${Date.now()}.txt`);
122
+ await Bun.write(tempPromptPath, reviewPrompt);
123
+
124
+ try {
125
+ // Run claude CLI in print mode, reading from temp file
126
+ const result = await $`cat ${tempPromptPath} | claude -p`.text();
127
+
128
+ // Clean up temp file
129
+ await $`rm ${tempPromptPath}`.quiet();
130
+
131
+ return result.trim();
132
+ } catch (error) {
133
+ // Clean up temp file on error too
134
+ await $`rm ${tempPromptPath}`.quiet();
135
+
136
+ const message = error instanceof Error ? error.message : String(error);
137
+ throw new Error(`Claude CLI review failed: ${message}`);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Read file content safely using Bun's file API
143
+ */
144
+ async function readFileSafe(path: string): Promise<{ success: boolean; content?: string; error?: string }> {
145
+ try {
146
+ const file = Bun.file(path);
147
+ if (!(await file.exists())) {
82
148
  return { success: false, error: `File not found: ${path}` };
83
149
  }
84
- const content = readFileSync(path, "utf-8");
150
+ const content = await file.text();
85
151
  return { success: true, content };
86
152
  } catch (error) {
87
153
  const message = error instanceof Error ? error.message : String(error);
@@ -218,7 +284,7 @@ export async function chatSendCommand(
218
284
  const errors: string[] = [];
219
285
 
220
286
  for (const filePath of filePaths) {
221
- const result = readFileSafe(filePath);
287
+ const result = await readFileSafe(filePath);
222
288
  if (result.success && result.content !== undefined) {
223
289
  files.push({ path: filePath, content: result.content });
224
290
  } else {
@@ -252,41 +318,26 @@ export async function chatSendCommand(
252
318
  const chatPath = join(chatsDir, `${chatId}.xml`);
253
319
  await Bun.write(chatPath, xmlContent);
254
320
 
255
- // Check if this is a review request - if so, output subagent spawn instruction
321
+ // Check if this is a review request - if so, run Claude CLI to do the review
256
322
  const isReviewMode = payload.mode === "review";
257
323
 
258
324
  if (isReviewMode) {
259
- // Build the review output that instructs Claude Code to spawn quality-auditor
260
- const reviewOutput = `Chat: \`${chatId}\`
261
-
262
- [CLAUDE_CODE_REVIEW_REQUEST]
263
- The context has been exported to: ${chatPath}
264
-
265
- To complete this review, spawn a quality-auditor subagent with the Task tool:
266
-
267
- Task(subagent_type="flow-next:quality-auditor", prompt="""
268
- Review the code changes in this context file: ${chatPath}
269
-
270
- Read the file, analyze the code for:
271
- - Correctness - Logic errors, bugs
272
- - Security - Injection, auth gaps
273
- - Simplicity - Over-engineering
274
- - Edge cases - Failure modes
275
-
276
- Provide findings by severity (Critical > Major > Minor).
277
-
278
- REQUIRED: End with exactly one verdict tag:
279
- <verdict>SHIP</verdict> - Code is production-ready
280
- <verdict>NEEDS_WORK</verdict> - Issues must be fixed first
281
- """)
325
+ // Check if claude CLI is available
326
+ if (!(await claudeCliAvailable())) {
327
+ return {
328
+ success: false,
329
+ error: "Review mode requires Claude CLI (claude) to be installed and in PATH",
330
+ };
331
+ }
282
332
 
283
- The subagent will return the review findings and verdict.
284
- [/CLAUDE_CODE_REVIEW_REQUEST]`;
333
+ // Run the review using Claude CLI
334
+ console.error("Running review with Claude CLI...");
335
+ const reviewOutput = await runClaudeReview(chatPath, prompt);
285
336
 
286
337
  return {
287
338
  success: true,
288
- data: { id: chatId, path: chatPath },
289
- output: reviewOutput,
339
+ data: { id: chatId, path: chatPath, review: reviewOutput },
340
+ output: `Chat: \`${chatId}\`\n\n${reviewOutput}`,
290
341
  };
291
342
  }
292
343
 
@@ -8,11 +8,14 @@
8
8
  * bunx wdyt init --global # Install globally
9
9
  */
10
10
 
11
- import { mkdirSync, existsSync, symlinkSync, unlinkSync } from "fs";
11
+ import { mkdirSync, symlinkSync, unlinkSync } from "fs";
12
12
  import { join } from "path";
13
13
  import { homedir } from "os";
14
14
  import { $ } from "bun";
15
15
  import * as readline from "readline";
16
+ import pkg from "../../package.json";
17
+
18
+ const CURRENT_VERSION = pkg.version;
16
19
 
17
20
  interface InitOptions {
18
21
  rpAlias?: boolean;
@@ -20,6 +23,30 @@ interface InitOptions {
20
23
  global?: boolean;
21
24
  }
22
25
 
26
+ /**
27
+ * Get the installed version of wdyt binary
28
+ */
29
+ async function getInstalledVersion(binPath: string): Promise<string | null> {
30
+ try {
31
+ const result = await $`${binPath} --version 2>/dev/null`.text();
32
+ return result.trim() || null;
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Compare semver versions, returns true if v1 > v2
40
+ */
41
+ function isNewerVersion(v1: string, v2: string): boolean {
42
+ const parse = (v: string) => v.split(".").map(Number);
43
+ const [a1, b1, c1] = parse(v1);
44
+ const [a2, b2, c2] = parse(v2);
45
+ if (a1 !== a2) return a1 > a2;
46
+ if (b1 !== b2) return b1 > b2;
47
+ return c1 > c2;
48
+ }
49
+
23
50
  /**
24
51
  * Prompt user for yes/no input
25
52
  */
@@ -102,25 +129,40 @@ export async function initCommand(options: InitOptions): Promise<{
102
129
  };
103
130
  }
104
131
 
105
- // 2. Check if already installed globally
132
+ // 2. Check if already installed globally and compare versions
106
133
  const alreadyInstalled = await commandExists("wdyt");
134
+ const wdytPath = join(binDir, "wdyt");
135
+ let needsUpdate = false;
107
136
 
108
- if (alreadyInstalled && !options.global) {
109
- lines.push("");
110
- lines.push("✓ wdyt is already available in PATH");
137
+ if (alreadyInstalled) {
138
+ const installedVersion = await getInstalledVersion(wdytPath);
139
+ if (installedVersion) {
140
+ if (isNewerVersion(CURRENT_VERSION, installedVersion)) {
141
+ lines.push("");
142
+ lines.push(`⚠️ Installed version: ${installedVersion}, available: ${CURRENT_VERSION}`);
143
+ lines.push(" Updating to latest version...");
144
+ needsUpdate = true;
145
+ } else if (!options.global) {
146
+ lines.push("");
147
+ lines.push(`✓ wdyt ${installedVersion} is already installed and up to date`);
148
+ }
149
+ } else if (!options.global) {
150
+ lines.push("");
151
+ lines.push("✓ wdyt is already available in PATH");
152
+ }
111
153
  }
112
154
 
113
- // 3. Global install if requested
114
- if (options.global) {
115
- lines.push("");
116
- lines.push("Installing globally...");
155
+ // 3. Global install if requested OR if update needed
156
+ if (options.global || needsUpdate) {
157
+ if (!needsUpdate) {
158
+ lines.push("");
159
+ lines.push("Installing globally...");
160
+ }
117
161
 
118
162
  try {
119
163
  // Ensure bin directory exists
120
164
  mkdirSync(binDir, { recursive: true });
121
165
 
122
- // Get the path to the current executable or script
123
- const currentExe = process.argv[1];
124
166
  const targetPath = join(binDir, "wdyt");
125
167
 
126
168
  // Build the binary
@@ -128,7 +170,7 @@ export async function initCommand(options: InitOptions): Promise<{
128
170
  const srcDir = join(import.meta.dir, "..");
129
171
  await $`bun build ${join(srcDir, "cli.ts")} --compile --outfile ${targetPath}`.quiet();
130
172
 
131
- lines.push(` ✓ Installed to ${targetPath}`);
173
+ lines.push(` ✓ ${needsUpdate ? "Updated" : "Installed"} to ${targetPath} (v${CURRENT_VERSION})`);
132
174
 
133
175
  // Check if ~/.local/bin is in PATH
134
176
  const path = process.env.PATH || "";
@@ -169,12 +211,12 @@ export async function initCommand(options: InitOptions): Promise<{
169
211
  mkdirSync(binDir, { recursive: true });
170
212
 
171
213
  // Remove existing symlink if present
172
- if (existsSync(rpCliPath)) {
214
+ if (await Bun.file(rpCliPath).exists()) {
173
215
  unlinkSync(rpCliPath);
174
216
  }
175
217
 
176
218
  // Check if wdyt binary exists
177
- if (existsSync(secondOpinionPath)) {
219
+ if (await Bun.file(secondOpinionPath).exists()) {
178
220
  symlinkSync(secondOpinionPath, rpCliPath);
179
221
  lines.push(` ✓ Created symlink: rp-cli -> wdyt`);
180
222
  } else {
package/src/state.ts CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { join } from "path";
9
9
  import { homedir } from "os";
10
- import { mkdirSync, existsSync, renameSync } from "fs";
10
+ import { mkdirSync, renameSync } from "fs";
11
11
  import type { StateFile, Window, Tab, TabUpdate } from "./types";
12
12
 
13
13
  const STATE_VERSION = 1;
@@ -60,14 +60,14 @@ function generateUUID(): string {
60
60
  export async function loadState(): Promise<StateFile> {
61
61
  const statePath = getStatePath();
62
62
 
63
- // Check if file exists first
64
- if (!existsSync(statePath)) {
63
+ // Check if file exists first using Bun's file API
64
+ const stateFile = Bun.file(statePath);
65
+ if (!(await stateFile.exists())) {
65
66
  return createDefaultState();
66
67
  }
67
68
 
68
69
  try {
69
- const file = Bun.file(statePath);
70
- const content = await file.text();
70
+ const content = await stateFile.text();
71
71
  const state = JSON.parse(content) as StateFile;
72
72
 
73
73
  // Validate basic structure