@pushary/agent-hooks 1.3.1 → 1.4.1
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/CHANGELOG.md +23 -0
- package/README.md +1 -1
- package/dist/bin/pushary-bell-hook.js +18 -18
- package/dist/bin/pushary-bell.js +8 -8
- package/dist/bin/pushary-claude.js +18 -18
- package/dist/bin/pushary-clean.js +490 -95
- package/dist/bin/pushary-codex-bridge.js +10 -10
- package/dist/bin/pushary-codex-hook.js +28 -26
- package/dist/bin/pushary-codex.js +6 -6
- package/dist/bin/pushary-connect.js +8 -8
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +24 -24
- package/dist/bin/pushary-disconnect.js +12 -12
- package/dist/bin/pushary-doctor.js +75 -26
- package/dist/bin/pushary-elicitation-hook.js +17 -17
- package/dist/bin/pushary-gemini-bridge.js +9 -9
- package/dist/bin/pushary-gemini-hook.js +19 -19
- package/dist/bin/pushary-hook.js +27 -27
- package/dist/bin/pushary-login.js +9 -9
- package/dist/bin/pushary-logout.js +6 -6
- package/dist/bin/pushary-mode.js +5 -5
- package/dist/bin/pushary-notification-hook.js +20 -20
- package/dist/bin/pushary-opencode-hook.js +19 -19
- package/dist/bin/pushary-permission-denied-hook.js +24 -24
- package/dist/bin/pushary-permission-hook.js +25 -25
- package/dist/bin/pushary-post-hook.js +20 -20
- package/dist/bin/pushary-prompt-hook.js +20 -20
- package/dist/bin/pushary-session-end-hook.js +20 -20
- package/dist/bin/pushary-session-start-hook.js +22 -22
- package/dist/bin/pushary-setup.js +38 -59
- package/dist/bin/pushary-stats.js +4 -4
- package/dist/bin/pushary-status.js +12 -12
- package/dist/bin/pushary-stop-hook.js +20 -20
- package/dist/bin/pushary-stopfailure-hook.js +20 -20
- package/dist/bin/pushary-suggestions.js +2 -2
- package/dist/bin/pushary-transcript-register.js +16 -16
- package/dist/bin/pushary-transcripts.js +6 -6
- package/dist/bin/pushary-upgrade.js +16 -16
- package/dist/bin/pushary-wait.js +5 -5
- package/dist/bin/pushary.js +1 -1
- package/dist/{chunk-RERJ2ZDV.js → chunk-2GMI6JH6.js} +5 -5
- package/dist/{chunk-AFOTOA24.js → chunk-37VLIYLI.js} +35 -4
- package/dist/{chunk-6HH4IH4K.js → chunk-3BPB2HYZ.js} +3 -3
- package/dist/{chunk-465UENKO.js → chunk-3T52B2OF.js} +1 -1
- package/dist/{chunk-PEV6ULSM.js → chunk-3UA6FPRC.js} +3 -3
- package/dist/{chunk-QDYJQLXT.js → chunk-43JKGLZZ.js} +2 -2
- package/dist/{chunk-CKLWJ6EL.js → chunk-4FN6VV56.js} +33 -7
- package/dist/{chunk-HCTSTMDD.js → chunk-ALFT7H5B.js} +3 -3
- package/dist/{chunk-35RPUNYU.js → chunk-BZBUPZC6.js} +1 -1
- package/dist/{chunk-CDSL3LLO.js → chunk-CRNAXFQ6.js} +1 -1
- package/dist/{chunk-NDSOOJDX.js → chunk-D7ERHWYB.js} +1 -1
- package/dist/{chunk-KHGIM2BN.js → chunk-ED5TA2FJ.js} +3 -3
- package/dist/{chunk-LZQVAEWP.js → chunk-FAH3D74E.js} +2 -1
- package/dist/{chunk-5UWDYOZX.js → chunk-FVKFDTN6.js} +81 -8
- package/dist/{chunk-I2OLM3DM.js → chunk-GOFDGTEI.js} +62 -12
- package/dist/{chunk-JZA2JOGO.js → chunk-HK3IXIQJ.js} +1 -1
- package/dist/{chunk-LWT34GSY.js → chunk-HQLAZWJJ.js} +7 -7
- package/dist/{chunk-5XKMOZO6.js → chunk-I6HRQOMT.js} +4 -4
- package/dist/{chunk-EAGMQMRM.js → chunk-JOVWYVSR.js} +1 -1
- package/dist/{chunk-5WLH2ENB.js → chunk-K6I5M2ME.js} +1 -1
- package/dist/{chunk-REIZ5GXG.js → chunk-KEMEBTCJ.js} +4 -4
- package/dist/{chunk-PHXU4NOP.js → chunk-KI36MAT6.js} +2 -2
- package/dist/{chunk-WOBHHXW5.js → chunk-LNGYW6G4.js} +5 -4
- package/dist/{chunk-AFKOWCMQ.js → chunk-LUT2EZLG.js} +1 -1
- package/dist/{chunk-KLZJEMSE.js → chunk-LYXX5NSB.js} +1 -1
- package/dist/{chunk-YXWM4TNX.js → chunk-MN3XOVRK.js} +1 -1
- package/dist/{chunk-XOUQUZ43.js → chunk-PHHGHHOW.js} +1 -1
- package/dist/{chunk-TW7KA27C.js → chunk-QBS6LGEK.js} +1 -1
- package/dist/{chunk-FGNABUPN.js → chunk-R3AMKNIW.js} +1 -1
- package/dist/{chunk-TGG4FWWB.js → chunk-S3EP4B35.js} +2 -2
- package/dist/{chunk-34NNTYK6.js → chunk-SAF6NNXY.js} +1 -1
- package/dist/{chunk-R5CU7ED6.js → chunk-SKXLIVFM.js} +1 -1
- package/dist/{chunk-OMP3KDDU.js → chunk-SWFF5U2N.js} +1 -1
- package/dist/{chunk-QOCWB4Q6.js → chunk-TF4IOINR.js} +3 -3
- package/dist/{chunk-5SALHATY.js → chunk-TI3ZWXZG.js} +11 -2
- package/dist/{chunk-P232YODQ.js → chunk-TNS3YLFE.js} +6 -6
- package/dist/{chunk-GOQHN2UH.js → chunk-TWWLVOPB.js} +1 -1
- package/dist/{chunk-XVE52YFZ.js → chunk-VYUDGQM7.js} +1 -1
- package/dist/{chunk-XP2KRTZD.js → chunk-WEO5JCB4.js} +1 -1
- package/dist/{chunk-SGZE2JBF.js → chunk-WIOXWUUH.js} +3 -3
- package/dist/{reapply-ADFRVH5N.js → reapply-BAWDJTG7.js} +8 -8
- package/dist/src/index.d.ts +19 -16
- package/dist/src/index.js +12 -12
- package/package.json +1 -1
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HERMES_CONFIG_SCRIPT,
|
|
4
4
|
HERMES_PIP_PACKAGE,
|
|
5
|
+
PUSHARY_SKILL_NAME,
|
|
6
|
+
PUSHARY_SKILL_SOURCE,
|
|
5
7
|
createGuards,
|
|
6
8
|
resolveHermesPython,
|
|
7
9
|
runHermesPythonScript
|
|
8
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-37VLIYLI.js";
|
|
9
11
|
import "../chunk-P32LPT2L.js";
|
|
10
12
|
import {
|
|
11
13
|
removeInstructionBlock,
|
|
12
14
|
shortenHome,
|
|
13
15
|
unregisterPluginLocation,
|
|
14
16
|
vscodeSettingsTargets
|
|
15
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-3UA6FPRC.js";
|
|
16
18
|
import {
|
|
17
19
|
OPENCODE_PLUGIN_SIBLING,
|
|
18
20
|
OPENCODE_PLUGIN_SIBLING_CONTENTS,
|
|
@@ -22,7 +24,7 @@ import {
|
|
|
22
24
|
openCodePluginDir,
|
|
23
25
|
openCodePluginFile,
|
|
24
26
|
removeOpenCodeMcpServer
|
|
25
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-VYUDGQM7.js";
|
|
26
28
|
import {
|
|
27
29
|
compatibleConfigFile,
|
|
28
30
|
compatibleInstructionsFile,
|
|
@@ -30,37 +32,40 @@ import {
|
|
|
30
32
|
compatiblePermissionRuleFile,
|
|
31
33
|
removeCompatibleHooks,
|
|
32
34
|
removeCompatibleMcpServer
|
|
33
|
-
} from "../chunk-
|
|
35
|
+
} from "../chunk-HQLAZWJJ.js";
|
|
34
36
|
import {
|
|
35
37
|
isClaudeMcpSnapshot,
|
|
36
38
|
removeClaudeMcpServers,
|
|
37
39
|
removePusharySettings,
|
|
38
40
|
removePusharyToolPermissions,
|
|
39
41
|
restoreClaudeMcpServer
|
|
40
|
-
} from "../chunk-
|
|
42
|
+
} from "../chunk-WIOXWUUH.js";
|
|
41
43
|
import {
|
|
42
44
|
hookOwnerOf,
|
|
43
45
|
isHookTakeoverReceipt,
|
|
44
46
|
restoreHookTakeover,
|
|
45
47
|
restoreMcpServer
|
|
46
|
-
} from "../chunk-
|
|
47
|
-
import "../chunk-
|
|
48
|
-
import
|
|
48
|
+
} from "../chunk-KEMEBTCJ.js";
|
|
49
|
+
import "../chunk-R3AMKNIW.js";
|
|
50
|
+
import {
|
|
51
|
+
liveBridgeProbe,
|
|
52
|
+
resolveBridgeStub
|
|
53
|
+
} from "../chunk-LYXX5NSB.js";
|
|
49
54
|
import {
|
|
50
55
|
pusharyHookExecutable
|
|
51
|
-
} from "../chunk-
|
|
56
|
+
} from "../chunk-TF4IOINR.js";
|
|
52
57
|
import {
|
|
53
58
|
disableDaemonService
|
|
54
|
-
} from "../chunk-
|
|
59
|
+
} from "../chunk-3BPB2HYZ.js";
|
|
55
60
|
import {
|
|
56
61
|
removeGeminiSettings
|
|
57
|
-
} from "../chunk-
|
|
58
|
-
import "../chunk-
|
|
62
|
+
} from "../chunk-ED5TA2FJ.js";
|
|
63
|
+
import "../chunk-JOVWYVSR.js";
|
|
59
64
|
import "../chunk-XHKBHWLX.js";
|
|
60
|
-
import "../chunk-
|
|
61
|
-
import "../chunk-
|
|
65
|
+
import "../chunk-LNGYW6G4.js";
|
|
66
|
+
import "../chunk-WEO5JCB4.js";
|
|
62
67
|
import "../chunk-LGXUGYUH.js";
|
|
63
|
-
import "../chunk-
|
|
68
|
+
import "../chunk-3T52B2OF.js";
|
|
64
69
|
import {
|
|
65
70
|
bridgeAgentKey,
|
|
66
71
|
claudeJson,
|
|
@@ -89,41 +94,319 @@ import {
|
|
|
89
94
|
restoreCodexHookTrust,
|
|
90
95
|
vscodePluginDir,
|
|
91
96
|
vscodePluginMcp
|
|
92
|
-
} from "../chunk-
|
|
97
|
+
} from "../chunk-SAF6NNXY.js";
|
|
93
98
|
import {
|
|
94
99
|
exitOnHelpFlag
|
|
95
|
-
} from "../chunk-
|
|
96
|
-
import "../chunk-
|
|
100
|
+
} from "../chunk-TWWLVOPB.js";
|
|
101
|
+
import "../chunk-FAH3D74E.js";
|
|
97
102
|
import {
|
|
98
103
|
rejectUnknownFlags
|
|
99
104
|
} from "../chunk-GMXKITVA.js";
|
|
100
105
|
import "../chunk-DINDL2CF.js";
|
|
101
106
|
import {
|
|
107
|
+
CANDIDATE_RCS,
|
|
108
|
+
isDisabledExport,
|
|
102
109
|
removeClaudeAlias
|
|
103
|
-
} from "../chunk-
|
|
110
|
+
} from "../chunk-TI3ZWXZG.js";
|
|
104
111
|
import "../chunk-EYRGBYZG.js";
|
|
105
112
|
import "../chunk-SAF6HGAA.js";
|
|
106
113
|
import "../chunk-7QLSKOSU.js";
|
|
107
114
|
import {
|
|
108
115
|
EXIT
|
|
109
116
|
} from "../chunk-KZERVKTD.js";
|
|
110
|
-
import "../chunk-
|
|
117
|
+
import "../chunk-K6I5M2ME.js";
|
|
111
118
|
import "../chunk-T7E75EKA.js";
|
|
112
119
|
import "../chunk-2UMNXADU.js";
|
|
113
120
|
import {
|
|
114
121
|
CURSOR_GATE_EVENTS,
|
|
115
122
|
HOOK_COMPATIBLE_PROFILES,
|
|
116
123
|
HOOK_LOCATOR_BINARY,
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
SUPPORT_DISCORD,
|
|
125
|
+
SUPPORT_EMAIL,
|
|
126
|
+
isValidApiKey,
|
|
127
|
+
redactSecrets
|
|
128
|
+
} from "../chunk-FVKFDTN6.js";
|
|
119
129
|
|
|
120
130
|
// bin/pushary-clean.ts
|
|
121
|
-
import { existsSync, readFileSync, copyFileSync, readdirSync } from "fs";
|
|
122
|
-
import { join } from "path";
|
|
123
|
-
import { homedir, tmpdir } from "os";
|
|
131
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, copyFileSync, readdirSync as readdirSync3 } from "fs";
|
|
132
|
+
import { dirname, join as join5 } from "path";
|
|
133
|
+
import { homedir as homedir5, tmpdir } from "os";
|
|
124
134
|
import { confirm } from "@inquirer/prompts";
|
|
125
135
|
import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
|
|
126
136
|
|
|
137
|
+
// src/cli/mac-leftovers.ts
|
|
138
|
+
import { execSync, execFileSync } from "child_process";
|
|
139
|
+
import { existsSync } from "fs";
|
|
140
|
+
import { homedir } from "os";
|
|
141
|
+
import { join } from "path";
|
|
142
|
+
var MAC_APP_BUNDLE = "/Applications/Pushary.app";
|
|
143
|
+
var installedPusharyBundles = (home = homedir()) => [MAC_APP_BUNDLE, join(home, "Applications", "Pushary.app")];
|
|
144
|
+
var LSREGISTER = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
|
|
145
|
+
var TEST_DEFAULTS_PREFIXES = [
|
|
146
|
+
"com.pushary.mac.tests.",
|
|
147
|
+
"com.pushary.tests.",
|
|
148
|
+
"pushary.tests.",
|
|
149
|
+
"pushary.quiet-mode-test.",
|
|
150
|
+
"pushary-optin-"
|
|
151
|
+
];
|
|
152
|
+
var isTestDefaultsDomain = (domain) => TEST_DEFAULTS_PREFIXES.some((prefix) => domain.startsWith(prefix));
|
|
153
|
+
var leakedTestDefaultsDomains = () => {
|
|
154
|
+
try {
|
|
155
|
+
return execSync("defaults domains", { encoding: "utf-8", timeout: 2e4 }).split(",").map((entry) => entry.trim()).filter(isTestDefaultsDomain);
|
|
156
|
+
} catch {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
var BUNDLE_NAME = "Pushary.app";
|
|
161
|
+
var DUMP_PATH_LINE = /^\s*path:\s+(.+?)(?: \(0x[0-9a-f]+\))?\s*$/;
|
|
162
|
+
var bundlesInLaunchServicesDump = (dump) => {
|
|
163
|
+
const found = /* @__PURE__ */ new Set();
|
|
164
|
+
for (const line of dump.split("\n")) {
|
|
165
|
+
const path = DUMP_PATH_LINE.exec(line)?.[1];
|
|
166
|
+
const bundle = path?.indexOf(`/${BUNDLE_NAME}`);
|
|
167
|
+
if (path === void 0 || bundle === void 0 || bundle === -1) continue;
|
|
168
|
+
found.add(path.slice(0, bundle + BUNDLE_NAME.length + 1));
|
|
169
|
+
}
|
|
170
|
+
return [...found];
|
|
171
|
+
};
|
|
172
|
+
var registeredPusharyBundles = (installed = installedPusharyBundles()) => {
|
|
173
|
+
if (!existsSync(LSREGISTER)) return [];
|
|
174
|
+
try {
|
|
175
|
+
const dump = execSync(`${LSREGISTER} -dump`, {
|
|
176
|
+
encoding: "utf-8",
|
|
177
|
+
timeout: 6e4,
|
|
178
|
+
maxBuffer: 256 * 1024 * 1024
|
|
179
|
+
});
|
|
180
|
+
return bundlesInLaunchServicesDump(dump).filter((path) => !installed.includes(path));
|
|
181
|
+
} catch {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
var MAC_KEYCHAIN_SERVICE = "com.pushary.app.mac";
|
|
186
|
+
var MAC_KEYCHAIN_ACCOUNTS = [
|
|
187
|
+
"pushary.site-api-key",
|
|
188
|
+
"pushary.site-id",
|
|
189
|
+
"clerk.device-token",
|
|
190
|
+
"clerk.session-id"
|
|
191
|
+
];
|
|
192
|
+
var MAC_DEFAULTS_DOMAINS = ["com.pushary.app", "pushary-bridge", "pushary.render"];
|
|
193
|
+
var macLibraryState = (home = homedir()) => [
|
|
194
|
+
join(home, "Library", "Application Support", "Pushary"),
|
|
195
|
+
join(home, "Library", "Caches", "com.pushary.app"),
|
|
196
|
+
join(home, "Library", "Caches", "com.pushary.app.sparkle"),
|
|
197
|
+
join(home, "Library", "Saved Application State", "com.pushary.app.savedState"),
|
|
198
|
+
join(home, "Library", "Caches", "pushary-bridge"),
|
|
199
|
+
join(home, "Library", "HTTPStorages", "com.pushary.app"),
|
|
200
|
+
join(home, "Library", "HTTPStorages", "com.pushary.app.binarycookies"),
|
|
201
|
+
join(home, "Library", "HTTPStorages", "pushary-bridge")
|
|
202
|
+
];
|
|
203
|
+
var DUMP_SERVICE_LINE = /^\s*"svce"<blob>="(.+)"\s*$/;
|
|
204
|
+
var pusharyKeychainServices = (dump) => {
|
|
205
|
+
const found = /* @__PURE__ */ new Set();
|
|
206
|
+
for (const line of dump.split("\n")) {
|
|
207
|
+
const service = DUMP_SERVICE_LINE.exec(line)?.[1];
|
|
208
|
+
if (service?.startsWith(MAC_KEYCHAIN_SERVICE)) found.add(service);
|
|
209
|
+
}
|
|
210
|
+
return [...found];
|
|
211
|
+
};
|
|
212
|
+
var storedPusharyKeychainServices = () => {
|
|
213
|
+
const dump = execFileSync("security", ["dump-keychain"], {
|
|
214
|
+
encoding: "utf-8",
|
|
215
|
+
timeout: 3e4,
|
|
216
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
217
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
218
|
+
});
|
|
219
|
+
const stored = pusharyKeychainServices(dump);
|
|
220
|
+
return stored.includes(MAC_KEYCHAIN_SERVICE) ? stored : [MAC_KEYCHAIN_SERVICE, ...stored];
|
|
221
|
+
};
|
|
222
|
+
var MAC_APP_BUNDLE_ID = "com.pushary.app";
|
|
223
|
+
var runningMacAppPIDs = () => {
|
|
224
|
+
if (process.platform !== "darwin") return [];
|
|
225
|
+
try {
|
|
226
|
+
return execSync('pgrep -f "Pushary.app/Contents/MacOS/Pushary"', {
|
|
227
|
+
encoding: "utf-8",
|
|
228
|
+
timeout: 1e4,
|
|
229
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
230
|
+
}).split("\n").map((line) => Number(line.trim())).filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
231
|
+
} catch {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
var waitForMacAppExit = (attempts = 20, everyMs = 250) => {
|
|
236
|
+
const idle = new Int32Array(new SharedArrayBuffer(4));
|
|
237
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
238
|
+
if (runningMacAppPIDs().length === 0) return true;
|
|
239
|
+
Atomics.wait(idle, 0, 0, everyMs);
|
|
240
|
+
}
|
|
241
|
+
return runningMacAppPIDs().length === 0;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// src/cli/clean-mac.ts
|
|
245
|
+
import { existsSync as existsSync2 } from "fs";
|
|
246
|
+
var removeDeveloperLeftovers = (guards2, domains, bundles) => {
|
|
247
|
+
let removed = 0;
|
|
248
|
+
for (const domain of domains) {
|
|
249
|
+
try {
|
|
250
|
+
guards2.execFile("defaults", ["delete", domain], { stdio: "ignore", timeout: 1e4 }, `delete preferences ${domain}`);
|
|
251
|
+
removed++;
|
|
252
|
+
} catch {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
for (const bundle of bundles) {
|
|
257
|
+
try {
|
|
258
|
+
guards2.execFile(LSREGISTER, ["-u", bundle], { stdio: "ignore", timeout: 2e4 }, `unregister ${bundle}`);
|
|
259
|
+
removed++;
|
|
260
|
+
} catch {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return removed;
|
|
265
|
+
};
|
|
266
|
+
var removeMacAppState = (guards2, readServices, home) => {
|
|
267
|
+
let removed = 0;
|
|
268
|
+
let failed = 0;
|
|
269
|
+
let services;
|
|
270
|
+
try {
|
|
271
|
+
services = readServices();
|
|
272
|
+
} catch {
|
|
273
|
+
return { removed, failed: 1 };
|
|
274
|
+
}
|
|
275
|
+
for (const service of services) {
|
|
276
|
+
for (const account of MAC_KEYCHAIN_ACCOUNTS) {
|
|
277
|
+
try {
|
|
278
|
+
guards2.execFile(
|
|
279
|
+
"security",
|
|
280
|
+
["delete-generic-password", "-s", service, "-a", account],
|
|
281
|
+
{ stdio: "ignore" },
|
|
282
|
+
`remove keychain item ${service} (${account})`
|
|
283
|
+
);
|
|
284
|
+
removed++;
|
|
285
|
+
} catch (error) {
|
|
286
|
+
if (!(error instanceof Error && "status" in error && error.status === 44)) failed++;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
for (const domain of MAC_DEFAULTS_DOMAINS) {
|
|
291
|
+
try {
|
|
292
|
+
guards2.execFile("defaults", ["delete", domain], { stdio: "ignore", timeout: 1e4 }, `delete preferences ${domain}`);
|
|
293
|
+
removed++;
|
|
294
|
+
} catch {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
for (const path of macLibraryState(home)) {
|
|
299
|
+
if (!existsSync2(path)) continue;
|
|
300
|
+
guards2.remove(path, { force: true });
|
|
301
|
+
removed++;
|
|
302
|
+
}
|
|
303
|
+
return { removed, failed };
|
|
304
|
+
};
|
|
305
|
+
var quitMacApp = (guards2, state) => {
|
|
306
|
+
if (state.platform !== "darwin" || state.running().length === 0) return "not-running";
|
|
307
|
+
try {
|
|
308
|
+
guards2.execFile(
|
|
309
|
+
"osascript",
|
|
310
|
+
["-e", `tell application id "${MAC_APP_BUNDLE_ID}" to quit`],
|
|
311
|
+
{ stdio: "ignore", timeout: 3e4 },
|
|
312
|
+
"quit Pushary"
|
|
313
|
+
);
|
|
314
|
+
} catch {
|
|
315
|
+
}
|
|
316
|
+
return state.dryRun || state.waitForExit() ? "quit" : "running";
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// src/cli/editor-extension.ts
|
|
320
|
+
import { existsSync as existsSync3, readFileSync, readdirSync } from "fs";
|
|
321
|
+
import { homedir as homedir2 } from "os";
|
|
322
|
+
import { join as join2 } from "path";
|
|
323
|
+
var EDITOR_EXTENSION_ID = "pushary.pushary";
|
|
324
|
+
var EDITOR_EXTENSION_DIRS = [
|
|
325
|
+
join2(".cursor", "extensions"),
|
|
326
|
+
join2(".vscode", "extensions")
|
|
327
|
+
];
|
|
328
|
+
var EXTENSIONS_JSON = "extensions.json";
|
|
329
|
+
var isOurEntry = (entry) => {
|
|
330
|
+
if (typeof entry !== "object" || entry === null) return false;
|
|
331
|
+
const identifier = entry.identifier;
|
|
332
|
+
if (typeof identifier !== "object" || identifier === null) return false;
|
|
333
|
+
const id = identifier.id;
|
|
334
|
+
return typeof id === "string" && id.toLowerCase() === EDITOR_EXTENSION_ID;
|
|
335
|
+
};
|
|
336
|
+
var recordWithoutUs = (directory) => {
|
|
337
|
+
const path = join2(directory, EXTENSIONS_JSON);
|
|
338
|
+
try {
|
|
339
|
+
const entries = JSON.parse(readFileSync(path, "utf-8"));
|
|
340
|
+
if (!Array.isArray(entries)) return void 0;
|
|
341
|
+
const kept = entries.filter((entry) => !isOurEntry(entry));
|
|
342
|
+
return kept.length === entries.length ? void 0 : { path, contents: JSON.stringify(kept) };
|
|
343
|
+
} catch {
|
|
344
|
+
return void 0;
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
var editorExtensionRemovals = (home = homedir2()) => EDITOR_EXTENSION_DIRS.map((relative) => join2(home, relative)).filter(existsSync3).map((directory) => {
|
|
348
|
+
const folders = readdirSync(directory).filter((name) => name.startsWith(`${EDITOR_EXTENSION_ID}-`)).map((name) => join2(directory, name));
|
|
349
|
+
return { directory, folders, record: recordWithoutUs(directory) };
|
|
350
|
+
}).filter((removal) => removal.folders.length > 0 || removal.record !== void 0);
|
|
351
|
+
|
|
352
|
+
// src/cli/skills-store.ts
|
|
353
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
354
|
+
import { homedir as homedir3 } from "os";
|
|
355
|
+
import { join as join3 } from "path";
|
|
356
|
+
var SKILLS_STORE_DIR = ".agents";
|
|
357
|
+
var LOCK_FILE = ".skill-lock.json";
|
|
358
|
+
var lockWithoutUs = (path) => {
|
|
359
|
+
try {
|
|
360
|
+
const lock = JSON.parse(readFileSync2(path, "utf-8"));
|
|
361
|
+
if (!lock || typeof lock !== "object" || Array.isArray(lock) || !("skills" in lock)) return void 0;
|
|
362
|
+
const entries = lock.skills;
|
|
363
|
+
if (!entries || typeof entries !== "object" || Array.isArray(entries) || !(PUSHARY_SKILL_NAME in entries)) return void 0;
|
|
364
|
+
const entry = entries[PUSHARY_SKILL_NAME];
|
|
365
|
+
if (!entry || typeof entry !== "object" || !("source" in entry) || entry.source !== PUSHARY_SKILL_SOURCE) return void 0;
|
|
366
|
+
const { [PUSHARY_SKILL_NAME]: _ours, ...skills } = entries;
|
|
367
|
+
return { path, contents: `${JSON.stringify({ ...lock, skills }, null, 2)}
|
|
368
|
+
` };
|
|
369
|
+
} catch {
|
|
370
|
+
return void 0;
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
var skillsStoreRemoval = (home = homedir3()) => {
|
|
374
|
+
const store = join3(home, SKILLS_STORE_DIR);
|
|
375
|
+
const skillDir = join3(store, "skills", PUSHARY_SKILL_NAME);
|
|
376
|
+
const lock = lockWithoutUs(join3(store, LOCK_FILE));
|
|
377
|
+
return {
|
|
378
|
+
skillDir: lock && existsSync4(skillDir) ? skillDir : void 0,
|
|
379
|
+
lock
|
|
380
|
+
};
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// src/cli/claude-backups.ts
|
|
384
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
385
|
+
import { homedir as homedir4 } from "os";
|
|
386
|
+
import { join as join4 } from "path";
|
|
387
|
+
var BACKUP_PREFIX = ".claude.json.backup";
|
|
388
|
+
var claudeJsonBackups = (home = homedir4()) => {
|
|
389
|
+
const rollingDir = join4(home, ".claude", "backups");
|
|
390
|
+
const legacy = join4(home, BACKUP_PREFIX);
|
|
391
|
+
const rolling = existsSync5(rollingDir) ? readdirSync2(rollingDir).filter((name) => name.startsWith(BACKUP_PREFIX)).sort().map((name) => join4(rollingDir, name)) : [];
|
|
392
|
+
return existsSync5(legacy) ? [...rolling, legacy] : rolling;
|
|
393
|
+
};
|
|
394
|
+
var parsed = (path) => {
|
|
395
|
+
try {
|
|
396
|
+
const value = JSON.parse(readFileSync3(path, "utf-8"));
|
|
397
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
398
|
+
} catch {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
var claudeBackupRewrites = (receipt, home = homedir4()) => claudeJsonBackups(home).flatMap((path) => {
|
|
403
|
+
const backup = parsed(path);
|
|
404
|
+
if (!backup) return [];
|
|
405
|
+
const changed = receipt ? restoreClaudeMcpServer(backup, receipt) : removeClaudeMcpServers(backup);
|
|
406
|
+
return changed ? [{ path, contents: `${JSON.stringify(backup, null, 2)}
|
|
407
|
+
` }] : [];
|
|
408
|
+
});
|
|
409
|
+
|
|
127
410
|
// src/app-owned-paths.ts
|
|
128
411
|
var APP_OWNED_ENTRIES = [
|
|
129
412
|
// PusharyPaths.bin: bridgeStub, bridgeCache
|
|
@@ -176,10 +459,15 @@ var CURSOR_PLUGIN_DIR = cursorPluginDir();
|
|
|
176
459
|
var CURSOR_USER_HOOKS = cursorUserHooks();
|
|
177
460
|
var VSCODE_PLUGIN_DIR = vscodePluginDir();
|
|
178
461
|
var PUSHARY_DIR = pusharyDir();
|
|
179
|
-
var SHELL_FILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"].map((f) =>
|
|
180
|
-
var
|
|
181
|
-
|
|
182
|
-
|
|
462
|
+
var SHELL_FILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"].map((f) => join5(homedir5(), f));
|
|
463
|
+
var PRESENCE_BY_RESOLUTION = {
|
|
464
|
+
bundle: "present",
|
|
465
|
+
missing: "gone",
|
|
466
|
+
"no-stub": "absent"
|
|
467
|
+
};
|
|
468
|
+
var macAppPresence = () => PRESENCE_BY_RESOLUTION[resolveBridgeStub(join5(PUSHARY_DIR, "bin", HOOK_LOCATOR_BINARY), liveBridgeProbe()).kind];
|
|
469
|
+
var removalScope = (keepMcpServer, keepAppWiring) => ({
|
|
470
|
+
keepAppWiring,
|
|
183
471
|
keepMcpServer
|
|
184
472
|
});
|
|
185
473
|
var codexMcpKey = (config) => {
|
|
@@ -188,7 +476,7 @@ var codexMcpKey = (config) => {
|
|
|
188
476
|
};
|
|
189
477
|
var readJson = (path) => {
|
|
190
478
|
try {
|
|
191
|
-
return JSON.parse(
|
|
479
|
+
return JSON.parse(readFileSync4(path, "utf-8"));
|
|
192
480
|
} catch {
|
|
193
481
|
return null;
|
|
194
482
|
}
|
|
@@ -201,7 +489,16 @@ var wouldNote = (action, path) => {
|
|
|
201
489
|
var guards = createGuards(() => dryRun, wouldNote);
|
|
202
490
|
var guardedWrite = (path, contents) => guards.write(path, contents);
|
|
203
491
|
var guardedRemove = (path, options) => guards.remove(path, options);
|
|
204
|
-
var
|
|
492
|
+
var CURSOR_PLUGIN_STAGING = ".pushary-staging-";
|
|
493
|
+
var cursorPluginLeftovers = () => {
|
|
494
|
+
const parent = dirname(CURSOR_PLUGIN_DIR);
|
|
495
|
+
const backupPrefix = `${CURSOR_PLUGIN_DIR.split("/").pop()}.pushary-backup-`;
|
|
496
|
+
try {
|
|
497
|
+
return readdirSync3(parent).filter((name) => name.startsWith(backupPrefix) || name.startsWith(CURSOR_PLUGIN_STAGING)).map((name) => join5(parent, name));
|
|
498
|
+
} catch {
|
|
499
|
+
return [];
|
|
500
|
+
}
|
|
501
|
+
};
|
|
205
502
|
var guardedDaemonRemoval = async () => {
|
|
206
503
|
if (dryRun) {
|
|
207
504
|
wouldNote("stop and uninstall", "Pushary background service");
|
|
@@ -211,14 +508,14 @@ var guardedDaemonRemoval = async () => {
|
|
|
211
508
|
};
|
|
212
509
|
var guardedInstructionBlockRemoval = (path) => {
|
|
213
510
|
if (!dryRun) return removeInstructionBlock(path);
|
|
214
|
-
if (!
|
|
215
|
-
const hasBlock =
|
|
511
|
+
if (!existsSync6(path)) return false;
|
|
512
|
+
const hasBlock = readFileSync4(path, "utf-8").includes("pushary");
|
|
216
513
|
if (hasBlock) wouldNote("rewrite", path);
|
|
217
514
|
return hasBlock;
|
|
218
515
|
};
|
|
219
516
|
var guardedAliasRemoval = () => {
|
|
220
517
|
if (!dryRun) return removeClaudeAlias();
|
|
221
|
-
const shells =
|
|
518
|
+
const shells = CANDIDATE_RCS.map((name) => join5(homedir5(), name)).filter((path) => existsSync6(path) && readFileSync4(path, "utf-8").includes("pushary claude"));
|
|
222
519
|
for (const path of shells) wouldNote("rewrite", path);
|
|
223
520
|
return shells;
|
|
224
521
|
};
|
|
@@ -226,15 +523,15 @@ var writeJson = (path, data) => {
|
|
|
226
523
|
guardedWrite(path, JSON.stringify(data, null, 2) + "\n");
|
|
227
524
|
};
|
|
228
525
|
var removeCursorUserHook = (scope) => {
|
|
229
|
-
if (!
|
|
230
|
-
let
|
|
526
|
+
if (!existsSync6(CURSOR_USER_HOOKS)) return "absent";
|
|
527
|
+
let parsed2;
|
|
231
528
|
try {
|
|
232
|
-
|
|
529
|
+
parsed2 = JSON.parse(readFileSync4(CURSOR_USER_HOOKS, "utf-8"));
|
|
233
530
|
} catch {
|
|
234
531
|
return "failed";
|
|
235
532
|
}
|
|
236
|
-
if (!
|
|
237
|
-
const hooks =
|
|
533
|
+
if (!parsed2 || typeof parsed2 !== "object") return "failed";
|
|
534
|
+
const hooks = parsed2.hooks ?? {};
|
|
238
535
|
let removedAny = false;
|
|
239
536
|
for (const event of CURSOR_GATE_EVENTS) {
|
|
240
537
|
const existing = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
@@ -248,15 +545,15 @@ var removeCursorUserHook = (scope) => {
|
|
|
248
545
|
else hooks[event] = others;
|
|
249
546
|
}
|
|
250
547
|
if (!removedAny) return "absent";
|
|
251
|
-
|
|
548
|
+
parsed2.hooks = hooks;
|
|
252
549
|
try {
|
|
253
|
-
writeJson(CURSOR_USER_HOOKS,
|
|
550
|
+
writeJson(CURSOR_USER_HOOKS, parsed2);
|
|
254
551
|
} catch {
|
|
255
552
|
return "failed";
|
|
256
553
|
}
|
|
257
554
|
if (dryRun) return "removed";
|
|
258
555
|
try {
|
|
259
|
-
const after = JSON.parse(
|
|
556
|
+
const after = JSON.parse(readFileSync4(CURSOR_USER_HOOKS, "utf-8"));
|
|
260
557
|
const afterHooks = after?.hooks ?? {};
|
|
261
558
|
const stillThere = CURSOR_GATE_EVENTS.some((event) => {
|
|
262
559
|
const entries = afterHooks[event];
|
|
@@ -296,15 +593,15 @@ var restoreTakeoverReceipts = () => {
|
|
|
296
593
|
["codex", codexHooksJson(), codexConfigToml(), "mcp_servers", "toml"],
|
|
297
594
|
["gemini_cli", geminiSettings(), geminiSettings(), "mcpServers", "json"],
|
|
298
595
|
["cursor", CURSOR_USER_HOOKS, null, null, null],
|
|
299
|
-
["vscode",
|
|
596
|
+
["vscode", join5(VSCODE_PLUGIN_DIR, "hooks", "hooks.json"), vscodePluginMcp(), "mcpServers", "json"]
|
|
300
597
|
];
|
|
301
598
|
for (const [agent, hookFile, mcpFile, mcpKey, mcpFormat] of targets) {
|
|
302
599
|
const receiptPath = hookTakeoverReceipt(agent);
|
|
303
|
-
if (!
|
|
600
|
+
if (!existsSync6(receiptPath)) continue;
|
|
304
601
|
try {
|
|
305
602
|
const receipt = readJson(receiptPath);
|
|
306
603
|
if (!isHookTakeoverReceipt(receipt) || receipt.agent !== agent) throw new Error("invalid receipt");
|
|
307
|
-
const document =
|
|
604
|
+
const document = existsSync6(hookFile) ? JSON.parse(readFileSync4(hookFile, "utf-8")) : {};
|
|
308
605
|
if (!document || typeof document !== "object" || Array.isArray(document)) throw new Error("invalid hooks file");
|
|
309
606
|
const appAlreadyOwns = hookOwnerOf(document) === "app";
|
|
310
607
|
const cliPositions = agent === "codex" ? codexHookPositions(document) : {};
|
|
@@ -315,7 +612,7 @@ var restoreTakeoverReceipts = () => {
|
|
|
315
612
|
if (mcpFile === hookFile) {
|
|
316
613
|
mcpDocument = document;
|
|
317
614
|
} else {
|
|
318
|
-
const raw =
|
|
615
|
+
const raw = existsSync6(mcpFile) ? readFileSync4(mcpFile, "utf-8") : "";
|
|
319
616
|
mcpDocument = raw.trim() ? mcpFormat === "toml" ? parseTOML(raw) : JSON.parse(raw) : {};
|
|
320
617
|
}
|
|
321
618
|
mcpChanged = restoreMcpServer(mcpDocument, mcpKey, receipt.mcpServer);
|
|
@@ -325,7 +622,7 @@ var restoreTakeoverReceipts = () => {
|
|
|
325
622
|
if (!appAlreadyOwns && agent === "codex") {
|
|
326
623
|
if (!mcpDocument) {
|
|
327
624
|
const configPath = codexConfigToml();
|
|
328
|
-
const raw =
|
|
625
|
+
const raw = existsSync6(configPath) ? readFileSync4(configPath, "utf-8") : "";
|
|
329
626
|
mcpDocument = raw.trim() ? parseTOML(raw) : {};
|
|
330
627
|
}
|
|
331
628
|
codexConfig = mcpDocument;
|
|
@@ -358,7 +655,7 @@ var main = async () => {
|
|
|
358
655
|
try {
|
|
359
656
|
rejectUnknownFlags(
|
|
360
657
|
process.argv.slice(2).filter((a) => a !== "clean"),
|
|
361
|
-
["--dry-run", "--yes", "-y", "--help", "-h"]
|
|
658
|
+
["--dry-run", "--everything", "--yes", "-y", "--help", "-h"]
|
|
362
659
|
);
|
|
363
660
|
} catch (err) {
|
|
364
661
|
process.stderr.write(` ! ${err instanceof Error ? err.message : String(err)}
|
|
@@ -366,6 +663,7 @@ var main = async () => {
|
|
|
366
663
|
process.exit(EXIT.USAGE);
|
|
367
664
|
}
|
|
368
665
|
dryRun = process.argv.includes("--dry-run");
|
|
666
|
+
const everything = process.argv.includes("--everything");
|
|
369
667
|
if (dryRun) {
|
|
370
668
|
console.log(` ${bold("Dry run")} ${dim("- nothing will be changed")}`);
|
|
371
669
|
console.log();
|
|
@@ -381,14 +679,31 @@ var main = async () => {
|
|
|
381
679
|
process.exit(0);
|
|
382
680
|
}
|
|
383
681
|
console.log();
|
|
384
|
-
const
|
|
385
|
-
const
|
|
682
|
+
const presence = macAppPresence();
|
|
683
|
+
const keepShared = presence === "present" && !everything;
|
|
684
|
+
const scope = removalScope(keepShared, keepShared);
|
|
386
685
|
if (keepShared) {
|
|
387
686
|
console.log(` ${dim("The Mac app is installed, so its wiring and the state it owns are left alone.")}`);
|
|
388
|
-
console.log();
|
|
687
|
+
console.log(` ${dim("To remove that too, re-run with")} --everything`);
|
|
688
|
+
} else if (presence === "gone") {
|
|
689
|
+
console.log(` ${dim("The Mac app is gone, so the wiring it left behind is removed too.")}`);
|
|
690
|
+
} else if (everything && presence === "present") {
|
|
691
|
+
console.log(` ${dim("Removing the Mac app's wiring and state as well.")}`);
|
|
389
692
|
}
|
|
693
|
+
if (everything) {
|
|
694
|
+
const quit = quitMacApp(guards, { platform: process.platform, dryRun, running: runningMacAppPIDs, waitForExit: waitForMacAppExit });
|
|
695
|
+
if (quit === "quit") {
|
|
696
|
+
console.log(` ${check} Pushary ${dim(did("quit"))} ${dim("(running, and its watcher puts back what this removes)")}`);
|
|
697
|
+
} else if (quit === "running") {
|
|
698
|
+
incompleteClean = true;
|
|
699
|
+
console.log(` ${cross} Pushary ${dim("would not quit, and it puts back what this removes")}`);
|
|
700
|
+
console.log(` ${dim("Quit it from the notch menu, then re-run.")}`);
|
|
701
|
+
process.exit(EXIT.FAILED);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (presence !== "absent") console.log();
|
|
390
705
|
const restoredByTakeover = new Set(
|
|
391
|
-
["claude_code", "codex", "gemini_cli"].filter((agent) =>
|
|
706
|
+
["claude_code", "codex", "gemini_cli"].filter((agent) => existsSync6(hookTakeoverReceipt(agent)))
|
|
392
707
|
);
|
|
393
708
|
if (keepShared) restoreTakeoverReceipts();
|
|
394
709
|
const sharedMcpKeys = {
|
|
@@ -414,12 +729,13 @@ var main = async () => {
|
|
|
414
729
|
const keepClaudeMcp = macAppKeeps("claude_code", mcpServerBearer(claudeJson2?.mcpServers));
|
|
415
730
|
const claudeHasPusharyEntry = () => Boolean(claudeJson2?.mcpServers?.pushary);
|
|
416
731
|
const claudeHadPusharyEntry = claudeHasPusharyEntry();
|
|
732
|
+
const rawReceipt = readJson(CLAUDE_MCP_RECEIPT);
|
|
733
|
+
const receipt = isClaudeMcpSnapshot(rawReceipt) ? rawReceipt : null;
|
|
734
|
+
const receiptUnreadable = existsSync6(CLAUDE_MCP_RECEIPT) && !receipt;
|
|
417
735
|
if (keepClaudeMcp) {
|
|
418
736
|
console.log(` ${skip} Claude Code MCP servers ${dim("(kept: the Mac app uses it)")}`);
|
|
419
737
|
} else if (claudeJson2) {
|
|
420
|
-
|
|
421
|
-
const receipt = isClaudeMcpSnapshot(rawReceipt) ? rawReceipt : null;
|
|
422
|
-
if (existsSync(CLAUDE_MCP_RECEIPT) && !receipt) {
|
|
738
|
+
if (receiptUnreadable) {
|
|
423
739
|
if (!keepShared) incompleteClean = true;
|
|
424
740
|
console.log(` ${skip} Claude Code MCP servers ${dim("(setup receipt is unreadable; left unchanged)")}`);
|
|
425
741
|
} else if (receipt && restoreClaudeMcpServer(claudeJson2, receipt) || !receipt && removeClaudeMcpServers(claudeJson2)) {
|
|
@@ -430,13 +746,20 @@ var main = async () => {
|
|
|
430
746
|
if (receipt && !keepShared) incompleteClean = true;
|
|
431
747
|
console.log(` ${skip} Claude Code MCP servers ${dim(receipt ? "(changed after setup; left unchanged)" : "(no pushary entries)")}`);
|
|
432
748
|
}
|
|
433
|
-
} else if (
|
|
749
|
+
} else if (existsSync6(CLAUDE_MCP_RECEIPT)) {
|
|
434
750
|
incompleteClean = true;
|
|
435
751
|
console.log(` ${skip} Claude Code MCP servers ${dim("(config missing; setup receipt kept for retry)")}`);
|
|
436
752
|
} else {
|
|
437
753
|
console.log(` ${skip} Claude Code MCP servers ${dim("(not found)")}`);
|
|
438
754
|
}
|
|
439
|
-
|
|
755
|
+
if (!keepClaudeMcp && !receiptUnreadable) {
|
|
756
|
+
const rewrites = claudeBackupRewrites(receipt);
|
|
757
|
+
for (const rewrite of rewrites) guardedWrite(rewrite.path, rewrite.contents);
|
|
758
|
+
if (rewrites.length > 0) {
|
|
759
|
+
console.log(` ${check} Claude Code backups ${dim(did("cleaned"))} ${dim(`(the Pushary entry and its key, in ${rewrites.length})`)}`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
const claudeScope = removalScope(keepShared && !(claudeHadPusharyEntry && !claudeHasPusharyEntry()), keepShared);
|
|
440
763
|
cleanSettingsFile(CLAUDE_SETTINGS, "Claude Code settings", claudeScope);
|
|
441
764
|
cleanSettingsFile(CLAUDE_SETTINGS_LOCAL, "Claude Code settings.local", claudeScope);
|
|
442
765
|
const cursorData = keepShared ? null : readJson(CURSOR_MCP);
|
|
@@ -467,22 +790,29 @@ var main = async () => {
|
|
|
467
790
|
}
|
|
468
791
|
if (cursorGate === "failed") {
|
|
469
792
|
console.log(` ${skip} Cursor plugin ${dim("(kept: removing it now would block matched commands)")}`);
|
|
470
|
-
} else if (
|
|
793
|
+
} else if (existsSync6(CURSOR_PLUGIN_DIR)) {
|
|
471
794
|
guardedRemove(CURSOR_PLUGIN_DIR);
|
|
472
795
|
console.log(` ${check} Cursor plugin ${dim("(removed from ~/.cursor/plugins/local)")}`);
|
|
473
796
|
} else {
|
|
474
797
|
console.log(` ${skip} Cursor plugin ${dim("(not installed)")}`);
|
|
475
798
|
}
|
|
799
|
+
if (cursorGate !== "failed") {
|
|
800
|
+
const stale = cursorPluginLeftovers();
|
|
801
|
+
for (const path of stale) guardedRemove(path);
|
|
802
|
+
if (stale.length > 0) {
|
|
803
|
+
console.log(` ${check} Cursor plugin copies ${dim(did("removed"))} ${dim(`(${stale.length}, each holding the key of an earlier install)`)}`);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
476
806
|
if (keepShared) {
|
|
477
807
|
console.log(` ${skip} VS Code plugin ${dim("(kept: the Mac app writes the same plugin directory)")}`);
|
|
478
808
|
}
|
|
479
|
-
for (const settingsPath of keepShared ? [] : vscodeSettingsTargets(
|
|
809
|
+
for (const settingsPath of keepShared ? [] : vscodeSettingsTargets(existsSync6)) {
|
|
480
810
|
const label = `VS Code plugin ${dim(`(${shortenHome(settingsPath)})`)}`;
|
|
481
|
-
if (!
|
|
811
|
+
if (!existsSync6(settingsPath)) {
|
|
482
812
|
console.log(` ${skip} ${label} ${dim("(no settings.json)")}`);
|
|
483
813
|
continue;
|
|
484
814
|
}
|
|
485
|
-
const result = unregisterPluginLocation(
|
|
815
|
+
const result = unregisterPluginLocation(readFileSync4(settingsPath, "utf-8"), VSCODE_PLUGIN_DIR);
|
|
486
816
|
if (result.kind === "removed") {
|
|
487
817
|
guardedWrite(settingsPath, result.content);
|
|
488
818
|
console.log(` ${check} ${label} ${dim(did("unregistered"))}`);
|
|
@@ -492,13 +822,26 @@ var main = async () => {
|
|
|
492
822
|
console.log(` ${skip} ${label} ${dim("(no pushary entry)")}`);
|
|
493
823
|
}
|
|
494
824
|
}
|
|
495
|
-
if (!keepShared &&
|
|
825
|
+
if (!keepShared && existsSync6(VSCODE_PLUGIN_DIR)) {
|
|
496
826
|
guardedRemove(VSCODE_PLUGIN_DIR);
|
|
497
827
|
console.log(` ${check} VS Code plugin files ${dim(did("removed"))}`);
|
|
498
828
|
}
|
|
829
|
+
if (keepShared) {
|
|
830
|
+
console.log(` ${skip} Editor extension ${dim("(kept: installed and updated by the Mac app)")}`);
|
|
831
|
+
} else {
|
|
832
|
+
const removals = editorExtensionRemovals();
|
|
833
|
+
for (const removal of removals) {
|
|
834
|
+
if (removal.record) guardedWrite(removal.record.path, removal.record.contents);
|
|
835
|
+
for (const folder of removal.folders) guardedRemove(folder);
|
|
836
|
+
console.log(` ${check} Editor extension ${dim(`(${did("removed")} from ${shortenHome(removal.directory)})`)}`);
|
|
837
|
+
}
|
|
838
|
+
if (removals.length === 0) {
|
|
839
|
+
console.log(` ${skip} Editor extension ${dim("(not installed in Cursor or VS Code)")}`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
499
842
|
if (keepShared) {
|
|
500
843
|
console.log(` ${skip} Skill directory ${dim("(kept: the Mac app uses it)")}`);
|
|
501
|
-
} else if (
|
|
844
|
+
} else if (existsSync6(SKILL_DIR)) {
|
|
502
845
|
guardedRemove(SKILL_DIR);
|
|
503
846
|
console.log(` ${check} Skill directory ${dim(did("removed"))}`);
|
|
504
847
|
} else {
|
|
@@ -509,26 +852,26 @@ var main = async () => {
|
|
|
509
852
|
const codexHooksData = readJson(codexHooksJson2);
|
|
510
853
|
const codexPositions = codexHookPositions(codexHooksData ?? void 0);
|
|
511
854
|
try {
|
|
512
|
-
const config =
|
|
513
|
-
const
|
|
514
|
-
const trustRemoved = hookOwnerOf(codexHooksData) === "app" ? false : removeCodexHookTrust(
|
|
855
|
+
const config = readFileSync4(codexConfig, "utf-8");
|
|
856
|
+
const parsed2 = parseTOML(config);
|
|
857
|
+
const trustRemoved = hookOwnerOf(codexHooksData) === "app" && keepShared ? false : removeCodexHookTrust(parsed2, codexHooksJson2, codexPositions);
|
|
515
858
|
const hadPushary = config.includes("pushary");
|
|
516
859
|
if (hadPushary || trustRemoved) {
|
|
517
|
-
const macAppKeepsCodexMcp = macAppKeeps("codex", codexMcpKey(
|
|
860
|
+
const macAppKeepsCodexMcp = macAppKeeps("codex", codexMcpKey(parsed2));
|
|
518
861
|
if (!macAppKeepsCodexMcp) {
|
|
519
|
-
const mcpServers =
|
|
862
|
+
const mcpServers = parsed2.mcp_servers ?? {};
|
|
520
863
|
delete mcpServers.pushary;
|
|
521
|
-
if (Object.keys(mcpServers).length === 0) delete
|
|
522
|
-
else
|
|
864
|
+
if (Object.keys(mcpServers).length === 0) delete parsed2.mcp_servers;
|
|
865
|
+
else parsed2.mcp_servers = mcpServers;
|
|
523
866
|
}
|
|
524
|
-
if (Array.isArray(
|
|
525
|
-
|
|
526
|
-
if (
|
|
867
|
+
if (Array.isArray(parsed2.notify)) {
|
|
868
|
+
parsed2.notify = parsed2.notify.filter((n) => typeof n !== "string" || !n.includes("pushary-codex"));
|
|
869
|
+
if (parsed2.notify.length === 0) delete parsed2.notify;
|
|
527
870
|
}
|
|
528
|
-
guardedWrite(codexConfig, stringifyTOML(
|
|
871
|
+
guardedWrite(codexConfig, stringifyTOML(parsed2));
|
|
529
872
|
const what = trustRemoved ? "cleaned, hook trust removed" : "cleaned";
|
|
530
873
|
console.log(` ${check} Codex config ${dim(did(what))}`);
|
|
531
|
-
if (macAppKeepsCodexMcp &&
|
|
874
|
+
if (macAppKeepsCodexMcp && parsed2.mcp_servers?.pushary) {
|
|
532
875
|
console.log(` ${skip} Codex MCP server ${dim("(kept: the Mac app uses it)")}`);
|
|
533
876
|
}
|
|
534
877
|
} else {
|
|
@@ -558,7 +901,7 @@ var main = async () => {
|
|
|
558
901
|
const codexSkillDir2 = codexSkillDir();
|
|
559
902
|
if (keepShared) {
|
|
560
903
|
console.log(` ${skip} Codex skill directory ${dim("(kept: the Mac app uses it)")}`);
|
|
561
|
-
} else if (
|
|
904
|
+
} else if (existsSync6(codexSkillDir2)) {
|
|
562
905
|
guardedRemove(codexSkillDir2);
|
|
563
906
|
console.log(` ${check} Codex skill directory ${dim(did("removed"))}`);
|
|
564
907
|
} else {
|
|
@@ -596,7 +939,7 @@ var main = async () => {
|
|
|
596
939
|
const geminiSettingsPath = geminiSettings();
|
|
597
940
|
const geminiSettings2 = readJson(geminiSettingsPath);
|
|
598
941
|
if (geminiSettings2) {
|
|
599
|
-
if (removeGeminiSettings(geminiSettings2, removalScope(macAppKeeps("gemini_cli", mcpServerBearer(geminiSettings2.mcpServers))))) {
|
|
942
|
+
if (removeGeminiSettings(geminiSettings2, removalScope(macAppKeeps("gemini_cli", mcpServerBearer(geminiSettings2.mcpServers)), keepShared))) {
|
|
600
943
|
writeJson(geminiSettingsPath, geminiSettings2);
|
|
601
944
|
console.log(` ${check} Gemini CLI settings ${dim(did("cleaned"))}`);
|
|
602
945
|
} else {
|
|
@@ -614,25 +957,25 @@ var main = async () => {
|
|
|
614
957
|
console.log(` ${skip} Gemini GEMINI.md ${dim("(no pushary block)")}`);
|
|
615
958
|
}
|
|
616
959
|
const openCodePlugin = openCodePluginFile();
|
|
617
|
-
const openCodePluginContents =
|
|
960
|
+
const openCodePluginContents = existsSync6(openCodePlugin) ? readFileSync4(openCodePlugin, "utf-8") : null;
|
|
618
961
|
if (openCodePluginContents === null) {
|
|
619
962
|
console.log(` ${skip} OpenCode plugin ${dim("(not installed)")}`);
|
|
620
963
|
} else if (!isOpenCodePluginOurs(openCodePluginContents)) {
|
|
621
964
|
console.log(` ${skip} OpenCode plugin ${dim("(a pushary.js we did not write)")}`);
|
|
622
|
-
} else if (openCodePluginContents.includes(HOOK_LOCATOR_BINARY)) {
|
|
965
|
+
} else if (openCodePluginContents.includes(HOOK_LOCATOR_BINARY) && keepShared) {
|
|
623
966
|
console.log(` ${skip} OpenCode plugin ${dim("(kept: written by the Mac app)")}`);
|
|
624
967
|
} else {
|
|
625
968
|
guardedRemove(openCodePlugin, { force: true });
|
|
626
969
|
console.log(` ${check} OpenCode plugin ${dim(did("removed"))}`);
|
|
627
|
-
const sibling =
|
|
970
|
+
const sibling = join5(openCodePluginDir(), OPENCODE_PLUGIN_SIBLING);
|
|
628
971
|
const remaining = (() => {
|
|
629
972
|
try {
|
|
630
|
-
return
|
|
973
|
+
return readdirSync3(openCodePluginDir()).filter((name) => name !== OPENCODE_PLUGIN_SIBLING);
|
|
631
974
|
} catch {
|
|
632
975
|
return [];
|
|
633
976
|
}
|
|
634
977
|
})();
|
|
635
|
-
const siblingIsOurs =
|
|
978
|
+
const siblingIsOurs = existsSync6(sibling) && readFileSync4(sibling, "utf-8") === OPENCODE_PLUGIN_SIBLING_CONTENTS;
|
|
636
979
|
if (siblingIsOurs && !hasForeignPlugins(remaining)) {
|
|
637
980
|
guardedRemove(sibling, { force: true });
|
|
638
981
|
console.log(` ${check} OpenCode plugin sibling ${dim(did("removed"))}`);
|
|
@@ -660,14 +1003,14 @@ var main = async () => {
|
|
|
660
1003
|
} else {
|
|
661
1004
|
console.log(` ${check} Hermes config ${dim("(setup-owned changes restored)")}`);
|
|
662
1005
|
try {
|
|
663
|
-
|
|
1006
|
+
guards.execFile(hermesPython, ["-m", "pip", "uninstall", "-y", HERMES_PIP_PACKAGE], { stdio: "pipe", timeout: 6e4 }, `pip uninstall ${HERMES_PIP_PACKAGE}`);
|
|
664
1007
|
console.log(` ${check} Hermes plugin ${dim("(pip uninstalled)")}`);
|
|
665
1008
|
} catch {
|
|
666
1009
|
incompleteClean = true;
|
|
667
1010
|
console.log(` ${skip} Hermes plugin ${dim("(could not uninstall; setup receipt kept for retry)")}`);
|
|
668
1011
|
}
|
|
669
1012
|
}
|
|
670
|
-
} else if (
|
|
1013
|
+
} else if (existsSync6(HERMES_CONFIG_RECEIPT)) {
|
|
671
1014
|
incompleteClean = true;
|
|
672
1015
|
console.log(` ${skip} Hermes ${dim("(runtime not found; setup receipt kept for retry)")}`);
|
|
673
1016
|
} else {
|
|
@@ -675,20 +1018,31 @@ var main = async () => {
|
|
|
675
1018
|
}
|
|
676
1019
|
for (const shellFile of SHELL_FILES) {
|
|
677
1020
|
try {
|
|
678
|
-
const content =
|
|
1021
|
+
const content = readFileSync4(shellFile, "utf-8");
|
|
679
1022
|
if (content.includes("PUSHARY_API_KEY")) {
|
|
680
|
-
const isOurExport = (line) => /^\s*export\s+PUSHARY_API_KEY=['"]?pk_[A-Za-z0-9]+\.[A-Za-z0-9]+['"]?\s*$/.test(line);
|
|
1023
|
+
const isOurExport = (line) => /^\s*export\s+PUSHARY_API_KEY=['"]?pk_[A-Za-z0-9]+\.[A-Za-z0-9]+['"]?\s*$/.test(line) || isDisabledExport(line);
|
|
681
1024
|
const lines = content.split("\n");
|
|
682
1025
|
const declined = lines.filter((l) => l.includes("PUSHARY_API_KEY") && !isOurExport(l));
|
|
683
1026
|
const cleaned = lines.filter((l) => !isOurExport(l)).join("\n");
|
|
1027
|
+
const name = shellFile.split("/").pop();
|
|
684
1028
|
if (cleaned !== content) {
|
|
685
1029
|
const backup = `${shellFile}.pushary-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
686
1030
|
if (!dryRun) copyFileSync(shellFile, backup);
|
|
687
1031
|
guardedWrite(shellFile, cleaned);
|
|
688
|
-
|
|
1032
|
+
const confirmed = !dryRun && readFileSync4(shellFile, "utf-8") === cleaned;
|
|
1033
|
+
if (confirmed) guardedRemove(backup, { force: true });
|
|
1034
|
+
const kept = confirmed ? "" : `, backup at ${backup}`;
|
|
1035
|
+
console.log(` ${check} ${name} ${dim(`(removed the export${kept})`)}`);
|
|
689
1036
|
}
|
|
690
1037
|
for (const line of declined) {
|
|
691
|
-
|
|
1038
|
+
if ((line.match(/pk_[A-Za-z0-9_]+\.[A-Za-z0-9_]+/g) ?? []).some(isValidApiKey)) {
|
|
1039
|
+
incompleteClean = true;
|
|
1040
|
+
console.log(` ${cross} ${name} ${dim("still sets PUSHARY_API_KEY, in a form this command will not rewrite:")}`);
|
|
1041
|
+
console.log(` ${dim(redactSecrets(line.trim()))}`);
|
|
1042
|
+
console.log(` ${dim("Remove it yourself. A setup run would sign this machine in as that account.")}`);
|
|
1043
|
+
} else {
|
|
1044
|
+
console.log(` ${skip} ${name} ${dim(`left a line referencing PUSHARY_API_KEY: ${redactSecrets(line.trim())}`)}`);
|
|
1045
|
+
}
|
|
692
1046
|
}
|
|
693
1047
|
}
|
|
694
1048
|
} catch {
|
|
@@ -700,15 +1054,15 @@ var main = async () => {
|
|
|
700
1054
|
} else {
|
|
701
1055
|
console.log(` ${skip} claude alias ${dim("(not set)")}`);
|
|
702
1056
|
}
|
|
703
|
-
if (
|
|
1057
|
+
if (existsSync6(PUSHARY_DIR)) {
|
|
704
1058
|
let removed = 0;
|
|
705
1059
|
const kept = [];
|
|
706
|
-
for (const entry of
|
|
1060
|
+
for (const entry of readdirSync3(PUSHARY_DIR)) {
|
|
707
1061
|
if (keepShared && isAppOwned(entry) || entry === "setup" && (keepShared || incompleteClean)) {
|
|
708
1062
|
kept.push(entry);
|
|
709
1063
|
continue;
|
|
710
1064
|
}
|
|
711
|
-
guardedRemove(
|
|
1065
|
+
guardedRemove(join5(PUSHARY_DIR, entry), { force: true });
|
|
712
1066
|
removed++;
|
|
713
1067
|
}
|
|
714
1068
|
if (removed > 0) {
|
|
@@ -725,10 +1079,10 @@ var main = async () => {
|
|
|
725
1079
|
}
|
|
726
1080
|
try {
|
|
727
1081
|
let swept = 0;
|
|
728
|
-
for (const f of
|
|
1082
|
+
for (const f of readdirSync3(tmpdir())) {
|
|
729
1083
|
if (!f.startsWith("pushary-")) continue;
|
|
730
1084
|
try {
|
|
731
|
-
guardedRemove(
|
|
1085
|
+
guardedRemove(join5(tmpdir(), f), { force: true });
|
|
732
1086
|
swept++;
|
|
733
1087
|
} catch {
|
|
734
1088
|
}
|
|
@@ -736,6 +1090,28 @@ var main = async () => {
|
|
|
736
1090
|
if (swept > 0) console.log(` ${check} Temp state ${dim(`(${swept} pushary entries swept)`)}`);
|
|
737
1091
|
} catch {
|
|
738
1092
|
}
|
|
1093
|
+
if (everything) {
|
|
1094
|
+
const store = skillsStoreRemoval();
|
|
1095
|
+
if (store.skillDir) guardedRemove(store.skillDir);
|
|
1096
|
+
if (store.lock) guardedWrite(store.lock.path, store.lock.contents);
|
|
1097
|
+
if (store.skillDir || store.lock) {
|
|
1098
|
+
console.log(` ${check} skills.sh registration ${dim(did("removed"))} ${dim("(~/.agents)")}`);
|
|
1099
|
+
}
|
|
1100
|
+
const leftovers = process.platform === "darwin" ? removeDeveloperLeftovers(guards, leakedTestDefaultsDomains(), registeredPusharyBundles()) : 0;
|
|
1101
|
+
if (leftovers > 0) {
|
|
1102
|
+
console.log(` ${check} Developer leftovers ${dim(did("removed"))} ${dim(`(${leftovers} leaked test preferences and stale app registrations)`)}`);
|
|
1103
|
+
}
|
|
1104
|
+
const { removed, failed } = process.platform === "darwin" ? removeMacAppState(guards, storedPusharyKeychainServices, homedir5()) : { removed: 0, failed: 0 };
|
|
1105
|
+
if (failed > 0) {
|
|
1106
|
+
incompleteClean = true;
|
|
1107
|
+
console.log(` ${cross} Mac keychain ${dim(`(${failed} operations failed; unlock your keychain and re-run)`)}`);
|
|
1108
|
+
}
|
|
1109
|
+
if (removed > 0) {
|
|
1110
|
+
console.log(` ${check} Mac app state ${dim(did("removed"))} ${dim(`(${removed} keychain items, preferences and caches)`)}`);
|
|
1111
|
+
} else if (failed === 0) {
|
|
1112
|
+
console.log(` ${skip} Mac app state ${dim("(nothing of ours in the keychain, preferences or caches)")}`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
739
1115
|
try {
|
|
740
1116
|
if (dryRun) {
|
|
741
1117
|
wouldNote("run ", "npm uninstall -g @pushary/agent-hooks");
|
|
@@ -750,13 +1126,32 @@ var main = async () => {
|
|
|
750
1126
|
if (incompleteClean) {
|
|
751
1127
|
console.log(` ${yellow(bold("Clean finished with something left behind."))}`);
|
|
752
1128
|
console.log(` ${dim("See the \u2717 above. Fix it, then re-run")} npx @pushary/agent-hooks@latest clean`);
|
|
1129
|
+
console.log(` ${dim("Still stuck? Send this output to")} ${SUPPORT_EMAIL} ${dim("or")} ${SUPPORT_DISCORD}`);
|
|
1130
|
+
} else if (everything) {
|
|
1131
|
+
console.log(` ${green(bold("Clean complete. Nothing of ours is left in any agent config."))}`);
|
|
1132
|
+
const bundle = installedPusharyBundles().find(existsSync6);
|
|
1133
|
+
if (bundle) {
|
|
1134
|
+
console.log(` ${dim("The app itself is still installed. Drag")} ${bundle} ${dim("to the Trash to finish.")}`);
|
|
1135
|
+
}
|
|
1136
|
+
console.log(` ${dim("Signing in as someone else? Sign out at pushary.com in your browser first.")}`);
|
|
1137
|
+
console.log(` ${dim("The app and")} pushary login ${dim("both sign in through it, and would pick the old account back up.")}`);
|
|
753
1138
|
} else {
|
|
754
1139
|
console.log(` ${green(bold("Clean complete."))}`);
|
|
1140
|
+
if (keepShared) {
|
|
1141
|
+
console.log(` ${dim("The Mac app's wiring and its stored key are still here. Re-run with")} --everything ${dim("to remove them.")}`);
|
|
1142
|
+
}
|
|
755
1143
|
}
|
|
756
1144
|
console.log(` ${dim("Run")} npx @pushary/agent-hooks@latest setup ${dim("to reinstall.")}`);
|
|
757
|
-
|
|
1145
|
+
const exportedKey = process.env.PUSHARY_API_KEY?.trim();
|
|
1146
|
+
if (aliasRemovedFrom.length > 0 || exportedKey) {
|
|
758
1147
|
console.log();
|
|
759
|
-
console.log(` ${yellow("!")} ${bold("
|
|
1148
|
+
console.log(` ${yellow("!")} ${bold("In your current terminal")}`);
|
|
1149
|
+
}
|
|
1150
|
+
if (exportedKey) {
|
|
1151
|
+
console.log(` ${dim("PUSHARY_API_KEY is still exported here. Run")} ${bold("unset PUSHARY_API_KEY")}`);
|
|
1152
|
+
console.log(` ${dim("(or open a new terminal) before setup, or it signs back in as the old account.")}`);
|
|
1153
|
+
}
|
|
1154
|
+
if (aliasRemovedFrom.length > 0) {
|
|
760
1155
|
console.log(` ${dim("It still has the")} ${bold("claude")} ${dim("alias loaded from before. Run")} ${bold("unalias claude")}`);
|
|
761
1156
|
console.log(` ${dim("(or open a new terminal) so")} ${bold("claude")} ${dim("runs Claude Code again.")}`);
|
|
762
1157
|
}
|