wdyt 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 user
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,83 @@
1
+ # wdyt
2
+
3
+ Code review context builder for LLMs - get a second opinion on your code.
4
+
5
+ A CLI tool that exports code context for AI review, compatible with flowctl/flow-next.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # Run directly with bunx (recommended)
11
+ bunx wdyt init
12
+
13
+ # Or install globally
14
+ bun add -g wdyt
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ # Interactive setup - creates data directory and optionally adds rp-cli alias
21
+ bunx wdyt init
22
+
23
+ # List windows
24
+ wdyt -e 'windows'
25
+
26
+ # Create a new tab in window 1
27
+ wdyt -w 1 -e 'builder {}'
28
+
29
+ # Add files to selection
30
+ wdyt -w 1 -t <tab-id> -e 'select add src/cli.ts'
31
+
32
+ # Export context for review
33
+ wdyt -w 1 -t <tab-id> -e 'call chat_send {"mode":"review"}'
34
+ ```
35
+
36
+ ## Commands
37
+
38
+ ### Setup
39
+
40
+ ```bash
41
+ wdyt init # Interactive setup
42
+ wdyt init --global # Install binary globally
43
+ wdyt init --rp-alias # Create rp-cli alias (for flowctl)
44
+ wdyt init --no-alias # Skip rp-cli alias prompt
45
+ ```
46
+
47
+ ### Expressions
48
+
49
+ | Expression | Description |
50
+ |------------|-------------|
51
+ | `windows` | List all windows |
52
+ | `builder {"summary":"..."}` | Create a new tab |
53
+ | `prompt get` | Get current prompt |
54
+ | `prompt export <file>` | Export prompt to file |
55
+ | `select get` | Get selected files |
56
+ | `select add "path"` | Add file to selection |
57
+ | `call chat_send {...}` | Export context for review |
58
+
59
+ ### Flags
60
+
61
+ | Flag | Description |
62
+ |------|-------------|
63
+ | `--raw-json` | Output raw JSON |
64
+ | `-w <id>` | Window ID |
65
+ | `-t <id>` | Tab ID |
66
+ | `-e <expr>` | Expression to execute |
67
+
68
+ ## flowctl Compatibility
69
+
70
+ This tool is compatible with [flow-next](https://github.com/gmickel/claude-marketplace) and provides the `rp-cli` interface expected by flowctl.
71
+
72
+ ```bash
73
+ # Create the rp-cli alias during init
74
+ bunx wdyt init --rp-alias
75
+ ```
76
+
77
+ ## Requirements
78
+
79
+ - [Bun](https://bun.sh) runtime
80
+
81
+ ## License
82
+
83
+ MIT
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "wdyt",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Code review context builder for LLMs - what do you think?",
6
+ "license": "MIT",
7
+ "author": "bewinxed",
8
+ "main": "src/cli.ts",
9
+ "bin": {
10
+ "wdyt": "./src/cli.ts"
11
+ },
12
+ "files": [
13
+ "src/**/*.ts",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/Bewinxed/reepoproompt.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/Bewinxed/reepoproompt/issues"
23
+ },
24
+ "homepage": "https://github.com/Bewinxed/reepoproompt#readme",
25
+ "keywords": [
26
+ "code-review",
27
+ "llm",
28
+ "context",
29
+ "ai",
30
+ "claude",
31
+ "cli",
32
+ "bun"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "scripts": {
41
+ "dev": "bun run src/cli.ts",
42
+ "build": "bun build ./src/cli.ts --compile --outfile wdyt",
43
+ "test": "bun test",
44
+ "prepublishOnly": "bun test"
45
+ },
46
+ "dependencies": {
47
+ "citty": "^0.1.6"
48
+ },
49
+ "devDependencies": {
50
+ "@types/bun": "^1.1.0"
51
+ }
52
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * wdyt - Code review context builder for LLMs
4
+ *
5
+ * Get a second opinion on your code by building context for AI review.
6
+ * Compatible with flowctl.py (rp-cli interface).
7
+ *
8
+ * Commands:
9
+ * - windows: list windows
10
+ * - builder: create tabs
11
+ * - prompt get/set/export: manage prompts
12
+ * - select get/add: track file selection
13
+ * - chat_send: export context for review
14
+ */
15
+
16
+ import { defineCommand, runMain } from "citty";
17
+ import type { CLIFlags } from "./types";
18
+ import { windowsCommand } from "./commands/windows";
19
+ import { builderCommand } from "./commands/builder";
20
+ import {
21
+ promptGetCommand,
22
+ promptSetCommand,
23
+ promptExportCommand,
24
+ } from "./commands/prompt";
25
+ import { selectGetCommand, selectAddCommand } from "./commands/select";
26
+ import { chatSendCommand } from "./commands/chat";
27
+ import { initCommand, parseInitArgs } from "./commands/init";
28
+
29
+ /**
30
+ * Parse and execute an expression
31
+ */
32
+ async function executeExpression(
33
+ expression: string,
34
+ flags: CLIFlags
35
+ ): Promise<{ success: boolean; data?: unknown; output?: string; error?: string }> {
36
+ const expr = expression.trim();
37
+
38
+ // Parse command and arguments
39
+ // Expressions can be: "windows", "builder {json}", "prompt get", etc.
40
+ const match = expr.match(/^(\w+)(?:\s+(.*))?$/);
41
+ if (!match) {
42
+ return { success: false, error: `Invalid expression: ${expression}` };
43
+ }
44
+
45
+ const [, command, args] = match;
46
+
47
+ switch (command) {
48
+ case "windows":
49
+ return await windowsCommand();
50
+
51
+ case "builder":
52
+ if (!flags.window) {
53
+ return { success: false, error: "builder requires -w <window>" };
54
+ }
55
+ return await builderCommand(flags.window, args);
56
+
57
+ case "prompt": {
58
+ if (!flags.window || !flags.tab) {
59
+ return {
60
+ success: false,
61
+ error: "prompt commands require -w <window> -t <tab>",
62
+ };
63
+ }
64
+
65
+ // Parse subcommand: "get", "export <file>"
66
+ const promptArgs = args?.trim();
67
+ if (!promptArgs || promptArgs === "get") {
68
+ return await promptGetCommand(flags.window, flags.tab);
69
+ }
70
+
71
+ if (promptArgs.startsWith("export ")) {
72
+ // Extract file path - may be quoted with shlex.quote
73
+ const filePath = promptArgs.slice(7).trim().replace(/^'|'$/g, "");
74
+ return await promptExportCommand(flags.window, flags.tab, filePath);
75
+ }
76
+
77
+ return { success: false, error: `Unknown prompt subcommand: ${promptArgs}` };
78
+ }
79
+
80
+ case "select": {
81
+ if (!flags.window || !flags.tab) {
82
+ return {
83
+ success: false,
84
+ error: "select commands require -w <window> -t <tab>",
85
+ };
86
+ }
87
+
88
+ // Parse subcommand: "get", "add <paths>"
89
+ const selectArgs = args?.trim();
90
+ if (!selectArgs || selectArgs === "get") {
91
+ return await selectGetCommand(flags.window, flags.tab);
92
+ }
93
+
94
+ if (selectArgs.startsWith("add ")) {
95
+ const pathsArg = selectArgs.slice(4).trim();
96
+ return await selectAddCommand(flags.window, flags.tab, pathsArg);
97
+ }
98
+
99
+ return { success: false, error: `Unknown select subcommand: ${selectArgs}` };
100
+ }
101
+
102
+ case "call": {
103
+ // Handle "call prompt {json}" and "call chat_send {json}"
104
+ if (args?.startsWith("prompt ")) {
105
+ if (!flags.window || !flags.tab) {
106
+ return {
107
+ success: false,
108
+ error: "prompt requires -w <window> -t <tab>",
109
+ };
110
+ }
111
+ const payload = args.slice(7).trim();
112
+ return await promptSetCommand(flags.window, flags.tab, payload);
113
+ }
114
+
115
+ if (args?.startsWith("chat_send")) {
116
+ if (!flags.window || !flags.tab) {
117
+ return {
118
+ success: false,
119
+ error: "chat_send requires -w <window> -t <tab>",
120
+ };
121
+ }
122
+ // Extract payload - "chat_send {json}" or "chat_send"
123
+ const payload = args.slice(9).trim() || "{}";
124
+ return await chatSendCommand(flags.window, flags.tab, payload);
125
+ }
126
+ return { success: false, error: `Unknown call: ${args}` };
127
+ }
128
+
129
+ default:
130
+ return { success: false, error: `Unknown command: ${command}` };
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Format output based on --raw-json flag
136
+ */
137
+ function formatOutput(
138
+ result: { success: boolean; data?: unknown; output?: string; error?: string },
139
+ rawJson: boolean
140
+ ): string {
141
+ if (rawJson) {
142
+ if (result.success) {
143
+ return JSON.stringify(result.data);
144
+ }
145
+ return JSON.stringify({ error: result.error });
146
+ }
147
+
148
+ // For non-raw-json mode, prefer the output field if present
149
+ // This allows commands like builder to return "Tab: <uuid>" format
150
+ if (result.success) {
151
+ // Check if output is defined (not undefined), allowing empty strings
152
+ if (result.output !== undefined) {
153
+ return result.output;
154
+ }
155
+ if (typeof result.data === "object") {
156
+ return JSON.stringify(result.data, null, 2);
157
+ }
158
+ return String(result.data);
159
+ }
160
+
161
+ return `Error: ${result.error}`;
162
+ }
163
+
164
+ // Check if init command and handle it before citty
165
+ const args = process.argv.slice(2);
166
+ if (args[0] === "init") {
167
+ const options = parseInitArgs(args.slice(1));
168
+ initCommand(options).then((result) => {
169
+ if (result.success) {
170
+ console.log(result.output);
171
+ process.exit(0);
172
+ } else {
173
+ console.error(result.error);
174
+ process.exit(1);
175
+ }
176
+ });
177
+ } else {
178
+ // Only run citty for non-init commands
179
+ const main = defineCommand({
180
+ meta: {
181
+ name: "wdyt",
182
+ version: "0.1.0",
183
+ description: "Code review context builder for LLMs",
184
+ },
185
+ args: {
186
+ "raw-json": {
187
+ type: "boolean",
188
+ description: "Output raw JSON (no formatting)",
189
+ default: false,
190
+ },
191
+ w: {
192
+ type: "string",
193
+ description: "Window ID",
194
+ },
195
+ t: {
196
+ type: "string",
197
+ description: "Tab ID",
198
+ },
199
+ e: {
200
+ type: "string",
201
+ description: "Expression to execute",
202
+ },
203
+ },
204
+ async run({ args }) {
205
+ // Parse and validate window ID
206
+ let windowId: number | undefined;
207
+ if (args.w) {
208
+ windowId = parseInt(args.w, 10);
209
+ if (isNaN(windowId) || windowId < 1) {
210
+ console.error(`Error: Invalid window ID "${args.w}". Must be a positive integer.`);
211
+ process.exit(1);
212
+ }
213
+ }
214
+
215
+ const flags: CLIFlags = {
216
+ rawJson: args["raw-json"],
217
+ window: windowId,
218
+ tab: args.t,
219
+ expression: args.e,
220
+ };
221
+
222
+ // If no expression, show help message
223
+ if (!flags.expression) {
224
+ console.log("wdyt - Code review context builder for LLMs");
225
+ console.log("");
226
+ console.log("Setup:");
227
+ console.log(" wdyt init # Interactive setup (prompts for options)");
228
+ console.log(" wdyt init --global # Install binary globally");
229
+ console.log(" wdyt init --rp-alias # Create rp-cli alias (for flowctl)");
230
+ console.log(" wdyt init --no-alias # Skip rp-cli alias prompt");
231
+ console.log("");
232
+ console.log("Usage:");
233
+ console.log(" wdyt --raw-json -e <expression>");
234
+ console.log(" wdyt -w <window> -e <expression>");
235
+ console.log(" wdyt -w <window> -t <tab> -e <expression>");
236
+ console.log("");
237
+ console.log("Expressions:");
238
+ console.log(" windows List all windows");
239
+ console.log(' builder {"summary":"..."} Create a new tab');
240
+ console.log(" prompt get Get current prompt");
241
+ console.log(" prompt export <file> Export prompt to file");
242
+ console.log(" select get Get selected files");
243
+ console.log(' select add "path" Add file to selection');
244
+ console.log(" call chat_send {...} Export context for review");
245
+ console.log("");
246
+ console.log("Flags:");
247
+ console.log(" --raw-json Output raw JSON");
248
+ console.log(" -w <id> Window ID");
249
+ console.log(" -t <id> Tab ID");
250
+ console.log(" -e <expr> Expression to execute");
251
+ process.exit(0);
252
+ }
253
+
254
+ // Execute expression
255
+ const result = await executeExpression(flags.expression, flags);
256
+ const output = formatOutput(result, flags.rawJson);
257
+ console.log(output);
258
+
259
+ // Exit with code 1 on error
260
+ if (!result.success) {
261
+ process.exit(1);
262
+ }
263
+ },
264
+ });
265
+
266
+ runMain(main);
267
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Builder command - create a new tab
3
+ *
4
+ * Parses JSON arg: {summary: string}
5
+ * Returns: Tab: <uuid>
6
+ * Compatible with flowctl.py parsing at line 255-259:
7
+ * match = re.search(r"Tab:\s*([A-Za-z0-9-]+)", output)
8
+ */
9
+
10
+ import { createTab, getWindow } from "../state";
11
+ import type { BuilderConfig } from "../types";
12
+
13
+ /**
14
+ * Builder command response
15
+ */
16
+ export interface BuilderResponse {
17
+ tabId: string;
18
+ }
19
+
20
+ /**
21
+ * Parse builder JSON argument
22
+ * @param args - JSON string like {"summary": "test"}
23
+ * @returns Parsed BuilderConfig or null if invalid
24
+ */
25
+ function parseBuilderArgs(args?: string): BuilderConfig | null {
26
+ if (!args) {
27
+ // Empty config is valid - just creates a blank tab
28
+ return {};
29
+ }
30
+
31
+ try {
32
+ const config = JSON.parse(args.trim()) as BuilderConfig;
33
+ return config;
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Execute the builder command
41
+ * Creates a new tab in the specified window
42
+ *
43
+ * @param windowId - The window ID to create the tab in
44
+ * @param args - JSON string with optional summary, path, name
45
+ * @returns Tab: <uuid> on success
46
+ */
47
+ export async function builderCommand(
48
+ windowId: number,
49
+ args?: string
50
+ ): Promise<{
51
+ success: boolean;
52
+ data?: BuilderResponse;
53
+ output?: string;
54
+ error?: string;
55
+ }> {
56
+ try {
57
+ // Verify window exists
58
+ await getWindow(windowId);
59
+
60
+ // Parse builder config (optional)
61
+ const config = parseBuilderArgs(args);
62
+ if (config === null) {
63
+ return {
64
+ success: false,
65
+ error: `Invalid builder config JSON: ${args}`,
66
+ };
67
+ }
68
+
69
+ // Create the tab
70
+ const tab = await createTab(windowId);
71
+
72
+ // Return in the format flowctl.py expects: Tab: <uuid>
73
+ return {
74
+ success: true,
75
+ data: { tabId: tab.id },
76
+ output: `Tab: ${tab.id}`,
77
+ };
78
+ } catch (error) {
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ return {
81
+ success: false,
82
+ error: `Failed to create tab: ${message}`,
83
+ };
84
+ }
85
+ }