pinpatch 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) 2026 Pinpatch
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,70 @@
1
+ # pinpatch
2
+
3
+ Pinpatch lets you place pins on a running local app, generate implementation tasks, and execute them through supported providers (`codex` or `claude`).
4
+
5
+ ## Install
6
+
7
+ Global install:
8
+
9
+ ```bash
10
+ npm install -g pinpatch
11
+ pinpatch --help
12
+ ```
13
+
14
+ Run without global install:
15
+
16
+ ```bash
17
+ npx pinpatch@latest --help
18
+ ```
19
+
20
+ ## Prerequisites
21
+
22
+ - Node.js `>=18`
23
+ - A local web app running on `localhost` (default target port `3000`)
24
+ - For Codex execution: `codex` CLI installed and authenticated
25
+ - For Claude execution: `claude` CLI installed and authenticated
26
+
27
+ ## Quick Start
28
+
29
+ 1. Start your local app (example on `3000`).
30
+ 2. Start Pinpatch:
31
+
32
+ ```bash
33
+ pinpatch dev --target 3000
34
+ ```
35
+
36
+ 3. Open the proxied app at `http://localhost:3030`.
37
+ 4. Press `c` to enter pin mode, click an element, and submit a request.
38
+
39
+ ## Commands
40
+
41
+ ```bash
42
+ pinpatch --help
43
+ pinpatch dev --target 3000
44
+ pinpatch implement <taskId>
45
+ pinpatch tasks
46
+ pinpatch tasks --prune
47
+ ```
48
+
49
+ ## Runtime Artifacts
50
+
51
+ Pinpatch writes runtime files to:
52
+
53
+ ```text
54
+ .pinpatch/
55
+ config.json
56
+ tasks/
57
+ sessions/
58
+ screenshots/
59
+ runtime/logs/
60
+ ```
61
+
62
+ ## Troubleshooting
63
+
64
+ - `Target localhost:<port> is unreachable`: start your app first.
65
+ - `Bridge port ... is already in use` / `Proxy port ... is already in use`: choose different ports.
66
+ - Provider process errors: verify `codex`/`claude` CLI authentication and PATH setup.
67
+
68
+ ## Monorepo Source
69
+
70
+ For contributor workflows and e2e testing, see the repository root docs in `README.md` and `CONTRIBUTING.md`.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/index.cjs";
package/dist/index.cjs ADDED
@@ -0,0 +1,379 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/index.ts
26
+ var import_node_path = __toESM(require("path"), 1);
27
+ var import_node_fs = require("fs");
28
+ var import_node_http = __toESM(require("http"), 1);
29
+ var import_node_child_process = require("child_process");
30
+ var import_commander = require("commander");
31
+ var import_core = require("@pinpatch/core");
32
+ var import_providers = require("@pinpatch/providers");
33
+ var import_proxy = require("@pinpatch/proxy");
34
+ var cliEntrypointDir = process.argv[1] ? import_node_path.default.dirname(import_node_path.default.resolve(process.argv[1])) : process.cwd();
35
+ var packageRootFromCli = ["bin", "dist", "src"].includes(
36
+ import_node_path.default.basename(cliEntrypointDir)
37
+ ) ? import_node_path.default.resolve(cliEntrypointDir, "..") : cliEntrypointDir;
38
+ var workspaceRootFromCli = import_node_path.default.resolve(packageRootFromCli, "../..");
39
+ var resolveRuntimeCwd = () => {
40
+ const initCwd = process.env.INIT_CWD?.trim();
41
+ if (initCwd) {
42
+ const resolved = import_node_path.default.resolve(initCwd);
43
+ if ((0, import_node_fs.existsSync)(resolved)) {
44
+ return resolved;
45
+ }
46
+ }
47
+ const cwd = process.cwd();
48
+ const workspaceRootFromCwd = import_node_path.default.resolve(cwd, "../..");
49
+ const workspaceMarkerFromCwd = import_node_path.default.join(
50
+ workspaceRootFromCwd,
51
+ "pnpm-workspace.yaml"
52
+ );
53
+ const appearsToBeCliPackage = import_node_path.default.basename(cwd) === "cli" && import_node_path.default.basename(import_node_path.default.dirname(cwd)) === "packages";
54
+ if (appearsToBeCliPackage && (0, import_node_fs.existsSync)(workspaceMarkerFromCwd)) {
55
+ return workspaceRootFromCwd;
56
+ }
57
+ const workspaceMarker = import_node_path.default.join(
58
+ workspaceRootFromCli,
59
+ "pnpm-workspace.yaml"
60
+ );
61
+ if (cwd === packageRootFromCli && (0, import_node_fs.existsSync)(workspaceMarker)) {
62
+ return workspaceRootFromCli;
63
+ }
64
+ return cwd;
65
+ };
66
+ var keyboardShortcutsHelp = [
67
+ "Keyboard shortcuts (when using `pinpatch dev` in the proxied app):",
68
+ " c Toggle pin mode",
69
+ " Escape Exit pin mode and close the composer",
70
+ " Cmd+Delete/Backspace Clear all pins (macOS)",
71
+ " Ctrl+Delete/Backspace Clear all pins (Windows/Linux)",
72
+ " Enter Submit from composer/follow-up textarea",
73
+ " Shift+Enter Insert a newline in composer/follow-up textarea"
74
+ ].join("\n");
75
+ var waitForSignal = async () => {
76
+ await new Promise((resolve) => {
77
+ const onSignal = () => {
78
+ process.off("SIGINT", onSignal);
79
+ process.off("SIGTERM", onSignal);
80
+ resolve();
81
+ };
82
+ process.on("SIGINT", onSignal);
83
+ process.on("SIGTERM", onSignal);
84
+ });
85
+ };
86
+ var targetReachable = async (port) => {
87
+ return await new Promise((resolve) => {
88
+ const request = import_node_http.default.get(
89
+ {
90
+ host: "localhost",
91
+ port,
92
+ path: "/",
93
+ timeout: 1500
94
+ },
95
+ () => {
96
+ request.destroy();
97
+ resolve(true);
98
+ }
99
+ );
100
+ request.on("error", () => resolve(false));
101
+ request.on("timeout", () => {
102
+ request.destroy();
103
+ resolve(false);
104
+ });
105
+ });
106
+ };
107
+ var resolveOverlayBundlePath = (cwd) => {
108
+ const candidates = [
109
+ process.env.PINPATCH_OVERLAY_SCRIPT_PATH,
110
+ import_node_path.default.join(cwd, "apps", "overlay", "dist", "pinpatch-overlay.iife.js"),
111
+ import_node_path.default.join(
112
+ cwd,
113
+ "node_modules",
114
+ "@pinpatch",
115
+ "overlay",
116
+ "dist",
117
+ "pinpatch-overlay.iife.js"
118
+ ),
119
+ import_node_path.default.join(
120
+ packageRootFromCli,
121
+ "node_modules",
122
+ "@pinpatch",
123
+ "overlay",
124
+ "dist",
125
+ "pinpatch-overlay.iife.js"
126
+ ),
127
+ import_node_path.default.join(
128
+ workspaceRootFromCli,
129
+ "apps",
130
+ "overlay",
131
+ "dist",
132
+ "pinpatch-overlay.iife.js"
133
+ ),
134
+ import_node_path.default.join(
135
+ resolveRuntimeCwd(),
136
+ "apps",
137
+ "overlay",
138
+ "dist",
139
+ "pinpatch-overlay.iife.js"
140
+ )
141
+ ].filter((value) => Boolean(value));
142
+ for (const candidate of candidates) {
143
+ if ((0, import_node_fs.existsSync)(candidate)) {
144
+ return candidate;
145
+ }
146
+ }
147
+ const maybeBuildOverlay = (workspaceRoot) => {
148
+ const overlayWorkspace = import_node_path.default.join(
149
+ workspaceRoot,
150
+ "apps",
151
+ "overlay",
152
+ "package.json"
153
+ );
154
+ if (!(0, import_node_fs.existsSync)(overlayWorkspace)) {
155
+ return void 0;
156
+ }
157
+ const output = (0, import_node_child_process.spawnSync)(
158
+ "pnpm",
159
+ ["--filter", "@pinpatch/overlay", "build"],
160
+ {
161
+ cwd: workspaceRoot,
162
+ stdio: "inherit"
163
+ }
164
+ );
165
+ if (output.status === 0) {
166
+ const builtPath = import_node_path.default.join(
167
+ workspaceRoot,
168
+ "apps",
169
+ "overlay",
170
+ "dist",
171
+ "pinpatch-overlay.iife.js"
172
+ );
173
+ if ((0, import_node_fs.existsSync)(builtPath)) {
174
+ return builtPath;
175
+ }
176
+ }
177
+ return void 0;
178
+ };
179
+ const buildRoots = Array.from(/* @__PURE__ */ new Set([cwd, workspaceRootFromCli]));
180
+ for (const buildRoot of buildRoots) {
181
+ const built = maybeBuildOverlay(buildRoot);
182
+ if (built) {
183
+ return built;
184
+ }
185
+ }
186
+ return void 0;
187
+ };
188
+ var runDev = async (options) => {
189
+ const cwd = resolveRuntimeCwd();
190
+ const store = new import_core.ArtifactStore(cwd);
191
+ await store.ensureStructure();
192
+ await store.ensureGitignoreEntry();
193
+ const config = await (0, import_core.resolveConfig)(cwd, {
194
+ provider: options.provider,
195
+ model: options.model,
196
+ target: options.target,
197
+ debug: options.debug,
198
+ bridgePort: options.bridgePort,
199
+ proxyPort: options.proxyPort
200
+ });
201
+ const logger = (0, import_core.createLogger)({
202
+ store,
203
+ component: "cli",
204
+ debugEnabled: config.debug
205
+ });
206
+ const reachable = await targetReachable(config.target);
207
+ if (!reachable) {
208
+ throw new Error(
209
+ `Target localhost:${config.target} is unreachable. Start your app first and retry. Hint: lsof -i :${config.target}`
210
+ );
211
+ }
212
+ const overlayScriptPath = resolveOverlayBundlePath(cwd);
213
+ const providerRegistry = (0, import_providers.createProviderRegistry)(["codex", "claude"]);
214
+ const bridge = (0, import_core.createBridgeServer)({
215
+ cwd,
216
+ port: config.bridgePort,
217
+ store,
218
+ logger,
219
+ overlayScriptPath,
220
+ getProviderAdapter: (provider) => providerRegistry.getAdapter(provider)
221
+ });
222
+ const proxy = (0, import_proxy.createReverseProxy)({
223
+ targetPort: config.target,
224
+ proxyPort: config.proxyPort,
225
+ bridgePort: config.bridgePort,
226
+ provider: config.provider,
227
+ model: config.model,
228
+ logger
229
+ });
230
+ try {
231
+ await bridge.start();
232
+ } catch (error) {
233
+ const code = error.code;
234
+ if (code === "EADDRINUSE") {
235
+ throw new Error(
236
+ `Bridge port ${config.bridgePort} is already in use. Hint: lsof -i :${config.bridgePort}`
237
+ );
238
+ }
239
+ throw error;
240
+ }
241
+ try {
242
+ await proxy.start();
243
+ } catch (error) {
244
+ const code = error.code;
245
+ if (code === "EADDRINUSE") {
246
+ await bridge.stop();
247
+ throw new Error(
248
+ `Proxy port ${config.proxyPort} is already in use. Hint: lsof -i :${config.proxyPort}`
249
+ );
250
+ }
251
+ await bridge.stop();
252
+ throw error;
253
+ }
254
+ console.log(`Pinpatch dev ready`);
255
+ console.log(`Target app: http://localhost:${config.target}`);
256
+ console.log(`Proxied app: http://localhost:${config.proxyPort}`);
257
+ console.log(`Bridge API: http://localhost:${config.bridgePort}`);
258
+ await waitForSignal();
259
+ await proxy.stop();
260
+ await bridge.stop();
261
+ };
262
+ var runImplement = async (taskId, options) => {
263
+ const cwd = resolveRuntimeCwd();
264
+ const store = new import_core.ArtifactStore(cwd);
265
+ await store.ensureStructure();
266
+ await store.ensureGitignoreEntry();
267
+ const config = await (0, import_core.resolveConfig)(cwd, {
268
+ provider: options.provider,
269
+ model: options.model,
270
+ debug: options.debug
271
+ });
272
+ const logger = (0, import_core.createLogger)({
273
+ store,
274
+ component: "cli",
275
+ debugEnabled: config.debug
276
+ });
277
+ const task = await store.getTask(taskId);
278
+ if (!task) {
279
+ throw new Error(`Task ${taskId} was not found under .pinpatch/tasks`);
280
+ }
281
+ const providerRegistry = (0, import_providers.createProviderRegistry)(["codex", "claude"]);
282
+ const eventBus = new import_core.TaskEventBus();
283
+ const runner = new import_core.TaskRunner({
284
+ cwd,
285
+ store,
286
+ logger,
287
+ eventBus,
288
+ getProviderAdapter: (provider) => providerRegistry.getAdapter(provider)
289
+ });
290
+ const sessionId = (0, import_core.generateSessionId)();
291
+ const result = await runner.runTask({
292
+ taskId,
293
+ sessionId,
294
+ provider: config.provider,
295
+ model: config.model,
296
+ dryRun: Boolean(options.dryRun),
297
+ debug: config.debug
298
+ });
299
+ console.log(`Task ${taskId} -> ${result.status}`);
300
+ console.log(result.summary);
301
+ if (result.changedFiles.length > 0) {
302
+ console.log("Changed files:");
303
+ for (const changedFile of result.changedFiles) {
304
+ console.log(` - ${changedFile}`);
305
+ }
306
+ }
307
+ if (result.status !== "completed") {
308
+ process.exitCode = 1;
309
+ }
310
+ };
311
+ var runTasks = async (options) => {
312
+ const cwd = resolveRuntimeCwd();
313
+ const store = new import_core.ArtifactStore(cwd);
314
+ await store.ensureStructure();
315
+ if (options.prune) {
316
+ const result = await store.prune();
317
+ console.log(`Pruned logs: ${result.removedLogs}`);
318
+ console.log(`Pruned orphan sessions: ${result.removedSessions}`);
319
+ return;
320
+ }
321
+ const tasks = await store.listTasks();
322
+ tasks.sort(
323
+ (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt)
324
+ );
325
+ if (tasks.length === 0) {
326
+ console.log("No tasks found in .pinpatch/tasks");
327
+ return;
328
+ }
329
+ console.table(
330
+ tasks.map((task) => ({
331
+ taskId: task.taskId,
332
+ status: task.status,
333
+ updatedAt: task.updatedAt,
334
+ provider: task.provider ?? "-",
335
+ model: task.model ?? "-",
336
+ latestSessionId: task.latestSessionId ?? "-"
337
+ }))
338
+ );
339
+ };
340
+ var program = new import_commander.Command();
341
+ program.name("pinpatch").description("Pinpatch CLI").version("0.1.0");
342
+ program.addHelpText(
343
+ "after",
344
+ `
345
+ Quick reference:
346
+ pinpatch dev --target <port>
347
+ pinpatch implement <taskId>
348
+ pinpatch tasks
349
+ pinpatch tasks --prune
350
+
351
+ ${keyboardShortcutsHelp}
352
+ `
353
+ );
354
+ program.command("dev").description("Start Pinpatch bridge + proxy runtime").option(
355
+ "--target <port>",
356
+ "Target app localhost port",
357
+ (value) => Number.parseInt(value, 10)
358
+ ).option("--provider <name>", "Provider name (codex|claude)").option("--model <model>", "Provider model").option(
359
+ "--bridge-port <port>",
360
+ "Bridge server port",
361
+ (value) => Number.parseInt(value, 10)
362
+ ).option(
363
+ "--proxy-port <port>",
364
+ "Proxy server port",
365
+ (value) => Number.parseInt(value, 10)
366
+ ).option("--debug", "Enable debug logs", false).action(async (options) => {
367
+ await runDev(options);
368
+ });
369
+ program.command("implement").description("Execute a saved task through provider adapter").argument("<taskId>", "Task id").option("--provider <name>", "Provider name").option("--model <model>", "Provider model").option("--dry-run", "Do not apply provider edits", false).option("--debug", "Enable debug logs", false).action(async (taskId, options) => {
370
+ await runImplement(taskId, options);
371
+ });
372
+ program.command("tasks").description("List or prune task/session artifacts").option("--prune", "Prune expired logs and orphan sessions", false).option("--debug", "Enable debug logs", false).action(async (options) => {
373
+ await runTasks(options);
374
+ });
375
+ program.parseAsync(process.argv).catch((error) => {
376
+ console.error(error instanceof Error ? error.message : String(error));
377
+ process.exitCode = 1;
378
+ });
379
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport http from \"node:http\";\nimport { spawnSync } from \"node:child_process\";\nimport { Command } from \"commander\";\nimport {\n ArtifactStore,\n createBridgeServer,\n createLogger,\n generateSessionId,\n resolveConfig,\n TaskEventBus,\n TaskRunner,\n type ProviderName,\n} from \"@pinpatch/core\";\nimport { createProviderRegistry } from \"@pinpatch/providers\";\nimport { createReverseProxy } from \"@pinpatch/proxy\";\n\nconst cliEntrypointDir = process.argv[1]\n ? path.dirname(path.resolve(process.argv[1]))\n : process.cwd();\nconst packageRootFromCli = [\"bin\", \"dist\", \"src\"].includes(\n path.basename(cliEntrypointDir),\n)\n ? path.resolve(cliEntrypointDir, \"..\")\n : cliEntrypointDir;\nconst workspaceRootFromCli = path.resolve(packageRootFromCli, \"../..\");\n\nconst resolveRuntimeCwd = (): string => {\n const initCwd = process.env.INIT_CWD?.trim();\n if (initCwd) {\n const resolved = path.resolve(initCwd);\n if (existsSync(resolved)) {\n return resolved;\n }\n }\n\n const cwd = process.cwd();\n\n const workspaceRootFromCwd = path.resolve(cwd, \"../..\");\n const workspaceMarkerFromCwd = path.join(\n workspaceRootFromCwd,\n \"pnpm-workspace.yaml\",\n );\n const appearsToBeCliPackage =\n path.basename(cwd) === \"cli\" &&\n path.basename(path.dirname(cwd)) === \"packages\";\n if (appearsToBeCliPackage && existsSync(workspaceMarkerFromCwd)) {\n return workspaceRootFromCwd;\n }\n\n const workspaceMarker = path.join(\n workspaceRootFromCli,\n \"pnpm-workspace.yaml\",\n );\n if (cwd === packageRootFromCli && existsSync(workspaceMarker)) {\n return workspaceRootFromCli;\n }\n\n return cwd;\n};\n\ntype DevCommandOptions = {\n target?: number;\n provider?: ProviderName;\n model?: string;\n debug?: boolean;\n bridgePort?: number;\n proxyPort?: number;\n};\n\ntype ImplementCommandOptions = {\n provider?: ProviderName;\n model?: string;\n dryRun?: boolean;\n debug?: boolean;\n};\n\ntype TasksCommandOptions = {\n prune?: boolean;\n debug?: boolean;\n};\n\nconst keyboardShortcutsHelp = [\n \"Keyboard shortcuts (when using `pinpatch dev` in the proxied app):\",\n \" c Toggle pin mode\",\n \" Escape Exit pin mode and close the composer\",\n \" Cmd+Delete/Backspace Clear all pins (macOS)\",\n \" Ctrl+Delete/Backspace Clear all pins (Windows/Linux)\",\n \" Enter Submit from composer/follow-up textarea\",\n \" Shift+Enter Insert a newline in composer/follow-up textarea\",\n].join(\"\\n\");\n\nconst waitForSignal = async (): Promise<void> => {\n await new Promise<void>((resolve) => {\n const onSignal = () => {\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n resolve();\n };\n\n process.on(\"SIGINT\", onSignal);\n process.on(\"SIGTERM\", onSignal);\n });\n};\n\nconst targetReachable = async (port: number): Promise<boolean> => {\n return await new Promise<boolean>((resolve) => {\n const request = http.get(\n {\n host: \"localhost\",\n port,\n path: \"/\",\n timeout: 1500,\n },\n () => {\n request.destroy();\n resolve(true);\n },\n );\n\n request.on(\"error\", () => resolve(false));\n request.on(\"timeout\", () => {\n request.destroy();\n resolve(false);\n });\n });\n};\n\nconst resolveOverlayBundlePath = (cwd: string): string | undefined => {\n const candidates = [\n process.env.PINPATCH_OVERLAY_SCRIPT_PATH,\n path.join(cwd, \"apps\", \"overlay\", \"dist\", \"pinpatch-overlay.iife.js\"),\n path.join(\n cwd,\n \"node_modules\",\n \"@pinpatch\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n ),\n path.join(\n packageRootFromCli,\n \"node_modules\",\n \"@pinpatch\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n ),\n path.join(\n workspaceRootFromCli,\n \"apps\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n ),\n path.join(\n resolveRuntimeCwd(),\n \"apps\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n ),\n ].filter((value): value is string => Boolean(value));\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n const maybeBuildOverlay = (workspaceRoot: string): string | undefined => {\n const overlayWorkspace = path.join(\n workspaceRoot,\n \"apps\",\n \"overlay\",\n \"package.json\",\n );\n if (!existsSync(overlayWorkspace)) {\n return undefined;\n }\n\n const output = spawnSync(\n \"pnpm\",\n [\"--filter\", \"@pinpatch/overlay\", \"build\"],\n {\n cwd: workspaceRoot,\n stdio: \"inherit\",\n },\n );\n\n if (output.status === 0) {\n const builtPath = path.join(\n workspaceRoot,\n \"apps\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n );\n if (existsSync(builtPath)) {\n return builtPath;\n }\n }\n\n return undefined;\n };\n\n const buildRoots = Array.from(new Set([cwd, workspaceRootFromCli]));\n for (const buildRoot of buildRoots) {\n const built = maybeBuildOverlay(buildRoot);\n if (built) {\n return built;\n }\n }\n\n return undefined;\n};\n\nconst runDev = async (options: DevCommandOptions): Promise<void> => {\n const cwd = resolveRuntimeCwd();\n const store = new ArtifactStore(cwd);\n await store.ensureStructure();\n await store.ensureGitignoreEntry();\n\n const config = await resolveConfig(cwd, {\n provider: options.provider,\n model: options.model,\n target: options.target,\n debug: options.debug,\n bridgePort: options.bridgePort,\n proxyPort: options.proxyPort,\n });\n\n const logger = createLogger({\n store,\n component: \"cli\",\n debugEnabled: config.debug,\n });\n\n const reachable = await targetReachable(config.target);\n if (!reachable) {\n throw new Error(\n `Target localhost:${config.target} is unreachable. Start your app first and retry. Hint: lsof -i :${config.target}`,\n );\n }\n\n const overlayScriptPath = resolveOverlayBundlePath(cwd);\n\n const providerRegistry = createProviderRegistry([\"codex\", \"claude\"]);\n const bridge = createBridgeServer({\n cwd,\n port: config.bridgePort,\n store,\n logger,\n overlayScriptPath,\n getProviderAdapter: (provider) => providerRegistry.getAdapter(provider),\n });\n\n const proxy = createReverseProxy({\n targetPort: config.target,\n proxyPort: config.proxyPort,\n bridgePort: config.bridgePort,\n provider: config.provider,\n model: config.model,\n logger,\n });\n\n try {\n await bridge.start();\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"EADDRINUSE\") {\n throw new Error(\n `Bridge port ${config.bridgePort} is already in use. Hint: lsof -i :${config.bridgePort}`,\n );\n }\n\n throw error;\n }\n\n try {\n await proxy.start();\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"EADDRINUSE\") {\n await bridge.stop();\n throw new Error(\n `Proxy port ${config.proxyPort} is already in use. Hint: lsof -i :${config.proxyPort}`,\n );\n }\n\n await bridge.stop();\n throw error;\n }\n\n console.log(`Pinpatch dev ready`);\n console.log(`Target app: http://localhost:${config.target}`);\n console.log(`Proxied app: http://localhost:${config.proxyPort}`);\n console.log(`Bridge API: http://localhost:${config.bridgePort}`);\n\n await waitForSignal();\n\n await proxy.stop();\n await bridge.stop();\n};\n\nconst runImplement = async (\n taskId: string,\n options: ImplementCommandOptions,\n): Promise<void> => {\n const cwd = resolveRuntimeCwd();\n const store = new ArtifactStore(cwd);\n await store.ensureStructure();\n await store.ensureGitignoreEntry();\n\n const config = await resolveConfig(cwd, {\n provider: options.provider,\n model: options.model,\n debug: options.debug,\n });\n\n const logger = createLogger({\n store,\n component: \"cli\",\n debugEnabled: config.debug,\n });\n\n const task = await store.getTask(taskId);\n if (!task) {\n throw new Error(`Task ${taskId} was not found under .pinpatch/tasks`);\n }\n\n const providerRegistry = createProviderRegistry([\"codex\", \"claude\"]);\n const eventBus = new TaskEventBus();\n const runner = new TaskRunner({\n cwd,\n store,\n logger,\n eventBus,\n getProviderAdapter: (provider) => providerRegistry.getAdapter(provider),\n });\n\n const sessionId = generateSessionId();\n const result = await runner.runTask({\n taskId,\n sessionId,\n provider: config.provider,\n model: config.model,\n dryRun: Boolean(options.dryRun),\n debug: config.debug,\n });\n\n console.log(`Task ${taskId} -> ${result.status}`);\n console.log(result.summary);\n if (result.changedFiles.length > 0) {\n console.log(\"Changed files:\");\n for (const changedFile of result.changedFiles) {\n console.log(` - ${changedFile}`);\n }\n }\n\n if (result.status !== \"completed\") {\n process.exitCode = 1;\n }\n};\n\nconst runTasks = async (options: TasksCommandOptions): Promise<void> => {\n const cwd = resolveRuntimeCwd();\n const store = new ArtifactStore(cwd);\n await store.ensureStructure();\n\n if (options.prune) {\n const result = await store.prune();\n console.log(`Pruned logs: ${result.removedLogs}`);\n console.log(`Pruned orphan sessions: ${result.removedSessions}`);\n return;\n }\n\n const tasks = await store.listTasks();\n tasks.sort(\n (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt),\n );\n\n if (tasks.length === 0) {\n console.log(\"No tasks found in .pinpatch/tasks\");\n return;\n }\n\n console.table(\n tasks.map((task) => ({\n taskId: task.taskId,\n status: task.status,\n updatedAt: task.updatedAt,\n provider: task.provider ?? \"-\",\n model: task.model ?? \"-\",\n latestSessionId: task.latestSessionId ?? \"-\",\n })),\n );\n};\n\nconst program = new Command();\nprogram.name(\"pinpatch\").description(\"Pinpatch CLI\").version(\"0.1.0\");\nprogram.addHelpText(\n \"after\",\n `\\nQuick reference:\\n pinpatch dev --target <port>\\n pinpatch implement <taskId>\\n pinpatch tasks\\n pinpatch tasks --prune\\n\\n${keyboardShortcutsHelp}\\n`,\n);\n\nprogram\n .command(\"dev\")\n .description(\"Start Pinpatch bridge + proxy runtime\")\n .option(\"--target <port>\", \"Target app localhost port\", (value) =>\n Number.parseInt(value, 10),\n )\n .option(\"--provider <name>\", \"Provider name (codex|claude)\")\n .option(\"--model <model>\", \"Provider model\")\n .option(\"--bridge-port <port>\", \"Bridge server port\", (value) =>\n Number.parseInt(value, 10),\n )\n .option(\"--proxy-port <port>\", \"Proxy server port\", (value) =>\n Number.parseInt(value, 10),\n )\n .option(\"--debug\", \"Enable debug logs\", false)\n .action(async (options: DevCommandOptions) => {\n await runDev(options);\n });\n\nprogram\n .command(\"implement\")\n .description(\"Execute a saved task through provider adapter\")\n .argument(\"<taskId>\", \"Task id\")\n .option(\"--provider <name>\", \"Provider name\")\n .option(\"--model <model>\", \"Provider model\")\n .option(\"--dry-run\", \"Do not apply provider edits\", false)\n .option(\"--debug\", \"Enable debug logs\", false)\n .action(async (taskId: string, options: ImplementCommandOptions) => {\n await runImplement(taskId, options);\n });\n\nprogram\n .command(\"tasks\")\n .description(\"List or prune task/session artifacts\")\n .option(\"--prune\", \"Prune expired logs and orphan sessions\", false)\n .option(\"--debug\", \"Enable debug logs\", false)\n .action(async (options: TasksCommandOptions) => {\n await runTasks(options);\n });\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n"],"mappings":"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","names":["path","http"]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,363 @@
1
+ // src/index.ts
2
+ import path from "path";
3
+ import { existsSync } from "fs";
4
+ import http from "http";
5
+ import { spawnSync } from "child_process";
6
+ import { Command } from "commander";
7
+ import {
8
+ ArtifactStore,
9
+ createBridgeServer,
10
+ createLogger,
11
+ generateSessionId,
12
+ resolveConfig,
13
+ TaskEventBus,
14
+ TaskRunner
15
+ } from "@pinpatch/core";
16
+ import { createProviderRegistry } from "@pinpatch/providers";
17
+ import { createReverseProxy } from "@pinpatch/proxy";
18
+ var cliEntrypointDir = process.argv[1] ? path.dirname(path.resolve(process.argv[1])) : process.cwd();
19
+ var packageRootFromCli = ["bin", "dist", "src"].includes(
20
+ path.basename(cliEntrypointDir)
21
+ ) ? path.resolve(cliEntrypointDir, "..") : cliEntrypointDir;
22
+ var workspaceRootFromCli = path.resolve(packageRootFromCli, "../..");
23
+ var resolveRuntimeCwd = () => {
24
+ const initCwd = process.env.INIT_CWD?.trim();
25
+ if (initCwd) {
26
+ const resolved = path.resolve(initCwd);
27
+ if (existsSync(resolved)) {
28
+ return resolved;
29
+ }
30
+ }
31
+ const cwd = process.cwd();
32
+ const workspaceRootFromCwd = path.resolve(cwd, "../..");
33
+ const workspaceMarkerFromCwd = path.join(
34
+ workspaceRootFromCwd,
35
+ "pnpm-workspace.yaml"
36
+ );
37
+ const appearsToBeCliPackage = path.basename(cwd) === "cli" && path.basename(path.dirname(cwd)) === "packages";
38
+ if (appearsToBeCliPackage && existsSync(workspaceMarkerFromCwd)) {
39
+ return workspaceRootFromCwd;
40
+ }
41
+ const workspaceMarker = path.join(
42
+ workspaceRootFromCli,
43
+ "pnpm-workspace.yaml"
44
+ );
45
+ if (cwd === packageRootFromCli && existsSync(workspaceMarker)) {
46
+ return workspaceRootFromCli;
47
+ }
48
+ return cwd;
49
+ };
50
+ var keyboardShortcutsHelp = [
51
+ "Keyboard shortcuts (when using `pinpatch dev` in the proxied app):",
52
+ " c Toggle pin mode",
53
+ " Escape Exit pin mode and close the composer",
54
+ " Cmd+Delete/Backspace Clear all pins (macOS)",
55
+ " Ctrl+Delete/Backspace Clear all pins (Windows/Linux)",
56
+ " Enter Submit from composer/follow-up textarea",
57
+ " Shift+Enter Insert a newline in composer/follow-up textarea"
58
+ ].join("\n");
59
+ var waitForSignal = async () => {
60
+ await new Promise((resolve) => {
61
+ const onSignal = () => {
62
+ process.off("SIGINT", onSignal);
63
+ process.off("SIGTERM", onSignal);
64
+ resolve();
65
+ };
66
+ process.on("SIGINT", onSignal);
67
+ process.on("SIGTERM", onSignal);
68
+ });
69
+ };
70
+ var targetReachable = async (port) => {
71
+ return await new Promise((resolve) => {
72
+ const request = http.get(
73
+ {
74
+ host: "localhost",
75
+ port,
76
+ path: "/",
77
+ timeout: 1500
78
+ },
79
+ () => {
80
+ request.destroy();
81
+ resolve(true);
82
+ }
83
+ );
84
+ request.on("error", () => resolve(false));
85
+ request.on("timeout", () => {
86
+ request.destroy();
87
+ resolve(false);
88
+ });
89
+ });
90
+ };
91
+ var resolveOverlayBundlePath = (cwd) => {
92
+ const candidates = [
93
+ process.env.PINPATCH_OVERLAY_SCRIPT_PATH,
94
+ path.join(cwd, "apps", "overlay", "dist", "pinpatch-overlay.iife.js"),
95
+ path.join(
96
+ cwd,
97
+ "node_modules",
98
+ "@pinpatch",
99
+ "overlay",
100
+ "dist",
101
+ "pinpatch-overlay.iife.js"
102
+ ),
103
+ path.join(
104
+ packageRootFromCli,
105
+ "node_modules",
106
+ "@pinpatch",
107
+ "overlay",
108
+ "dist",
109
+ "pinpatch-overlay.iife.js"
110
+ ),
111
+ path.join(
112
+ workspaceRootFromCli,
113
+ "apps",
114
+ "overlay",
115
+ "dist",
116
+ "pinpatch-overlay.iife.js"
117
+ ),
118
+ path.join(
119
+ resolveRuntimeCwd(),
120
+ "apps",
121
+ "overlay",
122
+ "dist",
123
+ "pinpatch-overlay.iife.js"
124
+ )
125
+ ].filter((value) => Boolean(value));
126
+ for (const candidate of candidates) {
127
+ if (existsSync(candidate)) {
128
+ return candidate;
129
+ }
130
+ }
131
+ const maybeBuildOverlay = (workspaceRoot) => {
132
+ const overlayWorkspace = path.join(
133
+ workspaceRoot,
134
+ "apps",
135
+ "overlay",
136
+ "package.json"
137
+ );
138
+ if (!existsSync(overlayWorkspace)) {
139
+ return void 0;
140
+ }
141
+ const output = spawnSync(
142
+ "pnpm",
143
+ ["--filter", "@pinpatch/overlay", "build"],
144
+ {
145
+ cwd: workspaceRoot,
146
+ stdio: "inherit"
147
+ }
148
+ );
149
+ if (output.status === 0) {
150
+ const builtPath = path.join(
151
+ workspaceRoot,
152
+ "apps",
153
+ "overlay",
154
+ "dist",
155
+ "pinpatch-overlay.iife.js"
156
+ );
157
+ if (existsSync(builtPath)) {
158
+ return builtPath;
159
+ }
160
+ }
161
+ return void 0;
162
+ };
163
+ const buildRoots = Array.from(/* @__PURE__ */ new Set([cwd, workspaceRootFromCli]));
164
+ for (const buildRoot of buildRoots) {
165
+ const built = maybeBuildOverlay(buildRoot);
166
+ if (built) {
167
+ return built;
168
+ }
169
+ }
170
+ return void 0;
171
+ };
172
+ var runDev = async (options) => {
173
+ const cwd = resolveRuntimeCwd();
174
+ const store = new ArtifactStore(cwd);
175
+ await store.ensureStructure();
176
+ await store.ensureGitignoreEntry();
177
+ const config = await resolveConfig(cwd, {
178
+ provider: options.provider,
179
+ model: options.model,
180
+ target: options.target,
181
+ debug: options.debug,
182
+ bridgePort: options.bridgePort,
183
+ proxyPort: options.proxyPort
184
+ });
185
+ const logger = createLogger({
186
+ store,
187
+ component: "cli",
188
+ debugEnabled: config.debug
189
+ });
190
+ const reachable = await targetReachable(config.target);
191
+ if (!reachable) {
192
+ throw new Error(
193
+ `Target localhost:${config.target} is unreachable. Start your app first and retry. Hint: lsof -i :${config.target}`
194
+ );
195
+ }
196
+ const overlayScriptPath = resolveOverlayBundlePath(cwd);
197
+ const providerRegistry = createProviderRegistry(["codex", "claude"]);
198
+ const bridge = createBridgeServer({
199
+ cwd,
200
+ port: config.bridgePort,
201
+ store,
202
+ logger,
203
+ overlayScriptPath,
204
+ getProviderAdapter: (provider) => providerRegistry.getAdapter(provider)
205
+ });
206
+ const proxy = createReverseProxy({
207
+ targetPort: config.target,
208
+ proxyPort: config.proxyPort,
209
+ bridgePort: config.bridgePort,
210
+ provider: config.provider,
211
+ model: config.model,
212
+ logger
213
+ });
214
+ try {
215
+ await bridge.start();
216
+ } catch (error) {
217
+ const code = error.code;
218
+ if (code === "EADDRINUSE") {
219
+ throw new Error(
220
+ `Bridge port ${config.bridgePort} is already in use. Hint: lsof -i :${config.bridgePort}`
221
+ );
222
+ }
223
+ throw error;
224
+ }
225
+ try {
226
+ await proxy.start();
227
+ } catch (error) {
228
+ const code = error.code;
229
+ if (code === "EADDRINUSE") {
230
+ await bridge.stop();
231
+ throw new Error(
232
+ `Proxy port ${config.proxyPort} is already in use. Hint: lsof -i :${config.proxyPort}`
233
+ );
234
+ }
235
+ await bridge.stop();
236
+ throw error;
237
+ }
238
+ console.log(`Pinpatch dev ready`);
239
+ console.log(`Target app: http://localhost:${config.target}`);
240
+ console.log(`Proxied app: http://localhost:${config.proxyPort}`);
241
+ console.log(`Bridge API: http://localhost:${config.bridgePort}`);
242
+ await waitForSignal();
243
+ await proxy.stop();
244
+ await bridge.stop();
245
+ };
246
+ var runImplement = async (taskId, options) => {
247
+ const cwd = resolveRuntimeCwd();
248
+ const store = new ArtifactStore(cwd);
249
+ await store.ensureStructure();
250
+ await store.ensureGitignoreEntry();
251
+ const config = await resolveConfig(cwd, {
252
+ provider: options.provider,
253
+ model: options.model,
254
+ debug: options.debug
255
+ });
256
+ const logger = createLogger({
257
+ store,
258
+ component: "cli",
259
+ debugEnabled: config.debug
260
+ });
261
+ const task = await store.getTask(taskId);
262
+ if (!task) {
263
+ throw new Error(`Task ${taskId} was not found under .pinpatch/tasks`);
264
+ }
265
+ const providerRegistry = createProviderRegistry(["codex", "claude"]);
266
+ const eventBus = new TaskEventBus();
267
+ const runner = new TaskRunner({
268
+ cwd,
269
+ store,
270
+ logger,
271
+ eventBus,
272
+ getProviderAdapter: (provider) => providerRegistry.getAdapter(provider)
273
+ });
274
+ const sessionId = generateSessionId();
275
+ const result = await runner.runTask({
276
+ taskId,
277
+ sessionId,
278
+ provider: config.provider,
279
+ model: config.model,
280
+ dryRun: Boolean(options.dryRun),
281
+ debug: config.debug
282
+ });
283
+ console.log(`Task ${taskId} -> ${result.status}`);
284
+ console.log(result.summary);
285
+ if (result.changedFiles.length > 0) {
286
+ console.log("Changed files:");
287
+ for (const changedFile of result.changedFiles) {
288
+ console.log(` - ${changedFile}`);
289
+ }
290
+ }
291
+ if (result.status !== "completed") {
292
+ process.exitCode = 1;
293
+ }
294
+ };
295
+ var runTasks = async (options) => {
296
+ const cwd = resolveRuntimeCwd();
297
+ const store = new ArtifactStore(cwd);
298
+ await store.ensureStructure();
299
+ if (options.prune) {
300
+ const result = await store.prune();
301
+ console.log(`Pruned logs: ${result.removedLogs}`);
302
+ console.log(`Pruned orphan sessions: ${result.removedSessions}`);
303
+ return;
304
+ }
305
+ const tasks = await store.listTasks();
306
+ tasks.sort(
307
+ (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt)
308
+ );
309
+ if (tasks.length === 0) {
310
+ console.log("No tasks found in .pinpatch/tasks");
311
+ return;
312
+ }
313
+ console.table(
314
+ tasks.map((task) => ({
315
+ taskId: task.taskId,
316
+ status: task.status,
317
+ updatedAt: task.updatedAt,
318
+ provider: task.provider ?? "-",
319
+ model: task.model ?? "-",
320
+ latestSessionId: task.latestSessionId ?? "-"
321
+ }))
322
+ );
323
+ };
324
+ var program = new Command();
325
+ program.name("pinpatch").description("Pinpatch CLI").version("0.1.0");
326
+ program.addHelpText(
327
+ "after",
328
+ `
329
+ Quick reference:
330
+ pinpatch dev --target <port>
331
+ pinpatch implement <taskId>
332
+ pinpatch tasks
333
+ pinpatch tasks --prune
334
+
335
+ ${keyboardShortcutsHelp}
336
+ `
337
+ );
338
+ program.command("dev").description("Start Pinpatch bridge + proxy runtime").option(
339
+ "--target <port>",
340
+ "Target app localhost port",
341
+ (value) => Number.parseInt(value, 10)
342
+ ).option("--provider <name>", "Provider name (codex|claude)").option("--model <model>", "Provider model").option(
343
+ "--bridge-port <port>",
344
+ "Bridge server port",
345
+ (value) => Number.parseInt(value, 10)
346
+ ).option(
347
+ "--proxy-port <port>",
348
+ "Proxy server port",
349
+ (value) => Number.parseInt(value, 10)
350
+ ).option("--debug", "Enable debug logs", false).action(async (options) => {
351
+ await runDev(options);
352
+ });
353
+ program.command("implement").description("Execute a saved task through provider adapter").argument("<taskId>", "Task id").option("--provider <name>", "Provider name").option("--model <model>", "Provider model").option("--dry-run", "Do not apply provider edits", false).option("--debug", "Enable debug logs", false).action(async (taskId, options) => {
354
+ await runImplement(taskId, options);
355
+ });
356
+ program.command("tasks").description("List or prune task/session artifacts").option("--prune", "Prune expired logs and orphan sessions", false).option("--debug", "Enable debug logs", false).action(async (options) => {
357
+ await runTasks(options);
358
+ });
359
+ program.parseAsync(process.argv).catch((error) => {
360
+ console.error(error instanceof Error ? error.message : String(error));
361
+ process.exitCode = 1;
362
+ });
363
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport http from \"node:http\";\nimport { spawnSync } from \"node:child_process\";\nimport { Command } from \"commander\";\nimport {\n ArtifactStore,\n createBridgeServer,\n createLogger,\n generateSessionId,\n resolveConfig,\n TaskEventBus,\n TaskRunner,\n type ProviderName,\n} from \"@pinpatch/core\";\nimport { createProviderRegistry } from \"@pinpatch/providers\";\nimport { createReverseProxy } from \"@pinpatch/proxy\";\n\nconst cliEntrypointDir = process.argv[1]\n ? path.dirname(path.resolve(process.argv[1]))\n : process.cwd();\nconst packageRootFromCli = [\"bin\", \"dist\", \"src\"].includes(\n path.basename(cliEntrypointDir),\n)\n ? path.resolve(cliEntrypointDir, \"..\")\n : cliEntrypointDir;\nconst workspaceRootFromCli = path.resolve(packageRootFromCli, \"../..\");\n\nconst resolveRuntimeCwd = (): string => {\n const initCwd = process.env.INIT_CWD?.trim();\n if (initCwd) {\n const resolved = path.resolve(initCwd);\n if (existsSync(resolved)) {\n return resolved;\n }\n }\n\n const cwd = process.cwd();\n\n const workspaceRootFromCwd = path.resolve(cwd, \"../..\");\n const workspaceMarkerFromCwd = path.join(\n workspaceRootFromCwd,\n \"pnpm-workspace.yaml\",\n );\n const appearsToBeCliPackage =\n path.basename(cwd) === \"cli\" &&\n path.basename(path.dirname(cwd)) === \"packages\";\n if (appearsToBeCliPackage && existsSync(workspaceMarkerFromCwd)) {\n return workspaceRootFromCwd;\n }\n\n const workspaceMarker = path.join(\n workspaceRootFromCli,\n \"pnpm-workspace.yaml\",\n );\n if (cwd === packageRootFromCli && existsSync(workspaceMarker)) {\n return workspaceRootFromCli;\n }\n\n return cwd;\n};\n\ntype DevCommandOptions = {\n target?: number;\n provider?: ProviderName;\n model?: string;\n debug?: boolean;\n bridgePort?: number;\n proxyPort?: number;\n};\n\ntype ImplementCommandOptions = {\n provider?: ProviderName;\n model?: string;\n dryRun?: boolean;\n debug?: boolean;\n};\n\ntype TasksCommandOptions = {\n prune?: boolean;\n debug?: boolean;\n};\n\nconst keyboardShortcutsHelp = [\n \"Keyboard shortcuts (when using `pinpatch dev` in the proxied app):\",\n \" c Toggle pin mode\",\n \" Escape Exit pin mode and close the composer\",\n \" Cmd+Delete/Backspace Clear all pins (macOS)\",\n \" Ctrl+Delete/Backspace Clear all pins (Windows/Linux)\",\n \" Enter Submit from composer/follow-up textarea\",\n \" Shift+Enter Insert a newline in composer/follow-up textarea\",\n].join(\"\\n\");\n\nconst waitForSignal = async (): Promise<void> => {\n await new Promise<void>((resolve) => {\n const onSignal = () => {\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n resolve();\n };\n\n process.on(\"SIGINT\", onSignal);\n process.on(\"SIGTERM\", onSignal);\n });\n};\n\nconst targetReachable = async (port: number): Promise<boolean> => {\n return await new Promise<boolean>((resolve) => {\n const request = http.get(\n {\n host: \"localhost\",\n port,\n path: \"/\",\n timeout: 1500,\n },\n () => {\n request.destroy();\n resolve(true);\n },\n );\n\n request.on(\"error\", () => resolve(false));\n request.on(\"timeout\", () => {\n request.destroy();\n resolve(false);\n });\n });\n};\n\nconst resolveOverlayBundlePath = (cwd: string): string | undefined => {\n const candidates = [\n process.env.PINPATCH_OVERLAY_SCRIPT_PATH,\n path.join(cwd, \"apps\", \"overlay\", \"dist\", \"pinpatch-overlay.iife.js\"),\n path.join(\n cwd,\n \"node_modules\",\n \"@pinpatch\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n ),\n path.join(\n packageRootFromCli,\n \"node_modules\",\n \"@pinpatch\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n ),\n path.join(\n workspaceRootFromCli,\n \"apps\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n ),\n path.join(\n resolveRuntimeCwd(),\n \"apps\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n ),\n ].filter((value): value is string => Boolean(value));\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n const maybeBuildOverlay = (workspaceRoot: string): string | undefined => {\n const overlayWorkspace = path.join(\n workspaceRoot,\n \"apps\",\n \"overlay\",\n \"package.json\",\n );\n if (!existsSync(overlayWorkspace)) {\n return undefined;\n }\n\n const output = spawnSync(\n \"pnpm\",\n [\"--filter\", \"@pinpatch/overlay\", \"build\"],\n {\n cwd: workspaceRoot,\n stdio: \"inherit\",\n },\n );\n\n if (output.status === 0) {\n const builtPath = path.join(\n workspaceRoot,\n \"apps\",\n \"overlay\",\n \"dist\",\n \"pinpatch-overlay.iife.js\",\n );\n if (existsSync(builtPath)) {\n return builtPath;\n }\n }\n\n return undefined;\n };\n\n const buildRoots = Array.from(new Set([cwd, workspaceRootFromCli]));\n for (const buildRoot of buildRoots) {\n const built = maybeBuildOverlay(buildRoot);\n if (built) {\n return built;\n }\n }\n\n return undefined;\n};\n\nconst runDev = async (options: DevCommandOptions): Promise<void> => {\n const cwd = resolveRuntimeCwd();\n const store = new ArtifactStore(cwd);\n await store.ensureStructure();\n await store.ensureGitignoreEntry();\n\n const config = await resolveConfig(cwd, {\n provider: options.provider,\n model: options.model,\n target: options.target,\n debug: options.debug,\n bridgePort: options.bridgePort,\n proxyPort: options.proxyPort,\n });\n\n const logger = createLogger({\n store,\n component: \"cli\",\n debugEnabled: config.debug,\n });\n\n const reachable = await targetReachable(config.target);\n if (!reachable) {\n throw new Error(\n `Target localhost:${config.target} is unreachable. Start your app first and retry. Hint: lsof -i :${config.target}`,\n );\n }\n\n const overlayScriptPath = resolveOverlayBundlePath(cwd);\n\n const providerRegistry = createProviderRegistry([\"codex\", \"claude\"]);\n const bridge = createBridgeServer({\n cwd,\n port: config.bridgePort,\n store,\n logger,\n overlayScriptPath,\n getProviderAdapter: (provider) => providerRegistry.getAdapter(provider),\n });\n\n const proxy = createReverseProxy({\n targetPort: config.target,\n proxyPort: config.proxyPort,\n bridgePort: config.bridgePort,\n provider: config.provider,\n model: config.model,\n logger,\n });\n\n try {\n await bridge.start();\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"EADDRINUSE\") {\n throw new Error(\n `Bridge port ${config.bridgePort} is already in use. Hint: lsof -i :${config.bridgePort}`,\n );\n }\n\n throw error;\n }\n\n try {\n await proxy.start();\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"EADDRINUSE\") {\n await bridge.stop();\n throw new Error(\n `Proxy port ${config.proxyPort} is already in use. Hint: lsof -i :${config.proxyPort}`,\n );\n }\n\n await bridge.stop();\n throw error;\n }\n\n console.log(`Pinpatch dev ready`);\n console.log(`Target app: http://localhost:${config.target}`);\n console.log(`Proxied app: http://localhost:${config.proxyPort}`);\n console.log(`Bridge API: http://localhost:${config.bridgePort}`);\n\n await waitForSignal();\n\n await proxy.stop();\n await bridge.stop();\n};\n\nconst runImplement = async (\n taskId: string,\n options: ImplementCommandOptions,\n): Promise<void> => {\n const cwd = resolveRuntimeCwd();\n const store = new ArtifactStore(cwd);\n await store.ensureStructure();\n await store.ensureGitignoreEntry();\n\n const config = await resolveConfig(cwd, {\n provider: options.provider,\n model: options.model,\n debug: options.debug,\n });\n\n const logger = createLogger({\n store,\n component: \"cli\",\n debugEnabled: config.debug,\n });\n\n const task = await store.getTask(taskId);\n if (!task) {\n throw new Error(`Task ${taskId} was not found under .pinpatch/tasks`);\n }\n\n const providerRegistry = createProviderRegistry([\"codex\", \"claude\"]);\n const eventBus = new TaskEventBus();\n const runner = new TaskRunner({\n cwd,\n store,\n logger,\n eventBus,\n getProviderAdapter: (provider) => providerRegistry.getAdapter(provider),\n });\n\n const sessionId = generateSessionId();\n const result = await runner.runTask({\n taskId,\n sessionId,\n provider: config.provider,\n model: config.model,\n dryRun: Boolean(options.dryRun),\n debug: config.debug,\n });\n\n console.log(`Task ${taskId} -> ${result.status}`);\n console.log(result.summary);\n if (result.changedFiles.length > 0) {\n console.log(\"Changed files:\");\n for (const changedFile of result.changedFiles) {\n console.log(` - ${changedFile}`);\n }\n }\n\n if (result.status !== \"completed\") {\n process.exitCode = 1;\n }\n};\n\nconst runTasks = async (options: TasksCommandOptions): Promise<void> => {\n const cwd = resolveRuntimeCwd();\n const store = new ArtifactStore(cwd);\n await store.ensureStructure();\n\n if (options.prune) {\n const result = await store.prune();\n console.log(`Pruned logs: ${result.removedLogs}`);\n console.log(`Pruned orphan sessions: ${result.removedSessions}`);\n return;\n }\n\n const tasks = await store.listTasks();\n tasks.sort(\n (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt),\n );\n\n if (tasks.length === 0) {\n console.log(\"No tasks found in .pinpatch/tasks\");\n return;\n }\n\n console.table(\n tasks.map((task) => ({\n taskId: task.taskId,\n status: task.status,\n updatedAt: task.updatedAt,\n provider: task.provider ?? \"-\",\n model: task.model ?? \"-\",\n latestSessionId: task.latestSessionId ?? \"-\",\n })),\n );\n};\n\nconst program = new Command();\nprogram.name(\"pinpatch\").description(\"Pinpatch CLI\").version(\"0.1.0\");\nprogram.addHelpText(\n \"after\",\n `\\nQuick reference:\\n pinpatch dev --target <port>\\n pinpatch implement <taskId>\\n pinpatch tasks\\n pinpatch tasks --prune\\n\\n${keyboardShortcutsHelp}\\n`,\n);\n\nprogram\n .command(\"dev\")\n .description(\"Start Pinpatch bridge + proxy runtime\")\n .option(\"--target <port>\", \"Target app localhost port\", (value) =>\n Number.parseInt(value, 10),\n )\n .option(\"--provider <name>\", \"Provider name (codex|claude)\")\n .option(\"--model <model>\", \"Provider model\")\n .option(\"--bridge-port <port>\", \"Bridge server port\", (value) =>\n Number.parseInt(value, 10),\n )\n .option(\"--proxy-port <port>\", \"Proxy server port\", (value) =>\n Number.parseInt(value, 10),\n )\n .option(\"--debug\", \"Enable debug logs\", false)\n .action(async (options: DevCommandOptions) => {\n await runDev(options);\n });\n\nprogram\n .command(\"implement\")\n .description(\"Execute a saved task through provider adapter\")\n .argument(\"<taskId>\", \"Task id\")\n .option(\"--provider <name>\", \"Provider name\")\n .option(\"--model <model>\", \"Provider model\")\n .option(\"--dry-run\", \"Do not apply provider edits\", false)\n .option(\"--debug\", \"Enable debug logs\", false)\n .action(async (taskId: string, options: ImplementCommandOptions) => {\n await runImplement(taskId, options);\n });\n\nprogram\n .command(\"tasks\")\n .description(\"List or prune task/session artifacts\")\n .option(\"--prune\", \"Prune expired logs and orphan sessions\", false)\n .option(\"--debug\", \"Enable debug logs\", false)\n .action(async (options: TasksCommandOptions) => {\n await runTasks(options);\n });\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n"],"mappings":";AAAA,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAC3B,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AAEnC,IAAM,mBAAmB,QAAQ,KAAK,CAAC,IACnC,KAAK,QAAQ,KAAK,QAAQ,QAAQ,KAAK,CAAC,CAAC,CAAC,IAC1C,QAAQ,IAAI;AAChB,IAAM,qBAAqB,CAAC,OAAO,QAAQ,KAAK,EAAE;AAAA,EAChD,KAAK,SAAS,gBAAgB;AAChC,IACI,KAAK,QAAQ,kBAAkB,IAAI,IACnC;AACJ,IAAM,uBAAuB,KAAK,QAAQ,oBAAoB,OAAO;AAErE,IAAM,oBAAoB,MAAc;AACtC,QAAM,UAAU,QAAQ,IAAI,UAAU,KAAK;AAC3C,MAAI,SAAS;AACX,UAAM,WAAW,KAAK,QAAQ,OAAO;AACrC,QAAI,WAAW,QAAQ,GAAG;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,MAAM,QAAQ,IAAI;AAExB,QAAM,uBAAuB,KAAK,QAAQ,KAAK,OAAO;AACtD,QAAM,yBAAyB,KAAK;AAAA,IAClC;AAAA,IACA;AAAA,EACF;AACA,QAAM,wBACJ,KAAK,SAAS,GAAG,MAAM,SACvB,KAAK,SAAS,KAAK,QAAQ,GAAG,CAAC,MAAM;AACvC,MAAI,yBAAyB,WAAW,sBAAsB,GAAG;AAC/D,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB,KAAK;AAAA,IAC3B;AAAA,IACA;AAAA,EACF;AACA,MAAI,QAAQ,sBAAsB,WAAW,eAAe,GAAG;AAC7D,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAuBA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,IAAM,gBAAgB,YAA2B;AAC/C,QAAM,IAAI,QAAc,CAAC,YAAY;AACnC,UAAM,WAAW,MAAM;AACrB,cAAQ,IAAI,UAAU,QAAQ;AAC9B,cAAQ,IAAI,WAAW,QAAQ;AAC/B,cAAQ;AAAA,IACV;AAEA,YAAQ,GAAG,UAAU,QAAQ;AAC7B,YAAQ,GAAG,WAAW,QAAQ;AAAA,EAChC,CAAC;AACH;AAEA,IAAM,kBAAkB,OAAO,SAAmC;AAChE,SAAO,MAAM,IAAI,QAAiB,CAAC,YAAY;AAC7C,UAAM,UAAU,KAAK;AAAA,MACnB;AAAA,QACE,MAAM;AAAA,QACN;AAAA,QACA,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,MACA,MAAM;AACJ,gBAAQ,QAAQ;AAChB,gBAAQ,IAAI;AAAA,MACd;AAAA,IACF;AAEA,YAAQ,GAAG,SAAS,MAAM,QAAQ,KAAK,CAAC;AACxC,YAAQ,GAAG,WAAW,MAAM;AAC1B,cAAQ,QAAQ;AAChB,cAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACH,CAAC;AACH;AAEA,IAAM,2BAA2B,CAAC,QAAoC;AACpE,QAAM,aAAa;AAAA,IACjB,QAAQ,IAAI;AAAA,IACZ,KAAK,KAAK,KAAK,QAAQ,WAAW,QAAQ,0BAA0B;AAAA,IACpE,KAAK;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,KAAK;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,KAAK;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,KAAK;AAAA,MACH,kBAAkB;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,EAAE,OAAO,CAAC,UAA2B,QAAQ,KAAK,CAAC;AAEnD,aAAW,aAAa,YAAY;AAClC,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,oBAAoB,CAAC,kBAA8C;AACvE,UAAM,mBAAmB,KAAK;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,WAAW,gBAAgB,GAAG;AACjC,aAAO;AAAA,IACT;AAEA,UAAM,SAAS;AAAA,MACb;AAAA,MACA,CAAC,YAAY,qBAAqB,OAAO;AAAA,MACzC;AAAA,QACE,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,YAAY,KAAK;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,UAAI,WAAW,SAAS,GAAG;AACzB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,MAAM,KAAK,oBAAI,IAAI,CAAC,KAAK,oBAAoB,CAAC,CAAC;AAClE,aAAW,aAAa,YAAY;AAClC,UAAM,QAAQ,kBAAkB,SAAS;AACzC,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,SAAS,OAAO,YAA8C;AAClE,QAAM,MAAM,kBAAkB;AAC9B,QAAM,QAAQ,IAAI,cAAc,GAAG;AACnC,QAAM,MAAM,gBAAgB;AAC5B,QAAM,MAAM,qBAAqB;AAEjC,QAAM,SAAS,MAAM,cAAc,KAAK;AAAA,IACtC,UAAU,QAAQ;AAAA,IAClB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,IAChB,OAAO,QAAQ;AAAA,IACf,YAAY,QAAQ;AAAA,IACpB,WAAW,QAAQ;AAAA,EACrB,CAAC;AAED,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA,WAAW;AAAA,IACX,cAAc,OAAO;AAAA,EACvB,CAAC;AAED,QAAM,YAAY,MAAM,gBAAgB,OAAO,MAAM;AACrD,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR,oBAAoB,OAAO,MAAM,mEAAmE,OAAO,MAAM;AAAA,IACnH;AAAA,EACF;AAEA,QAAM,oBAAoB,yBAAyB,GAAG;AAEtD,QAAM,mBAAmB,uBAAuB,CAAC,SAAS,QAAQ,CAAC;AACnE,QAAM,SAAS,mBAAmB;AAAA,IAChC;AAAA,IACA,MAAM,OAAO;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB,CAAC,aAAa,iBAAiB,WAAW,QAAQ;AAAA,EACxE,CAAC;AAED,QAAM,QAAQ,mBAAmB;AAAA,IAC/B,YAAY,OAAO;AAAA,IACnB,WAAW,OAAO;AAAA,IAClB,YAAY,OAAO;AAAA,IACnB,UAAU,OAAO;AAAA,IACjB,OAAO,OAAO;AAAA,IACd;AAAA,EACF,CAAC;AAED,MAAI;AACF,UAAM,OAAO,MAAM;AAAA,EACrB,SAAS,OAAO;AACd,UAAM,OAAQ,MAAgC;AAC9C,QAAI,SAAS,cAAc;AACzB,YAAM,IAAI;AAAA,QACR,eAAe,OAAO,UAAU,sCAAsC,OAAO,UAAU;AAAA,MACzF;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AAEA,MAAI;AACF,UAAM,MAAM,MAAM;AAAA,EACpB,SAAS,OAAO;AACd,UAAM,OAAQ,MAAgC;AAC9C,QAAI,SAAS,cAAc;AACzB,YAAM,OAAO,KAAK;AAClB,YAAM,IAAI;AAAA,QACR,cAAc,OAAO,SAAS,sCAAsC,OAAO,SAAS;AAAA,MACtF;AAAA,IACF;AAEA,UAAM,OAAO,KAAK;AAClB,UAAM;AAAA,EACR;AAEA,UAAQ,IAAI,oBAAoB;AAChC,UAAQ,IAAI,gCAAgC,OAAO,MAAM,EAAE;AAC3D,UAAQ,IAAI,iCAAiC,OAAO,SAAS,EAAE;AAC/D,UAAQ,IAAI,gCAAgC,OAAO,UAAU,EAAE;AAE/D,QAAM,cAAc;AAEpB,QAAM,MAAM,KAAK;AACjB,QAAM,OAAO,KAAK;AACpB;AAEA,IAAM,eAAe,OACnB,QACA,YACkB;AAClB,QAAM,MAAM,kBAAkB;AAC9B,QAAM,QAAQ,IAAI,cAAc,GAAG;AACnC,QAAM,MAAM,gBAAgB;AAC5B,QAAM,MAAM,qBAAqB;AAEjC,QAAM,SAAS,MAAM,cAAc,KAAK;AAAA,IACtC,UAAU,QAAQ;AAAA,IAClB,OAAO,QAAQ;AAAA,IACf,OAAO,QAAQ;AAAA,EACjB,CAAC;AAED,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA,WAAW;AAAA,IACX,cAAc,OAAO;AAAA,EACvB,CAAC;AAED,QAAM,OAAO,MAAM,MAAM,QAAQ,MAAM;AACvC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,QAAQ,MAAM,sCAAsC;AAAA,EACtE;AAEA,QAAM,mBAAmB,uBAAuB,CAAC,SAAS,QAAQ,CAAC;AACnE,QAAM,WAAW,IAAI,aAAa;AAClC,QAAM,SAAS,IAAI,WAAW;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB,CAAC,aAAa,iBAAiB,WAAW,QAAQ;AAAA,EACxE,CAAC;AAED,QAAM,YAAY,kBAAkB;AACpC,QAAM,SAAS,MAAM,OAAO,QAAQ;AAAA,IAClC;AAAA,IACA;AAAA,IACA,UAAU,OAAO;AAAA,IACjB,OAAO,OAAO;AAAA,IACd,QAAQ,QAAQ,QAAQ,MAAM;AAAA,IAC9B,OAAO,OAAO;AAAA,EAChB,CAAC;AAED,UAAQ,IAAI,QAAQ,MAAM,OAAO,OAAO,MAAM,EAAE;AAChD,UAAQ,IAAI,OAAO,OAAO;AAC1B,MAAI,OAAO,aAAa,SAAS,GAAG;AAClC,YAAQ,IAAI,gBAAgB;AAC5B,eAAW,eAAe,OAAO,cAAc;AAC7C,cAAQ,IAAI,MAAM,WAAW,EAAE;AAAA,IACjC;AAAA,EACF;AAEA,MAAI,OAAO,WAAW,aAAa;AACjC,YAAQ,WAAW;AAAA,EACrB;AACF;AAEA,IAAM,WAAW,OAAO,YAAgD;AACtE,QAAM,MAAM,kBAAkB;AAC9B,QAAM,QAAQ,IAAI,cAAc,GAAG;AACnC,QAAM,MAAM,gBAAgB;AAE5B,MAAI,QAAQ,OAAO;AACjB,UAAM,SAAS,MAAM,MAAM,MAAM;AACjC,YAAQ,IAAI,gBAAgB,OAAO,WAAW,EAAE;AAChD,YAAQ,IAAI,2BAA2B,OAAO,eAAe,EAAE;AAC/D;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,MAAM,UAAU;AACpC,QAAM;AAAA,IACJ,CAAC,MAAM,UAAU,KAAK,MAAM,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,SAAS;AAAA,EAC1E;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAI,mCAAmC;AAC/C;AAAA,EACF;AAEA,UAAQ;AAAA,IACN,MAAM,IAAI,CAAC,UAAU;AAAA,MACnB,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK;AAAA,MACb,WAAW,KAAK;AAAA,MAChB,UAAU,KAAK,YAAY;AAAA,MAC3B,OAAO,KAAK,SAAS;AAAA,MACrB,iBAAiB,KAAK,mBAAmB;AAAA,IAC3C,EAAE;AAAA,EACJ;AACF;AAEA,IAAM,UAAU,IAAI,QAAQ;AAC5B,QAAQ,KAAK,UAAU,EAAE,YAAY,cAAc,EAAE,QAAQ,OAAO;AACpE,QAAQ;AAAA,EACN;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAoI,qBAAqB;AAAA;AAC3J;AAEA,QACG,QAAQ,KAAK,EACb,YAAY,uCAAuC,EACnD;AAAA,EAAO;AAAA,EAAmB;AAAA,EAA6B,CAAC,UACvD,OAAO,SAAS,OAAO,EAAE;AAC3B,EACC,OAAO,qBAAqB,8BAA8B,EAC1D,OAAO,mBAAmB,gBAAgB,EAC1C;AAAA,EAAO;AAAA,EAAwB;AAAA,EAAsB,CAAC,UACrD,OAAO,SAAS,OAAO,EAAE;AAC3B,EACC;AAAA,EAAO;AAAA,EAAuB;AAAA,EAAqB,CAAC,UACnD,OAAO,SAAS,OAAO,EAAE;AAC3B,EACC,OAAO,WAAW,qBAAqB,KAAK,EAC5C,OAAO,OAAO,YAA+B;AAC5C,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QACG,QAAQ,WAAW,EACnB,YAAY,+CAA+C,EAC3D,SAAS,YAAY,SAAS,EAC9B,OAAO,qBAAqB,eAAe,EAC3C,OAAO,mBAAmB,gBAAgB,EAC1C,OAAO,aAAa,+BAA+B,KAAK,EACxD,OAAO,WAAW,qBAAqB,KAAK,EAC5C,OAAO,OAAO,QAAgB,YAAqC;AAClE,QAAM,aAAa,QAAQ,OAAO;AACpC,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,sCAAsC,EAClD,OAAO,WAAW,0CAA0C,KAAK,EACjE,OAAO,WAAW,qBAAqB,KAAK,EAC5C,OAAO,OAAO,YAAiC;AAC9C,QAAM,SAAS,OAAO;AACxB,CAAC;AAEH,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,UAAmB;AACzD,UAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACpE,UAAQ,WAAW;AACrB,CAAC;","names":[]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "pinpatch",
3
+ "version": "0.1.0",
4
+ "description": "CLI for Pinpatch pin-to-code workflow with local proxy and overlay runtime.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/alex1craig/pinpatch.git"
9
+ },
10
+ "keywords": [
11
+ "pinpatch",
12
+ "cli",
13
+ "developer-tools",
14
+ "proxy",
15
+ "overlay"
16
+ ],
17
+ "type": "module",
18
+ "main": "dist/index.cjs",
19
+ "module": "dist/index.js",
20
+ "types": "dist/index.d.ts",
21
+ "files": [
22
+ "dist",
23
+ "bin"
24
+ ],
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "bin": {
32
+ "pinpatch": "bin/pinpatch.mjs"
33
+ },
34
+ "dependencies": {
35
+ "commander": "^13.1.0",
36
+ "@pinpatch/core": "0.1.0",
37
+ "@pinpatch/proxy": "0.1.0",
38
+ "@pinpatch/providers": "0.1.0",
39
+ "@pinpatch/overlay": "0.1.0"
40
+ },
41
+ "devDependencies": {
42
+ "tsup": "^8.3.5",
43
+ "tsx": "^4.19.2",
44
+ "vitest": "^3.0.6"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "dev": "tsx src/index.ts",
49
+ "test": "vitest run --passWithNoTests",
50
+ "typecheck": "tsc -p tsconfig.json --noEmit",
51
+ "lint": "echo 'lint not configured'",
52
+ "clean": "rm -rf dist coverage"
53
+ }
54
+ }