@pushary/agent-hooks 1.3.0 → 1.4.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/CHANGELOG.md +24 -0
- package/README.md +1 -1
- package/data/SKILL.md +3 -3
- package/data/cursor-plugin/skills/pushary/SKILL.md +2 -2
- package/data/vscode-plugin/skills/pushary/SKILL.md +2 -2
- package/dist/bin/pushary-bell-hook.js +20 -20
- package/dist/bin/pushary-bell.js +9 -9
- package/dist/bin/pushary-claude.js +20 -20
- package/dist/bin/pushary-clean.js +472 -94
- package/dist/bin/pushary-codex-bridge.js +11 -11
- package/dist/bin/pushary-codex-hook.js +30 -28
- package/dist/bin/pushary-codex.js +7 -7
- package/dist/bin/pushary-connect.js +9 -9
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +26 -26
- package/dist/bin/pushary-disconnect.js +13 -13
- package/dist/bin/pushary-doctor.js +86 -24
- package/dist/bin/pushary-elicitation-hook.js +19 -19
- package/dist/bin/pushary-gemini-bridge.js +10 -10
- package/dist/bin/pushary-gemini-hook.js +21 -21
- package/dist/bin/pushary-hook.js +34 -44
- package/dist/bin/pushary-login.js +10 -10
- package/dist/bin/pushary-logout.js +7 -7
- package/dist/bin/pushary-mode.js +5 -5
- package/dist/bin/pushary-notification-hook.js +22 -22
- package/dist/bin/pushary-opencode-hook.js +21 -21
- package/dist/bin/pushary-permission-denied-hook.js +26 -26
- package/dist/bin/pushary-permission-hook.js +32 -31
- package/dist/bin/pushary-post-hook.js +22 -22
- package/dist/bin/pushary-prompt-hook.js +22 -22
- package/dist/bin/pushary-session-end-hook.js +22 -22
- package/dist/bin/pushary-session-start-hook.js +24 -24
- package/dist/bin/pushary-setup.js +39 -60
- package/dist/bin/pushary-stats.js +4 -4
- package/dist/bin/pushary-status.js +13 -13
- package/dist/bin/pushary-stop-hook.js +22 -22
- package/dist/bin/pushary-stopfailure-hook.js +22 -22
- package/dist/bin/pushary-suggestions.js +2 -2
- package/dist/bin/pushary-transcript-register.js +18 -18
- package/dist/bin/pushary-transcripts.js +8 -8
- package/dist/bin/pushary-upgrade.js +17 -17
- package/dist/bin/pushary-wait.js +5 -5
- package/dist/bin/pushary.js +1 -1
- package/dist/{chunk-3AIFWTVB.js → chunk-2TOTCUJC.js} +2 -2
- package/dist/{chunk-X3KIGNIP.js → chunk-43Z3RLS5.js} +83 -24
- package/dist/{chunk-YF4JDCU4.js → chunk-5F5QQMCA.js} +1 -1
- package/dist/{chunk-ZHL62DRA.js → chunk-5IBVIMWT.js} +3 -3
- package/dist/{chunk-BYGEZI7B.js → chunk-5MSBI6NP.js} +1 -1
- package/dist/{chunk-WPJS3OZH.js → chunk-7IEB2GV4.js} +100 -9
- package/dist/{chunk-2PLEJ7SY.js → chunk-7PQZYCEP.js} +1 -1
- package/dist/{chunk-OE6U2AXM.js → chunk-A7XIL2WD.js} +3 -3
- package/dist/{chunk-A2URMNFM.js → chunk-BYAXXBNO.js} +1 -1
- package/dist/{chunk-DON24PNT.js → chunk-D4TNSUDY.js} +1 -1
- package/dist/{chunk-EU6JH7KU.js → chunk-ESLLVHDY.js} +8 -8
- package/dist/{chunk-LZQVAEWP.js → chunk-FAH3D74E.js} +2 -1
- package/dist/{chunk-MU275U5A.js → chunk-GIXLZFEY.js} +2 -2
- package/dist/{chunk-4V24U44H.js → chunk-GLPWCQ6V.js} +2 -2
- package/dist/{chunk-BL4SQNO6.js → chunk-GS5EI2YL.js} +1 -1
- package/dist/{chunk-EKQAKSGI.js → chunk-H25UB3CG.js} +1 -1
- package/dist/{chunk-BNDHXPTQ.js → chunk-HGN6VZTY.js} +20 -13
- package/dist/{chunk-WXXDHGTZ.js → chunk-IF46POXX.js} +5 -4
- package/dist/{chunk-A5ZWYQFV.js → chunk-JCWCTBYH.js} +4 -4
- package/dist/{chunk-IAEVVUXO.js → chunk-KZIXHLPQ.js} +1 -1
- package/dist/{chunk-FGVNU7XD.js → chunk-NBCTKC4Z.js} +34 -3
- package/dist/{chunk-XBO42HKH.js → chunk-NYHNLMQO.js} +12 -3
- package/dist/{chunk-K4TAUYUJ.js → chunk-O5L2OJBB.js} +3 -3
- package/dist/{chunk-DVO4SZMJ.js → chunk-O6RN46OS.js} +3 -3
- package/dist/{chunk-POAFXBNH.js → chunk-P4JVVJ2D.js} +1 -1
- package/dist/{chunk-E7WBNGJS.js → chunk-Q4UAMWXK.js} +1 -1
- package/dist/{chunk-KS4P6F3R.js → chunk-RGZY77CD.js} +4 -4
- package/dist/{chunk-K7UIAE7I.js → chunk-RQM5SMMR.js} +2 -2
- package/dist/{chunk-UVXNYYHG.js → chunk-S3CLZSV3.js} +6 -6
- package/dist/{chunk-B3S4SF3D.js → chunk-S3EUZZJB.js} +1 -1
- package/dist/{chunk-XBJW4MV5.js → chunk-SGZ3IAWF.js} +1 -1
- package/dist/{chunk-YKLCWWEI.js → chunk-SGZJ5LYH.js} +1 -1
- package/dist/{chunk-4LL6LG5X.js → chunk-T7E75EKA.js} +4 -3
- package/dist/{chunk-GOQHN2UH.js → chunk-TWWLVOPB.js} +1 -1
- package/dist/{chunk-RVDHN2TJ.js → chunk-U4FB623J.js} +2 -2
- package/dist/{chunk-RHLUJE4H.js → chunk-UCGQEAKZ.js} +1 -1
- package/dist/{chunk-KPNGPRYU.js → chunk-UIN3RD2U.js} +4 -4
- package/dist/{chunk-XEXOHUJ6.js → chunk-UTYTPDWN.js} +4 -4
- package/dist/{chunk-EJZCV5G4.js → chunk-VENQSVKA.js} +1 -1
- package/dist/{chunk-EYMMU5HC.js → chunk-XJWMPQ6X.js} +1 -1
- package/dist/{chunk-JYEGOYQC.js → chunk-YYRZPEMA.js} +1 -1
- package/dist/{chunk-N43EQPJK.js → chunk-ZIGGEKZW.js} +41 -7
- package/dist/{reapply-FUCAKR6Z.js → reapply-B3S2LRMT.js} +9 -9
- package/dist/src/index.d.ts +20 -17
- package/dist/src/index.js +13 -13
- package/package.json +1 -1
- /package/dist/{chunk-KBITFQST.js → chunk-P32LPT2L.js} +0 -0
|
@@ -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-
|
|
9
|
-
import "../chunk-
|
|
10
|
+
} from "../chunk-NBCTKC4Z.js";
|
|
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-O5L2OJBB.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-Q4UAMWXK.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-HGN6VZTY.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-5IBVIMWT.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-UTYTPDWN.js";
|
|
49
|
+
import "../chunk-5MSBI6NP.js";
|
|
50
|
+
import {
|
|
51
|
+
liveBridgeProbe,
|
|
52
|
+
resolveBridgeStub
|
|
53
|
+
} from "../chunk-YYRZPEMA.js";
|
|
49
54
|
import {
|
|
50
55
|
pusharyHookExecutable
|
|
51
|
-
} from "../chunk-
|
|
56
|
+
} from "../chunk-O6RN46OS.js";
|
|
52
57
|
import {
|
|
53
58
|
disableDaemonService
|
|
54
|
-
} from "../chunk-
|
|
59
|
+
} from "../chunk-RGZY77CD.js";
|
|
55
60
|
import {
|
|
56
61
|
removeGeminiSettings
|
|
57
|
-
} from "../chunk-
|
|
58
|
-
import "../chunk-
|
|
62
|
+
} from "../chunk-A7XIL2WD.js";
|
|
63
|
+
import "../chunk-7PQZYCEP.js";
|
|
59
64
|
import "../chunk-XHKBHWLX.js";
|
|
60
|
-
import "../chunk-
|
|
61
|
-
import "../chunk-
|
|
65
|
+
import "../chunk-IF46POXX.js";
|
|
66
|
+
import "../chunk-KZIXHLPQ.js";
|
|
62
67
|
import "../chunk-LGXUGYUH.js";
|
|
63
|
-
import "../chunk-
|
|
68
|
+
import "../chunk-RQM5SMMR.js";
|
|
64
69
|
import {
|
|
65
70
|
bridgeAgentKey,
|
|
66
71
|
claudeJson,
|
|
@@ -89,41 +94,244 @@ import {
|
|
|
89
94
|
restoreCodexHookTrust,
|
|
90
95
|
vscodePluginDir,
|
|
91
96
|
vscodePluginMcp
|
|
92
|
-
} from "../chunk-
|
|
97
|
+
} from "../chunk-UCGQEAKZ.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-NYHNLMQO.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-
|
|
111
|
-
import "../chunk-
|
|
117
|
+
import "../chunk-P4JVVJ2D.js";
|
|
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,
|
|
124
|
+
SUPPORT_DISCORD,
|
|
125
|
+
SUPPORT_EMAIL,
|
|
117
126
|
isValidApiKey
|
|
118
|
-
} from "../chunk-
|
|
127
|
+
} from "../chunk-7IEB2GV4.js";
|
|
119
128
|
|
|
120
129
|
// bin/pushary-clean.ts
|
|
121
|
-
import { existsSync, readFileSync, copyFileSync, readdirSync } from "fs";
|
|
122
|
-
import { join } from "path";
|
|
123
|
-
import { homedir, tmpdir } from "os";
|
|
130
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, copyFileSync, readdirSync as readdirSync3 } from "fs";
|
|
131
|
+
import { dirname, join as join5 } from "path";
|
|
132
|
+
import { homedir as homedir5, tmpdir } from "os";
|
|
124
133
|
import { confirm } from "@inquirer/prompts";
|
|
125
134
|
import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
|
|
126
135
|
|
|
136
|
+
// src/cli/mac-leftovers.ts
|
|
137
|
+
import { execSync } from "child_process";
|
|
138
|
+
import { existsSync } from "fs";
|
|
139
|
+
import { homedir } from "os";
|
|
140
|
+
import { join } from "path";
|
|
141
|
+
var MAC_APP_BUNDLE = "/Applications/Pushary.app";
|
|
142
|
+
var installedPusharyBundles = (home = homedir()) => [MAC_APP_BUNDLE, join(home, "Applications", "Pushary.app")];
|
|
143
|
+
var LSREGISTER = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
|
|
144
|
+
var TEST_DEFAULTS_PREFIXES = [
|
|
145
|
+
"com.pushary.mac.tests.",
|
|
146
|
+
"com.pushary.tests.",
|
|
147
|
+
"pushary.tests.",
|
|
148
|
+
"pushary.quiet-mode-test.",
|
|
149
|
+
"pushary-optin-"
|
|
150
|
+
];
|
|
151
|
+
var isTestDefaultsDomain = (domain) => TEST_DEFAULTS_PREFIXES.some((prefix) => domain.startsWith(prefix));
|
|
152
|
+
var leakedTestDefaultsDomains = () => {
|
|
153
|
+
try {
|
|
154
|
+
return execSync("defaults domains", { encoding: "utf-8", timeout: 2e4 }).split(",").map((entry) => entry.trim()).filter(isTestDefaultsDomain);
|
|
155
|
+
} catch {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var BUNDLE_NAME = "Pushary.app";
|
|
160
|
+
var DUMP_PATH_LINE = /^\s*path:\s+(.+?)(?: \(0x[0-9a-f]+\))?\s*$/;
|
|
161
|
+
var bundlesInLaunchServicesDump = (dump) => {
|
|
162
|
+
const found = /* @__PURE__ */ new Set();
|
|
163
|
+
for (const line of dump.split("\n")) {
|
|
164
|
+
const path = DUMP_PATH_LINE.exec(line)?.[1];
|
|
165
|
+
const bundle = path?.indexOf(`/${BUNDLE_NAME}`);
|
|
166
|
+
if (path === void 0 || bundle === void 0 || bundle === -1) continue;
|
|
167
|
+
found.add(path.slice(0, bundle + BUNDLE_NAME.length + 1));
|
|
168
|
+
}
|
|
169
|
+
return [...found];
|
|
170
|
+
};
|
|
171
|
+
var registeredPusharyBundles = (installed = installedPusharyBundles()) => {
|
|
172
|
+
if (!existsSync(LSREGISTER)) return [];
|
|
173
|
+
try {
|
|
174
|
+
const dump = execSync(`${LSREGISTER} -dump`, {
|
|
175
|
+
encoding: "utf-8",
|
|
176
|
+
timeout: 6e4,
|
|
177
|
+
maxBuffer: 256 * 1024 * 1024
|
|
178
|
+
});
|
|
179
|
+
return bundlesInLaunchServicesDump(dump).filter((path) => !installed.includes(path));
|
|
180
|
+
} catch {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
var MAC_KEYCHAIN_SERVICE = "com.pushary.app.mac";
|
|
185
|
+
var MAC_KEYCHAIN_ACCOUNTS = [
|
|
186
|
+
"pushary.site-api-key",
|
|
187
|
+
"pushary.site-id",
|
|
188
|
+
"clerk.device-token",
|
|
189
|
+
"clerk.session-id"
|
|
190
|
+
];
|
|
191
|
+
var MAC_DEFAULTS_DOMAINS = ["com.pushary.app", "pushary-bridge", "pushary.render"];
|
|
192
|
+
var macLibraryState = (home = homedir()) => [
|
|
193
|
+
join(home, "Library", "Application Support", "Pushary"),
|
|
194
|
+
join(home, "Library", "Caches", "com.pushary.app"),
|
|
195
|
+
join(home, "Library", "Caches", "com.pushary.app.sparkle"),
|
|
196
|
+
join(home, "Library", "Saved Application State", "com.pushary.app.savedState"),
|
|
197
|
+
join(home, "Library", "Caches", "pushary-bridge"),
|
|
198
|
+
join(home, "Library", "HTTPStorages", "com.pushary.app"),
|
|
199
|
+
join(home, "Library", "HTTPStorages", "com.pushary.app.binarycookies"),
|
|
200
|
+
join(home, "Library", "HTTPStorages", "pushary-bridge")
|
|
201
|
+
];
|
|
202
|
+
var DUMP_SERVICE_LINE = /^\s*"svce"<blob>="(.+)"\s*$/;
|
|
203
|
+
var pusharyKeychainServices = (dump) => {
|
|
204
|
+
const found = /* @__PURE__ */ new Set();
|
|
205
|
+
for (const line of dump.split("\n")) {
|
|
206
|
+
const service = DUMP_SERVICE_LINE.exec(line)?.[1];
|
|
207
|
+
if (service?.startsWith(MAC_KEYCHAIN_SERVICE)) found.add(service);
|
|
208
|
+
}
|
|
209
|
+
return [...found];
|
|
210
|
+
};
|
|
211
|
+
var storedPusharyKeychainServices = () => {
|
|
212
|
+
try {
|
|
213
|
+
const dump = execSync("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
|
+
} catch {
|
|
222
|
+
return [MAC_KEYCHAIN_SERVICE];
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
var MAC_APP_BUNDLE_ID = "com.pushary.app";
|
|
226
|
+
var QUIT_MAC_APP = `osascript -e 'tell application id "${MAC_APP_BUNDLE_ID}" to quit'`;
|
|
227
|
+
var runningMacAppPIDs = () => {
|
|
228
|
+
if (process.platform !== "darwin") return [];
|
|
229
|
+
try {
|
|
230
|
+
return execSync('pgrep -f "Pushary.app/Contents/MacOS/Pushary"', {
|
|
231
|
+
encoding: "utf-8",
|
|
232
|
+
timeout: 1e4,
|
|
233
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
234
|
+
}).split("\n").map((line) => Number(line.trim())).filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
235
|
+
} catch {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
var waitForMacAppExit = (attempts = 20, everyMs = 250) => {
|
|
240
|
+
const idle = new Int32Array(new SharedArrayBuffer(4));
|
|
241
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
242
|
+
if (runningMacAppPIDs().length === 0) return true;
|
|
243
|
+
Atomics.wait(idle, 0, 0, everyMs);
|
|
244
|
+
}
|
|
245
|
+
return runningMacAppPIDs().length === 0;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// src/cli/editor-extension.ts
|
|
249
|
+
import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
|
|
250
|
+
import { homedir as homedir2 } from "os";
|
|
251
|
+
import { join as join2 } from "path";
|
|
252
|
+
var EDITOR_EXTENSION_ID = "pushary.pushary";
|
|
253
|
+
var EDITOR_EXTENSION_DIRS = [
|
|
254
|
+
join2(".cursor", "extensions"),
|
|
255
|
+
join2(".vscode", "extensions")
|
|
256
|
+
];
|
|
257
|
+
var EXTENSIONS_JSON = "extensions.json";
|
|
258
|
+
var isOurEntry = (entry) => {
|
|
259
|
+
if (typeof entry !== "object" || entry === null) return false;
|
|
260
|
+
const identifier = entry.identifier;
|
|
261
|
+
if (typeof identifier !== "object" || identifier === null) return false;
|
|
262
|
+
const id = identifier.id;
|
|
263
|
+
return typeof id === "string" && id.toLowerCase() === EDITOR_EXTENSION_ID;
|
|
264
|
+
};
|
|
265
|
+
var recordWithoutUs = (directory) => {
|
|
266
|
+
const path = join2(directory, EXTENSIONS_JSON);
|
|
267
|
+
try {
|
|
268
|
+
const entries = JSON.parse(readFileSync(path, "utf-8"));
|
|
269
|
+
if (!Array.isArray(entries)) return void 0;
|
|
270
|
+
const kept = entries.filter((entry) => !isOurEntry(entry));
|
|
271
|
+
return kept.length === entries.length ? void 0 : { path, contents: JSON.stringify(kept) };
|
|
272
|
+
} catch {
|
|
273
|
+
return void 0;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
var editorExtensionRemovals = (home = homedir2()) => EDITOR_EXTENSION_DIRS.map((relative) => join2(home, relative)).filter(existsSync2).map((directory) => {
|
|
277
|
+
const folders = readdirSync(directory).filter((name) => name.startsWith(`${EDITOR_EXTENSION_ID}-`)).map((name) => join2(directory, name));
|
|
278
|
+
return { directory, folders, record: recordWithoutUs(directory) };
|
|
279
|
+
}).filter((removal) => removal.folders.length > 0 || removal.record !== void 0);
|
|
280
|
+
|
|
281
|
+
// src/cli/skills-store.ts
|
|
282
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
283
|
+
import { homedir as homedir3 } from "os";
|
|
284
|
+
import { join as join3 } from "path";
|
|
285
|
+
var SKILLS_STORE_DIR = ".agents";
|
|
286
|
+
var LOCK_FILE = ".skill-lock.json";
|
|
287
|
+
var lockWithoutUs = (path) => {
|
|
288
|
+
try {
|
|
289
|
+
const lock = JSON.parse(readFileSync2(path, "utf-8"));
|
|
290
|
+
const entry = lock.skills?.[PUSHARY_SKILL_NAME];
|
|
291
|
+
if (entry?.source !== PUSHARY_SKILL_SOURCE) return void 0;
|
|
292
|
+
const { [PUSHARY_SKILL_NAME]: _ours, ...skills } = lock.skills ?? {};
|
|
293
|
+
return { path, contents: `${JSON.stringify({ ...lock, skills }, null, 2)}
|
|
294
|
+
` };
|
|
295
|
+
} catch {
|
|
296
|
+
return void 0;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
var skillsStoreRemoval = (home = homedir3()) => {
|
|
300
|
+
const store = join3(home, SKILLS_STORE_DIR);
|
|
301
|
+
const skillDir = join3(store, "skills", PUSHARY_SKILL_NAME);
|
|
302
|
+
return {
|
|
303
|
+
skillDir: existsSync3(skillDir) ? skillDir : void 0,
|
|
304
|
+
lock: lockWithoutUs(join3(store, LOCK_FILE))
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// src/cli/claude-backups.ts
|
|
309
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
310
|
+
import { homedir as homedir4 } from "os";
|
|
311
|
+
import { join as join4 } from "path";
|
|
312
|
+
var BACKUP_PREFIX = ".claude.json.backup";
|
|
313
|
+
var claudeJsonBackups = (home = homedir4()) => {
|
|
314
|
+
const rollingDir = join4(home, ".claude", "backups");
|
|
315
|
+
const legacy = join4(home, BACKUP_PREFIX);
|
|
316
|
+
const rolling = existsSync4(rollingDir) ? readdirSync2(rollingDir).filter((name) => name.startsWith(BACKUP_PREFIX)).sort().map((name) => join4(rollingDir, name)) : [];
|
|
317
|
+
return existsSync4(legacy) ? [...rolling, legacy] : rolling;
|
|
318
|
+
};
|
|
319
|
+
var parsed = (path) => {
|
|
320
|
+
try {
|
|
321
|
+
const value = JSON.parse(readFileSync3(path, "utf-8"));
|
|
322
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
323
|
+
} catch {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
var claudeBackupRewrites = (receipt, home = homedir4()) => claudeJsonBackups(home).flatMap((path) => {
|
|
328
|
+
const backup = parsed(path);
|
|
329
|
+
if (!backup) return [];
|
|
330
|
+
const changed = receipt ? restoreClaudeMcpServer(backup, receipt) : removeClaudeMcpServers(backup);
|
|
331
|
+
return changed ? [{ path, contents: `${JSON.stringify(backup, null, 2)}
|
|
332
|
+
` }] : [];
|
|
333
|
+
});
|
|
334
|
+
|
|
127
335
|
// src/app-owned-paths.ts
|
|
128
336
|
var APP_OWNED_ENTRIES = [
|
|
129
337
|
// PusharyPaths.bin: bridgeStub, bridgeCache
|
|
@@ -176,10 +384,15 @@ var CURSOR_PLUGIN_DIR = cursorPluginDir();
|
|
|
176
384
|
var CURSOR_USER_HOOKS = cursorUserHooks();
|
|
177
385
|
var VSCODE_PLUGIN_DIR = vscodePluginDir();
|
|
178
386
|
var PUSHARY_DIR = pusharyDir();
|
|
179
|
-
var SHELL_FILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"].map((f) =>
|
|
180
|
-
var
|
|
181
|
-
|
|
182
|
-
|
|
387
|
+
var SHELL_FILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"].map((f) => join5(homedir5(), f));
|
|
388
|
+
var PRESENCE_BY_RESOLUTION = {
|
|
389
|
+
bundle: "present",
|
|
390
|
+
missing: "gone",
|
|
391
|
+
"no-stub": "absent"
|
|
392
|
+
};
|
|
393
|
+
var macAppPresence = () => PRESENCE_BY_RESOLUTION[resolveBridgeStub(join5(PUSHARY_DIR, "bin", HOOK_LOCATOR_BINARY), liveBridgeProbe()).kind];
|
|
394
|
+
var removalScope = (keepMcpServer, keepAppWiring) => ({
|
|
395
|
+
keepAppWiring,
|
|
183
396
|
keepMcpServer
|
|
184
397
|
});
|
|
185
398
|
var codexMcpKey = (config) => {
|
|
@@ -188,7 +401,7 @@ var codexMcpKey = (config) => {
|
|
|
188
401
|
};
|
|
189
402
|
var readJson = (path) => {
|
|
190
403
|
try {
|
|
191
|
-
return JSON.parse(
|
|
404
|
+
return JSON.parse(readFileSync4(path, "utf-8"));
|
|
192
405
|
} catch {
|
|
193
406
|
return null;
|
|
194
407
|
}
|
|
@@ -202,6 +415,78 @@ var guards = createGuards(() => dryRun, wouldNote);
|
|
|
202
415
|
var guardedWrite = (path, contents) => guards.write(path, contents);
|
|
203
416
|
var guardedRemove = (path, options) => guards.remove(path, options);
|
|
204
417
|
var guardedExec = (command, options, describe) => guards.exec(command, options ?? {}, describe);
|
|
418
|
+
var removeDeveloperLeftovers = () => {
|
|
419
|
+
if (process.platform !== "darwin") return 0;
|
|
420
|
+
let removed = 0;
|
|
421
|
+
for (const domain of leakedTestDefaultsDomains()) {
|
|
422
|
+
const command = `defaults delete ${JSON.stringify(domain)}`;
|
|
423
|
+
try {
|
|
424
|
+
guardedExec(command, { stdio: "ignore", timeout: 1e4 }, command);
|
|
425
|
+
removed++;
|
|
426
|
+
} catch {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
for (const bundle of registeredPusharyBundles()) {
|
|
431
|
+
const command = `${LSREGISTER} -u ${JSON.stringify(bundle)}`;
|
|
432
|
+
try {
|
|
433
|
+
guardedExec(command, { stdio: "ignore", timeout: 2e4 }, command);
|
|
434
|
+
removed++;
|
|
435
|
+
} catch {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return removed;
|
|
440
|
+
};
|
|
441
|
+
var removeMacAppState = () => {
|
|
442
|
+
if (process.platform !== "darwin") return 0;
|
|
443
|
+
let removed = 0;
|
|
444
|
+
for (const service of storedPusharyKeychainServices()) {
|
|
445
|
+
for (const account of MAC_KEYCHAIN_ACCOUNTS) {
|
|
446
|
+
const command = `security delete-generic-password -s ${JSON.stringify(service)} -a ${account}`;
|
|
447
|
+
try {
|
|
448
|
+
guardedExec(command, { stdio: "ignore", timeout: 1e4 }, command);
|
|
449
|
+
removed++;
|
|
450
|
+
} catch {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
for (const domain of MAC_DEFAULTS_DOMAINS) {
|
|
456
|
+
const command = `defaults delete ${JSON.stringify(domain)}`;
|
|
457
|
+
try {
|
|
458
|
+
guardedExec(command, { stdio: "ignore", timeout: 1e4 }, command);
|
|
459
|
+
removed++;
|
|
460
|
+
} catch {
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
for (const path of macLibraryState(homedir5())) {
|
|
465
|
+
if (!existsSync5(path)) continue;
|
|
466
|
+
guardedRemove(path, { force: true });
|
|
467
|
+
removed++;
|
|
468
|
+
}
|
|
469
|
+
return removed;
|
|
470
|
+
};
|
|
471
|
+
var quitMacApp = () => {
|
|
472
|
+
if (process.platform !== "darwin") return "not-running";
|
|
473
|
+
if (runningMacAppPIDs().length === 0) return "not-running";
|
|
474
|
+
try {
|
|
475
|
+
guardedExec(QUIT_MAC_APP, { stdio: "ignore", timeout: 3e4 }, QUIT_MAC_APP);
|
|
476
|
+
} catch {
|
|
477
|
+
}
|
|
478
|
+
return dryRun || waitForMacAppExit() ? "quit" : "running";
|
|
479
|
+
};
|
|
480
|
+
var CURSOR_PLUGIN_STAGING = ".pushary-staging-";
|
|
481
|
+
var cursorPluginLeftovers = () => {
|
|
482
|
+
const parent = dirname(CURSOR_PLUGIN_DIR);
|
|
483
|
+
const backupPrefix = `${CURSOR_PLUGIN_DIR.split("/").pop()}.pushary-backup-`;
|
|
484
|
+
try {
|
|
485
|
+
return readdirSync3(parent).filter((name) => name.startsWith(backupPrefix) || name.startsWith(CURSOR_PLUGIN_STAGING)).map((name) => join5(parent, name));
|
|
486
|
+
} catch {
|
|
487
|
+
return [];
|
|
488
|
+
}
|
|
489
|
+
};
|
|
205
490
|
var guardedDaemonRemoval = async () => {
|
|
206
491
|
if (dryRun) {
|
|
207
492
|
wouldNote("stop and uninstall", "Pushary background service");
|
|
@@ -211,14 +496,14 @@ var guardedDaemonRemoval = async () => {
|
|
|
211
496
|
};
|
|
212
497
|
var guardedInstructionBlockRemoval = (path) => {
|
|
213
498
|
if (!dryRun) return removeInstructionBlock(path);
|
|
214
|
-
if (!
|
|
215
|
-
const hasBlock =
|
|
499
|
+
if (!existsSync5(path)) return false;
|
|
500
|
+
const hasBlock = readFileSync4(path, "utf-8").includes("pushary");
|
|
216
501
|
if (hasBlock) wouldNote("rewrite", path);
|
|
217
502
|
return hasBlock;
|
|
218
503
|
};
|
|
219
504
|
var guardedAliasRemoval = () => {
|
|
220
505
|
if (!dryRun) return removeClaudeAlias();
|
|
221
|
-
const shells =
|
|
506
|
+
const shells = CANDIDATE_RCS.map((name) => join5(homedir5(), name)).filter((path) => existsSync5(path) && readFileSync4(path, "utf-8").includes("pushary claude"));
|
|
222
507
|
for (const path of shells) wouldNote("rewrite", path);
|
|
223
508
|
return shells;
|
|
224
509
|
};
|
|
@@ -226,15 +511,15 @@ var writeJson = (path, data) => {
|
|
|
226
511
|
guardedWrite(path, JSON.stringify(data, null, 2) + "\n");
|
|
227
512
|
};
|
|
228
513
|
var removeCursorUserHook = (scope) => {
|
|
229
|
-
if (!
|
|
230
|
-
let
|
|
514
|
+
if (!existsSync5(CURSOR_USER_HOOKS)) return "absent";
|
|
515
|
+
let parsed2;
|
|
231
516
|
try {
|
|
232
|
-
|
|
517
|
+
parsed2 = JSON.parse(readFileSync4(CURSOR_USER_HOOKS, "utf-8"));
|
|
233
518
|
} catch {
|
|
234
519
|
return "failed";
|
|
235
520
|
}
|
|
236
|
-
if (!
|
|
237
|
-
const hooks =
|
|
521
|
+
if (!parsed2 || typeof parsed2 !== "object") return "failed";
|
|
522
|
+
const hooks = parsed2.hooks ?? {};
|
|
238
523
|
let removedAny = false;
|
|
239
524
|
for (const event of CURSOR_GATE_EVENTS) {
|
|
240
525
|
const existing = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
@@ -248,15 +533,15 @@ var removeCursorUserHook = (scope) => {
|
|
|
248
533
|
else hooks[event] = others;
|
|
249
534
|
}
|
|
250
535
|
if (!removedAny) return "absent";
|
|
251
|
-
|
|
536
|
+
parsed2.hooks = hooks;
|
|
252
537
|
try {
|
|
253
|
-
writeJson(CURSOR_USER_HOOKS,
|
|
538
|
+
writeJson(CURSOR_USER_HOOKS, parsed2);
|
|
254
539
|
} catch {
|
|
255
540
|
return "failed";
|
|
256
541
|
}
|
|
257
542
|
if (dryRun) return "removed";
|
|
258
543
|
try {
|
|
259
|
-
const after = JSON.parse(
|
|
544
|
+
const after = JSON.parse(readFileSync4(CURSOR_USER_HOOKS, "utf-8"));
|
|
260
545
|
const afterHooks = after?.hooks ?? {};
|
|
261
546
|
const stillThere = CURSOR_GATE_EVENTS.some((event) => {
|
|
262
547
|
const entries = afterHooks[event];
|
|
@@ -296,15 +581,15 @@ var restoreTakeoverReceipts = () => {
|
|
|
296
581
|
["codex", codexHooksJson(), codexConfigToml(), "mcp_servers", "toml"],
|
|
297
582
|
["gemini_cli", geminiSettings(), geminiSettings(), "mcpServers", "json"],
|
|
298
583
|
["cursor", CURSOR_USER_HOOKS, null, null, null],
|
|
299
|
-
["vscode",
|
|
584
|
+
["vscode", join5(VSCODE_PLUGIN_DIR, "hooks", "hooks.json"), vscodePluginMcp(), "mcpServers", "json"]
|
|
300
585
|
];
|
|
301
586
|
for (const [agent, hookFile, mcpFile, mcpKey, mcpFormat] of targets) {
|
|
302
587
|
const receiptPath = hookTakeoverReceipt(agent);
|
|
303
|
-
if (!
|
|
588
|
+
if (!existsSync5(receiptPath)) continue;
|
|
304
589
|
try {
|
|
305
590
|
const receipt = readJson(receiptPath);
|
|
306
591
|
if (!isHookTakeoverReceipt(receipt) || receipt.agent !== agent) throw new Error("invalid receipt");
|
|
307
|
-
const document =
|
|
592
|
+
const document = existsSync5(hookFile) ? JSON.parse(readFileSync4(hookFile, "utf-8")) : {};
|
|
308
593
|
if (!document || typeof document !== "object" || Array.isArray(document)) throw new Error("invalid hooks file");
|
|
309
594
|
const appAlreadyOwns = hookOwnerOf(document) === "app";
|
|
310
595
|
const cliPositions = agent === "codex" ? codexHookPositions(document) : {};
|
|
@@ -315,7 +600,7 @@ var restoreTakeoverReceipts = () => {
|
|
|
315
600
|
if (mcpFile === hookFile) {
|
|
316
601
|
mcpDocument = document;
|
|
317
602
|
} else {
|
|
318
|
-
const raw =
|
|
603
|
+
const raw = existsSync5(mcpFile) ? readFileSync4(mcpFile, "utf-8") : "";
|
|
319
604
|
mcpDocument = raw.trim() ? mcpFormat === "toml" ? parseTOML(raw) : JSON.parse(raw) : {};
|
|
320
605
|
}
|
|
321
606
|
mcpChanged = restoreMcpServer(mcpDocument, mcpKey, receipt.mcpServer);
|
|
@@ -325,7 +610,7 @@ var restoreTakeoverReceipts = () => {
|
|
|
325
610
|
if (!appAlreadyOwns && agent === "codex") {
|
|
326
611
|
if (!mcpDocument) {
|
|
327
612
|
const configPath = codexConfigToml();
|
|
328
|
-
const raw =
|
|
613
|
+
const raw = existsSync5(configPath) ? readFileSync4(configPath, "utf-8") : "";
|
|
329
614
|
mcpDocument = raw.trim() ? parseTOML(raw) : {};
|
|
330
615
|
}
|
|
331
616
|
codexConfig = mcpDocument;
|
|
@@ -358,7 +643,7 @@ var main = async () => {
|
|
|
358
643
|
try {
|
|
359
644
|
rejectUnknownFlags(
|
|
360
645
|
process.argv.slice(2).filter((a) => a !== "clean"),
|
|
361
|
-
["--dry-run", "--yes", "-y", "--help", "-h"]
|
|
646
|
+
["--dry-run", "--everything", "--yes", "-y", "--help", "-h"]
|
|
362
647
|
);
|
|
363
648
|
} catch (err) {
|
|
364
649
|
process.stderr.write(` ! ${err instanceof Error ? err.message : String(err)}
|
|
@@ -366,6 +651,7 @@ var main = async () => {
|
|
|
366
651
|
process.exit(EXIT.USAGE);
|
|
367
652
|
}
|
|
368
653
|
dryRun = process.argv.includes("--dry-run");
|
|
654
|
+
const everything = process.argv.includes("--everything");
|
|
369
655
|
if (dryRun) {
|
|
370
656
|
console.log(` ${bold("Dry run")} ${dim("- nothing will be changed")}`);
|
|
371
657
|
console.log();
|
|
@@ -381,14 +667,30 @@ var main = async () => {
|
|
|
381
667
|
process.exit(0);
|
|
382
668
|
}
|
|
383
669
|
console.log();
|
|
384
|
-
const
|
|
385
|
-
const
|
|
670
|
+
const presence = macAppPresence();
|
|
671
|
+
const keepShared = presence === "present" && !everything;
|
|
672
|
+
const scope = removalScope(keepShared, keepShared);
|
|
386
673
|
if (keepShared) {
|
|
387
674
|
console.log(` ${dim("The Mac app is installed, so its wiring and the state it owns are left alone.")}`);
|
|
388
|
-
console.log();
|
|
675
|
+
console.log(` ${dim("To remove that too, re-run with")} --everything`);
|
|
676
|
+
} else if (presence === "gone") {
|
|
677
|
+
console.log(` ${dim("The Mac app is gone, so the wiring it left behind is removed too.")}`);
|
|
678
|
+
} else if (everything && presence === "present") {
|
|
679
|
+
console.log(` ${dim("Removing the Mac app's wiring and state as well.")}`);
|
|
680
|
+
}
|
|
681
|
+
if (everything) {
|
|
682
|
+
const quit = quitMacApp();
|
|
683
|
+
if (quit === "quit") {
|
|
684
|
+
console.log(` ${check} Pushary ${dim(did("quit"))} ${dim("(running, and its watcher puts back what this removes)")}`);
|
|
685
|
+
} else if (quit === "running") {
|
|
686
|
+
incompleteClean = true;
|
|
687
|
+
console.log(` ${cross} Pushary ${dim("would not quit, and it puts back what this removes")}`);
|
|
688
|
+
console.log(` ${dim("Quit it from the notch menu, then re-run.")}`);
|
|
689
|
+
}
|
|
389
690
|
}
|
|
691
|
+
if (presence !== "absent") console.log();
|
|
390
692
|
const restoredByTakeover = new Set(
|
|
391
|
-
["claude_code", "codex", "gemini_cli"].filter((agent) =>
|
|
693
|
+
["claude_code", "codex", "gemini_cli"].filter((agent) => existsSync5(hookTakeoverReceipt(agent)))
|
|
392
694
|
);
|
|
393
695
|
if (keepShared) restoreTakeoverReceipts();
|
|
394
696
|
const sharedMcpKeys = {
|
|
@@ -414,12 +716,13 @@ var main = async () => {
|
|
|
414
716
|
const keepClaudeMcp = macAppKeeps("claude_code", mcpServerBearer(claudeJson2?.mcpServers));
|
|
415
717
|
const claudeHasPusharyEntry = () => Boolean(claudeJson2?.mcpServers?.pushary);
|
|
416
718
|
const claudeHadPusharyEntry = claudeHasPusharyEntry();
|
|
719
|
+
const rawReceipt = readJson(CLAUDE_MCP_RECEIPT);
|
|
720
|
+
const receipt = isClaudeMcpSnapshot(rawReceipt) ? rawReceipt : null;
|
|
721
|
+
const receiptUnreadable = existsSync5(CLAUDE_MCP_RECEIPT) && !receipt;
|
|
417
722
|
if (keepClaudeMcp) {
|
|
418
723
|
console.log(` ${skip} Claude Code MCP servers ${dim("(kept: the Mac app uses it)")}`);
|
|
419
724
|
} else if (claudeJson2) {
|
|
420
|
-
|
|
421
|
-
const receipt = isClaudeMcpSnapshot(rawReceipt) ? rawReceipt : null;
|
|
422
|
-
if (existsSync(CLAUDE_MCP_RECEIPT) && !receipt) {
|
|
725
|
+
if (receiptUnreadable) {
|
|
423
726
|
if (!keepShared) incompleteClean = true;
|
|
424
727
|
console.log(` ${skip} Claude Code MCP servers ${dim("(setup receipt is unreadable; left unchanged)")}`);
|
|
425
728
|
} else if (receipt && restoreClaudeMcpServer(claudeJson2, receipt) || !receipt && removeClaudeMcpServers(claudeJson2)) {
|
|
@@ -430,13 +733,20 @@ var main = async () => {
|
|
|
430
733
|
if (receipt && !keepShared) incompleteClean = true;
|
|
431
734
|
console.log(` ${skip} Claude Code MCP servers ${dim(receipt ? "(changed after setup; left unchanged)" : "(no pushary entries)")}`);
|
|
432
735
|
}
|
|
433
|
-
} else if (
|
|
736
|
+
} else if (existsSync5(CLAUDE_MCP_RECEIPT)) {
|
|
434
737
|
incompleteClean = true;
|
|
435
738
|
console.log(` ${skip} Claude Code MCP servers ${dim("(config missing; setup receipt kept for retry)")}`);
|
|
436
739
|
} else {
|
|
437
740
|
console.log(` ${skip} Claude Code MCP servers ${dim("(not found)")}`);
|
|
438
741
|
}
|
|
439
|
-
|
|
742
|
+
if (!keepClaudeMcp && !receiptUnreadable) {
|
|
743
|
+
const rewrites = claudeBackupRewrites(receipt);
|
|
744
|
+
for (const rewrite of rewrites) guardedWrite(rewrite.path, rewrite.contents);
|
|
745
|
+
if (rewrites.length > 0) {
|
|
746
|
+
console.log(` ${check} Claude Code backups ${dim(did("cleaned"))} ${dim(`(the Pushary entry and its key, in ${rewrites.length})`)}`);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
const claudeScope = removalScope(keepShared && !(claudeHadPusharyEntry && !claudeHasPusharyEntry()), keepShared);
|
|
440
750
|
cleanSettingsFile(CLAUDE_SETTINGS, "Claude Code settings", claudeScope);
|
|
441
751
|
cleanSettingsFile(CLAUDE_SETTINGS_LOCAL, "Claude Code settings.local", claudeScope);
|
|
442
752
|
const cursorData = keepShared ? null : readJson(CURSOR_MCP);
|
|
@@ -467,22 +777,29 @@ var main = async () => {
|
|
|
467
777
|
}
|
|
468
778
|
if (cursorGate === "failed") {
|
|
469
779
|
console.log(` ${skip} Cursor plugin ${dim("(kept: removing it now would block matched commands)")}`);
|
|
470
|
-
} else if (
|
|
780
|
+
} else if (existsSync5(CURSOR_PLUGIN_DIR)) {
|
|
471
781
|
guardedRemove(CURSOR_PLUGIN_DIR);
|
|
472
782
|
console.log(` ${check} Cursor plugin ${dim("(removed from ~/.cursor/plugins/local)")}`);
|
|
473
783
|
} else {
|
|
474
784
|
console.log(` ${skip} Cursor plugin ${dim("(not installed)")}`);
|
|
475
785
|
}
|
|
786
|
+
if (cursorGate !== "failed") {
|
|
787
|
+
const stale = cursorPluginLeftovers();
|
|
788
|
+
for (const path of stale) guardedRemove(path);
|
|
789
|
+
if (stale.length > 0) {
|
|
790
|
+
console.log(` ${check} Cursor plugin copies ${dim(did("removed"))} ${dim(`(${stale.length}, each holding the key of an earlier install)`)}`);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
476
793
|
if (keepShared) {
|
|
477
794
|
console.log(` ${skip} VS Code plugin ${dim("(kept: the Mac app writes the same plugin directory)")}`);
|
|
478
795
|
}
|
|
479
|
-
for (const settingsPath of keepShared ? [] : vscodeSettingsTargets(
|
|
796
|
+
for (const settingsPath of keepShared ? [] : vscodeSettingsTargets(existsSync5)) {
|
|
480
797
|
const label = `VS Code plugin ${dim(`(${shortenHome(settingsPath)})`)}`;
|
|
481
|
-
if (!
|
|
798
|
+
if (!existsSync5(settingsPath)) {
|
|
482
799
|
console.log(` ${skip} ${label} ${dim("(no settings.json)")}`);
|
|
483
800
|
continue;
|
|
484
801
|
}
|
|
485
|
-
const result = unregisterPluginLocation(
|
|
802
|
+
const result = unregisterPluginLocation(readFileSync4(settingsPath, "utf-8"), VSCODE_PLUGIN_DIR);
|
|
486
803
|
if (result.kind === "removed") {
|
|
487
804
|
guardedWrite(settingsPath, result.content);
|
|
488
805
|
console.log(` ${check} ${label} ${dim(did("unregistered"))}`);
|
|
@@ -492,13 +809,26 @@ var main = async () => {
|
|
|
492
809
|
console.log(` ${skip} ${label} ${dim("(no pushary entry)")}`);
|
|
493
810
|
}
|
|
494
811
|
}
|
|
495
|
-
if (!keepShared &&
|
|
812
|
+
if (!keepShared && existsSync5(VSCODE_PLUGIN_DIR)) {
|
|
496
813
|
guardedRemove(VSCODE_PLUGIN_DIR);
|
|
497
814
|
console.log(` ${check} VS Code plugin files ${dim(did("removed"))}`);
|
|
498
815
|
}
|
|
816
|
+
if (keepShared) {
|
|
817
|
+
console.log(` ${skip} Editor extension ${dim("(kept: installed and updated by the Mac app)")}`);
|
|
818
|
+
} else {
|
|
819
|
+
const removals = editorExtensionRemovals();
|
|
820
|
+
for (const removal of removals) {
|
|
821
|
+
if (removal.record) guardedWrite(removal.record.path, removal.record.contents);
|
|
822
|
+
for (const folder of removal.folders) guardedRemove(folder);
|
|
823
|
+
console.log(` ${check} Editor extension ${dim(`(${did("removed")} from ${shortenHome(removal.directory)})`)}`);
|
|
824
|
+
}
|
|
825
|
+
if (removals.length === 0) {
|
|
826
|
+
console.log(` ${skip} Editor extension ${dim("(not installed in Cursor or VS Code)")}`);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
499
829
|
if (keepShared) {
|
|
500
830
|
console.log(` ${skip} Skill directory ${dim("(kept: the Mac app uses it)")}`);
|
|
501
|
-
} else if (
|
|
831
|
+
} else if (existsSync5(SKILL_DIR)) {
|
|
502
832
|
guardedRemove(SKILL_DIR);
|
|
503
833
|
console.log(` ${check} Skill directory ${dim(did("removed"))}`);
|
|
504
834
|
} else {
|
|
@@ -509,26 +839,26 @@ var main = async () => {
|
|
|
509
839
|
const codexHooksData = readJson(codexHooksJson2);
|
|
510
840
|
const codexPositions = codexHookPositions(codexHooksData ?? void 0);
|
|
511
841
|
try {
|
|
512
|
-
const config =
|
|
513
|
-
const
|
|
514
|
-
const trustRemoved = hookOwnerOf(codexHooksData) === "app" ? false : removeCodexHookTrust(
|
|
842
|
+
const config = readFileSync4(codexConfig, "utf-8");
|
|
843
|
+
const parsed2 = parseTOML(config);
|
|
844
|
+
const trustRemoved = hookOwnerOf(codexHooksData) === "app" && keepShared ? false : removeCodexHookTrust(parsed2, codexHooksJson2, codexPositions);
|
|
515
845
|
const hadPushary = config.includes("pushary");
|
|
516
846
|
if (hadPushary || trustRemoved) {
|
|
517
|
-
const macAppKeepsCodexMcp = macAppKeeps("codex", codexMcpKey(
|
|
847
|
+
const macAppKeepsCodexMcp = macAppKeeps("codex", codexMcpKey(parsed2));
|
|
518
848
|
if (!macAppKeepsCodexMcp) {
|
|
519
|
-
const mcpServers =
|
|
849
|
+
const mcpServers = parsed2.mcp_servers ?? {};
|
|
520
850
|
delete mcpServers.pushary;
|
|
521
|
-
if (Object.keys(mcpServers).length === 0) delete
|
|
522
|
-
else
|
|
851
|
+
if (Object.keys(mcpServers).length === 0) delete parsed2.mcp_servers;
|
|
852
|
+
else parsed2.mcp_servers = mcpServers;
|
|
523
853
|
}
|
|
524
|
-
if (Array.isArray(
|
|
525
|
-
|
|
526
|
-
if (
|
|
854
|
+
if (Array.isArray(parsed2.notify)) {
|
|
855
|
+
parsed2.notify = parsed2.notify.filter((n) => typeof n !== "string" || !n.includes("pushary-codex"));
|
|
856
|
+
if (parsed2.notify.length === 0) delete parsed2.notify;
|
|
527
857
|
}
|
|
528
|
-
guardedWrite(codexConfig, stringifyTOML(
|
|
858
|
+
guardedWrite(codexConfig, stringifyTOML(parsed2));
|
|
529
859
|
const what = trustRemoved ? "cleaned, hook trust removed" : "cleaned";
|
|
530
860
|
console.log(` ${check} Codex config ${dim(did(what))}`);
|
|
531
|
-
if (macAppKeepsCodexMcp &&
|
|
861
|
+
if (macAppKeepsCodexMcp && parsed2.mcp_servers?.pushary) {
|
|
532
862
|
console.log(` ${skip} Codex MCP server ${dim("(kept: the Mac app uses it)")}`);
|
|
533
863
|
}
|
|
534
864
|
} else {
|
|
@@ -558,7 +888,7 @@ var main = async () => {
|
|
|
558
888
|
const codexSkillDir2 = codexSkillDir();
|
|
559
889
|
if (keepShared) {
|
|
560
890
|
console.log(` ${skip} Codex skill directory ${dim("(kept: the Mac app uses it)")}`);
|
|
561
|
-
} else if (
|
|
891
|
+
} else if (existsSync5(codexSkillDir2)) {
|
|
562
892
|
guardedRemove(codexSkillDir2);
|
|
563
893
|
console.log(` ${check} Codex skill directory ${dim(did("removed"))}`);
|
|
564
894
|
} else {
|
|
@@ -596,7 +926,7 @@ var main = async () => {
|
|
|
596
926
|
const geminiSettingsPath = geminiSettings();
|
|
597
927
|
const geminiSettings2 = readJson(geminiSettingsPath);
|
|
598
928
|
if (geminiSettings2) {
|
|
599
|
-
if (removeGeminiSettings(geminiSettings2, removalScope(macAppKeeps("gemini_cli", mcpServerBearer(geminiSettings2.mcpServers))))) {
|
|
929
|
+
if (removeGeminiSettings(geminiSettings2, removalScope(macAppKeeps("gemini_cli", mcpServerBearer(geminiSettings2.mcpServers)), keepShared))) {
|
|
600
930
|
writeJson(geminiSettingsPath, geminiSettings2);
|
|
601
931
|
console.log(` ${check} Gemini CLI settings ${dim(did("cleaned"))}`);
|
|
602
932
|
} else {
|
|
@@ -614,25 +944,25 @@ var main = async () => {
|
|
|
614
944
|
console.log(` ${skip} Gemini GEMINI.md ${dim("(no pushary block)")}`);
|
|
615
945
|
}
|
|
616
946
|
const openCodePlugin = openCodePluginFile();
|
|
617
|
-
const openCodePluginContents =
|
|
947
|
+
const openCodePluginContents = existsSync5(openCodePlugin) ? readFileSync4(openCodePlugin, "utf-8") : null;
|
|
618
948
|
if (openCodePluginContents === null) {
|
|
619
949
|
console.log(` ${skip} OpenCode plugin ${dim("(not installed)")}`);
|
|
620
950
|
} else if (!isOpenCodePluginOurs(openCodePluginContents)) {
|
|
621
951
|
console.log(` ${skip} OpenCode plugin ${dim("(a pushary.js we did not write)")}`);
|
|
622
|
-
} else if (openCodePluginContents.includes(HOOK_LOCATOR_BINARY)) {
|
|
952
|
+
} else if (openCodePluginContents.includes(HOOK_LOCATOR_BINARY) && keepShared) {
|
|
623
953
|
console.log(` ${skip} OpenCode plugin ${dim("(kept: written by the Mac app)")}`);
|
|
624
954
|
} else {
|
|
625
955
|
guardedRemove(openCodePlugin, { force: true });
|
|
626
956
|
console.log(` ${check} OpenCode plugin ${dim(did("removed"))}`);
|
|
627
|
-
const sibling =
|
|
957
|
+
const sibling = join5(openCodePluginDir(), OPENCODE_PLUGIN_SIBLING);
|
|
628
958
|
const remaining = (() => {
|
|
629
959
|
try {
|
|
630
|
-
return
|
|
960
|
+
return readdirSync3(openCodePluginDir()).filter((name) => name !== OPENCODE_PLUGIN_SIBLING);
|
|
631
961
|
} catch {
|
|
632
962
|
return [];
|
|
633
963
|
}
|
|
634
964
|
})();
|
|
635
|
-
const siblingIsOurs =
|
|
965
|
+
const siblingIsOurs = existsSync5(sibling) && readFileSync4(sibling, "utf-8") === OPENCODE_PLUGIN_SIBLING_CONTENTS;
|
|
636
966
|
if (siblingIsOurs && !hasForeignPlugins(remaining)) {
|
|
637
967
|
guardedRemove(sibling, { force: true });
|
|
638
968
|
console.log(` ${check} OpenCode plugin sibling ${dim(did("removed"))}`);
|
|
@@ -667,7 +997,7 @@ var main = async () => {
|
|
|
667
997
|
console.log(` ${skip} Hermes plugin ${dim("(could not uninstall; setup receipt kept for retry)")}`);
|
|
668
998
|
}
|
|
669
999
|
}
|
|
670
|
-
} else if (
|
|
1000
|
+
} else if (existsSync5(HERMES_CONFIG_RECEIPT)) {
|
|
671
1001
|
incompleteClean = true;
|
|
672
1002
|
console.log(` ${skip} Hermes ${dim("(runtime not found; setup receipt kept for retry)")}`);
|
|
673
1003
|
} else {
|
|
@@ -675,20 +1005,31 @@ var main = async () => {
|
|
|
675
1005
|
}
|
|
676
1006
|
for (const shellFile of SHELL_FILES) {
|
|
677
1007
|
try {
|
|
678
|
-
const content =
|
|
1008
|
+
const content = readFileSync4(shellFile, "utf-8");
|
|
679
1009
|
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);
|
|
1010
|
+
const isOurExport = (line) => /^\s*export\s+PUSHARY_API_KEY=['"]?pk_[A-Za-z0-9]+\.[A-Za-z0-9]+['"]?\s*$/.test(line) || isDisabledExport(line);
|
|
681
1011
|
const lines = content.split("\n");
|
|
682
1012
|
const declined = lines.filter((l) => l.includes("PUSHARY_API_KEY") && !isOurExport(l));
|
|
683
1013
|
const cleaned = lines.filter((l) => !isOurExport(l)).join("\n");
|
|
1014
|
+
const name = shellFile.split("/").pop();
|
|
684
1015
|
if (cleaned !== content) {
|
|
685
1016
|
const backup = `${shellFile}.pushary-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
686
1017
|
if (!dryRun) copyFileSync(shellFile, backup);
|
|
687
1018
|
guardedWrite(shellFile, cleaned);
|
|
688
|
-
|
|
1019
|
+
const confirmed = !dryRun && readFileSync4(shellFile, "utf-8") === cleaned;
|
|
1020
|
+
if (confirmed) guardedRemove(backup, { force: true });
|
|
1021
|
+
const kept = confirmed ? "" : `, backup at ${backup}`;
|
|
1022
|
+
console.log(` ${check} ${name} ${dim(`(removed the export${kept})`)}`);
|
|
689
1023
|
}
|
|
690
1024
|
for (const line of declined) {
|
|
691
|
-
|
|
1025
|
+
if ((line.match(/pk_[A-Za-z0-9_]+\.[A-Za-z0-9_]+/g) ?? []).some(isValidApiKey)) {
|
|
1026
|
+
incompleteClean = true;
|
|
1027
|
+
console.log(` ${cross} ${name} ${dim("still sets PUSHARY_API_KEY, in a form this command will not rewrite:")}`);
|
|
1028
|
+
console.log(` ${dim(line.trim())}`);
|
|
1029
|
+
console.log(` ${dim("Remove it yourself. A setup run would sign this machine in as that account.")}`);
|
|
1030
|
+
} else {
|
|
1031
|
+
console.log(` ${skip} ${name} ${dim(`left a line referencing PUSHARY_API_KEY: ${line.trim()}`)}`);
|
|
1032
|
+
}
|
|
692
1033
|
}
|
|
693
1034
|
}
|
|
694
1035
|
} catch {
|
|
@@ -700,15 +1041,15 @@ var main = async () => {
|
|
|
700
1041
|
} else {
|
|
701
1042
|
console.log(` ${skip} claude alias ${dim("(not set)")}`);
|
|
702
1043
|
}
|
|
703
|
-
if (
|
|
1044
|
+
if (existsSync5(PUSHARY_DIR)) {
|
|
704
1045
|
let removed = 0;
|
|
705
1046
|
const kept = [];
|
|
706
|
-
for (const entry of
|
|
1047
|
+
for (const entry of readdirSync3(PUSHARY_DIR)) {
|
|
707
1048
|
if (keepShared && isAppOwned(entry) || entry === "setup" && (keepShared || incompleteClean)) {
|
|
708
1049
|
kept.push(entry);
|
|
709
1050
|
continue;
|
|
710
1051
|
}
|
|
711
|
-
guardedRemove(
|
|
1052
|
+
guardedRemove(join5(PUSHARY_DIR, entry), { force: true });
|
|
712
1053
|
removed++;
|
|
713
1054
|
}
|
|
714
1055
|
if (removed > 0) {
|
|
@@ -725,10 +1066,10 @@ var main = async () => {
|
|
|
725
1066
|
}
|
|
726
1067
|
try {
|
|
727
1068
|
let swept = 0;
|
|
728
|
-
for (const f of
|
|
1069
|
+
for (const f of readdirSync3(tmpdir())) {
|
|
729
1070
|
if (!f.startsWith("pushary-")) continue;
|
|
730
1071
|
try {
|
|
731
|
-
guardedRemove(
|
|
1072
|
+
guardedRemove(join5(tmpdir(), f), { force: true });
|
|
732
1073
|
swept++;
|
|
733
1074
|
} catch {
|
|
734
1075
|
}
|
|
@@ -736,6 +1077,24 @@ var main = async () => {
|
|
|
736
1077
|
if (swept > 0) console.log(` ${check} Temp state ${dim(`(${swept} pushary entries swept)`)}`);
|
|
737
1078
|
} catch {
|
|
738
1079
|
}
|
|
1080
|
+
if (everything) {
|
|
1081
|
+
const store = skillsStoreRemoval();
|
|
1082
|
+
if (store.skillDir) guardedRemove(store.skillDir);
|
|
1083
|
+
if (store.lock) guardedWrite(store.lock.path, store.lock.contents);
|
|
1084
|
+
if (store.skillDir || store.lock) {
|
|
1085
|
+
console.log(` ${check} skills.sh registration ${dim(did("removed"))} ${dim("(~/.agents)")}`);
|
|
1086
|
+
}
|
|
1087
|
+
const leftovers = removeDeveloperLeftovers();
|
|
1088
|
+
if (leftovers > 0) {
|
|
1089
|
+
console.log(` ${check} Developer leftovers ${dim(did("removed"))} ${dim(`(${leftovers} leaked test preferences and stale app registrations)`)}`);
|
|
1090
|
+
}
|
|
1091
|
+
const removed = removeMacAppState();
|
|
1092
|
+
if (removed > 0) {
|
|
1093
|
+
console.log(` ${check} Mac app state ${dim(did("removed"))} ${dim(`(${removed} keychain items, preferences and caches)`)}`);
|
|
1094
|
+
} else {
|
|
1095
|
+
console.log(` ${skip} Mac app state ${dim("(nothing of ours in the keychain, preferences or caches)")}`);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
739
1098
|
try {
|
|
740
1099
|
if (dryRun) {
|
|
741
1100
|
wouldNote("run ", "npm uninstall -g @pushary/agent-hooks");
|
|
@@ -750,13 +1109,32 @@ var main = async () => {
|
|
|
750
1109
|
if (incompleteClean) {
|
|
751
1110
|
console.log(` ${yellow(bold("Clean finished with something left behind."))}`);
|
|
752
1111
|
console.log(` ${dim("See the \u2717 above. Fix it, then re-run")} npx @pushary/agent-hooks@latest clean`);
|
|
1112
|
+
console.log(` ${dim("Still stuck? Send this output to")} ${SUPPORT_EMAIL} ${dim("or")} ${SUPPORT_DISCORD}`);
|
|
1113
|
+
} else if (everything) {
|
|
1114
|
+
console.log(` ${green(bold("Clean complete. Nothing of ours is left in any agent config."))}`);
|
|
1115
|
+
const bundle = installedPusharyBundles().find(existsSync5);
|
|
1116
|
+
if (bundle) {
|
|
1117
|
+
console.log(` ${dim("The app itself is still installed. Drag")} ${bundle} ${dim("to the Trash to finish.")}`);
|
|
1118
|
+
}
|
|
1119
|
+
console.log(` ${dim("Signing in as someone else? Sign out at pushary.com in your browser first.")}`);
|
|
1120
|
+
console.log(` ${dim("The app and")} pushary login ${dim("both sign in through it, and would pick the old account back up.")}`);
|
|
753
1121
|
} else {
|
|
754
1122
|
console.log(` ${green(bold("Clean complete."))}`);
|
|
1123
|
+
if (keepShared) {
|
|
1124
|
+
console.log(` ${dim("The Mac app's wiring and its stored key are still here. Re-run with")} --everything ${dim("to remove them.")}`);
|
|
1125
|
+
}
|
|
755
1126
|
}
|
|
756
1127
|
console.log(` ${dim("Run")} npx @pushary/agent-hooks@latest setup ${dim("to reinstall.")}`);
|
|
757
|
-
|
|
1128
|
+
const exportedKey = process.env.PUSHARY_API_KEY?.trim();
|
|
1129
|
+
if (aliasRemovedFrom.length > 0 || exportedKey) {
|
|
758
1130
|
console.log();
|
|
759
|
-
console.log(` ${yellow("!")} ${bold("
|
|
1131
|
+
console.log(` ${yellow("!")} ${bold("In your current terminal")}`);
|
|
1132
|
+
}
|
|
1133
|
+
if (exportedKey) {
|
|
1134
|
+
console.log(` ${dim("PUSHARY_API_KEY is still exported here. Run")} ${bold("unset PUSHARY_API_KEY")}`);
|
|
1135
|
+
console.log(` ${dim("(or open a new terminal) before setup, or it signs back in as the old account.")}`);
|
|
1136
|
+
}
|
|
1137
|
+
if (aliasRemovedFrom.length > 0) {
|
|
760
1138
|
console.log(` ${dim("It still has the")} ${bold("claude")} ${dim("alias loaded from before. Run")} ${bold("unalias claude")}`);
|
|
761
1139
|
console.log(` ${dim("(or open a new terminal) so")} ${bold("claude")} ${dim("runs Claude Code again.")}`);
|
|
762
1140
|
}
|