set-prompt 0.5.4 → 0.7.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 +48 -0
- package/README.md +82 -26
- package/dist/index.js +1245 -401
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk23 from "chalk";
|
|
6
6
|
import figlet from "figlet";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import fs14 from "fs";
|
|
8
|
+
import path13 from "path";
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
10
10
|
|
|
11
11
|
// src/commands/install-command.ts
|
|
12
|
-
import
|
|
12
|
+
import fs4 from "fs";
|
|
13
13
|
import path3 from "path";
|
|
14
|
-
import { spawnSync } from "child_process";
|
|
15
|
-
import
|
|
14
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
15
|
+
import chalk5 from "chalk";
|
|
16
16
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
17
17
|
|
|
18
18
|
// src/_defs/index.ts
|
|
@@ -34,6 +34,10 @@ var ANTIGRAVITY_BACKUP_DIR = path.join(ANTIGRAVITY_DIR, "SET_PROMPT_BACKUP");
|
|
|
34
34
|
var CODEX_DIR = path.join(HOME_DIR, "codex");
|
|
35
35
|
var CODEX_BACKUP_DIR = path.join(CODEX_DIR, "SET_PROMPT_BACKUP");
|
|
36
36
|
var CURSOR_DIR = path.join(os.homedir(), ".cursor");
|
|
37
|
+
var OPENCODE_DIR = path.join(os.homedir(), ".config", "opencode");
|
|
38
|
+
var OPENCODE_BACKUP_DIR = path.join(OPENCODE_DIR, "SET_PROMPT_BACKUP");
|
|
39
|
+
var GEMINICLI_DIR = path.join(os.homedir(), ".gemini");
|
|
40
|
+
var GEMINICLI_BACKUP_DIR = path.join(GEMINICLI_DIR, "SET_PROMPT_BACKUP");
|
|
37
41
|
var PROMPT_DIR_NAMES = ["skills", "commands", "hooks", "agents", "rules"];
|
|
38
42
|
var AGENT_PROMPT_DIRS = {
|
|
39
43
|
["claudecode" /* CLAUDECODE */]: ["skills", "commands", "hooks", "agents"],
|
|
@@ -41,7 +45,9 @@ var AGENT_PROMPT_DIRS = {
|
|
|
41
45
|
["openclaw" /* OPENCLAW */]: ["skills"],
|
|
42
46
|
["codex" /* CODEX */]: ["skills"],
|
|
43
47
|
["antigravity" /* ANTIGRAVITY */]: ["skills"],
|
|
44
|
-
["cursor" /* CURSOR */]: ["skills", "agents", "commands", "hooks"]
|
|
48
|
+
["cursor" /* CURSOR */]: ["skills", "agents", "commands", "hooks"],
|
|
49
|
+
["opencode" /* OPENCODE */]: ["skills", "commands", "agents"],
|
|
50
|
+
["geminicli" /* GEMINICLI */]: ["skills", "commands", "agents"]
|
|
45
51
|
};
|
|
46
52
|
var ALL_AGENTS = [
|
|
47
53
|
{ name: "Claude Code", value: "claudecode" /* CLAUDECODE */ },
|
|
@@ -49,7 +55,9 @@ var ALL_AGENTS = [
|
|
|
49
55
|
{ name: "OpenClaw", value: "openclaw" /* OPENCLAW */ },
|
|
50
56
|
{ name: "Codex", value: "codex" /* CODEX */ },
|
|
51
57
|
{ name: "Antigravity", value: "antigravity" /* ANTIGRAVITY */ },
|
|
52
|
-
{ name: "Cursor", value: "cursor" /* CURSOR */ }
|
|
58
|
+
{ name: "Cursor", value: "cursor" /* CURSOR */ },
|
|
59
|
+
{ name: "OpenCode", value: "opencode" /* OPENCODE */ },
|
|
60
|
+
{ name: "Gemini CLI", value: "geminicli" /* GEMINICLI */ }
|
|
53
61
|
];
|
|
54
62
|
|
|
55
63
|
// src/_libs/config.ts
|
|
@@ -83,6 +91,14 @@ var CursorConfigSchema = z.object({
|
|
|
83
91
|
path: z.string().nullable(),
|
|
84
92
|
backup_path: z.string().nullish().optional()
|
|
85
93
|
});
|
|
94
|
+
var OpencodeConfigSchema = z.object({
|
|
95
|
+
path: z.string().nullable(),
|
|
96
|
+
backup_path: z.string().nullish().optional()
|
|
97
|
+
});
|
|
98
|
+
var GeminicliConfigSchema = z.object({
|
|
99
|
+
path: z.string().nullable(),
|
|
100
|
+
backup_path: z.string().nullish().optional()
|
|
101
|
+
});
|
|
86
102
|
var GlobalConfigSchema = z.object({
|
|
87
103
|
repo_path: z.string(),
|
|
88
104
|
remote_url: z.string().nullable(),
|
|
@@ -91,7 +107,9 @@ var GlobalConfigSchema = z.object({
|
|
|
91
107
|
openclaw: OpenclawConfigSchema.nullable(),
|
|
92
108
|
codex: CodexConfigSchema.nullish().optional(),
|
|
93
109
|
antigravity: AntigravityConfigSchema.nullish().optional(),
|
|
94
|
-
cursor: CursorConfigSchema.nullish().optional()
|
|
110
|
+
cursor: CursorConfigSchema.nullish().optional(),
|
|
111
|
+
opencode: OpencodeConfigSchema.nullish().optional(),
|
|
112
|
+
geminicli: GeminicliConfigSchema.nullish().optional()
|
|
95
113
|
});
|
|
96
114
|
|
|
97
115
|
// src/_libs/config.ts
|
|
@@ -105,6 +123,8 @@ var ConfigManager = class {
|
|
|
105
123
|
this._codex = null;
|
|
106
124
|
this._antigravity = null;
|
|
107
125
|
this._cursor = null;
|
|
126
|
+
this._opencode = null;
|
|
127
|
+
this._geminicli = null;
|
|
108
128
|
}
|
|
109
129
|
get repo_path() {
|
|
110
130
|
return this._repo_path;
|
|
@@ -130,6 +150,12 @@ var ConfigManager = class {
|
|
|
130
150
|
get cursor() {
|
|
131
151
|
return this._cursor;
|
|
132
152
|
}
|
|
153
|
+
get opencode() {
|
|
154
|
+
return this._opencode;
|
|
155
|
+
}
|
|
156
|
+
get geminicli() {
|
|
157
|
+
return this._geminicli;
|
|
158
|
+
}
|
|
133
159
|
set repo_path(v) {
|
|
134
160
|
this._repo_path = v;
|
|
135
161
|
}
|
|
@@ -154,6 +180,12 @@ var ConfigManager = class {
|
|
|
154
180
|
set cursor(v) {
|
|
155
181
|
this._cursor = v;
|
|
156
182
|
}
|
|
183
|
+
set opencode(v) {
|
|
184
|
+
this._opencode = v;
|
|
185
|
+
}
|
|
186
|
+
set geminicli(v) {
|
|
187
|
+
this._geminicli = v;
|
|
188
|
+
}
|
|
157
189
|
init() {
|
|
158
190
|
this._loadFromDisk();
|
|
159
191
|
if (this._repo_path != null) {
|
|
@@ -175,7 +207,9 @@ var ConfigManager = class {
|
|
|
175
207
|
openclaw: this._openclaw,
|
|
176
208
|
codex: this._codex,
|
|
177
209
|
antigravity: this._antigravity,
|
|
178
|
-
cursor: this._cursor
|
|
210
|
+
cursor: this._cursor,
|
|
211
|
+
opencode: this._opencode,
|
|
212
|
+
geminicli: this._geminicli
|
|
179
213
|
}, null, 4);
|
|
180
214
|
fs.writeFileSync(CONFIG_PATH, configStr, "utf-8");
|
|
181
215
|
console.log(chalk.green(`Config saved`) + chalk.dim(` \u2192 ${CONFIG_PATH}`));
|
|
@@ -212,6 +246,12 @@ var ConfigManager = class {
|
|
|
212
246
|
isCursorEnabled() {
|
|
213
247
|
return this._cursor != null;
|
|
214
248
|
}
|
|
249
|
+
isOpencodeEnabled() {
|
|
250
|
+
return this._opencode != null;
|
|
251
|
+
}
|
|
252
|
+
isGeminicliEnabled() {
|
|
253
|
+
return this._geminicli != null;
|
|
254
|
+
}
|
|
215
255
|
_assign(config) {
|
|
216
256
|
this._repo_path = config.repo_path;
|
|
217
257
|
this._remote_url = config.remote_url;
|
|
@@ -221,6 +261,8 @@ var ConfigManager = class {
|
|
|
221
261
|
this._codex = config.codex ?? null;
|
|
222
262
|
this._antigravity = config.antigravity ?? null;
|
|
223
263
|
this._cursor = config.cursor ?? null;
|
|
264
|
+
this._opencode = config.opencode ?? null;
|
|
265
|
+
this._geminicli = config.geminicli ?? null;
|
|
224
266
|
}
|
|
225
267
|
_loadFromDisk() {
|
|
226
268
|
if (fs.existsSync(CONFIG_PATH) === false) {
|
|
@@ -239,6 +281,8 @@ var ConfigManager = class {
|
|
|
239
281
|
var configManager = new ConfigManager();
|
|
240
282
|
|
|
241
283
|
// src/_libs/index.ts
|
|
284
|
+
import { spawnSync } from "child_process";
|
|
285
|
+
import fs2 from "fs";
|
|
242
286
|
import chalk2 from "chalk";
|
|
243
287
|
var isGitUrl = (source) => source.startsWith("http://") || source.startsWith("https://") || source.startsWith("git@") || source.startsWith("ssh://") || source.endsWith(".git") && (source.startsWith("http") || source.startsWith("git@") || source.startsWith("ssh://"));
|
|
244
288
|
var resolveRepoPath = () => {
|
|
@@ -249,11 +293,71 @@ var resolveRepoPath = () => {
|
|
|
249
293
|
}
|
|
250
294
|
return configManager.repo_path;
|
|
251
295
|
};
|
|
296
|
+
var isOnPath = (bin) => {
|
|
297
|
+
const probeCmd = process.platform === "win32" ? "where" : "which";
|
|
298
|
+
const probe = spawnSync(probeCmd, [bin], { stdio: "ignore" });
|
|
299
|
+
return probe.status === 0;
|
|
300
|
+
};
|
|
301
|
+
var firstExistingPath = (candidates) => {
|
|
302
|
+
for (const candidate of candidates) {
|
|
303
|
+
if (candidate != null && candidate !== "" && fs2.existsSync(candidate)) return candidate;
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// src/_libs/repo.ts
|
|
309
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
310
|
+
import chalk3 from "chalk";
|
|
311
|
+
var parsePorcelainLine = (line) => {
|
|
312
|
+
if (line.length < 4) return null;
|
|
313
|
+
const status = line.slice(0, 2);
|
|
314
|
+
let name = line.slice(3);
|
|
315
|
+
const arrowIdx = name.indexOf(" -> ");
|
|
316
|
+
if (arrowIdx >= 0) name = name.slice(arrowIdx + 4);
|
|
317
|
+
if (name.startsWith('"') && name.endsWith('"')) name = name.slice(1, -1);
|
|
318
|
+
if (status.includes("?")) return { kind: "?", name };
|
|
319
|
+
if (status.includes("D")) return { kind: "D", name };
|
|
320
|
+
if (status.includes("R")) return { kind: "R", name };
|
|
321
|
+
if (status.includes("A")) return { kind: "A", name };
|
|
322
|
+
if (status.includes("M")) return { kind: "M", name };
|
|
323
|
+
return null;
|
|
324
|
+
};
|
|
325
|
+
var pickVerb = (files) => {
|
|
326
|
+
const allAdded = files.every((f) => f.kind === "A" || f.kind === "?");
|
|
327
|
+
const allDeleted = files.every((f) => f.kind === "D");
|
|
328
|
+
if (allAdded) return "add";
|
|
329
|
+
if (allDeleted) return "remove";
|
|
330
|
+
return "update";
|
|
331
|
+
};
|
|
332
|
+
var generateCommitMessage = (repoPath) => {
|
|
333
|
+
const result = spawnSync2("git", ["status", "--porcelain", "--untracked-files=all"], {
|
|
334
|
+
cwd: repoPath,
|
|
335
|
+
encoding: "utf8"
|
|
336
|
+
});
|
|
337
|
+
if (result.status !== 0) return null;
|
|
338
|
+
const files = result.stdout.split("\n").map(parsePorcelainLine).filter((f) => f !== null);
|
|
339
|
+
if (files.length === 0) return null;
|
|
340
|
+
const verb = pickVerb(files);
|
|
341
|
+
const noun = files.length === 1 ? "file" : "files";
|
|
342
|
+
const subject = `${verb} ${files.length} ${noun}`;
|
|
343
|
+
const body = files.map((f) => `- ${f.name}`).join("\n");
|
|
344
|
+
return `${subject}
|
|
345
|
+
|
|
346
|
+
${body}`;
|
|
347
|
+
};
|
|
348
|
+
var printSaveHint = (repoPath) => {
|
|
349
|
+
const generated = generateCommitMessage(repoPath);
|
|
350
|
+
if (generated == null) return;
|
|
351
|
+
const subject = generated.split("\n")[0];
|
|
352
|
+
console.log(chalk3.yellow("\nUncommitted changes detected."));
|
|
353
|
+
console.log(chalk3.dim(` Pending: ${subject}`));
|
|
354
|
+
console.log(chalk3.cyan(" Tip: run `sppt repo save` to commit and push."));
|
|
355
|
+
};
|
|
252
356
|
|
|
253
357
|
// src/commands/scaffold-command.ts
|
|
254
|
-
import
|
|
358
|
+
import fs3 from "fs";
|
|
255
359
|
import path2 from "path";
|
|
256
|
-
import
|
|
360
|
+
import chalk4 from "chalk";
|
|
257
361
|
import { confirm } from "@inquirer/prompts";
|
|
258
362
|
|
|
259
363
|
// src/_libs/templates.ts
|
|
@@ -275,7 +379,7 @@ This is a shared prompt repository linked to various AI agents via \`set-prompt
|
|
|
275
379
|
\u2502 \u251C\u2500\u2500 COMMAND.md # Platform-specific frontmatter + prompt content
|
|
276
380
|
\u2502 \u2514\u2500\u2500 ... # Supporting files
|
|
277
381
|
\u251C\u2500\u2500 hooks/ # Lifecycle shell hooks (Claude Code)
|
|
278
|
-
\u251C\u2500\u2500 agents/ # Agent definitions (Claude Code, Cursor)
|
|
382
|
+
\u251C\u2500\u2500 agents/ # Agent definitions (Claude Code, Cursor, OpenCode, Gemini CLI)
|
|
279
383
|
\u2502 \u2514\u2500\u2500 <agent-name>/
|
|
280
384
|
\u2502 \u2514\u2500\u2500 AGENT.md
|
|
281
385
|
\u251C\u2500\u2500 rules/ # Rule definitions (Cursor)
|
|
@@ -299,8 +403,23 @@ set-prompt scaffold .
|
|
|
299
403
|
set-prompt install https://github.com/you/my-prompts
|
|
300
404
|
set-prompt link
|
|
301
405
|
|
|
302
|
-
#
|
|
303
|
-
set-prompt
|
|
406
|
+
# Inspect current state (branch, ahead/behind, changed files)
|
|
407
|
+
set-prompt repo status
|
|
408
|
+
|
|
409
|
+
# Pull latest changes from remote
|
|
410
|
+
set-prompt repo pull
|
|
411
|
+
|
|
412
|
+
# Commit + push local edits in one step (auto-generates message if -m omitted)
|
|
413
|
+
set-prompt repo save -m "update skills"
|
|
414
|
+
set-prompt repo save
|
|
415
|
+
|
|
416
|
+
# Or commit and push separately
|
|
417
|
+
set-prompt repo commit -m "update skills"
|
|
418
|
+
set-prompt repo push
|
|
419
|
+
|
|
420
|
+
# Jump into the repo or open it in an editor
|
|
421
|
+
cd "$(set-prompt repo path)"
|
|
422
|
+
set-prompt repo open --code
|
|
304
423
|
\`\`\`
|
|
305
424
|
|
|
306
425
|
## Frontmatter Reference
|
|
@@ -359,13 +478,27 @@ compatibility: "Requires Node.js 18+"
|
|
|
359
478
|
metadata:
|
|
360
479
|
category: "development"
|
|
361
480
|
disable-model-invocation: false
|
|
481
|
+
|
|
482
|
+
# OpenCode
|
|
483
|
+
name: "my-skill"
|
|
484
|
+
description: "What this skill does and when to use it"
|
|
485
|
+
license: "MIT"
|
|
486
|
+
compatibility: "opencode"
|
|
487
|
+
metadata:
|
|
488
|
+
audience: "maintainers"
|
|
489
|
+
|
|
490
|
+
# Gemini CLI
|
|
491
|
+
name: my-skill
|
|
492
|
+
description: "What this skill does and when Gemini should use it"
|
|
362
493
|
---
|
|
363
494
|
\`\`\`
|
|
364
495
|
|
|
496
|
+
> **Gemini CLI note**: Only \`name\` and \`description\` are recognized. \`name\` must be lowercase with hyphens and match the directory name.
|
|
497
|
+
|
|
365
498
|
| Field | Required | Platform | Description |
|
|
366
499
|
|-------|----------|----------|-------------|
|
|
367
|
-
| \`name\` | Yes | All | Display name. Claude Code: lowercase, numbers, hyphens only (
|
|
368
|
-
| \`description\` | Yes | All | What it does and when to use it. Claude uses this to decide auto-loading. |
|
|
500
|
+
| \`name\` | Yes | All | Display name. Claude Code / OpenCode / RooCode: lowercase, numbers, hyphens only (no underscores; Claude Code / OpenCode limit to 64 chars). Gemini CLI: lowercase with hyphens, must match the directory name. Antigravity: optional, defaults to folder name. |
|
|
501
|
+
| \`description\` | Yes | All | What it does and when to use it. Claude / OpenCode / Gemini CLI uses this to decide auto-loading. OpenCode limits to 1\u20131024 chars. |
|
|
369
502
|
| \`allowed-tools\` | No | Claude Code | Tools Claude can use without asking. e.g. \`Read\` \`Write\` \`Edit\` \`Bash\` \`Grep\` \`Glob\` |
|
|
370
503
|
| \`model\` | No | Claude Code | Model to use when active. \`sonnet\` or \`haiku\` |
|
|
371
504
|
| \`context\` | No | Claude Code | \`fork\` = run in a forked subagent context |
|
|
@@ -384,9 +517,9 @@ disable-model-invocation: false
|
|
|
384
517
|
| \`command-dispatch\` | No | OpenClaw | \`"tool"\` = bypass model, dispatch directly to a tool |
|
|
385
518
|
| \`command-tool\` | No | OpenClaw | Tool to invoke when \`command-dispatch: "tool"\` |
|
|
386
519
|
| \`command-arg-mode\` | No | OpenClaw | How arguments are forwarded to the tool. (default: \`"raw"\`) |
|
|
387
|
-
| \`license\` | No | Cursor | License name or reference to a bundled license file. |
|
|
388
|
-
| \`compatibility\` | No | Cursor | Environment requirements (system packages, network access, etc.) |
|
|
389
|
-
| \`metadata\` | No | Cursor | Arbitrary key-value mapping for additional metadata. |
|
|
520
|
+
| \`license\` | No | Cursor, OpenCode | License name or reference to a bundled license file. |
|
|
521
|
+
| \`compatibility\` | No | Cursor, OpenCode | Environment requirements (system packages, network access, etc.) |
|
|
522
|
+
| \`metadata\` | No | Cursor, OpenCode | Arbitrary key-value mapping for additional metadata. OpenCode requires string-to-string values only. |
|
|
390
523
|
|
|
391
524
|
---
|
|
392
525
|
|
|
@@ -423,9 +556,41 @@ hooks:
|
|
|
423
556
|
user-invocable: true
|
|
424
557
|
command-dispatch: "tool" # bypass model, dispatch directly to a tool
|
|
425
558
|
command-tool: "Bash"
|
|
559
|
+
|
|
560
|
+
# OpenCode
|
|
561
|
+
description: "What this command does"
|
|
562
|
+
template: "Run the full test suite and summarise failures for $ARGUMENTS"
|
|
563
|
+
agent: build
|
|
564
|
+
model: anthropic/claude-sonnet-4-20250514
|
|
565
|
+
subtask: false
|
|
426
566
|
---
|
|
427
567
|
\`\`\`
|
|
428
568
|
|
|
569
|
+
> **OpenCode note**: \`template\` is required in the frontmatter \u2014 it's the prompt sent to the LLM when the command runs. The markdown body is ignored. Placeholders: \`$ARGUMENTS\`, \`$1\`/\`$2\`/\`$3\`, \`\` !\`cmd\` \`\` (bash output), \`@path\` (file contents).
|
|
570
|
+
|
|
571
|
+
#### Gemini CLI commands (TOML, not Markdown)
|
|
572
|
+
|
|
573
|
+
Gemini CLI reads \`.toml\` files \u2014 not \`.md\`. The entire command is a TOML file with two top-level keys:
|
|
574
|
+
|
|
575
|
+
\`\`\`toml
|
|
576
|
+
# ~/.gemini/commands/refactor/pure.toml \u2192 /refactor:pure
|
|
577
|
+
description = "Refactor current context into a pure function."
|
|
578
|
+
prompt = """
|
|
579
|
+
Please analyze the code provided.
|
|
580
|
+
Refactor it into a pure function.
|
|
581
|
+
Include: 1) Refactored code. 2) Explanation of changes.
|
|
582
|
+
"""
|
|
583
|
+
\`\`\`
|
|
584
|
+
|
|
585
|
+
| Field | Required | Description |
|
|
586
|
+
|-------|----------|-------------|
|
|
587
|
+
| \`prompt\` | Yes | Single or multi-line prompt sent to Gemini. |
|
|
588
|
+
| \`description\` | No | One-line description shown in \`/help\` menu. Auto-generated from prompt if omitted. |
|
|
589
|
+
|
|
590
|
+
**Placeholders inside the prompt**: \`{{args}}\` (user input, shell-escaped inside \`!{...}\`), \`!{cmd}\` (shell output injection, asks for confirmation), \`@{path}\` (file/dir content injection, supports images/PDFs). If \`{{args}}\` is absent, the user's text is appended to the prompt end.
|
|
591
|
+
|
|
592
|
+
**Namespacing**: subdirectories create namespaced commands \u2014 \`commands/git/commit.toml\` becomes \`/git:commit\`. After edits, run \`/commands reload\` in Gemini CLI.
|
|
593
|
+
|
|
429
594
|
| Field | Required | Platform | Description |
|
|
430
595
|
|-------|----------|----------|-------------|
|
|
431
596
|
| \`name\` | No | All | Display name \u2014 lowercase, numbers, hyphens only (max 64 chars). Defaults to directory name. |
|
|
@@ -437,6 +602,10 @@ command-tool: "Bash"
|
|
|
437
602
|
| \`context\` | No | Claude Code | \`fork\` = run in a forked subagent context |
|
|
438
603
|
| \`agent\` | No | Claude Code | Subagent type when \`context: fork\`. e.g. \`general-purpose\` \`Explore\` \`Plan\` |
|
|
439
604
|
| \`hooks\` | No | Claude Code | Lifecycle hooks for pre/post processing. |
|
|
605
|
+
| \`template\` | Yes | OpenCode | Prompt text sent to the LLM when the command runs. |
|
|
606
|
+
| \`agent\` | No | OpenCode | Agent to route the command to. Defaults to the current agent. |
|
|
607
|
+
| \`model\` | No | OpenCode | Overrides default model. Format: \`provider/model-id\`. |
|
|
608
|
+
| \`subtask\` | No | OpenCode | \`true\` = force subagent invocation to avoid polluting main context. (default: \`false\`) |
|
|
440
609
|
|
|
441
610
|
---
|
|
442
611
|
|
|
@@ -461,18 +630,79 @@ description: "What this agent does and when to use it"
|
|
|
461
630
|
model: inherit
|
|
462
631
|
readonly: false
|
|
463
632
|
is_background: false
|
|
633
|
+
|
|
634
|
+
# OpenCode
|
|
635
|
+
description: "Reviews code for quality and best practices"
|
|
636
|
+
mode: subagent
|
|
637
|
+
model: anthropic/claude-sonnet-4-20250514
|
|
638
|
+
temperature: 0.1
|
|
639
|
+
top_p: 0.9
|
|
640
|
+
steps: 20
|
|
641
|
+
color: "#FF5733"
|
|
642
|
+
hidden: false
|
|
643
|
+
disable: false
|
|
644
|
+
prompt: "{file:./prompts/review.txt}"
|
|
645
|
+
tools:
|
|
646
|
+
write: false
|
|
647
|
+
edit: false
|
|
648
|
+
bash: false
|
|
649
|
+
permission:
|
|
650
|
+
edit: deny
|
|
651
|
+
bash:
|
|
652
|
+
"*": ask
|
|
653
|
+
"git diff": allow
|
|
654
|
+
webfetch: deny
|
|
655
|
+
|
|
656
|
+
# Gemini CLI
|
|
657
|
+
name: security-auditor
|
|
658
|
+
description: "Specialized in finding security vulnerabilities in code."
|
|
659
|
+
kind: local
|
|
660
|
+
tools:
|
|
661
|
+
- read_file
|
|
662
|
+
- grep_search
|
|
663
|
+
model: gemini-3-flash-preview
|
|
664
|
+
temperature: 0.2
|
|
665
|
+
max_turns: 10
|
|
666
|
+
timeout_mins: 15
|
|
667
|
+
mcpServers:
|
|
668
|
+
my-custom-server:
|
|
669
|
+
command: 'node'
|
|
670
|
+
args: ['path/to/server.js']
|
|
464
671
|
---
|
|
465
672
|
\`\`\`
|
|
466
673
|
|
|
674
|
+
> **OpenCode note**: The filename (without \`.md\`) becomes the agent ID. \`name\` is **not** a frontmatter field \u2014 don't include it.
|
|
675
|
+
|
|
676
|
+
> **\u26A0 Gemini CLI note (strict validation)**: Gemini CLI **rejects any unknown frontmatter key** and fails to load the agent with an error like:
|
|
677
|
+
> \`\`\`
|
|
678
|
+
> Agent loading error: ... Validation failed:
|
|
679
|
+
> Unrecognized key(s) in object: 'color', 'skills'
|
|
680
|
+
> \`\`\`
|
|
681
|
+
> Only these keys are allowed: \`name\`, \`description\`, \`kind\`, \`tools\`, \`mcpServers\`, \`model\`, \`temperature\`, \`max_turns\`, \`timeout_mins\`. Fields from other platforms (\`allowed-tools\`, \`mode\`, \`context\`, \`color\`, \`readonly\`, \`is_background\`, etc.) **must not appear** in an agent file if you want Gemini CLI to load it. If you need platform-specific agents, keep them in separate \`.md\` files per agent.
|
|
682
|
+
|
|
467
683
|
| Field | Required | Platform | Description |
|
|
468
684
|
|-------|----------|----------|-------------|
|
|
469
|
-
| \`name\` | Yes |
|
|
685
|
+
| \`name\` | Yes | Claude Code, Cursor, Gemini CLI | Display name. Claude Code: lowercase, numbers, hyphens only (max 64 chars). Cursor: defaults to folder name. OpenCode uses the filename instead. Gemini CLI uses \`name\` as the tool slug. |
|
|
470
686
|
| \`description\` | Yes | All | When and how to use this agent. Used to decide when to spawn/delegate. |
|
|
471
687
|
| \`allowed-tools\` | No | Claude Code | Tools this agent can use without asking |
|
|
472
|
-
| \`
|
|
688
|
+
| \`tools\` | No | OpenCode, Gemini CLI | OpenCode: per-tool enable/disable map with wildcards. Gemini CLI: array of tool names the agent can use (e.g. \`[read_file, grep_search]\`). |
|
|
689
|
+
| \`model\` | No | All | Claude Code: \`sonnet\` or \`haiku\`. Cursor: \`fast\`, \`inherit\`, or a specific model ID. OpenCode: \`provider/model-id\`. Gemini CLI: specific Gemini model override. |
|
|
473
690
|
| \`context\` | No | Claude Code | \`fork\` = run in isolated subagent context |
|
|
474
691
|
| \`readonly\` | No | Cursor | \`true\` = sub-agent runs with restricted write permissions (no file edits or state-changing shell commands). (default: \`false\`) |
|
|
475
692
|
| \`is_background\` | No | Cursor | \`true\` = sub-agent runs in background without blocking parent. (default: \`false\`) |
|
|
693
|
+
| \`mode\` | No | OpenCode | \`primary\`, \`subagent\`, or \`all\`. (default: \`all\`) |
|
|
694
|
+
| \`temperature\` | No | OpenCode, Gemini CLI | Response randomness. OpenCode: \`0.0\`\u2013\`1.0\`. Gemini CLI: \`0.0\`\u2013\`2.0\`. |
|
|
695
|
+
| \`top_p\` | No | OpenCode | Alternative diversity control, \`0.0\`\u2013\`1.0\`. |
|
|
696
|
+
| \`steps\` | No | OpenCode | Max agentic iterations before forcing a text response. |
|
|
697
|
+
| \`disable\` | No | OpenCode | \`true\` = disable the agent. (default: \`false\`) |
|
|
698
|
+
| \`hidden\` | No | OpenCode | \`true\` = hide from \`@\` autocomplete. Subagents only. (default: \`false\`) |
|
|
699
|
+
| \`color\` | No | OpenCode | Hex color (e.g. \`#FF5733\`) or theme color. |
|
|
700
|
+
| \`prompt\` | No | OpenCode | Custom system prompt source. Supports file references like \`{file:./prompts/review.txt}\`. |
|
|
701
|
+
| \`permission\` | No | OpenCode | Fine-grained action permissions. Values: \`allow\`, \`ask\`, \`deny\`. Applies to \`edit\`, \`bash\` (supports per-command globs like \`"git diff": allow\`), \`webfetch\`, \`task\`. |
|
|
702
|
+
| \`kind\` | No | Gemini CLI | \`local\` or \`remote\`. (default: \`local\`) |
|
|
703
|
+
| \`max_turns\` | No | Gemini CLI | Conversation turn limit. (default: \`30\`) |
|
|
704
|
+
| \`timeout_mins\` | No | Gemini CLI | Execution time limit in minutes. (default: \`10\`) |
|
|
705
|
+
| \`mcpServers\` | No | Gemini CLI | Inline MCP server configuration. Each server is a nested YAML map with \`command\` and \`args\` keys \u2014 define MCP servers unique to this agent (not from the global registry). |
|
|
476
706
|
|
|
477
707
|
---
|
|
478
708
|
|
|
@@ -650,43 +880,86 @@ echo '{"permission":"deny","user_message":"Blocked by policy","agent_message":"N
|
|
|
650
880
|
`;
|
|
651
881
|
|
|
652
882
|
// src/commands/scaffold-command.ts
|
|
883
|
+
var validateClaudePluginManifest = (data) => {
|
|
884
|
+
const issues = [];
|
|
885
|
+
if (typeof data !== "object" || data === null) return ["root must be a JSON object"];
|
|
886
|
+
const obj = data;
|
|
887
|
+
if (typeof obj.name !== "string" || obj.name.length === 0) {
|
|
888
|
+
issues.push('"name" is required and must be a non-empty string');
|
|
889
|
+
}
|
|
890
|
+
return issues;
|
|
891
|
+
};
|
|
892
|
+
var validateCodexPluginManifest = (data) => {
|
|
893
|
+
const issues = [];
|
|
894
|
+
if (typeof data !== "object" || data === null) return ["root must be a JSON object"];
|
|
895
|
+
const obj = data;
|
|
896
|
+
if (typeof obj.name !== "string" || obj.name.length === 0) {
|
|
897
|
+
issues.push('"name" is required and must be a non-empty string');
|
|
898
|
+
}
|
|
899
|
+
if (typeof obj.skills !== "string" || obj.skills.length === 0) {
|
|
900
|
+
issues.push('"skills" is required and must be a string path (e.g. "./skills/")');
|
|
901
|
+
}
|
|
902
|
+
if (typeof obj.mcpServers !== "string" || obj.mcpServers.length === 0) {
|
|
903
|
+
issues.push('"mcpServers" is required and must be a string path (e.g. "./.mcp.json")');
|
|
904
|
+
}
|
|
905
|
+
if (typeof obj.apps !== "string" || obj.apps.length === 0) {
|
|
906
|
+
issues.push('"apps" is required and must be a string path (e.g. "./.app.json")');
|
|
907
|
+
}
|
|
908
|
+
return issues;
|
|
909
|
+
};
|
|
910
|
+
var ensureManifest = (jsonPath, metaDir, validate, defaultData, label) => {
|
|
911
|
+
if (fs3.existsSync(jsonPath)) {
|
|
912
|
+
try {
|
|
913
|
+
const parsed = JSON.parse(fs3.readFileSync(jsonPath, "utf-8"));
|
|
914
|
+
const issues = validate(parsed);
|
|
915
|
+
if (issues.length === 0) return "valid";
|
|
916
|
+
console.warn(chalk4.yellow(` \u26A0 ${label} has issues \u2014 keeping existing file:`));
|
|
917
|
+
for (const issue of issues) console.warn(chalk4.dim(` ${issue}`));
|
|
918
|
+
return "invalid";
|
|
919
|
+
} catch (ex) {
|
|
920
|
+
console.warn(chalk4.yellow(` \u26A0 ${label} failed to parse: ${ex.message} \u2014 keeping existing file`));
|
|
921
|
+
return "invalid";
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
fs3.mkdirSync(metaDir, { recursive: true });
|
|
925
|
+
fs3.writeFileSync(jsonPath, JSON.stringify(defaultData, null, 4), { encoding: "utf-8" });
|
|
926
|
+
return "created";
|
|
927
|
+
};
|
|
653
928
|
var ensureClaudePluginManifest = (repoPath) => {
|
|
654
929
|
const metaDir = path2.join(repoPath, ".claude-plugin");
|
|
655
930
|
const jsonPath = path2.join(metaDir, "plugin.json");
|
|
656
|
-
|
|
657
|
-
fs2.writeFileSync(jsonPath, JSON.stringify({
|
|
931
|
+
return ensureManifest(jsonPath, metaDir, validateClaudePluginManifest, {
|
|
658
932
|
name: PLUGIN_NAME,
|
|
659
933
|
version: "1.0.0",
|
|
660
934
|
description: "Managed by set-prompt"
|
|
661
|
-
},
|
|
935
|
+
}, ".claude-plugin/plugin.json");
|
|
662
936
|
};
|
|
663
937
|
var ensureCodexPluginManifest = (repoPath) => {
|
|
664
938
|
const metaDir = path2.join(repoPath, ".codex-plugin");
|
|
665
939
|
const jsonPath = path2.join(metaDir, "plugin.json");
|
|
666
|
-
|
|
667
|
-
fs2.writeFileSync(jsonPath, JSON.stringify({
|
|
940
|
+
return ensureManifest(jsonPath, metaDir, validateCodexPluginManifest, {
|
|
668
941
|
name: PLUGIN_NAME,
|
|
669
942
|
version: "1.0.0",
|
|
670
943
|
description: "Managed by set-prompt",
|
|
671
944
|
skills: "./skills/",
|
|
672
945
|
mcpServers: "./.mcp.json",
|
|
673
946
|
apps: "./.app.json"
|
|
674
|
-
},
|
|
947
|
+
}, ".codex-plugin/plugin.json");
|
|
675
948
|
};
|
|
676
949
|
var ensureMcpJson = (repoPath) => {
|
|
677
950
|
const mcpJsonPath = path2.join(repoPath, ".mcp.json");
|
|
678
|
-
if (
|
|
951
|
+
if (fs3.existsSync(mcpJsonPath)) {
|
|
679
952
|
return false;
|
|
680
953
|
}
|
|
681
|
-
|
|
954
|
+
fs3.writeFileSync(mcpJsonPath, JSON.stringify({ mcpServers: {} }, null, 4), { encoding: "utf-8" });
|
|
682
955
|
return true;
|
|
683
956
|
};
|
|
684
957
|
var ensureAppJson = (repoPath) => {
|
|
685
958
|
const appJsonPath = path2.join(repoPath, ".app.json");
|
|
686
|
-
if (
|
|
959
|
+
if (fs3.existsSync(appJsonPath)) {
|
|
687
960
|
return false;
|
|
688
961
|
}
|
|
689
|
-
|
|
962
|
+
fs3.writeFileSync(appJsonPath, JSON.stringify({ apps: {} }, null, 4), { encoding: "utf-8" });
|
|
690
963
|
return true;
|
|
691
964
|
};
|
|
692
965
|
var scaffoldCommand = async (localPath) => {
|
|
@@ -698,15 +971,15 @@ var scaffoldCommand = async (localPath) => {
|
|
|
698
971
|
if (configManager.repo_path != null) {
|
|
699
972
|
targetPath = configManager.repo_path;
|
|
700
973
|
} else {
|
|
701
|
-
console.error(
|
|
974
|
+
console.error(chalk4.red("No path provided and no repo registered. Please provide a path."));
|
|
702
975
|
process.exit(1);
|
|
703
976
|
}
|
|
704
977
|
}
|
|
705
|
-
if (
|
|
706
|
-
console.error(
|
|
978
|
+
if (fs3.existsSync(targetPath) === false || fs3.statSync(targetPath).isDirectory() === false) {
|
|
979
|
+
console.error(chalk4.red(`Invalid directory path: '${targetPath}'`));
|
|
707
980
|
process.exit(1);
|
|
708
981
|
}
|
|
709
|
-
console.log(
|
|
982
|
+
console.log(chalk4.dim(`Scaffolding: ${targetPath}
|
|
710
983
|
`));
|
|
711
984
|
const writeGuide = await confirm({
|
|
712
985
|
message: "Generate SET_PROMPT_GUIDE.md? (reference doc for writing prompts)",
|
|
@@ -714,32 +987,36 @@ var scaffoldCommand = async (localPath) => {
|
|
|
714
987
|
});
|
|
715
988
|
if (writeGuide) {
|
|
716
989
|
const guideMdPath = path2.join(targetPath, "SET_PROMPT_GUIDE.md");
|
|
717
|
-
|
|
718
|
-
console.log(`${TAB}${
|
|
990
|
+
fs3.writeFileSync(guideMdPath, SET_PROMPT_GUIDE, { encoding: "utf-8", flag: "w" });
|
|
991
|
+
console.log(`${TAB}${chalk4.green("\u2713")} SET_PROMPT_GUIDE.md`);
|
|
719
992
|
}
|
|
720
993
|
for (const dirName of PROMPT_DIR_NAMES) {
|
|
721
994
|
const dirPath = path2.join(targetPath, dirName);
|
|
722
|
-
if (
|
|
723
|
-
console.log(`${TAB}${
|
|
995
|
+
if (fs3.existsSync(dirPath)) {
|
|
996
|
+
console.log(`${TAB}${chalk4.dim("\u2713")} ${dirName}/`);
|
|
724
997
|
} else {
|
|
725
|
-
|
|
726
|
-
console.log(`${TAB}${
|
|
998
|
+
fs3.mkdirSync(dirPath, { recursive: true });
|
|
999
|
+
console.log(`${TAB}${chalk4.green("+")} ${dirName}/`);
|
|
727
1000
|
}
|
|
728
1001
|
const gitkeepPath = path2.join(dirPath, ".gitkeep");
|
|
729
|
-
if (!
|
|
730
|
-
|
|
1002
|
+
if (!fs3.existsSync(gitkeepPath)) {
|
|
1003
|
+
fs3.writeFileSync(gitkeepPath, "", { encoding: "utf-8" });
|
|
731
1004
|
}
|
|
732
1005
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
console.log(`${TAB}${
|
|
738
|
-
console.log(`${TAB}${
|
|
739
|
-
console.log(
|
|
1006
|
+
const symFor = (r) => r === "created" ? chalk4.green("+") : r === "valid" ? chalk4.dim("\u2713") : chalk4.yellow("\u26A0");
|
|
1007
|
+
const claudeResult = ensureClaudePluginManifest(targetPath);
|
|
1008
|
+
console.log(`${TAB}${symFor(claudeResult)} .claude-plugin/plugin.json`);
|
|
1009
|
+
const codexResult = ensureCodexPluginManifest(targetPath);
|
|
1010
|
+
console.log(`${TAB}${symFor(codexResult)} .codex-plugin/plugin.json`);
|
|
1011
|
+
console.log(`${TAB}${ensureMcpJson(targetPath) ? chalk4.green("+") : chalk4.dim("\u2713")} .mcp.json`);
|
|
1012
|
+
console.log(`${TAB}${ensureAppJson(targetPath) ? chalk4.green("+") : chalk4.dim("\u2713")} .app.json`);
|
|
1013
|
+
console.log(chalk4.green("\nScaffold complete."));
|
|
1014
|
+
if (configManager.repo_path != null && path2.resolve(targetPath) === path2.resolve(configManager.repo_path)) {
|
|
1015
|
+
printSaveHint(targetPath);
|
|
1016
|
+
}
|
|
740
1017
|
return true;
|
|
741
1018
|
} catch (ex) {
|
|
742
|
-
console.error(
|
|
1019
|
+
console.error(chalk4.red(`Failed to scaffold repo structure: ${ex.message}`), ex);
|
|
743
1020
|
throw ex;
|
|
744
1021
|
}
|
|
745
1022
|
};
|
|
@@ -748,86 +1025,87 @@ var scaffoldCommand = async (localPath) => {
|
|
|
748
1025
|
var cloneRepo = async (remoteUrl) => {
|
|
749
1026
|
const localPath = path3.join(HOME_DIR, "repo");
|
|
750
1027
|
let backupPath = null;
|
|
751
|
-
if (
|
|
1028
|
+
if (fs4.existsSync(localPath) == true) {
|
|
752
1029
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
753
1030
|
backupPath = path3.join(HOME_DIR, `repo.bak.${timestamp}`);
|
|
754
1031
|
try {
|
|
755
|
-
|
|
756
|
-
console.log(
|
|
1032
|
+
fs4.renameSync(localPath, backupPath);
|
|
1033
|
+
console.log(chalk5.yellow(" backed up") + chalk5.dim(` existing repo \u2192 ${backupPath}`));
|
|
757
1034
|
} catch (ex) {
|
|
758
1035
|
if (ex.code === "EPERM") {
|
|
759
|
-
console.error(
|
|
760
|
-
console.log(
|
|
1036
|
+
console.error(chalk5.red("\u274C Cannot rename existing repo \u2014 it may be open in another process."));
|
|
1037
|
+
console.log(chalk5.dim(` Close any editors or terminals using: ${localPath}`));
|
|
761
1038
|
} else {
|
|
762
|
-
console.error(
|
|
1039
|
+
console.error(chalk5.red(`\u274C Failed to backup existing repo: ${ex.message}`));
|
|
763
1040
|
}
|
|
764
1041
|
return false;
|
|
765
1042
|
}
|
|
766
1043
|
}
|
|
767
|
-
|
|
1044
|
+
fs4.mkdirSync(path3.dirname(localPath), { recursive: true });
|
|
768
1045
|
console.log(`Cloning ${remoteUrl}...`);
|
|
769
|
-
const result =
|
|
1046
|
+
const result = spawnSync3("git", ["clone", remoteUrl, localPath], { stdio: "inherit" });
|
|
770
1047
|
if (result.status !== 0) {
|
|
771
1048
|
console.log("\u274C Failed to clone. Check the URL and your git credentials.");
|
|
772
1049
|
process.exit(1);
|
|
773
1050
|
}
|
|
774
1051
|
console.log("\u2705 Cloned successfully.");
|
|
775
1052
|
if (backupPath != null) {
|
|
776
|
-
|
|
777
|
-
console.log(
|
|
1053
|
+
fs4.rmSync(backupPath, { recursive: true, force: true });
|
|
1054
|
+
console.log(chalk5.red(" removed") + chalk5.dim(` backup \u2192 ${backupPath}`));
|
|
778
1055
|
}
|
|
779
|
-
await scaffoldCommand(localPath
|
|
1056
|
+
await scaffoldCommand(localPath);
|
|
780
1057
|
configManager.repo_path = localPath;
|
|
781
1058
|
configManager.remote_url = remoteUrl;
|
|
782
1059
|
if (configManager.save() === false) {
|
|
783
|
-
console.error(
|
|
1060
|
+
console.error(chalk5.red("Failed to save config."));
|
|
784
1061
|
return false;
|
|
785
1062
|
}
|
|
1063
|
+
printSaveHint(localPath);
|
|
786
1064
|
return true;
|
|
787
1065
|
};
|
|
788
1066
|
var installCommand = async (target) => {
|
|
789
1067
|
try {
|
|
790
1068
|
if (isGitUrl(target) === false) {
|
|
791
|
-
console.error(
|
|
792
|
-
console.log(
|
|
1069
|
+
console.error(chalk5.red("\u274C Only remote git URLs are supported."));
|
|
1070
|
+
console.log(chalk5.dim(" Example: set-prompt install https://github.com/you/my-prompts"));
|
|
793
1071
|
process.exit(1);
|
|
794
1072
|
}
|
|
795
1073
|
const normalizeUrl = (url) => url.replace(/\.git$/, "").toLowerCase();
|
|
796
1074
|
if (configManager.repo_path != null) {
|
|
797
1075
|
if (normalizeUrl(configManager.remote_url ?? "") === normalizeUrl(target)) {
|
|
798
|
-
console.error(
|
|
799
|
-
console.log(
|
|
1076
|
+
console.error(chalk5.red(`\u274C Already installed from the same URL: ${target}`));
|
|
1077
|
+
console.log(chalk5.dim(" Use `set-prompt repo pull` to pull the latest changes."));
|
|
800
1078
|
return false;
|
|
801
1079
|
}
|
|
802
|
-
console.warn(
|
|
1080
|
+
console.warn(chalk5.yellow(`\u26A0 Switching repo: ${configManager.remote_url} \u2192 ${target}`));
|
|
803
1081
|
const proceed = await confirm2({ message: "Replace existing installation?", default: false });
|
|
804
1082
|
if (!proceed) {
|
|
805
|
-
console.log(
|
|
1083
|
+
console.log(chalk5.yellow("Cancelled."));
|
|
806
1084
|
return false;
|
|
807
1085
|
}
|
|
808
1086
|
} else {
|
|
809
1087
|
const proceed = await confirm2({ message: `Clone and register "${target}"?`, default: true });
|
|
810
1088
|
if (!proceed) {
|
|
811
|
-
console.log(
|
|
1089
|
+
console.log(chalk5.yellow("Cancelled."));
|
|
812
1090
|
return false;
|
|
813
1091
|
}
|
|
814
1092
|
}
|
|
815
1093
|
return await cloneRepo(target);
|
|
816
1094
|
} catch (ex) {
|
|
817
|
-
console.error(
|
|
1095
|
+
console.error(chalk5.red(`Unexpected error: ${ex.message}`), ex);
|
|
818
1096
|
process.exit(1);
|
|
819
1097
|
}
|
|
820
1098
|
};
|
|
821
1099
|
|
|
822
1100
|
// src/commands/link-command.ts
|
|
823
|
-
import
|
|
1101
|
+
import chalk14 from "chalk";
|
|
824
1102
|
import { checkbox } from "@inquirer/prompts";
|
|
825
1103
|
|
|
826
1104
|
// src/link/claudecode.ts
|
|
827
1105
|
import path4 from "path";
|
|
828
|
-
import
|
|
1106
|
+
import fs5 from "fs";
|
|
829
1107
|
import os2 from "os";
|
|
830
|
-
import
|
|
1108
|
+
import chalk6 from "chalk";
|
|
831
1109
|
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
832
1110
|
var linkClaudeCode = async () => {
|
|
833
1111
|
const repoPath = resolveRepoPath();
|
|
@@ -837,32 +1115,32 @@ var linkClaudeCode = async () => {
|
|
|
837
1115
|
const buildMarketplace = () => {
|
|
838
1116
|
try {
|
|
839
1117
|
const marketplaceMetaDir = path4.join(CLAUDE_CODE_DIR, ".claude-plugin");
|
|
840
|
-
|
|
1118
|
+
fs5.mkdirSync(marketplaceMetaDir, { recursive: true });
|
|
841
1119
|
const marketplaceJson = {
|
|
842
1120
|
name: MARKET_NAME,
|
|
843
1121
|
owner: { name: os2.userInfo().username },
|
|
844
1122
|
metadata: { description: "Managed by set-prompt", version: "1.0.0" },
|
|
845
1123
|
plugins: [{ name: PLUGIN_NAME, source: `./plugins/${PLUGIN_NAME}`, description: "Managed by set-prompt" }]
|
|
846
1124
|
};
|
|
847
|
-
|
|
1125
|
+
fs5.writeFileSync(
|
|
848
1126
|
path4.join(marketplaceMetaDir, "marketplace.json"),
|
|
849
1127
|
JSON.stringify(marketplaceJson, null, 4),
|
|
850
1128
|
"utf-8"
|
|
851
1129
|
);
|
|
852
|
-
console.log(
|
|
853
|
-
console.log(
|
|
1130
|
+
console.log(chalk6.dim(" \u251C\u2500\u2500 .claude-plugin/"));
|
|
1131
|
+
console.log(chalk6.dim(" \u2502 \u2514\u2500\u2500 marketplace.json") + chalk6.green(" \u2713"));
|
|
854
1132
|
const pluginLink = path4.join(CLAUDE_CODE_DIR, "plugins", PLUGIN_NAME);
|
|
855
|
-
|
|
856
|
-
if (
|
|
857
|
-
|
|
1133
|
+
fs5.mkdirSync(path4.dirname(pluginLink), { recursive: true });
|
|
1134
|
+
if (fs5.existsSync(pluginLink)) {
|
|
1135
|
+
fs5.rmSync(pluginLink, { recursive: true, force: true });
|
|
858
1136
|
}
|
|
859
1137
|
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
860
|
-
|
|
861
|
-
console.log(
|
|
862
|
-
console.log(
|
|
1138
|
+
fs5.symlinkSync(repoPath, pluginLink, symlinkType);
|
|
1139
|
+
console.log(chalk6.dim(" \u2514\u2500\u2500 plugins/"));
|
|
1140
|
+
console.log(chalk6.dim(` \u2514\u2500\u2500 ${PLUGIN_NAME}/`) + chalk6.dim(` \u2192 ${repoPath}`) + chalk6.green(" \u2713"));
|
|
863
1141
|
return true;
|
|
864
1142
|
} catch (ex) {
|
|
865
|
-
console.error(
|
|
1143
|
+
console.error(chalk6.red(`\u274C Failed to build marketplace structure: ${ex.message}`));
|
|
866
1144
|
return false;
|
|
867
1145
|
}
|
|
868
1146
|
};
|
|
@@ -870,18 +1148,18 @@ var linkClaudeCode = async () => {
|
|
|
870
1148
|
const claudeSettingsPath = path4.join(os2.homedir(), ".claude", "settings.json");
|
|
871
1149
|
try {
|
|
872
1150
|
let settings = {};
|
|
873
|
-
if (
|
|
874
|
-
const raw =
|
|
1151
|
+
if (fs5.existsSync(claudeSettingsPath)) {
|
|
1152
|
+
const raw = fs5.readFileSync(claudeSettingsPath, "utf-8");
|
|
875
1153
|
try {
|
|
876
1154
|
const parsed = JSON.parse(raw);
|
|
877
1155
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
878
1156
|
settings = parsed;
|
|
879
1157
|
} else {
|
|
880
|
-
console.warn(
|
|
1158
|
+
console.warn(chalk6.yellow(" \u26A0 settings.json has unexpected format \u2014 proceeding with caution"));
|
|
881
1159
|
}
|
|
882
1160
|
} catch {
|
|
883
|
-
console.warn(
|
|
884
|
-
console.error(
|
|
1161
|
+
console.warn(chalk6.yellow(" \u26A0 Failed to parse settings.json \u2014 will not overwrite existing file"));
|
|
1162
|
+
console.error(chalk6.red("\u274C Could not register plugin. Please add manually."));
|
|
885
1163
|
return false;
|
|
886
1164
|
}
|
|
887
1165
|
}
|
|
@@ -894,43 +1172,43 @@ var linkClaudeCode = async () => {
|
|
|
894
1172
|
[`${PLUGIN_NAME}@${MARKET_NAME}`]: true
|
|
895
1173
|
};
|
|
896
1174
|
let backupPath = null;
|
|
897
|
-
if (
|
|
1175
|
+
if (fs5.existsSync(claudeSettingsPath)) {
|
|
898
1176
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
899
1177
|
backupPath = `${claudeSettingsPath}.bak.${timestamp}`;
|
|
900
1178
|
try {
|
|
901
|
-
|
|
1179
|
+
fs5.copyFileSync(claudeSettingsPath, backupPath);
|
|
902
1180
|
} catch (ex) {
|
|
903
|
-
console.warn(
|
|
1181
|
+
console.warn(chalk6.yellow(` \u26A0 Could not create backup: ${ex.message}`));
|
|
904
1182
|
backupPath = null;
|
|
905
1183
|
}
|
|
906
1184
|
}
|
|
907
1185
|
try {
|
|
908
|
-
|
|
909
|
-
|
|
1186
|
+
fs5.mkdirSync(path4.dirname(claudeSettingsPath), { recursive: true });
|
|
1187
|
+
fs5.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 4), "utf-8");
|
|
910
1188
|
} catch (ex) {
|
|
911
1189
|
if (backupPath !== null) {
|
|
912
1190
|
try {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
console.warn(
|
|
1191
|
+
fs5.copyFileSync(backupPath, claudeSettingsPath);
|
|
1192
|
+
fs5.unlinkSync(backupPath);
|
|
1193
|
+
console.warn(chalk6.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
|
|
916
1194
|
} catch {
|
|
917
|
-
console.error(
|
|
1195
|
+
console.error(chalk6.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
|
|
918
1196
|
}
|
|
919
1197
|
}
|
|
920
1198
|
throw ex;
|
|
921
1199
|
}
|
|
922
1200
|
if (backupPath !== null) {
|
|
923
1201
|
try {
|
|
924
|
-
|
|
1202
|
+
fs5.unlinkSync(backupPath);
|
|
925
1203
|
} catch {
|
|
926
1204
|
}
|
|
927
1205
|
}
|
|
928
1206
|
console.log(`\u2705 Registered to Claude Code settings.`);
|
|
929
|
-
console.log(
|
|
1207
|
+
console.log(chalk6.dim(` ${claudeSettingsPath}`));
|
|
930
1208
|
return true;
|
|
931
1209
|
} catch (ex) {
|
|
932
|
-
console.error(
|
|
933
|
-
console.log(
|
|
1210
|
+
console.error(chalk6.red(`\u274C Failed to update settings.json: ${ex.message}`));
|
|
1211
|
+
console.log(chalk6.dim(" Please add the plugin manually via Claude Code /plugins."));
|
|
934
1212
|
return false;
|
|
935
1213
|
}
|
|
936
1214
|
};
|
|
@@ -940,9 +1218,9 @@ var linkClaudeCode = async () => {
|
|
|
940
1218
|
const pluginKey = `${PLUGIN_NAME}@${MARKET_NAME}`;
|
|
941
1219
|
try {
|
|
942
1220
|
let data = { version: 2, plugins: {} };
|
|
943
|
-
if (
|
|
1221
|
+
if (fs5.existsSync(installedPluginsPath)) {
|
|
944
1222
|
try {
|
|
945
|
-
data = JSON.parse(
|
|
1223
|
+
data = JSON.parse(fs5.readFileSync(installedPluginsPath, "utf-8"));
|
|
946
1224
|
} catch {
|
|
947
1225
|
}
|
|
948
1226
|
}
|
|
@@ -958,17 +1236,17 @@ var linkClaudeCode = async () => {
|
|
|
958
1236
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
959
1237
|
}
|
|
960
1238
|
];
|
|
961
|
-
|
|
962
|
-
|
|
1239
|
+
fs5.mkdirSync(path4.dirname(installedPluginsPath), { recursive: true });
|
|
1240
|
+
fs5.writeFileSync(installedPluginsPath, JSON.stringify(data, null, 4), "utf-8");
|
|
963
1241
|
console.log(`\u2705 Patched installed_plugins.json \u2192 installPath points to source.`);
|
|
964
|
-
console.log(
|
|
1242
|
+
console.log(chalk6.dim(` ${installPath}`));
|
|
965
1243
|
} catch (ex) {
|
|
966
|
-
console.warn(
|
|
1244
|
+
console.warn(chalk6.yellow(` \u26A0 Could not patch installed_plugins.json: ${ex.message}`));
|
|
967
1245
|
}
|
|
968
1246
|
};
|
|
969
|
-
console.log(
|
|
1247
|
+
console.log(chalk6.green(`
|
|
970
1248
|
Setting up Claude Code plugin...`));
|
|
971
|
-
console.log(
|
|
1249
|
+
console.log(chalk6.dim(CLAUDE_CODE_DIR));
|
|
972
1250
|
const marketplaceOk = buildMarketplace();
|
|
973
1251
|
if (marketplaceOk === false) {
|
|
974
1252
|
return;
|
|
@@ -988,20 +1266,20 @@ var unlinkClaudeCode = async (force = false) => {
|
|
|
988
1266
|
default: false
|
|
989
1267
|
});
|
|
990
1268
|
if (!ok) {
|
|
991
|
-
console.log(
|
|
1269
|
+
console.log(chalk6.yellow("Cancelled."));
|
|
992
1270
|
return;
|
|
993
1271
|
}
|
|
994
1272
|
}
|
|
995
|
-
console.log(
|
|
1273
|
+
console.log(chalk6.red(`
|
|
996
1274
|
Removing Claude Code plugin...`));
|
|
997
|
-
console.log(
|
|
1275
|
+
console.log(chalk6.dim(CLAUDE_CODE_DIR));
|
|
998
1276
|
const claudeSettingsPath = path4.join(os2.homedir(), ".claude", "settings.json");
|
|
999
|
-
if (
|
|
1277
|
+
if (fs5.existsSync(claudeSettingsPath)) {
|
|
1000
1278
|
try {
|
|
1001
1279
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1002
1280
|
const backupPath = `${claudeSettingsPath}.bak.${timestamp}`;
|
|
1003
|
-
|
|
1004
|
-
const settings = JSON.parse(
|
|
1281
|
+
fs5.copyFileSync(claudeSettingsPath, backupPath);
|
|
1282
|
+
const settings = JSON.parse(fs5.readFileSync(claudeSettingsPath, "utf-8"));
|
|
1005
1283
|
if (settings?.extraKnownMarketplaces?.[MARKET_NAME] !== void 0) {
|
|
1006
1284
|
delete settings.extraKnownMarketplaces[MARKET_NAME];
|
|
1007
1285
|
}
|
|
@@ -1009,26 +1287,26 @@ Removing Claude Code plugin...`));
|
|
|
1009
1287
|
delete settings.enabledPlugins[`${PLUGIN_NAME}@${MARKET_NAME}`];
|
|
1010
1288
|
}
|
|
1011
1289
|
try {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
console.log(
|
|
1290
|
+
fs5.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 4), "utf-8");
|
|
1291
|
+
fs5.unlinkSync(backupPath);
|
|
1292
|
+
console.log(chalk6.red(" removed") + chalk6.dim(` set-prompt entries from: ${claudeSettingsPath}`));
|
|
1015
1293
|
} catch (ex) {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
console.warn(
|
|
1294
|
+
fs5.copyFileSync(backupPath, claudeSettingsPath);
|
|
1295
|
+
fs5.unlinkSync(backupPath);
|
|
1296
|
+
console.warn(chalk6.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
|
|
1019
1297
|
}
|
|
1020
1298
|
} catch (ex) {
|
|
1021
|
-
console.error(
|
|
1299
|
+
console.error(chalk6.red(` \u274C Failed to clean up settings.json: ${ex.message}`));
|
|
1022
1300
|
}
|
|
1023
1301
|
}
|
|
1024
1302
|
const claudePluginsDir = path4.join(os2.homedir(), ".claude", "plugins");
|
|
1025
1303
|
const installedPluginsPath = path4.join(claudePluginsDir, "installed_plugins.json");
|
|
1026
|
-
if (
|
|
1304
|
+
if (fs5.existsSync(installedPluginsPath)) {
|
|
1027
1305
|
try {
|
|
1028
1306
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1029
1307
|
const backupPath = `${installedPluginsPath}.bak.${timestamp}`;
|
|
1030
|
-
|
|
1031
|
-
const installed = JSON.parse(
|
|
1308
|
+
fs5.copyFileSync(installedPluginsPath, backupPath);
|
|
1309
|
+
const installed = JSON.parse(fs5.readFileSync(installedPluginsPath, "utf-8"));
|
|
1032
1310
|
if (installed?.plugins && typeof installed.plugins === "object") {
|
|
1033
1311
|
for (const key of Object.keys(installed.plugins)) {
|
|
1034
1312
|
if (key.endsWith(`@${MARKET_NAME}`)) {
|
|
@@ -1037,44 +1315,44 @@ Removing Claude Code plugin...`));
|
|
|
1037
1315
|
}
|
|
1038
1316
|
}
|
|
1039
1317
|
try {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
console.log(
|
|
1318
|
+
fs5.writeFileSync(installedPluginsPath, JSON.stringify(installed, null, 4), "utf-8");
|
|
1319
|
+
fs5.unlinkSync(backupPath);
|
|
1320
|
+
console.log(chalk6.red(" removed") + chalk6.dim(` set-prompt entries from: ${installedPluginsPath}`));
|
|
1043
1321
|
} catch (ex) {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
console.warn(
|
|
1322
|
+
fs5.copyFileSync(backupPath, installedPluginsPath);
|
|
1323
|
+
fs5.unlinkSync(backupPath);
|
|
1324
|
+
console.warn(chalk6.yellow(" \u26A0 Write failed \u2014 rolled back installed_plugins.json."));
|
|
1047
1325
|
}
|
|
1048
1326
|
} catch (ex) {
|
|
1049
|
-
console.error(
|
|
1327
|
+
console.error(chalk6.red(` \u274C Failed to clean up installed_plugins.json: ${ex.message}`));
|
|
1050
1328
|
}
|
|
1051
1329
|
}
|
|
1052
1330
|
const knownMarketplacesPath = path4.join(claudePluginsDir, "known_marketplaces.json");
|
|
1053
|
-
if (
|
|
1331
|
+
if (fs5.existsSync(knownMarketplacesPath)) {
|
|
1054
1332
|
try {
|
|
1055
1333
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1056
1334
|
const backupPath = `${knownMarketplacesPath}.bak.${timestamp}`;
|
|
1057
|
-
|
|
1058
|
-
const marketplaces = JSON.parse(
|
|
1335
|
+
fs5.copyFileSync(knownMarketplacesPath, backupPath);
|
|
1336
|
+
const marketplaces = JSON.parse(fs5.readFileSync(knownMarketplacesPath, "utf-8"));
|
|
1059
1337
|
if (marketplaces?.[MARKET_NAME] !== void 0) {
|
|
1060
1338
|
delete marketplaces[MARKET_NAME];
|
|
1061
1339
|
}
|
|
1062
1340
|
try {
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
console.log(
|
|
1341
|
+
fs5.writeFileSync(knownMarketplacesPath, JSON.stringify(marketplaces, null, 4), "utf-8");
|
|
1342
|
+
fs5.unlinkSync(backupPath);
|
|
1343
|
+
console.log(chalk6.red(" removed") + chalk6.dim(` set-prompt entry from: ${knownMarketplacesPath}`));
|
|
1066
1344
|
} catch (ex) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
console.warn(
|
|
1345
|
+
fs5.copyFileSync(backupPath, knownMarketplacesPath);
|
|
1346
|
+
fs5.unlinkSync(backupPath);
|
|
1347
|
+
console.warn(chalk6.yellow(" \u26A0 Write failed \u2014 rolled back known_marketplaces.json."));
|
|
1070
1348
|
}
|
|
1071
1349
|
} catch (ex) {
|
|
1072
|
-
console.error(
|
|
1350
|
+
console.error(chalk6.red(` \u274C Failed to clean up known_marketplaces.json: ${ex.message}`));
|
|
1073
1351
|
}
|
|
1074
1352
|
}
|
|
1075
|
-
if (
|
|
1076
|
-
|
|
1077
|
-
console.log(
|
|
1353
|
+
if (fs5.existsSync(CLAUDE_CODE_DIR)) {
|
|
1354
|
+
fs5.rmSync(CLAUDE_CODE_DIR, { recursive: true, force: true });
|
|
1355
|
+
console.log(chalk6.red(" removed") + chalk6.dim(`: ${CLAUDE_CODE_DIR}`));
|
|
1078
1356
|
}
|
|
1079
1357
|
configManager.claude_code = null;
|
|
1080
1358
|
configManager.save();
|
|
@@ -1082,8 +1360,8 @@ Removing Claude Code plugin...`));
|
|
|
1082
1360
|
|
|
1083
1361
|
// src/link/roocode.ts
|
|
1084
1362
|
import path5 from "path";
|
|
1085
|
-
import
|
|
1086
|
-
import
|
|
1363
|
+
import fs6 from "fs";
|
|
1364
|
+
import chalk7 from "chalk";
|
|
1087
1365
|
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
1088
1366
|
import { pathExists } from "fs-extra";
|
|
1089
1367
|
var linkRooCode = async () => {
|
|
@@ -1091,44 +1369,44 @@ var linkRooCode = async () => {
|
|
|
1091
1369
|
if (repoPath == null) {
|
|
1092
1370
|
return;
|
|
1093
1371
|
}
|
|
1094
|
-
console.log(
|
|
1372
|
+
console.log(chalk7.green(`
|
|
1095
1373
|
Setting up RooCode integration...`));
|
|
1096
|
-
console.log(
|
|
1374
|
+
console.log(chalk7.dim(ROO_DIR));
|
|
1097
1375
|
const roocodeDirs = AGENT_PROMPT_DIRS["roocode" /* ROOCODE */];
|
|
1098
1376
|
const backupExistingRooCodeFiles = async () => {
|
|
1099
1377
|
try {
|
|
1100
|
-
|
|
1378
|
+
fs6.mkdirSync(ROO_DIR, { recursive: true });
|
|
1101
1379
|
const dirsToBackup = [];
|
|
1102
1380
|
for (const dir of roocodeDirs) {
|
|
1103
1381
|
const target = path5.join(ROO_DIR, dir);
|
|
1104
|
-
if (
|
|
1382
|
+
if (fs6.existsSync(target) && fs6.lstatSync(target).isSymbolicLink() === false && fs6.readdirSync(target).length > 0) {
|
|
1105
1383
|
dirsToBackup.push(dir);
|
|
1106
1384
|
}
|
|
1107
1385
|
}
|
|
1108
1386
|
if (dirsToBackup.length === 0) {
|
|
1109
1387
|
return true;
|
|
1110
1388
|
}
|
|
1111
|
-
console.log(
|
|
1389
|
+
console.log(chalk7.yellow(`
|
|
1112
1390
|
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
1113
1391
|
for (const dir of dirsToBackup) {
|
|
1114
|
-
console.log(
|
|
1392
|
+
console.log(chalk7.dim(` - ${path5.join(ROO_DIR, dir)}`));
|
|
1115
1393
|
}
|
|
1116
|
-
console.log(
|
|
1394
|
+
console.log(chalk7.yellow(` They will be backed up to: `) + chalk7.dim(ROO_BACKUP_DIR));
|
|
1117
1395
|
const ok = await confirm4({ message: "Back up existing directories?", default: true });
|
|
1118
1396
|
if (!ok) {
|
|
1119
|
-
console.log(
|
|
1397
|
+
console.log(chalk7.yellow("Skipped RooCode linking."));
|
|
1120
1398
|
return false;
|
|
1121
1399
|
}
|
|
1122
|
-
|
|
1400
|
+
fs6.mkdirSync(ROO_BACKUP_DIR, { recursive: true });
|
|
1123
1401
|
for (const dir of dirsToBackup) {
|
|
1124
1402
|
const src = path5.join(ROO_DIR, dir);
|
|
1125
1403
|
const dest = path5.join(ROO_BACKUP_DIR, dir);
|
|
1126
|
-
|
|
1127
|
-
console.log(
|
|
1404
|
+
fs6.renameSync(src, dest);
|
|
1405
|
+
console.log(chalk7.yellow(" backed up") + chalk7.dim(`: ${dir}/ \u2192 ${dest}`));
|
|
1128
1406
|
}
|
|
1129
1407
|
return true;
|
|
1130
1408
|
} catch (ex) {
|
|
1131
|
-
console.error(
|
|
1409
|
+
console.error(chalk7.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
1132
1410
|
return false;
|
|
1133
1411
|
}
|
|
1134
1412
|
};
|
|
@@ -1141,21 +1419,21 @@ Setting up RooCode integration...`));
|
|
|
1141
1419
|
if (await pathExists(src) === false) {
|
|
1142
1420
|
continue;
|
|
1143
1421
|
}
|
|
1144
|
-
if (
|
|
1145
|
-
|
|
1422
|
+
if (fs6.existsSync(dest)) {
|
|
1423
|
+
fs6.rmSync(dest, { recursive: true, force: true });
|
|
1146
1424
|
}
|
|
1147
1425
|
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1148
|
-
|
|
1426
|
+
fs6.symlinkSync(src, dest, symlinkType);
|
|
1149
1427
|
linked.push({ dir, src });
|
|
1150
1428
|
}
|
|
1151
1429
|
for (const { dir, src } of linked) {
|
|
1152
1430
|
const isLast = linked[linked.length - 1].dir === dir;
|
|
1153
1431
|
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
1154
|
-
console.log(
|
|
1432
|
+
console.log(chalk7.dim(` ${branch} `) + chalk7.bold(`${dir}/`) + chalk7.dim(` \u2192 ${src}`) + chalk7.green(" \u2713"));
|
|
1155
1433
|
}
|
|
1156
1434
|
return true;
|
|
1157
1435
|
} catch (ex) {
|
|
1158
|
-
console.error(
|
|
1436
|
+
console.error(chalk7.red(`\u274C Failed to create symlinks: ${ex.message}`));
|
|
1159
1437
|
return false;
|
|
1160
1438
|
}
|
|
1161
1439
|
};
|
|
@@ -1177,35 +1455,35 @@ var unlinkRooCode = async (force = false) => {
|
|
|
1177
1455
|
default: false
|
|
1178
1456
|
});
|
|
1179
1457
|
if (!ok) {
|
|
1180
|
-
console.log(
|
|
1458
|
+
console.log(chalk7.yellow("Cancelled."));
|
|
1181
1459
|
return;
|
|
1182
1460
|
}
|
|
1183
1461
|
}
|
|
1184
|
-
console.log(
|
|
1462
|
+
console.log(chalk7.red(`
|
|
1185
1463
|
Removing RooCode integration...`));
|
|
1186
|
-
console.log(
|
|
1464
|
+
console.log(chalk7.dim(ROO_DIR));
|
|
1187
1465
|
const backupPath = configManager.roocode?.backup_path ?? ROO_BACKUP_DIR;
|
|
1188
1466
|
for (const dir of AGENT_PROMPT_DIRS["roocode" /* ROOCODE */]) {
|
|
1189
1467
|
const target = path5.join(ROO_DIR, dir);
|
|
1190
|
-
if (
|
|
1191
|
-
|
|
1192
|
-
console.log(
|
|
1468
|
+
if (fs6.existsSync(target) && fs6.lstatSync(target).isSymbolicLink()) {
|
|
1469
|
+
fs6.unlinkSync(target);
|
|
1470
|
+
console.log(chalk7.red(" removed symlink") + chalk7.dim(`: ${target}`));
|
|
1193
1471
|
}
|
|
1194
1472
|
}
|
|
1195
|
-
if (
|
|
1473
|
+
if (fs6.existsSync(backupPath)) {
|
|
1196
1474
|
try {
|
|
1197
1475
|
for (const dir of AGENT_PROMPT_DIRS["roocode" /* ROOCODE */]) {
|
|
1198
1476
|
const src = path5.join(backupPath, dir);
|
|
1199
1477
|
const dest = path5.join(ROO_DIR, dir);
|
|
1200
|
-
if (!
|
|
1478
|
+
if (!fs6.existsSync(src)) {
|
|
1201
1479
|
continue;
|
|
1202
1480
|
}
|
|
1203
|
-
|
|
1204
|
-
console.log(
|
|
1481
|
+
fs6.renameSync(src, dest);
|
|
1482
|
+
console.log(chalk7.green(" restored") + chalk7.dim(`: ${dir}/`));
|
|
1205
1483
|
}
|
|
1206
|
-
|
|
1484
|
+
fs6.rmdirSync(backupPath);
|
|
1207
1485
|
} catch (ex) {
|
|
1208
|
-
console.error(
|
|
1486
|
+
console.error(chalk7.red(` \u274C Failed to restore RooCode backup: ${ex.message}`));
|
|
1209
1487
|
}
|
|
1210
1488
|
}
|
|
1211
1489
|
configManager.roocode = null;
|
|
@@ -1214,8 +1492,8 @@ Removing RooCode integration...`));
|
|
|
1214
1492
|
|
|
1215
1493
|
// src/link/openclaw.ts
|
|
1216
1494
|
import path6 from "path";
|
|
1217
|
-
import
|
|
1218
|
-
import
|
|
1495
|
+
import fs7 from "fs";
|
|
1496
|
+
import chalk8 from "chalk";
|
|
1219
1497
|
import { confirm as confirm5 } from "@inquirer/prompts";
|
|
1220
1498
|
import { pathExists as pathExists2 } from "fs-extra";
|
|
1221
1499
|
var linkOpenclaw = async () => {
|
|
@@ -1223,44 +1501,44 @@ var linkOpenclaw = async () => {
|
|
|
1223
1501
|
if (repoPath == null) {
|
|
1224
1502
|
return;
|
|
1225
1503
|
}
|
|
1226
|
-
console.log(
|
|
1504
|
+
console.log(chalk8.green(`
|
|
1227
1505
|
Setting up OpenClaw integration...`));
|
|
1228
|
-
console.log(
|
|
1506
|
+
console.log(chalk8.dim(OPENCLAW_DIR));
|
|
1229
1507
|
const openclawDirs = AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */];
|
|
1230
1508
|
const backupExistingOpenclawFiles = async () => {
|
|
1231
1509
|
try {
|
|
1232
|
-
|
|
1510
|
+
fs7.mkdirSync(OPENCLAW_DIR, { recursive: true });
|
|
1233
1511
|
const dirsToBackup = [];
|
|
1234
1512
|
for (const dir of openclawDirs) {
|
|
1235
1513
|
const target = path6.join(OPENCLAW_DIR, dir);
|
|
1236
|
-
if (
|
|
1514
|
+
if (fs7.existsSync(target) && fs7.lstatSync(target).isSymbolicLink() === false && fs7.readdirSync(target).length > 0) {
|
|
1237
1515
|
dirsToBackup.push(dir);
|
|
1238
1516
|
}
|
|
1239
1517
|
}
|
|
1240
1518
|
if (dirsToBackup.length === 0) {
|
|
1241
1519
|
return true;
|
|
1242
1520
|
}
|
|
1243
|
-
console.log(
|
|
1521
|
+
console.log(chalk8.yellow(`
|
|
1244
1522
|
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
1245
1523
|
for (const dir of dirsToBackup) {
|
|
1246
|
-
console.log(
|
|
1524
|
+
console.log(chalk8.dim(` - ${path6.join(OPENCLAW_DIR, dir)}`));
|
|
1247
1525
|
}
|
|
1248
|
-
console.log(
|
|
1526
|
+
console.log(chalk8.yellow(` They will be backed up to: `) + chalk8.dim(OPENCLAW_BACKUP_DIR));
|
|
1249
1527
|
const ok = await confirm5({ message: "Back up existing directories?", default: true });
|
|
1250
1528
|
if (!ok) {
|
|
1251
|
-
console.log(
|
|
1529
|
+
console.log(chalk8.yellow("Skipped OpenClaw linking."));
|
|
1252
1530
|
return false;
|
|
1253
1531
|
}
|
|
1254
|
-
|
|
1532
|
+
fs7.mkdirSync(OPENCLAW_BACKUP_DIR, { recursive: true });
|
|
1255
1533
|
for (const dir of dirsToBackup) {
|
|
1256
1534
|
const src = path6.join(OPENCLAW_DIR, dir);
|
|
1257
1535
|
const dest = path6.join(OPENCLAW_BACKUP_DIR, dir);
|
|
1258
|
-
|
|
1259
|
-
console.log(
|
|
1536
|
+
fs7.renameSync(src, dest);
|
|
1537
|
+
console.log(chalk8.yellow(" backed up") + chalk8.dim(`: ${dir}/ \u2192 ${dest}`));
|
|
1260
1538
|
}
|
|
1261
1539
|
return true;
|
|
1262
1540
|
} catch (ex) {
|
|
1263
|
-
console.error(
|
|
1541
|
+
console.error(chalk8.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
1264
1542
|
return false;
|
|
1265
1543
|
}
|
|
1266
1544
|
};
|
|
@@ -1273,21 +1551,21 @@ Setting up OpenClaw integration...`));
|
|
|
1273
1551
|
if (await pathExists2(src) === false) {
|
|
1274
1552
|
continue;
|
|
1275
1553
|
}
|
|
1276
|
-
if (
|
|
1277
|
-
|
|
1554
|
+
if (fs7.existsSync(dest)) {
|
|
1555
|
+
fs7.rmSync(dest, { recursive: true, force: true });
|
|
1278
1556
|
}
|
|
1279
1557
|
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1280
|
-
|
|
1558
|
+
fs7.symlinkSync(src, dest, symlinkType);
|
|
1281
1559
|
linked.push({ dir, src });
|
|
1282
1560
|
}
|
|
1283
1561
|
for (const { dir, src } of linked) {
|
|
1284
1562
|
const isLast = linked[linked.length - 1].dir === dir;
|
|
1285
1563
|
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
1286
|
-
console.log(
|
|
1564
|
+
console.log(chalk8.dim(` ${branch} `) + chalk8.bold(`${dir}/`) + chalk8.dim(` \u2192 ${src}`) + chalk8.green(" \u2713"));
|
|
1287
1565
|
}
|
|
1288
1566
|
return true;
|
|
1289
1567
|
} catch (ex) {
|
|
1290
|
-
console.error(
|
|
1568
|
+
console.error(chalk8.red(`\u274C Failed to create symlinks: ${ex.message}`));
|
|
1291
1569
|
return false;
|
|
1292
1570
|
}
|
|
1293
1571
|
};
|
|
@@ -1309,35 +1587,35 @@ var unlinkOpenclaw = async (force = false) => {
|
|
|
1309
1587
|
default: false
|
|
1310
1588
|
});
|
|
1311
1589
|
if (!ok) {
|
|
1312
|
-
console.log(
|
|
1590
|
+
console.log(chalk8.yellow("Cancelled."));
|
|
1313
1591
|
return;
|
|
1314
1592
|
}
|
|
1315
1593
|
}
|
|
1316
|
-
console.log(
|
|
1594
|
+
console.log(chalk8.red(`
|
|
1317
1595
|
Removing OpenClaw integration...`));
|
|
1318
|
-
console.log(
|
|
1596
|
+
console.log(chalk8.dim(OPENCLAW_DIR));
|
|
1319
1597
|
const backupPath = configManager.openclaw?.backup_path ?? OPENCLAW_BACKUP_DIR;
|
|
1320
1598
|
for (const dir of AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */]) {
|
|
1321
1599
|
const target = path6.join(OPENCLAW_DIR, dir);
|
|
1322
|
-
if (
|
|
1323
|
-
|
|
1324
|
-
console.log(
|
|
1600
|
+
if (fs7.existsSync(target) && fs7.lstatSync(target).isSymbolicLink()) {
|
|
1601
|
+
fs7.unlinkSync(target);
|
|
1602
|
+
console.log(chalk8.red(" removed symlink") + chalk8.dim(`: ${target}`));
|
|
1325
1603
|
}
|
|
1326
1604
|
}
|
|
1327
|
-
if (
|
|
1605
|
+
if (fs7.existsSync(backupPath)) {
|
|
1328
1606
|
try {
|
|
1329
1607
|
for (const dir of AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */]) {
|
|
1330
1608
|
const src = path6.join(backupPath, dir);
|
|
1331
1609
|
const dest = path6.join(OPENCLAW_DIR, dir);
|
|
1332
|
-
if (!
|
|
1610
|
+
if (!fs7.existsSync(src)) {
|
|
1333
1611
|
continue;
|
|
1334
1612
|
}
|
|
1335
|
-
|
|
1336
|
-
console.log(
|
|
1613
|
+
fs7.renameSync(src, dest);
|
|
1614
|
+
console.log(chalk8.green(" restored") + chalk8.dim(`: ${dir}/`));
|
|
1337
1615
|
}
|
|
1338
|
-
|
|
1616
|
+
fs7.rmdirSync(backupPath);
|
|
1339
1617
|
} catch (ex) {
|
|
1340
|
-
console.error(
|
|
1618
|
+
console.error(chalk8.red(` \u274C Failed to restore OpenClaw backup: ${ex.message}`));
|
|
1341
1619
|
}
|
|
1342
1620
|
}
|
|
1343
1621
|
configManager.openclaw = null;
|
|
@@ -1346,8 +1624,8 @@ Removing OpenClaw integration...`));
|
|
|
1346
1624
|
|
|
1347
1625
|
// src/link/antigravity.ts
|
|
1348
1626
|
import path7 from "path";
|
|
1349
|
-
import
|
|
1350
|
-
import
|
|
1627
|
+
import fs8 from "fs";
|
|
1628
|
+
import chalk9 from "chalk";
|
|
1351
1629
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
1352
1630
|
import { pathExists as pathExists3 } from "fs-extra";
|
|
1353
1631
|
var linkAntigravity = async () => {
|
|
@@ -1355,44 +1633,44 @@ var linkAntigravity = async () => {
|
|
|
1355
1633
|
if (repoPath == null) {
|
|
1356
1634
|
return;
|
|
1357
1635
|
}
|
|
1358
|
-
console.log(
|
|
1636
|
+
console.log(chalk9.green(`
|
|
1359
1637
|
Setting up Antigravity integration...`));
|
|
1360
|
-
console.log(
|
|
1638
|
+
console.log(chalk9.dim(ANTIGRAVITY_DIR));
|
|
1361
1639
|
const antigravityDirs = AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */];
|
|
1362
1640
|
const backupExistingAntigravityFiles = async () => {
|
|
1363
1641
|
try {
|
|
1364
|
-
|
|
1642
|
+
fs8.mkdirSync(ANTIGRAVITY_DIR, { recursive: true });
|
|
1365
1643
|
const dirsToBackup = [];
|
|
1366
1644
|
for (const dir of antigravityDirs) {
|
|
1367
1645
|
const target = path7.join(ANTIGRAVITY_DIR, dir);
|
|
1368
|
-
if (
|
|
1646
|
+
if (fs8.existsSync(target) && fs8.lstatSync(target).isSymbolicLink() === false && fs8.readdirSync(target).length > 0) {
|
|
1369
1647
|
dirsToBackup.push(dir);
|
|
1370
1648
|
}
|
|
1371
1649
|
}
|
|
1372
1650
|
if (dirsToBackup.length === 0) {
|
|
1373
1651
|
return true;
|
|
1374
1652
|
}
|
|
1375
|
-
console.log(
|
|
1653
|
+
console.log(chalk9.yellow(`
|
|
1376
1654
|
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
1377
1655
|
for (const dir of dirsToBackup) {
|
|
1378
|
-
console.log(
|
|
1656
|
+
console.log(chalk9.dim(` - ${path7.join(ANTIGRAVITY_DIR, dir)}`));
|
|
1379
1657
|
}
|
|
1380
|
-
console.log(
|
|
1658
|
+
console.log(chalk9.yellow(` They will be backed up to: `) + chalk9.dim(ANTIGRAVITY_BACKUP_DIR));
|
|
1381
1659
|
const ok = await confirm6({ message: "Back up existing directories?", default: true });
|
|
1382
1660
|
if (!ok) {
|
|
1383
|
-
console.log(
|
|
1661
|
+
console.log(chalk9.yellow("Skipped Antigravity linking."));
|
|
1384
1662
|
return false;
|
|
1385
1663
|
}
|
|
1386
|
-
|
|
1664
|
+
fs8.mkdirSync(ANTIGRAVITY_BACKUP_DIR, { recursive: true });
|
|
1387
1665
|
for (const dir of antigravityDirs) {
|
|
1388
1666
|
const src = path7.join(ANTIGRAVITY_DIR, dir);
|
|
1389
1667
|
const dest = path7.join(ANTIGRAVITY_BACKUP_DIR, dir);
|
|
1390
|
-
|
|
1391
|
-
console.log(
|
|
1668
|
+
fs8.renameSync(src, dest);
|
|
1669
|
+
console.log(chalk9.yellow(" backed up") + chalk9.dim(`: ${dir}/ \u2192 ${dest}`));
|
|
1392
1670
|
}
|
|
1393
1671
|
return true;
|
|
1394
1672
|
} catch (ex) {
|
|
1395
|
-
console.error(
|
|
1673
|
+
console.error(chalk9.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
1396
1674
|
return false;
|
|
1397
1675
|
}
|
|
1398
1676
|
};
|
|
@@ -1405,21 +1683,21 @@ Setting up Antigravity integration...`));
|
|
|
1405
1683
|
if (await pathExists3(src) === false) {
|
|
1406
1684
|
continue;
|
|
1407
1685
|
}
|
|
1408
|
-
if (
|
|
1409
|
-
|
|
1686
|
+
if (fs8.existsSync(dest)) {
|
|
1687
|
+
fs8.rmSync(dest, { recursive: true, force: true });
|
|
1410
1688
|
}
|
|
1411
1689
|
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1412
|
-
|
|
1690
|
+
fs8.symlinkSync(src, dest, symlinkType);
|
|
1413
1691
|
linked.push({ dir, src });
|
|
1414
1692
|
}
|
|
1415
1693
|
for (const { dir, src } of linked) {
|
|
1416
1694
|
const isLast = linked[linked.length - 1].dir === dir;
|
|
1417
1695
|
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
1418
|
-
console.log(
|
|
1696
|
+
console.log(chalk9.dim(` ${branch} `) + chalk9.bold(`${dir}/`) + chalk9.dim(` \u2192 ${src}`) + chalk9.green(" \u2713"));
|
|
1419
1697
|
}
|
|
1420
1698
|
return true;
|
|
1421
1699
|
} catch (ex) {
|
|
1422
|
-
console.error(
|
|
1700
|
+
console.error(chalk9.red(`\u274C Failed to create symlinks: ${ex.message}`));
|
|
1423
1701
|
return false;
|
|
1424
1702
|
}
|
|
1425
1703
|
};
|
|
@@ -1441,35 +1719,35 @@ var unlinkAntigravity = async (force = false) => {
|
|
|
1441
1719
|
default: false
|
|
1442
1720
|
});
|
|
1443
1721
|
if (!ok) {
|
|
1444
|
-
console.log(
|
|
1722
|
+
console.log(chalk9.yellow("Cancelled."));
|
|
1445
1723
|
return;
|
|
1446
1724
|
}
|
|
1447
1725
|
}
|
|
1448
|
-
console.log(
|
|
1726
|
+
console.log(chalk9.red(`
|
|
1449
1727
|
Removing Antigravity integration...`));
|
|
1450
|
-
console.log(
|
|
1728
|
+
console.log(chalk9.dim(ANTIGRAVITY_DIR));
|
|
1451
1729
|
const backupPath = configManager.antigravity?.backup_path ?? ANTIGRAVITY_BACKUP_DIR;
|
|
1452
1730
|
for (const dir of AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */]) {
|
|
1453
1731
|
const target = path7.join(ANTIGRAVITY_DIR, dir);
|
|
1454
|
-
if (
|
|
1455
|
-
|
|
1456
|
-
console.log(
|
|
1732
|
+
if (fs8.existsSync(target) && fs8.lstatSync(target).isSymbolicLink()) {
|
|
1733
|
+
fs8.unlinkSync(target);
|
|
1734
|
+
console.log(chalk9.red(" removed symlink") + chalk9.dim(`: ${target}`));
|
|
1457
1735
|
}
|
|
1458
1736
|
}
|
|
1459
|
-
if (
|
|
1737
|
+
if (fs8.existsSync(backupPath)) {
|
|
1460
1738
|
try {
|
|
1461
1739
|
for (const dir of AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */]) {
|
|
1462
1740
|
const src = path7.join(backupPath, dir);
|
|
1463
1741
|
const dest = path7.join(ANTIGRAVITY_DIR, dir);
|
|
1464
|
-
if (!
|
|
1742
|
+
if (!fs8.existsSync(src)) {
|
|
1465
1743
|
continue;
|
|
1466
1744
|
}
|
|
1467
|
-
|
|
1468
|
-
console.log(
|
|
1745
|
+
fs8.renameSync(src, dest);
|
|
1746
|
+
console.log(chalk9.green(" restored") + chalk9.dim(`: ${dir}/`));
|
|
1469
1747
|
}
|
|
1470
|
-
|
|
1748
|
+
fs8.rmdirSync(backupPath);
|
|
1471
1749
|
} catch (ex) {
|
|
1472
|
-
console.error(
|
|
1750
|
+
console.error(chalk9.red(` \u274C Failed to restore Antigravity backup: ${ex.message}`));
|
|
1473
1751
|
}
|
|
1474
1752
|
}
|
|
1475
1753
|
configManager.antigravity = null;
|
|
@@ -1478,9 +1756,9 @@ Removing Antigravity integration...`));
|
|
|
1478
1756
|
|
|
1479
1757
|
// src/link/codex.ts
|
|
1480
1758
|
import path8 from "path";
|
|
1481
|
-
import
|
|
1759
|
+
import fs9 from "fs";
|
|
1482
1760
|
import os3 from "os";
|
|
1483
|
-
import
|
|
1761
|
+
import chalk10 from "chalk";
|
|
1484
1762
|
import { confirm as confirm7 } from "@inquirer/prompts";
|
|
1485
1763
|
import TOML from "smol-toml";
|
|
1486
1764
|
var CODEX_AGENTS_DIR = path8.join(os3.homedir(), ".agents", "plugins");
|
|
@@ -1502,16 +1780,16 @@ var linkCodex = async () => {
|
|
|
1502
1780
|
interface: { displayName: "Local Repository" },
|
|
1503
1781
|
plugins: []
|
|
1504
1782
|
};
|
|
1505
|
-
if (
|
|
1506
|
-
const raw =
|
|
1783
|
+
if (fs9.existsSync(marketplacePath)) {
|
|
1784
|
+
const raw = fs9.readFileSync(marketplacePath, "utf-8");
|
|
1507
1785
|
try {
|
|
1508
1786
|
const parsed = JSON.parse(raw);
|
|
1509
1787
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1510
1788
|
marketplace = parsed;
|
|
1511
1789
|
}
|
|
1512
1790
|
} catch {
|
|
1513
|
-
console.warn(
|
|
1514
|
-
console.error(
|
|
1791
|
+
console.warn(chalk10.yellow(" \u26A0 Failed to parse marketplace.json \u2014 will not overwrite existing file"));
|
|
1792
|
+
console.error(chalk10.red("\u274C Could not register plugin. Please add manually."));
|
|
1515
1793
|
return false;
|
|
1516
1794
|
}
|
|
1517
1795
|
}
|
|
@@ -1534,58 +1812,58 @@ var linkCodex = async () => {
|
|
|
1534
1812
|
category: "Productivity"
|
|
1535
1813
|
});
|
|
1536
1814
|
let backupPath = null;
|
|
1537
|
-
if (
|
|
1815
|
+
if (fs9.existsSync(marketplacePath)) {
|
|
1538
1816
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1539
1817
|
backupPath = `${marketplacePath}.bak.${timestamp}`;
|
|
1540
1818
|
try {
|
|
1541
|
-
|
|
1819
|
+
fs9.copyFileSync(marketplacePath, backupPath);
|
|
1542
1820
|
} catch (ex) {
|
|
1543
|
-
console.warn(
|
|
1821
|
+
console.warn(chalk10.yellow(` \u26A0 Could not create backup: ${ex.message}`));
|
|
1544
1822
|
backupPath = null;
|
|
1545
1823
|
}
|
|
1546
1824
|
}
|
|
1547
1825
|
try {
|
|
1548
|
-
|
|
1549
|
-
|
|
1826
|
+
fs9.mkdirSync(path8.dirname(marketplacePath), { recursive: true });
|
|
1827
|
+
fs9.writeFileSync(marketplacePath, JSON.stringify(marketplace, null, 4), "utf-8");
|
|
1550
1828
|
} catch (ex) {
|
|
1551
1829
|
if (backupPath !== null) {
|
|
1552
1830
|
try {
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
console.warn(
|
|
1831
|
+
fs9.copyFileSync(backupPath, marketplacePath);
|
|
1832
|
+
fs9.unlinkSync(backupPath);
|
|
1833
|
+
console.warn(chalk10.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
|
|
1556
1834
|
} catch {
|
|
1557
|
-
console.error(
|
|
1835
|
+
console.error(chalk10.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
|
|
1558
1836
|
}
|
|
1559
1837
|
}
|
|
1560
1838
|
throw ex;
|
|
1561
1839
|
}
|
|
1562
1840
|
if (backupPath !== null) {
|
|
1563
1841
|
try {
|
|
1564
|
-
|
|
1842
|
+
fs9.unlinkSync(backupPath);
|
|
1565
1843
|
} catch {
|
|
1566
1844
|
}
|
|
1567
1845
|
}
|
|
1568
1846
|
console.log(`\u2705 Registered to marketplace.json`);
|
|
1569
|
-
console.log(
|
|
1847
|
+
console.log(chalk10.dim(` ${marketplacePath}`));
|
|
1570
1848
|
return true;
|
|
1571
1849
|
} catch (ex) {
|
|
1572
|
-
console.error(
|
|
1850
|
+
console.error(chalk10.red(`\u274C Failed to update marketplace.json: ${ex.message}`));
|
|
1573
1851
|
return false;
|
|
1574
1852
|
}
|
|
1575
1853
|
};
|
|
1576
1854
|
const patchPluginCache = () => {
|
|
1577
1855
|
const cachePath = path8.join(CODEX_CACHE_DIR, CODEX_MARKETPLACE_NAME, PLUGIN_NAME, "1.0.0");
|
|
1578
1856
|
try {
|
|
1579
|
-
|
|
1580
|
-
if (
|
|
1581
|
-
|
|
1857
|
+
fs9.mkdirSync(path8.dirname(cachePath), { recursive: true });
|
|
1858
|
+
if (fs9.existsSync(cachePath)) {
|
|
1859
|
+
fs9.rmSync(cachePath, { recursive: true, force: true });
|
|
1582
1860
|
}
|
|
1583
1861
|
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1584
|
-
|
|
1862
|
+
fs9.symlinkSync(repoPath, cachePath, symlinkType);
|
|
1585
1863
|
console.log(`\u2705 Patched plugin cache.`);
|
|
1586
|
-
console.log(
|
|
1864
|
+
console.log(chalk10.dim(` ${cachePath}`) + chalk10.dim(" \u2192 ") + chalk10.dim(repoPath));
|
|
1587
1865
|
} catch (ex) {
|
|
1588
|
-
console.warn(
|
|
1866
|
+
console.warn(chalk10.yellow(` \u26A0 Could not patch plugin cache: ${ex.message}`));
|
|
1589
1867
|
}
|
|
1590
1868
|
};
|
|
1591
1869
|
const enableInConfig = () => {
|
|
@@ -1593,22 +1871,22 @@ var linkCodex = async () => {
|
|
|
1593
1871
|
const pluginKey = `${PLUGIN_NAME}@${CODEX_MARKETPLACE_NAME}`;
|
|
1594
1872
|
try {
|
|
1595
1873
|
let config = {};
|
|
1596
|
-
if (
|
|
1597
|
-
config = TOML.parse(
|
|
1874
|
+
if (fs9.existsSync(configPath)) {
|
|
1875
|
+
config = TOML.parse(fs9.readFileSync(configPath, "utf-8"));
|
|
1598
1876
|
}
|
|
1599
1877
|
if (config.plugins == null) {
|
|
1600
1878
|
config.plugins = {};
|
|
1601
1879
|
}
|
|
1602
1880
|
config.plugins[pluginKey] = { enabled: true };
|
|
1603
|
-
|
|
1604
|
-
|
|
1881
|
+
fs9.mkdirSync(path8.dirname(configPath), { recursive: true });
|
|
1882
|
+
fs9.writeFileSync(configPath, TOML.stringify(config), "utf-8");
|
|
1605
1883
|
console.log(`\u2705 Enabled plugin in config.toml`);
|
|
1606
|
-
console.log(
|
|
1884
|
+
console.log(chalk10.dim(` ${configPath}`));
|
|
1607
1885
|
} catch (ex) {
|
|
1608
|
-
console.warn(
|
|
1886
|
+
console.warn(chalk10.yellow(` \u26A0 Could not update config.toml: ${ex.message}`));
|
|
1609
1887
|
}
|
|
1610
1888
|
};
|
|
1611
|
-
console.log(
|
|
1889
|
+
console.log(chalk10.green(`
|
|
1612
1890
|
Setting up Codex plugin...`));
|
|
1613
1891
|
const marketplaceOk = registerToMarketplace();
|
|
1614
1892
|
if (marketplaceOk === false) {
|
|
@@ -1626,55 +1904,55 @@ var unlinkCodex = async (force = false) => {
|
|
|
1626
1904
|
default: false
|
|
1627
1905
|
});
|
|
1628
1906
|
if (!ok) {
|
|
1629
|
-
console.log(
|
|
1907
|
+
console.log(chalk10.yellow("Cancelled."));
|
|
1630
1908
|
return;
|
|
1631
1909
|
}
|
|
1632
1910
|
}
|
|
1633
|
-
console.log(
|
|
1911
|
+
console.log(chalk10.red(`
|
|
1634
1912
|
Removing Codex plugin...`));
|
|
1635
1913
|
const marketplacePath = path8.join(CODEX_AGENTS_DIR, "marketplace.json");
|
|
1636
|
-
if (
|
|
1914
|
+
if (fs9.existsSync(marketplacePath)) {
|
|
1637
1915
|
try {
|
|
1638
1916
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1639
1917
|
const backupPath = `${marketplacePath}.bak.${timestamp}`;
|
|
1640
|
-
|
|
1641
|
-
const marketplace = JSON.parse(
|
|
1918
|
+
fs9.copyFileSync(marketplacePath, backupPath);
|
|
1919
|
+
const marketplace = JSON.parse(fs9.readFileSync(marketplacePath, "utf-8"));
|
|
1642
1920
|
if (Array.isArray(marketplace?.plugins)) {
|
|
1643
1921
|
marketplace.plugins = marketplace.plugins.filter(
|
|
1644
1922
|
(p) => p?.name !== PLUGIN_NAME
|
|
1645
1923
|
);
|
|
1646
1924
|
}
|
|
1647
1925
|
try {
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
console.log(
|
|
1926
|
+
fs9.writeFileSync(marketplacePath, JSON.stringify(marketplace, null, 4), "utf-8");
|
|
1927
|
+
fs9.unlinkSync(backupPath);
|
|
1928
|
+
console.log(chalk10.red(" removed") + chalk10.dim(` ${PLUGIN_NAME} from: ${marketplacePath}`));
|
|
1651
1929
|
} catch (ex) {
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
console.warn(
|
|
1930
|
+
fs9.copyFileSync(backupPath, marketplacePath);
|
|
1931
|
+
fs9.unlinkSync(backupPath);
|
|
1932
|
+
console.warn(chalk10.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
|
|
1655
1933
|
}
|
|
1656
1934
|
} catch (ex) {
|
|
1657
|
-
console.error(
|
|
1935
|
+
console.error(chalk10.red(` \u274C Failed to clean up marketplace.json: ${ex.message}`));
|
|
1658
1936
|
}
|
|
1659
1937
|
}
|
|
1660
1938
|
const configPath = path8.join(os3.homedir(), ".codex", "config.toml");
|
|
1661
|
-
if (
|
|
1939
|
+
if (fs9.existsSync(configPath)) {
|
|
1662
1940
|
try {
|
|
1663
|
-
const config = TOML.parse(
|
|
1941
|
+
const config = TOML.parse(fs9.readFileSync(configPath, "utf-8"));
|
|
1664
1942
|
const pluginKey = `${PLUGIN_NAME}@${CODEX_MARKETPLACE_NAME}`;
|
|
1665
1943
|
if (config.plugins?.[pluginKey] !== void 0) {
|
|
1666
1944
|
delete config.plugins[pluginKey];
|
|
1667
|
-
|
|
1668
|
-
console.log(
|
|
1945
|
+
fs9.writeFileSync(configPath, TOML.stringify(config), "utf-8");
|
|
1946
|
+
console.log(chalk10.red(" removed") + chalk10.dim(` ${pluginKey} from: ${configPath}`));
|
|
1669
1947
|
}
|
|
1670
1948
|
} catch (ex) {
|
|
1671
|
-
console.error(
|
|
1949
|
+
console.error(chalk10.red(` \u274C Failed to clean up config.toml: ${ex.message}`));
|
|
1672
1950
|
}
|
|
1673
1951
|
}
|
|
1674
1952
|
const cacheMarketDir = path8.join(CODEX_CACHE_DIR, CODEX_MARKETPLACE_NAME);
|
|
1675
|
-
if (
|
|
1676
|
-
|
|
1677
|
-
console.log(
|
|
1953
|
+
if (fs9.existsSync(cacheMarketDir)) {
|
|
1954
|
+
fs9.rmSync(cacheMarketDir, { recursive: true, force: true });
|
|
1955
|
+
console.log(chalk10.red(" removed") + chalk10.dim(`: ${cacheMarketDir}`));
|
|
1678
1956
|
}
|
|
1679
1957
|
configManager.codex = null;
|
|
1680
1958
|
configManager.save();
|
|
@@ -1682,8 +1960,8 @@ Removing Codex plugin...`));
|
|
|
1682
1960
|
|
|
1683
1961
|
// src/link/cursor.ts
|
|
1684
1962
|
import path9 from "path";
|
|
1685
|
-
import
|
|
1686
|
-
import
|
|
1963
|
+
import fs10 from "fs";
|
|
1964
|
+
import chalk11 from "chalk";
|
|
1687
1965
|
import { confirm as confirm8 } from "@inquirer/prompts";
|
|
1688
1966
|
import { pathExists as pathExists4 } from "fs-extra";
|
|
1689
1967
|
var CURSOR_BACKUP_DIR = path9.join(CURSOR_DIR, "SET_PROMPT_BACKUP");
|
|
@@ -1692,35 +1970,35 @@ var linkCursor = async () => {
|
|
|
1692
1970
|
if (repoPath == null) {
|
|
1693
1971
|
return;
|
|
1694
1972
|
}
|
|
1695
|
-
console.log(
|
|
1973
|
+
console.log(chalk11.green(`
|
|
1696
1974
|
Setting up Cursor integration...`));
|
|
1697
|
-
console.log(
|
|
1975
|
+
console.log(chalk11.dim(CURSOR_DIR));
|
|
1698
1976
|
const cursorDirs = AGENT_PROMPT_DIRS["cursor" /* CURSOR */];
|
|
1699
1977
|
const dirsToBackup = [];
|
|
1700
1978
|
for (const dir of cursorDirs) {
|
|
1701
1979
|
const target = path9.join(CURSOR_DIR, dir);
|
|
1702
|
-
if (
|
|
1980
|
+
if (fs10.existsSync(target) && fs10.lstatSync(target).isSymbolicLink() === false && fs10.readdirSync(target).length > 0) {
|
|
1703
1981
|
dirsToBackup.push(dir);
|
|
1704
1982
|
}
|
|
1705
1983
|
}
|
|
1706
1984
|
if (dirsToBackup.length > 0) {
|
|
1707
|
-
console.log(
|
|
1985
|
+
console.log(chalk11.yellow(`
|
|
1708
1986
|
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
1709
1987
|
for (const dir of dirsToBackup) {
|
|
1710
|
-
console.log(
|
|
1988
|
+
console.log(chalk11.dim(` - ${path9.join(CURSOR_DIR, dir)}`));
|
|
1711
1989
|
}
|
|
1712
|
-
console.log(
|
|
1990
|
+
console.log(chalk11.yellow(` They will be backed up to: `) + chalk11.dim(CURSOR_BACKUP_DIR));
|
|
1713
1991
|
const ok = await confirm8({ message: "Back up existing directories?", default: true });
|
|
1714
1992
|
if (!ok) {
|
|
1715
|
-
console.log(
|
|
1993
|
+
console.log(chalk11.yellow("Skipped Cursor linking."));
|
|
1716
1994
|
return;
|
|
1717
1995
|
}
|
|
1718
|
-
|
|
1996
|
+
fs10.mkdirSync(CURSOR_BACKUP_DIR, { recursive: true });
|
|
1719
1997
|
for (const dir of dirsToBackup) {
|
|
1720
1998
|
const src = path9.join(CURSOR_DIR, dir);
|
|
1721
1999
|
const dest = path9.join(CURSOR_BACKUP_DIR, dir);
|
|
1722
|
-
|
|
1723
|
-
console.log(
|
|
2000
|
+
fs10.renameSync(src, dest);
|
|
2001
|
+
console.log(chalk11.yellow(" backed up") + chalk11.dim(`: ${dir}/ \u2192 ${dest}`));
|
|
1724
2002
|
}
|
|
1725
2003
|
}
|
|
1726
2004
|
try {
|
|
@@ -1731,33 +2009,33 @@ Setting up Cursor integration...`));
|
|
|
1731
2009
|
if (await pathExists4(src) === false) {
|
|
1732
2010
|
continue;
|
|
1733
2011
|
}
|
|
1734
|
-
if (
|
|
1735
|
-
|
|
2012
|
+
if (fs10.existsSync(dest)) {
|
|
2013
|
+
fs10.rmSync(dest, { recursive: true, force: true });
|
|
1736
2014
|
}
|
|
1737
2015
|
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1738
|
-
|
|
2016
|
+
fs10.symlinkSync(src, dest, symlinkType);
|
|
1739
2017
|
linked.push({ dir, src });
|
|
1740
2018
|
}
|
|
1741
2019
|
for (const { dir, src } of linked) {
|
|
1742
|
-
console.log(
|
|
2020
|
+
console.log(chalk11.dim(` \u251C\u2500\u2500 `) + chalk11.bold(`${dir}/`) + chalk11.dim(` \u2192 ${src}`) + chalk11.green(" \u2713"));
|
|
1743
2021
|
}
|
|
1744
2022
|
const mcpSrc = path9.join(repoPath, ".mcp.json");
|
|
1745
2023
|
const mcpDest = path9.join(CURSOR_DIR, "mcp.json");
|
|
1746
|
-
if (
|
|
1747
|
-
if (
|
|
2024
|
+
if (fs10.existsSync(mcpSrc)) {
|
|
2025
|
+
if (fs10.existsSync(mcpDest) && fs10.statSync(mcpDest).ino !== fs10.statSync(mcpSrc).ino) {
|
|
1748
2026
|
const mcpBackup = path9.join(CURSOR_BACKUP_DIR, "mcp.json");
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
console.log(
|
|
2027
|
+
fs10.mkdirSync(CURSOR_BACKUP_DIR, { recursive: true });
|
|
2028
|
+
fs10.renameSync(mcpDest, mcpBackup);
|
|
2029
|
+
console.log(chalk11.yellow(" backed up") + chalk11.dim(`: mcp.json \u2192 ${mcpBackup}`));
|
|
1752
2030
|
}
|
|
1753
|
-
if (
|
|
1754
|
-
|
|
2031
|
+
if (fs10.existsSync(mcpDest)) {
|
|
2032
|
+
fs10.unlinkSync(mcpDest);
|
|
1755
2033
|
}
|
|
1756
|
-
|
|
1757
|
-
console.log(
|
|
2034
|
+
fs10.linkSync(mcpSrc, mcpDest);
|
|
2035
|
+
console.log(chalk11.dim(` \u2514\u2500\u2500 `) + chalk11.bold("mcp.json") + chalk11.dim(` \u21D4 ${mcpSrc}`) + chalk11.green(" \u2713"));
|
|
1758
2036
|
}
|
|
1759
2037
|
} catch (ex) {
|
|
1760
|
-
console.error(
|
|
2038
|
+
console.error(chalk11.red(`\u274C Failed to set up Cursor: ${ex.message}`));
|
|
1761
2039
|
return;
|
|
1762
2040
|
}
|
|
1763
2041
|
configManager.cursor = { path: CURSOR_DIR, backup_path: CURSOR_BACKUP_DIR };
|
|
@@ -1770,51 +2048,324 @@ var unlinkCursor = async (force = false) => {
|
|
|
1770
2048
|
default: false
|
|
1771
2049
|
});
|
|
1772
2050
|
if (!ok) {
|
|
1773
|
-
console.log(
|
|
2051
|
+
console.log(chalk11.yellow("Cancelled."));
|
|
1774
2052
|
return;
|
|
1775
2053
|
}
|
|
1776
2054
|
}
|
|
1777
|
-
console.log(
|
|
2055
|
+
console.log(chalk11.red(`
|
|
1778
2056
|
Removing Cursor integration...`));
|
|
1779
|
-
console.log(
|
|
2057
|
+
console.log(chalk11.dim(CURSOR_DIR));
|
|
1780
2058
|
const backupPath = configManager.cursor?.backup_path ?? CURSOR_BACKUP_DIR;
|
|
1781
2059
|
for (const dir of AGENT_PROMPT_DIRS["cursor" /* CURSOR */]) {
|
|
1782
2060
|
const target = path9.join(CURSOR_DIR, dir);
|
|
1783
|
-
if (
|
|
1784
|
-
|
|
1785
|
-
console.log(
|
|
2061
|
+
if (fs10.existsSync(target) && fs10.lstatSync(target).isSymbolicLink()) {
|
|
2062
|
+
fs10.unlinkSync(target);
|
|
2063
|
+
console.log(chalk11.red(" removed symlink") + chalk11.dim(`: ${target}`));
|
|
1786
2064
|
}
|
|
1787
2065
|
}
|
|
1788
2066
|
const mcpDest = path9.join(CURSOR_DIR, "mcp.json");
|
|
1789
|
-
if (
|
|
1790
|
-
|
|
1791
|
-
console.log(
|
|
2067
|
+
if (fs10.existsSync(mcpDest)) {
|
|
2068
|
+
fs10.unlinkSync(mcpDest);
|
|
2069
|
+
console.log(chalk11.red(" removed") + chalk11.dim(`: ${mcpDest}`));
|
|
1792
2070
|
}
|
|
1793
|
-
if (
|
|
2071
|
+
if (fs10.existsSync(backupPath)) {
|
|
1794
2072
|
try {
|
|
1795
2073
|
for (const dir of AGENT_PROMPT_DIRS["cursor" /* CURSOR */]) {
|
|
1796
2074
|
const src = path9.join(backupPath, dir);
|
|
1797
2075
|
const dest = path9.join(CURSOR_DIR, dir);
|
|
1798
|
-
if (!
|
|
2076
|
+
if (!fs10.existsSync(src)) {
|
|
1799
2077
|
continue;
|
|
1800
2078
|
}
|
|
1801
|
-
|
|
1802
|
-
console.log(
|
|
2079
|
+
fs10.renameSync(src, dest);
|
|
2080
|
+
console.log(chalk11.green(" restored") + chalk11.dim(`: ${dir}/`));
|
|
1803
2081
|
}
|
|
1804
2082
|
const mcpBackup = path9.join(backupPath, "mcp.json");
|
|
1805
|
-
if (
|
|
1806
|
-
|
|
1807
|
-
console.log(
|
|
2083
|
+
if (fs10.existsSync(mcpBackup)) {
|
|
2084
|
+
fs10.renameSync(mcpBackup, mcpDest);
|
|
2085
|
+
console.log(chalk11.green(" restored") + chalk11.dim(`: mcp.json`));
|
|
1808
2086
|
}
|
|
1809
|
-
|
|
2087
|
+
fs10.rmSync(backupPath, { recursive: true, force: true });
|
|
1810
2088
|
} catch (ex) {
|
|
1811
|
-
console.error(
|
|
2089
|
+
console.error(chalk11.red(` \u274C Failed to restore Cursor backup: ${ex.message}`));
|
|
1812
2090
|
}
|
|
1813
2091
|
}
|
|
1814
2092
|
configManager.cursor = null;
|
|
1815
2093
|
configManager.save();
|
|
1816
2094
|
};
|
|
1817
2095
|
|
|
2096
|
+
// src/link/opencode.ts
|
|
2097
|
+
import path10 from "path";
|
|
2098
|
+
import fs11 from "fs";
|
|
2099
|
+
import chalk12 from "chalk";
|
|
2100
|
+
import { confirm as confirm9 } from "@inquirer/prompts";
|
|
2101
|
+
import { pathExists as pathExists5 } from "fs-extra";
|
|
2102
|
+
var linkOpencode = async () => {
|
|
2103
|
+
const repoPath = resolveRepoPath();
|
|
2104
|
+
if (repoPath == null) {
|
|
2105
|
+
return;
|
|
2106
|
+
}
|
|
2107
|
+
console.log(chalk12.green(`
|
|
2108
|
+
Setting up OpenCode integration...`));
|
|
2109
|
+
console.log(chalk12.dim(OPENCODE_DIR));
|
|
2110
|
+
const opencodeDirs = AGENT_PROMPT_DIRS["opencode" /* OPENCODE */];
|
|
2111
|
+
const backupExistingOpencodeFiles = async () => {
|
|
2112
|
+
try {
|
|
2113
|
+
fs11.mkdirSync(OPENCODE_DIR, { recursive: true });
|
|
2114
|
+
const dirsToBackup = [];
|
|
2115
|
+
for (const dir of opencodeDirs) {
|
|
2116
|
+
const target = path10.join(OPENCODE_DIR, dir);
|
|
2117
|
+
if (fs11.existsSync(target) && fs11.lstatSync(target).isSymbolicLink() === false && fs11.readdirSync(target).length > 0) {
|
|
2118
|
+
dirsToBackup.push(dir);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
if (dirsToBackup.length === 0) {
|
|
2122
|
+
return true;
|
|
2123
|
+
}
|
|
2124
|
+
console.log(chalk12.yellow(`
|
|
2125
|
+
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
2126
|
+
for (const dir of dirsToBackup) {
|
|
2127
|
+
console.log(chalk12.dim(` - ${path10.join(OPENCODE_DIR, dir)}`));
|
|
2128
|
+
}
|
|
2129
|
+
console.log(chalk12.yellow(` They will be backed up to: `) + chalk12.dim(OPENCODE_BACKUP_DIR));
|
|
2130
|
+
const ok = await confirm9({ message: "Back up existing directories?", default: true });
|
|
2131
|
+
if (!ok) {
|
|
2132
|
+
console.log(chalk12.yellow("Skipped OpenCode linking."));
|
|
2133
|
+
return false;
|
|
2134
|
+
}
|
|
2135
|
+
fs11.mkdirSync(OPENCODE_BACKUP_DIR, { recursive: true });
|
|
2136
|
+
for (const dir of opencodeDirs) {
|
|
2137
|
+
const src = path10.join(OPENCODE_DIR, dir);
|
|
2138
|
+
const dest = path10.join(OPENCODE_BACKUP_DIR, dir);
|
|
2139
|
+
if (!fs11.existsSync(src)) {
|
|
2140
|
+
continue;
|
|
2141
|
+
}
|
|
2142
|
+
fs11.renameSync(src, dest);
|
|
2143
|
+
console.log(chalk12.yellow(" backed up") + chalk12.dim(`: ${dir}/ \u2192 ${dest}`));
|
|
2144
|
+
}
|
|
2145
|
+
return true;
|
|
2146
|
+
} catch (ex) {
|
|
2147
|
+
console.error(chalk12.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
2148
|
+
return false;
|
|
2149
|
+
}
|
|
2150
|
+
};
|
|
2151
|
+
const setOpencodeAssets = async () => {
|
|
2152
|
+
try {
|
|
2153
|
+
const linked = [];
|
|
2154
|
+
for (const dir of opencodeDirs) {
|
|
2155
|
+
const src = path10.join(repoPath, dir);
|
|
2156
|
+
const dest = path10.join(OPENCODE_DIR, dir);
|
|
2157
|
+
if (await pathExists5(src) === false) {
|
|
2158
|
+
continue;
|
|
2159
|
+
}
|
|
2160
|
+
if (fs11.existsSync(dest)) {
|
|
2161
|
+
fs11.rmSync(dest, { recursive: true, force: true });
|
|
2162
|
+
}
|
|
2163
|
+
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
2164
|
+
fs11.symlinkSync(src, dest, symlinkType);
|
|
2165
|
+
linked.push({ dir, src });
|
|
2166
|
+
}
|
|
2167
|
+
for (const { dir, src } of linked) {
|
|
2168
|
+
const isLast = linked[linked.length - 1].dir === dir;
|
|
2169
|
+
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
2170
|
+
console.log(chalk12.dim(` ${branch} `) + chalk12.bold(`${dir}/`) + chalk12.dim(` \u2192 ${src}`) + chalk12.green(" \u2713"));
|
|
2171
|
+
}
|
|
2172
|
+
return true;
|
|
2173
|
+
} catch (ex) {
|
|
2174
|
+
console.error(chalk12.red(`\u274C Failed to create symlinks: ${ex.message}`));
|
|
2175
|
+
return false;
|
|
2176
|
+
}
|
|
2177
|
+
};
|
|
2178
|
+
const backupOk = await backupExistingOpencodeFiles();
|
|
2179
|
+
if (backupOk === false) {
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
2182
|
+
const linkOk = await setOpencodeAssets();
|
|
2183
|
+
if (linkOk === false) {
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
configManager.opencode = { path: OPENCODE_DIR, backup_path: OPENCODE_BACKUP_DIR };
|
|
2187
|
+
configManager.save();
|
|
2188
|
+
};
|
|
2189
|
+
var unlinkOpencode = async (force = false) => {
|
|
2190
|
+
if (!force) {
|
|
2191
|
+
const ok = await confirm9({
|
|
2192
|
+
message: `Remove OpenCode symlinks from ${OPENCODE_DIR}?`,
|
|
2193
|
+
default: false
|
|
2194
|
+
});
|
|
2195
|
+
if (!ok) {
|
|
2196
|
+
console.log(chalk12.yellow("Cancelled."));
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
console.log(chalk12.red(`
|
|
2201
|
+
Removing OpenCode integration...`));
|
|
2202
|
+
console.log(chalk12.dim(OPENCODE_DIR));
|
|
2203
|
+
const backupPath = configManager.opencode?.backup_path ?? OPENCODE_BACKUP_DIR;
|
|
2204
|
+
for (const dir of AGENT_PROMPT_DIRS["opencode" /* OPENCODE */]) {
|
|
2205
|
+
const target = path10.join(OPENCODE_DIR, dir);
|
|
2206
|
+
if (fs11.existsSync(target) && fs11.lstatSync(target).isSymbolicLink()) {
|
|
2207
|
+
fs11.unlinkSync(target);
|
|
2208
|
+
console.log(chalk12.red(" removed symlink") + chalk12.dim(`: ${target}`));
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
if (fs11.existsSync(backupPath)) {
|
|
2212
|
+
try {
|
|
2213
|
+
for (const dir of AGENT_PROMPT_DIRS["opencode" /* OPENCODE */]) {
|
|
2214
|
+
const src = path10.join(backupPath, dir);
|
|
2215
|
+
const dest = path10.join(OPENCODE_DIR, dir);
|
|
2216
|
+
if (!fs11.existsSync(src)) {
|
|
2217
|
+
continue;
|
|
2218
|
+
}
|
|
2219
|
+
fs11.renameSync(src, dest);
|
|
2220
|
+
console.log(chalk12.green(" restored") + chalk12.dim(`: ${dir}/`));
|
|
2221
|
+
}
|
|
2222
|
+
fs11.rmdirSync(backupPath);
|
|
2223
|
+
} catch (ex) {
|
|
2224
|
+
console.error(chalk12.red(` \u274C Failed to restore OpenCode backup: ${ex.message}`));
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
configManager.opencode = null;
|
|
2228
|
+
configManager.save();
|
|
2229
|
+
};
|
|
2230
|
+
|
|
2231
|
+
// src/link/geminicli.ts
|
|
2232
|
+
import path11 from "path";
|
|
2233
|
+
import fs12 from "fs";
|
|
2234
|
+
import chalk13 from "chalk";
|
|
2235
|
+
import { confirm as confirm10 } from "@inquirer/prompts";
|
|
2236
|
+
import { pathExists as pathExists6 } from "fs-extra";
|
|
2237
|
+
var linkGeminicli = async () => {
|
|
2238
|
+
const repoPath = resolveRepoPath();
|
|
2239
|
+
if (repoPath == null) {
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
console.log(chalk13.green(`
|
|
2243
|
+
Setting up Gemini CLI integration...`));
|
|
2244
|
+
console.log(chalk13.dim(GEMINICLI_DIR));
|
|
2245
|
+
const geminicliDirs = AGENT_PROMPT_DIRS["geminicli" /* GEMINICLI */];
|
|
2246
|
+
const backupExistingGeminicliFiles = async () => {
|
|
2247
|
+
try {
|
|
2248
|
+
fs12.mkdirSync(GEMINICLI_DIR, { recursive: true });
|
|
2249
|
+
const dirsToBackup = [];
|
|
2250
|
+
for (const dir of geminicliDirs) {
|
|
2251
|
+
const target = path11.join(GEMINICLI_DIR, dir);
|
|
2252
|
+
if (fs12.existsSync(target) && fs12.lstatSync(target).isSymbolicLink() === false && fs12.readdirSync(target).length > 0) {
|
|
2253
|
+
dirsToBackup.push(dir);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
if (dirsToBackup.length === 0) {
|
|
2257
|
+
return true;
|
|
2258
|
+
}
|
|
2259
|
+
console.log(chalk13.yellow(`
|
|
2260
|
+
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
2261
|
+
for (const dir of dirsToBackup) {
|
|
2262
|
+
console.log(chalk13.dim(` - ${path11.join(GEMINICLI_DIR, dir)}`));
|
|
2263
|
+
}
|
|
2264
|
+
console.log(chalk13.yellow(` They will be backed up to: `) + chalk13.dim(GEMINICLI_BACKUP_DIR));
|
|
2265
|
+
const ok = await confirm10({ message: "Back up existing directories?", default: true });
|
|
2266
|
+
if (!ok) {
|
|
2267
|
+
console.log(chalk13.yellow("Skipped Gemini CLI linking."));
|
|
2268
|
+
return false;
|
|
2269
|
+
}
|
|
2270
|
+
fs12.mkdirSync(GEMINICLI_BACKUP_DIR, { recursive: true });
|
|
2271
|
+
for (const dir of geminicliDirs) {
|
|
2272
|
+
const src = path11.join(GEMINICLI_DIR, dir);
|
|
2273
|
+
const dest = path11.join(GEMINICLI_BACKUP_DIR, dir);
|
|
2274
|
+
if (!fs12.existsSync(src)) {
|
|
2275
|
+
continue;
|
|
2276
|
+
}
|
|
2277
|
+
fs12.renameSync(src, dest);
|
|
2278
|
+
console.log(chalk13.yellow(" backed up") + chalk13.dim(`: ${dir}/ \u2192 ${dest}`));
|
|
2279
|
+
}
|
|
2280
|
+
return true;
|
|
2281
|
+
} catch (ex) {
|
|
2282
|
+
console.error(chalk13.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
2283
|
+
return false;
|
|
2284
|
+
}
|
|
2285
|
+
};
|
|
2286
|
+
const setGeminicliAssets = async () => {
|
|
2287
|
+
try {
|
|
2288
|
+
const linked = [];
|
|
2289
|
+
for (const dir of geminicliDirs) {
|
|
2290
|
+
const src = path11.join(repoPath, dir);
|
|
2291
|
+
const dest = path11.join(GEMINICLI_DIR, dir);
|
|
2292
|
+
if (await pathExists6(src) === false) {
|
|
2293
|
+
continue;
|
|
2294
|
+
}
|
|
2295
|
+
if (fs12.existsSync(dest)) {
|
|
2296
|
+
fs12.rmSync(dest, { recursive: true, force: true });
|
|
2297
|
+
}
|
|
2298
|
+
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
2299
|
+
fs12.symlinkSync(src, dest, symlinkType);
|
|
2300
|
+
linked.push({ dir, src });
|
|
2301
|
+
}
|
|
2302
|
+
for (const { dir, src } of linked) {
|
|
2303
|
+
const isLast = linked[linked.length - 1].dir === dir;
|
|
2304
|
+
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
2305
|
+
console.log(chalk13.dim(` ${branch} `) + chalk13.bold(`${dir}/`) + chalk13.dim(` \u2192 ${src}`) + chalk13.green(" \u2713"));
|
|
2306
|
+
}
|
|
2307
|
+
return true;
|
|
2308
|
+
} catch (ex) {
|
|
2309
|
+
console.error(chalk13.red(`\u274C Failed to create symlinks: ${ex.message}`));
|
|
2310
|
+
return false;
|
|
2311
|
+
}
|
|
2312
|
+
};
|
|
2313
|
+
const backupOk = await backupExistingGeminicliFiles();
|
|
2314
|
+
if (backupOk === false) {
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
const linkOk = await setGeminicliAssets();
|
|
2318
|
+
if (linkOk === false) {
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
configManager.geminicli = { path: GEMINICLI_DIR, backup_path: GEMINICLI_BACKUP_DIR };
|
|
2322
|
+
configManager.save();
|
|
2323
|
+
console.log(chalk13.dim(`
|
|
2324
|
+
\u2139 Gemini CLI file formats: skills/<name>/SKILL.md \xB7 commands/*.toml \xB7 agents/*.md`));
|
|
2325
|
+
console.log(chalk13.yellow(` \u26A0 Gemini CLI rejects agents with unknown frontmatter keys. Allowed keys only: `) + chalk13.dim(`name, description, kind, tools, mcpServers, model, temperature, max_turns, timeout_mins`));
|
|
2326
|
+
};
|
|
2327
|
+
var unlinkGeminicli = async (force = false) => {
|
|
2328
|
+
if (!force) {
|
|
2329
|
+
const ok = await confirm10({
|
|
2330
|
+
message: `Remove Gemini CLI symlinks from ${GEMINICLI_DIR}?`,
|
|
2331
|
+
default: false
|
|
2332
|
+
});
|
|
2333
|
+
if (!ok) {
|
|
2334
|
+
console.log(chalk13.yellow("Cancelled."));
|
|
2335
|
+
return;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
console.log(chalk13.red(`
|
|
2339
|
+
Removing Gemini CLI integration...`));
|
|
2340
|
+
console.log(chalk13.dim(GEMINICLI_DIR));
|
|
2341
|
+
const backupPath = configManager.geminicli?.backup_path ?? GEMINICLI_BACKUP_DIR;
|
|
2342
|
+
for (const dir of AGENT_PROMPT_DIRS["geminicli" /* GEMINICLI */]) {
|
|
2343
|
+
const target = path11.join(GEMINICLI_DIR, dir);
|
|
2344
|
+
if (fs12.existsSync(target) && fs12.lstatSync(target).isSymbolicLink()) {
|
|
2345
|
+
fs12.unlinkSync(target);
|
|
2346
|
+
console.log(chalk13.red(" removed symlink") + chalk13.dim(`: ${target}`));
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
if (fs12.existsSync(backupPath)) {
|
|
2350
|
+
try {
|
|
2351
|
+
for (const dir of AGENT_PROMPT_DIRS["geminicli" /* GEMINICLI */]) {
|
|
2352
|
+
const src = path11.join(backupPath, dir);
|
|
2353
|
+
const dest = path11.join(GEMINICLI_DIR, dir);
|
|
2354
|
+
if (!fs12.existsSync(src)) {
|
|
2355
|
+
continue;
|
|
2356
|
+
}
|
|
2357
|
+
fs12.renameSync(src, dest);
|
|
2358
|
+
console.log(chalk13.green(" restored") + chalk13.dim(`: ${dir}/`));
|
|
2359
|
+
}
|
|
2360
|
+
fs12.rmdirSync(backupPath);
|
|
2361
|
+
} catch (ex) {
|
|
2362
|
+
console.error(chalk13.red(` \u274C Failed to restore Gemini CLI backup: ${ex.message}`));
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
configManager.geminicli = null;
|
|
2366
|
+
configManager.save();
|
|
2367
|
+
};
|
|
2368
|
+
|
|
1818
2369
|
// src/commands/link-command.ts
|
|
1819
2370
|
var LINK_MAP = {
|
|
1820
2371
|
["claudecode" /* CLAUDECODE */]: linkClaudeCode,
|
|
@@ -1822,7 +2373,9 @@ var LINK_MAP = {
|
|
|
1822
2373
|
["openclaw" /* OPENCLAW */]: linkOpenclaw,
|
|
1823
2374
|
["codex" /* CODEX */]: linkCodex,
|
|
1824
2375
|
["antigravity" /* ANTIGRAVITY */]: linkAntigravity,
|
|
1825
|
-
["cursor" /* CURSOR */]: linkCursor
|
|
2376
|
+
["cursor" /* CURSOR */]: linkCursor,
|
|
2377
|
+
["opencode" /* OPENCODE */]: linkOpencode,
|
|
2378
|
+
["geminicli" /* GEMINICLI */]: linkGeminicli
|
|
1826
2379
|
};
|
|
1827
2380
|
var UNLINK_MAP = {
|
|
1828
2381
|
["claudecode" /* CLAUDECODE */]: unlinkClaudeCode,
|
|
@@ -1830,13 +2383,15 @@ var UNLINK_MAP = {
|
|
|
1830
2383
|
["openclaw" /* OPENCLAW */]: unlinkOpenclaw,
|
|
1831
2384
|
["codex" /* CODEX */]: unlinkCodex,
|
|
1832
2385
|
["antigravity" /* ANTIGRAVITY */]: unlinkAntigravity,
|
|
1833
|
-
["cursor" /* CURSOR */]: unlinkCursor
|
|
2386
|
+
["cursor" /* CURSOR */]: unlinkCursor,
|
|
2387
|
+
["opencode" /* OPENCODE */]: unlinkOpencode,
|
|
2388
|
+
["geminicli" /* GEMINICLI */]: unlinkGeminicli
|
|
1834
2389
|
};
|
|
1835
2390
|
var linkCommand = async (tool) => {
|
|
1836
2391
|
if (tool != null) {
|
|
1837
2392
|
const known = ALL_AGENTS.some((a) => a.value === tool);
|
|
1838
2393
|
if (!known) {
|
|
1839
|
-
console.log(
|
|
2394
|
+
console.log(chalk14.red(`Unknown vendor: ${tool}`));
|
|
1840
2395
|
process.exit(1);
|
|
1841
2396
|
}
|
|
1842
2397
|
await LINK_MAP[tool]();
|
|
@@ -1848,24 +2403,28 @@ var linkCommand = async (tool) => {
|
|
|
1848
2403
|
["openclaw" /* OPENCLAW */]: configManager.isOpenclawEnabled(),
|
|
1849
2404
|
["codex" /* CODEX */]: configManager.isCodexEnabled(),
|
|
1850
2405
|
["antigravity" /* ANTIGRAVITY */]: configManager.isAntigravityEnabled(),
|
|
1851
|
-
["cursor" /* CURSOR */]: configManager.isCursorEnabled()
|
|
2406
|
+
["cursor" /* CURSOR */]: configManager.isCursorEnabled(),
|
|
2407
|
+
["opencode" /* OPENCODE */]: configManager.isOpencodeEnabled(),
|
|
2408
|
+
["geminicli" /* GEMINICLI */]: configManager.isGeminicliEnabled()
|
|
1852
2409
|
};
|
|
1853
2410
|
const selected = await checkbox({
|
|
1854
2411
|
message: "Which AI agent do you want to integrate?",
|
|
1855
2412
|
choices: ALL_AGENTS.map((a) => ({
|
|
1856
|
-
name: prevLinked[a.value] ? `${a.name} ${
|
|
2413
|
+
name: prevLinked[a.value] ? `${a.name} ${chalk14.dim("(applied)")}` : a.name,
|
|
1857
2414
|
value: a.value,
|
|
1858
2415
|
checked: prevLinked[a.value]
|
|
1859
|
-
}))
|
|
2416
|
+
})),
|
|
2417
|
+
pageSize: ALL_AGENTS.length,
|
|
2418
|
+
loop: false
|
|
1860
2419
|
});
|
|
1861
2420
|
const toLink = ALL_AGENTS.filter((a) => !prevLinked[a.value] && selected.includes(a.value));
|
|
1862
2421
|
const toUnlink = ALL_AGENTS.filter((a) => prevLinked[a.value] && !selected.includes(a.value));
|
|
1863
2422
|
console.log();
|
|
1864
2423
|
if (toLink.length > 0) {
|
|
1865
|
-
console.log(
|
|
2424
|
+
console.log(chalk14.green(" Link ") + chalk14.dim("\u2192 ") + toLink.map((a) => chalk14.bold(a.name)).join(chalk14.dim(", ")));
|
|
1866
2425
|
}
|
|
1867
2426
|
if (toUnlink.length > 0) {
|
|
1868
|
-
console.log(
|
|
2427
|
+
console.log(chalk14.red(" Unlink ") + chalk14.dim("\u2192 ") + toUnlink.map((a) => chalk14.bold(a.name)).join(chalk14.dim(", ")));
|
|
1869
2428
|
}
|
|
1870
2429
|
console.log();
|
|
1871
2430
|
if (toLink.length === 0 && toUnlink.length === 0) {
|
|
@@ -1884,14 +2443,14 @@ var linkCommand = async (tool) => {
|
|
|
1884
2443
|
};
|
|
1885
2444
|
|
|
1886
2445
|
// src/commands/uninstall-command.ts
|
|
1887
|
-
import
|
|
1888
|
-
import
|
|
1889
|
-
import { confirm as
|
|
2446
|
+
import fs13 from "fs";
|
|
2447
|
+
import chalk15 from "chalk";
|
|
2448
|
+
import { confirm as confirm11 } from "@inquirer/prompts";
|
|
1890
2449
|
var uninstallCommand = async () => {
|
|
1891
2450
|
const targets = [
|
|
1892
|
-
{ label: `Config file ${
|
|
1893
|
-
{ label: `Home dir ${
|
|
1894
|
-
].filter((t) =>
|
|
2451
|
+
{ label: `Config file ${chalk15.dim(CONFIG_PATH)}`, path: CONFIG_PATH },
|
|
2452
|
+
{ label: `Home dir ${chalk15.dim(HOME_DIR)}`, path: HOME_DIR }
|
|
2453
|
+
].filter((t) => fs13.existsSync(t.path));
|
|
1895
2454
|
const hasClaudeCode = configManager.isClaudeCodeEnabled();
|
|
1896
2455
|
const hasRooCode = configManager.isRooCodeEnabled();
|
|
1897
2456
|
const hasOpenclaw = configManager.isOpenclawEnabled();
|
|
@@ -1899,32 +2458,32 @@ var uninstallCommand = async () => {
|
|
|
1899
2458
|
const hasCodex = configManager.isCodexEnabled();
|
|
1900
2459
|
const hasCursor = configManager.isCursorEnabled();
|
|
1901
2460
|
if (targets.length === 0 && !hasClaudeCode && !hasRooCode && !hasOpenclaw && !hasAntigravity && !hasCodex && !hasCursor) {
|
|
1902
|
-
console.log(
|
|
2461
|
+
console.log(chalk15.yellow("Nothing to remove."));
|
|
1903
2462
|
return;
|
|
1904
2463
|
}
|
|
1905
|
-
console.log(
|
|
2464
|
+
console.log(chalk15.red("\nThe following will be removed:"));
|
|
1906
2465
|
targets.forEach((t) => console.log(` ${t.label}`));
|
|
1907
2466
|
if (hasClaudeCode) {
|
|
1908
|
-
console.log(` Claude Code plugin dir ${
|
|
2467
|
+
console.log(` Claude Code plugin dir ${chalk15.dim(CLAUDE_CODE_DIR)}`);
|
|
1909
2468
|
}
|
|
1910
2469
|
if (hasRooCode) {
|
|
1911
|
-
console.log(` RooCode symlinks ${
|
|
2470
|
+
console.log(` RooCode symlinks ${chalk15.dim("(backup will be restored)")}`);
|
|
1912
2471
|
}
|
|
1913
2472
|
if (hasOpenclaw) {
|
|
1914
|
-
console.log(` OpenClaw symlinks ${
|
|
2473
|
+
console.log(` OpenClaw symlinks ${chalk15.dim("(backup will be restored)")}`);
|
|
1915
2474
|
}
|
|
1916
2475
|
if (hasAntigravity) {
|
|
1917
|
-
console.log(` Antigravity symlinks ${
|
|
2476
|
+
console.log(` Antigravity symlinks ${chalk15.dim("(backup will be restored)")}`);
|
|
1918
2477
|
}
|
|
1919
2478
|
if (hasCodex) {
|
|
1920
|
-
console.log(` Codex symlinks ${
|
|
2479
|
+
console.log(` Codex symlinks ${chalk15.dim("(backup will be restored)")}`);
|
|
1921
2480
|
}
|
|
1922
2481
|
if (hasCursor) {
|
|
1923
|
-
console.log(` Cursor plugin dir ${
|
|
2482
|
+
console.log(` Cursor plugin dir ${chalk15.dim("(symlink will be removed)")}`);
|
|
1924
2483
|
}
|
|
1925
|
-
const ok = await
|
|
2484
|
+
const ok = await confirm11({ message: "Proceed?", default: false });
|
|
1926
2485
|
if (!ok) {
|
|
1927
|
-
console.log(
|
|
2486
|
+
console.log(chalk15.yellow("Cancelled."));
|
|
1928
2487
|
return;
|
|
1929
2488
|
}
|
|
1930
2489
|
if (hasClaudeCode) {
|
|
@@ -1946,26 +2505,26 @@ var uninstallCommand = async () => {
|
|
|
1946
2505
|
await unlinkCursor(true);
|
|
1947
2506
|
}
|
|
1948
2507
|
for (const t of targets) {
|
|
1949
|
-
|
|
1950
|
-
console.log(
|
|
2508
|
+
fs13.rmSync(t.path, { recursive: true, force: true });
|
|
2509
|
+
console.log(chalk15.dim(` removed: ${t.path}`));
|
|
1951
2510
|
}
|
|
1952
|
-
console.log(
|
|
2511
|
+
console.log(chalk15.green("\nUninstalled."));
|
|
1953
2512
|
};
|
|
1954
2513
|
|
|
1955
2514
|
// src/commands/status-command.ts
|
|
1956
|
-
import
|
|
2515
|
+
import chalk16 from "chalk";
|
|
1957
2516
|
var statusCommand = () => {
|
|
1958
2517
|
if (configManager.repo_path == null) {
|
|
1959
|
-
console.log(
|
|
1960
|
-
console.log(
|
|
2518
|
+
console.log(chalk16.yellow("\u274C No repo installed."));
|
|
2519
|
+
console.log(chalk16.dim(` Run: set-prompt install <repo-url>`));
|
|
1961
2520
|
return;
|
|
1962
2521
|
}
|
|
1963
|
-
console.log(
|
|
1964
|
-
console.log(`${TAB}path ${
|
|
2522
|
+
console.log(chalk16.bold("\nRepo"));
|
|
2523
|
+
console.log(`${TAB}path ${chalk16.cyan(configManager.repo_path)}`);
|
|
1965
2524
|
if (configManager.remote_url != null) {
|
|
1966
|
-
console.log(`${TAB}remote ${
|
|
2525
|
+
console.log(`${TAB}remote ${chalk16.dim(configManager.remote_url)}`);
|
|
1967
2526
|
}
|
|
1968
|
-
console.log(
|
|
2527
|
+
console.log(chalk16.bold("\nLinked agents"));
|
|
1969
2528
|
for (const agent of ALL_AGENTS) {
|
|
1970
2529
|
let linked = false;
|
|
1971
2530
|
let agentPath = null;
|
|
@@ -1985,78 +2544,363 @@ var statusCommand = () => {
|
|
|
1985
2544
|
linked = configManager.isAntigravityEnabled();
|
|
1986
2545
|
agentPath = configManager.antigravity?.path;
|
|
1987
2546
|
}
|
|
1988
|
-
const label = linked ?
|
|
1989
|
-
const pathStr = linked && agentPath ?
|
|
2547
|
+
const label = linked ? chalk16.green("linked") : chalk16.dim("not linked");
|
|
2548
|
+
const pathStr = linked && agentPath ? chalk16.dim(` \u2192 ${agentPath}`) : "";
|
|
1990
2549
|
console.log(`${TAB}${agent.name.padEnd(12)} ${label}${pathStr}`);
|
|
1991
2550
|
}
|
|
1992
2551
|
console.log("");
|
|
1993
2552
|
};
|
|
1994
2553
|
|
|
1995
|
-
// src/commands/
|
|
1996
|
-
import { spawnSync as
|
|
1997
|
-
import
|
|
1998
|
-
var
|
|
2554
|
+
// src/commands/repo/pull-command.ts
|
|
2555
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
2556
|
+
import chalk17 from "chalk";
|
|
2557
|
+
var repoPullCommand = () => {
|
|
1999
2558
|
const repoPath = configManager.repo_path;
|
|
2000
2559
|
if (repoPath == null) {
|
|
2001
|
-
console.error(
|
|
2002
|
-
console.log(
|
|
2560
|
+
console.error(chalk17.red("\u274C No repo installed."));
|
|
2561
|
+
console.log(chalk17.yellow("Run: set-prompt install <git-url>"));
|
|
2003
2562
|
return;
|
|
2004
2563
|
}
|
|
2005
2564
|
if (configManager.remote_url == null) {
|
|
2006
|
-
console.error(
|
|
2565
|
+
console.error(chalk17.red("\u274C No remote URL registered. Cannot pull."));
|
|
2007
2566
|
return;
|
|
2008
2567
|
}
|
|
2009
|
-
console.log(
|
|
2010
|
-
console.log(
|
|
2011
|
-
const fetch =
|
|
2568
|
+
console.log(chalk17.green("\nPulling prompt repo..."));
|
|
2569
|
+
console.log(chalk17.dim(repoPath));
|
|
2570
|
+
const fetch = spawnSync4("git", ["fetch"], { cwd: repoPath, stdio: "inherit" });
|
|
2012
2571
|
if (fetch.status !== 0) {
|
|
2013
|
-
console.error(
|
|
2572
|
+
console.error(chalk17.red("\u274C git fetch failed."));
|
|
2014
2573
|
return;
|
|
2015
2574
|
}
|
|
2016
|
-
const pull =
|
|
2575
|
+
const pull = spawnSync4("git", ["pull"], { cwd: repoPath, stdio: "inherit" });
|
|
2017
2576
|
if (pull.status !== 0) {
|
|
2018
|
-
console.error(
|
|
2577
|
+
console.error(chalk17.red("\u274C git pull failed."));
|
|
2019
2578
|
return;
|
|
2020
2579
|
}
|
|
2021
|
-
console.log(
|
|
2580
|
+
console.log(chalk17.green("\u2705 Repo pulled."));
|
|
2581
|
+
};
|
|
2582
|
+
|
|
2583
|
+
// src/commands/repo/commit-command.ts
|
|
2584
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
2585
|
+
import chalk18 from "chalk";
|
|
2586
|
+
var repoCommitCommand = (options = {}) => {
|
|
2587
|
+
const repoPath = configManager.repo_path;
|
|
2588
|
+
if (repoPath == null) {
|
|
2589
|
+
console.error(chalk18.red("\u274C No repo installed."));
|
|
2590
|
+
console.log(chalk18.yellow("Run: set-prompt install <git-url>"));
|
|
2591
|
+
return false;
|
|
2592
|
+
}
|
|
2593
|
+
let message = options.message;
|
|
2594
|
+
if (message == null || message.trim() === "") {
|
|
2595
|
+
const generated = generateCommitMessage(repoPath);
|
|
2596
|
+
if (generated == null) {
|
|
2597
|
+
console.error(chalk18.red("\u274C Nothing to commit \u2014 working tree is clean."));
|
|
2598
|
+
return false;
|
|
2599
|
+
}
|
|
2600
|
+
message = generated;
|
|
2601
|
+
const subject = message.split("\n")[0];
|
|
2602
|
+
console.log(chalk18.dim(` (auto-generated: ${subject})`));
|
|
2603
|
+
}
|
|
2604
|
+
console.log(chalk18.green("\nCommitting prompt repo changes..."));
|
|
2605
|
+
console.log(chalk18.dim(repoPath));
|
|
2606
|
+
const add = spawnSync5("git", ["add", "-A"], { cwd: repoPath, stdio: "inherit" });
|
|
2607
|
+
if (add.status !== 0) {
|
|
2608
|
+
console.error(chalk18.red("\u274C git add failed."));
|
|
2609
|
+
return false;
|
|
2610
|
+
}
|
|
2611
|
+
const commit = spawnSync5("git", ["commit", "-m", message], { cwd: repoPath, stdio: "inherit" });
|
|
2612
|
+
if (commit.status !== 0) {
|
|
2613
|
+
console.error(chalk18.red("\u274C git commit failed \u2014 nothing to commit, or commit rejected."));
|
|
2614
|
+
return false;
|
|
2615
|
+
}
|
|
2616
|
+
console.log(chalk18.green("\u2705 Committed."));
|
|
2617
|
+
return true;
|
|
2618
|
+
};
|
|
2619
|
+
|
|
2620
|
+
// src/commands/repo/push-command.ts
|
|
2621
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
2622
|
+
import chalk19 from "chalk";
|
|
2623
|
+
var repoPushCommand = () => {
|
|
2624
|
+
const repoPath = configManager.repo_path;
|
|
2625
|
+
if (repoPath == null) {
|
|
2626
|
+
console.error(chalk19.red("\u274C No repo installed."));
|
|
2627
|
+
console.log(chalk19.yellow("Run: set-prompt install <git-url>"));
|
|
2628
|
+
return false;
|
|
2629
|
+
}
|
|
2630
|
+
if (configManager.remote_url == null) {
|
|
2631
|
+
console.error(chalk19.red("\u274C No remote URL registered. Cannot push."));
|
|
2632
|
+
return false;
|
|
2633
|
+
}
|
|
2634
|
+
console.log(chalk19.green("\nPushing prompt repo..."));
|
|
2635
|
+
console.log(chalk19.dim(repoPath));
|
|
2636
|
+
const push = spawnSync6("git", ["push"], { cwd: repoPath, stdio: "inherit" });
|
|
2637
|
+
if (push.status !== 0) {
|
|
2638
|
+
console.error(chalk19.red("\u274C git push failed."));
|
|
2639
|
+
return false;
|
|
2640
|
+
}
|
|
2641
|
+
console.log(chalk19.green("\u2705 Pushed."));
|
|
2642
|
+
return true;
|
|
2643
|
+
};
|
|
2644
|
+
|
|
2645
|
+
// src/commands/repo/save-command.ts
|
|
2646
|
+
var repoSaveCommand = (options = {}) => {
|
|
2647
|
+
const committed = repoCommitCommand({ message: options.message });
|
|
2648
|
+
if (committed === false) return;
|
|
2649
|
+
repoPushCommand();
|
|
2650
|
+
};
|
|
2651
|
+
|
|
2652
|
+
// src/commands/repo/status-command.ts
|
|
2653
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
2654
|
+
import chalk20 from "chalk";
|
|
2655
|
+
var parseBranchLine = (line) => {
|
|
2656
|
+
const body = line.replace(/^## /, "");
|
|
2657
|
+
if (body.startsWith("HEAD ") || body.includes("(no branch)")) {
|
|
2658
|
+
return { branch: null, upstream: null, ahead: 0, behind: 0 };
|
|
2659
|
+
}
|
|
2660
|
+
const [refPart, bracketPart] = body.split(/\s+(?=\[)/);
|
|
2661
|
+
const [branch, upstream] = refPart.split("...");
|
|
2662
|
+
let ahead = 0;
|
|
2663
|
+
let behind = 0;
|
|
2664
|
+
if (bracketPart != null) {
|
|
2665
|
+
const inside = bracketPart.replace(/[\[\]]/g, "");
|
|
2666
|
+
for (const piece of inside.split(",").map((s) => s.trim())) {
|
|
2667
|
+
const m = piece.match(/^(ahead|behind) (\d+)$/);
|
|
2668
|
+
if (m == null) continue;
|
|
2669
|
+
if (m[1] === "ahead") ahead = Number(m[2]);
|
|
2670
|
+
if (m[1] === "behind") behind = Number(m[2]);
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
return { branch: branch ?? null, upstream: upstream ?? null, ahead, behind };
|
|
2674
|
+
};
|
|
2675
|
+
var parseFileLine = (line) => {
|
|
2676
|
+
if (line.length < 4) return null;
|
|
2677
|
+
const status = line.slice(0, 2);
|
|
2678
|
+
let name = line.slice(3);
|
|
2679
|
+
const arrowIdx = name.indexOf(" -> ");
|
|
2680
|
+
if (arrowIdx >= 0) name = name.slice(arrowIdx + 4);
|
|
2681
|
+
if (name.startsWith('"') && name.endsWith('"')) name = name.slice(1, -1);
|
|
2682
|
+
if (status.includes("?")) return { label: "untracked", color: chalk20.gray, path: name };
|
|
2683
|
+
if (status.includes("D")) return { label: "deleted", color: chalk20.red, path: name };
|
|
2684
|
+
if (status.includes("R")) return { label: "renamed", color: chalk20.cyan, path: name };
|
|
2685
|
+
if (status.includes("A")) return { label: "added", color: chalk20.green, path: name };
|
|
2686
|
+
if (status.includes("M")) return { label: "modified", color: chalk20.yellow, path: name };
|
|
2687
|
+
return null;
|
|
2688
|
+
};
|
|
2689
|
+
var formatUpstream = (info) => {
|
|
2690
|
+
if (info.branch == null) return chalk20.red("(detached HEAD)");
|
|
2691
|
+
if (info.upstream == null) return `${info.branch} ${chalk20.yellow("(no upstream)")}`;
|
|
2692
|
+
const segs = [];
|
|
2693
|
+
if (info.ahead > 0) segs.push(chalk20.green(`ahead ${info.ahead}`));
|
|
2694
|
+
if (info.behind > 0) segs.push(chalk20.red(`behind ${info.behind}`));
|
|
2695
|
+
const trailing = segs.length > 0 ? ` (${segs.join(", ")})` : chalk20.dim(" (up to date)");
|
|
2696
|
+
return `${info.branch} ${chalk20.dim("\u2192")} ${info.upstream}${trailing}`;
|
|
2697
|
+
};
|
|
2698
|
+
var repoStatusCommand = () => {
|
|
2699
|
+
const repoPath = configManager.repo_path;
|
|
2700
|
+
if (repoPath == null) {
|
|
2701
|
+
console.error(chalk20.red("\u274C No repo installed."));
|
|
2702
|
+
console.log(chalk20.yellow("Run: set-prompt install <git-url>"));
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
const result = spawnSync7("git", ["status", "--porcelain=v1", "--branch", "--untracked-files=all"], {
|
|
2706
|
+
cwd: repoPath,
|
|
2707
|
+
encoding: "utf8"
|
|
2708
|
+
});
|
|
2709
|
+
if (result.status !== 0) {
|
|
2710
|
+
console.error(chalk20.red("\u274C git status failed."));
|
|
2711
|
+
if (result.stderr) console.error(chalk20.dim(result.stderr));
|
|
2712
|
+
return;
|
|
2713
|
+
}
|
|
2714
|
+
const lines = result.stdout.split("\n").filter((l) => l.length > 0);
|
|
2715
|
+
const branchLine = lines[0] ?? "## (unknown)";
|
|
2716
|
+
const fileLines = lines.slice(1);
|
|
2717
|
+
const branchInfo = parseBranchLine(branchLine);
|
|
2718
|
+
const changes = fileLines.map(parseFileLine).filter((f) => f !== null);
|
|
2719
|
+
console.log(`${chalk20.cyan("\u{1F4C2}")} ${chalk20.dim(repoPath)}`);
|
|
2720
|
+
console.log(`${chalk20.cyan("\u{1F33F}")} ${formatUpstream(branchInfo)}`);
|
|
2721
|
+
console.log("");
|
|
2722
|
+
if (changes.length === 0) {
|
|
2723
|
+
console.log(chalk20.green("\u2705 Working tree clean"));
|
|
2724
|
+
return;
|
|
2725
|
+
}
|
|
2726
|
+
console.log(chalk20.bold(`\u{1F4DD} Changes (${changes.length}):`));
|
|
2727
|
+
const labelWidth = Math.max(...changes.map((c) => c.label.length));
|
|
2728
|
+
for (const c of changes) {
|
|
2729
|
+
const label = c.color(c.label.padEnd(labelWidth));
|
|
2730
|
+
console.log(` ${label} ${c.path}`);
|
|
2731
|
+
}
|
|
2732
|
+
};
|
|
2733
|
+
|
|
2734
|
+
// src/commands/repo/path-command.ts
|
|
2735
|
+
import chalk21 from "chalk";
|
|
2736
|
+
var repoPathCommand = () => {
|
|
2737
|
+
const repoPath = configManager.repo_path;
|
|
2738
|
+
if (repoPath == null) {
|
|
2739
|
+
console.error(chalk21.red("\u274C No repo installed."));
|
|
2740
|
+
console.error(chalk21.yellow("Run: set-prompt install <git-url>"));
|
|
2741
|
+
process.exitCode = 1;
|
|
2742
|
+
return;
|
|
2743
|
+
}
|
|
2744
|
+
console.log(repoPath);
|
|
2745
|
+
};
|
|
2746
|
+
|
|
2747
|
+
// src/commands/repo/open-command.ts
|
|
2748
|
+
import { spawn } from "child_process";
|
|
2749
|
+
import path12 from "path";
|
|
2750
|
+
import chalk22 from "chalk";
|
|
2751
|
+
var resolveVscodeTarget = (repoPath) => {
|
|
2752
|
+
if (isOnPath("code") === false) return null;
|
|
2753
|
+
return { bin: "code", args: [repoPath] };
|
|
2754
|
+
};
|
|
2755
|
+
var resolveSourcetreeTarget = (repoPath) => {
|
|
2756
|
+
if (isOnPath("stree")) {
|
|
2757
|
+
return { bin: "stree", args: [repoPath] };
|
|
2758
|
+
}
|
|
2759
|
+
if (process.platform === "win32") {
|
|
2760
|
+
const exe = firstExistingPath([
|
|
2761
|
+
process.env.LOCALAPPDATA && path12.join(process.env.LOCALAPPDATA, "SourceTree", "SourceTree.exe"),
|
|
2762
|
+
process.env["ProgramFiles(x86)"] && path12.join(process.env["ProgramFiles(x86)"], "Atlassian", "SourceTree", "SourceTree.exe"),
|
|
2763
|
+
process.env.ProgramFiles && path12.join(process.env.ProgramFiles, "Atlassian", "SourceTree", "SourceTree.exe")
|
|
2764
|
+
]);
|
|
2765
|
+
if (exe != null) return { bin: exe, args: ["-f", repoPath] };
|
|
2766
|
+
}
|
|
2767
|
+
return null;
|
|
2768
|
+
};
|
|
2769
|
+
var VSCODE_INSTALL_HINT = `Install VSCode CLI: View \u2192 Command Palette \u2192 "Shell Command: Install 'code' command"`;
|
|
2770
|
+
var sourcetreeInstallHint = () => {
|
|
2771
|
+
if (process.platform === "darwin") {
|
|
2772
|
+
return 'Install Sourcetree, then: Sourcetree menu bar \u2192 "Install Command Line Tools"';
|
|
2773
|
+
}
|
|
2774
|
+
if (process.platform === "win32") {
|
|
2775
|
+
return "Install Sourcetree from https://sourcetreeapp.com/ \u2014 this CLI auto-detects it at %LOCALAPPDATA%\\SourceTree";
|
|
2776
|
+
}
|
|
2777
|
+
return "Sourcetree is not available on Linux \u2014 try a native Git GUI instead";
|
|
2778
|
+
};
|
|
2779
|
+
var runLaunch = (label, target) => {
|
|
2780
|
+
console.log(chalk22.green(`Opening in ${label}: ${chalk22.dim(target.args[target.args.length - 1])}`));
|
|
2781
|
+
const child = spawn(target.bin, target.args, { stdio: "ignore", detached: true, shell: true });
|
|
2782
|
+
child.unref();
|
|
2783
|
+
};
|
|
2784
|
+
var repoOpenCommand = (options = {}) => {
|
|
2785
|
+
const repoPath = configManager.repo_path;
|
|
2786
|
+
if (repoPath == null) {
|
|
2787
|
+
console.error(chalk22.red("\u274C No repo installed."));
|
|
2788
|
+
console.log(chalk22.yellow("Run: set-prompt install <git-url>"));
|
|
2789
|
+
return;
|
|
2790
|
+
}
|
|
2791
|
+
if (options.code === true) {
|
|
2792
|
+
const target = resolveVscodeTarget(repoPath);
|
|
2793
|
+
if (target == null) {
|
|
2794
|
+
console.error(chalk22.red("\u274C VSCode CLI (`code`) not found on PATH."));
|
|
2795
|
+
console.log(chalk22.dim(` ${VSCODE_INSTALL_HINT}`));
|
|
2796
|
+
return;
|
|
2797
|
+
}
|
|
2798
|
+
runLaunch("VSCode", target);
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
if (options.stree === true) {
|
|
2802
|
+
const target = resolveSourcetreeTarget(repoPath);
|
|
2803
|
+
if (target == null) {
|
|
2804
|
+
console.error(chalk22.red("\u274C Sourcetree not found."));
|
|
2805
|
+
console.log(chalk22.dim(` ${sourcetreeInstallHint()}`));
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2808
|
+
runLaunch("Sourcetree", target);
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2811
|
+
const platform = process.platform;
|
|
2812
|
+
const opener = platform === "win32" ? "explorer" : platform === "darwin" ? "open" : "xdg-open";
|
|
2813
|
+
console.log(chalk22.green(`Opening: ${chalk22.dim(repoPath)}`));
|
|
2814
|
+
const child = spawn(opener, [repoPath], { stdio: "ignore", detached: true });
|
|
2815
|
+
child.on("error", (ex) => {
|
|
2816
|
+
console.error(chalk22.red(`\u274C Failed to open: ${ex.message}`));
|
|
2817
|
+
});
|
|
2818
|
+
child.unref();
|
|
2022
2819
|
};
|
|
2023
2820
|
|
|
2024
2821
|
// src/index.ts
|
|
2025
2822
|
process.on("SIGINT", () => {
|
|
2026
|
-
console.log(
|
|
2823
|
+
console.log(chalk23.yellow("\nCancelled."));
|
|
2027
2824
|
process.exit(0);
|
|
2028
2825
|
});
|
|
2029
2826
|
process.on("unhandledRejection", (reason) => {
|
|
2030
2827
|
if (reason instanceof Error && reason.name === "ExitPromptError") {
|
|
2031
|
-
console.log(
|
|
2828
|
+
console.log(chalk23.yellow("\nCancelled."));
|
|
2032
2829
|
process.exit(0);
|
|
2033
2830
|
}
|
|
2034
2831
|
throw reason;
|
|
2035
2832
|
});
|
|
2036
|
-
var __dirname =
|
|
2037
|
-
var pkg = JSON.parse(
|
|
2833
|
+
var __dirname = path13.dirname(fileURLToPath(import.meta.url));
|
|
2834
|
+
var pkg = JSON.parse(fs14.readFileSync(path13.join(__dirname, "../package.json"), "utf-8"));
|
|
2038
2835
|
configManager.init();
|
|
2039
2836
|
var program = new Command();
|
|
2040
|
-
var banner =
|
|
2041
|
-
program.name("set-prompt").description(pkg.description).version(pkg.version).addHelpText("beforeAll", banner + "\n"
|
|
2042
|
-
|
|
2043
|
-
});
|
|
2044
|
-
program.command("install").description(`\u{1F4E6} Clone a ${chalk15.cyan("git repo")} into ${chalk15.dim("~/.set-prompt/repo/")} and register it as your prompt source`).argument("<url>", "remote git URL").action(async (source) => {
|
|
2837
|
+
var banner = chalk23.cyan(figlet.textSync("Set-Prompt", { horizontalLayout: "full" }));
|
|
2838
|
+
program.name("set-prompt").description(pkg.description).version(pkg.version).addHelpText("beforeAll", ({ command }) => command === program ? banner + "\n" : "");
|
|
2839
|
+
program.command("install").description(`\u{1F4E6} Clone a ${chalk23.cyan("git repo")} into ${chalk23.dim("~/.set-prompt/repo/")} and register it as your prompt source`).argument("<url>", "remote git URL").action(async (source) => {
|
|
2045
2840
|
await installCommand(source);
|
|
2046
2841
|
});
|
|
2047
|
-
program.command("link").description(`\u{1F517} Symlink your prompt repo into an ${
|
|
2842
|
+
program.command("link").description(`\u{1F517} Symlink your prompt repo into an ${chalk23.cyan("AI agent")} plugin dir ${chalk23.dim("(claudecode | roocode | openclaw | codex | antigravity)")}`).argument("[agent]", `target agent ${chalk23.dim("(omit for interactive selection)")}`).action(async (agent) => {
|
|
2048
2843
|
await linkCommand(agent);
|
|
2049
2844
|
});
|
|
2050
|
-
program.command("scaffold").description(`\u{1F6E0}\uFE0F Verify and create ${
|
|
2845
|
+
program.command("scaffold").description(`\u{1F6E0}\uFE0F Verify and create ${chalk23.cyan("required directories")} in a prompt repo ${chalk23.dim("(-f to force overwrite)")}`).argument("[path]", `path to repo ${chalk23.dim("(defaults to installed source)")}`).action(async (localPath) => {
|
|
2051
2846
|
await scaffoldCommand(localPath);
|
|
2052
2847
|
});
|
|
2053
|
-
program.command("status").description(`\u{1F4CB} Show registered ${
|
|
2848
|
+
program.command("status").description(`\u{1F4CB} Show registered ${chalk23.cyan("repo")} and which ${chalk23.cyan("agents")} are linked`).action(() => {
|
|
2054
2849
|
statusCommand();
|
|
2055
2850
|
});
|
|
2056
|
-
program.command("
|
|
2057
|
-
|
|
2851
|
+
var repo = program.command("repo").description(`\u{1F5C2}\uFE0F Manage the installed prompt repo ${chalk23.dim("(status | pull | commit | push | save | path | open)")}`);
|
|
2852
|
+
repo.command("status").description(`\u{1F4CB} Show VCS status of the repo ${chalk23.dim("(branch, ahead/behind, changed files)")}`).addHelpText("after", `
|
|
2853
|
+
Example output:
|
|
2854
|
+
\u{1F4C2} ~/.set-prompt/repo
|
|
2855
|
+
\u{1F33F} main \u2192 origin/main (ahead 2)
|
|
2856
|
+
|
|
2857
|
+
\u{1F4DD} Changes (2):
|
|
2858
|
+
modified skills/foo.md
|
|
2859
|
+
untracked draft.md
|
|
2860
|
+
`).action(() => {
|
|
2861
|
+
repoStatusCommand();
|
|
2862
|
+
});
|
|
2863
|
+
repo.command("pull").description(`\u{1F504} Fetch and pull the latest changes from the ${chalk23.cyan("remote repo")}`).action(() => {
|
|
2864
|
+
repoPullCommand();
|
|
2865
|
+
});
|
|
2866
|
+
repo.command("commit").description(`\u{1F4DD} Stage all changes and commit ${chalk23.dim("(auto-generates message if -m omitted; does not push)")}`).option("-m, --message <msg>", "commit message (auto-generated from changed files if omitted)").addHelpText("after", `
|
|
2867
|
+
Examples:
|
|
2868
|
+
$ sppt repo commit -m "edit dbml skill"
|
|
2869
|
+
$ sppt repo commit ${chalk23.dim('# auto-generates "update N files" + file list')}
|
|
2870
|
+
`).action((opts) => {
|
|
2871
|
+
repoCommitCommand({ message: opts.message });
|
|
2872
|
+
});
|
|
2873
|
+
repo.command("push").description(`\u2B06\uFE0F Push local commits to the remote`).action(() => {
|
|
2874
|
+
repoPushCommand();
|
|
2875
|
+
});
|
|
2876
|
+
repo.command("save").description(`\u{1F4BE} Stage + commit + push in one step ${chalk23.dim("(macro for commit \u2192 push; auto-generates message if -m omitted)")}`).option("-m, --message <msg>", "commit message (auto-generated from changed files if omitted)").addHelpText("after", `
|
|
2877
|
+
Examples:
|
|
2878
|
+
$ sppt repo save -m "edit dbml skill"
|
|
2879
|
+
$ sppt repo save ${chalk23.dim("# auto-generates message and pushes")}
|
|
2880
|
+
|
|
2881
|
+
Equivalent to: sppt repo commit && sppt repo push
|
|
2882
|
+
`).action((opts) => {
|
|
2883
|
+
repoSaveCommand({ message: opts.message });
|
|
2884
|
+
});
|
|
2885
|
+
repo.command("path").description(`\u{1F4CD} Print the repo path to stdout ${chalk23.dim("(e.g. cd $(sppt repo path))")}`).addHelpText("after", `
|
|
2886
|
+
Examples:
|
|
2887
|
+
$ sppt repo path
|
|
2888
|
+
${chalk23.dim("/Users/me/.set-prompt/repo")}
|
|
2889
|
+
|
|
2890
|
+
$ cd "$(sppt repo path)" ${chalk23.dim("# jump into the repo")}
|
|
2891
|
+
$ code "$(sppt repo path)" ${chalk23.dim("# open in VSCode")}
|
|
2892
|
+
`).action(() => {
|
|
2893
|
+
repoPathCommand();
|
|
2894
|
+
});
|
|
2895
|
+
repo.command("open").description(`\u{1F4C2} Open the repo in the OS file manager ${chalk23.dim("(--code: VSCode, --stree: Sourcetree)")}`).option("--code", "open with VSCode (`code` CLI)").option("--stree", "open with Sourcetree (`stree` CLI)").addHelpText("after", `
|
|
2896
|
+
Examples:
|
|
2897
|
+
$ sppt repo open ${chalk23.dim("# Explorer / Finder / xdg-open")}
|
|
2898
|
+
$ sppt repo open --code ${chalk23.dim("# open in VSCode")}
|
|
2899
|
+
$ sppt repo open --stree ${chalk23.dim("# open in Sourcetree")}
|
|
2900
|
+
`).action((opts) => {
|
|
2901
|
+
repoOpenCommand({ code: opts.code, stree: opts.stree });
|
|
2058
2902
|
});
|
|
2059
|
-
program.command("uninstall").description(`\u{1F5D1}\uFE0F Remove all set-prompt data ${
|
|
2903
|
+
program.command("uninstall").description(`\u{1F5D1}\uFE0F Remove all set-prompt data ${chalk23.dim("(~/.set-prompt/, plugin dirs, settings entries)")}`).action(async () => {
|
|
2060
2904
|
await uninstallCommand();
|
|
2061
2905
|
});
|
|
2062
2906
|
program.parse(process.argv);
|