set-prompt 0.3.0 → 0.5.2

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,23 +2,25 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk8 from "chalk";
5
+ import chalk15 from "chalk";
6
6
  import figlet from "figlet";
7
- import fs6 from "fs";
8
- import path5 from "path";
7
+ import fs11 from "fs";
8
+ import path10 from "path";
9
9
  import { fileURLToPath } from "url";
10
10
 
11
11
  // src/commands/install-command.ts
12
12
  import fs3 from "fs";
13
13
  import path3 from "path";
14
14
  import { spawnSync } from "child_process";
15
- import chalk3 from "chalk";
15
+ import chalk4 from "chalk";
16
16
  import { confirm as confirm2 } from "@inquirer/prompts";
17
17
 
18
18
  // src/_defs/index.ts
19
19
  import path from "path";
20
20
  import os from "os";
21
21
  var TAB = ` `;
22
+ var MARKET_NAME = "set-prompt";
23
+ var PLUGIN_NAME = "sppt";
22
24
  var HOME_DIR = path.join(os.homedir(), ".set-prompt");
23
25
  var CONFIG_PATH = path.join(HOME_DIR, "config.json");
24
26
  var REPO_DIR = path.join(HOME_DIR, "repo");
@@ -29,20 +31,25 @@ var OPENCLAW_DIR = path.join(os.homedir(), ".openclaw", "workspace");
29
31
  var OPENCLAW_BACKUP_DIR = path.join(OPENCLAW_DIR, "SET_PROMPT_BACKUP");
30
32
  var ANTIGRAVITY_DIR = path.join(os.homedir(), ".gemini", "antigravity");
31
33
  var ANTIGRAVITY_BACKUP_DIR = path.join(ANTIGRAVITY_DIR, "SET_PROMPT_BACKUP");
32
- var PROMPT_DIR_NAMES = ["skills", "commands", "hooks", "agents"];
34
+ var CODEX_DIR = path.join(HOME_DIR, "codex");
35
+ var CODEX_BACKUP_DIR = path.join(CODEX_DIR, "SET_PROMPT_BACKUP");
36
+ var CURSOR_DIR = path.join(os.homedir(), ".cursor");
37
+ var PROMPT_DIR_NAMES = ["skills", "commands", "hooks", "agents", "rules"];
33
38
  var AGENT_PROMPT_DIRS = {
34
39
  ["claudecode" /* CLAUDECODE */]: ["skills", "commands", "hooks", "agents"],
35
40
  ["roocode" /* ROOCODE */]: ["skills", "commands"],
36
41
  ["openclaw" /* OPENCLAW */]: ["skills"],
37
- ["codex" /* CODEX */]: ["skills", "commands"],
38
- ["antigravity" /* ANTIGRAVITY */]: ["skills"]
42
+ ["codex" /* CODEX */]: ["skills"],
43
+ ["antigravity" /* ANTIGRAVITY */]: ["skills"],
44
+ ["cursor" /* CURSOR */]: ["skills", "agents", "commands", "hooks"]
39
45
  };
40
46
  var ALL_AGENTS = [
41
47
  { name: "Claude Code", value: "claudecode" /* CLAUDECODE */ },
42
48
  { name: "RooCode", value: "roocode" /* ROOCODE */ },
43
49
  { name: "OpenClaw", value: "openclaw" /* OPENCLAW */ },
44
50
  { name: "Codex", value: "codex" /* CODEX */ },
45
- { name: "Antigravity", value: "antigravity" /* ANTIGRAVITY */ }
51
+ { name: "Antigravity", value: "antigravity" /* ANTIGRAVITY */ },
52
+ { name: "Cursor", value: "cursor" /* CURSOR */ }
46
53
  ];
47
54
 
48
55
  // src/_libs/config.ts
@@ -65,12 +72,17 @@ var OpenclawConfigSchema = z.object({
65
72
  backup_path: z.string().nullish().optional()
66
73
  });
67
74
  var CodexConfigSchema = z.object({
68
- path: z.string().nullable()
75
+ path: z.string().nullable(),
76
+ backup_path: z.string().nullish().optional()
69
77
  });
70
78
  var AntigravityConfigSchema = z.object({
71
79
  path: z.string().nullable(),
72
80
  backup_path: z.string().nullish().optional()
73
81
  });
