clisponsor 1.0.11 → 1.0.15

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # CLIsponsor Hook
2
2
 
3
- Hook package codebase for the `clisponsor` installer and Codex, Claude Code, and Gemini hook templates.
3
+ Hook package codebase for the `clisponsor` installer and Codex, Claude Code, Gemini, Antigravity, OpenCode, Pi, GitHub Copilot CLI, and Qwen Code hook templates.
4
4
 
5
5
  This codebase owns local installation, device login, diagnostics, and hook adapters. It must not contain public website, dashboard app, API account, or ad-serving server code.
6
6
 
@@ -16,6 +16,30 @@ const DEFAULT_BACKEND_BASE_URL = process.env.CLISPONSOR_BACKEND_BASE_URL || "htt
16
16
  const HOOK_VERSION = "1.0.0";
17
17
  const NETWORK_TIMEOUT_MS = 3000;
18
18
  const ANTIGRAVITY_EVENTS = ["PreInvocation"];
19
+ const OPENCODE_NON_TUI_COMMANDS = new Set([
20
+ "completion",
21
+ "acp",
22
+ "mcp",
23
+ "run",
24
+ "debug",
25
+ "providers",
26
+ "auth",
27
+ "agent",
28
+ "upgrade",
29
+ "uninstall",
30
+ "serve",
31
+ "web",
32
+ "models",
33
+ "stats",
34
+ "export",
35
+ "import",
36
+ "github",
37
+ "pr",
38
+ "session",
39
+ "plugin",
40
+ "plug",
41
+ "db",
42
+ ]);
19
43
 
20
44
  function argValue(name) {
21
45
  const prefix = `${name}=`;
@@ -130,6 +154,22 @@ function chmodExecutable(file) {
130
154
  } catch {}
131
155
  }
132
156
 
