diffact 0.1.0 → 0.2.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 (52) hide show
  1. package/dist/cli.mjs +2 -0
  2. package/dist/index-node-bKTmbwGt.mjs +1 -0
  3. package/dist/src-CPKE75x0.mjs +11 -0
  4. package/dist/src-Ceryd8j5.mjs +1 -0
  5. package/package.json +4 -5
  6. package/web/dist/assets/{code-block-37QAKDTI-C97XC0lL.js → code-block-37QAKDTI-yDNOZoY4.js} +1 -1
  7. package/web/dist/assets/{index-DWCfDth4.js → index-BlaXWu6U.js} +30 -30
  8. package/web/dist/assets/index-DMEToi1s.css +1 -0
  9. package/web/dist/index.html +2 -2
  10. package/dist/agent-manager.d.ts +0 -32
  11. package/dist/agent-manager.js +0 -502
  12. package/dist/app-server-client.d.ts +0 -38
  13. package/dist/app-server-client.js +0 -249
  14. package/dist/capabilities.d.ts +0 -2
  15. package/dist/capabilities.js +0 -27
  16. package/dist/cli.d.ts +0 -6
  17. package/dist/cli.js +0 -13
  18. package/dist/command-runner.d.ts +0 -83
  19. package/dist/command-runner.js +0 -427
  20. package/dist/editors.d.ts +0 -26
  21. package/dist/editors.js +0 -144
  22. package/dist/gh.d.ts +0 -61
  23. package/dist/gh.js +0 -185
  24. package/dist/git.d.ts +0 -57
  25. package/dist/git.js +0 -482
  26. package/dist/http.d.ts +0 -7
  27. package/dist/http.js +0 -98
  28. package/dist/index-node.d.ts +0 -5
  29. package/dist/index-node.js +0 -51
  30. package/dist/index.d.ts +0 -6
  31. package/dist/index.js +0 -1011
  32. package/dist/list-directories.d.ts +0 -8
  33. package/dist/list-directories.js +0 -32
  34. package/dist/log.d.ts +0 -2
  35. package/dist/log.js +0 -2
  36. package/dist/open-browser.d.ts +0 -5
  37. package/dist/open-browser.js +0 -23
  38. package/dist/project-commands.d.ts +0 -17
  39. package/dist/project-commands.js +0 -152
  40. package/dist/project-path.d.ts +0 -5
  41. package/dist/project-path.js +0 -33
  42. package/dist/runtime.d.ts +0 -65
  43. package/dist/runtime.js +0 -235
  44. package/dist/static.d.ts +0 -10
  45. package/dist/static.js +0 -127
  46. package/dist/types.d.ts +0 -17
  47. package/dist/types.js +0 -1
  48. package/dist/utils.d.ts +0 -3
  49. package/dist/utils.js +0 -26
  50. package/dist/ws-hub.d.ts +0 -20
  51. package/dist/ws-hub.js +0 -123
  52. package/web/dist/assets/index-CRDz04kv.css +0 -1