82
+ var CursorConfigSchema = z.object({
83
+ path: z.string().nullable(),
84
+ backup_path: z.string().nullish().optional()
85
+ });
74
86
  var GlobalConfigSchema = z.object({
75
87
  repo_path: z.string(),
76
88
  remote_url: z.string().nullable(),
@@ -78,7 +90,8 @@ var GlobalConfigSchema = z.object({
78
90
  roocode: RoocodeConfigSchema.nullable(),
79
91
  openclaw: OpenclawConfigSchema.nullable(),
80
92
  codex: CodexConfigSchema.nullish().optional(),
81
- antigravity: AntigravityConfigSchema.nullish().optional()
93
+ antigravity: AntigravityConfigSchema.nullish().optional(),
94
+ cursor: CursorConfigSchema.nullish().optional()
82
95
  });
83
96
 
84
97
  // src/_libs/config.ts
@@ -91,6 +104,7 @@ var ConfigManager = class {
91
104
  this._openclaw = null;
92
105
  this._codex = null;
93
106
  this._antigravity = null;
107
+ this._cursor = null;
94
108
  }
95
109
  get repo_path() {
96
110
  return this._repo_path;
@@ -113,6 +127,9 @@ var ConfigManager = class {
113
127
  get antigravity() {
114
128
  return this._antigravity;
115
129
  }
130
+ get cursor() {
131
+ return this._cursor;
132
+ }
116
133
  set repo_path(v) {
117
134
  this._repo_path = v;
118
135
  }
@@ -134,10 +151,13 @@ var ConfigManager = class {
134
151
  set antigravity(v) {
135
152
  this._antigravity = v;
136
153
  }
154
+ set cursor(v) {
155
+ this._cursor = v;
156
+ }
137
157
  init() {
138
158
  this._loadFromDisk();
139
159
  if (this._repo_path != null) {
140
- console.log(chalk.dim(`Config loaded from ${CONFIG_PATH}`));
160
+ console.log(chalk.green(`Config loaded`) + chalk.dim(` from ${CONFIG_PATH}`));
141
161
  }
142
162
  }
143
163
  save() {
@@ -154,7 +174,8 @@ var ConfigManager = class {
154
174
  roocode: this._roocode,
155
175
  openclaw: this._openclaw,
156
176
  codex: this._codex,
157
- antigravity: this._antigravity
177
+ antigravity: this._antigravity,
178
+ cursor: this._cursor
158
179
  }, null, 4);
159
180
  fs.writeFileSync(CONFIG_PATH, configStr, "utf-8");
160
181
  console.log(chalk.green(`Config saved`) + chalk.dim(` \u2192 ${CONFIG_PATH}`));
@@ -188,6 +209,9 @@ var ConfigManager = class {
188
209
  isAntigravityEnabled() {
189
210
  return this._antigravity != null;
190
211
  }
212
+ isCursorEnabled() {
213
+ return this._cursor != null;
214
+ }
191
215
  _assign(config) {
192
216
  this._repo_path = config.repo_path;
193
217
  this._remote_url = config.remote_url;
@@ -196,6 +220,7 @@ var ConfigManager = class {
196
220
  this._openclaw = config.openclaw;
197
221
  this._codex = config.codex ?? null;
198
222
  this._antigravity = config.antigravity ?? null;
223
+ this._cursor = config.cursor ?? null;
199
224
  }
200
225
  _loadFromDisk() {
201
226
  if (fs.existsSync(CONFIG_PATH) === false) {
@@ -214,23 +239,33 @@ var ConfigManager = class {
214
239
  var configManager = new ConfigManager();
215
240
 
216
241
  // src/_libs/index.ts
242
+ import chalk2 from "chalk";
217
243
  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
+ var resolveRepoPath = () => {
245
+ if (configManager.repo_path == null) {
246
+ console.error(chalk2.red("\u274C No repo installed."));
247
+ console.log(chalk2.yellow("Run: set-prompt install <git-url>"));
248
+ return null;
249
+ }
250
+ return configManager.repo_path;
251
+ };
218
252
 
219
253
  // src/commands/scaffold-command.ts
220
254
  import fs2 from "fs";
221
255
  import path2 from "path";
222
- import chalk2 from "chalk";
256
+ import chalk3 from "chalk";
223
257
  import { confirm } from "@inquirer/prompts";
224
258
 
225
259
  // src/_libs/templates.ts
226
260
  var SET_PROMPT_GUIDE = `# Set Prompt Repository Guide
227
261
 
228
- > Managed by [set-prompt](https://github.com/alkemic-studio/set-prompt)
262
+ > Managed by [set-prompt](https://github.com/juncha9/set-prompt)
263
+
264
+ This is a shared prompt repository linked to various AI agents via \`set-prompt link\`. When writing or editing prompts in this repository, refer to the structure below and the frontmatter reference for each platform's conventions.
229
265
 
230
266
  ## Structure
231
267
 
232
268
  \`\`\`
233
- \u251C\u2500\u2500 set-prompt.toml # Repository configuration
234
269
  \u251C\u2500\u2500 skills/
235
270
  \u2502 \u2514\u2500\u2500 <skill-name>/
236
271
  \u2502 \u251C\u2500\u2500 SKILL.md # Platform-specific frontmatter + prompt content
@@ -239,10 +274,27 @@ var SET_PROMPT_GUIDE = `# Set Prompt Repository Guide
239
274
  \u2502 \u2514\u2500\u2500 <command-name>/
240
275
  \u2502 \u251C\u2500\u2500 COMMAND.md # Platform-specific frontmatter + prompt content
241
276
  \u2502 \u2514\u2500\u2500 ... # Supporting files
242
- \u251C\u2500\u2500 hooks/ # Lifecycle shell hooks
243
- \u2514\u2500\u2500 agents/ # Agent definitions (Claude Code)
244
- \u2514\u2500\u2500 <agent-name>/
245
- \u2514\u2500\u2500 AGENT.md
277
+ \u251C\u2500\u2500 hooks/ # Lifecycle shell hooks (Claude Code)
278
+ \u251C\u2500\u2500 agents/ # Agent definitions (Claude Code, Cursor)
279
+ \u2502 \u2514\u2500\u2500 <agent-name>/
280
+ \u2502 \u2514\u2500\u2500 AGENT.md
281
+ \u2514\u2500\u2500 rules/ # Rule definitions (Cursor)
282
+ \u2514\u2500\u2500 <rule-name>/
283
+ \u2514\u2500\u2500 RULE.md
284
+ \`\`\`
285
+
286
+ ## Usage
287
+
288
+ \`\`\`bash
289
+ # Scaffold this repo's directory structure
290
+ set-prompt scaffold .
291
+
292
+ # Install from remote and link to AI tools
293
+ set-prompt install https://github.com/you/my-prompts
294
+ set-prompt link
295
+
296
+ # Pull latest changes
297
+ set-prompt update
246
298
  \`\`\`
247
299
 
248
300
  ## Frontmatter Reference
@@ -292,6 +344,15 @@ metadata: {"os":["darwin","linux"],"requires":{"bins":["git"],"env":["MY_API_KEY
292
344
  # Antigravity
293
345
  name: my-skill
294
346
  description: "What this skill does and when to use it"
347
+
348
+ # Cursor
349
+ name: "my-skill"
350
+ description: "What this skill does and when to use it"
351
+ license: "MIT"
352
+ compatibility: "Requires Node.js 18+"
353
+ metadata:
354
+ category: "development"
355
+ disable-model-invocation: false
295
356
  ---
296
357
  \`\`\`
297
358
 
@@ -313,10 +374,13 @@ description: "What this skill does and when to use it"
313
374
  | \`metadata\` | No | OpenClaw | Single-line JSON for platform gating: \`os\` (platform filter), \`requires.bins\` (required binaries), \`requires.env\` (required env vars) |
314
375
  | \`homepage\` | No | OpenClaw | URL shown as "Website" in the macOS Skills UI. Also settable via \`metadata.openclaw.homepage\`. |
315
376
  | \`user-invocable\` | No | OpenClaw | \`false\` = hidden from \`/\` menu. (default: \`true\`) |
316
- | \`disable-model-invocation\` | No | CC, OpenClaw | \`true\` = skill excluded from model prompt, still available via user invocation. (default: \`false\`) |
377
+ | \`disable-model-invocation\` | No | Claude Code, OpenClaw, Cursor | \`true\` = skill only included when explicitly invoked via \`/skill-name\`. (default: \`false\`) |
317
378
  | \`command-dispatch\` | No | OpenClaw | \`"tool"\` = bypass model, dispatch directly to a tool |
318
379
  | \`command-tool\` | No | OpenClaw | Tool to invoke when \`command-dispatch: "tool"\` |
319
380
  | \`command-arg-mode\` | No | OpenClaw | How arguments are forwarded to the tool. (default: \`"raw"\`) |
381
+ | \`license\` | No | Cursor | License name or reference to a bundled license file. |
382
+ | \`compatibility\` | No | Cursor | Environment requirements (system packages, network access, etc.) |
383
+ | \`metadata\` | No | Cursor | Arbitrary key-value mapping for additional metadata. |
320
384
 
321
385
  ---
322
386
 
@@ -360,7 +424,7 @@ command-tool: "Bash"
360
424
  |-------|----------|----------|-------------|
361
425
  | \`name\` | No | All | Display name \u2014 lowercase, numbers, hyphens only (max 64 chars). Defaults to directory name. |
362
426
  | \`description\` | Yes | All | Shown in \`/\` menu. Claude uses this to decide auto-loading. |
363
- | \`user-invocable\` | No | CC, OpenClaw | \`false\` = hidden from \`/\` menu, background knowledge only. (default: \`true\`) |
427
+ | \`user-invocable\` | No | Claude Code, OpenClaw | \`false\` = hidden from \`/\` menu, background knowledge only. (default: \`true\`) |
364
428
  | \`allowed-tools\` | No | Claude Code | Tools Claude can use without asking. e.g. \`Read\` \`Write\` \`Edit\` \`Bash\` \`Grep\` \`Glob\` |
365
429
  | \`argument-hint\` | No | Claude Code | Hint shown during autocomplete. e.g. \`[issue-number]\` |
366
430
  | \`model\` | No | Claude Code | Model to use when active. \`sonnet\` or \`haiku\` |
@@ -372,10 +436,11 @@ command-tool: "Bash"
372
436
 
373
437
  ### Agents
374
438
 
375
- Custom subagent definitions loaded by Claude Code.
439
+ Custom subagent definitions loaded by Claude Code and Cursor.
376
440
 
377
441
  \`\`\`yaml
378
442
  ---
443
+ # Claude Code
379
444
  name: "my-agent"
380
445
  description: "What this agent does and when to use it"
381
446
  allowed-tools:
@@ -383,37 +448,87 @@ allowed-tools:
383
448
  - Bash
384
449
  model: sonnet
385
450
  context: fork
451
+
452
+ # Cursor
453
+ name: "my-agent"
454
+ description: "What this agent does and when to use it"
455
+ model: inherit
456
+ readonly: false
457
+ is_background: false
458
+ ---
459
+ \`\`\`
460
+
461
+ | Field | Required | Platform | Description |
462
+ |-------|----------|----------|-------------|
463
+ | \`name\` | Yes | All | Display name. Claude Code: lowercase, numbers, hyphens only (max 64 chars). Cursor: defaults to folder name. |
464
+ | \`description\` | Yes | All | When and how to use this agent. Used to decide when to spawn/delegate. |
465
+ | \`allowed-tools\` | No | Claude Code | Tools this agent can use without asking |
466
+ | \`model\` | No | All | Claude Code: \`sonnet\` or \`haiku\`. Cursor: \`fast\`, \`inherit\`, or a specific model ID. |
467
+ | \`context\` | No | Claude Code | \`fork\` = run in isolated subagent context |
468
+ | \`readonly\` | No | Cursor | \`true\` = sub-agent runs with restricted write permissions (no file edits or state-changing shell commands). (default: \`false\`) |
469
+ | \`is_background\` | No | Cursor | \`true\` = sub-agent runs in background without blocking parent. (default: \`false\`) |
470
+
471
+ ---
472
+
473
+ ### Rules
474
+
475
+ System-level instructions for the AI agent. Cursor only. Rules are markdown files (\`.md\` or \`.mdc\`) stored in \`.cursor/rules/\`. When a rule is active, its content is prepended to the model context.
476
+
477
+ \`\`\`yaml
478
+ ---
479
+ description: "When and how this rule should be applied"
480
+ globs:
481
+ - "**/*.ts"
482
+ - "**/*.tsx"
483
+ alwaysApply: false
386
484
  ---
485
+
486
+ Use \`@filename\` to reference files instead of copying content.
387
487
  \`\`\`
388
488
 
489
+ **Activation Types**
490
+
491
+ | Type | \`alwaysApply\` | \`globs\` | \`description\` | Behavior |
492
+ |------|:-----------:|:-----:|:-----------:|----------|
493
+ | Always | \`true\` | ignored | optional | Included in every conversation |
494
+ | Auto Attached | \`false\` | set | optional | Included when open files match glob patterns |
495
+ | Agent Requested | \`false\` | empty | set | AI decides based on description relevance |
496
+ | Manual | \`false\` | empty | empty | Invoked via \`@rule-name\` mention only |
497
+
389
498
  | Field | Required | Description |
390
499
  |-------|----------|-------------|
391
- | \`name\` | Yes | Display name \u2014 lowercase, numbers, hyphens only (max 64 chars) |
392
- | \`description\` | Yes | When and how to use this agent. Claude uses this to decide when to spawn it. |
393
- | \`allowed-tools\` | No | Tools this agent can use without asking |
394
- | \`model\` | No | Model override. \`sonnet\` or \`haiku\` |
395
- | \`context\` | No | \`fork\` = run in isolated subagent context |
500
+ | \`description\` | No | Describes the rule's purpose. Used by AI to decide relevance (Agent Requested type). |
501
+ | \`globs\` | No | File patterns for auto-attachment. e.g. \`["**/*.ts", "src/components/**"]\` |
502
+ | \`alwaysApply\` | No | \`true\` = always included regardless of context. (default: \`false\`) |
503
+
504
+ **Priority**: Team Rules > Project Rules (\`.cursor/rules/\`) > User Rules (Cursor Settings) > \`AGENTS.md\`
505
+
506
+ **Best practices**: Keep rules under 500 lines. Reference files with \`@filename\` instead of copying. Be specific \u2014 avoid duplicating what linters or the agent already knows.
396
507
 
397
508
  ---
398
509
 
399
510
  ### Hooks
400
511
 
401
- Lifecycle shell commands (or LLM prompts) that fire at specific points. Hooks in skill/command frontmatter are scoped to that component \u2014 active while it runs, removed when it finishes.
512
+ Lifecycle scripts that fire at specific points in the agent loop. Both Claude Code and Cursor support hooks, but with different configuration formats and event models.
513
+
514
+ #### Claude Code Hooks
515
+
516
+ Defined in YAML frontmatter within skill/command files. Scoped to that component \u2014 active while it runs, removed when it finishes.
402
517
 
403
518
  \`\`\`yaml
404
519
  hooks:
405
520
  PreToolUse:
406
- - matcher: "Bash" # regex matched against tool name
521
+ - matcher: "Bash"
407
522
  hooks:
408
523
  - type: command
409
524
  command: ".claude/hooks/validate.sh"
410
- timeout: 30 # seconds (default: 600)
525
+ timeout: 30
411
526
  PostToolUse:
412
527
  - matcher: "Write|Edit"
413
528
  hooks:
414
529
  - type: command
415
530
  command: "npm run lint"
416
- async: true # run in background, non-blocking
531
+ async: true
417
532
  Stop:
418
533
  - hooks:
419
534
  - type: prompt
@@ -429,12 +544,10 @@ hooks:
429
544
  | \`PostToolUseFailure\` | tool name | No | After a tool fails |
430
545
  | \`UserPromptSubmit\` | \u2014 | Yes | When user submits a prompt |
431
546
  | \`SessionStart\` | \`startup\` \\| \`resume\` \\| \`clear\` \\| \`compact\` | No | Session begins |
432
- | \`Stop\` | \u2014 | Yes | Claude finishes responding |
547
+ | \`Stop\` | \u2014 | Yes | Agent finishes responding |
433
548
  | \`Notification\` | \`permission_prompt\` \\| \`idle_prompt\` | No | Notification fires |
434
549
  | \`SubagentStart\` / \`SubagentStop\` | agent type | No / Yes | Subagent spawned / finished |
435
550
 
436
- Full event list: [hooks reference](https://code.claude.com/docs/en/hooks)
437
-
438
551
  **Handler fields**
439
552
 
440
553
  | Field | Type | Description |
@@ -443,57 +556,134 @@ Full event list: [hooks reference](https://code.claude.com/docs/en/hooks)
443
556
  | \`command\` | command only | Shell command to execute. Receives hook JSON on stdin |
444
557
  | \`prompt\` | prompt/agent | Prompt text. Use \`$ARGUMENTS\` for the hook JSON input |
445
558
  | \`timeout\` | all | Seconds before cancel. Defaults: 600 / 30 / 60 |
446
- | \`async\` | command only | \`true\` = run in background, cannot block Claude |
559
+ | \`async\` | command only | \`true\` = run in background, cannot block |
447
560
  | \`once\` | command only | \`true\` = run once per session then remove (skills only) |
448
561
 
449
- **Decision control** (command hooks)
562
+ Full event list: [Claude Code hooks reference](https://code.claude.com/docs/en/hooks)
450
563
 
451
- Exit \`0\` = allow. Exit \`2\` = block (stderr is fed to Claude as the reason). Print JSON to stdout for richer control:
564
+ ---
452
565
 
453
- \`\`\`bash
454
- # PreToolUse: deny via JSON
455
- echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"reason"}}'
566
+ #### Cursor Hooks
567
+
568
+ Defined in \`hooks.json\` \u2014 not in frontmatter. Project-level (\`.cursor/hooks.json\`) or user-level (\`~/.cursor/hooks.json\`). Supports both command-based and prompt-based (LLM evaluation) handlers.
456
569
 
457
- # Stop/PostToolUse: block via JSON
458
- echo '{"decision":"block","reason":"Tests must pass first"}'
570
+ \`\`\`json
571
+ {
572
+ "version": 1,
573
+ "hooks": {
574
+ "afterFileEdit": [{ "command": ".cursor/hooks/format.sh" }],
575
+ "beforeShellExecution": [
576
+ { "command": ".cursor/hooks/approve.sh", "matcher": "curl|wget", "timeout": 30 }
577
+ ],
578
+ "stop": [{ "command": ".cursor/hooks/audit.sh", "loop_limit": 10 }],
579
+ "beforeReadFile": [{ "command": ".cursor/hooks/redact.sh", "failClosed": true }],
580
+ "preToolUse": [
581
+ { "type": "prompt", "prompt": "Is this tool call safe?", "matcher": "Shell" }
582
+ ]
583
+ }
584
+ }
459
585
  \`\`\`
460
586
 
461
- ## Usage
587
+ **Agent Events**
462
588
 
463
- \`\`\`bash
464
- # Scaffold this repo's directory structure
465
- set-prompt scaffold .
589
+ | Event | Matcher | Can Block | When it fires |
590
+ |-------|---------|:---------:|---------------|
591
+ | \`sessionStart\` | \u2014 | No | New conversation created |
592
+ | \`sessionEnd\` | \u2014 | No | Conversation ends |
593
+ | \`preToolUse\` | tool type | Yes | Before any tool runs |
594
+ | \`postToolUse\` | tool type | No | After a tool succeeds |
595
+ | \`postToolUseFailure\` | tool type | No | After a tool fails/times out |
596
+ | \`subagentStart\` | agent type | Yes | Before sub-agent spawns |
597
+ | \`subagentStop\` | agent type | Yes\\* | Sub-agent completes (\\*followup_message) |
598
+ | \`beforeShellExecution\` | command text | Yes | Before shell command runs |
599
+ | \`afterShellExecution\` | \u2014 | No | After shell command completes |
600
+ | \`beforeMCPExecution\` | \u2014 | Yes | Before MCP tool runs |
601
+ | \`afterMCPExecution\` | \u2014 | No | After MCP tool completes |
602
+ | \`beforeReadFile\` | tool type | Yes | Before file read |
603
+ | \`afterFileEdit\` | tool type | No | After file is edited |
604
+ | \`beforeSubmitPrompt\` | \u2014 | Yes | Before prompt sent to backend |
605
+ | \`preCompact\` | \u2014 | No | Before context compaction (observe only) |
606
+ | \`stop\` | \u2014 | Yes\\* | Agent loop ends (\\*followup_message) |
607
+ | \`afterAgentResponse\` | \u2014 | No | After assistant message |
608
+ | \`afterAgentThought\` | \u2014 | No | After thinking block |
466
609
 
467
- # Install from remote and link to AI tools
468
- set-prompt install https://github.com/you/my-prompts
469
- set-prompt link
610
+ **Tab Events** (inline autocomplete only)
470
611
 
471
- # Pull latest changes
472
- set-prompt update
612
+ | Event | When it fires |
613
+ |-------|---------------|
614
+ | \`beforeTabFileRead\` | Before Tab reads a file |
615
+ | \`afterTabFileEdit\` | After Tab edits a file |
616
+
617
+ **Handler fields**
618
+
619
+ | Field | Type | Default | Description |
620
+ |-------|------|---------|-------------|
621
+ | \`command\` | string | required | Script path or shell command |
622
+ | \`type\` | string | \`"command"\` | \`"command"\` or \`"prompt"\` (LLM evaluation) |
623
+ | \`timeout\` | number | platform default | Execution timeout in seconds |
624
+ | \`matcher\` | string | \u2014 | Regex filter for when the hook fires |
625
+ | \`loop_limit\` | number\\|null | \`5\` | Max auto-followups for stop/subagentStop. \`null\` = unlimited |
626
+ | \`failClosed\` | boolean | \`false\` | \`true\` = block on hook failure instead of fail-open |
627
+
628
+ Full event list: [Cursor hooks reference](https://cursor.com/docs/hooks)
629
+
630
+ ---
631
+
632
+ **Decision control** (both platforms)
633
+
634
+ Exit \`0\` = allow. Exit \`2\` = block. Claude Code reads stderr as the reason. Cursor uses JSON stdout:
635
+
636
+ \`\`\`bash
637
+ # Claude Code: deny via JSON
638
+ echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"reason"}}'
639
+
640
+ # Cursor: deny via JSON
641
+ echo '{"permission":"deny","user_message":"Blocked by policy","agent_message":"Not allowed"}'
473
642
  \`\`\`
643
+
474
644
  `;
475
645
 
476
646
  // src/commands/scaffold-command.ts
477
- var REQUIRED_DIRS = ["skills", "commands"];
478
- var OPTIONAL_DIRS = ["hooks", "agents"];
479
- var printStructure = async (localPath) => {
480
- let valid = true;
481
- for (const dir of REQUIRED_DIRS) {
482
- const exists = fs2.existsSync(path2.join(localPath, dir));
483
- if (exists) {
484
- console.log(`${TAB}\u2705 ${dir}/`);
485
- } else {
486
- console.log(`${TAB}\u274C ${dir}/ ${chalk2.red("(missing)")}`);
487
- valid = false;
488
- }
647
+ var ensureClaudePluginManifest = (repoPath) => {
648
+ const metaDir = path2.join(repoPath, ".claude-plugin");
649
+ const jsonPath = path2.join(metaDir, "plugin.json");
650
+ fs2.mkdirSync(metaDir, { recursive: true });
651
+ fs2.writeFileSync(jsonPath, JSON.stringify({
652
+ name: PLUGIN_NAME,
653
+ version: "1.0.0",
654
+ description: `Managed by set-prompt \u2014 ${repoPath}`
655
+ }, null, 4), { encoding: "utf-8" });
656
+ };
657
+ var ensureCodexPluginManifest = (repoPath) => {
658
+ const metaDir = path2.join(repoPath, ".codex-plugin");
659
+ const jsonPath = path2.join(metaDir, "plugin.json");
660
+ fs2.mkdirSync(metaDir, { recursive: true });
661
+ fs2.writeFileSync(jsonPath, JSON.stringify({
662
+ name: PLUGIN_NAME,
663
+ version: "1.0.0",
664
+ description: `Managed by set-prompt \u2014 ${repoPath}`,
665
+ skills: "./skills/",
666
+ mcpServers: "./mcp.json",
667
+ apps: "./.app.json"
668
+ }, null, 4), { encoding: "utf-8" });
669
+ };
670
+ var ensureMcpJson = (repoPath) => {
671
+ const mcpJsonPath = path2.join(repoPath, "mcp.json");
672
+ if (fs2.existsSync(mcpJsonPath)) {
673
+ return false;
489
674
  }
490
- for (const dir of OPTIONAL_DIRS) {
491
- const exists = fs2.existsSync(path2.join(localPath, dir));
492
- console.log(`${TAB}${chalk2.dim(exists ? "\u2713" : "\u25CB")} ${dir}/ ${exists ? "" : chalk2.dim("(optional)")}`);
675
+ fs2.writeFileSync(mcpJsonPath, JSON.stringify({ mcpServers: {} }, null, 4), { encoding: "utf-8" });
676
+ return true;
677
+ };
678
+ var ensureAppJson = (repoPath) => {
679
+ const appJsonPath = path2.join(repoPath, ".app.json");
680
+ if (fs2.existsSync(appJsonPath)) {
681
+ return false;
493
682
  }
494
- return valid;
683
+ fs2.writeFileSync(appJsonPath, JSON.stringify({ apps: {} }, null, 4), { encoding: "utf-8" });
684
+ return true;
495
685
  };
496
- var scaffoldCommand = async (localPath, options = {}) => {
686
+ var scaffoldCommand = async (localPath) => {
497
687
  try {
498
688
  let targetPath = null;
499
689
  if (localPath != null) {
@@ -502,82 +692,71 @@ var scaffoldCommand = async (localPath, options = {}) => {
502
692
  if (configManager.repo_path != null) {
503
693
  targetPath = configManager.repo_path;
504
694
  } else {
505
- console.error(chalk2.red("No path provided and no repo registered. Please provide a path."));
695
+ console.error(chalk3.red("No path provided and no repo registered. Please provide a path."));
506
696
  process.exit(1);
507
697
  }
508
698
  }
509
699
  if (fs2.existsSync(targetPath) === false || fs2.statSync(targetPath).isDirectory() === false) {
510
- console.error(chalk2.red(`Invalid directory path: '${targetPath}'`));
700
+ console.error(chalk3.red(`Invalid directory path: '${targetPath}'`));
511
701
  process.exit(1);
512
702
  }
513
- if (options.force !== true) {
514
- console.log(chalk2.dim(`Checking repo structure at: ${targetPath}`));
515
- }
516
- const valid = options.force === true ? false : await printStructure(targetPath);
517
- if (valid) {
518
- console.log(chalk2.green("Repo structure is valid."));
519
- return true;
520
- }
521
- if (!valid) {
522
- if (options.force !== true) {
523
- const proceed = await confirm({
524
- message: "Some directories are missing. Scaffold them now?",
525
- default: true
526
- });
527
- if (proceed === false) {
528
- console.log(chalk2.yellow("Scaffold skipped."));
529
- return false;
530
- }
531
- }
532
- const created = [];
703
+ console.log(chalk3.dim(`Scaffolding: ${targetPath}
704
+ `));
705
+ const writeGuide = await confirm({
706
+ message: "Generate SET_PROMPT_GUIDE.md? (reference doc for writing prompts)",
707
+ default: true
708
+ });
709
+ if (writeGuide) {
533
710
  const guideMdPath = path2.join(targetPath, "SET_PROMPT_GUIDE.md");
534
711
  fs2.writeFileSync(guideMdPath, SET_PROMPT_GUIDE, { encoding: "utf-8", flag: "w" });
535
- created.push(" SET_PROMPT_GUIDE.md");
536
- for (const dirName of PROMPT_DIR_NAMES) {
537
- const dirPath = path2.join(targetPath, dirName);
538
- if (fs2.existsSync(dirPath)) {
539
- console.warn(chalk2.yellow(`Directory already exists: '${dirName}' (skipping)`));
540
- } else {
541
- fs2.mkdirSync(dirPath, { recursive: true });
542
- created.push(` ${dirName}/`);
543
- }
544
- const gitkeepPath = path2.join(dirPath, ".gitkeep");
545
- if (!fs2.existsSync(gitkeepPath)) {
546
- fs2.writeFileSync(gitkeepPath, "", { encoding: "utf-8" });
547
- if (!created.includes(` ${dirName}/`)) {
548
- created.push(` ${dirName}/.gitkeep`);
549
- }
550
- }
712
+ console.log(`${TAB}${chalk3.green("\u2713")} SET_PROMPT_GUIDE.md`);
713
+ }
714
+ for (const dirName of PROMPT_DIR_NAMES) {
715
+ const dirPath = path2.join(targetPath, dirName);
716
+ if (fs2.existsSync(dirPath)) {
717
+ console.log(`${TAB}${chalk3.dim("\u2713")} ${dirName}/`);
718
+ } else {
719
+ fs2.mkdirSync(dirPath, { recursive: true });
720
+ console.log(`${TAB}${chalk3.green("+")} ${dirName}/`);
551
721
  }
552
- if (created.length > 0) {
553
- console.log(chalk2.green("Created:"));
554
- created.forEach((line) => console.log(line));
722
+ const gitkeepPath = path2.join(dirPath, ".gitkeep");
723
+ if (!fs2.existsSync(gitkeepPath)) {
724
+ fs2.writeFileSync(gitkeepPath, "", { encoding: "utf-8" });
555
725
  }
556
726
  }
727
+ ensureClaudePluginManifest(targetPath);
728
+ console.log(`${TAB}${chalk3.green("\u2713")} .claude-plugin/plugin.json`);
729
+ ensureCodexPluginManifest(targetPath);
730
+ console.log(`${TAB}${chalk3.green("\u2713")} .codex-plugin/plugin.json`);
731
+ console.log(`${TAB}${ensureMcpJson(targetPath) ? chalk3.green("+") : chalk3.dim("\u2713")} mcp.json`);
732
+ console.log(`${TAB}${ensureAppJson(targetPath) ? chalk3.green("+") : chalk3.dim("\u2713")} .app.json`);
733
+ console.log(chalk3.green("\nScaffold complete."));
557
734
  return true;
558
735
  } catch (ex) {
559
- console.error(chalk2.red(`Failed to scaffold repo structure: ${ex.message}`), ex);
736
+ console.error(chalk3.red(`Failed to scaffold repo structure: ${ex.message}`), ex);
560
737
  throw ex;
561
738
  }
562
739
  };
563
740
 
564
741
  // src/commands/install-command.ts
565
742
  var cloneRepo = async (remoteUrl) => {
566
- const proceed = await confirm2({
567
- message: `Clone and register "${remoteUrl}"?`,
568
- default: true
569
- });
570
- if (proceed == false) {
571
- console.log(chalk3.yellow("Cancelled."));
572
- return false;
573
- }
574
743
  const localPath = path3.join(HOME_DIR, "repo");
744
+ let backupPath = null;
575
745
  if (fs3.existsSync(localPath) == true) {
576
- console.warn(chalk3.yellow(`Existing repo found. Backing up before proceeding.`));
577
746
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
578
- const backupPath = path3.join(HOME_DIR, `repo.bak.${timestamp}`);
579
- fs3.renameSync(localPath, backupPath);
580
- console.log(chalk3.dim(` Backed up to: ${backupPath}`));
747
+ backupPath = path3.join(HOME_DIR, `repo.bak.${timestamp}`);
748
+ try {
749
+ fs3.renameSync(localPath, backupPath);
750
+ console.log(chalk4.yellow(" backed up") + chalk4.dim(` existing repo \u2192 ${backupPath}`));
751
+ } catch (ex) {
752
+ if (ex.code === "EPERM") {
753
+ console.error(chalk4.red("\u274C Cannot rename existing repo \u2014 it may be open in another process."));
754
+ console.log(chalk4.dim(` Close any editors or terminals using: ${localPath}`));
755
+ } else {
756
+ console.error(chalk4.red(`\u274C Failed to backup existing repo: ${ex.message}`));
757
+ }
758
+ return false;
759
+ }
581
760
  }
582
761
  fs3.mkdirSync(path3.dirname(localPath), { recursive: true });
583
762
  console.log(`Cloning ${remoteUrl}...`);
@@ -587,11 +766,15 @@ var cloneRepo = async (remoteUrl) => {
587
766
  process.exit(1);
588
767
  }
589
768
  console.log("\u2705 Cloned successfully.");
769
+ if (backupPath != null) {
770
+ fs3.rmSync(backupPath, { recursive: true, force: true });
771
+ console.log(chalk4.red(" removed") + chalk4.dim(` backup \u2192 ${backupPath}`));
772
+ }
590
773
  await scaffoldCommand(localPath, { force: true });
591
774
  configManager.repo_path = localPath;
592
775
  configManager.remote_url = remoteUrl;
593
776
  if (configManager.save() === false) {
594
- console.error(chalk3.red("Failed to save config."));
777
+ console.error(chalk4.red("Failed to save config."));
595
778
  return false;
596
779
  }
597
780
  return true;
@@ -599,97 +782,81 @@ var cloneRepo = async (remoteUrl) => {
599
782
  var installCommand = async (target) => {
600
783
  try {
601
784
  if (isGitUrl(target) === false) {
602
- console.error(chalk3.red("\u274C Only remote git URLs are supported."));
603
- console.log(chalk3.dim(" Example: set-prompt install https://github.com/you/my-prompts"));
785
+ console.error(chalk4.red("\u274C Only remote git URLs are supported."));
786
+ console.log(chalk4.dim(" Example: set-prompt install https://github.com/you/my-prompts"));
604
787
  process.exit(1);
605
788
  }
789
+ const normalizeUrl = (url) => url.replace(/\.git$/, "").toLowerCase();
790
+ if (configManager.repo_path != null) {
791
+ if (normalizeUrl(configManager.remote_url ?? "") === normalizeUrl(target)) {
792
+ console.error(chalk4.red(`\u274C Already installed from the same URL: ${target}`));
793
+ console.log(chalk4.dim(" Use `set-prompt update` to pull the latest changes."));
794
+ return false;
795
+ }
796
+ console.warn(chalk4.yellow(`\u26A0 Switching repo: ${configManager.remote_url} \u2192 ${target}`));
797
+ const proceed = await confirm2({ message: "Replace existing installation?", default: false });
798
+ if (!proceed) {
799
+ console.log(chalk4.yellow("Cancelled."));
800
+ return false;
801
+ }
802
+ } else {
803
+ const proceed = await confirm2({ message: `Clone and register "${target}"?`, default: true });
804
+ if (!proceed) {
805
+ console.log(chalk4.yellow("Cancelled."));
806
+ return false;
807
+ }
808
+ }
606
809
  return await cloneRepo(target);
607
810
  } catch (ex) {
608
- console.error(chalk3.red(`Unexpected error: ${ex.message}`), ex);
811
+ console.error(chalk4.red(`Unexpected error: ${ex.message}`), ex);
609
812
  process.exit(1);
610
813
  }
611
814
  };
612
815
 
613
816
  // src/commands/link-command.ts
817
+ import chalk11 from "chalk";
818
+ import { checkbox } from "@inquirer/prompts";
819
+
820
+ // src/link/claudecode.ts
614
821
  import path4 from "path";
615
822
  import fs4 from "fs";
616
823
  import os2 from "os";
617
- import chalk4 from "chalk";
618
- import { confirm as confirm3, checkbox } from "@inquirer/prompts";
619
- import { pathExists } from "fs-extra";
620
- var resolveRepoPath = () => {
621
- if (configManager.repo_path == null) {
622
- console.error(chalk4.red("\u274C No repo installed."));
623
- console.log(chalk4.yellow("Run: set-prompt install <git-url>"));
624
- return null;
625
- }
626
- return configManager.repo_path;
627
- };
628
- var PLUGIN_NAME = "set-prompt";
824
+ import chalk5 from "chalk";
825
+ import { confirm as confirm3 } from "@inquirer/prompts";
629
826
  var linkClaudeCode = async () => {
630
827
  const repoPath = resolveRepoPath();
631
828
  if (repoPath == null) {
632
829
  return;
633
830
  }
634
- const setClaudeCodeAssets = async () => {
831
+ const buildMarketplace = () => {
635
832
  try {
636
- fs4.mkdirSync(CLAUDE_CODE_DIR, { recursive: true });
637
833
  const marketplaceMetaDir = path4.join(CLAUDE_CODE_DIR, ".claude-plugin");
638
834
  fs4.mkdirSync(marketplaceMetaDir, { recursive: true });
639
835
  const marketplaceJson = {
640
- name: PLUGIN_NAME,
836
+ name: MARKET_NAME,
641
837
  owner: { name: os2.userInfo().username },
642
838
  metadata: { description: "Managed by set-prompt", version: "1.0.0" },
643
839
  plugins: [{ name: PLUGIN_NAME, source: `./plugins/${PLUGIN_NAME}`, description: "Managed by set-prompt" }]
644
840
  };
645
841
  fs4.writeFileSync(
646
842
  path4.join(marketplaceMetaDir, "marketplace.json"),
647
- JSON.stringify(marketplaceJson, null, 2),
648
- "utf-8"
649
- );
650
- console.log(chalk4.dim(" \u251C\u2500\u2500 .claude-plugin/"));
651
- console.log(chalk4.dim(" \u2502 \u2514\u2500\u2500 marketplace.json") + chalk4.green(" \u2713"));
652
- const pluginDir = path4.join(CLAUDE_CODE_DIR, "plugins", PLUGIN_NAME);
653
- fs4.mkdirSync(pluginDir, { recursive: true });
654
- console.log(chalk4.dim(" \u2514\u2500\u2500 plugins/"));
655
- console.log(chalk4.dim(` \u2514\u2500\u2500 ${PLUGIN_NAME}/`));
656
- const pluginMetaDir = path4.join(pluginDir, ".claude-plugin");
657
- fs4.mkdirSync(pluginMetaDir, { recursive: true });
658
- const pluginJson = {
659
- name: PLUGIN_NAME,
660
- version: "1.0.0",
661
- description: "Managed by set-prompt",
662
- author: { name: path4.basename(repoPath) }
663
- };
664
- fs4.writeFileSync(
665
- path4.join(pluginMetaDir, "plugin.json"),
666
- JSON.stringify(pluginJson, null, 2),
843
+ JSON.stringify(marketplaceJson, null, 4),
667
844
  "utf-8"
668
845
  );
669
- console.log(chalk4.dim(" \u251C\u2500\u2500 .claude-plugin/"));
670
- console.log(chalk4.dim(" \u2502 \u2514\u2500\u2500 plugin.json") + chalk4.green(" \u2713"));
671
- const linked = [];
672
- for (const dir of AGENT_PROMPT_DIRS["claudecode" /* CLAUDECODE */]) {
673
- const src = path4.join(repoPath, dir);
674
- const dest = path4.join(pluginDir, dir);
675
- if (await pathExists(src) === false) {
676
- continue;
677
- }
678
- if (fs4.existsSync(dest)) {
679
- fs4.rmSync(dest, { recursive: true, force: true });
680
- }
681
- const symlinkType = process.platform === "win32" ? "junction" : "dir";
682
- fs4.symlinkSync(src, dest, symlinkType);
683
- linked.push({ dir, src });
684
- }
685
- for (const { dir, src } of linked) {
686
- const isLast = linked[linked.length - 1].dir === dir;
687
- const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
688
- console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
846
+ console.log(chalk5.dim(" \u251C\u2500\u2500 .claude-plugin/"));
847
+ console.log(chalk5.dim(" \u2502 \u2514\u2500\u2500 marketplace.json") + chalk5.green(" \u2713"));
848
+ const pluginLink = path4.join(CLAUDE_CODE_DIR, "plugins", PLUGIN_NAME);
849
+ fs4.mkdirSync(path4.dirname(pluginLink), { recursive: true });
850
+ if (fs4.existsSync(pluginLink)) {
851
+ fs4.rmSync(pluginLink, { recursive: true, force: true });
689
852
  }
853
+ const symlinkType = process.platform === "win32" ? "junction" : "dir";
854
+ fs4.symlinkSync(repoPath, pluginLink, symlinkType);
855
+ console.log(chalk5.dim(" \u2514\u2500\u2500 plugins/"));
856
+ console.log(chalk5.dim(` \u2514\u2500\u2500 ${PLUGIN_NAME}/`) + chalk5.dim(` \u2192 ${repoPath}`) + chalk5.green(" \u2713"));
690
857
  return true;
691
858
  } catch (ex) {
692
- console.error(chalk4.red(`\u274C Failed to build plugin structure: ${ex.message}`));
859
+ console.error(chalk5.red(`\u274C Failed to build marketplace structure: ${ex.message}`));
693
860
  return false;
694
861
  }
695
862
  };
@@ -704,21 +871,21 @@ var linkClaudeCode = async () => {
704
871
  if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
705
872
  settings = parsed;
706
873
  } else {
707
- console.warn(chalk4.yellow(" \u26A0 settings.json has unexpected format \u2014 proceeding with caution"));
874
+ console.warn(chalk5.yellow(" \u26A0 settings.json has unexpected format \u2014 proceeding with caution"));
708
875
  }
709
876
  } catch {
710
- console.warn(chalk4.yellow(" \u26A0 Failed to parse settings.json \u2014 will not overwrite existing file"));
711
- console.error(chalk4.red("\u274C Could not register plugin. Please add manually."));
877
+ console.warn(chalk5.yellow(" \u26A0 Failed to parse settings.json \u2014 will not overwrite existing file"));
878
+ console.error(chalk5.red("\u274C Could not register plugin. Please add manually."));
712
879
  return false;
713
880
  }
714
881
  }
715
882
  settings.extraKnownMarketplaces = {
716
883
  ...settings.extraKnownMarketplaces,
717
- [PLUGIN_NAME]: { source: { source: "directory", path: CLAUDE_CODE_DIR } }
884
+ [MARKET_NAME]: { source: { source: "directory", path: CLAUDE_CODE_DIR } }
718
885
  };
719
886
  settings.enabledPlugins = {
720
887
  ...settings.enabledPlugins,
721
- [`${PLUGIN_NAME}@${PLUGIN_NAME}`]: true
888
+ [`${PLUGIN_NAME}@${MARKET_NAME}`]: true
722
889
  };
723
890
  let backupPath = null;
724
891
  if (fs4.existsSync(claudeSettingsPath)) {
@@ -727,21 +894,21 @@ var linkClaudeCode = async () => {
727
894
  try {
728
895
  fs4.copyFileSync(claudeSettingsPath, backupPath);
729
896
  } catch (ex) {
730
- console.warn(chalk4.yellow(` \u26A0 Could not create backup: ${ex.message}`));
897
+ console.warn(chalk5.yellow(` \u26A0 Could not create backup: ${ex.message}`));
731
898
  backupPath = null;
732
899
  }
733
900
  }
734
901
  try {
735
902
  fs4.mkdirSync(path4.dirname(claudeSettingsPath), { recursive: true });
736
- fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2), "utf-8");
903
+ fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 4), "utf-8");
737
904
  } catch (ex) {
738
905
  if (backupPath !== null) {
739
906
  try {
740
907
  fs4.copyFileSync(backupPath, claudeSettingsPath);
741
908
  fs4.unlinkSync(backupPath);
742
- console.warn(chalk4.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
909
+ console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
743
910
  } catch {
744
- console.error(chalk4.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
911
+ console.error(chalk5.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
745
912
  }
746
913
  }
747
914
  throw ex;
@@ -753,60 +920,209 @@ var linkClaudeCode = async () => {
753
920
  }
754
921
  }
755
922
  console.log(`\u2705 Registered to Claude Code settings.`);
756
- console.log(chalk4.dim(` ${claudeSettingsPath}`));
923
+ console.log(chalk5.dim(` ${claudeSettingsPath}`));
757
924
  return true;
758
925
  } catch (ex) {
759
- console.error(chalk4.red(`\u274C Failed to update settings.json: ${ex.message}`));
760
- console.log(chalk4.dim(" Please add the plugin manually via Claude Code /plugins."));
926
+ console.error(chalk5.red(`\u274C Failed to update settings.json: ${ex.message}`));
927
+ console.log(chalk5.dim(" Please add the plugin manually via Claude Code /plugins."));
761
928
  return false;
762
929
  }
763
930
  };
764
- console.log(chalk4.green(`
931
+ const patchInstalledPlugins = () => {
932
+ const installPath = repoPath;
933
+ const installedPluginsPath = path4.join(os2.homedir(), ".claude", "plugins", "installed_plugins.json");
934
+ const pluginKey = `${PLUGIN_NAME}@${MARKET_NAME}`;
935
+ try {
936
+ let data = { version: 2, plugins: {} };
937
+ if (fs4.existsSync(installedPluginsPath)) {
938
+ try {
939
+ data = JSON.parse(fs4.readFileSync(installedPluginsPath, "utf-8"));
940
+ } catch {
941
+ }
942
+ }
943
+ if (data.plugins == null) {
944
+ data.plugins = {};
945
+ }
946
+ data.plugins[pluginKey] = [
947
+ {
948
+ scope: "user",
949
+ installPath,
950
+ version: "1.0.0",
951
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
952
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
953
+ }
954
+ ];
955
+ fs4.mkdirSync(path4.dirname(installedPluginsPath), { recursive: true });
956
+ fs4.writeFileSync(installedPluginsPath, JSON.stringify(data, null, 4), "utf-8");
957
+ console.log(`\u2705 Patched installed_plugins.json \u2192 installPath points to source.`);
958
+ console.log(chalk5.dim(` ${installPath}`));
959
+ } catch (ex) {
960
+ console.warn(chalk5.yellow(` \u26A0 Could not patch installed_plugins.json: ${ex.message}`));
961
+ }
962
+ };
963
+ console.log(chalk5.green(`
765
964
  Setting up Claude Code plugin...`));
766
- console.log(chalk4.dim(CLAUDE_CODE_DIR));
767
- const structureOk = await setClaudeCodeAssets();
768
- if (structureOk === false) {
965
+ console.log(chalk5.dim(CLAUDE_CODE_DIR));
966
+ const marketplaceOk = buildMarketplace();
967
+ if (marketplaceOk === false) {
769
968
  return;
770
969
  }
771
970
  const settingsOk = registerToClaudeSettings();
772
971
  if (settingsOk === false) {
773
972
  return;
774
973
  }
974
+ patchInstalledPlugins();
775
975
  configManager.claude_code = { path: CLAUDE_CODE_DIR };
776
976
  configManager.save();
777
977
  };
978
+ var unlinkClaudeCode = async (force = false) => {
979
+ if (!force) {
980
+ const ok = await confirm3({
981
+ message: `Remove Claude Code plugin dir (${CLAUDE_CODE_DIR}) and settings entries?`,
982
+ default: false
983
+ });
984
+ if (!ok) {
985
+ console.log(chalk5.yellow("Cancelled."));
986
+ return;
987
+ }
988
+ }
989
+ console.log(chalk5.red(`
990
+ Removing Claude Code plugin...`));
991
+ console.log(chalk5.dim(CLAUDE_CODE_DIR));
992
+ const claudeSettingsPath = path4.join(os2.homedir(), ".claude", "settings.json");
993
+ if (fs4.existsSync(claudeSettingsPath)) {
994
+ try {
995
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
996
+ const backupPath = `${claudeSettingsPath}.bak.${timestamp}`;
997
+ fs4.copyFileSync(claudeSettingsPath, backupPath);
998
+ const settings = JSON.parse(fs4.readFileSync(claudeSettingsPath, "utf-8"));
999
+ if (settings?.extraKnownMarketplaces?.[MARKET_NAME] !== void 0) {
1000
+ delete settings.extraKnownMarketplaces[MARKET_NAME];
1001
+ }
1002
+ if (settings?.enabledPlugins?.[`${PLUGIN_NAME}@${MARKET_NAME}`] !== void 0) {
1003
+ delete settings.enabledPlugins[`${PLUGIN_NAME}@${MARKET_NAME}`];
1004
+ }
1005
+ try {
1006
+ fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 4), "utf-8");
1007
+ fs4.unlinkSync(backupPath);
1008
+ console.log(chalk5.red(" removed") + chalk5.dim(` set-prompt entries from: ${claudeSettingsPath}`));
1009
+ } catch (ex) {
1010
+ fs4.copyFileSync(backupPath, claudeSettingsPath);
1011
+ fs4.unlinkSync(backupPath);
1012
+ console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
1013
+ }
1014
+ } catch (ex) {
1015
+ console.error(chalk5.red(` \u274C Failed to clean up settings.json: ${ex.message}`));
1016
+ }
1017
+ }
1018
+ const claudePluginsDir = path4.join(os2.homedir(), ".claude", "plugins");
1019
+ const installedPluginsPath = path4.join(claudePluginsDir, "installed_plugins.json");
1020
+ if (fs4.existsSync(installedPluginsPath)) {
1021
+ try {
1022
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1023
+ const backupPath = `${installedPluginsPath}.bak.${timestamp}`;
1024
+ fs4.copyFileSync(installedPluginsPath, backupPath);
1025
+ const installed = JSON.parse(fs4.readFileSync(installedPluginsPath, "utf-8"));
1026
+ if (installed?.plugins && typeof installed.plugins === "object") {
1027
+ for (const key of Object.keys(installed.plugins)) {
1028
+ if (key.endsWith(`@${MARKET_NAME}`)) {
1029
+ delete installed.plugins[key];
1030
+ }
1031
+ }
1032
+ }
1033
+ try {
1034
+ fs4.writeFileSync(installedPluginsPath, JSON.stringify(installed, null, 4), "utf-8");
1035
+ fs4.unlinkSync(backupPath);
1036
+ console.log(chalk5.red(" removed") + chalk5.dim(` set-prompt entries from: ${installedPluginsPath}`));
1037
+ } catch (ex) {
1038
+ fs4.copyFileSync(backupPath, installedPluginsPath);
1039
+ fs4.unlinkSync(backupPath);
1040
+ console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back installed_plugins.json."));
1041
+ }
1042
+ } catch (ex) {
1043
+ console.error(chalk5.red(` \u274C Failed to clean up installed_plugins.json: ${ex.message}`));
1044
+ }
1045
+ }
1046
+ const knownMarketplacesPath = path4.join(claudePluginsDir, "known_marketplaces.json");
1047
+ if (fs4.existsSync(knownMarketplacesPath)) {
1048
+ try {
1049
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1050
+ const backupPath = `${knownMarketplacesPath}.bak.${timestamp}`;
1051
+ fs4.copyFileSync(knownMarketplacesPath, backupPath);
1052
+ const marketplaces = JSON.parse(fs4.readFileSync(knownMarketplacesPath, "utf-8"));
1053
+ if (marketplaces?.[MARKET_NAME] !== void 0) {
1054
+ delete marketplaces[MARKET_NAME];
1055
+ }
1056
+ try {
1057
+ fs4.writeFileSync(knownMarketplacesPath, JSON.stringify(marketplaces, null, 4), "utf-8");
1058
+ fs4.unlinkSync(backupPath);
1059
+ console.log(chalk5.red(" removed") + chalk5.dim(` set-prompt entry from: ${knownMarketplacesPath}`));
1060
+ } catch (ex) {
1061
+ fs4.copyFileSync(backupPath, knownMarketplacesPath);
1062
+ fs4.unlinkSync(backupPath);
1063
+ console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back known_marketplaces.json."));
1064
+ }
1065
+ } catch (ex) {
1066
+ console.error(chalk5.red(` \u274C Failed to clean up known_marketplaces.json: ${ex.message}`));
1067
+ }
1068
+ }
1069
+ if (fs4.existsSync(CLAUDE_CODE_DIR)) {
1070
+ fs4.rmSync(CLAUDE_CODE_DIR, { recursive: true, force: true });
1071
+ console.log(chalk5.red(" removed") + chalk5.dim(`: ${CLAUDE_CODE_DIR}`));
1072
+ }
1073
+ configManager.claude_code = null;
1074
+ configManager.save();
1075
+ };
1076
+
1077
+ // src/link/roocode.ts
1078
+ import path5 from "path";
1079
+ import fs5 from "fs";
1080
+ import chalk6 from "chalk";
1081
+ import { confirm as confirm4 } from "@inquirer/prompts";
1082
+ import { pathExists } from "fs-extra";
778
1083
  var linkRooCode = async () => {
779
1084
  const repoPath = resolveRepoPath();
780
1085
  if (repoPath == null) {
781
1086
  return;
782
1087
  }
783
- console.log(chalk4.green(`
1088
+ console.log(chalk6.green(`
784
1089
  Setting up RooCode integration...`));
785
- console.log(chalk4.dim(ROO_DIR));
1090
+ console.log(chalk6.dim(ROO_DIR));
786
1091
  const roocodeDirs = AGENT_PROMPT_DIRS["roocode" /* ROOCODE */];
787
1092
  const backupExistingRooCodeFiles = async () => {
788
1093
  try {
789
- fs4.mkdirSync(ROO_DIR, { recursive: true });
1094
+ fs5.mkdirSync(ROO_DIR, { recursive: true });
790
1095
  const dirsToBackup = [];
791
1096
  for (const dir of roocodeDirs) {
792
- const target = path4.join(ROO_DIR, dir);
793
- if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false) {
1097
+ const target = path5.join(ROO_DIR, dir);
1098
+ if (fs5.existsSync(target) && fs5.lstatSync(target).isSymbolicLink() === false && fs5.readdirSync(target).length > 0) {
794
1099
  dirsToBackup.push(dir);
795
1100
  }
796
1101
  }
797
1102
  if (dirsToBackup.length === 0) {
798
1103
  return true;
799
1104
  }
800
- fs4.mkdirSync(ROO_BACKUP_DIR, { recursive: true });
1105
+ console.log(chalk6.yellow(`
1106
+ \u26A0 The following existing directories will be replaced by symlinks:`));
1107
+ for (const dir of dirsToBackup) {
1108
+ console.log(chalk6.dim(` - ${path5.join(ROO_DIR, dir)}`));
1109
+ }
1110
+ console.log(chalk6.yellow(` They will be backed up to: `) + chalk6.dim(ROO_BACKUP_DIR));
1111
+ const ok = await confirm4({ message: "Back up existing directories?", default: true });
1112
+ if (!ok) {
1113
+ console.log(chalk6.yellow("Skipped RooCode linking."));
1114
+ return false;
1115
+ }
1116
+ fs5.mkdirSync(ROO_BACKUP_DIR, { recursive: true });
801
1117
  for (const dir of dirsToBackup) {
802
- const src = path4.join(ROO_DIR, dir);
803
- const dest = path4.join(ROO_BACKUP_DIR, dir);
804
- fs4.renameSync(src, dest);
805
- console.log(chalk4.dim(` backed up: ${dir}/ \u2192 ${dest}`));
1118
+ const src = path5.join(ROO_DIR, dir);
1119
+ const dest = path5.join(ROO_BACKUP_DIR, dir);
1120
+ fs5.renameSync(src, dest);
1121
+ console.log(chalk6.yellow(" backed up") + chalk6.dim(`: ${dir}/ \u2192 ${dest}`));
806
1122
  }
807
1123
  return true;
808
1124
  } catch (ex) {
809
- console.error(chalk4.red(`\u274C Failed to backup existing directories: ${ex.message}`));
1125
+ console.error(chalk6.red(`\u274C Failed to backup existing directories: ${ex.message}`));
810
1126
  return false;
811
1127
  }
812
1128
  };
@@ -814,26 +1130,26 @@ Setting up RooCode integration...`));
814
1130
  try {
815
1131
  const linked = [];
816
1132
  for (const dir of roocodeDirs) {
817
- const src = path4.join(repoPath, dir);
818
- const dest = path4.join(ROO_DIR, dir);
1133
+ const src = path5.join(repoPath, dir);
1134
+ const dest = path5.join(ROO_DIR, dir);
819
1135
  if (await pathExists(src) === false) {
820
1136
  continue;
821
1137
  }
822
- if (fs4.existsSync(dest)) {
823
- fs4.rmSync(dest, { recursive: true, force: true });
1138
+ if (fs5.existsSync(dest)) {
1139
+ fs5.rmSync(dest, { recursive: true, force: true });
824
1140
  }
825
1141
  const symlinkType = process.platform === "win32" ? "junction" : "dir";
826
- fs4.symlinkSync(src, dest, symlinkType);
1142
+ fs5.symlinkSync(src, dest, symlinkType);
827
1143
  linked.push({ dir, src });
828
1144
  }
829
1145
  for (const { dir, src } of linked) {
830
1146
  const isLast = linked[linked.length - 1].dir === dir;
831
1147
  const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
832
- console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
1148
+ console.log(chalk6.dim(` ${branch} `) + chalk6.bold(`${dir}/`) + chalk6.dim(` \u2192 ${src}`) + chalk6.green(" \u2713"));
833
1149
  }
834
1150
  return true;
835
1151
  } catch (ex) {
836
- console.error(chalk4.red(`\u274C Failed to create symlinks: ${ex.message}`));
1152
+ console.error(chalk6.red(`\u274C Failed to create symlinks: ${ex.message}`));
837
1153
  return false;
838
1154
  }
839
1155
  };
@@ -848,38 +1164,97 @@ Setting up RooCode integration...`));
848
1164
  configManager.roocode = { path: ROO_DIR, backup_path: ROO_BACKUP_DIR };
849
1165
  configManager.save();
850
1166
  };
851
- var linkOpenclaw = async () => {
852
- const repoPath = resolveRepoPath();
853
- if (repoPath == null) {
854
- return;
1167
+ var unlinkRooCode = async (force = false) => {
1168
+ if (!force) {
1169
+ const ok = await confirm4({
1170
+ message: `Remove RooCode symlinks from ${ROO_DIR}?`,
1171
+ default: false
1172
+ });
1173
+ if (!ok) {
1174
+ console.log(chalk6.yellow("Cancelled."));
1175
+ return;
1176
+ }
855
1177
  }
856
- console.log(chalk4.green(`
857
- Setting up OpenClaw integration...`));
858
- console.log(chalk4.dim(OPENCLAW_DIR));
859
- const openclawDirs = AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */];
860
- const backupExistingOpenclawFiles = async () => {
1178
+ console.log(chalk6.red(`
1179
+ Removing RooCode integration...`));
1180
+ console.log(chalk6.dim(ROO_DIR));
1181
+ const backupPath = configManager.roocode?.backup_path ?? ROO_BACKUP_DIR;
1182
+ for (const dir of AGENT_PROMPT_DIRS["roocode" /* ROOCODE */]) {
1183
+ const target = path5.join(ROO_DIR, dir);
1184
+ if (fs5.existsSync(target) && fs5.lstatSync(target).isSymbolicLink()) {
1185
+ fs5.unlinkSync(target);
1186
+ console.log(chalk6.red(" removed symlink") + chalk6.dim(`: ${target}`));
1187
+ }
1188
+ }
1189
+ if (fs5.existsSync(backupPath)) {
861
1190
  try {
862
- fs4.mkdirSync(OPENCLAW_DIR, { recursive: true });
863
- const dirsToBackup = [];
1191
+ for (const dir of AGENT_PROMPT_DIRS["roocode" /* ROOCODE */]) {
1192
+ const src = path5.join(backupPath, dir);
1193
+ const dest = path5.join(ROO_DIR, dir);
1194
+ if (!fs5.existsSync(src)) {
1195
+ continue;
1196
+ }
1197
+ fs5.renameSync(src, dest);
1198
+ console.log(chalk6.green(" restored") + chalk6.dim(`: ${dir}/`));
1199
+ }
1200
+ fs5.rmdirSync(backupPath);
1201
+ } catch (ex) {
1202
+ console.error(chalk6.red(` \u274C Failed to restore RooCode backup: ${ex.message}`));
1203
+ }
1204
+ }
1205
+ configManager.roocode = null;
1206
+ configManager.save();
1207
+ };
1208
+
1209
+ // src/link/openclaw.ts
1210
+ import path6 from "path";
1211
+ import fs6 from "fs";
1212
+ import chalk7 from "chalk";
1213
+ import { confirm as confirm5 } from "@inquirer/prompts";
1214
+ import { pathExists as pathExists2 } from "fs-extra";
1215
+ var linkOpenclaw = async () => {
1216
+ const repoPath = resolveRepoPath();
1217
+ if (repoPath == null) {
1218
+ return;
1219
+ }
1220
+ console.log(chalk7.green(`
1221
+ Setting up OpenClaw integration...`));
1222
+ console.log(chalk7.dim(OPENCLAW_DIR));
1223
+ const openclawDirs = AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */];
1224
+ const backupExistingOpenclawFiles = async () => {
1225
+ try {
1226
+ fs6.mkdirSync(OPENCLAW_DIR, { recursive: true });
1227
+ const dirsToBackup = [];
864
1228
  for (const dir of openclawDirs) {
865
- const target = path4.join(OPENCLAW_DIR, dir);
866
- if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false) {
1229
+ const target = path6.join(OPENCLAW_DIR, dir);
1230
+ if (fs6.existsSync(target) && fs6.lstatSync(target).isSymbolicLink() === false && fs6.readdirSync(target).length > 0) {
867
1231
  dirsToBackup.push(dir);
868
1232
  }
869
1233
  }
870
1234
  if (dirsToBackup.length === 0) {
871
1235
  return true;
872
1236
  }
873
- fs4.mkdirSync(OPENCLAW_BACKUP_DIR, { recursive: true });
1237
+ console.log(chalk7.yellow(`
1238
+ \u26A0 The following existing directories will be replaced by symlinks:`));
874
1239
  for (const dir of dirsToBackup) {
875
- const src = path4.join(OPENCLAW_DIR, dir);
876
- const dest = path4.join(OPENCLAW_BACKUP_DIR, dir);
877
- fs4.renameSync(src, dest);
878
- console.log(chalk4.dim(` backed up: ${dir}/ \u2192 ${dest}`));
1240
+ console.log(chalk7.dim(` - ${path6.join(OPENCLAW_DIR, dir)}`));
1241
+ }
1242
+ console.log(chalk7.yellow(` They will be backed up to: `) + chalk7.dim(OPENCLAW_BACKUP_DIR));
1243
+ const ok = await confirm5({ message: "Back up existing directories?", default: true });
1244
+ if (!ok) {
1245
+ console.log(chalk7.yellow("Skipped OpenClaw linking."));
1246
+ return false;
1247
+ }
1248
+ fs6.mkdirSync(OPENCLAW_BACKUP_DIR, { recursive: true });
1249
+ for (const dir of dirsToBackup) {
1250
+ const src = path6.join(OPENCLAW_DIR, dir);
1251
+ const dest = path6.join(OPENCLAW_BACKUP_DIR, dir);
1252
+ fs6.renameSync(src, dest);
1253
+ console.log(chalk7.yellow(" backed up") + chalk7.dim(`: ${dir}/ \u2192 ${dest}`));
879
1254
  }
880
1255
  return true;
881
1256
  } catch (ex) {
882
- console.error(chalk4.red(`\u274C Failed to backup existing directories: ${ex.message}`));
1257
+ console.error(chalk7.red(`\u274C Failed to backup existing directories: ${ex.message}`));
883
1258
  return false;
884
1259
  }
885
1260
  };
@@ -887,26 +1262,26 @@ Setting up OpenClaw integration...`));
887
1262
  try {
888
1263
  const linked = [];
889
1264
  for (const dir of openclawDirs) {
890
- const src = path4.join(repoPath, dir);
891
- const dest = path4.join(OPENCLAW_DIR, dir);
892
- if (await pathExists(src) === false) {
1265
+ const src = path6.join(repoPath, dir);
1266
+ const dest = path6.join(OPENCLAW_DIR, dir);
1267
+ if (await pathExists2(src) === false) {
893
1268
  continue;
894
1269
  }
895
- if (fs4.existsSync(dest)) {
896
- fs4.rmSync(dest, { recursive: true, force: true });
1270
+ if (fs6.existsSync(dest)) {
1271
+ fs6.rmSync(dest, { recursive: true, force: true });
897
1272
  }
898
1273
  const symlinkType = process.platform === "win32" ? "junction" : "dir";
899
- fs4.symlinkSync(src, dest, symlinkType);
1274
+ fs6.symlinkSync(src, dest, symlinkType);
900
1275
  linked.push({ dir, src });
901
1276
  }
902
1277
  for (const { dir, src } of linked) {
903
1278
  const isLast = linked[linked.length - 1].dir === dir;
904
1279
  const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
905
- console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
1280
+ console.log(chalk7.dim(` ${branch} `) + chalk7.bold(`${dir}/`) + chalk7.dim(` \u2192 ${src}`) + chalk7.green(" \u2713"));
906
1281
  }
907
1282
  return true;
908
1283
  } catch (ex) {
909
- console.error(chalk4.red(`\u274C Failed to create symlinks: ${ex.message}`));
1284
+ console.error(chalk7.red(`\u274C Failed to create symlinks: ${ex.message}`));
910
1285
  return false;
911
1286
  }
912
1287
  };
@@ -921,261 +1296,544 @@ Setting up OpenClaw integration...`));
921
1296
  configManager.openclaw = { path: OPENCLAW_DIR, backup_path: OPENCLAW_BACKUP_DIR };
922
1297
  configManager.save();
923
1298
  };
924
- var linkCodex = async () => {
925
- if (resolveRepoPath() == null) {
926
- return;
927
- }
928
- console.log(chalk4.yellow("Codex integration is not yet implemented."));
929
- };
930
- var unlinkClaudeCode = async (force = false) => {
931
- if (!force) {
932
- const ok = await confirm3({
933
- message: `Remove Claude Code plugin dir (${CLAUDE_CODE_DIR}) and settings entries?`,
934
- default: false
935
- });
936
- if (!ok) {
937
- console.log(chalk4.yellow("Cancelled."));
938
- return;
939
- }
940
- }
941
- const claudeSettingsPath = path4.join(os2.homedir(), ".claude", "settings.json");
942
- if (fs4.existsSync(claudeSettingsPath)) {
943
- try {
944
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
945
- const backupPath = `${claudeSettingsPath}.bak.${timestamp}`;
946
- fs4.copyFileSync(claudeSettingsPath, backupPath);
947
- const settings = JSON.parse(fs4.readFileSync(claudeSettingsPath, "utf-8"));
948
- if (settings?.extraKnownMarketplaces?.[PLUGIN_NAME] !== void 0) {
949
- delete settings.extraKnownMarketplaces[PLUGIN_NAME];
950
- }
951
- if (settings?.enabledPlugins?.[`${PLUGIN_NAME}@${PLUGIN_NAME}`] !== void 0) {
952
- delete settings.enabledPlugins[`${PLUGIN_NAME}@${PLUGIN_NAME}`];
953
- }
954
- try {
955
- fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2), "utf-8");
956
- fs4.unlinkSync(backupPath);
957
- console.log(chalk4.dim(` removed set-prompt entries from: ${claudeSettingsPath}`));
958
- } catch (ex) {
959
- fs4.copyFileSync(backupPath, claudeSettingsPath);
960
- fs4.unlinkSync(backupPath);
961
- console.warn(chalk4.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
962
- }
963
- } catch (ex) {
964
- console.error(chalk4.red(` \u274C Failed to clean up settings.json: ${ex.message}`));
965
- }
966
- }
967
- if (fs4.existsSync(CLAUDE_CODE_DIR)) {
968
- fs4.rmSync(CLAUDE_CODE_DIR, { recursive: true, force: true });
969
- console.log(chalk4.dim(` removed: ${CLAUDE_CODE_DIR}`));
970
- }
971
- configManager.claude_code = null;
972
- configManager.save();
973
- };
974
- var unlinkRooCode = async (force = false) => {
1299
+ var unlinkOpenclaw = async (force = false) => {
975
1300
  if (!force) {
976
- const ok = await confirm3({
977
- message: `Remove RooCode symlinks from ${ROO_DIR}?`,
1301
+ const ok = await confirm5({
1302
+ message: `Remove OpenClaw symlinks from ${OPENCLAW_DIR}?`,
978
1303
  default: false
979
1304
  });
980
1305
  if (!ok) {
981
- console.log(chalk4.yellow("Cancelled."));
1306
+ console.log(chalk7.yellow("Cancelled."));
982
1307
  return;
983
1308
  }
984
1309
  }
985
- const backupPath = configManager.roocode?.backup_path ?? ROO_BACKUP_DIR;
986
- for (const dir of AGENT_PROMPT_DIRS["roocode" /* ROOCODE */]) {
987
- const target = path4.join(ROO_DIR, dir);
988
- if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink()) {
989
- fs4.unlinkSync(target);
990
- console.log(chalk4.dim(` removed symlink: ${target}`));
1310
+ console.log(chalk7.red(`
1311
+ Removing OpenClaw integration...`));
1312
+ console.log(chalk7.dim(OPENCLAW_DIR));
1313
+ const backupPath = configManager.openclaw?.backup_path ?? OPENCLAW_BACKUP_DIR;
1314
+ for (const dir of AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */]) {
1315
+ const target = path6.join(OPENCLAW_DIR, dir);
1316
+ if (fs6.existsSync(target) && fs6.lstatSync(target).isSymbolicLink()) {
1317
+ fs6.unlinkSync(target);
1318
+ console.log(chalk7.red(" removed symlink") + chalk7.dim(`: ${target}`));
991
1319
  }
992
1320
  }
993
- if (fs4.existsSync(backupPath)) {
1321
+ if (fs6.existsSync(backupPath)) {
994
1322
  try {
995
- for (const dir of AGENT_PROMPT_DIRS["roocode" /* ROOCODE */]) {
996
- const src = path4.join(backupPath, dir);
997
- const dest = path4.join(ROO_DIR, dir);
998
- if (!fs4.existsSync(src)) {
1323
+ for (const dir of AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */]) {
1324
+ const src = path6.join(backupPath, dir);
1325
+ const dest = path6.join(OPENCLAW_DIR, dir);
1326
+ if (!fs6.existsSync(src)) {
999
1327
  continue;
1000
1328
  }
1001
- fs4.renameSync(src, dest);
1002
- console.log(chalk4.dim(` restored: ${dir}/`));
1329
+ fs6.renameSync(src, dest);
1330
+ console.log(chalk7.green(" restored") + chalk7.dim(`: ${dir}/`));
1003
1331
  }
1004
- fs4.rmdirSync(backupPath);
1332
+ fs6.rmdirSync(backupPath);
1005
1333
  } catch (ex) {
1006
- console.error(chalk4.red(` \u274C Failed to restore RooCode backup: ${ex.message}`));
1334
+ console.error(chalk7.red(` \u274C Failed to restore OpenClaw backup: ${ex.message}`));
1007
1335
  }
1008
1336
  }
1009
- configManager.roocode = null;
1337
+ configManager.openclaw = null;
1010
1338
  configManager.save();
1011
1339
  };
1012
- var unlinkOpenclaw = async (force = false) => {
1013
- if (!force) {
1014
- const ok = await confirm3({
1015
- message: `Remove OpenClaw symlinks from ${OPENCLAW_DIR}?`,
1016
- default: false
1017
- });
1018
- if (!ok) {
1019
- console.log(chalk4.yellow("Cancelled."));
1020
- return;
1021
- }
1340
+
1341
+ // src/link/antigravity.ts
1342
+ import path7 from "path";
1343
+ import fs7 from "fs";
1344
+ import chalk8 from "chalk";
1345
+ import { confirm as confirm6 } from "@inquirer/prompts";
1346
+ import { pathExists as pathExists3 } from "fs-extra";
1347
+ var linkAntigravity = async () => {
1348
+ const repoPath = resolveRepoPath();
1349
+ if (repoPath == null) {
1350
+ return;
1022
1351
  }
1023
- const backupPath = configManager.openclaw?.backup_path ?? OPENCLAW_BACKUP_DIR;
1024
- for (const dir of AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */]) {
1025
- const target = path4.join(OPENCLAW_DIR, dir);
1026
- if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink()) {
1027
- fs4.unlinkSync(target);
1028
- console.log(chalk4.dim(` removed symlink: ${target}`));
1352
+ console.log(chalk8.green(`
1353
+ Setting up Antigravity integration...`));
1354
+ console.log(chalk8.dim(ANTIGRAVITY_DIR));
1355
+ const antigravityDirs = AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */];
1356
+ const backupExistingAntigravityFiles = async () => {
1357
+ try {
1358
+ fs7.mkdirSync(ANTIGRAVITY_DIR, { recursive: true });
1359
+ const dirsToBackup = [];
1360
+ for (const dir of antigravityDirs) {
1361
+ const target = path7.join(ANTIGRAVITY_DIR, dir);
1362
+ if (fs7.existsSync(target) && fs7.lstatSync(target).isSymbolicLink() === false && fs7.readdirSync(target).length > 0) {
1363
+ dirsToBackup.push(dir);
1364
+ }
1365
+ }
1366
+ if (dirsToBackup.length === 0) {
1367
+ return true;
1368
+ }
1369
+ console.log(chalk8.yellow(`
1370
+ \u26A0 The following existing directories will be replaced by symlinks:`));
1371
+ for (const dir of dirsToBackup) {
1372
+ console.log(chalk8.dim(` - ${path7.join(ANTIGRAVITY_DIR, dir)}`));
1373
+ }
1374
+ console.log(chalk8.yellow(` They will be backed up to: `) + chalk8.dim(ANTIGRAVITY_BACKUP_DIR));
1375
+ const ok = await confirm6({ message: "Back up existing directories?", default: true });
1376
+ if (!ok) {
1377
+ console.log(chalk8.yellow("Skipped Antigravity linking."));
1378
+ return false;
1379
+ }
1380
+ fs7.mkdirSync(ANTIGRAVITY_BACKUP_DIR, { recursive: true });
1381
+ for (const dir of antigravityDirs) {
1382
+ const src = path7.join(ANTIGRAVITY_DIR, dir);
1383
+ const dest = path7.join(ANTIGRAVITY_BACKUP_DIR, dir);
1384
+ fs7.renameSync(src, dest);
1385
+ console.log(chalk8.yellow(" backed up") + chalk8.dim(`: ${dir}/ \u2192 ${dest}`));
1386
+ }
1387
+ return true;
1388
+ } catch (ex) {
1389
+ console.error(chalk8.red(`\u274C Failed to backup existing directories: ${ex.message}`));
1390
+ return false;
1029
1391
  }
1030
- }
1031
- if (fs4.existsSync(backupPath)) {
1392
+ };
1393
+ const setAntigravityAssets = async () => {
1032
1394
  try {
1033
- for (const dir of AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */]) {
1034
- const src = path4.join(backupPath, dir);
1035
- const dest = path4.join(OPENCLAW_DIR, dir);
1036
- if (!fs4.existsSync(src)) {
1395
+ const linked = [];
1396
+ for (const dir of antigravityDirs) {
1397
+ const src = path7.join(repoPath, dir);
1398
+ const dest = path7.join(ANTIGRAVITY_DIR, dir);
1399
+ if (await pathExists3(src) === false) {
1037
1400
  continue;
1038
1401
  }
1039
- fs4.renameSync(src, dest);
1040
- console.log(chalk4.dim(` restored: ${dir}/`));
1402
+ if (fs7.existsSync(dest)) {
1403
+ fs7.rmSync(dest, { recursive: true, force: true });
1404
+ }
1405
+ const symlinkType = process.platform === "win32" ? "junction" : "dir";
1406
+ fs7.symlinkSync(src, dest, symlinkType);
1407
+ linked.push({ dir, src });
1041
1408
  }
1042
- fs4.rmdirSync(backupPath);
1409
+ for (const { dir, src } of linked) {
1410
+ const isLast = linked[linked.length - 1].dir === dir;
1411
+ const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
1412
+ console.log(chalk8.dim(` ${branch} `) + chalk8.bold(`${dir}/`) + chalk8.dim(` \u2192 ${src}`) + chalk8.green(" \u2713"));
1413
+ }
1414
+ return true;
1043
1415
  } catch (ex) {
1044
- console.error(chalk4.red(` \u274C Failed to restore OpenClaw backup: ${ex.message}`));
1416
+ console.error(chalk8.red(`\u274C Failed to create symlinks: ${ex.message}`));
1417
+ return false;
1045
1418
  }
1419
+ };
1420
+ const backupOk = await backupExistingAntigravityFiles();
1421
+ if (backupOk === false) {
1422
+ return;
1046
1423
  }
1047
- configManager.openclaw = null;
1424
+ const linkOk = await setAntigravityAssets();
1425
+ if (linkOk === false) {
1426
+ return;
1427
+ }
1428
+ configManager.antigravity = { path: ANTIGRAVITY_DIR, backup_path: ANTIGRAVITY_BACKUP_DIR };
1048
1429
  configManager.save();
1049
1430
  };
1050
1431
  var unlinkAntigravity = async (force = false) => {
1051
1432
  if (!force) {
1052
- const ok = await confirm3({
1433
+ const ok = await confirm6({
1053
1434
  message: `Remove Antigravity symlinks from ${ANTIGRAVITY_DIR}?`,
1054
1435
  default: false
1055
1436
  });
1056
1437
  if (!ok) {
1057
- console.log(chalk4.yellow("Cancelled."));
1438
+ console.log(chalk8.yellow("Cancelled."));
1058
1439
  return;
1059
1440
  }
1060
1441
  }
1442
+ console.log(chalk8.red(`
1443
+ Removing Antigravity integration...`));
1444
+ console.log(chalk8.dim(ANTIGRAVITY_DIR));
1061
1445
  const backupPath = configManager.antigravity?.backup_path ?? ANTIGRAVITY_BACKUP_DIR;
1062
1446
  for (const dir of AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */]) {
1063
- const target = path4.join(ANTIGRAVITY_DIR, dir);
1064
- if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink()) {
1065
- fs4.unlinkSync(target);
1066
- console.log(chalk4.dim(` removed symlink: ${target}`));
1447
+ const target = path7.join(ANTIGRAVITY_DIR, dir);
1448
+ if (fs7.existsSync(target) && fs7.lstatSync(target).isSymbolicLink()) {
1449
+ fs7.unlinkSync(target);
1450
+ console.log(chalk8.red(" removed symlink") + chalk8.dim(`: ${target}`));
1067
1451
  }
1068
1452
  }
1069
- if (fs4.existsSync(backupPath)) {
1453
+ if (fs7.existsSync(backupPath)) {
1070
1454
  try {
1071
1455
  for (const dir of AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */]) {
1072
- const src = path4.join(backupPath, dir);
1073
- const dest = path4.join(ANTIGRAVITY_DIR, dir);
1074
- if (!fs4.existsSync(src)) {
1456
+ const src = path7.join(backupPath, dir);
1457
+ const dest = path7.join(ANTIGRAVITY_DIR, dir);
1458
+ if (!fs7.existsSync(src)) {
1075
1459
  continue;
1076
1460
  }
1077
- fs4.renameSync(src, dest);
1078
- console.log(chalk4.dim(` restored: ${dir}/`));
1461
+ fs7.renameSync(src, dest);
1462
+ console.log(chalk8.green(" restored") + chalk8.dim(`: ${dir}/`));
1079
1463
  }
1080
- fs4.rmdirSync(backupPath);
1464
+ fs7.rmdirSync(backupPath);
1081
1465
  } catch (ex) {
1082
- console.error(chalk4.red(` \u274C Failed to restore Antigravity backup: ${ex.message}`));
1466
+ console.error(chalk8.red(` \u274C Failed to restore Antigravity backup: ${ex.message}`));
1083
1467
  }
1084
1468
  }
1085
1469
  configManager.antigravity = null;
1086
1470
  configManager.save();
1087
1471
  };
1088
- var linkAntigravity = async () => {
1472
+
1473
+ // src/link/codex.ts
1474
+ import path8 from "path";
1475
+ import fs8 from "fs";
1476
+ import os3 from "os";
1477
+ import chalk9 from "chalk";
1478
+ import { confirm as confirm7 } from "@inquirer/prompts";
1479
+ import TOML from "smol-toml";
1480
+ var CODEX_AGENTS_DIR = path8.join(os3.homedir(), ".agents", "plugins");
1481
+ var CODEX_CACHE_DIR = path8.join(os3.homedir(), ".codex", "plugins", "cache");
1482
+ var CODEX_MARKETPLACE_NAME = "local-repo";
1483
+ var linkCodex = async () => {
1089
1484
  const repoPath = resolveRepoPath();
1090
1485
  if (repoPath == null) {
1091
1486
  return;
1092
1487
  }
1093
- console.log(chalk4.green(`
1094
- Setting up Antigravity integration...`));
1095
- console.log(chalk4.dim(ANTIGRAVITY_DIR));
1096
- const antigravityDirs = AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */];
1097
- const backupExistingAntigravityFiles = async () => {
1488
+ ensureCodexPluginManifest(repoPath);
1489
+ ensureMcpJson(repoPath);
1490
+ ensureAppJson(repoPath);
1491
+ const registerToMarketplace = () => {
1492
+ const marketplacePath = path8.join(CODEX_AGENTS_DIR, "marketplace.json");
1098
1493
  try {
1099
- fs4.mkdirSync(ANTIGRAVITY_DIR, { recursive: true });
1100
- const dirsToBackup = [];
1101
- for (const dir of antigravityDirs) {
1102
- const target = path4.join(ANTIGRAVITY_DIR, dir);
1103
- if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false) {
1104
- dirsToBackup.push(dir);
1494
+ let marketplace = {
1495
+ name: CODEX_MARKETPLACE_NAME,
1496
+ interface: { displayName: "Local Repository" },
1497
+ plugins: []
1498
+ };
1499
+ if (fs8.existsSync(marketplacePath)) {
1500
+ const raw = fs8.readFileSync(marketplacePath, "utf-8");
1501
+ try {
1502
+ const parsed = JSON.parse(raw);
1503
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
1504
+ marketplace = parsed;
1505
+ }
1506
+ } catch {
1507
+ console.warn(chalk9.yellow(" \u26A0 Failed to parse marketplace.json \u2014 will not overwrite existing file"));
1508
+ console.error(chalk9.red("\u274C Could not register plugin. Please add manually."));
1509
+ return false;
1105
1510
  }
1106
1511
  }
1107
- if (dirsToBackup.length === 0) {
1108
- return true;
1512
+ if (!Array.isArray(marketplace.plugins)) {
1513
+ marketplace.plugins = [];
1109
1514
  }
1110
- fs4.mkdirSync(ANTIGRAVITY_BACKUP_DIR, { recursive: true });
1111
- for (const dir of dirsToBackup) {
1112
- const src = path4.join(ANTIGRAVITY_DIR, dir);
1113
- const dest = path4.join(ANTIGRAVITY_BACKUP_DIR, dir);
1114
- fs4.renameSync(src, dest);
1115
- console.log(chalk4.dim(` backed up: ${dir}/ \u2192 ${dest}`));
1515
+ marketplace.plugins = marketplace.plugins.filter(
1516
+ (p) => p?.name !== PLUGIN_NAME
1517
+ );
1518
+ const relRepoPath = `./${path8.relative(os3.homedir(), repoPath).replace(/\\/g, "/")}`;
1519
+ marketplace.plugins.push({
1520
+ name: PLUGIN_NAME,
1521
+ source: {
1522
+ source: "local",
1523
+ path: relRepoPath
1524
+ },
1525
+ policy: {
1526
+ installation: "AVAILABLE"
1527
+ },
1528
+ category: "Productivity"
1529
+ });
1530
+ let backupPath = null;
1531
+ if (fs8.existsSync(marketplacePath)) {
1532
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1533
+ backupPath = `${marketplacePath}.bak.${timestamp}`;
1534
+ try {
1535
+ fs8.copyFileSync(marketplacePath, backupPath);
1536
+ } catch (ex) {
1537
+ console.warn(chalk9.yellow(` \u26A0 Could not create backup: ${ex.message}`));
1538
+ backupPath = null;
1539
+ }
1540
+ }
1541
+ try {
1542
+ fs8.mkdirSync(path8.dirname(marketplacePath), { recursive: true });
1543
+ fs8.writeFileSync(marketplacePath, JSON.stringify(marketplace, null, 4), "utf-8");
1544
+ } catch (ex) {
1545
+ if (backupPath !== null) {
1546
+ try {
1547
+ fs8.copyFileSync(backupPath, marketplacePath);
1548
+ fs8.unlinkSync(backupPath);
1549
+ console.warn(chalk9.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
1550
+ } catch {
1551
+ console.error(chalk9.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
1552
+ }
1553
+ }
1554
+ throw ex;
1116
1555
  }
1556
+ if (backupPath !== null) {
1557
+ try {
1558
+ fs8.unlinkSync(backupPath);
1559
+ } catch {
1560
+ }
1561
+ }
1562
+ console.log(`\u2705 Registered to marketplace.json`);
1563
+ console.log(chalk9.dim(` ${marketplacePath}`));
1117
1564
  return true;
1118
1565
  } catch (ex) {
1119
- console.error(chalk4.red(`\u274C Failed to backup existing directories: ${ex.message}`));
1566
+ console.error(chalk9.red(`\u274C Failed to update marketplace.json: ${ex.message}`));
1120
1567
  return false;
1121
1568
  }
1122
1569
  };
1123
- const setAntigravityAssets = async () => {
1570
+ const patchPluginCache = () => {
1571
+ const cachePath = path8.join(CODEX_CACHE_DIR, CODEX_MARKETPLACE_NAME, PLUGIN_NAME, "1.0.0");
1124
1572
  try {
1125
- const linked = [];
1126
- for (const dir of antigravityDirs) {
1127
- const src = path4.join(repoPath, dir);
1128
- const dest = path4.join(ANTIGRAVITY_DIR, dir);
1129
- if (await pathExists(src) === false) {
1130
- continue;
1131
- }
1132
- if (fs4.existsSync(dest)) {
1133
- fs4.rmSync(dest, { recursive: true, force: true });
1134
- }
1135
- const symlinkType = process.platform === "win32" ? "junction" : "dir";
1136
- fs4.symlinkSync(src, dest, symlinkType);
1137
- linked.push({ dir, src });
1573
+ fs8.mkdirSync(path8.dirname(cachePath), { recursive: true });
1574
+ if (fs8.existsSync(cachePath)) {
1575
+ fs8.rmSync(cachePath, { recursive: true, force: true });
1138
1576
  }
1139
- for (const { dir, src } of linked) {
1140
- const isLast = linked[linked.length - 1].dir === dir;
1141
- const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
1142
- console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
1577
+ const symlinkType = process.platform === "win32" ? "junction" : "dir";
1578
+ fs8.symlinkSync(repoPath, cachePath, symlinkType);
1579
+ console.log(`\u2705 Patched plugin cache.`);
1580
+ console.log(chalk9.dim(` ${cachePath}`) + chalk9.dim(" \u2192 ") + chalk9.dim(repoPath));
1581
+ } catch (ex) {
1582
+ console.warn(chalk9.yellow(` \u26A0 Could not patch plugin cache: ${ex.message}`));
1583
+ }
1584
+ };
1585
+ const enableInConfig = () => {
1586
+ const configPath = path8.join(os3.homedir(), ".codex", "config.toml");
1587
+ const pluginKey = `${PLUGIN_NAME}@${CODEX_MARKETPLACE_NAME}`;
1588
+ try {
1589
+ let config = {};
1590
+ if (fs8.existsSync(configPath)) {
1591
+ config = TOML.parse(fs8.readFileSync(configPath, "utf-8"));
1143
1592
  }
1144
- return true;
1593
+ if (config.plugins == null) {
1594
+ config.plugins = {};
1595
+ }
1596
+ config.plugins[pluginKey] = { enabled: true };
1597
+ fs8.mkdirSync(path8.dirname(configPath), { recursive: true });
1598
+ fs8.writeFileSync(configPath, TOML.stringify(config), "utf-8");
1599
+ console.log(`\u2705 Enabled plugin in config.toml`);
1600
+ console.log(chalk9.dim(` ${configPath}`));
1145
1601
  } catch (ex) {
1146
- console.error(chalk4.red(`\u274C Failed to create symlinks: ${ex.message}`));
1147
- return false;
1602
+ console.warn(chalk9.yellow(` \u26A0 Could not update config.toml: ${ex.message}`));
1148
1603
  }
1149
1604
  };
1150
- const backupOk = await backupExistingAntigravityFiles();
1151
- if (backupOk === false) {
1605
+ console.log(chalk9.green(`
1606
+ Setting up Codex plugin...`));
1607
+ const marketplaceOk = registerToMarketplace();
1608
+ if (marketplaceOk === false) {
1152
1609
  return;
1153
1610
  }
1154
- const linkOk = await setAntigravityAssets();
1155
- if (linkOk === false) {
1611
+ patchPluginCache();
1612
+ enableInConfig();
1613
+ configManager.codex = { path: CODEX_DIR };
1614
+ configManager.save();
1615
+ };
1616
+ var unlinkCodex = async (force = false) => {
1617
+ if (!force) {
1618
+ const ok = await confirm7({
1619
+ message: `Remove Codex plugin and marketplace entries?`,
1620
+ default: false
1621
+ });
1622
+ if (!ok) {
1623
+ console.log(chalk9.yellow("Cancelled."));
1624
+ return;
1625
+ }
1626
+ }
1627
+ console.log(chalk9.red(`
1628
+ Removing Codex plugin...`));
1629
+ const marketplacePath = path8.join(CODEX_AGENTS_DIR, "marketplace.json");
1630
+ if (fs8.existsSync(marketplacePath)) {
1631
+ try {
1632
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1633
+ const backupPath = `${marketplacePath}.bak.${timestamp}`;
1634
+ fs8.copyFileSync(marketplacePath, backupPath);
1635
+ const marketplace = JSON.parse(fs8.readFileSync(marketplacePath, "utf-8"));
1636
+ if (Array.isArray(marketplace?.plugins)) {
1637
+ marketplace.plugins = marketplace.plugins.filter(
1638
+ (p) => p?.name !== PLUGIN_NAME
1639
+ );
1640
+ }
1641
+ try {
1642
+ fs8.writeFileSync(marketplacePath, JSON.stringify(marketplace, null, 4), "utf-8");
1643
+ fs8.unlinkSync(backupPath);
1644
+ console.log(chalk9.red(" removed") + chalk9.dim(` ${PLUGIN_NAME} from: ${marketplacePath}`));
1645
+ } catch (ex) {
1646
+ fs8.copyFileSync(backupPath, marketplacePath);
1647
+ fs8.unlinkSync(backupPath);
1648
+ console.warn(chalk9.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
1649
+ }
1650
+ } catch (ex) {
1651
+ console.error(chalk9.red(` \u274C Failed to clean up marketplace.json: ${ex.message}`));
1652
+ }
1653
+ }
1654
+ const configPath = path8.join(os3.homedir(), ".codex", "config.toml");
1655
+ if (fs8.existsSync(configPath)) {
1656
+ try {
1657
+ const config = TOML.parse(fs8.readFileSync(configPath, "utf-8"));
1658
+ const pluginKey = `${PLUGIN_NAME}@${CODEX_MARKETPLACE_NAME}`;
1659
+ if (config.plugins?.[pluginKey] !== void 0) {
1660
+ delete config.plugins[pluginKey];
1661
+ fs8.writeFileSync(configPath, TOML.stringify(config), "utf-8");
1662
+ console.log(chalk9.red(" removed") + chalk9.dim(` ${pluginKey} from: ${configPath}`));
1663
+ }
1664
+ } catch (ex) {
1665
+ console.error(chalk9.red(` \u274C Failed to clean up config.toml: ${ex.message}`));
1666
+ }
1667
+ }
1668
+ const cacheMarketDir = path8.join(CODEX_CACHE_DIR, CODEX_MARKETPLACE_NAME);
1669
+ if (fs8.existsSync(cacheMarketDir)) {
1670
+ fs8.rmSync(cacheMarketDir, { recursive: true, force: true });
1671
+ console.log(chalk9.red(" removed") + chalk9.dim(`: ${cacheMarketDir}`));
1672
+ }
1673
+ configManager.codex = null;
1674
+ configManager.save();
1675
+ };
1676
+
1677
+ // src/link/cursor.ts
1678
+ import path9 from "path";
1679
+ import fs9 from "fs";
1680
+ import chalk10 from "chalk";
1681
+ import { confirm as confirm8 } from "@inquirer/prompts";
1682
+ import { pathExists as pathExists4 } from "fs-extra";
1683
+ var CURSOR_BACKUP_DIR = path9.join(CURSOR_DIR, "SET_PROMPT_BACKUP");
1684
+ var linkCursor = async () => {
1685
+ const repoPath = resolveRepoPath();
1686
+ if (repoPath == null) {
1156
1687
  return;
1157
1688
  }
1158
- configManager.antigravity = { path: ANTIGRAVITY_DIR, backup_path: ANTIGRAVITY_BACKUP_DIR };
1689
+ console.log(chalk10.green(`
1690
+ Setting up Cursor integration...`));
1691
+ console.log(chalk10.dim(CURSOR_DIR));
1692
+ const cursorDirs = AGENT_PROMPT_DIRS["cursor" /* CURSOR */];
1693
+ const dirsToBackup = [];
1694
+ for (const dir of cursorDirs) {
1695
+ const target = path9.join(CURSOR_DIR, dir);
1696
+ if (fs9.existsSync(target) && fs9.lstatSync(target).isSymbolicLink() === false && fs9.readdirSync(target).length > 0) {
1697
+ dirsToBackup.push(dir);
1698
+ }
1699
+ }
1700
+ if (dirsToBackup.length > 0) {
1701
+ console.log(chalk10.yellow(`
1702
+ \u26A0 The following existing directories will be replaced by symlinks:`));
1703
+ for (const dir of dirsToBackup) {
1704
+ console.log(chalk10.dim(` - ${path9.join(CURSOR_DIR, dir)}`));
1705
+ }
1706
+ console.log(chalk10.yellow(` They will be backed up to: `) + chalk10.dim(CURSOR_BACKUP_DIR));
1707
+ const ok = await confirm8({ message: "Back up existing directories?", default: true });
1708
+ if (!ok) {
1709
+ console.log(chalk10.yellow("Skipped Cursor linking."));
1710
+ return;
1711
+ }
1712
+ fs9.mkdirSync(CURSOR_BACKUP_DIR, { recursive: true });
1713
+ for (const dir of dirsToBackup) {
1714
+ const src = path9.join(CURSOR_DIR, dir);
1715
+ const dest = path9.join(CURSOR_BACKUP_DIR, dir);
1716
+ fs9.renameSync(src, dest);
1717
+ console.log(chalk10.yellow(" backed up") + chalk10.dim(`: ${dir}/ \u2192 ${dest}`));
1718
+ }
1719
+ }
1720
+ try {
1721
+ const linked = [];
1722
+ for (const dir of cursorDirs) {
1723
+ const src = path9.join(repoPath, dir);
1724
+ const dest = path9.join(CURSOR_DIR, dir);
1725
+ if (await pathExists4(src) === false) {
1726
+ continue;
1727
+ }
1728
+ if (fs9.existsSync(dest)) {
1729
+ fs9.rmSync(dest, { recursive: true, force: true });
1730
+ }
1731
+ const symlinkType = process.platform === "win32" ? "junction" : "dir";
1732
+ fs9.symlinkSync(src, dest, symlinkType);
1733
+ linked.push({ dir, src });
1734
+ }
1735
+ for (const { dir, src } of linked) {
1736
+ console.log(chalk10.dim(` \u251C\u2500\u2500 `) + chalk10.bold(`${dir}/`) + chalk10.dim(` \u2192 ${src}`) + chalk10.green(" \u2713"));
1737
+ }
1738
+ const mcpSrc = path9.join(repoPath, "mcp.json");
1739
+ const mcpDest = path9.join(CURSOR_DIR, "mcp.json");
1740
+ if (fs9.existsSync(mcpSrc)) {
1741
+ if (fs9.existsSync(mcpDest) && fs9.statSync(mcpDest).ino !== fs9.statSync(mcpSrc).ino) {
1742
+ const mcpBackup = path9.join(CURSOR_BACKUP_DIR, "mcp.json");
1743
+ fs9.mkdirSync(CURSOR_BACKUP_DIR, { recursive: true });
1744
+ fs9.renameSync(mcpDest, mcpBackup);
1745
+ console.log(chalk10.yellow(" backed up") + chalk10.dim(`: mcp.json \u2192 ${mcpBackup}`));
1746
+ }
1747
+ if (fs9.existsSync(mcpDest)) {
1748
+ fs9.unlinkSync(mcpDest);
1749
+ }
1750
+ fs9.linkSync(mcpSrc, mcpDest);
1751
+ console.log(chalk10.dim(` \u2514\u2500\u2500 `) + chalk10.bold("mcp.json") + chalk10.dim(` \u21D4 ${mcpSrc}`) + chalk10.green(" \u2713"));
1752
+ }
1753
+ } catch (ex) {
1754
+ console.error(chalk10.red(`\u274C Failed to set up Cursor: ${ex.message}`));
1755
+ return;
1756
+ }
1757
+ configManager.cursor = { path: CURSOR_DIR, backup_path: CURSOR_BACKUP_DIR };
1159
1758
  configManager.save();
1160
1759
  };
1760
+ var unlinkCursor = async (force = false) => {
1761
+ if (!force) {
1762
+ const ok = await confirm8({
1763
+ message: `Remove Cursor symlinks from ${CURSOR_DIR}?`,
1764
+ default: false
1765
+ });
1766
+ if (!ok) {
1767
+ console.log(chalk10.yellow("Cancelled."));
1768
+ return;
1769
+ }
1770
+ }
1771
+ console.log(chalk10.red(`
1772
+ Removing Cursor integration...`));
1773
+ console.log(chalk10.dim(CURSOR_DIR));
1774
+ const backupPath = configManager.cursor?.backup_path ?? CURSOR_BACKUP_DIR;
1775
+ for (const dir of AGENT_PROMPT_DIRS["cursor" /* CURSOR */]) {
1776
+ const target = path9.join(CURSOR_DIR, dir);
1777
+ if (fs9.existsSync(target) && fs9.lstatSync(target).isSymbolicLink()) {
1778
+ fs9.unlinkSync(target);
1779
+ console.log(chalk10.red(" removed symlink") + chalk10.dim(`: ${target}`));
1780
+ }
1781
+ }
1782
+ const mcpDest = path9.join(CURSOR_DIR, "mcp.json");
1783
+ if (fs9.existsSync(mcpDest)) {
1784
+ fs9.unlinkSync(mcpDest);
1785
+ console.log(chalk10.red(" removed") + chalk10.dim(`: ${mcpDest}`));
1786
+ }
1787
+ if (fs9.existsSync(backupPath)) {
1788
+ try {
1789
+ for (const dir of AGENT_PROMPT_DIRS["cursor" /* CURSOR */]) {
1790
+ const src = path9.join(backupPath, dir);
1791
+ const dest = path9.join(CURSOR_DIR, dir);
1792
+ if (!fs9.existsSync(src)) {
1793
+ continue;
1794
+ }
1795
+ fs9.renameSync(src, dest);
1796
+ console.log(chalk10.green(" restored") + chalk10.dim(`: ${dir}/`));
1797
+ }
1798
+ const mcpBackup = path9.join(backupPath, "mcp.json");
1799
+ if (fs9.existsSync(mcpBackup)) {
1800
+ fs9.renameSync(mcpBackup, mcpDest);
1801
+ console.log(chalk10.green(" restored") + chalk10.dim(`: mcp.json`));
1802
+ }
1803
+ fs9.rmSync(backupPath, { recursive: true, force: true });
1804
+ } catch (ex) {
1805
+ console.error(chalk10.red(` \u274C Failed to restore Cursor backup: ${ex.message}`));
1806
+ }
1807
+ }
1808
+ configManager.cursor = null;
1809
+ configManager.save();
1810
+ };
1811
+
1812
+ // src/commands/link-command.ts
1813
+ var LINK_MAP = {
1814
+ ["claudecode" /* CLAUDECODE */]: linkClaudeCode,
1815
+ ["roocode" /* ROOCODE */]: linkRooCode,
1816
+ ["openclaw" /* OPENCLAW */]: linkOpenclaw,
1817
+ ["codex" /* CODEX */]: linkCodex,
1818
+ ["antigravity" /* ANTIGRAVITY */]: linkAntigravity,
1819
+ ["cursor" /* CURSOR */]: linkCursor
1820
+ };
1821
+ var UNLINK_MAP = {
1822
+ ["claudecode" /* CLAUDECODE */]: unlinkClaudeCode,
1823
+ ["roocode" /* ROOCODE */]: unlinkRooCode,
1824
+ ["openclaw" /* OPENCLAW */]: unlinkOpenclaw,
1825
+ ["codex" /* CODEX */]: unlinkCodex,
1826
+ ["antigravity" /* ANTIGRAVITY */]: unlinkAntigravity,
1827
+ ["cursor" /* CURSOR */]: unlinkCursor
1828
+ };
1161
1829
  var linkCommand = async (tool) => {
1162
1830
  if (tool != null) {
1163
1831
  const known = ALL_AGENTS.some((a) => a.value === tool);
1164
1832
  if (!known) {
1165
- console.log(chalk4.red(`Unknown vendor: ${tool}`));
1833
+ console.log(chalk11.red(`Unknown vendor: ${tool}`));
1166
1834
  process.exit(1);
1167
1835
  }
1168
- if (tool === "claudecode" /* CLAUDECODE */) {
1169
- await linkClaudeCode();
1170
- } else if (tool === "roocode" /* ROOCODE */) {
1171
- await linkRooCode();
1172
- } else if (tool === "openclaw" /* OPENCLAW */) {
1173
- await linkOpenclaw();
1174
- } else if (tool === "codex" /* CODEX */) {
1175
- await linkCodex();
1176
- } else if (tool === "antigravity" /* ANTIGRAVITY */) {
1177
- await linkAntigravity();
1178
- }
1836
+ await LINK_MAP[tool]();
1179
1837
  return;
1180
1838
  }
1181
1839
  const prevLinked = {
@@ -1183,80 +1841,84 @@ var linkCommand = async (tool) => {
1183
1841
  ["roocode" /* ROOCODE */]: configManager.isRooCodeEnabled(),
1184
1842
  ["openclaw" /* OPENCLAW */]: configManager.isOpenclawEnabled(),
1185
1843
  ["codex" /* CODEX */]: configManager.isCodexEnabled(),
1186
- ["antigravity" /* ANTIGRAVITY */]: configManager.isAntigravityEnabled()
1844
+ ["antigravity" /* ANTIGRAVITY */]: configManager.isAntigravityEnabled(),
1845
+ ["cursor" /* CURSOR */]: configManager.isCursorEnabled()
1187
1846
  };
1188
1847
  const selected = await checkbox({
1189
1848
  message: "Which AI agent do you want to integrate?",
1190
1849
  choices: ALL_AGENTS.map((a) => ({
1191
- name: prevLinked[a.value] ? `${a.name} ${chalk4.dim("(applied)")}` : a.name,
1850
+ name: prevLinked[a.value] ? `${a.name} ${chalk11.dim("(applied)")}` : a.name,
1192
1851
  value: a.value,
1193
1852
  checked: prevLinked[a.value]
1194
1853
  }))
1195
1854
  });
1855
+ const toLink = ALL_AGENTS.filter((a) => !prevLinked[a.value] && selected.includes(a.value));
1856
+ const toUnlink = ALL_AGENTS.filter((a) => prevLinked[a.value] && !selected.includes(a.value));
1857
+ console.log();
1858
+ if (toLink.length > 0) {
1859
+ console.log(chalk11.green(" Link ") + chalk11.dim("\u2192 ") + toLink.map((a) => chalk11.bold(a.name)).join(chalk11.dim(", ")));
1860
+ }
1861
+ if (toUnlink.length > 0) {
1862
+ console.log(chalk11.red(" Unlink ") + chalk11.dim("\u2192 ") + toUnlink.map((a) => chalk11.bold(a.name)).join(chalk11.dim(", ")));
1863
+ }
1864
+ console.log();
1865
+ if (toLink.length === 0 && toUnlink.length === 0) {
1866
+ return;
1867
+ }
1196
1868
  for (const a of ALL_AGENTS) {
1197
1869
  const was = prevLinked[a.value];
1198
1870
  const now = selected.includes(a.value);
1199
1871
  if (!was && now) {
1200
- if (a.value === "claudecode" /* CLAUDECODE */) {
1201
- await linkClaudeCode();
1202
- } else if (a.value === "roocode" /* ROOCODE */) {
1203
- await linkRooCode();
1204
- } else if (a.value === "openclaw" /* OPENCLAW */) {
1205
- await linkOpenclaw();
1206
- } else if (a.value === "codex" /* CODEX */) {
1207
- await linkCodex();
1208
- } else if (a.value === "antigravity" /* ANTIGRAVITY */) {
1209
- await linkAntigravity();
1210
- }
1872
+ await LINK_MAP[a.value]();
1211
1873
  }
1212
1874
  if (was && !now) {
1213
- if (a.value === "claudecode" /* CLAUDECODE */) {
1214
- await unlinkClaudeCode(true);
1215
- } else if (a.value === "roocode" /* ROOCODE */) {
1216
- await unlinkRooCode(true);
1217
- } else if (a.value === "openclaw" /* OPENCLAW */) {
1218
- await unlinkOpenclaw(true);
1219
- } else if (a.value === "antigravity" /* ANTIGRAVITY */) {
1220
- await unlinkAntigravity(true);
1221
- }
1875
+ await UNLINK_MAP[a.value](true);
1222
1876
  }
1223
1877
  }
1224
1878
  };
1225
1879
 
1226
1880
  // src/commands/uninstall-command.ts
1227
- import fs5 from "fs";
1228
- import chalk5 from "chalk";
1229
- import { confirm as confirm4 } from "@inquirer/prompts";
1881
+ import fs10 from "fs";
1882
+ import chalk12 from "chalk";
1883
+ import { confirm as confirm9 } from "@inquirer/prompts";
1230
1884
  var uninstallCommand = async () => {
1231
1885
  const targets = [
1232
- { label: `Config file ${chalk5.dim(CONFIG_PATH)}`, path: CONFIG_PATH },
1233
- { label: `Home dir ${chalk5.dim(HOME_DIR)}`, path: HOME_DIR }
1234
- ].filter((t) => fs5.existsSync(t.path));
1886
+ { label: `Config file ${chalk12.dim(CONFIG_PATH)}`, path: CONFIG_PATH },
1887
+ { label: `Home dir ${chalk12.dim(HOME_DIR)}`, path: HOME_DIR }
1888
+ ].filter((t) => fs10.existsSync(t.path));
1235
1889
  const hasClaudeCode = configManager.isClaudeCodeEnabled();
1236
1890
  const hasRooCode = configManager.isRooCodeEnabled();
1237
1891
  const hasOpenclaw = configManager.isOpenclawEnabled();
1238
1892
  const hasAntigravity = configManager.isAntigravityEnabled();
1239
- if (targets.length === 0 && !hasClaudeCode && !hasRooCode && !hasOpenclaw && !hasAntigravity) {
1240
- console.log(chalk5.yellow("Nothing to remove."));
1893
+ const hasCodex = configManager.isCodexEnabled();
1894
+ const hasCursor = configManager.isCursorEnabled();
1895
+ if (targets.length === 0 && !hasClaudeCode && !hasRooCode && !hasOpenclaw && !hasAntigravity && !hasCodex && !hasCursor) {
1896
+ console.log(chalk12.yellow("Nothing to remove."));
1241
1897
  return;
1242
1898
  }
1243
- console.log(chalk5.red("\nThe following will be removed:"));
1899
+ console.log(chalk12.red("\nThe following will be removed:"));
1244
1900
  targets.forEach((t) => console.log(` ${t.label}`));
1245
1901
  if (hasClaudeCode) {
1246
- console.log(` Claude Code plugin dir ${chalk5.dim(CLAUDE_CODE_DIR)}`);
1902
+ console.log(` Claude Code plugin dir ${chalk12.dim(CLAUDE_CODE_DIR)}`);
1247
1903
  }
1248
1904
  if (hasRooCode) {
1249
- console.log(` RooCode symlinks ${chalk5.dim("(backup will be restored)")}`);
1905
+ console.log(` RooCode symlinks ${chalk12.dim("(backup will be restored)")}`);
1250
1906
  }
1251
1907
  if (hasOpenclaw) {
1252
- console.log(` OpenClaw symlinks ${chalk5.dim("(backup will be restored)")}`);
1908
+ console.log(` OpenClaw symlinks ${chalk12.dim("(backup will be restored)")}`);
1253
1909
  }
1254
1910
  if (hasAntigravity) {
1255
- console.log(` Antigravity symlinks ${chalk5.dim("(backup will be restored)")}`);
1911
+ console.log(` Antigravity symlinks ${chalk12.dim("(backup will be restored)")}`);
1912
+ }
1913
+ if (hasCodex) {
1914
+ console.log(` Codex symlinks ${chalk12.dim("(backup will be restored)")}`);
1915
+ }
1916
+ if (hasCursor) {
1917
+ console.log(` Cursor plugin dir ${chalk12.dim("(symlink will be removed)")}`);
1256
1918
  }
1257
- const ok = await confirm4({ message: "Proceed?", default: false });
1919
+ const ok = await confirm9({ message: "Proceed?", default: false });
1258
1920
  if (!ok) {
1259
- console.log(chalk5.yellow("Cancelled."));
1921
+ console.log(chalk12.yellow("Cancelled."));
1260
1922
  return;
1261
1923
  }
1262
1924
  if (hasClaudeCode) {
@@ -1271,27 +1933,33 @@ var uninstallCommand = async () => {
1271
1933
  if (hasAntigravity) {
1272
1934
  await unlinkAntigravity(true);
1273
1935
  }
1936
+ if (hasCodex) {
1937
+ await unlinkCodex(true);
1938
+ }
1939
+ if (hasCursor) {
1940
+ await unlinkCursor(true);
1941
+ }
1274
1942
  for (const t of targets) {
1275
- fs5.rmSync(t.path, { recursive: true, force: true });
1276
- console.log(chalk5.dim(` removed: ${t.path}`));
1943
+ fs10.rmSync(t.path, { recursive: true, force: true });
1944
+ console.log(chalk12.dim(` removed: ${t.path}`));
1277
1945
  }
1278
- console.log(chalk5.green("\nUninstalled."));
1946
+ console.log(chalk12.green("\nUninstalled."));
1279
1947
  };
1280
1948
 
1281
1949
  // src/commands/status-command.ts
1282
- import chalk6 from "chalk";
1950
+ import chalk13 from "chalk";
1283
1951
  var statusCommand = () => {
1284
1952
  if (configManager.repo_path == null) {
1285
- console.log(chalk6.yellow("\u274C No repo installed."));
1286
- console.log(chalk6.dim(` Run: set-prompt install <repo-url>`));
1953
+ console.log(chalk13.yellow("\u274C No repo installed."));
1954
+ console.log(chalk13.dim(` Run: set-prompt install <repo-url>`));
1287
1955
  return;
1288
1956
  }
1289
- console.log(chalk6.bold("\nRepo"));
1290
- console.log(`${TAB}path ${chalk6.cyan(configManager.repo_path)}`);
1957
+ console.log(chalk13.bold("\nRepo"));
1958
+ console.log(`${TAB}path ${chalk13.cyan(configManager.repo_path)}`);
1291
1959
  if (configManager.remote_url != null) {
1292
- console.log(`${TAB}remote ${chalk6.dim(configManager.remote_url)}`);
1960
+ console.log(`${TAB}remote ${chalk13.dim(configManager.remote_url)}`);
1293
1961
  }
1294
- console.log(chalk6.bold("\nLinked agents"));
1962
+ console.log(chalk13.bold("\nLinked agents"));
1295
1963
  for (const agent of ALL_AGENTS) {
1296
1964
  let linked = false;
1297
1965
  let agentPath = null;
@@ -1311,8 +1979,8 @@ var statusCommand = () => {
1311
1979
  linked = configManager.isAntigravityEnabled();
1312
1980
  agentPath = configManager.antigravity?.path;
1313
1981
  }
1314
- const label = linked ? chalk6.green("linked") : chalk6.dim("not linked");
1315
- const pathStr = linked && agentPath ? chalk6.dim(` \u2192 ${agentPath}`) : "";
1982
+ const label = linked ? chalk13.green("linked") : chalk13.dim("not linked");
1983
+ const pathStr = linked && agentPath ? chalk13.dim(` \u2192 ${agentPath}`) : "";
1316
1984
  console.log(`${TAB}${agent.name.padEnd(12)} ${label}${pathStr}`);
1317
1985
  }
1318
1986
  console.log("");
@@ -1320,69 +1988,69 @@ var statusCommand = () => {
1320
1988
 
1321
1989
  // src/commands/update-command.ts
1322
1990
  import { spawnSync as spawnSync2 } from "child_process";
1323
- import chalk7 from "chalk";
1991
+ import chalk14 from "chalk";
1324
1992
  var updateCommand = () => {
1325
1993
  const repoPath = configManager.repo_path;
1326
1994
  if (repoPath == null) {
1327
- console.error(chalk7.red("\u274C No repo installed."));
1328
- console.log(chalk7.yellow("Run: set-prompt install <git-url>"));
1995
+ console.error(chalk14.red("\u274C No repo installed."));
1996
+ console.log(chalk14.yellow("Run: set-prompt install <git-url>"));
1329
1997
  return;
1330
1998
  }
1331
1999
  if (configManager.remote_url == null) {
1332
- console.error(chalk7.red("\u274C No remote URL registered. Cannot update."));
2000
+ console.error(chalk14.red("\u274C No remote URL registered. Cannot update."));
1333
2001
  return;
1334
2002
  }
1335
- console.log(chalk7.green("\nUpdating prompt repo..."));
1336
- console.log(chalk7.dim(repoPath));
2003
+ console.log(chalk14.green("\nUpdating prompt repo..."));
2004
+ console.log(chalk14.dim(repoPath));
1337
2005
  const fetch = spawnSync2("git", ["fetch"], { cwd: repoPath, stdio: "inherit" });
1338
2006
  if (fetch.status !== 0) {
1339
- console.error(chalk7.red("\u274C git fetch failed."));
2007
+ console.error(chalk14.red("\u274C git fetch failed."));
1340
2008
  return;
1341
2009
  }
1342
2010
  const pull = spawnSync2("git", ["pull"], { cwd: repoPath, stdio: "inherit" });
1343
2011
  if (pull.status !== 0) {
1344
- console.error(chalk7.red("\u274C git pull failed."));
2012
+ console.error(chalk14.red("\u274C git pull failed."));
1345
2013
  return;
1346
2014
  }
1347
- console.log(chalk7.green("\u2705 Repo updated."));
2015
+ console.log(chalk14.green("\u2705 Repo updated."));
1348
2016
  };
1349
2017
 
1350
2018
  // src/index.ts
1351
2019
  process.on("SIGINT", () => {
1352
- console.log(chalk8.yellow("\nCancelled."));
2020
+ console.log(chalk15.yellow("\nCancelled."));
1353
2021
  process.exit(0);
1354
2022
  });
1355
2023
  process.on("unhandledRejection", (reason) => {
1356
2024
  if (reason instanceof Error && reason.name === "ExitPromptError") {
1357
- console.log(chalk8.yellow("\nCancelled."));
2025
+ console.log(chalk15.yellow("\nCancelled."));
1358
2026
  process.exit(0);
1359
2027
  }
1360
2028
  throw reason;
1361
2029
  });
1362
- var __dirname = path5.dirname(fileURLToPath(import.meta.url));
1363
- var pkg = JSON.parse(fs6.readFileSync(path5.join(__dirname, "../package.json"), "utf-8"));
2030
+ var __dirname = path10.dirname(fileURLToPath(import.meta.url));
2031
+ var pkg = JSON.parse(fs11.readFileSync(path10.join(__dirname, "../package.json"), "utf-8"));
1364
2032
  configManager.init();
1365
2033
  var program = new Command();
1366
- var banner = chalk8.cyan(figlet.textSync("Set-Prompt", { horizontalLayout: "full" }));
2034
+ var banner = chalk15.cyan(figlet.textSync("Set-Prompt", { horizontalLayout: "full" }));
1367
2035
  program.name("set-prompt").description(pkg.description).version(pkg.version).addHelpText("beforeAll", banner + "\n").action(() => {
1368
2036
  program.help();
1369
2037
  });
1370
- program.command("install").description(`\u{1F4E6} Clone a ${chalk8.cyan("git repo")} into ${chalk8.dim("~/.set-prompt/repo/")} and register it as your prompt source`).argument("<url>", "remote git URL").action(async (source) => {
2038
+ 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) => {
1371
2039
  await installCommand(source);
1372
2040
  });
1373
- program.command("link").description(`\u{1F517} Symlink your prompt repo into an ${chalk8.cyan("AI agent")} plugin dir ${chalk8.dim("(claudecode | roocode | openclaw | codex | antigravity)")}`).argument("[agent]", `target agent ${chalk8.dim("(omit for interactive selection)")}`).action(async (agent) => {
2041
+ 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) => {
1374
2042
  await linkCommand(agent);
1375
2043
  });
1376
- program.command("scaffold").description(`\u{1F6E0}\uFE0F Verify and create ${chalk8.cyan("required directories")} in a prompt repo ${chalk8.dim("(-f to force overwrite)")}`).argument("[path]", `path to repo ${chalk8.dim("(defaults to installed source)")}`).option("-f, --force", "overwrite existing files without prompting").action(async (localPath, options) => {
1377
- await scaffoldCommand(localPath, options);
2044
+ 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) => {
2045
+ await scaffoldCommand(localPath);
1378
2046
  });
1379
- program.command("status").description(`\u{1F4CB} Show registered ${chalk8.cyan("repo")} and which ${chalk8.cyan("agents")} are linked`).action(() => {
2047
+ program.command("status").description(`\u{1F4CB} Show registered ${chalk15.cyan("repo")} and which ${chalk15.cyan("agents")} are linked`).action(() => {
1380
2048
  statusCommand();
1381
2049
  });
1382
- program.command("update").description(`\u{1F504} Fetch and pull the latest changes from the ${chalk8.cyan("remote repo")}`).action(() => {
2050
+ program.command("update").description(`\u{1F504} Fetch and pull the latest changes from the ${chalk15.cyan("remote repo")}`).action(() => {
1383
2051
  updateCommand();
1384
2052
  });
1385
- program.command("uninstall").description(`\u{1F5D1}\uFE0F Remove all set-prompt data ${chalk8.dim("(~/.set-prompt/, plugin dirs, settings entries)")}`).action(async () => {
2053
+ program.command("uninstall").description(`\u{1F5D1}\uFE0F Remove all set-prompt data ${chalk15.dim("(~/.set-prompt/, plugin dirs, settings entries)")}`).action(async () => {
1386
2054
  await uninstallCommand();
1387
2055
  });
1388
2056
  program.parse(process.argv);