157
+ function xdgConfigHome() {
158
+ return process.env.XDG_CONFIG_HOME || path.join(HOME, ".config");
159
+ }
160
+
161
+ function openCodeConfigDir() {
162
+ return process.env.OPENCODE_CONFIG_DIR || path.join(xdgConfigHome(), "opencode");
163
+ }
164
+
165
+ function copilotHome() {
166
+ return process.env.COPILOT_HOME || path.join(HOME, ".copilot");
167
+ }
168
+
169
+ function qwenHome() {
170
+ return process.env.QWEN_HOME || path.join(HOME, ".qwen");
171
+ }
172
+
133
173
  function commandExists(command) {
134
174
  const paths = String(process.env.PATH || "").split(path.delimiter).filter(Boolean);
135
175
  const extensions = process.platform === "win32" ? String(process.env.PATHEXT || ".EXE;.CMD;.BAT").split(";") : [""];
@@ -162,6 +202,10 @@ function isClisponsorCommand(value) {
162
202
  (value.includes("clisponsor_claude_hook.mjs") ||
163
203
  value.includes("clisponsor_gemini_hook.mjs") ||
164
204
  value.includes("clisponsor_antigravity_hook.mjs") ||
205
+ value.includes("clisponsor_opencode_plugin") ||
206
+ value.includes("clisponsor_pi_extension") ||
207
+ value.includes("clisponsor_copilot_hook.mjs") ||
208
+ value.includes("clisponsor_qwen_hook.mjs") ||
165
209
  value.includes(`${path.sep}.clisponsor${path.sep}`) ||
166
210
  value.includes("/.clisponsor/") ||
167
211
  value.includes("\\.clisponsor\\"))
@@ -235,6 +279,28 @@ function addGeminiCommandHook(settings, eventName, matcher, command) {
235
279
  return true;
236
280
  }
237
281
 
282
+ function addQwenCommandHook(settings, eventName, command) {
283
+ if (!isPlainObject(settings.hooks)) settings.hooks = {};
284
+ const current = Array.isArray(settings.hooks[eventName]) ? settings.hooks[eventName] : [];
285
+ const exists = current.some((entry) => isClisponsorHookEntry(entry));
286
+ if (exists) return false;
287
+
288
+ settings.hooks[eventName] = [
289
+ ...current,
290
+ {
291
+ hooks: [
292
+ {
293
+ type: "command",
294
+ command,
295
+ name: "clisponsor",
296
+ timeout: 5000,
297
+ },
298
+ ],
299
+ },
300
+ ];
301
+ return true;
302
+ }
303
+
238
304
  function removeClaudeCommandHooks(settings) {
239
305
  if (!isPlainObject(settings.hooks)) return false;
240
306
  let changed = false;
@@ -481,6 +547,270 @@ try {
481
547
  `;
482
548
  }
483
549
 
550
+ function openCodePluginSource() {
551
+ return `import fs from "node:fs";
552
+ import crypto from "node:crypto";
553
+
554
+ const CONFIG_PATH = ${JSON.stringify(CONFIG_PATH)};
555
+ const HOOK_VERSION = ${JSON.stringify(HOOK_VERSION)};
556
+ const NON_TUI_COMMANDS = new Set(${JSON.stringify([...OPENCODE_NON_TUI_COMMANDS])});
557
+
558
+ function readConfig() {
559
+ try {
560
+ return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
561
+ } catch {
562
+ return {};
563
+ }
564
+ }
565
+
566
+ function likelyTuiInvocation() {
567
+ const args = process.argv.slice(2).filter((arg) => !arg.startsWith("-"));
568
+ if (args.length === 0) return true;
569
+ return !NON_TUI_COMMANDS.has(args[0]);
570
+ }
571
+
572
+ function sponsoredLine(line) {
573
+ return "[Sponsored] " + line;
574
+ }
575
+
576
+ export const CLIsponsorOpenCodePlugin = async ({ client }) => {
577
+ if (!likelyTuiInvocation()) return {};
578
+ let startSessionRequested = false;
579
+
580
+ async function serve(event, placement, metadata = {}) {
581
+ if (placement === "StartSession") {
582
+ if (startSessionRequested) return;
583
+ startSessionRequested = true;
584
+ }
585
+ const cfg = readConfig();
586
+ const serveBaseUrl = cfg.serveBaseUrl || cfg.apiBaseUrl;
587
+ if (!serveBaseUrl || !cfg.userId || !cfg.deviceCode || !cfg.deviceSecret) return;
588
+
589
+ try {
590
+ const res = await fetch(serveBaseUrl + "/v1/ads/serve", {
591
+ method: "POST",
592
+ headers: {
593
+ "content-type": "application/json",
594
+ "authorization": "Bearer " + cfg.deviceSecret,
595
+ "x-clisponsor-hook-version": HOOK_VERSION,
596
+ },
597
+ body: JSON.stringify({
598
+ user_id: cfg.userId,
599
+ device_code: cfg.deviceCode,
600
+ client: "OpenCode",
601
+ hook_event: event,
602
+ placement,
603
+ idempotency_key: crypto.randomUUID(),
604
+ metadata: {
605
+ hookVersion: HOOK_VERSION,
606
+ openCode: metadata,
607
+ },
608
+ }),
609
+ });
610
+ if (!res.ok) return;
611
+ const ad = await res.json();
612
+ if (!ad.display_line) return;
613
+ await client.tui.showToast({
614
+ body: {
615
+ title: "CLIsponsor " + placement,
616
+ message: sponsoredLine(ad.display_line),
617
+ variant: "info",
618
+ duration: 10000,
619
+ },
620
+ });
621
+ } catch {}
622
+ }
623
+
624
+ const startupTimer = setTimeout(() => {
625
+ void serve("plugin.start", "StartSession");
626
+ }, 2500);
627
+ startupTimer.unref?.();
628
+
629
+ return {
630
+ event: async ({ event }) => {
631
+ if (event.type === "session.created") {
632
+ await serve("session.created", "StartSession", {
633
+ sessionID: event.properties?.sessionID,
634
+ });
635
+ } else if (event.type === "session.idle") {
636
+ await serve("session.idle", "EndTurn", {
637
+ sessionID: event.properties?.sessionID,
638
+ });
639
+ }
640
+ },
641
+ "chat.message": async (input) => {
642
+ await serve("chat.message", "StartTurn", {
643
+ sessionID: input.sessionID,
644
+ agent: input.agent,
645
+ });
646
+ },
647
+ };
648
+ };
649
+ `;
650
+ }
651
+
652
+ function piExtensionSource() {
653
+ return `import fs from "node:fs";
654
+ import crypto from "node:crypto";
655
+
656
+ const CONFIG_PATH = ${JSON.stringify(CONFIG_PATH)};
657
+ const HOOK_VERSION = ${JSON.stringify(HOOK_VERSION)};
658
+
659
+ function readConfig() {
660
+ try {
661
+ return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
662
+ } catch {
663
+ return {};
664
+ }
665
+ }
666
+
667
+ function sponsoredLine(line) {
668
+ return "[Sponsored] " + line;
669
+ }
670
+
671
+ function canShow(ctx) {
672
+ return Boolean(ctx?.hasUI && ctx?.ui?.notify);
673
+ }
674
+
675
+ export default function CLIsponsorPiExtension(pi) {
676
+ async function serve(event, placement, ctx, metadata = {}) {
677
+ if (!canShow(ctx)) return;
678
+ const cfg = readConfig();
679
+ const serveBaseUrl = cfg.serveBaseUrl || cfg.apiBaseUrl;
680
+ if (!serveBaseUrl || !cfg.userId || !cfg.deviceCode || !cfg.deviceSecret) return;
681
+
682
+ try {
683
+ const res = await fetch(serveBaseUrl + "/v1/ads/serve", {
684
+ method: "POST",
685
+ headers: {
686
+ "content-type": "application/json",
687
+ "authorization": "Bearer " + cfg.deviceSecret,
688
+ "x-clisponsor-hook-version": HOOK_VERSION,
689
+ },
690
+ body: JSON.stringify({
691
+ user_id: cfg.userId,
692
+ device_code: cfg.deviceCode,
693
+ client: "Pi",
694
+ hook_event: event,
695
+ placement,
696
+ idempotency_key: crypto.randomUUID(),
697
+ metadata: {
698
+ hookVersion: HOOK_VERSION,
699
+ pi: metadata,
700
+ },
701
+ }),
702
+ });
703
+ if (!res.ok) return;
704
+ const ad = await res.json();
705
+ if (!ad.display_line) return;
706
+ ctx.ui.notify("CLIsponsor " + placement + "\\n" + sponsoredLine(ad.display_line), "info");
707
+ } catch {}
708
+ }
709
+
710
+ pi.on("session_start", async (event, ctx) => {
711
+ await serve("session_start", "StartSession", ctx, {
712
+ reason: event?.reason,
713
+ });
714
+ });
715
+
716
+ pi.on("agent_start", async (_event, ctx) => {
717
+ await serve("agent_start", "StartTurn", ctx);
718
+ });
719
+
720
+ pi.on("agent_end", async (_event, ctx) => {
721
+ await serve("agent_end", "EndTurn", ctx);
722
+ });
723
+ }
724
+ `;
725
+ }
726
+
727
+ function copilotHookSource() {
728
+ return `#!/usr/bin/env node
729
+ import fs from "node:fs";
730
+ import crypto from "node:crypto";
731
+
732
+ const event = process.argv[2] || "userPromptSubmitted";
733
+ const CONFIG_PATH = ${JSON.stringify(CONFIG_PATH)};
734
+ const HOOK_VERSION = ${JSON.stringify(HOOK_VERSION)};
735
+ const placements = {
736
+ sessionStart: "StartSession",
737
+ userPromptSubmitted: "StartTurn",
738
+ agentStop: "EndTurn",
739
+ };
740
+
741
+ function sponsoredLine(line) {
742
+ return "[Sponsored] " + line;
743
+ }
744
+
745
+ function readStdin() {
746
+ return new Promise((resolve) => {
747
+ let data = "";
748
+ process.stdin.on("data", (chunk) => (data += chunk));
749
+ process.stdin.on("end", () => resolve(data));
750
+ });
751
+ }
752
+
753
+ function readConfig() {
754
+ try {
755
+ return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
756
+ } catch {
757
+ return {};
758
+ }
759
+ }
760
+
761
+ function progress(message) {
762
+ console.log(JSON.stringify({ type: "progress", message }));
763
+ }
764
+
765
+ const inputRaw = await readStdin();
766
+ let input = {};
767
+ try {
768
+ input = inputRaw.trim() ? JSON.parse(inputRaw) : {};
769
+ } catch {}
770
+
771
+ try {
772
+ const placement = placements[event];
773
+ if (!placement) process.exit(0);
774
+ const cfg = readConfig();
775
+ const serveBaseUrl = cfg.serveBaseUrl || cfg.apiBaseUrl;
776
+ if (!serveBaseUrl || !cfg.userId || !cfg.deviceCode || !cfg.deviceSecret) process.exit(0);
777
+
778
+ const res = await fetch(serveBaseUrl + "/v1/ads/serve", {
779
+ method: "POST",
780
+ headers: {
781
+ "content-type": "application/json",
782
+ "authorization": "Bearer " + cfg.deviceSecret,
783
+ "x-clisponsor-hook-version": HOOK_VERSION,
784
+ },
785
+ body: JSON.stringify({
786
+ user_id: cfg.userId,
787
+ device_code: cfg.deviceCode,
788
+ client: "GitHubCopilot",
789
+ hook_event: event,
790
+ placement,
791
+ idempotency_key: crypto.randomUUID(),
792
+ metadata: {
793
+ hookVersion: HOOK_VERSION,
794
+ copilot: {
795
+ sessionId: input.sessionId || input.session_id,
796
+ source: input.source,
797
+ reason: input.reason,
798
+ stopReason: input.stopReason || input.stop_reason,
799
+ },
800
+ },
801
+ }),
802
+ });
803
+ if (!res.ok) process.exit(0);
804
+ const ad = await res.json();
805
+ if (!ad.display_line) process.exit(0);
806
+ progress("CLIsponsor " + placement + ": " + sponsoredLine(ad.display_line));
807
+ console.log(JSON.stringify({}));
808
+ } catch {
809
+ process.exit(0);
810
+ }
811
+ `;
812
+ }
813
+
484
814
  function installGemini() {
485
815
  if (!commandExists("gemini")) {
486
816
  console.log("Gemini CLI not found. To enable CLIsponsor for Gemini, install Gemini CLI and rerun: npx clisponsor@latest install");
@@ -521,23 +851,132 @@ function installAntigravity() {
521
851
  console.log("Antigravity CLI hook installed.");
522
852
  }
523
853
 
854
+ function installOpenCode() {
855
+ if (!commandExists("opencode")) {
856
+ console.log("OpenCode CLI not found. To enable CLIsponsor for OpenCode, install OpenCode CLI and rerun: npx clisponsor@latest install");
857
+ return;
858
+ }
859
+
860
+ const opencodeDir = path.join(CONFIG_DIR, "opencode");
861
+ fs.mkdirSync(opencodeDir, { recursive: true });
862
+ const stagedPlugin = path.join(opencodeDir, "clisponsor_opencode_plugin.js");
863
+ fs.writeFileSync(stagedPlugin, openCodePluginSource(), { mode: 0o644 });
864
+
865
+ const pluginsDir = path.join(openCodeConfigDir(), "plugins");
866
+ fs.mkdirSync(pluginsDir, { recursive: true });
867
+ const installedPlugin = path.join(pluginsDir, "clisponsor_opencode_plugin.js");
868
+ fs.copyFileSync(stagedPlugin, installedPlugin);
869
+ console.log(`Updated ${installedPlugin}`);
870
+ console.log("OpenCode CLI plugin installed.");
871
+ }
872
+
873
+ function installPi() {
874
+ if (!commandExists("pi")) {
875
+ console.log("Pi CLI not found. To enable CLIsponsor for Pi, install Pi Coding Agent and rerun: npx clisponsor@latest install");
876
+ return;
877
+ }
878
+
879
+ const piDir = path.join(CONFIG_DIR, "pi");
880
+ fs.mkdirSync(piDir, { recursive: true });
881
+ const stagedExtension = path.join(piDir, "clisponsor_pi_extension.ts");
882
+ fs.writeFileSync(stagedExtension, piExtensionSource(), { mode: 0o644 });
883
+
884
+ const extensionsDir = path.join(HOME, ".pi", "agent", "extensions");
885
+ fs.mkdirSync(extensionsDir, { recursive: true });
886
+ const installedExtension = path.join(extensionsDir, "clisponsor_pi_extension.ts");
887
+ fs.copyFileSync(stagedExtension, installedExtension);
888
+ console.log(`Updated ${installedExtension}`);
889
+ console.log("Pi CLI extension installed.");
890
+ }
891
+
892
+ function installCopilot() {
893
+ if (!commandExists("copilot")) {
894
+ console.log("GitHub Copilot CLI not found. To enable CLIsponsor for GitHub Copilot CLI, install Copilot CLI and rerun: npx clisponsor@latest install");
895
+ return;
896
+ }
897
+
898
+ const copilotDir = path.join(CONFIG_DIR, "copilot");
899
+ fs.mkdirSync(copilotDir, { recursive: true });
900
+ const hookPath = path.join(copilotDir, "clisponsor_copilot_hook.mjs");
901
+ fs.writeFileSync(hookPath, copilotHookSource(), { mode: 0o755 });
902
+
903
+ const hooksPath = path.join(copilotHome(), "hooks", "clisponsor.json");
904
+ writeJson(hooksPath, {
905
+ version: 1,
906
+ hooks: {
907
+ sessionStart: [
908
+ {
909
+ type: "command",
910
+ command: `node ${JSON.stringify(hookPath)} sessionStart`,
911
+ timeoutSec: 5,
912
+ },
913
+ ],
914
+ userPromptSubmitted: [
915
+ {
916
+ type: "command",
917
+ command: `node ${JSON.stringify(hookPath)} userPromptSubmitted`,
918
+ timeoutSec: 5,
919
+ },
920
+ ],
921
+ agentStop: [
922
+ {
923
+ type: "command",
924
+ command: `node ${JSON.stringify(hookPath)} agentStop`,
925
+ timeoutSec: 5,
926
+ },
927
+ ],
928
+ },
929
+ });
930
+ console.log(`Updated ${hooksPath}`);
931
+ console.log("GitHub Copilot CLI hook installed.");
932
+ }
933
+
934
+ function installQwen() {
935
+ if (!commandExists("qwen")) {
936
+ console.log("Qwen Code CLI not found. To enable CLIsponsor for Qwen Code, install Qwen Code and rerun: npx clisponsor@latest install");
937
+ return;
938
+ }
939
+
940
+ const qwenDir = path.join(CONFIG_DIR, "qwen");
941
+ fs.mkdirSync(qwenDir, { recursive: true });
942
+ const hookPath = path.join(qwenDir, "clisponsor_qwen_hook.mjs");
943
+ fs.writeFileSync(hookPath, agentHookSource("QwenCode"), { mode: 0o755 });
944
+
945
+ const settingsPath = path.join(qwenHome(), "settings.json");
946
+ const settings = readEditableJson(settingsPath, {});
947
+ addQwenCommandHook(settings, "SessionStart", `node ${JSON.stringify(hookPath)} SessionStart`);
948
+ addQwenCommandHook(settings, "UserPromptSubmit", `node ${JSON.stringify(hookPath)} UserPromptSubmit`);
949
+ addQwenCommandHook(settings, "Stop", `node ${JSON.stringify(hookPath)} Stop`);
950
+ writeJson(settingsPath, settings);
951
+ console.log(`Updated ${settingsPath}`);
952
+ console.log("Qwen Code CLI hook installed.");
953
+ }
954
+
524
955
  function installAll() {
525
956
  installCodex();
526
957
  installClaude();
527
958
  installGemini();
528
959
  installAntigravity();
960
+ installOpenCode();
961
+ installPi();
962
+ installCopilot();
963
+ installQwen();
529
964
  }
530
965
 
531
966
  function install() {
532
967
  const target = process.argv[3] && !process.argv[3].startsWith("--") ? process.argv[3] : "all";
533
- if (!["all", "codex", "claude", "gemini", "antigravity", "agy"].includes(target)) {
534
- console.error("Unknown install target. Use: codex, claude, gemini, antigravity, or all.");
968
+ if (!["all", "codex", "claude", "gemini", "antigravity", "agy", "opencode", "pi", "copilot", "qwen"].includes(target)) {
969
+ console.error("Unknown install target. Use: codex, claude, gemini, antigravity, opencode, pi, copilot, qwen, or all.");
535
970
  process.exit(1);
536
971
  }
537
972
  if (target === "all") installAll();
538
973
  else if (target === "codex") installCodex();
539
974
  else if (target === "claude") installClaude();
540
975
  else if (target === "gemini") installGemini();
976
+ else if (target === "opencode") installOpenCode();
977
+ else if (target === "pi") installPi();
978
+ else if (target === "copilot") installCopilot();
979
+ else if (target === "qwen") installQwen();
541
980
  else installAntigravity();
542
981
  }
543
982
 
@@ -591,16 +1030,69 @@ function uninstallAntigravity() {
591
1030
  console.log("Removed Antigravity hook script.");
592
1031
  }
593
1032
 
1033
+ function uninstallOpenCode() {
1034
+ const installedPlugin = path.join(openCodeConfigDir(), "plugins", "clisponsor_opencode_plugin.js");
1035
+ if (fs.existsSync(installedPlugin)) {
1036
+ fs.rmSync(installedPlugin, { force: true });
1037
+ console.log(`Removed ${installedPlugin}`);
1038
+ } else {
1039
+ console.log("No CLIsponsor OpenCode plugin found.");
1040
+ }
1041
+ fs.rmSync(path.join(CONFIG_DIR, "opencode"), { recursive: true, force: true });
1042
+ }
1043
+
1044
+ function uninstallPi() {
1045
+ const installedExtension = path.join(HOME, ".pi", "agent", "extensions", "clisponsor_pi_extension.ts");
1046
+ if (fs.existsSync(installedExtension)) {
1047
+ fs.rmSync(installedExtension, { force: true });
1048
+ console.log(`Removed ${installedExtension}`);
1049
+ } else {
1050
+ console.log("No CLIsponsor Pi extension found.");
1051
+ }
1052
+ fs.rmSync(path.join(CONFIG_DIR, "pi"), { recursive: true, force: true });
1053
+ }
1054
+
1055
+ function uninstallCopilot() {
1056
+ const hooksPath = path.join(copilotHome(), "hooks", "clisponsor.json");
1057
+ if (fs.existsSync(hooksPath)) {
1058
+ fs.rmSync(hooksPath, { force: true });
1059
+ console.log(`Removed ${hooksPath}`);
1060
+ } else {
1061
+ console.log("No CLIsponsor GitHub Copilot CLI hook found.");
1062
+ }
1063
+ fs.rmSync(path.join(CONFIG_DIR, "copilot"), { recursive: true, force: true });
1064
+ }
1065
+
1066
+ function uninstallQwen() {
1067
+ const settingsPath = path.join(qwenHome(), "settings.json");
1068
+ const settings = readEditableJson(settingsPath, {});
1069
+ if (removeClaudeCommandHooks(settings)) {
1070
+ writeJson(settingsPath, settings);
1071
+ console.log(`Removed CLIsponsor hooks from ${settingsPath}`);
1072
+ } else {
1073
+ console.log("No CLIsponsor Qwen Code hooks found.");
1074
+ }
1075
+ fs.rmSync(path.join(CONFIG_DIR, "qwen", "clisponsor_qwen_hook.mjs"), { force: true });
1076
+ try {
1077
+ fs.rmdirSync(path.join(CONFIG_DIR, "qwen"));
1078
+ } catch {}
1079
+ console.log("Removed Qwen Code hook script.");
1080
+ }
1081
+
594
1082
  function uninstall() {
595
1083
  const target = process.argv[3] && !process.argv[3].startsWith("--") ? process.argv[3] : "all";
596
- if (!["all", "codex", "claude", "gemini", "antigravity", "agy"].includes(target)) {
597
- console.error("Unknown uninstall target. Use: codex, claude, gemini, antigravity, or all.");
1084
+ if (!["all", "codex", "claude", "gemini", "antigravity", "agy", "opencode", "pi", "copilot", "qwen"].includes(target)) {
1085
+ console.error("Unknown uninstall target. Use: codex, claude, gemini, antigravity, opencode, pi, copilot, qwen, or all.");
598
1086
  process.exit(1);
599
1087
  }
600
1088
  if (target === "all" || target === "codex") uninstallCodex();
601
1089
  if (target === "all" || target === "claude") uninstallClaude();
602
1090
  if (target === "all" || target === "gemini") uninstallGemini();
603
1091
  if (target === "all" || target === "antigravity" || target === "agy") uninstallAntigravity();
1092
+ if (target === "all" || target === "opencode") uninstallOpenCode();
1093
+ if (target === "all" || target === "pi") uninstallPi();
1094
+ if (target === "all" || target === "copilot") uninstallCopilot();
1095
+ if (target === "all" || target === "qwen") uninstallQwen();
604
1096
  if (hasFlag("--config")) {
605
1097
  fs.rmSync(CONFIG_PATH, { force: true });
606
1098
  console.log(`Removed ${CONFIG_PATH}`);
@@ -660,6 +1152,10 @@ async function doctor() {
660
1152
  claudeHookScript: fs.existsSync(path.join(CONFIG_DIR, "claude", "clisponsor_claude_hook.mjs")),
661
1153
  geminiHookScript: fs.existsSync(path.join(CONFIG_DIR, "gemini", "clisponsor_gemini_hook.mjs")),
662
1154
  antigravityHookScript: fs.existsSync(path.join(CONFIG_DIR, "antigravity", "clisponsor_antigravity_hook.mjs")),
1155
+ opencodePlugin: fs.existsSync(path.join(openCodeConfigDir(), "plugins", "clisponsor_opencode_plugin.js")),
1156
+ piExtension: fs.existsSync(path.join(HOME, ".pi", "agent", "extensions", "clisponsor_pi_extension.ts")),
1157
+ copilotHook: fs.existsSync(path.join(copilotHome(), "hooks", "clisponsor.json")),
1158
+ qwenHookScript: fs.existsSync(path.join(CONFIG_DIR, "qwen", "clisponsor_qwen_hook.mjs")),
663
1159
  },
664
1160
  network: {},
665
1161
  };
@@ -687,6 +1183,10 @@ async function doctor() {
687
1183
  console.log(`Claude hook script: ${diagnostics.installed.claudeHookScript ? "yes" : "no"}`);
688
1184
  console.log(`Gemini hook script: ${diagnostics.installed.geminiHookScript ? "yes" : "no"}`);
689
1185
  console.log(`Antigravity hook script: ${diagnostics.installed.antigravityHookScript ? "yes" : "no"}`);
1186
+ console.log(`OpenCode plugin: ${diagnostics.installed.opencodePlugin ? "yes" : "no"}`);
1187
+ console.log(`Pi extension: ${diagnostics.installed.piExtension ? "yes" : "no"}`);
1188
+ console.log(`GitHub Copilot CLI hook: ${diagnostics.installed.copilotHook ? "yes" : "no"}`);
1189
+ console.log(`Qwen Code hook script: ${diagnostics.installed.qwenHookScript ? "yes" : "no"}`);
690
1190
  if (skipNetwork) {
691
1191
  console.log("Network: skipped");
692
1192
  } else {
@@ -697,9 +1197,9 @@ async function doctor() {
697
1197
 
698
1198
  function help() {
699
1199
  console.log(`clisponsor commands:
700
- clisponsor install [all|codex|claude|gemini|antigravity]
1200
+ clisponsor install [all|codex|claude|gemini|antigravity|opencode|pi|copilot|qwen]
701
1201
  clisponsor login <email> [--label=<device-label>]
702
- clisponsor uninstall [all|codex|claude|gemini|antigravity] [--config]
1202
+ clisponsor uninstall [all|codex|claude|gemini|antigravity|opencode|pi|copilot|qwen] [--config]
703
1203
  clisponsor status
704
1204
  clisponsor doctor [--json] [--skip-network]
705
1205
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clisponsor",
3
- "version": "1.0.11",
4
- "description": "CLIsponsor installer for Codex, Claude Code, and Gemini sponsored CLI placements.",
3
+ "version": "1.0.15",
4
+ "description": "CLIsponsor installer for Codex, Claude Code, Gemini, Antigravity, OpenCode, Pi, GitHub Copilot CLI, and Qwen Code sponsored CLI placements.",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=20"