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/dist/index.js CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk15 from "chalk";
5
+ import chalk23 from "chalk";
6
6
  import figlet from "figlet";
7
- import fs11 from "fs";
8
- import path10 from "path";
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 fs3 from "fs";
12
+ import fs4 from "fs";
13
13
  import path3 from "path";
14
- import { spawnSync } from "child_process";
15
- import chalk4 from "chalk";
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 fs2 from "fs";
358
+ import fs3 from "fs";
255
359
  import path2 from "path";
256
- import chalk3 from "chalk";
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
- # Pull latest changes
303
- set-prompt update
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 (max 64 chars). RooCode: emoji allowed. Antigravity: optional, defaults to folder name. |
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 | All | Display name. Claude Code: lowercase, numbers, hyphens only (max 64 chars). Cursor: defaults to folder name. |
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
- | \`model\` | No | All | Claude Code: \`sonnet\` or \`haiku\`. Cursor: \`fast\`, \`inherit\`, or a specific model ID. |
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
- fs2.mkdirSync(metaDir, { recursive: true });
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
- }, null, 4), { encoding: "utf-8" });
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
- fs2.mkdirSync(metaDir, { recursive: true });
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
- }, null, 4), { encoding: "utf-8" });
947
+ }, ".codex-plugin/plugin.json");
675
948
  };
676
949
  var ensureMcpJson = (repoPath) => {
677
950
  const mcpJsonPath = path2.join(repoPath, ".mcp.json");
678
- if (fs2.existsSync(mcpJsonPath)) {
951
+ if (fs3.existsSync(mcpJsonPath)) {
679
952
  return false;
680
953
  }
681
- fs2.writeFileSync(mcpJsonPath, JSON.stringify({ mcpServers: {} }, null, 4), { encoding: "utf-8" });
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 (fs2.existsSync(appJsonPath)) {
959
+ if (fs3.existsSync(appJsonPath)) {
687
960
  return false;
688
961
  }
689
- fs2.writeFileSync(appJsonPath, JSON.stringify({ apps: {} }, null, 4), { encoding: "utf-8" });
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(chalk3.red("No path provided and no repo registered. Please provide a path."));
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 (fs2.existsSync(targetPath) === false || fs2.statSync(targetPath).isDirectory() === false) {
706
- console.error(chalk3.red(`Invalid directory path: '${targetPath}'`));
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(chalk3.dim(`Scaffolding: ${targetPath}
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
- fs2.writeFileSync(guideMdPath, SET_PROMPT_GUIDE, { encoding: "utf-8", flag: "w" });
718
- console.log(`${TAB}${chalk3.green("\u2713")} SET_PROMPT_GUIDE.md`);
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 (fs2.existsSync(dirPath)) {
723
- console.log(`${TAB}${chalk3.dim("\u2713")} ${dirName}/`);
995
+ if (fs3.existsSync(dirPath)) {
996
+ console.log(`${TAB}${chalk4.dim("\u2713")} ${dirName}/`);
724
997
  } else {
725
- fs2.mkdirSync(dirPath, { recursive: true });
726
- console.log(`${TAB}${chalk3.green("+")} ${dirName}/`);
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 (!fs2.existsSync(gitkeepPath)) {
730
- fs2.writeFileSync(gitkeepPath, "", { encoding: "utf-8" });
1002
+ if (!fs3.existsSync(gitkeepPath)) {
1003
+ fs3.writeFileSync(gitkeepPath, "", { encoding: "utf-8" });
731
1004
  }
732
1005
  }
733
- ensureClaudePluginManifest(targetPath);
734
- console.log(`${TAB}${chalk3.green("\u2713")} .claude-plugin/plugin.json`);
735
- ensureCodexPluginManifest(targetPath);
736
- console.log(`${TAB}${chalk3.green("\u2713")} .codex-plugin/plugin.json`);
737
- console.log(`${TAB}${ensureMcpJson(targetPath) ? chalk3.green("+") : chalk3.dim("\u2713")} .mcp.json`);
738
- console.log(`${TAB}${ensureAppJson(targetPath) ? chalk3.green("+") : chalk3.dim("\u2713")} .app.json`);
739
- console.log(chalk3.green("\nScaffold complete."));
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(chalk3.red(`Failed to scaffold repo structure: ${ex.message}`), ex);
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 (fs3.existsSync(localPath) == true) {
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
- fs3.renameSync(localPath, backupPath);
756
- console.log(chalk4.yellow(" backed up") + chalk4.dim(` existing repo \u2192 ${backupPath}`));
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(chalk4.red("\u274C Cannot rename existing repo \u2014 it may be open in another process."));
760
- console.log(chalk4.dim(` Close any editors or terminals using: ${localPath}`));
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(chalk4.red(`\u274C Failed to backup existing repo: ${ex.message}`));
1039
+ console.error(chalk5.red(`\u274C Failed to backup existing repo: ${ex.message}`));
763
1040
  }
764
1041
  return false;
765
1042
  }
766
1043
  }
767
- fs3.mkdirSync(path3.dirname(localPath), { recursive: true });
1044
+ fs4.mkdirSync(path3.dirname(localPath), { recursive: true });
768
1045
  console.log(`Cloning ${remoteUrl}...`);
