remodex-cli 1.0.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 (99) hide show
  1. package/LICENSE +12 -0
  2. package/README.md +105 -0
  3. package/dist/archive-store.d.ts +28 -0
  4. package/dist/archive-store.js +68 -0
  5. package/dist/archive-store.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +88 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/codex-process.d.ts +186 -0
  10. package/dist/codex-process.js +2111 -0
  11. package/dist/codex-process.js.map +1 -0
  12. package/dist/debug-trace-store.d.ts +15 -0
  13. package/dist/debug-trace-store.js +78 -0
  14. package/dist/debug-trace-store.js.map +1 -0
  15. package/dist/doctor.d.ts +58 -0
  16. package/dist/doctor.js +670 -0
  17. package/dist/doctor.js.map +1 -0
  18. package/dist/firebase-auth.d.ts +35 -0
  19. package/dist/firebase-auth.js +132 -0
  20. package/dist/firebase-auth.js.map +1 -0
  21. package/dist/gallery-store.d.ts +67 -0
  22. package/dist/gallery-store.js +333 -0
  23. package/dist/gallery-store.js.map +1 -0
  24. package/dist/git-assist.d.ts +7 -0
  25. package/dist/git-assist.js +51 -0
  26. package/dist/git-assist.js.map +1 -0
  27. package/dist/git-operations.d.ts +63 -0
  28. package/dist/git-operations.js +292 -0
  29. package/dist/git-operations.js.map +1 -0
  30. package/dist/image-store.d.ts +23 -0
  31. package/dist/image-store.js +142 -0
  32. package/dist/image-store.js.map +1 -0
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +198 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/mdns.d.ts +7 -0
  37. package/dist/mdns.js +49 -0
  38. package/dist/mdns.js.map +1 -0
  39. package/dist/parser.d.ts +620 -0
  40. package/dist/parser.js +423 -0
  41. package/dist/parser.js.map +1 -0
  42. package/dist/path-utils.d.ts +4 -0
  43. package/dist/path-utils.js +34 -0
  44. package/dist/path-utils.js.map +1 -0
  45. package/dist/project-history.d.ts +10 -0
  46. package/dist/project-history.js +73 -0
  47. package/dist/project-history.js.map +1 -0
  48. package/dist/prompt-history-backup.d.ts +15 -0
  49. package/dist/prompt-history-backup.js +46 -0
  50. package/dist/prompt-history-backup.js.map +1 -0
  51. package/dist/proxy.d.ts +15 -0
  52. package/dist/proxy.js +95 -0
  53. package/dist/proxy.js.map +1 -0
  54. package/dist/push-i18n.d.ts +7 -0
  55. package/dist/push-i18n.js +75 -0
  56. package/dist/push-i18n.js.map +1 -0
  57. package/dist/push-relay.d.ts +29 -0
  58. package/dist/push-relay.js +70 -0
  59. package/dist/push-relay.js.map +1 -0
  60. package/dist/recording-store.d.ts +51 -0
  61. package/dist/recording-store.js +158 -0
  62. package/dist/recording-store.js.map +1 -0
  63. package/dist/screenshot.d.ts +28 -0
  64. package/dist/screenshot.js +98 -0
  65. package/dist/screenshot.js.map +1 -0
  66. package/dist/sdk-process.d.ts +180 -0
  67. package/dist/sdk-process.js +960 -0
  68. package/dist/sdk-process.js.map +1 -0
  69. package/dist/session.d.ts +144 -0
  70. package/dist/session.js +687 -0
  71. package/dist/session.js.map +1 -0
  72. package/dist/sessions-index.d.ts +130 -0
  73. package/dist/sessions-index.js +1817 -0
  74. package/dist/sessions-index.js.map +1 -0
  75. package/dist/setup-launchd.d.ts +9 -0
  76. package/dist/setup-launchd.js +115 -0
  77. package/dist/setup-launchd.js.map +1 -0
  78. package/dist/setup-systemd.d.ts +9 -0
  79. package/dist/setup-systemd.js +122 -0
  80. package/dist/setup-systemd.js.map +1 -0
  81. package/dist/startup-info.d.ts +9 -0
  82. package/dist/startup-info.js +116 -0
  83. package/dist/startup-info.js.map +1 -0
  84. package/dist/usage.d.ts +69 -0
  85. package/dist/usage.js +545 -0
  86. package/dist/usage.js.map +1 -0
  87. package/dist/version.d.ts +13 -0
  88. package/dist/version.js +43 -0
  89. package/dist/version.js.map +1 -0
  90. package/dist/websocket.d.ts +132 -0
  91. package/dist/websocket.js +3551 -0
  92. package/dist/websocket.js.map +1 -0
  93. package/dist/worktree-store.d.ts +26 -0
  94. package/dist/worktree-store.js +61 -0
  95. package/dist/worktree-store.js.map +1 -0
  96. package/dist/worktree.d.ts +47 -0
  97. package/dist/worktree.js +330 -0
  98. package/dist/worktree.js.map +1 -0
  99. package/package.json +62 -0