@@ -1,427 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import fs from "node:fs/promises";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import { log } from "./log.js";
6
- import { runtime, } from "./runtime.js";
7
- const DEFAULT_TERMINAL_COLS = 120;
8
- const DEFAULT_TERMINAL_ROWS = 30;
9
- const MIN_TERMINAL_COLS = 2;
10
- const MIN_TERMINAL_ROWS = 1;
11
- const RUN_STOP_TIMEOUT_MS = 2000;
12
- const RUN_CLEANUP_DELAY_MS = 5 * 60 * 1000;
13
- const RUNS = new Map();
14
- const OUTPUT_DIR = path.join(os.tmpdir(), "diffact-command-output");
15
- const TERMINAL_POOL = new Map();
16
- let outputDirPromise = null;
17
- const deriveStatus = (exitCode, signal) => {
18
- if (signal) {
19
- return "stopped";
20
- }
21
- if (exitCode === 0) {
22
- return "completed";
23
- }
24
- return "failed";
25
- };
26
- const resolveShellPath = (value) => {
27
- const trimmed = value.trim();
28
- if (!trimmed) {
29
- return "";
30
- }
31
- if (trimmed.includes("/")) {
32
- return trimmed;
33
- }
34
- return runtime.which(trimmed) ?? trimmed;
35
- };
36
- const getDefaultShellPath = () => {
37
- if (process.env.SHELL) {
38
- return resolveShellPath(process.env.SHELL);
39
- }
40
- try {
41
- const info = os.userInfo();
42
- if (info.shell) {
43
- return resolveShellPath(info.shell);
44
- }
45
- }
46
- catch {
47
- return "";
48
- }
49
- return "";
50
- };
51
- const shellSupportsProcessSubstitution = (shellPath) => {
52
- const name = path.basename(shellPath);
53
- return name === "bash" || name === "zsh";
54
- };
55
- const buildInteractiveShellArgs = (shellPath, commandLine) => {
56
- const name = path.basename(shellPath);
57
- if (name === "bash" || name === "zsh" || name === "fish" || name === "sh") {
58
- return ["-ilc", commandLine];
59
- }
60
- return ["-ic", commandLine];
61
- };
62
- const toPtyEnv = (env) => {
63
- const next = {};
64
- for (const [key, value] of Object.entries(env)) {
65
- if (typeof value === "string") {
66
- next[key] = value;
67
- }
68
- }
69
- return next;
70
- };
71
- const ensureOutputDir = async () => {
72
- if (!outputDirPromise) {
73
- outputDirPromise = fs
74
- .mkdir(OUTPUT_DIR, { recursive: true })
75
- .then(() => undefined);
76
- }
77
- await outputDirPromise;
78
- };
79
- const quoteShellArg = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
80
- const buildCommandLine = (plan) => plan.commandLine ?? [plan.command, ...plan.args].map(quoteShellArg).join(" ");
81
- const acquireTerminal = (projectRoot, cols, rows, onData) => {
82
- const pool = TERMINAL_POOL.get(projectRoot) ?? [];
83
- const existing = pool.find((entry) => !entry.busy);
84
- if (existing) {
85
- existing.busy = true;
86
- existing.onData = onData;
87
- if (existing.cols !== cols || existing.rows !== rows) {
88
- existing.cols = cols;
89
- existing.rows = rows;
90
- existing.terminal.resize(cols, rows);
91
- }
92
- return existing;
93
- }
94
- const entry = {
95
- id: randomUUID(),
96
- terminal: null,
97
- busy: true,
98
- cols,
99
- rows,
100
- onData,
101
- };
102
- entry.terminal = runtime.createTerminal({
103
- cols,
104
- rows,
105
- data: (_terminal, data) => {
106
- entry.onData?.(data);
107
- },
108
- });
109
- pool.push(entry);
110
- TERMINAL_POOL.set(projectRoot, pool);
111
- return entry;
112
- };
113
- const releaseTerminal = (projectRoot, entry) => {
114
- const pool = TERMINAL_POOL.get(projectRoot);
115
- if (!pool) {
116
- return;
117
- }
118
- entry.busy = false;
119
- entry.onData = undefined;
120
- };
121
- const readFileSafe = async (filePath) => {
122
- if (!filePath) {
123
- return null;
124
- }
125
- try {
126
- return await fs.readFile(filePath, "utf8");
127
- }
128
- catch {
129
- return null;
130
- }
131
- };
132
- const createExitTracker = () => {
133
- let resolved = false;
134
- let resolveExit = null;
135
- const exitPromise = new Promise((resolve) => {
136
- resolveExit = resolve;
137
- });
138
- const resolveOnce = (value) => {
139
- if (resolved) {
140
- return;
141
- }
142
- resolved = true;
143
- resolveExit?.(value);
144
- };
145
- const onExit = (_proc, exitCode, signalCode, error) => {
146
- if (error) {
147
- log.error({ err: error }, "command run exit error");
148
- }
149
- resolveOnce({
150
- exitCode: exitCode ?? null,
151
- signal: signalCode ?? null,
152
- });
153
- };
154
- return { exitPromise, onExit, resolveOnce };
155
- };
156
- const scheduleCleanup = (runId) => {
157
- setTimeout(() => {
158
- const record = RUNS.get(runId);
159
- if (record?.stdoutPath) {
160
- fs.unlink(record.stdoutPath).catch(() => undefined);
161
- }
162
- if (record?.stderrPath) {
163
- fs.unlink(record.stderrPath).catch(() => undefined);
164
- }
165
- RUNS.delete(runId);
166
- }, RUN_CLEANUP_DELAY_MS);
167
- };
168
- export const startCommandRun = async (params) => {
169
- const runId = randomUUID();
170
- let proc;
171
- let spawnFile = "";
172
- let spawnCwd = "";
173
- let terminalLease = null;
174
- let terminalId = "";
175
- try {
176
- const startedAt = Date.now();
177
- const output = {
178
- stdout: "",
179
- stderr: "",
180
- combined: "",
181
- };
182
- const terminalDecoder = new TextDecoder();
183
- const pushOutput = (stream, data) => {
184
- if (!data) {
185
- return;
186
- }
187
- if (stream === "stdout") {
188
- output.stdout += data;
189
- }
190
- else {
191
- output.stderr += data;
192
- }
193
- output.combined += data;
194
- params.listener?.onOutput?.({
195
- runId,
196
- stream,
197
- data,
198
- });
199
- };
200
- const defaultShell = getDefaultShellPath();
201
- const shellPath = defaultShell || "/bin/sh";
202
- const commandLine = buildCommandLine(params.plan);
203
- const env = toPtyEnv({
204
- ...process.env,
205
- ...(params.plan.env || {}),
206
- SHELL: shellPath,
207
- TERM: "xterm-256color",
208
- FORCE_COLOR: "1",
209
- CLICOLOR_FORCE: "1",
210
- });
211
- const cwd = params.plan.cwd || params.projectRoot;
212
- spawnCwd = cwd;
213
- try {
214
- const stat = await fs.stat(cwd);
215
- if (!stat.isDirectory()) {
216
- throw new Error(`invalid_cwd:${cwd}`);
217
- }
218
- }
219
- catch (error) {
220
- const detail = error instanceof Error ? error.message : `invalid_cwd:${cwd}`;
221
- throw new Error(detail);
222
- }
223
- let file = shellPath;
224
- let args = buildInteractiveShellArgs(shellPath, commandLine);
225
- let stdoutPath;
226
- let stderrPath;
227
- let wrapperShell = shellSupportsProcessSubstitution(shellPath)
228
- ? shellPath
229
- : "";
230
- if (wrapperShell) {
231
- await ensureOutputDir();
232
- stdoutPath = path.join(OUTPUT_DIR, `${runId}.out`);
233
- stderrPath = path.join(OUTPUT_DIR, `${runId}.err`);
234
- const wrapped = `set -o pipefail; ${commandLine} > >(tee -a ${quoteShellArg(stdoutPath)}) 2> >(tee -a ${quoteShellArg(stderrPath)} >&2)`;
235
- file = wrapperShell;
236
- args = buildInteractiveShellArgs(wrapperShell, wrapped);
237
- }
238
- const spawnProcess = async () => {
239
- const { exitPromise, onExit, resolveOnce } = createExitTracker();
240
- spawnFile = file;
241
- log.info({
242
- runId,
243
- commandId: params.command.id,
244
- commandName: params.command.name,
245
- commandSource: params.command.source,
246
- projectRoot: params.projectRoot,
247
- cwd,
248
- file,
249
- args,
250
- defaultShell,
251
- shellPath,
252
- wrapperShell,
253
- }, "command run spawn");
254
- if (file.includes("/")) {
255
- await fs.access(file);
256
- }
257
- const cmd = [file, ...args];
258
- const terminalCols = Math.max(MIN_TERMINAL_COLS, Math.floor(params.terminal?.cols ?? DEFAULT_TERMINAL_COLS));
259
- const terminalRows = Math.max(MIN_TERMINAL_ROWS, Math.floor(params.terminal?.rows ?? DEFAULT_TERMINAL_ROWS));
260
- terminalLease = acquireTerminal(params.projectRoot, terminalCols, terminalRows, (data) => {
261
- const text = terminalDecoder.decode(data, { stream: true });
262
- if (text) {
263
- pushOutput("stdout", text);
264
- }
265
- });
266
- terminalId = terminalLease.id;
267
- const process = runtime.spawn(cmd, {
268
- cwd,
269
- env,
270
- terminal: terminalLease.terminal,
271
- onExit,
272
- });
273
- process.exited
274
- .then((exitCode) => {
275
- resolveOnce({
276
- exitCode: exitCode ?? null,
277
- signal: process.signalCode ? String(process.signalCode) : null,
278
- });
279
- })
280
- .catch((error) => {
281
- log.error({ err: error, runId, file: spawnFile }, "command run exit failed");
282
- resolveOnce({ exitCode: null, signal: null });
283
- });
284
- return { process, exitPromise };
285
- };
286
- let exitPromise;
287
- try {
288
- const result = await spawnProcess();
289
- proc = result.process;
290
- exitPromise = result.exitPromise;
291
- }
292
- catch (error) {
293
- if (terminalLease) {
294
- releaseTerminal(params.projectRoot, terminalLease);
295
- terminalLease = null;
296
- terminalId = "";
297
- }
298
- if (!wrapperShell) {
299
- throw error;
300
- }
301
- log.warn({ err: error, runId, wrapperShell }, "command run wrapper failed, retrying without wrapper");
302
- wrapperShell = "";
303
- file = shellPath;
304
- args = buildInteractiveShellArgs(shellPath, commandLine);
305
- stdoutPath = undefined;
306
- stderrPath = undefined;
307
- const result = await spawnProcess();
308
- proc = result.process;
309
- exitPromise = result.exitPromise;
310
- }
311
- const record = {
312
- id: runId,
313
- command: params.command,
314
- projectRoot: params.projectRoot,
315
- terminalId,
316
- status: "running",
317
- exitCode: null,
318
- signal: null,
319
- startedAt: new Date(startedAt).toISOString(),
320
- output,
321
- process: proc,
322
- stdoutPath,
323
- stderrPath,
324
- };
325
- RUNS.set(runId, record);
326
- params.listener?.onStatus?.(record);
327
- const finalize = async (exitCode, signal) => {
328
- const tail = terminalDecoder.decode();
329
- if (tail) {
330
- pushOutput("stdout", tail);
331
- }
332
- record.exitCode = exitCode;
333
- record.signal = signal ? String(signal) : null;
334
- record.status = deriveStatus(exitCode, record.signal);
335
- record.completedAt = new Date().toISOString();
336
- const [stdout, stderr] = await Promise.all([
337
- readFileSafe(stdoutPath),
338
- readFileSafe(stderrPath),
339
- ]);
340
- if (stdout !== null) {
341
- record.output.stdout = stdout;
342
- }
343
- if (stderr !== null) {
344
- record.output.stderr = stderr;
345
- }
346
- if (!stdoutPath && !stderrPath) {
347
- if (!record.output.stdout && !record.output.stderr) {
348
- record.output.stdout = record.output.combined;
349
- record.output.stderr = "";
350
- }
351
- }
352
- else if (!stdoutPath && !record.output.stdout) {
353
- record.output.stdout = record.output.combined;
354
- }
355
- scheduleCleanup(runId);
356
- params.listener?.onStatus?.(record);
357
- if (terminalLease) {
358
- releaseTerminal(params.projectRoot, terminalLease);
359
- terminalLease = null;
360
- }
361
- log.info({
362
- runId,
363
- status: record.status,
364
- exitCode: record.exitCode,
365
- signal: record.signal,
366
- }, "command run exit");
367
- };
368
- // Always return immediately with running status; completion is notified via onStatus callback
369
- exitPromise.then(({ exitCode, signal }) => {
370
- void finalize(exitCode, signal);
371
- });
372
- return {
373
- status: "running",
374
- runId,
375
- exitCode: null,
376
- signal: null,
377
- };
378
- }
379
- catch (error) {
380
- if (terminalLease) {
381
- releaseTerminal(params.projectRoot, terminalLease);
382
- terminalLease = null;
383
- }
384
- const detail = error instanceof Error ? error.message : "spawn_failed";
385
- const record = error;
386
- const extra = typeof record?.code === "string" || typeof record?.errno === "number"
387
- ? `:code=${String(record.code ?? "")}:errno=${String(record.errno ?? "")}`
388
- : "";
389
- log.error({ err: error, runId, file: spawnFile, cwd: spawnCwd }, "command run failed");
390
- return {
391
- status: "failed",
392
- runId: null,
393
- exitCode: null,
394
- signal: null,
395
- error: `command_spawn_failed:${detail}${extra}:file=${spawnFile}:cwd=${spawnCwd}`,
396
- };
397
- }
398
- };
399
- export const getCommandRun = (runId) => RUNS.get(runId) ?? null;
400
- export const listCommandRuns = (projectRoot) => [...RUNS.values()].filter((record) => record.projectRoot === projectRoot);
401
- export const getCommandRunOutput = (runId) => {
402
- const record = RUNS.get(runId);
403
- if (!record) {
404
- return null;
405
- }
406
- return {
407
- stdout: record.output.stdout,
408
- stderr: record.output.stderr,
409
- combined: record.output.combined,
410
- };
411
- };
412
- export const stopCommandRun = (runId) => {
413
- const record = RUNS.get(runId);
414
- if (!record) {
415
- return null;
416
- }
417
- if (record.status !== "running") {
418
- return record;
419
- }
420
- record.process.kill("SIGTERM");
421
- setTimeout(() => {
422
- if (record.status === "running") {
423
- record.process.kill("SIGKILL");
424
- }
425
- }, RUN_STOP_TIMEOUT_MS);
426
- return record;
427
- };
package/dist/editors.d.ts DELETED
@@ -1,26 +0,0 @@
1
- export type EditorId = "vscode" | "cursor" | "zed";
2
- export type EditorInfo = {
3
- id: EditorId;
4
- name: string;
5
- command: string;
6
- available: boolean;
7
- };
8
- export declare const listAvailableEditors: () => Promise<EditorInfo[]>;
9
- export declare const getEditor: (id: EditorId) => Promise<EditorInfo | null>;
10
- export type OpenTarget = {
11
- type: "project";
12
- } | {
13
- type: "file";
14
- filePath: string;
15
- line?: number;
16
- } | {
17
- type: "diff";
18
- filePath: string;
19
- } | {
20
- type: "commit";
21
- sha: string;
22
- };
23
- export declare const openInEditor: (editorId: EditorId, projectPath: string, target?: OpenTarget) => Promise<{
24
- success: boolean;
25
- error?: string;
26
- }>;
package/dist/editors.js DELETED
@@ -1,144 +0,0 @@
1
- import { access, constants } from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { runtime } from "./runtime.js";
5
- const EDITORS = [
6
- {
7
- id: "vscode",
8
- name: "VS Code",
9
- commands: {
10
- darwin: [
11
- "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
12
- "/usr/local/bin/code",
13
- "/opt/homebrew/bin/code",
14
- ],
15
- linux: ["/usr/bin/code", "/usr/local/bin/code", "/snap/bin/code"],
16
- win32: [
17
- "C:\\Program Files\\Microsoft VS Code\\bin\\code.cmd",
18
- "C:\\Users\\%USERNAME%\\AppData\\Local\\Programs\\Microsoft VS Code\\bin\\code.cmd",
19
- ],
20
- },
21
- },
22
- {
23
- id: "cursor",
24
- name: "Cursor",
25
- commands: {
26
- darwin: [
27
- "/Applications/Cursor.app/Contents/Resources/app/bin/cursor",
28
- "/usr/local/bin/cursor",
29
- ],
30
- linux: ["/usr/bin/cursor", "/usr/local/bin/cursor", "/opt/cursor/cursor"],
31
- win32: [
32
- "C:\\Program Files\\Cursor\\cursor.exe",
33
- "C:\\Users\\%USERNAME%\\AppData\\Local\\Programs\\Cursor\\cursor.exe",
34
- ],
35
- },
36
- },
37
- {
38
- id: "zed",
39
- name: "Zed",
40
- commands: {
41
- darwin: [
42
- "/Applications/Zed.app/Contents/MacOS/cli",
43
- "/usr/local/bin/zed",
44
- ],
45
- linux: ["/usr/bin/zed", "/usr/local/bin/zed", "~/.local/bin/zed"],
46
- win32: [],
47
- },
48
- },
49
- ];
50
- const expandPath = (p) => {
51
- if (p.startsWith("~")) {
52
- return path.join(os.homedir(), p.slice(1));
53
- }
54
- if (process.platform === "win32" && p.includes("%USERNAME%")) {
55
- return p.replace("%USERNAME%", os.userInfo().username);
56
- }
57
- return p;
58
- };
59
- const fileExists = async (filePath) => {
60
- try {
61
- await access(filePath, constants.X_OK);
62
- return true;
63
- }
64
- catch {
65
- return false;
66
- }
67
- };
68
- const findCommand = async (commands) => {
69
- for (const cmd of commands) {
70
- const expanded = expandPath(cmd);
71
- if (await fileExists(expanded)) {
72
- return expanded;
73
- }
74
- }
75
- return null;
76
- };
77
- export const listAvailableEditors = async () => {
78
- const platform = os.platform();
79
- const results = [];
80
- for (const editor of EDITORS) {
81
- const commands = editor.commands[platform] ?? [];
82
- const command = await findCommand(commands);
83
- results.push({
84
- id: editor.id,
85
- name: editor.name,
86
- command: command ?? "",
87
- available: Boolean(command),
88
- });
89
- }
90
- return results;
91
- };
92
- export const getEditor = async (id) => {
93
- const editors = await listAvailableEditors();
94
- return editors.find((e) => e.id === id) ?? null;
95
- };
96
- const buildEditorArgs = (editorId, projectPath, target) => {
97
- // All editors: code/cursor/zed support `editor <folder>` and `editor <file>:<line>`
98
- // For diff: VSCode/Cursor support `-d file1 file2`, Zed doesn't have built-in diff
99
- // For git diff: open the file, editors have built-in git integration
100
- if (target.type === "project") {
101
- return [projectPath];
102
- }
103
- if (target.type === "file") {
104
- const fullPath = path.join(projectPath, target.filePath);
105
- if (target.line && target.line > 0) {
106
- // VSCode/Cursor: --goto file:line or file:line:column
107
- // Zed: file:line
108
- if (editorId === "vscode" || editorId === "cursor") {
109
- return [projectPath, "--goto", `${fullPath}:${target.line}`];
110
- }
111
- return [projectPath, `${fullPath}:${target.line}`];
112
- }
113
- return [projectPath, fullPath];
114
- }
115
- if (target.type === "diff") {
116
- // Open the file - editors will show git changes via their built-in git integration
117
- const fullPath = path.join(projectPath, target.filePath);
118
- return [projectPath, fullPath];
119
- }
120
- if (target.type === "commit") {
121
- // Can't directly open a commit view, just open the project
122
- // User can use editor's git history panel
123
- return [projectPath];
124
- }
125
- return [projectPath];
126
- };
127
- export const openInEditor = async (editorId, projectPath, target = { type: "project" }) => {
128
- const editor = await getEditor(editorId);
129
- if (!editor) {
130
- return { success: false, error: "editor_not_found" };
131
- }
132
- if (!editor.available) {
133
- return { success: false, error: "editor_not_installed" };
134
- }
135
- const args = buildEditorArgs(editorId, projectPath, target);
136
- try {
137
- runtime.spawn([editor.command, ...args]);
138
- return { success: true };
139
- }
140
- catch (err) {
141
- const message = err instanceof Error ? err.message : "spawn_failed";
142
- return { success: false, error: message };
143
- }
144
- };
package/dist/gh.d.ts DELETED
@@ -1,61 +0,0 @@
1
- export type PullRequestInfo = {
2
- number: number;
3
- title: string;
4
- headRefName: string;
5
- updatedAt: string;
6
- author?: string;
7
- url?: string;
8
- };
9
- export type PullRequestListing = {
10
- available: boolean;
11
- items: PullRequestInfo[];
12
- hasMore: boolean;
13
- nextCursor: string | null;
14
- error?: string;
15
- };
16
- export type IssueLabel = {
17
- name: string;
18
- color: string;
19
- };
20
- export type IssueInfo = {
21
- number: number;
22
- title: string;
23
- state: "open" | "closed";
24
- updatedAt: string;
25
- author?: string;
26
- url?: string;
27
- labels: IssueLabel[];
28
- };
29
- export type IssueListing = {
30
- available: boolean;
31
- items: IssueInfo[];
32
- hasMore: boolean;
33
- nextCursor: string | null;
34
- error?: string;
35
- };
36
- export type IssueDetail = {
37
- number: number;
38
- title: string;
39
- body: string;
40
- state: "open" | "closed";
41
- author?: string;
42
- url?: string;
43
- labels: IssueLabel[];
44
- createdAt: string;
45
- updatedAt: string;
46
- commentsCount: number;
47
- };
48
- export type ListPullRequestsOptions = {
49
- limit?: number;
50
- cursor?: string | null;
51
- search?: string | null;
52
- };
53
- export declare function ghListPullRequests(cwd: string, options?: ListPullRequestsOptions): Promise<PullRequestListing>;
54
- export type ListIssuesOptions = {
55
- limit?: number;
56
- cursor?: string | null;
57
- search?: string | null;
58
- state?: "open" | "closed" | "all";
59
- };
60
- export declare function ghListIssues(cwd: string, options?: ListIssuesOptions): Promise<IssueListing>;
61
- export declare function ghGetIssueDetail(cwd: string, issueNumber: number): Promise<IssueDetail | null>;