769
- const result = spawnSync("git", ["clone", remoteUrl, localPath], { stdio: "inherit" });
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
- fs3.rmSync(backupPath, { recursive: true, force: true });
777
- console.log(chalk4.red(" removed") + chalk4.dim(` backup \u2192 ${backupPath}`));
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, { force: true });
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(chalk4.red("Failed to save config."));
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(chalk4.red("\u274C Only remote git URLs are supported."));
792
- console.log(chalk4.dim(" Example: set-prompt install https://github.com/you/my-prompts"));
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(chalk4.red(`\u274C Already installed from the same URL: ${target}`));
799
- console.log(chalk4.dim(" Use `set-prompt update` to pull the latest changes."));
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(chalk4.yellow(`\u26A0 Switching repo: ${configManager.remote_url} \u2192 ${target}`));
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(chalk4.yellow("Cancelled."));
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(chalk4.yellow("Cancelled."));
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(chalk4.red(`Unexpected error: ${ex.message}`), ex);
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 chalk11 from "chalk";
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 fs4 from "fs";
1106
+ import fs5 from "fs";
829
1107
  import os2 from "os";
830
- import chalk5 from "chalk";
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
- fs4.mkdirSync(marketplaceMetaDir, { recursive: true });
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
- fs4.writeFileSync(
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(chalk5.dim(" \u251C\u2500\u2500 .claude-plugin/"));
853
- console.log(chalk5.dim(" \u2502 \u2514\u2500\u2500 marketplace.json") + chalk5.green(" \u2713"));
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
- fs4.mkdirSync(path4.dirname(pluginLink), { recursive: true });
856
- if (fs4.existsSync(pluginLink)) {
857
- fs4.rmSync(pluginLink, { recursive: true, force: true });
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
- fs4.symlinkSync(repoPath, pluginLink, symlinkType);
861
- console.log(chalk5.dim(" \u2514\u2500\u2500 plugins/"));
862
- console.log(chalk5.dim(` \u2514\u2500\u2500 ${PLUGIN_NAME}/`) + chalk5.dim(` \u2192 ${repoPath}`) + chalk5.green(" \u2713"));
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(chalk5.red(`\u274C Failed to build marketplace structure: ${ex.message}`));
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 (fs4.existsSync(claudeSettingsPath)) {
874
- const raw = fs4.readFileSync(claudeSettingsPath, "utf-8");
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(chalk5.yellow(" \u26A0 settings.json has unexpected format \u2014 proceeding with caution"));
1158
+ console.warn(chalk6.yellow(" \u26A0 settings.json has unexpected format \u2014 proceeding with caution"));
881
1159
  }
882
1160
  } catch {
883
- console.warn(chalk5.yellow(" \u26A0 Failed to parse settings.json \u2014 will not overwrite existing file"));
884
- console.error(chalk5.red("\u274C Could not register plugin. Please add manually."));
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 (fs4.existsSync(claudeSettingsPath)) {
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
- fs4.copyFileSync(claudeSettingsPath, backupPath);
1179
+ fs5.copyFileSync(claudeSettingsPath, backupPath);
902
1180
  } catch (ex) {
903
- console.warn(chalk5.yellow(` \u26A0 Could not create backup: ${ex.message}`));
1181
+ console.warn(chalk6.yellow(` \u26A0 Could not create backup: ${ex.message}`));
904
1182
  backupPath = null;
905
1183
  }
906
1184
  }
907
1185
  try {
908
- fs4.mkdirSync(path4.dirname(claudeSettingsPath), { recursive: true });
909
- fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 4), "utf-8");
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
- fs4.copyFileSync(backupPath, claudeSettingsPath);
914
- fs4.unlinkSync(backupPath);
915
- console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
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(chalk5.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
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
- fs4.unlinkSync(backupPath);
1202
+ fs5.unlinkSync(backupPath);
925
1203
  } catch {
926
1204
  }
927
1205
  }
928
1206
  console.log(`\u2705 Registered to Claude Code settings.`);
929
- console.log(chalk5.dim(` ${claudeSettingsPath}`));
1207
+ console.log(chalk6.dim(` ${claudeSettingsPath}`));
930
1208
  return true;