@@ -0,0 +1,51 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { getStagedDiff } from "./git-operations.js";
6
+ const COMMIT_MESSAGE_PROMPT = "Write a single Conventional Commits message in English for the staged changes below. Output only the commit message, with no quotes or explanation.";
7
+ const CODEX_COMMIT_MODEL = "gpt-5.4-mini";
8
+ export function generateCommitMessage(options) {
9
+ const diff = getStagedDiff(options.projectPath).trim();
10
+ if (!diff) {
11
+ throw new Error("Nothing to commit: no files are staged");
12
+ }
13
+ const cwd = resolve(options.projectPath);
14
+ const output = options.provider === "codex"
15
+ ? runCodexCommitAssist(cwd, diff, options.model)
16
+ : execFileSync("claude", [
17
+ "-p",
18
+ ...(options.model ? ["--model", options.model] : []),
19
+ COMMIT_MESSAGE_PROMPT,
20
+ ], {
21
+ cwd,
22
+ encoding: "utf-8",
23
+ input: diff,
24
+ maxBuffer: 1024 * 1024,
25
+ });
26
+ const message = output
27
+ .split("\n")
28
+ .map((line) => line.trim())
29
+ .find(Boolean);
30
+ if (!message) {
31
+ throw new Error("Commit message generation returned empty output");
32
+ }
33
+ return message;
34
+ }
35
+ function runCodexCommitAssist(cwd, diff, _model) {
36
+ const outputDir = mkdtempSync(join(tmpdir(), "remodex-git-assist-"));
37
+ const outputPath = join(outputDir, "last-message.txt");
38
+ try {
39
+ execFileSync("codex", ["exec", "-m", CODEX_COMMIT_MODEL, "-o", outputPath, "-"], {
40
+ cwd,
41
+ encoding: "utf-8",
42
+ input: `${COMMIT_MESSAGE_PROMPT}\n\n${diff}`,
43
+ maxBuffer: 1024 * 1024,
44
+ });
45
+ return readFileSync(outputPath, "utf-8");
46
+ }
47
+ finally {
48
+ rmSync(outputDir, { recursive: true, force: true });
49
+ }
50
+ }
51
+ //# sourceMappingURL=git-assist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-assist.js","sourceRoot":"","sources":["../src/git-assist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,qBAAqB,GACzB,qJAAqJ,CAAC;AACxJ,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAQ1C,MAAM,UAAU,qBAAqB,CAAC,OAAyB;IAC7D,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,MAAM,GACV,OAAO,CAAC,QAAQ,KAAK,OAAO;QAC1B,CAAC,CAAC,oBAAoB,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC;QAChD,CAAC,CAAC,YAAY,CACV,QAAQ,EACR;YACE,IAAI;YACJ,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,qBAAqB;SACtB,EACD;YACE,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,IAAI;YACX,SAAS,EAAE,IAAI,GAAG,IAAI;SACvB,CACF,CAAC;IAER,MAAM,OAAO,GAAG,MAAM;SACnB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,IAAI,CAAC,OAAO,CAAC,CAAC;IACjB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,oBAAoB,CAC3B,GAAW,EACX,IAAY,EACZ,MAAe;IAEf,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;IAEvD,IAAI,CAAC;QACH,YAAY,CACV,OAAO,EACP,CAAC,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,EACzD;YACE,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,GAAG,qBAAqB,OAAO,IAAI,EAAE;YAC5C,SAAS,EAAE,IAAI,GAAG,IAAI;SACvB,CACF,CAAC;QACF,OAAO,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
@@ -0,0 +1,63 @@
1
+ export interface HunkRef {
2
+ file: string;
3
+ hunkIndex: number;
4
+ }
5
+ export interface CommitResult {
6
+ hash: string;
7
+ message: string;
8
+ }
9
+ export interface BranchRemoteStatus {
10
+ ahead: number;
11
+ behind: number;
12
+ hasUpstream: boolean;
13
+ }
14
+ export interface BranchListResult {
15
+ current: string;
16
+ branches: string[];
17
+ /** Branches currently checked out by main repo or worktrees (cannot switch to). */
18
+ checkedOutBranches: string[];
19
+ remoteStatusByBranch: Record<string, BranchRemoteStatus>;
20
+ }
21
+ /** Stage entire files. */
22
+ export declare function stageFiles(projectPath: string, files: string[]): void;
23
+ /**
24
+ * Stage specific hunks by extracting them from `git diff` and applying via `git apply --cached`.
25
+ *
26
+ * Groups hunks by file, extracts the diff header + requested hunks, then pipes through `git apply`.
27
+ */
28
+ export declare function stageHunks(projectPath: string, hunks: HunkRef[]): void;
29
+ /** Unstage files (remove from index, keep working tree changes). */
30
+ export declare function unstageFiles(projectPath: string, files: string[]): void;
31
+ /** Unstage specific hunks from the index, leaving the working tree intact. */
32
+ export declare function unstageHunks(projectPath: string, hunks: HunkRef[]): void;
33
+ /** Create a commit with the given message. Throws if nothing is staged. */
34
+ export declare function gitCommit(projectPath: string, message: string): CommitResult;
35
+ /** Return staged diff content for commit-message generation. */
36
+ export declare function getStagedDiff(projectPath: string): string;
37
+ /** Push to remote. */
38
+ export declare function gitPush(projectPath: string): void;
39
+ /** List branches and branches checked out by worktrees. */
40
+ export declare function listBranches(projectPath: string): BranchListResult;
41
+ /** Create a new branch, optionally checking it out. */
42
+ export declare function createBranch(projectPath: string, name: string, checkout?: boolean): void;
43
+ /** Checkout an existing branch. */
44
+ export declare function checkoutBranch(projectPath: string, branch: string): void;
45
+ /** Revert (discard) unstaged changes for specific files. */
46
+ export declare function revertFiles(projectPath: string, files: string[]): void;
47
+ /** Revert specific working-tree hunks, leaving the index intact. */
48
+ export declare function revertHunks(projectPath: string, hunks: HunkRef[]): void;
49
+ export interface RemoteStatusResult {
50
+ ahead: number;
51
+ behind: number;
52
+ branch: string;
53
+ hasUpstream: boolean;
54
+ }
55
+ /** Fetch from remote (non-blocking, returns when done). */
56
+ export declare function gitFetch(projectPath: string): void;
57
+ /** Get ahead/behind counts relative to upstream. */
58
+ export declare function gitRemoteStatus(projectPath: string): RemoteStatusResult;
59
+ /** Pull from remote (fetch + merge). */
60
+ export declare function gitPull(projectPath: string): {
61
+ success: boolean;
62
+ message: string;
63
+ };
@@ -0,0 +1,292 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { realpathSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ // ---- Helpers ----
5
+ function resolveProject(projectPath) {
6
+ return realpathSync(resolve(projectPath));
7
+ }
8
+ function git(args, cwd) {
9
+ return execFileSync("git", args, { cwd, encoding: "utf-8" }).trim();
10
+ }
11
+ function buildHunkPatch(diffText, file, indices) {
12
+ if (!diffText)
13
+ return null;
14
+ const lines = diffText.split("\n");
15
+ const hunkStarts = [];
16
+ for (let i = 0; i < lines.length; i++) {
17
+ if (lines[i].startsWith("@@")) {
18
+ hunkStarts.push(i);
19
+ }
20
+ }
21
+ if (hunkStarts.length === 0)
22
+ return null;
23
+ const header = lines.slice(0, hunkStarts[0]).join("\n") + "\n";
24
+ const sortedIndices = [...new Set(indices)].sort((a, b) => a - b);
25
+ let patch = header;
26
+ for (const idx of sortedIndices) {
27
+ if (idx < 0 || idx >= hunkStarts.length) {
28
+ throw new Error(`Hunk index ${idx} out of range for file ${file} (${hunkStarts.length} hunks)`);
29
+ }
30
+ const start = hunkStarts[idx];
31
+ const end = idx + 1 < hunkStarts.length ? hunkStarts[idx + 1] : lines.length;
32
+ patch += lines.slice(start, end).join("\n") + "\n";
33
+ }
34
+ return patch;
35
+ }
36
+ function applyHunks(projectPath, hunks, options) {
37
+ const cwd = resolveProject(projectPath);
38
+ const byFile = new Map();
39
+ for (const h of hunks) {
40
+ const list = byFile.get(h.file) ?? [];
41
+ list.push(h.hunkIndex);
42
+ byFile.set(h.file, list);
43
+ }
44
+ for (const [file, indices] of byFile) {
45
+ let diffText = "";
46
+ let addedIntentToAdd = false;
47
+ if (options.includeUntracked) {
48
+ const tracked = git(["ls-files", "--", file], cwd);
49
+ if (!tracked) {
50
+ execFileSync("git", ["add", "--intent-to-add", "--", file], {
51
+ cwd,
52
+ encoding: "utf-8",
53
+ });
54
+ addedIntentToAdd = true;
55
+ }
56
+ }
57
+ try {
58
+ diffText = git([...options.diffArgs, "--", file], cwd);
59
+ }
60
+ finally {
61
+ if (addedIntentToAdd) {
62
+ execFileSync("git", ["reset", "--", file], {
63
+ cwd,
64
+ encoding: "utf-8",
65
+ });
66
+ }
67
+ }
68
+ const patch = buildHunkPatch(diffText, file, indices);
69
+ if (!patch)
70
+ continue;
71
+ execFileSync("git", [...options.applyArgs, "-"], {
72
+ cwd,
73
+ encoding: "utf-8",
74
+ input: patch,
75
+ });
76
+ }
77
+ }
78
+ // ---- Phase 1: Staging ----
79
+ /** Stage entire files. */
80
+ export function stageFiles(projectPath, files) {
81
+ const cwd = resolveProject(projectPath);
82
+ execFileSync("git", ["add", "--", ...files], { cwd, encoding: "utf-8" });
83
+ }
84
+ /**
85
+ * Stage specific hunks by extracting them from `git diff` and applying via `git apply --cached`.
86
+ *
87
+ * Groups hunks by file, extracts the diff header + requested hunks, then pipes through `git apply`.
88
+ */
89
+ export function stageHunks(projectPath, hunks) {
90
+ applyHunks(projectPath, hunks, {
91
+ diffArgs: ["diff", "--unified=0"],
92
+ applyArgs: ["apply", "--cached", "--unidiff-zero"],
93
+ includeUntracked: true,
94
+ });
95
+ }
96
+ /** Unstage files (remove from index, keep working tree changes). */
97
+ export function unstageFiles(projectPath, files) {
98
+ const cwd = resolveProject(projectPath);
99
+ execFileSync("git", ["reset", "HEAD", "--", ...files], {
100
+ cwd,
101
+ encoding: "utf-8",
102
+ });
103
+ }
104
+ /** Unstage specific hunks from the index, leaving the working tree intact. */
105
+ export function unstageHunks(projectPath, hunks) {
106
+ applyHunks(projectPath, hunks, {
107
+ diffArgs: ["diff", "--cached", "--unified=0"],
108
+ applyArgs: ["apply", "-R", "--cached", "--unidiff-zero"],
109
+ });
110
+ }
111
+ // ---- Phase 2: Commit / Push ----
112
+ /** Create a commit with the given message. Throws if nothing is staged. */
113
+ export function gitCommit(projectPath, message) {
114
+ const cwd = resolveProject(projectPath);
115
+ // Check if there's anything staged
116
+ const staged = git(["diff", "--cached", "--name-only"], cwd);
117
+ if (!staged) {
118
+ throw new Error("Nothing to commit: no files are staged");
119
+ }
120
+ execFileSync("git", ["commit", "-m", message], { cwd, encoding: "utf-8" });
121
+ const hash = git(["rev-parse", "--short", "HEAD"], cwd);
122
+ return { hash, message };
123
+ }
124
+ /** Return staged diff content for commit-message generation. */
125
+ export function getStagedDiff(projectPath) {
126
+ const cwd = resolveProject(projectPath);
127
+ return execFileSync("git", ["diff", "--cached", "--no-color"], {
128
+ cwd,
129
+ encoding: "utf-8",
130
+ });
131
+ }
132
+ /** Push to remote. */
133
+ export function gitPush(projectPath) {
134
+ const cwd = resolveProject(projectPath);
135
+ const branch = git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
136
+ execFileSync("git", ["push", "--set-upstream", "origin", branch], {
137
+ cwd,
138
+ encoding: "utf-8",
139
+ });
140
+ }
141
+ // ---- Phase 3: Branch Operations ----
142
+ /** List branches and branches checked out by worktrees. */
143
+ export function listBranches(projectPath) {
144
+ const cwd = resolveProject(projectPath);
145
+ const current = git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
146
+ const output = git(["branch", "--list", "--format=%(refname:short)"], cwd);
147
+ const branches = output ? output.split("\n").filter(Boolean) : [];
148
+ // Collect branches checked out by worktrees (+ main repo)
149
+ const checkedOutBranches = [];
150
+ try {
151
+ const wtOutput = execFileSync("git", ["worktree", "list", "--porcelain"], {
152
+ cwd,
153
+ encoding: "utf-8",
154
+ });
155
+ for (const line of wtOutput.split("\n")) {
156
+ if (line.startsWith("branch ")) {
157
+ const branch = line
158
+ .slice("branch ".length)
159
+ .replace(/^refs\/heads\//, "");
160
+ checkedOutBranches.push(branch);
161
+ }
162
+ }
163
+ }
164
+ catch {
165
+ /* ignore if worktree command fails */
166
+ }
167
+ const remoteStatusByBranch = Object.fromEntries(branches.map((branch) => [branch, getBranchRemoteStatus(cwd, branch)]));
168
+ return { current, branches, checkedOutBranches, remoteStatusByBranch };
169
+ }
170
+ function getBranchRemoteStatus(cwd, branch) {
171
+ const upstream = execFileSync("git", ["for-each-ref", "--format=%(upstream:short)", `refs/heads/${branch}`], { cwd, encoding: "utf-8" }).trim();
172
+ if (!upstream) {
173
+ return { ahead: 0, behind: 0, hasUpstream: false };
174
+ }
175
+ let ahead = 0;
176
+ let behind = 0;
177
+ try {
178
+ ahead =
179
+ parseInt(git(["rev-list", "--count", `${upstream}..${branch}`], cwd), 10) || 0;
180
+ }
181
+ catch {
182
+ ahead = 0;
183
+ }
184
+ try {
185
+ behind =
186
+ parseInt(git(["rev-list", "--count", `${branch}..${upstream}`], cwd), 10) || 0;
187
+ }
188
+ catch {
189
+ behind = 0;
190
+ }
191
+ return { ahead, behind, hasUpstream: true };
192
+ }
193
+ /** Create a new branch, optionally checking it out. */
194
+ export function createBranch(projectPath, name, checkout) {
195
+ const cwd = resolveProject(projectPath);
196
+ if (checkout) {
197
+ execFileSync("git", ["checkout", "-b", name], { cwd, encoding: "utf-8" });
198
+ }
199
+ else {
200
+ execFileSync("git", ["branch", name], { cwd, encoding: "utf-8" });
201
+ }
202
+ }
203
+ /** Checkout an existing branch. */
204
+ export function checkoutBranch(projectPath, branch) {
205
+ const cwd = resolveProject(projectPath);
206
+ execFileSync("git", ["checkout", branch], { cwd, encoding: "utf-8" });
207
+ }
208
+ /** Revert (discard) unstaged changes for specific files. */
209
+ export function revertFiles(projectPath, files) {
210
+ const cwd = resolveProject(projectPath);
211
+ if (files.length === 0)
212
+ return;
213
+ const trackedOutput = git(["ls-files", "--", ...files], cwd);
214
+ const trackedFiles = trackedOutput ? trackedOutput.split("\n").filter(Boolean) : [];
215
+ const trackedSet = new Set(trackedFiles);
216
+ const untrackedFiles = files.filter((file) => !trackedSet.has(file));
217
+ if (trackedFiles.length > 0) {
218
+ execFileSync("git", ["checkout", "--", ...trackedFiles], {
219
+ cwd,
220
+ encoding: "utf-8",
221
+ });
222
+ }
223
+ if (untrackedFiles.length > 0) {
224
+ execFileSync("git", ["clean", "-fd", "--", ...untrackedFiles], {
225
+ cwd,
226
+ encoding: "utf-8",
227
+ });
228
+ }
229
+ }
230
+ /** Revert specific working-tree hunks, leaving the index intact. */
231
+ export function revertHunks(projectPath, hunks) {
232
+ applyHunks(projectPath, hunks, {
233
+ diffArgs: ["diff", "--unified=0"],
234
+ applyArgs: ["apply", "-R", "--unidiff-zero"],
235
+ includeUntracked: true,
236
+ });
237
+ }
238
+ /** Fetch from remote (non-blocking, returns when done). */
239
+ export function gitFetch(projectPath) {
240
+ const cwd = resolveProject(projectPath);
241
+ execFileSync("git", ["fetch", "--quiet"], {
242
+ cwd,
243
+ encoding: "utf-8",
244
+ timeout: 30000,
245
+ });
246
+ }
247
+ /** Get ahead/behind counts relative to upstream. */
248
+ export function gitRemoteStatus(projectPath) {
249
+ const cwd = resolveProject(projectPath);
250
+ const branch = git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
251
+ // Check if upstream is configured
252
+ let hasUpstream = false;
253
+ try {
254
+ git(["rev-parse", "--abbrev-ref", `${branch}@{upstream}`], cwd);
255
+ hasUpstream = true;
256
+ }
257
+ catch {
258
+ return { ahead: 0, behind: 0, branch, hasUpstream: false };
259
+ }
260
+ let ahead = 0;
261
+ let behind = 0;
262
+ try {
263
+ const aheadStr = git(["rev-list", "--count", `@{upstream}..HEAD`], cwd);
264
+ ahead = parseInt(aheadStr, 10) || 0;
265
+ }
266
+ catch {
267
+ /* ignore */
268
+ }
269
+ try {
270
+ const behindStr = git(["rev-list", "--count", `HEAD..@{upstream}`], cwd);
271
+ behind = parseInt(behindStr, 10) || 0;
272
+ }
273
+ catch {
274
+ /* ignore */
275
+ }
276
+ return { ahead, behind, branch, hasUpstream };
277
+ }
278
+ /** Pull from remote (fetch + merge). */
279
+ export function gitPull(projectPath) {
280
+ const cwd = resolveProject(projectPath);
281
+ try {
282
+ const output = execFileSync("git", ["pull"], {
283
+ cwd,
284
+ encoding: "utf-8",
285
+ }).trim();
286
+ return { success: true, message: output };
287
+ }
288
+ catch (err) {
289
+ return { success: false, message: String(err) };
290
+ }
291
+ }
292
+ //# sourceMappingURL=git-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-operations.js","sourceRoot":"","sources":["../src/git-operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ export interface ImageRef {
3
+ id: string;
4
+ url: string;
5
+ mimeType: string;
6
+ }
7
+ export declare class ImageStore {
8
+ private store;
9
+ private resolveReadablePath;
10
+ /** Evict least-recently-used entries if store exceeds MAX_ENTRIES. */
11
+ private evictLRU;
12
+ /** Extract local image file paths from text (ignores URLs). */
13
+ extractImagePaths(text: unknown): string[];
14
+ /** Register an image from raw base64 data. Returns an ImageRef with a URL for HTTP access. */
15
+ registerFromBase64(base64Data: string, mimeType: string): ImageRef | null;
16
+ /** Read files from disk, assign UUIDs, store in memory. */
17
+ registerImages(paths: string[], projectPath?: string): Promise<ImageRef[]>;
18
+ /**
19
+ * Handle HTTP request for image serving.
20
+ * Returns true if the request was handled, false otherwise.
21
+ */
22
+ handleRequest(req: IncomingMessage, res: ServerResponse): boolean;
23
+ }
@@ -0,0 +1,142 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFile, stat } from "node:fs/promises";
3
+ import { extname, isAbsolute, resolve } from "node:path";
4
+ const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
5
+ const MAX_ENTRIES = 100;
6
+ const MIME_TYPES = {
7
+ ".png": "image/png",
8
+ ".jpg": "image/jpeg",
9
+ ".jpeg": "image/jpeg",
10
+ ".gif": "image/gif",
11
+ ".webp": "image/webp",
12
+ };
13
+ // Matches absolute paths ending with image extensions
14
+ const IMAGE_PATH_RE = /(\/[\w./_-]+\.(?:png|jpe?g|gif|webp))/gi;
15
+ export class ImageStore {
16
+ store = new Map();
17
+ async resolveReadablePath(filePath, projectPath) {
18
+ const candidates = [];
19
+ if (projectPath) {
20
+ if (isAbsolute(filePath)) {
21
+ candidates.push(filePath);
22
+ // Tool outputs sometimes return project-root-relative paths with a
23
+ // leading slash (e.g. /images/foo.png). Try resolving against project.
24
+ candidates.push(resolve(projectPath, filePath.replace(/^\/+/, "")));
25
+ }
26
+ else {
27
+ candidates.push(resolve(projectPath, filePath));
28
+ candidates.push(filePath);
29
+ }
30
+ }
31
+ else {
32
+ candidates.push(filePath);
33
+ }
34
+ for (const candidate of candidates) {
35
+ try {
36
+ const st = await stat(candidate);
37
+ if (st.isFile() && st.size <= MAX_FILE_SIZE)
38
+ return candidate;
39
+ }
40
+ catch {
41
+ // Try next candidate.
42
+ }
43
+ }
44
+ return null;
45
+ }
46
+ /** Evict least-recently-used entries if store exceeds MAX_ENTRIES. */
47
+ evictLRU() {
48
+ while (this.store.size > MAX_ENTRIES) {
49
+ let oldestId = null;
50
+ let oldestTime = Infinity;
51
+ for (const [key, val] of this.store) {
52
+ if (val.accessedAt < oldestTime) {
53
+ oldestTime = val.accessedAt;
54
+ oldestId = key;
55
+ }
56
+ }
57
+ if (oldestId)
58
+ this.store.delete(oldestId);
59
+ }
60
+ }
61
+ /** Extract local image file paths from text (ignores URLs). */
62
+ extractImagePaths(text) {
63
+ const str = typeof text === "string" ? text : JSON.stringify(text ?? "");
64
+ const matches = str.match(IMAGE_PATH_RE);
65
+ if (!matches)
66
+ return [];
67
+ // Filter out URLs (paths starting with //) and deduplicate
68
+ const localPaths = matches.filter((p) => !p.startsWith("//"));
69
+ return [...new Set(localPaths)];
70
+ }
71
+ /** Register an image from raw base64 data. Returns an ImageRef with a URL for HTTP access. */
72
+ registerFromBase64(base64Data, mimeType) {
73
+ try {
74
+ const buffer = Buffer.from(base64Data, "base64");
75
+ if (buffer.length > MAX_FILE_SIZE) {
76
+ console.warn(`[image-store] Skipping base64 image (>10MB)`);
77
+ return null;
78
+ }
79
+ const id = randomUUID();
80
+ this.store.set(id, { id, mimeType, buffer, accessedAt: Date.now() });
81
+ this.evictLRU();
82
+ return { id, url: `/images/${id}`, mimeType };
83
+ }
84
+ catch (err) {
85
+ console.warn(`[image-store] Failed to register base64 image:`, err);
86
+ return null;
87
+ }
88
+ }
89
+ /** Read files from disk, assign UUIDs, store in memory. */
90
+ async registerImages(paths, projectPath) {
91
+ const refs = [];
92
+ for (const filePath of paths) {
93
+ try {
94
+ const resolvedPath = await this.resolveReadablePath(filePath, projectPath);
95
+ if (!resolvedPath) {
96
+ console.warn(`[image-store] Skipping ${filePath} (not file or >10MB)`);
97
+ continue;
98
+ }
99
+ const ext = extname(resolvedPath).toLowerCase();
100
+ const mimeType = MIME_TYPES[ext];
101
+ if (!mimeType)
102
+ continue;
103
+ const buffer = await readFile(resolvedPath);
104
+ const id = randomUUID();
105
+ this.store.set(id, { id, mimeType, buffer, accessedAt: Date.now() });
106
+ const ref = { id, url: `/images/${id}`, mimeType };
107
+ refs.push(ref);
108
+ this.evictLRU();
109
+ }
110
+ catch (err) {
111
+ console.warn(`[image-store] Failed to read ${filePath}:`, err);
112
+ }
113
+ }
114
+ return refs;
115
+ }
116
+ /**
117
+ * Handle HTTP request for image serving.
118
+ * Returns true if the request was handled, false otherwise.
119
+ */
120
+ handleRequest(req, res) {
121
+ const url = req.url ?? "";
122
+ const match = url.match(/^\/images\/([a-f0-9-]+)$/);
123
+ if (!match)
124
+ return false;
125
+ const id = match[1];
126
+ const entry = this.store.get(id);
127
+ if (!entry) {
128
+ res.writeHead(404, { "Content-Type": "text/plain" });
129
+ res.end("Not Found");
130
+ return true;
131
+ }
132
+ entry.accessedAt = Date.now();
133
+ res.writeHead(200, {
134
+ "Content-Type": entry.mimeType,
135
+ "Content-Length": entry.buffer.length,
136
+ "Cache-Control": "public, max-age=3600",
137
+ });
138
+ res.end(entry.buffer);
139
+ return true;
140
+ }
141
+ }
142
+ //# sourceMappingURL=image-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-store.js","sourceRoot":"","sources":["../src/image-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function startServer(): Promise<void>;