set-prompt 0.3.0 → 0.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 +21 -0
- package/README.md +10 -6
- package/dist/index.js +471 -103
- package/package.json +4 -5
- package/dist/bin/set-prompt.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.4.0] - 2026-04-10
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Cursor integration (`link cursor`) — creates plugin structure at `~/.cursor/` with backup/restore
|
|
11
|
+
- `linkCommand` now shows a Link / Unlink summary before executing; exits early when there are no changes
|
|
12
|
+
- All `unlink*` functions print a colored header banner on start (consistent with `link*`)
|
|
13
|
+
- Console output: `removed` → red, `restored` → green, `backed up` → yellow
|
|
14
|
+
- Console output: `Config loaded` is now green (consistent with `Config saved`)
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Claude Code plugin: `~/.claude/plugins/installed_plugins.json` is now directly patched so `installPath` points to the source plugin directory — bypasses Claude Code's cache management that was deleting the symlink on startup
|
|
18
|
+
- `install`: re-installing the same URL is blocked with a `set-prompt update` hint
|
|
19
|
+
- `install`: re-installing a different URL shows a "Switching repo" warning with confirmation (default: No)
|
|
20
|
+
- `install`: EPERM on directory rename is handled gracefully with a descriptive error message
|
|
21
|
+
- `install`: existing repo backup is automatically removed after a successful clone
|
|
22
|
+
|
|
23
|
+
### Disabled
|
|
24
|
+
- `link codex` is temporarily unavailable in this release (implementation preserved, re-enable in next release)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
7
28
|
## [0.3.0] - 2026-04-06
|
|
8
29
|
|
|
9
30
|
### Added
|
package/README.md
CHANGED
|
@@ -80,16 +80,18 @@ set-prompt link claudecode # link Claude Code only
|
|
|
80
80
|
set-prompt link roocode # link RooCode only
|
|
81
81
|
set-prompt link openclaw # link OpenClaw only
|
|
82
82
|
set-prompt link antigravity # link Antigravity only
|
|
83
|
+
set-prompt link cursor # link Cursor only
|
|
83
84
|
```
|
|
84
85
|
|
|
85
86
|
The interactive mode shows all agents with their current state. **Check to link, uncheck to unlink** — existing directories are backed up before being replaced.
|
|
86
87
|
|
|
87
88
|
| Agent | Where prompts land | What gets linked |
|
|
88
89
|
|---|---|---|
|
|
89
|
-
| Claude Code | `~/.set-prompt/
|
|
90
|
+
| Claude Code | `~/.set-prompt/claude-code/` (plugin) | `skills/`, `commands/`, `hooks/`, `agents/` |
|
|
90
91
|
| RooCode | `~/.roo/` | `skills/`, `commands/` |
|
|
91
92
|
| OpenClaw | `~/.openclaw/workspace/` | `skills/` |
|
|
92
93
|
| Antigravity | `~/.gemini/antigravity/` | `skills/` |
|
|
94
|
+
| Cursor | `~/.cursor/` (plugin) | `skills/`, `commands/` |
|
|
93
95
|
|
|
94
96
|
---
|
|
95
97
|
|
|
@@ -141,9 +143,10 @@ npm test # Run tests with vitest
|
|
|
141
143
|
|
|
142
144
|
`set-prompt` modifies configuration files managed by third-party AI agent applications:
|
|
143
145
|
|
|
144
|
-
- **Claude Code** — writes to `~/.claude/settings.json`
|
|
146
|
+
- **Claude Code** — writes to `~/.claude/settings.json` and `~/.claude/plugins/installed_plugins.json`
|
|
145
147
|
- **RooCode** — replaces directories in `~/.roo/`
|
|
146
148
|
- **OpenClaw** — replaces directories in `~/.openclaw/workspace/`
|
|
149
|
+
- **Cursor** — writes to `~/.cursor/`
|
|
147
150
|
|
|
148
151
|
Before making any changes, `set-prompt` creates a backup and rolls back automatically on failure. However, you should be aware that:
|
|
149
152
|
|
|
@@ -164,14 +167,15 @@ Stored at `~/.set-prompt/config.json`, managed via `ConfigManager`.
|
|
|
164
167
|
│ ├── skills/
|
|
165
168
|
│ ├── commands/
|
|
166
169
|
│ └── hooks/
|
|
167
|
-
└──
|
|
170
|
+
└── claude-code/ # Claude Code plugin output
|
|
168
171
|
├── .claude-plugin/
|
|
169
172
|
│ └── marketplace.json
|
|
170
|
-
└── plugins/
|
|
173
|
+
└── plugins/sppt/
|
|
171
174
|
├── .claude-plugin/plugin.json
|
|
172
175
|
├── skills/ → symlink to repo/skills/
|
|
173
176
|
├── commands/ → symlink to repo/commands/
|
|
174
|
-
|
|
177
|
+
├── hooks/ → symlink to repo/hooks/
|
|
178
|
+
└── agents/ → symlink to repo/agents/
|
|
175
179
|
|
|
176
180
|
~/.roo/ # RooCode integration (symlinks)
|
|
177
181
|
├── SET_PROMPT_BACKUP/ # backup of original dirs before linking
|
|
@@ -195,7 +199,7 @@ Stored at `~/.set-prompt/config.json`, managed via `ConfigManager`.
|
|
|
195
199
|
|
|
196
200
|
- **Node.js** 18+
|
|
197
201
|
- **Git** (must be available in `PATH` for `set-prompt install`)
|
|
198
|
-
- **Windows**: symlink creation requires
|
|
202
|
+
- **Windows only**: symlink creation requires Developer Mode enabled or running as Administrator (Linux/macOS work out of the box)
|
|
199
203
|
|
|
200
204
|
## 🤝 Contributing
|
|
201
205
|
|
package/dist/index.js
CHANGED
|
@@ -29,20 +29,26 @@ var OPENCLAW_DIR = path.join(os.homedir(), ".openclaw", "workspace");
|
|
|
29
29
|
var OPENCLAW_BACKUP_DIR = path.join(OPENCLAW_DIR, "SET_PROMPT_BACKUP");
|
|
30
30
|
var ANTIGRAVITY_DIR = path.join(os.homedir(), ".gemini", "antigravity");
|
|
31
31
|
var ANTIGRAVITY_BACKUP_DIR = path.join(ANTIGRAVITY_DIR, "SET_PROMPT_BACKUP");
|
|
32
|
+
var CODEX_DIR = path.join(os.homedir(), ".codex");
|
|
33
|
+
var CODEX_BACKUP_DIR = path.join(CODEX_DIR, "SET_PROMPT_BACKUP");
|
|
34
|
+
var CURSOR_DIR = path.join(HOME_DIR, "cursor");
|
|
35
|
+
var CURSOR_PLUGIN_DIR = path.join(os.homedir(), ".cursor", "plugins", "local", "set-prompt");
|
|
32
36
|
var PROMPT_DIR_NAMES = ["skills", "commands", "hooks", "agents"];
|
|
33
37
|
var AGENT_PROMPT_DIRS = {
|
|
34
38
|
["claudecode" /* CLAUDECODE */]: ["skills", "commands", "hooks", "agents"],
|
|
35
39
|
["roocode" /* ROOCODE */]: ["skills", "commands"],
|
|
36
40
|
["openclaw" /* OPENCLAW */]: ["skills"],
|
|
37
41
|
["codex" /* CODEX */]: ["skills", "commands"],
|
|
38
|
-
["antigravity" /* ANTIGRAVITY */]: ["skills"]
|
|
42
|
+
["antigravity" /* ANTIGRAVITY */]: ["skills"],
|
|
43
|
+
["cursor" /* CURSOR */]: ["skills", "agents", "rules"]
|
|
39
44
|
};
|
|
40
45
|
var ALL_AGENTS = [
|
|
41
46
|
{ name: "Claude Code", value: "claudecode" /* CLAUDECODE */ },
|
|
42
47
|
{ name: "RooCode", value: "roocode" /* ROOCODE */ },
|
|
43
48
|
{ name: "OpenClaw", value: "openclaw" /* OPENCLAW */ },
|
|
44
49
|
{ name: "Codex", value: "codex" /* CODEX */ },
|
|
45
|
-
{ name: "Antigravity", value: "antigravity" /* ANTIGRAVITY */ }
|
|
50
|
+
{ name: "Antigravity", value: "antigravity" /* ANTIGRAVITY */ },
|
|
51
|
+
{ name: "Cursor", value: "cursor" /* CURSOR */ }
|
|
46
52
|
];
|
|
47
53
|
|
|
48
54
|
// src/_libs/config.ts
|
|
@@ -65,12 +71,19 @@ var OpenclawConfigSchema = z.object({
|
|
|
65
71
|
backup_path: z.string().nullish().optional()
|
|
66
72
|
});
|
|
67
73
|
var CodexConfigSchema = z.object({
|
|
68
|
-
path: z.string().nullable()
|
|
74
|
+
path: z.string().nullable(),
|
|
75
|
+
backup_path: z.string().nullish().optional()
|
|
69
76
|
});
|
|
70
77
|
var AntigravityConfigSchema = z.object({
|
|
71
78
|
path: z.string().nullable(),
|
|
72
79
|
backup_path: z.string().nullish().optional()
|
|
73
80
|
});
|
|
81
|
+
var CursorConfigSchema = z.object({
|
|
82
|
+
path: z.string().nullable(),
|
|
83
|
+
// ~/.set-prompt/cursor (플러그인 파일 구조)
|
|
84
|
+
plugin_dir: z.string().nullable()
|
|
85
|
+
// ~/.cursor/plugins/set-prompt (설치 symlink)
|
|
86
|
+
});
|
|
74
87
|
var GlobalConfigSchema = z.object({
|
|
75
88
|
repo_path: z.string(),
|
|
76
89
|
remote_url: z.string().nullable(),
|
|
@@ -78,7 +91,8 @@ var GlobalConfigSchema = z.object({
|
|
|
78
91
|
roocode: RoocodeConfigSchema.nullable(),
|
|
79
92
|
openclaw: OpenclawConfigSchema.nullable(),
|
|
80
93
|
codex: CodexConfigSchema.nullish().optional(),
|
|
81
|
-
antigravity: AntigravityConfigSchema.nullish().optional()
|
|
94
|
+
antigravity: AntigravityConfigSchema.nullish().optional(),
|
|
95
|
+
cursor: CursorConfigSchema.nullish().optional()
|
|
82
96
|
});
|
|
83
97
|
|
|
84
98
|
// src/_libs/config.ts
|
|
@@ -91,6 +105,7 @@ var ConfigManager = class {
|
|
|
91
105
|
this._openclaw = null;
|
|
92
106
|
this._codex = null;
|
|
93
107
|
this._antigravity = null;
|
|
108
|
+
this._cursor = null;
|
|
94
109
|
}
|
|
95
110
|
get repo_path() {
|
|
96
111
|
return this._repo_path;
|
|
@@ -113,6 +128,9 @@ var ConfigManager = class {
|
|
|
113
128
|
get antigravity() {
|
|
114
129
|
return this._antigravity;
|
|
115
130
|
}
|
|
131
|
+
get cursor() {
|
|
132
|
+
return this._cursor;
|
|
133
|
+
}
|
|
116
134
|
set repo_path(v) {
|
|
117
135
|
this._repo_path = v;
|
|
118
136
|
}
|
|
@@ -134,10 +152,13 @@ var ConfigManager = class {
|
|
|
134
152
|
set antigravity(v) {
|
|
135
153
|
this._antigravity = v;
|
|
136
154
|
}
|
|
155
|
+
set cursor(v) {
|
|
156
|
+
this._cursor = v;
|
|
157
|
+
}
|
|
137
158
|
init() {
|
|
138
159
|
this._loadFromDisk();
|
|
139
160
|
if (this._repo_path != null) {
|
|
140
|
-
console.log(chalk.
|
|
161
|
+
console.log(chalk.green(`Config loaded`) + chalk.dim(` from ${CONFIG_PATH}`));
|
|
141
162
|
}
|
|
142
163
|
}
|
|
143
164
|
save() {
|
|
@@ -154,7 +175,8 @@ var ConfigManager = class {
|
|
|
154
175
|
roocode: this._roocode,
|
|
155
176
|
openclaw: this._openclaw,
|
|
156
177
|
codex: this._codex,
|
|
157
|
-
antigravity: this._antigravity
|
|
178
|
+
antigravity: this._antigravity,
|
|
179
|
+
cursor: this._cursor
|
|
158
180
|
}, null, 4);
|
|
159
181
|
fs.writeFileSync(CONFIG_PATH, configStr, "utf-8");
|
|
160
182
|
console.log(chalk.green(`Config saved`) + chalk.dim(` \u2192 ${CONFIG_PATH}`));
|
|
@@ -188,6 +210,9 @@ var ConfigManager = class {
|
|
|
188
210
|
isAntigravityEnabled() {
|
|
189
211
|
return this._antigravity != null;
|
|
190
212
|
}
|
|
213
|
+
isCursorEnabled() {
|
|
214
|
+
return this._cursor != null;
|
|
215
|
+
}
|
|
191
216
|
_assign(config) {
|
|
192
217
|
this._repo_path = config.repo_path;
|
|
193
218
|
this._remote_url = config.remote_url;
|
|
@@ -196,6 +221,7 @@ var ConfigManager = class {
|
|
|
196
221
|
this._openclaw = config.openclaw;
|
|
197
222
|
this._codex = config.codex ?? null;
|
|
198
223
|
this._antigravity = config.antigravity ?? null;
|
|
224
|
+
this._cursor = config.cursor ?? null;
|
|
199
225
|
}
|
|
200
226
|
_loadFromDisk() {
|
|
201
227
|
if (fs.existsSync(CONFIG_PATH) === false) {
|
|
@@ -563,21 +589,23 @@ var scaffoldCommand = async (localPath, options = {}) => {
|
|
|
563
589
|
|
|
564
590
|
// src/commands/install-command.ts
|
|
565
591
|
var cloneRepo = async (remoteUrl) => {
|
|
566
|
-
const proceed = await confirm2({
|
|
567
|
-
message: `Clone and register "${remoteUrl}"?`,
|
|
568
|
-
default: true
|
|
569
|
-
});
|
|
570
|
-
if (proceed == false) {
|
|
571
|
-
console.log(chalk3.yellow("Cancelled."));
|
|
572
|
-
return false;
|
|
573
|
-
}
|
|
574
592
|
const localPath = path3.join(HOME_DIR, "repo");
|
|
593
|
+
let backupPath = null;
|
|
575
594
|
if (fs3.existsSync(localPath) == true) {
|
|
576
|
-
console.warn(chalk3.yellow(`Existing repo found. Backing up before proceeding.`));
|
|
577
595
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
596
|
+
backupPath = path3.join(HOME_DIR, `repo.bak.${timestamp}`);
|
|
597
|
+
try {
|
|
598
|
+
fs3.renameSync(localPath, backupPath);
|
|
599
|
+
console.log(chalk3.yellow(" backed up") + chalk3.dim(` existing repo \u2192 ${backupPath}`));
|
|
600
|
+
} catch (ex) {
|
|
601
|
+
if (ex.code === "EPERM") {
|
|
602
|
+
console.error(chalk3.red("\u274C Cannot rename existing repo \u2014 it may be open in another process."));
|
|
603
|
+
console.log(chalk3.dim(` Close any editors or terminals using: ${localPath}`));
|
|
604
|
+
} else {
|
|
605
|
+
console.error(chalk3.red(`\u274C Failed to backup existing repo: ${ex.message}`));
|
|
606
|
+
}
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
581
609
|
}
|
|
582
610
|
fs3.mkdirSync(path3.dirname(localPath), { recursive: true });
|
|
583
611
|
console.log(`Cloning ${remoteUrl}...`);
|
|
@@ -587,6 +615,10 @@ var cloneRepo = async (remoteUrl) => {
|
|
|
587
615
|
process.exit(1);
|
|
588
616
|
}
|
|
589
617
|
console.log("\u2705 Cloned successfully.");
|
|
618
|
+
if (backupPath != null) {
|
|
619
|
+
fs3.rmSync(backupPath, { recursive: true, force: true });
|
|
620
|
+
console.log(chalk3.red(" removed") + chalk3.dim(` backup \u2192 ${backupPath}`));
|
|
621
|
+
}
|
|
590
622
|
await scaffoldCommand(localPath, { force: true });
|
|
591
623
|
configManager.repo_path = localPath;
|
|
592
624
|
configManager.remote_url = remoteUrl;
|
|
@@ -603,6 +635,26 @@ var installCommand = async (target) => {
|
|
|
603
635
|
console.log(chalk3.dim(" Example: set-prompt install https://github.com/you/my-prompts"));
|
|
604
636
|
process.exit(1);
|
|
605
637
|
}
|
|
638
|
+
const normalizeUrl = (url) => url.replace(/\.git$/, "").toLowerCase();
|
|
639
|
+
if (configManager.repo_path != null) {
|
|
640
|
+
if (normalizeUrl(configManager.remote_url ?? "") === normalizeUrl(target)) {
|
|
641
|
+
console.error(chalk3.red(`\u274C Already installed from the same URL: ${target}`));
|
|
642
|
+
console.log(chalk3.dim(" Use `set-prompt update` to pull the latest changes."));
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
console.warn(chalk3.yellow(`\u26A0 Switching repo: ${configManager.remote_url} \u2192 ${target}`));
|
|
646
|
+
const proceed = await confirm2({ message: "Replace existing installation?", default: false });
|
|
647
|
+
if (!proceed) {
|
|
648
|
+
console.log(chalk3.yellow("Cancelled."));
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
} else {
|
|
652
|
+
const proceed = await confirm2({ message: `Clone and register "${target}"?`, default: true });
|
|
653
|
+
if (!proceed) {
|
|
654
|
+
console.log(chalk3.yellow("Cancelled."));
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
606
658
|
return await cloneRepo(target);
|
|
607
659
|
} catch (ex) {
|
|
608
660
|
console.error(chalk3.red(`Unexpected error: ${ex.message}`), ex);
|
|
@@ -625,7 +677,8 @@ var resolveRepoPath = () => {
|
|
|
625
677
|
}
|
|
626
678
|
return configManager.repo_path;
|
|
627
679
|
};
|
|
628
|
-
var
|
|
680
|
+
var MARKET_NAME = "set-prompt";
|
|
681
|
+
var PLUGIN_NAME = "sppt";
|
|
629
682
|
var linkClaudeCode = async () => {
|
|
630
683
|
const repoPath = resolveRepoPath();
|
|
631
684
|
if (repoPath == null) {
|
|
@@ -637,7 +690,7 @@ var linkClaudeCode = async () => {
|
|
|
637
690
|
const marketplaceMetaDir = path4.join(CLAUDE_CODE_DIR, ".claude-plugin");
|
|
638
691
|
fs4.mkdirSync(marketplaceMetaDir, { recursive: true });
|
|
639
692
|
const marketplaceJson = {
|
|
640
|
-
name:
|
|
693
|
+
name: MARKET_NAME,
|
|
641
694
|
owner: { name: os2.userInfo().username },
|
|
642
695
|
metadata: { description: "Managed by set-prompt", version: "1.0.0" },
|
|
643
696
|
plugins: [{ name: PLUGIN_NAME, source: `./plugins/${PLUGIN_NAME}`, description: "Managed by set-prompt" }]
|
|
@@ -714,11 +767,11 @@ var linkClaudeCode = async () => {
|
|
|
714
767
|
}
|
|
715
768
|
settings.extraKnownMarketplaces = {
|
|
716
769
|
...settings.extraKnownMarketplaces,
|
|
717
|
-
[
|
|
770
|
+
[MARKET_NAME]: { source: { source: "directory", path: CLAUDE_CODE_DIR } }
|
|
718
771
|
};
|
|
719
772
|
settings.enabledPlugins = {
|
|
720
773
|
...settings.enabledPlugins,
|
|
721
|
-
[`${PLUGIN_NAME}@${
|
|
774
|
+
[`${PLUGIN_NAME}@${MARKET_NAME}`]: true
|
|
722
775
|
};
|
|
723
776
|
let backupPath = null;
|
|
724
777
|
if (fs4.existsSync(claudeSettingsPath)) {
|
|
@@ -761,6 +814,38 @@ var linkClaudeCode = async () => {
|
|
|
761
814
|
return false;
|
|
762
815
|
}
|
|
763
816
|
};
|
|
817
|
+
const patchInstalledPlugins = () => {
|
|
818
|
+
const installPath = path4.join(CLAUDE_CODE_DIR, "plugins", PLUGIN_NAME);
|
|
819
|
+
const installedPluginsPath = path4.join(os2.homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
820
|
+
const pluginKey = `${PLUGIN_NAME}@${MARKET_NAME}`;
|
|
821
|
+
try {
|
|
822
|
+
let data = { version: 2, plugins: {} };
|
|
823
|
+
if (fs4.existsSync(installedPluginsPath)) {
|
|
824
|
+
try {
|
|
825
|
+
data = JSON.parse(fs4.readFileSync(installedPluginsPath, "utf-8"));
|
|
826
|
+
} catch {
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (data.plugins == null) {
|
|
830
|
+
data.plugins = {};
|
|
831
|
+
}
|
|
832
|
+
data.plugins[pluginKey] = [
|
|
833
|
+
{
|
|
834
|
+
scope: "user",
|
|
835
|
+
installPath,
|
|
836
|
+
version: "1.0.0",
|
|
837
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
838
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
839
|
+
}
|
|
840
|
+
];
|
|
841
|
+
fs4.mkdirSync(path4.dirname(installedPluginsPath), { recursive: true });
|
|
842
|
+
fs4.writeFileSync(installedPluginsPath, JSON.stringify(data, null, 2), "utf-8");
|
|
843
|
+
console.log(`\u2705 Patched installed_plugins.json \u2192 installPath points to source.`);
|
|
844
|
+
console.log(chalk4.dim(` ${installPath}`));
|
|
845
|
+
} catch (ex) {
|
|
846
|
+
console.warn(chalk4.yellow(` \u26A0 Could not patch installed_plugins.json: ${ex.message}`));
|
|
847
|
+
}
|
|
848
|
+
};
|
|
764
849
|
console.log(chalk4.green(`
|
|
765
850
|
Setting up Claude Code plugin...`));
|
|
766
851
|
console.log(chalk4.dim(CLAUDE_CODE_DIR));
|
|
@@ -772,6 +857,7 @@ Setting up Claude Code plugin...`));
|
|
|
772
857
|
if (settingsOk === false) {
|
|
773
858
|
return;
|
|
774
859
|
}
|
|
860
|
+
patchInstalledPlugins();
|
|
775
861
|
configManager.claude_code = { path: CLAUDE_CODE_DIR };
|
|
776
862
|
configManager.save();
|
|
777
863
|
};
|
|
@@ -790,19 +876,30 @@ Setting up RooCode integration...`));
|
|
|
790
876
|
const dirsToBackup = [];
|
|
791
877
|
for (const dir of roocodeDirs) {
|
|
792
878
|
const target = path4.join(ROO_DIR, dir);
|
|
793
|
-
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false) {
|
|
879
|
+
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false && fs4.readdirSync(target).length > 0) {
|
|
794
880
|
dirsToBackup.push(dir);
|
|
795
881
|
}
|
|
796
882
|
}
|
|
797
883
|
if (dirsToBackup.length === 0) {
|
|
798
884
|
return true;
|
|
799
885
|
}
|
|
886
|
+
console.log(chalk4.yellow(`
|
|
887
|
+
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
888
|
+
for (const dir of dirsToBackup) {
|
|
889
|
+
console.log(chalk4.dim(` - ${path4.join(ROO_DIR, dir)}`));
|
|
890
|
+
}
|
|
891
|
+
console.log(chalk4.yellow(` They will be backed up to: `) + chalk4.dim(ROO_BACKUP_DIR));
|
|
892
|
+
const ok = await confirm3({ message: "Back up existing directories?", default: true });
|
|
893
|
+
if (!ok) {
|
|
894
|
+
console.log(chalk4.yellow("Skipped RooCode linking."));
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
800
897
|
fs4.mkdirSync(ROO_BACKUP_DIR, { recursive: true });
|
|
801
898
|
for (const dir of dirsToBackup) {
|
|
802
899
|
const src = path4.join(ROO_DIR, dir);
|
|
803
900
|
const dest = path4.join(ROO_BACKUP_DIR, dir);
|
|
804
901
|
fs4.renameSync(src, dest);
|
|
805
|
-
console.log(chalk4.
|
|
902
|
+
console.log(chalk4.yellow(" backed up") + chalk4.dim(`: ${dir}/ \u2192 ${dest}`));
|
|
806
903
|
}
|
|
807
904
|
return true;
|
|
808
905
|
} catch (ex) {
|
|
@@ -863,19 +960,30 @@ Setting up OpenClaw integration...`));
|
|
|
863
960
|
const dirsToBackup = [];
|
|
864
961
|
for (const dir of openclawDirs) {
|
|
865
962
|
const target = path4.join(OPENCLAW_DIR, dir);
|
|
866
|
-
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false) {
|
|
963
|
+
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false && fs4.readdirSync(target).length > 0) {
|
|
867
964
|
dirsToBackup.push(dir);
|
|
868
965
|
}
|
|
869
966
|
}
|
|
870
967
|
if (dirsToBackup.length === 0) {
|
|
871
968
|
return true;
|
|
872
969
|
}
|
|
970
|
+
console.log(chalk4.yellow(`
|
|
971
|
+
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
972
|
+
for (const dir of dirsToBackup) {
|
|
973
|
+
console.log(chalk4.dim(` - ${path4.join(OPENCLAW_DIR, dir)}`));
|
|
974
|
+
}
|
|
975
|
+
console.log(chalk4.yellow(` They will be backed up to: `) + chalk4.dim(OPENCLAW_BACKUP_DIR));
|
|
976
|
+
const ok = await confirm3({ message: "Back up existing directories?", default: true });
|
|
977
|
+
if (!ok) {
|
|
978
|
+
console.log(chalk4.yellow("Skipped OpenClaw linking."));
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
873
981
|
fs4.mkdirSync(OPENCLAW_BACKUP_DIR, { recursive: true });
|
|
874
982
|
for (const dir of dirsToBackup) {
|
|
875
983
|
const src = path4.join(OPENCLAW_DIR, dir);
|
|
876
984
|
const dest = path4.join(OPENCLAW_BACKUP_DIR, dir);
|
|
877
985
|
fs4.renameSync(src, dest);
|
|
878
|
-
console.log(chalk4.
|
|
986
|
+
console.log(chalk4.yellow(" backed up") + chalk4.dim(`: ${dir}/ \u2192 ${dest}`));
|
|
879
987
|
}
|
|
880
988
|
return true;
|
|
881
989
|
} catch (ex) {
|
|
@@ -921,11 +1029,181 @@ Setting up OpenClaw integration...`));
|
|
|
921
1029
|
configManager.openclaw = { path: OPENCLAW_DIR, backup_path: OPENCLAW_BACKUP_DIR };
|
|
922
1030
|
configManager.save();
|
|
923
1031
|
};
|
|
1032
|
+
var linkAntigravity = async () => {
|
|
1033
|
+
const repoPath = resolveRepoPath();
|
|
1034
|
+
if (repoPath == null) {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
console.log(chalk4.green(`
|
|
1038
|
+
Setting up Antigravity integration...`));
|
|
1039
|
+
console.log(chalk4.dim(ANTIGRAVITY_DIR));
|
|
1040
|
+
const antigravityDirs = AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */];
|
|
1041
|
+
const backupExistingAntigravityFiles = async () => {
|
|
1042
|
+
try {
|
|
1043
|
+
fs4.mkdirSync(ANTIGRAVITY_DIR, { recursive: true });
|
|
1044
|
+
const dirsToBackup = [];
|
|
1045
|
+
for (const dir of antigravityDirs) {
|
|
1046
|
+
const target = path4.join(ANTIGRAVITY_DIR, dir);
|
|
1047
|
+
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false && fs4.readdirSync(target).length > 0) {
|
|
1048
|
+
dirsToBackup.push(dir);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
if (dirsToBackup.length === 0) {
|
|
1052
|
+
return true;
|
|
1053
|
+
}
|
|
1054
|
+
console.log(chalk4.yellow(`
|
|
1055
|
+
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
1056
|
+
for (const dir of dirsToBackup) {
|
|
1057
|
+
console.log(chalk4.dim(` - ${path4.join(ANTIGRAVITY_DIR, dir)}`));
|
|
1058
|
+
}
|
|
1059
|
+
console.log(chalk4.yellow(` They will be backed up to: `) + chalk4.dim(ANTIGRAVITY_BACKUP_DIR));
|
|
1060
|
+
const ok = await confirm3({ message: "Back up existing directories?", default: true });
|
|
1061
|
+
if (!ok) {
|
|
1062
|
+
console.log(chalk4.yellow("Skipped Antigravity linking."));
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
fs4.mkdirSync(ANTIGRAVITY_BACKUP_DIR, { recursive: true });
|
|
1066
|
+
for (const dir of antigravityDirs) {
|
|
1067
|
+
const src = path4.join(ANTIGRAVITY_DIR, dir);
|
|
1068
|
+
const dest = path4.join(ANTIGRAVITY_BACKUP_DIR, dir);
|
|
1069
|
+
fs4.renameSync(src, dest);
|
|
1070
|
+
console.log(chalk4.yellow(" backed up") + chalk4.dim(`: ${dir}/ \u2192 ${dest}`));
|
|
1071
|
+
}
|
|
1072
|
+
return true;
|
|
1073
|
+
} catch (ex) {
|
|
1074
|
+
console.error(chalk4.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
1075
|
+
return false;
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
const setAntigravityAssets = async () => {
|
|
1079
|
+
try {
|
|
1080
|
+
const linked = [];
|
|
1081
|
+
for (const dir of antigravityDirs) {
|
|
1082
|
+
const src = path4.join(repoPath, dir);
|
|
1083
|
+
const dest = path4.join(ANTIGRAVITY_DIR, dir);
|
|
1084
|
+
if (await pathExists(src) === false) {
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
if (fs4.existsSync(dest)) {
|
|
1088
|
+
fs4.rmSync(dest, { recursive: true, force: true });
|
|
1089
|
+
}
|
|
1090
|
+
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1091
|
+
fs4.symlinkSync(src, dest, symlinkType);
|
|
1092
|
+
linked.push({ dir, src });
|
|
1093
|
+
}
|
|
1094
|
+
for (const { dir, src } of linked) {
|
|
1095
|
+
const isLast = linked[linked.length - 1].dir === dir;
|
|
1096
|
+
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
1097
|
+
console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
|
|
1098
|
+
}
|
|
1099
|
+
return true;
|
|
1100
|
+
} catch (ex) {
|
|
1101
|
+
console.error(chalk4.red(`\u274C Failed to create symlinks: ${ex.message}`));
|
|
1102
|
+
return false;
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
const backupOk = await backupExistingAntigravityFiles();
|
|
1106
|
+
if (backupOk === false) {
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
const linkOk = await setAntigravityAssets();
|
|
1110
|
+
if (linkOk === false) {
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
configManager.antigravity = { path: ANTIGRAVITY_DIR, backup_path: ANTIGRAVITY_BACKUP_DIR };
|
|
1114
|
+
configManager.save();
|
|
1115
|
+
};
|
|
924
1116
|
var linkCodex = async () => {
|
|
925
|
-
|
|
1117
|
+
console.error(chalk4.red("\u274C Codex integration is not available in this version."));
|
|
1118
|
+
return;
|
|
1119
|
+
};
|
|
1120
|
+
var linkCursor = async () => {
|
|
1121
|
+
const repoPath = resolveRepoPath();
|
|
1122
|
+
if (repoPath == null) {
|
|
926
1123
|
return;
|
|
927
1124
|
}
|
|
928
|
-
|
|
1125
|
+
const CURSOR_LOCAL_DIR = "local";
|
|
1126
|
+
const CURSOR_PLUGIN_NAME = "set-prompt";
|
|
1127
|
+
const setCursorAssets = async () => {
|
|
1128
|
+
try {
|
|
1129
|
+
const marketplaceMetaDir = path4.join(CURSOR_DIR, ".cursor-plugin");
|
|
1130
|
+
fs4.mkdirSync(marketplaceMetaDir, { recursive: true });
|
|
1131
|
+
const marketplaceJson = {
|
|
1132
|
+
name: CURSOR_PLUGIN_NAME,
|
|
1133
|
+
owner: { name: os2.userInfo().username },
|
|
1134
|
+
metadata: { description: "Managed by set-prompt" },
|
|
1135
|
+
plugins: [{ name: CURSOR_PLUGIN_NAME, source: `./plugins/${CURSOR_LOCAL_DIR}/${CURSOR_PLUGIN_NAME}`, description: "Managed by set-prompt" }]
|
|
1136
|
+
};
|
|
1137
|
+
fs4.writeFileSync(
|
|
1138
|
+
path4.join(marketplaceMetaDir, "marketplace.json"),
|
|
1139
|
+
JSON.stringify(marketplaceJson, null, 2),
|
|
1140
|
+
"utf-8"
|
|
1141
|
+
);
|
|
1142
|
+
console.log(chalk4.dim(" \u251C\u2500\u2500 .cursor-plugin/"));
|
|
1143
|
+
console.log(chalk4.dim(" \u2502 \u2514\u2500\u2500 marketplace.json") + chalk4.green(" \u2713"));
|
|
1144
|
+
const pluginDir = path4.join(CURSOR_DIR, "plugins", CURSOR_LOCAL_DIR, CURSOR_PLUGIN_NAME);
|
|
1145
|
+
const pluginMetaDir = path4.join(pluginDir, ".cursor-plugin");
|
|
1146
|
+
fs4.mkdirSync(pluginMetaDir, { recursive: true });
|
|
1147
|
+
const cursorDirs = AGENT_PROMPT_DIRS["cursor" /* CURSOR */];
|
|
1148
|
+
const pluginJson = {
|
|
1149
|
+
name: CURSOR_PLUGIN_NAME,
|
|
1150
|
+
displayName: "set-prompt",
|
|
1151
|
+
version: "1.0.0",
|
|
1152
|
+
description: "Managed by set-prompt"
|
|
1153
|
+
};
|
|
1154
|
+
for (const dir of cursorDirs) {
|
|
1155
|
+
pluginJson[dir] = `./${dir}/`;
|
|
1156
|
+
}
|
|
1157
|
+
fs4.writeFileSync(
|
|
1158
|
+
path4.join(pluginMetaDir, "plugin.json"),
|
|
1159
|
+
JSON.stringify(pluginJson, null, 2),
|
|
1160
|
+
"utf-8"
|
|
1161
|
+
);
|
|
1162
|
+
console.log(chalk4.dim(" \u2514\u2500\u2500 plugins/"));
|
|
1163
|
+
console.log(chalk4.dim(` \u2514\u2500\u2500 ${CURSOR_PLUGIN_NAME}/`));
|
|
1164
|
+
console.log(chalk4.dim(" \u251C\u2500\u2500 .cursor-plugin/"));
|
|
1165
|
+
console.log(chalk4.dim(" \u2502 \u2514\u2500\u2500 plugin.json") + chalk4.green(" \u2713"));
|
|
1166
|
+
const linked = [];
|
|
1167
|
+
for (const dir of cursorDirs) {
|
|
1168
|
+
const src = path4.join(repoPath, dir);
|
|
1169
|
+
const dest = path4.join(pluginDir, dir);
|
|
1170
|
+
if (await pathExists(src) === false) {
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
if (fs4.existsSync(dest)) {
|
|
1174
|
+
fs4.rmSync(dest, { recursive: true, force: true });
|
|
1175
|
+
}
|
|
1176
|
+
const symlinkType2 = process.platform === "win32" ? "junction" : "dir";
|
|
1177
|
+
fs4.symlinkSync(src, dest, symlinkType2);
|
|
1178
|
+
linked.push({ dir, src });
|
|
1179
|
+
}
|
|
1180
|
+
for (const { dir, src } of linked) {
|
|
1181
|
+
const isLast = linked[linked.length - 1].dir === dir;
|
|
1182
|
+
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
1183
|
+
console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
|
|
1184
|
+
}
|
|
1185
|
+
fs4.rmSync(CURSOR_PLUGIN_DIR, { recursive: true, force: true });
|
|
1186
|
+
fs4.mkdirSync(path4.dirname(CURSOR_PLUGIN_DIR), { recursive: true });
|
|
1187
|
+
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1188
|
+
fs4.symlinkSync(pluginDir, CURSOR_PLUGIN_DIR, symlinkType);
|
|
1189
|
+
console.log(chalk4.green(`
|
|
1190
|
+
\u2705 Installed to Cursor plugins.`));
|
|
1191
|
+
console.log(chalk4.dim(` ${CURSOR_PLUGIN_DIR}`));
|
|
1192
|
+
return true;
|
|
1193
|
+
} catch (ex) {
|
|
1194
|
+
console.error(chalk4.red(`\u274C Failed to build Cursor plugin structure: ${ex.message}`));
|
|
1195
|
+
return false;
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
console.log(chalk4.green(`
|
|
1199
|
+
Setting up Cursor plugin...`));
|
|
1200
|
+
console.log(chalk4.dim(CURSOR_DIR));
|
|
1201
|
+
const structureOk = await setCursorAssets();
|
|
1202
|
+
if (structureOk === false) {
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
configManager.cursor = { path: CURSOR_DIR, plugin_dir: CURSOR_PLUGIN_DIR };
|
|
1206
|
+
configManager.save();
|
|
929
1207
|
};
|
|
930
1208
|
var unlinkClaudeCode = async (force = false) => {
|
|
931
1209
|
if (!force) {
|
|
@@ -938,6 +1216,9 @@ var unlinkClaudeCode = async (force = false) => {
|
|
|
938
1216
|
return;
|
|
939
1217
|
}
|
|
940
1218
|
}
|
|
1219
|
+
console.log(chalk4.red(`
|
|
1220
|
+
Removing Claude Code plugin...`));
|
|
1221
|
+
console.log(chalk4.dim(CLAUDE_CODE_DIR));
|
|
941
1222
|
const claudeSettingsPath = path4.join(os2.homedir(), ".claude", "settings.json");
|
|
942
1223
|
if (fs4.existsSync(claudeSettingsPath)) {
|
|
943
1224
|
try {
|
|
@@ -945,16 +1226,16 @@ var unlinkClaudeCode = async (force = false) => {
|
|
|
945
1226
|
const backupPath = `${claudeSettingsPath}.bak.${timestamp}`;
|
|
946
1227
|
fs4.copyFileSync(claudeSettingsPath, backupPath);
|
|
947
1228
|
const settings = JSON.parse(fs4.readFileSync(claudeSettingsPath, "utf-8"));
|
|
948
|
-
if (settings?.extraKnownMarketplaces?.[
|
|
949
|
-
delete settings.extraKnownMarketplaces[
|
|
1229
|
+
if (settings?.extraKnownMarketplaces?.[MARKET_NAME] !== void 0) {
|
|
1230
|
+
delete settings.extraKnownMarketplaces[MARKET_NAME];
|
|
950
1231
|
}
|
|
951
|
-
if (settings?.enabledPlugins?.[`${PLUGIN_NAME}@${
|
|
952
|
-
delete settings.enabledPlugins[`${PLUGIN_NAME}@${
|
|
1232
|
+
if (settings?.enabledPlugins?.[`${PLUGIN_NAME}@${MARKET_NAME}`] !== void 0) {
|
|
1233
|
+
delete settings.enabledPlugins[`${PLUGIN_NAME}@${MARKET_NAME}`];
|
|
953
1234
|
}
|
|
954
1235
|
try {
|
|
955
1236
|
fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
956
1237
|
fs4.unlinkSync(backupPath);
|
|
957
|
-
console.log(chalk4.
|
|
1238
|
+
console.log(chalk4.red(" removed") + chalk4.dim(` set-prompt entries from: ${claudeSettingsPath}`));
|
|
958
1239
|
} catch (ex) {
|
|
959
1240
|
fs4.copyFileSync(backupPath, claudeSettingsPath);
|
|
960
1241
|
fs4.unlinkSync(backupPath);
|
|
@@ -964,9 +1245,60 @@ var unlinkClaudeCode = async (force = false) => {
|
|
|
964
1245
|
console.error(chalk4.red(` \u274C Failed to clean up settings.json: ${ex.message}`));
|
|
965
1246
|
}
|
|
966
1247
|
}
|
|
1248
|
+
const claudePluginsDir = path4.join(os2.homedir(), ".claude", "plugins");
|
|
1249
|
+
const installedPluginsPath = path4.join(claudePluginsDir, "installed_plugins.json");
|
|
1250
|
+
if (fs4.existsSync(installedPluginsPath)) {
|
|
1251
|
+
try {
|
|
1252
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1253
|
+
const backupPath = `${installedPluginsPath}.bak.${timestamp}`;
|
|
1254
|
+
fs4.copyFileSync(installedPluginsPath, backupPath);
|
|
1255
|
+
const installed = JSON.parse(fs4.readFileSync(installedPluginsPath, "utf-8"));
|
|
1256
|
+
if (installed?.plugins && typeof installed.plugins === "object") {
|
|
1257
|
+
for (const key of Object.keys(installed.plugins)) {
|
|
1258
|
+
if (key.endsWith(`@${MARKET_NAME}`)) {
|
|
1259
|
+
delete installed.plugins[key];
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
try {
|
|
1264
|
+
fs4.writeFileSync(installedPluginsPath, JSON.stringify(installed, null, 2), "utf-8");
|
|
1265
|
+
fs4.unlinkSync(backupPath);
|
|
1266
|
+
console.log(chalk4.red(" removed") + chalk4.dim(` set-prompt entries from: ${installedPluginsPath}`));
|
|
1267
|
+
} catch (ex) {
|
|
1268
|
+
fs4.copyFileSync(backupPath, installedPluginsPath);
|
|
1269
|
+
fs4.unlinkSync(backupPath);
|
|
1270
|
+
console.warn(chalk4.yellow(" \u26A0 Write failed \u2014 rolled back installed_plugins.json."));
|
|
1271
|
+
}
|
|
1272
|
+
} catch (ex) {
|
|
1273
|
+
console.error(chalk4.red(` \u274C Failed to clean up installed_plugins.json: ${ex.message}`));
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
const knownMarketplacesPath = path4.join(claudePluginsDir, "known_marketplaces.json");
|
|
1277
|
+
if (fs4.existsSync(knownMarketplacesPath)) {
|
|
1278
|
+
try {
|
|
1279
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1280
|
+
const backupPath = `${knownMarketplacesPath}.bak.${timestamp}`;
|
|
1281
|
+
fs4.copyFileSync(knownMarketplacesPath, backupPath);
|
|
1282
|
+
const marketplaces = JSON.parse(fs4.readFileSync(knownMarketplacesPath, "utf-8"));
|
|
1283
|
+
if (marketplaces?.[MARKET_NAME] !== void 0) {
|
|
1284
|
+
delete marketplaces[MARKET_NAME];
|
|
1285
|
+
}
|
|
1286
|
+
try {
|
|
1287
|
+
fs4.writeFileSync(knownMarketplacesPath, JSON.stringify(marketplaces, null, 2), "utf-8");
|
|
1288
|
+
fs4.unlinkSync(backupPath);
|
|
1289
|
+
console.log(chalk4.red(" removed") + chalk4.dim(` set-prompt entry from: ${knownMarketplacesPath}`));
|
|
1290
|
+
} catch (ex) {
|
|
1291
|
+
fs4.copyFileSync(backupPath, knownMarketplacesPath);
|
|
1292
|
+
fs4.unlinkSync(backupPath);
|
|
1293
|
+
console.warn(chalk4.yellow(" \u26A0 Write failed \u2014 rolled back known_marketplaces.json."));
|
|
1294
|
+
}
|
|
1295
|
+
} catch (ex) {
|
|
1296
|
+
console.error(chalk4.red(` \u274C Failed to clean up known_marketplaces.json: ${ex.message}`));
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
967
1299
|
if (fs4.existsSync(CLAUDE_CODE_DIR)) {
|
|
968
1300
|
fs4.rmSync(CLAUDE_CODE_DIR, { recursive: true, force: true });
|
|
969
|
-
console.log(chalk4.
|
|
1301
|
+
console.log(chalk4.red(" removed") + chalk4.dim(`: ${CLAUDE_CODE_DIR}`));
|
|
970
1302
|
}
|
|
971
1303
|
configManager.claude_code = null;
|
|
972
1304
|
configManager.save();
|
|
@@ -982,12 +1314,15 @@ var unlinkRooCode = async (force = false) => {
|
|
|
982
1314
|
return;
|
|
983
1315
|
}
|
|
984
1316
|
}
|
|
1317
|
+
console.log(chalk4.red(`
|
|
1318
|
+
Removing RooCode integration...`));
|
|
1319
|
+
console.log(chalk4.dim(ROO_DIR));
|
|
985
1320
|
const backupPath = configManager.roocode?.backup_path ?? ROO_BACKUP_DIR;
|
|
986
1321
|
for (const dir of AGENT_PROMPT_DIRS["roocode" /* ROOCODE */]) {
|
|
987
1322
|
const target = path4.join(ROO_DIR, dir);
|
|
988
1323
|
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink()) {
|
|
989
1324
|
fs4.unlinkSync(target);
|
|
990
|
-
console.log(chalk4.
|
|
1325
|
+
console.log(chalk4.red(" removed symlink") + chalk4.dim(`: ${target}`));
|
|
991
1326
|
}
|
|
992
1327
|
}
|
|
993
1328
|
if (fs4.existsSync(backupPath)) {
|
|
@@ -999,7 +1334,7 @@ var unlinkRooCode = async (force = false) => {
|
|
|
999
1334
|
continue;
|
|
1000
1335
|
}
|
|
1001
1336
|
fs4.renameSync(src, dest);
|
|
1002
|
-
console.log(chalk4.
|
|
1337
|
+
console.log(chalk4.green(" restored") + chalk4.dim(`: ${dir}/`));
|
|
1003
1338
|
}
|
|
1004
1339
|
fs4.rmdirSync(backupPath);
|
|
1005
1340
|
} catch (ex) {
|
|
@@ -1020,12 +1355,15 @@ var unlinkOpenclaw = async (force = false) => {
|
|
|
1020
1355
|
return;
|
|
1021
1356
|
}
|
|
1022
1357
|
}
|
|
1358
|
+
console.log(chalk4.red(`
|
|
1359
|
+
Removing OpenClaw integration...`));
|
|
1360
|
+
console.log(chalk4.dim(OPENCLAW_DIR));
|
|
1023
1361
|
const backupPath = configManager.openclaw?.backup_path ?? OPENCLAW_BACKUP_DIR;
|
|
1024
1362
|
for (const dir of AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */]) {
|
|
1025
1363
|
const target = path4.join(OPENCLAW_DIR, dir);
|
|
1026
1364
|
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink()) {
|
|
1027
1365
|
fs4.unlinkSync(target);
|
|
1028
|
-
console.log(chalk4.
|
|
1366
|
+
console.log(chalk4.red(" removed symlink") + chalk4.dim(`: ${target}`));
|
|
1029
1367
|
}
|
|
1030
1368
|
}
|
|
1031
1369
|
if (fs4.existsSync(backupPath)) {
|
|
@@ -1037,7 +1375,7 @@ var unlinkOpenclaw = async (force = false) => {
|
|
|
1037
1375
|
continue;
|
|
1038
1376
|
}
|
|
1039
1377
|
fs4.renameSync(src, dest);
|
|
1040
|
-
console.log(chalk4.
|
|
1378
|
+
console.log(chalk4.green(" restored") + chalk4.dim(`: ${dir}/`));
|
|
1041
1379
|
}
|
|
1042
1380
|
fs4.rmdirSync(backupPath);
|
|
1043
1381
|
} catch (ex) {
|
|
@@ -1058,12 +1396,15 @@ var unlinkAntigravity = async (force = false) => {
|
|
|
1058
1396
|
return;
|
|
1059
1397
|
}
|
|
1060
1398
|
}
|
|
1399
|
+
console.log(chalk4.red(`
|
|
1400
|
+
Removing Antigravity integration...`));
|
|
1401
|
+
console.log(chalk4.dim(ANTIGRAVITY_DIR));
|
|
1061
1402
|
const backupPath = configManager.antigravity?.backup_path ?? ANTIGRAVITY_BACKUP_DIR;
|
|
1062
1403
|
for (const dir of AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */]) {
|
|
1063
1404
|
const target = path4.join(ANTIGRAVITY_DIR, dir);
|
|
1064
1405
|
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink()) {
|
|
1065
1406
|
fs4.unlinkSync(target);
|
|
1066
|
-
console.log(chalk4.
|
|
1407
|
+
console.log(chalk4.red(" removed symlink") + chalk4.dim(`: ${target}`));
|
|
1067
1408
|
}
|
|
1068
1409
|
}
|
|
1069
1410
|
if (fs4.existsSync(backupPath)) {
|
|
@@ -1075,7 +1416,7 @@ var unlinkAntigravity = async (force = false) => {
|
|
|
1075
1416
|
continue;
|
|
1076
1417
|
}
|
|
1077
1418
|
fs4.renameSync(src, dest);
|
|
1078
|
-
console.log(chalk4.
|
|
1419
|
+
console.log(chalk4.green(" restored") + chalk4.dim(`: ${dir}/`));
|
|
1079
1420
|
}
|
|
1080
1421
|
fs4.rmdirSync(backupPath);
|
|
1081
1422
|
} catch (ex) {
|
|
@@ -1085,77 +1426,68 @@ var unlinkAntigravity = async (force = false) => {
|
|
|
1085
1426
|
configManager.antigravity = null;
|
|
1086
1427
|
configManager.save();
|
|
1087
1428
|
};
|
|
1088
|
-
var
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1429
|
+
var unlinkCodex = async (force = false) => {
|
|
1430
|
+
if (!force) {
|
|
1431
|
+
const ok = await confirm3({
|
|
1432
|
+
message: `Remove Codex symlinks from ${CODEX_DIR}?`,
|
|
1433
|
+
default: false
|
|
1434
|
+
});
|
|
1435
|
+
if (!ok) {
|
|
1436
|
+
console.log(chalk4.yellow("Cancelled."));
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1092
1439
|
}
|
|
1093
|
-
console.log(chalk4.
|
|
1094
|
-
|
|
1095
|
-
console.log(chalk4.dim(
|
|
1096
|
-
const
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
const target = path4.join(ANTIGRAVITY_DIR, dir);
|
|
1103
|
-
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false) {
|
|
1104
|
-
dirsToBackup.push(dir);
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
if (dirsToBackup.length === 0) {
|
|
1108
|
-
return true;
|
|
1109
|
-
}
|
|
1110
|
-
fs4.mkdirSync(ANTIGRAVITY_BACKUP_DIR, { recursive: true });
|
|
1111
|
-
for (const dir of dirsToBackup) {
|
|
1112
|
-
const src = path4.join(ANTIGRAVITY_DIR, dir);
|
|
1113
|
-
const dest = path4.join(ANTIGRAVITY_BACKUP_DIR, dir);
|
|
1114
|
-
fs4.renameSync(src, dest);
|
|
1115
|
-
console.log(chalk4.dim(` backed up: ${dir}/ \u2192 ${dest}`));
|
|
1116
|
-
}
|
|
1117
|
-
return true;
|
|
1118
|
-
} catch (ex) {
|
|
1119
|
-
console.error(chalk4.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
1120
|
-
return false;
|
|
1440
|
+
console.log(chalk4.red(`
|
|
1441
|
+
Removing Codex integration...`));
|
|
1442
|
+
console.log(chalk4.dim(CODEX_DIR));
|
|
1443
|
+
const backupPath = configManager.codex?.backup_path ?? CODEX_BACKUP_DIR;
|
|
1444
|
+
for (const dir of AGENT_PROMPT_DIRS["codex" /* CODEX */]) {
|
|
1445
|
+
const target = path4.join(CODEX_DIR, dir);
|
|
1446
|
+
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink()) {
|
|
1447
|
+
fs4.unlinkSync(target);
|
|
1448
|
+
console.log(chalk4.red(" removed symlink") + chalk4.dim(`: ${target}`));
|
|
1121
1449
|
}
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1450
|
+
}
|
|
1451
|
+
if (fs4.existsSync(backupPath)) {
|
|
1124
1452
|
try {
|
|
1125
|
-
const
|
|
1126
|
-
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1129
|
-
if (await pathExists(src) === false) {
|
|
1453
|
+
for (const dir of AGENT_PROMPT_DIRS["codex" /* CODEX */]) {
|
|
1454
|
+
const src = path4.join(backupPath, dir);
|
|
1455
|
+
const dest = path4.join(CODEX_DIR, dir);
|
|
1456
|
+
if (!fs4.existsSync(src)) {
|
|
1130
1457
|
continue;
|
|
1131
1458
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
}
|
|
1135
|
-
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1136
|
-
fs4.symlinkSync(src, dest, symlinkType);
|
|
1137
|
-
linked.push({ dir, src });
|
|
1138
|
-
}
|
|
1139
|
-
for (const { dir, src } of linked) {
|
|
1140
|
-
const isLast = linked[linked.length - 1].dir === dir;
|
|
1141
|
-
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
1142
|
-
console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
|
|
1459
|
+
fs4.renameSync(src, dest);
|
|
1460
|
+
console.log(chalk4.green(" restored") + chalk4.dim(`: ${dir}/`));
|
|
1143
1461
|
}
|
|
1144
|
-
|
|
1462
|
+
fs4.rmdirSync(backupPath);
|
|
1145
1463
|
} catch (ex) {
|
|
1146
|
-
console.error(chalk4.red(
|
|
1147
|
-
return false;
|
|
1464
|
+
console.error(chalk4.red(` \u274C Failed to restore Codex backup: ${ex.message}`));
|
|
1148
1465
|
}
|
|
1149
|
-
};
|
|
1150
|
-
const backupOk = await backupExistingAntigravityFiles();
|
|
1151
|
-
if (backupOk === false) {
|
|
1152
|
-
return;
|
|
1153
1466
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1467
|
+
configManager.codex = null;
|
|
1468
|
+
configManager.save();
|
|
1469
|
+
};
|
|
1470
|
+
var unlinkCursor = async (force = false) => {
|
|
1471
|
+
if (!force) {
|
|
1472
|
+
const ok = await confirm3({
|
|
1473
|
+
message: `Remove Cursor plugin dir (${CURSOR_PLUGIN_DIR}) and plugin structure?`,
|
|
1474
|
+
default: false
|
|
1475
|
+
});
|
|
1476
|
+
if (!ok) {
|
|
1477
|
+
console.log(chalk4.yellow("Cancelled."));
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1157
1480
|
}
|
|
1158
|
-
|
|
1481
|
+
console.log(chalk4.red(`
|
|
1482
|
+
Removing Cursor plugin...`));
|
|
1483
|
+
console.log(chalk4.dim(CURSOR_PLUGIN_DIR));
|
|
1484
|
+
fs4.rmSync(CURSOR_PLUGIN_DIR, { recursive: true, force: true });
|
|
1485
|
+
console.log(chalk4.red(" removed") + chalk4.dim(`: ${CURSOR_PLUGIN_DIR}`));
|
|
1486
|
+
if (fs4.existsSync(CURSOR_DIR)) {
|
|
1487
|
+
fs4.rmSync(CURSOR_DIR, { recursive: true, force: true });
|
|
1488
|
+
console.log(chalk4.red(" removed") + chalk4.dim(`: ${CURSOR_DIR}`));
|
|
1489
|
+
}
|
|
1490
|
+
configManager.cursor = null;
|
|
1159
1491
|
configManager.save();
|
|
1160
1492
|
};
|
|
1161
1493
|
var linkCommand = async (tool) => {
|
|
@@ -1175,6 +1507,8 @@ var linkCommand = async (tool) => {
|
|
|
1175
1507
|
await linkCodex();
|
|
1176
1508
|
} else if (tool === "antigravity" /* ANTIGRAVITY */) {
|
|
1177
1509
|
await linkAntigravity();
|
|
1510
|
+
} else if (tool === "cursor" /* CURSOR */) {
|
|
1511
|
+
await linkCursor();
|
|
1178
1512
|
}
|
|
1179
1513
|
return;
|
|
1180
1514
|
}
|
|
@@ -1183,7 +1517,8 @@ var linkCommand = async (tool) => {
|
|
|
1183
1517
|
["roocode" /* ROOCODE */]: configManager.isRooCodeEnabled(),
|
|
1184
1518
|
["openclaw" /* OPENCLAW */]: configManager.isOpenclawEnabled(),
|
|
1185
1519
|
["codex" /* CODEX */]: configManager.isCodexEnabled(),
|
|
1186
|
-
["antigravity" /* ANTIGRAVITY */]: configManager.isAntigravityEnabled()
|
|
1520
|
+
["antigravity" /* ANTIGRAVITY */]: configManager.isAntigravityEnabled(),
|
|
1521
|
+
["cursor" /* CURSOR */]: configManager.isCursorEnabled()
|
|
1187
1522
|
};
|
|
1188
1523
|
const selected = await checkbox({
|
|
1189
1524
|
message: "Which AI agent do you want to integrate?",
|
|
@@ -1193,6 +1528,19 @@ var linkCommand = async (tool) => {
|
|
|
1193
1528
|
checked: prevLinked[a.value]
|
|
1194
1529
|
}))
|
|
1195
1530
|
});
|
|
1531
|
+
const toLink = ALL_AGENTS.filter((a) => !prevLinked[a.value] && selected.includes(a.value));
|
|
1532
|
+
const toUnlink = ALL_AGENTS.filter((a) => prevLinked[a.value] && !selected.includes(a.value));
|
|
1533
|
+
console.log();
|
|
1534
|
+
if (toLink.length > 0) {
|
|
1535
|
+
console.log(chalk4.green(" Link ") + chalk4.dim("\u2192 ") + toLink.map((a) => chalk4.bold(a.name)).join(chalk4.dim(", ")));
|
|
1536
|
+
}
|
|
1537
|
+
if (toUnlink.length > 0) {
|
|
1538
|
+
console.log(chalk4.red(" Unlink ") + chalk4.dim("\u2192 ") + toUnlink.map((a) => chalk4.bold(a.name)).join(chalk4.dim(", ")));
|
|
1539
|
+
}
|
|
1540
|
+
console.log();
|
|
1541
|
+
if (toLink.length === 0 && toUnlink.length === 0) {
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1196
1544
|
for (const a of ALL_AGENTS) {
|
|
1197
1545
|
const was = prevLinked[a.value];
|
|
1198
1546
|
const now = selected.includes(a.value);
|
|
@@ -1207,6 +1555,8 @@ var linkCommand = async (tool) => {
|
|
|
1207
1555
|
await linkCodex();
|
|
1208
1556
|
} else if (a.value === "antigravity" /* ANTIGRAVITY */) {
|
|
1209
1557
|
await linkAntigravity();
|
|
1558
|
+
} else if (a.value === "cursor" /* CURSOR */) {
|
|
1559
|
+
await linkCursor();
|
|
1210
1560
|
}
|
|
1211
1561
|
}
|
|
1212
1562
|
if (was && !now) {
|
|
@@ -1216,8 +1566,12 @@ var linkCommand = async (tool) => {
|
|
|
1216
1566
|
await unlinkRooCode(true);
|
|
1217
1567
|
} else if (a.value === "openclaw" /* OPENCLAW */) {
|
|
1218
1568
|
await unlinkOpenclaw(true);
|
|
1569
|
+
} else if (a.value === "codex" /* CODEX */) {
|
|
1570
|
+
await unlinkCodex(true);
|
|
1219
1571
|
} else if (a.value === "antigravity" /* ANTIGRAVITY */) {
|
|
1220
1572
|
await unlinkAntigravity(true);
|
|
1573
|
+
} else if (a.value === "cursor" /* CURSOR */) {
|
|
1574
|
+
await unlinkCursor(true);
|
|
1221
1575
|
}
|
|
1222
1576
|
}
|
|
1223
1577
|
}
|
|
@@ -1236,7 +1590,9 @@ var uninstallCommand = async () => {
|
|
|
1236
1590
|
const hasRooCode = configManager.isRooCodeEnabled();
|
|
1237
1591
|
const hasOpenclaw = configManager.isOpenclawEnabled();
|
|
1238
1592
|
const hasAntigravity = configManager.isAntigravityEnabled();
|
|
1239
|
-
|
|
1593
|
+
const hasCodex = configManager.isCodexEnabled();
|
|
1594
|
+
const hasCursor = configManager.isCursorEnabled();
|
|
1595
|
+
if (targets.length === 0 && !hasClaudeCode && !hasRooCode && !hasOpenclaw && !hasAntigravity && !hasCodex && !hasCursor) {
|
|
1240
1596
|
console.log(chalk5.yellow("Nothing to remove."));
|
|
1241
1597
|
return;
|
|
1242
1598
|
}
|
|
@@ -1254,6 +1610,12 @@ var uninstallCommand = async () => {
|
|
|
1254
1610
|
if (hasAntigravity) {
|
|
1255
1611
|
console.log(` Antigravity symlinks ${chalk5.dim("(backup will be restored)")}`);
|
|
1256
1612
|
}
|
|
1613
|
+
if (hasCodex) {
|
|
1614
|
+
console.log(` Codex symlinks ${chalk5.dim("(backup will be restored)")}`);
|
|
1615
|
+
}
|
|
1616
|
+
if (hasCursor) {
|
|
1617
|
+
console.log(` Cursor plugin dir ${chalk5.dim("(symlink will be removed)")}`);
|
|
1618
|
+
}
|
|
1257
1619
|
const ok = await confirm4({ message: "Proceed?", default: false });
|
|
1258
1620
|
if (!ok) {
|
|
1259
1621
|
console.log(chalk5.yellow("Cancelled."));
|
|
@@ -1271,6 +1633,12 @@ var uninstallCommand = async () => {
|
|
|
1271
1633
|
if (hasAntigravity) {
|
|
1272
1634
|
await unlinkAntigravity(true);
|
|
1273
1635
|
}
|
|
1636
|
+
if (hasCodex) {
|
|
1637
|
+
await unlinkCodex(true);
|
|
1638
|
+
}
|
|
1639
|
+
if (hasCursor) {
|
|
1640
|
+
await unlinkCursor(true);
|
|
1641
|
+
}
|
|
1274
1642
|
for (const t of targets) {
|
|
1275
1643
|
fs5.rmSync(t.path, { recursive: true, force: true });
|
|
1276
1644
|
console.log(chalk5.dim(` removed: ${t.path}`));
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "set-prompt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Sync your prompt library across AI coding tools from a single git repo",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"set-prompt": "./dist/index.js"
|
|
7
|
+
"set-prompt": "./dist/index.js",
|
|
8
|
+
"sppt": "./dist/index.js"
|
|
8
9
|
},
|
|
9
10
|
"type": "module",
|
|
10
11
|
"scripts": {
|
|
11
12
|
"start:dev": "tsx src/index.ts",
|
|
12
|
-
"build": "rimraf dist && tsup
|
|
13
|
-
"copy_bin": "cpx \"src/bin/set-prompt.js\" dist/bin",
|
|
13
|
+
"build": "rimraf dist && tsup",
|
|
14
14
|
"test": "vitest --reporter=verbose"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
@@ -46,7 +46,6 @@
|
|
|
46
46
|
"@types/fs-extra": "^11.0.4",
|
|
47
47
|
"@types/node": "^25.4.0",
|
|
48
48
|
"@vitest/coverage-v8": "^4.1.0",
|
|
49
|
-
"cpx": "^1.5.0",
|
|
50
49
|
"memfs": "^4.56.11",
|
|
51
50
|
"prettier": "^3.8.1",
|
|
52
51
|
"rimraf": "^6.1.3",
|
package/dist/bin/set-prompt.js
DELETED