931
1209
  } catch (ex) {
932
- console.error(chalk5.red(`\u274C Failed to update settings.json: ${ex.message}`));
933
- console.log(chalk5.dim(" Please add the plugin manually via Claude Code /plugins."));
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 (fs4.existsSync(installedPluginsPath)) {
1221
+ if (fs5.existsSync(installedPluginsPath)) {
944
1222
  try {
945
- data = JSON.parse(fs4.readFileSync(installedPluginsPath, "utf-8"));
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
- fs4.mkdirSync(path4.dirname(installedPluginsPath), { recursive: true });
962
- fs4.writeFileSync(installedPluginsPath, JSON.stringify(data, null, 4), "utf-8");
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(chalk5.dim(` ${installPath}`));
1242
+ console.log(chalk6.dim(` ${installPath}`));
965
1243
  } catch (ex) {
966
- console.warn(chalk5.yellow(` \u26A0 Could not patch installed_plugins.json: ${ex.message}`));
1244
+ console.warn(chalk6.yellow(` \u26A0 Could not patch installed_plugins.json: ${ex.message}`));
967
1245
  }
968
1246
  };
969
- console.log(chalk5.green(`
1247
+ console.log(chalk6.green(`
970
1248
  Setting up Claude Code plugin...`));
971
- console.log(chalk5.dim(CLAUDE_CODE_DIR));
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(chalk5.yellow("Cancelled."));
1269
+ console.log(chalk6.yellow("Cancelled."));
992
1270
  return;
993
1271
  }
994
1272
  }
995
- console.log(chalk5.red(`
1273
+ console.log(chalk6.red(`
996
1274
  Removing Claude Code plugin...`));
997
- console.log(chalk5.dim(CLAUDE_CODE_DIR));
1275
+ console.log(chalk6.dim(CLAUDE_CODE_DIR));
998
1276
  const claudeSettingsPath = path4.join(os2.homedir(), ".claude", "settings.json");
999
- if (fs4.existsSync(claudeSettingsPath)) {
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
- fs4.copyFileSync(claudeSettingsPath, backupPath);
1004
- const settings = JSON.parse(fs4.readFileSync(claudeSettingsPath, "utf-8"));
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
- fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 4), "utf-8");
1013
- fs4.unlinkSync(backupPath);
1014
- console.log(chalk5.red(" removed") + chalk5.dim(` set-prompt entries from: ${claudeSettingsPath}`));
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
- fs4.copyFileSync(backupPath, claudeSettingsPath);
1017
- fs4.unlinkSync(backupPath);
1018
- console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
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(chalk5.red(` \u274C Failed to clean up settings.json: ${ex.message}`));
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 (fs4.existsSync(installedPluginsPath)) {
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
- fs4.copyFileSync(installedPluginsPath, backupPath);
1031
- const installed = JSON.parse(fs4.readFileSync(installedPluginsPath, "utf-8"));
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
- fs4.writeFileSync(installedPluginsPath, JSON.stringify(installed, null, 4), "utf-8");
1041
- fs4.unlinkSync(backupPath);
1042
- console.log(chalk5.red(" removed") + chalk5.dim(` set-prompt entries from: ${installedPluginsPath}`));
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
- fs4.copyFileSync(backupPath, installedPluginsPath);
1045
- fs4.unlinkSync(backupPath);
1046
- console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back installed_plugins.json."));
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(chalk5.red(` \u274C Failed to clean up installed_plugins.json: ${ex.message}`));
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 (fs4.existsSync(knownMarketplacesPath)) {
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
- fs4.copyFileSync(knownMarketplacesPath, backupPath);
1058
- const marketplaces = JSON.parse(fs4.readFileSync(knownMarketplacesPath, "utf-8"));
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
- fs4.writeFileSync(knownMarketplacesPath, JSON.stringify(marketplaces, null, 4), "utf-8");
1064
- fs4.unlinkSync(backupPath);
1065
- console.log(chalk5.red(" removed") + chalk5.dim(` set-prompt entry from: ${knownMarketplacesPath}`));
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
- fs4.copyFileSync(backupPath, knownMarketplacesPath);
1068
- fs4.unlinkSync(backupPath);
1069
- console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back known_marketplaces.json."));
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(chalk5.red(` \u274C Failed to clean up known_marketplaces.json: ${ex.message}`));
1350
+ console.error(chalk6.red(` \u274C Failed to clean up known_marketplaces.json: ${ex.message}`));
1073
1351
  }
1074
1352
  }
1075
- if (fs4.existsSync(CLAUDE_CODE_DIR)) {
1076
- fs4.rmSync(CLAUDE_CODE_DIR, { recursive: true, force: true });
1077
- console.log(chalk5.red(" removed") + chalk5.dim(`: ${CLAUDE_CODE_DIR}`));
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 fs5 from "fs";
1086
- import chalk6 from "chalk";
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(chalk6.green(`
1372
+ console.log(chalk7.green(`
1095
1373
  Setting up RooCode integration...`));
1096
- console.log(chalk6.dim(ROO_DIR));
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
- fs5.mkdirSync(ROO_DIR, { recursive: true });
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 (fs5.existsSync(target) && fs5.lstatSync(target).isSymbolicLink() === false && fs5.readdirSync(target).length > 0) {
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(chalk6.yellow(`
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(chalk6.dim(` - ${path5.join(ROO_DIR, dir)}`));
1392
+ console.log(chalk7.dim(` - ${path5.join(ROO_DIR, dir)}`));
1115
1393
  }
1116
- console.log(chalk6.yellow(` They will be backed up to: `) + chalk6.dim(ROO_BACKUP_DIR));
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(chalk6.yellow("Skipped RooCode linking."));
1397
+ console.log(chalk7.yellow("Skipped RooCode linking."));
1120
1398
  return false;
1121
1399
  }
1122
- fs5.mkdirSync(ROO_BACKUP_DIR, { recursive: true });
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
- fs5.renameSync(src, dest);
1127
- console.log(chalk6.yellow(" backed up") + chalk6.dim(`: ${dir}/ \u2192 ${dest}`));
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(chalk6.red(`\u274C Failed to backup existing directories: ${ex.message}`));
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 (fs5.existsSync(dest)) {
1145
- fs5.rmSync(dest, { recursive: true, force: true });
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
- fs5.symlinkSync(src, dest, symlinkType);
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(chalk6.dim(` ${branch} `) + chalk6.bold(`${dir}/`) + chalk6.dim(` \u2192 ${src}`) + chalk6.green(" \u2713"));
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(chalk6.red(`\u274C Failed to create symlinks: ${ex.message}`));
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(chalk6.yellow("Cancelled."));
1458
+ console.log(chalk7.yellow("Cancelled."));
1181
1459
  return;
1182
1460
  }
1183
1461
  }
1184
- console.log(chalk6.red(`
1462
+ console.log(chalk7.red(`
1185
1463
  Removing RooCode integration...`));
1186
- console.log(chalk6.dim(ROO_DIR));
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 (fs5.existsSync(target) && fs5.lstatSync(target).isSymbolicLink()) {
1191
- fs5.unlinkSync(target);
1192
- console.log(chalk6.red(" removed symlink") + chalk6.dim(`: ${target}`));
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 (fs5.existsSync(backupPath)) {
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 (!fs5.existsSync(src)) {
1478
+ if (!fs6.existsSync(src)) {
1201
1479
  continue;
1202
1480
  }
1203
- fs5.renameSync(src, dest);
1204
- console.log(chalk6.green(" restored") + chalk6.dim(`: ${dir}/`));
1481
+ fs6.renameSync(src, dest);
1482
+ console.log(chalk7.green(" restored") + chalk7.dim(`: ${dir}/`));
1205
1483
  }
1206
- fs5.rmdirSync(backupPath);
1484
+ fs6.rmdirSync(backupPath);
1207
1485
  } catch (ex) {
1208
- console.error(chalk6.red(` \u274C Failed to restore RooCode backup: ${ex.message}`));
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 fs6 from "fs";
1218
- import chalk7 from "chalk";
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(chalk7.green(`
1504
+ console.log(chalk8.green(`
1227
1505
  Setting up OpenClaw integration...`));
1228
- console.log(chalk7.dim(OPENCLAW_DIR));
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
- fs6.mkdirSync(OPENCLAW_DIR, { recursive: true });
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 (fs6.existsSync(target) && fs6.lstatSync(target).isSymbolicLink() === false && fs6.readdirSync(target).length > 0) {
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(chalk7.yellow(`
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(chalk7.dim(` - ${path6.join(OPENCLAW_DIR, dir)}`));
1524
+ console.log(chalk8.dim(` - ${path6.join(OPENCLAW_DIR, dir)}`));
1247
1525
  }
1248
- console.log(chalk7.yellow(` They will be backed up to: `) + chalk7.dim(OPENCLAW_BACKUP_DIR));
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(chalk7.yellow("Skipped OpenClaw linking."));
1529
+ console.log(chalk8.yellow("Skipped OpenClaw linking."));
1252
1530
  return false;
1253
1531
  }
1254
- fs6.mkdirSync(OPENCLAW_BACKUP_DIR, { recursive: true });
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
- fs6.renameSync(src, dest);
1259
- console.log(chalk7.yellow(" backed up") + chalk7.dim(`: ${dir}/ \u2192 ${dest}`));
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(chalk7.red(`\u274C Failed to backup existing directories: ${ex.message}`));
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 (fs6.existsSync(dest)) {
1277
- fs6.rmSync(dest, { recursive: true, force: true });
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
- fs6.symlinkSync(src, dest, symlinkType);
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(chalk7.dim(` ${branch} `) + chalk7.bold(`${dir}/`) + chalk7.dim(` \u2192 ${src}`) + chalk7.green(" \u2713"));
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(chalk7.red(`\u274C Failed to create symlinks: ${ex.message}`));
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(chalk7.yellow("Cancelled."));
1590
+ console.log(chalk8.yellow("Cancelled."));
1313
1591
  return;
1314
1592
  }
1315
1593
  }
1316
- console.log(chalk7.red(`
1594
+ console.log(chalk8.red(`
1317
1595
  Removing OpenClaw integration...`));
1318
- console.log(chalk7.dim(OPENCLAW_DIR));
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 (fs6.existsSync(target) && fs6.lstatSync(target).isSymbolicLink()) {
1323
- fs6.unlinkSync(target);
1324
- console.log(chalk7.red(" removed symlink") + chalk7.dim(`: ${target}`));
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 (fs6.existsSync(backupPath)) {
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 (!fs6.existsSync(src)) {
1610
+ if (!fs7.existsSync(src)) {
1333
1611
  continue;
1334
1612
  }
1335
- fs6.renameSync(src, dest);
1336
- console.log(chalk7.green(" restored") + chalk7.dim(`: ${dir}/`));
1613
+ fs7.renameSync(src, dest);
1614
+ console.log(chalk8.green(" restored") + chalk8.dim(`: ${dir}/`));
1337
1615
  }
1338
- fs6.rmdirSync(backupPath);
1616
+ fs7.rmdirSync(backupPath);
1339
1617
  } catch (ex) {
1340
- console.error(chalk7.red(` \u274C Failed to restore OpenClaw backup: ${ex.message}`));
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 fs7 from "fs";
1350
- import chalk8 from "chalk";
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(chalk8.green(`
1636
+ console.log(chalk9.green(`
1359
1637
  Setting up Antigravity integration...`));
1360
- console.log(chalk8.dim(ANTIGRAVITY_DIR));
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
- fs7.mkdirSync(ANTIGRAVITY_DIR, { recursive: true });
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 (fs7.existsSync(target) && fs7.lstatSync(target).isSymbolicLink() === false && fs7.readdirSync(target).length > 0) {
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(chalk8.yellow(`
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(chalk8.dim(` - ${path7.join(ANTIGRAVITY_DIR, dir)}`));
1656
+ console.log(chalk9.dim(` - ${path7.join(ANTIGRAVITY_DIR, dir)}`));
1379
1657
  }
1380
- console.log(chalk8.yellow(` They will be backed up to: `) + chalk8.dim(ANTIGRAVITY_BACKUP_DIR));
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(chalk8.yellow("Skipped Antigravity linking."));
1661
+ console.log(chalk9.yellow("Skipped Antigravity linking."));
1384
1662
  return false;
1385
1663
  }
1386
- fs7.mkdirSync(ANTIGRAVITY_BACKUP_DIR, { recursive: true });
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
- fs7.renameSync(src, dest);
1391
- console.log(chalk8.yellow(" backed up") + chalk8.dim(`: ${dir}/ \u2192 ${dest}`));
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(chalk8.red(`\u274C Failed to backup existing directories: ${ex.message}`));
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 (fs7.existsSync(dest)) {
1409
- fs7.rmSync(dest, { recursive: true, force: true });
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
- fs7.symlinkSync(src, dest, symlinkType);
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(chalk8.dim(` ${branch} `) + chalk8.bold(`${dir}/`) + chalk8.dim(` \u2192 ${src}`) + chalk8.green(" \u2713"));
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(chalk8.red(`\u274C Failed to create symlinks: ${ex.message}`));
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(chalk8.yellow("Cancelled."));
1722
+ console.log(chalk9.yellow("Cancelled."));
1445
1723
  return;
1446
1724
  }
1447
1725
  }
1448
- console.log(chalk8.red(`
1726
+ console.log(chalk9.red(`
1449
1727
  Removing Antigravity integration...`));
1450
- console.log(chalk8.dim(ANTIGRAVITY_DIR));
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 (fs7.existsSync(target) && fs7.lstatSync(target).isSymbolicLink()) {
1455
- fs7.unlinkSync(target);
1456
- console.log(chalk8.red(" removed symlink") + chalk8.dim(`: ${target}`));
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 (fs7.existsSync(backupPath)) {
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 (!fs7.existsSync(src)) {
1742
+ if (!fs8.existsSync(src)) {
1465
1743
  continue;
1466
1744
  }
1467
- fs7.renameSync(src, dest);
1468
- console.log(chalk8.green(" restored") + chalk8.dim(`: ${dir}/`));
1745
+ fs8.renameSync(src, dest);
1746
+ console.log(chalk9.green(" restored") + chalk9.dim(`: ${dir}/`));
1469
1747
  }
1470
- fs7.rmdirSync(backupPath);
1748
+ fs8.rmdirSync(backupPath);
1471
1749
  } catch (ex) {
1472
- console.error(chalk8.red(` \u274C Failed to restore Antigravity backup: ${ex.message}`));
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 fs8 from "fs";
1759
+ import fs9 from "fs";
1482
1760
  import os3 from "os";
1483
- import chalk9 from "chalk";
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 (fs8.existsSync(marketplacePath)) {
1506
- const raw = fs8.readFileSync(marketplacePath, "utf-8");
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(chalk9.yellow(" \u26A0 Failed to parse marketplace.json \u2014 will not overwrite existing file"));
1514
- console.error(chalk9.red("\u274C Could not register plugin. Please add manually."));
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 (fs8.existsSync(marketplacePath)) {
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
- fs8.copyFileSync(marketplacePath, backupPath);
1819
+ fs9.copyFileSync(marketplacePath, backupPath);
1542
1820
  } catch (ex) {
1543
- console.warn(chalk9.yellow(` \u26A0 Could not create backup: ${ex.message}`));
1821
+ console.warn(chalk10.yellow(` \u26A0 Could not create backup: ${ex.message}`));
1544
1822
  backupPath = null;
1545
1823
  }
1546
1824
  }
1547
1825
  try {
1548
- fs8.mkdirSync(path8.dirname(marketplacePath), { recursive: true });
1549
- fs8.writeFileSync(marketplacePath, JSON.stringify(marketplace, null, 4), "utf-8");
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
- fs8.copyFileSync(backupPath, marketplacePath);
1554
- fs8.unlinkSync(backupPath);
1555
- console.warn(chalk9.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
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(chalk9.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
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
- fs8.unlinkSync(backupPath);
1842
+ fs9.unlinkSync(backupPath);
1565
1843
  } catch {
1566
1844
  }
1567
1845
  }
1568
1846
  console.log(`\u2705 Registered to marketplace.json`);
1569
- console.log(chalk9.dim(` ${marketplacePath}`));
1847
+ console.log(chalk10.dim(` ${marketplacePath}`));
1570
1848
  return true;
1571
1849
  } catch (ex) {
1572
- console.error(chalk9.red(`\u274C Failed to update marketplace.json: ${ex.message}`));
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
- fs8.mkdirSync(path8.dirname(cachePath), { recursive: true });
1580
- if (fs8.existsSync(cachePath)) {
1581
- fs8.rmSync(cachePath, { recursive: true, force: true });
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
- fs8.symlinkSync(repoPath, cachePath, symlinkType);
1862
+ fs9.symlinkSync(repoPath, cachePath, symlinkType);
1585
1863
  console.log(`\u2705 Patched plugin cache.`);
1586
- console.log(chalk9.dim(` ${cachePath}`) + chalk9.dim(" \u2192 ") + chalk9.dim(repoPath));
1864
+ console.log(chalk10.dim(` ${cachePath}`) + chalk10.dim(" \u2192 ") + chalk10.dim(repoPath));
1587
1865
  } catch (ex) {
1588
- console.warn(chalk9.yellow(` \u26A0 Could not patch plugin cache: ${ex.message}`));
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 (fs8.existsSync(configPath)) {
1597
- config = TOML.parse(fs8.readFileSync(configPath, "utf-8"));
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
- fs8.mkdirSync(path8.dirname(configPath), { recursive: true });
1604
- fs8.writeFileSync(configPath, TOML.stringify(config), "utf-8");
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(chalk9.dim(` ${configPath}`));
1884
+ console.log(chalk10.dim(` ${configPath}`));
1607
1885
  } catch (ex) {
1608
- console.warn(chalk9.yellow(` \u26A0 Could not update config.toml: ${ex.message}`));
1886
+ console.warn(chalk10.yellow(` \u26A0 Could not update config.toml: ${ex.message}`));
1609
1887
  }
1610
1888
  };
1611
- console.log(chalk9.green(`
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(chalk9.yellow("Cancelled."));
1907
+ console.log(chalk10.yellow("Cancelled."));
1630
1908
  return;
1631
1909
  }
1632
1910
  }
1633
- console.log(chalk9.red(`
1911
+ console.log(chalk10.red(`
1634
1912
  Removing Codex plugin...`));
1635
1913
  const marketplacePath = path8.join(CODEX_AGENTS_DIR, "marketplace.json");
1636
- if (fs8.existsSync(marketplacePath)) {
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
- fs8.copyFileSync(marketplacePath, backupPath);
1641
- const marketplace = JSON.parse(fs8.readFileSync(marketplacePath, "utf-8"));
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
- fs8.writeFileSync(marketplacePath, JSON.stringify(marketplace, null, 4), "utf-8");
1649
- fs8.unlinkSync(backupPath);
1650
- console.log(chalk9.red(" removed") + chalk9.dim(` ${PLUGIN_NAME} from: ${marketplacePath}`));
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
- fs8.copyFileSync(backupPath, marketplacePath);
1653
- fs8.unlinkSync(backupPath);
1654
- console.warn(chalk9.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
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(chalk9.red(` \u274C Failed to clean up marketplace.json: ${ex.message}`));
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 (fs8.existsSync(configPath)) {
1939
+ if (fs9.existsSync(configPath)) {
1662
1940
  try {
1663
- const config = TOML.parse(fs8.readFileSync(configPath, "utf-8"));
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
- fs8.writeFileSync(configPath, TOML.stringify(config), "utf-8");
1668
- console.log(chalk9.red(" removed") + chalk9.dim(` ${pluginKey} from: ${configPath}`));
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(chalk9.red(` \u274C Failed to clean up config.toml: ${ex.message}`));
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 (fs8.existsSync(cacheMarketDir)) {
1676
- fs8.rmSync(cacheMarketDir, { recursive: true, force: true });
1677
- console.log(chalk9.red(" removed") + chalk9.dim(`: ${cacheMarketDir}`));
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 fs9 from "fs";
1686
- import chalk10 from "chalk";
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(chalk10.green(`
1973
+ console.log(chalk11.green(`
1696
1974
  Setting up Cursor integration...`));
1697
- console.log(chalk10.dim(CURSOR_DIR));
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 (fs9.existsSync(target) && fs9.lstatSync(target).isSymbolicLink() === false && fs9.readdirSync(target).length > 0) {
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(chalk10.yellow(`
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(chalk10.dim(` - ${path9.join(CURSOR_DIR, dir)}`));
1988
+ console.log(chalk11.dim(` - ${path9.join(CURSOR_DIR, dir)}`));
1711
1989
  }
1712
- console.log(chalk10.yellow(` They will be backed up to: `) + chalk10.dim(CURSOR_BACKUP_DIR));
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(chalk10.yellow("Skipped Cursor linking."));
1993
+ console.log(chalk11.yellow("Skipped Cursor linking."));
1716
1994
  return;
1717
1995
  }
1718
- fs9.mkdirSync(CURSOR_BACKUP_DIR, { recursive: true });
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
- fs9.renameSync(src, dest);
1723
- console.log(chalk10.yellow(" backed up") + chalk10.dim(`: ${dir}/ \u2192 ${dest}`));
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 (fs9.existsSync(dest)) {
1735
- fs9.rmSync(dest, { recursive: true, force: true });
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
- fs9.symlinkSync(src, dest, symlinkType);
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(chalk10.dim(` \u251C\u2500\u2500 `) + chalk10.bold(`${dir}/`) + chalk10.dim(` \u2192 ${src}`) + chalk10.green(" \u2713"));
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 (fs9.existsSync(mcpSrc)) {
1747
- if (fs9.existsSync(mcpDest) && fs9.statSync(mcpDest).ino !== fs9.statSync(mcpSrc).ino) {
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
- fs9.mkdirSync(CURSOR_BACKUP_DIR, { recursive: true });
1750
- fs9.renameSync(mcpDest, mcpBackup);
1751
- console.log(chalk10.yellow(" backed up") + chalk10.dim(`: mcp.json \u2192 ${mcpBackup}`));
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 (fs9.existsSync(mcpDest)) {
1754
- fs9.unlinkSync(mcpDest);
2031
+ if (fs10.existsSync(mcpDest)) {
2032
+ fs10.unlinkSync(mcpDest);
1755
2033
  }
1756
- fs9.linkSync(mcpSrc, mcpDest);
1757
- console.log(chalk10.dim(` \u2514\u2500\u2500 `) + chalk10.bold("mcp.json") + chalk10.dim(` \u21D4 ${mcpSrc}`) + chalk10.green(" \u2713"));
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(chalk10.red(`\u274C Failed to set up Cursor: ${ex.message}`));
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(chalk10.yellow("Cancelled."));
2051
+ console.log(chalk11.yellow("Cancelled."));
1774
2052
  return;
1775
2053
  }
1776
2054
  }
1777
- console.log(chalk10.red(`
2055
+ console.log(chalk11.red(`
1778
2056
  Removing Cursor integration...`));
1779
- console.log(chalk10.dim(CURSOR_DIR));
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 (fs9.existsSync(target) && fs9.lstatSync(target).isSymbolicLink()) {
1784
- fs9.unlinkSync(target);
1785
- console.log(chalk10.red(" removed symlink") + chalk10.dim(`: ${target}`));
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 (fs9.existsSync(mcpDest)) {
1790
- fs9.unlinkSync(mcpDest);
1791
- console.log(chalk10.red(" removed") + chalk10.dim(`: ${mcpDest}`));
2067
+ if (fs10.existsSync(mcpDest)) {
2068
+ fs10.unlinkSync(mcpDest);
2069
+ console.log(chalk11.red(" removed") + chalk11.dim(`: ${mcpDest}`));
1792
2070
  }
1793
- if (fs9.existsSync(backupPath)) {
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 (!fs9.existsSync(src)) {
2076
+ if (!fs10.existsSync(src)) {
1799
2077
  continue;
1800
2078
  }
1801
- fs9.renameSync(src, dest);
1802
- console.log(chalk10.green(" restored") + chalk10.dim(`: ${dir}/`));
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 (fs9.existsSync(mcpBackup)) {
1806
- fs9.renameSync(mcpBackup, mcpDest);
1807
- console.log(chalk10.green(" restored") + chalk10.dim(`: mcp.json`));
2083
+ if (fs10.existsSync(mcpBackup)) {
2084
+ fs10.renameSync(mcpBackup, mcpDest);
2085
+ console.log(chalk11.green(" restored") + chalk11.dim(`: mcp.json`));
1808
2086
  }
1809
- fs9.rmSync(backupPath, { recursive: true, force: true });
2087
+ fs10.rmSync(backupPath, { recursive: true, force: true });
1810
2088
  } catch (ex) {
1811
- console.error(chalk10.red(` \u274C Failed to restore Cursor backup: ${ex.message}`));
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(chalk11.red(`Unknown vendor: ${tool}`));
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} ${chalk11.dim("(applied)")}` : 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(chalk11.green(" Link ") + chalk11.dim("\u2192 ") + toLink.map((a) => chalk11.bold(a.name)).join(chalk11.dim(", ")));
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(chalk11.red(" Unlink ") + chalk11.dim("\u2192 ") + toUnlink.map((a) => chalk11.bold(a.name)).join(chalk11.dim(", ")));
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 fs10 from "fs";
1888
- import chalk12 from "chalk";
1889
- import { confirm as confirm9 } from "@inquirer/prompts";
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 ${chalk12.dim(CONFIG_PATH)}`, path: CONFIG_PATH },
1893
- { label: `Home dir ${chalk12.dim(HOME_DIR)}`, path: HOME_DIR }
1894
- ].filter((t) => fs10.existsSync(t.path));
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(chalk12.yellow("Nothing to remove."));
2461
+ console.log(chalk15.yellow("Nothing to remove."));
1903
2462
  return;
1904
2463
  }
1905
- console.log(chalk12.red("\nThe following will be removed:"));
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 ${chalk12.dim(CLAUDE_CODE_DIR)}`);
2467
+ console.log(` Claude Code plugin dir ${chalk15.dim(CLAUDE_CODE_DIR)}`);
1909
2468
  }
1910
2469
  if (hasRooCode) {
1911
- console.log(` RooCode symlinks ${chalk12.dim("(backup will be restored)")}`);
2470
+ console.log(` RooCode symlinks ${chalk15.dim("(backup will be restored)")}`);
1912
2471
  }
1913
2472
  if (hasOpenclaw) {
1914
- console.log(` OpenClaw symlinks ${chalk12.dim("(backup will be restored)")}`);
2473
+ console.log(` OpenClaw symlinks ${chalk15.dim("(backup will be restored)")}`);
1915
2474
  }
1916
2475
  if (hasAntigravity) {
1917
- console.log(` Antigravity symlinks ${chalk12.dim("(backup will be restored)")}`);
2476
+ console.log(` Antigravity symlinks ${chalk15.dim("(backup will be restored)")}`);
1918
2477
  }
1919
2478
  if (hasCodex) {
1920
- console.log(` Codex symlinks ${chalk12.dim("(backup will be restored)")}`);
2479
+ console.log(` Codex symlinks ${chalk15.dim("(backup will be restored)")}`);
1921
2480
  }
1922
2481
  if (hasCursor) {
1923
- console.log(` Cursor plugin dir ${chalk12.dim("(symlink will be removed)")}`);
2482
+ console.log(` Cursor plugin dir ${chalk15.dim("(symlink will be removed)")}`);
1924
2483
  }
1925
- const ok = await confirm9({ message: "Proceed?", default: false });
2484
+ const ok = await confirm11({ message: "Proceed?", default: false });
1926
2485
  if (!ok) {
1927
- console.log(chalk12.yellow("Cancelled."));
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
- fs10.rmSync(t.path, { recursive: true, force: true });
1950
- console.log(chalk12.dim(` removed: ${t.path}`));
2508
+ fs13.rmSync(t.path, { recursive: true, force: true });
2509
+ console.log(chalk15.dim(` removed: ${t.path}`));
1951
2510
  }
1952
- console.log(chalk12.green("\nUninstalled."));
2511
+ console.log(chalk15.green("\nUninstalled."));
1953
2512
  };
1954
2513
 
1955
2514
  // src/commands/status-command.ts
1956
- import chalk13 from "chalk";
2515
+ import chalk16 from "chalk";
1957
2516
  var statusCommand = () => {
1958
2517
  if (configManager.repo_path == null) {
1959
- console.log(chalk13.yellow("\u274C No repo installed."));
1960
- console.log(chalk13.dim(` Run: set-prompt install <repo-url>`));
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(chalk13.bold("\nRepo"));
1964
- console.log(`${TAB}path ${chalk13.cyan(configManager.repo_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 ${chalk13.dim(configManager.remote_url)}`);
2525
+ console.log(`${TAB}remote ${chalk16.dim(configManager.remote_url)}`);
1967
2526
  }
1968
- console.log(chalk13.bold("\nLinked agents"));
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 ? chalk13.green("linked") : chalk13.dim("not linked");
1989
- const pathStr = linked && agentPath ? chalk13.dim(` \u2192 ${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/update-command.ts
1996
- import { spawnSync as spawnSync2 } from "child_process";
1997
- import chalk14 from "chalk";
1998
- var updateCommand = () => {
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(chalk14.red("\u274C No repo installed."));
2002
- console.log(chalk14.yellow("Run: set-prompt install <git-url>"));
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(chalk14.red("\u274C No remote URL registered. Cannot update."));
2565
+ console.error(chalk17.red("\u274C No remote URL registered. Cannot pull."));
2007
2566
  return;
2008
2567
  }
2009
- console.log(chalk14.green("\nUpdating prompt repo..."));
2010
- console.log(chalk14.dim(repoPath));
2011
- const fetch = spawnSync2("git", ["fetch"], { cwd: repoPath, stdio: "inherit" });
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(chalk14.red("\u274C git fetch failed."));
2572
+ console.error(chalk17.red("\u274C git fetch failed."));
2014
2573
  return;
2015
2574
  }
2016
- const pull = spawnSync2("git", ["pull"], { cwd: repoPath, stdio: "inherit" });
2575
+ const pull = spawnSync4("git", ["pull"], { cwd: repoPath, stdio: "inherit" });
2017
2576
  if (pull.status !== 0) {
2018
- console.error(chalk14.red("\u274C git pull failed."));
2577
+ console.error(chalk17.red("\u274C git pull failed."));
2019
2578
  return;
2020
2579
  }
2021
- console.log(chalk14.green("\u2705 Repo updated."));
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(chalk15.yellow("\nCancelled."));
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(chalk15.yellow("\nCancelled."));
2828
+ console.log(chalk23.yellow("\nCancelled."));
2032
2829
  process.exit(0);
2033
2830
  }
2034
2831
  throw reason;
2035
2832
  });
2036
- var __dirname = path10.dirname(fileURLToPath(import.meta.url));
2037
- var pkg = JSON.parse(fs11.readFileSync(path10.join(__dirname, "../package.json"), "utf-8"));
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 = chalk15.cyan(figlet.textSync("Set-Prompt", { horizontalLayout: "full" }));
2041
- program.name("set-prompt").description(pkg.description).version(pkg.version).addHelpText("beforeAll", banner + "\n").action(() => {
2042
- program.help();
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 ${chalk15.cyan("AI agent")} plugin dir ${chalk15.dim("(claudecode | roocode | openclaw | codex | antigravity)")}`).argument("[agent]", `target agent ${chalk15.dim("(omit for interactive selection)")}`).action(async (agent) => {
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 ${chalk15.cyan("required directories")} in a prompt repo ${chalk15.dim("(-f to force overwrite)")}`).argument("[path]", `path to repo ${chalk15.dim("(defaults to installed source)")}`).action(async (localPath) => {
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 ${chalk15.cyan("repo")} and which ${chalk15.cyan("agents")} are linked`).action(() => {
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("update").description(`\u{1F504} Fetch and pull the latest changes from the ${chalk15.cyan("remote repo")}`).action(() => {
2057
- updateCommand();
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 ${chalk15.dim("(~/.set-prompt/, plugin dirs, settings entries)")}`).action(async () => {
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);