set-prompt 0.4.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/CHANGELOG.md +131 -100
- package/README.md +136 -104
- package/dist/index.js +957 -657
- package/package.json +10 -3
package/dist/index.js
CHANGED
|
@@ -2,23 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk15 from "chalk";
|
|
6
6
|
import figlet from "figlet";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
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
|
|
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,18 +31,17 @@ 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 CODEX_DIR = path.join(
|
|
34
|
+
var CODEX_DIR = path.join(HOME_DIR, "codex");
|
|
33
35
|
var CODEX_BACKUP_DIR = path.join(CODEX_DIR, "SET_PROMPT_BACKUP");
|
|
34
|
-
var CURSOR_DIR = path.join(
|
|
35
|
-
var
|
|
36
|
-
var PROMPT_DIR_NAMES = ["skills", "commands", "hooks", "agents"];
|
|
36
|
+
var CURSOR_DIR = path.join(os.homedir(), ".cursor");
|
|
37
|
+
var PROMPT_DIR_NAMES = ["skills", "commands", "hooks", "agents", "rules"];
|
|
37
38
|
var AGENT_PROMPT_DIRS = {
|
|
38
39
|
["claudecode" /* CLAUDECODE */]: ["skills", "commands", "hooks", "agents"],
|
|
39
40
|
["roocode" /* ROOCODE */]: ["skills", "commands"],
|
|
40
41
|
["openclaw" /* OPENCLAW */]: ["skills"],
|
|
41
|
-
["codex" /* CODEX */]: ["skills"
|
|
42
|
+
["codex" /* CODEX */]: ["skills"],
|
|
42
43
|
["antigravity" /* ANTIGRAVITY */]: ["skills"],
|
|
43
|
-
["cursor" /* CURSOR */]: ["skills", "agents", "
|
|
44
|
+
["cursor" /* CURSOR */]: ["skills", "agents", "commands", "hooks"]
|
|
44
45
|
};
|
|
45
46
|
var ALL_AGENTS = [
|
|
46
47
|
{ name: "Claude Code", value: "claudecode" /* CLAUDECODE */ },
|
|
@@ -80,9 +81,7 @@ var AntigravityConfigSchema = z.object({
|
|
|
80
81
|
});
|
|
81
82
|
var CursorConfigSchema = z.object({
|
|
82
83
|
path: z.string().nullable(),
|
|
83
|
-
|
|
84
|
-
plugin_dir: z.string().nullable()
|
|
85
|
-
// ~/.cursor/plugins/set-prompt (설치 symlink)
|
|
84
|
+
backup_path: z.string().nullish().optional()
|
|
86
85
|
});
|
|
87
86
|
var GlobalConfigSchema = z.object({
|
|
88
87
|
repo_path: z.string(),
|
|
@@ -240,23 +239,33 @@ var ConfigManager = class {
|
|
|
240
239
|
var configManager = new ConfigManager();
|
|
241
240
|
|
|
242
241
|
// src/_libs/index.ts
|
|
242
|
+
import chalk2 from "chalk";
|
|
243
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
|
+
};
|
|
244
252
|
|
|
245
253
|
// src/commands/scaffold-command.ts
|
|
246
254
|
import fs2 from "fs";
|
|
247
255
|
import path2 from "path";
|
|
248
|
-
import
|
|
256
|
+
import chalk3 from "chalk";
|
|
249
257
|
import { confirm } from "@inquirer/prompts";
|
|
250
258
|
|
|
251
259
|
// src/_libs/templates.ts
|
|
252
260
|
var SET_PROMPT_GUIDE = `# Set Prompt Repository Guide
|
|
253
261
|
|
|
254
|
-
> Managed by [set-prompt](https://github.com/
|
|
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.
|
|
255
265
|
|
|
256
266
|
## Structure
|
|
257
267
|
|
|
258
268
|
\`\`\`
|
|
259
|
-
\u251C\u2500\u2500 set-prompt.toml # Repository configuration
|
|
260
269
|
\u251C\u2500\u2500 skills/
|
|
261
270
|
\u2502 \u2514\u2500\u2500 <skill-name>/
|
|
262
271
|
\u2502 \u251C\u2500\u2500 SKILL.md # Platform-specific frontmatter + prompt content
|
|
@@ -265,10 +274,27 @@ var SET_PROMPT_GUIDE = `# Set Prompt Repository Guide
|
|
|
265
274
|
\u2502 \u2514\u2500\u2500 <command-name>/
|
|
266
275
|
\u2502 \u251C\u2500\u2500 COMMAND.md # Platform-specific frontmatter + prompt content
|
|
267
276
|
\u2502 \u2514\u2500\u2500 ... # Supporting files
|
|
268
|
-
\u251C\u2500\u2500 hooks/ # Lifecycle shell hooks
|
|
269
|
-
\
|
|
270
|
-
|
|
271
|
-
|
|
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
|
|
272
298
|
\`\`\`
|
|
273
299
|
|
|
274
300
|
## Frontmatter Reference
|
|
@@ -318,6 +344,15 @@ metadata: {"os":["darwin","linux"],"requires":{"bins":["git"],"env":["MY_API_KEY
|
|
|
318
344
|
# Antigravity
|
|
319
345
|
name: my-skill
|
|
320
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
|
|
321
356
|
---
|
|
322
357
|
\`\`\`
|
|
323
358
|
|
|
@@ -339,10 +374,13 @@ description: "What this skill does and when to use it"
|
|
|
339
374
|
| \`metadata\` | No | OpenClaw | Single-line JSON for platform gating: \`os\` (platform filter), \`requires.bins\` (required binaries), \`requires.env\` (required env vars) |
|
|
340
375
|
| \`homepage\` | No | OpenClaw | URL shown as "Website" in the macOS Skills UI. Also settable via \`metadata.openclaw.homepage\`. |
|
|
341
376
|
| \`user-invocable\` | No | OpenClaw | \`false\` = hidden from \`/\` menu. (default: \`true\`) |
|
|
342
|
-
| \`disable-model-invocation\` | No |
|
|
377
|
+
| \`disable-model-invocation\` | No | Claude Code, OpenClaw, Cursor | \`true\` = skill only included when explicitly invoked via \`/skill-name\`. (default: \`false\`) |
|
|
343
378
|
| \`command-dispatch\` | No | OpenClaw | \`"tool"\` = bypass model, dispatch directly to a tool |
|
|
344
379
|
| \`command-tool\` | No | OpenClaw | Tool to invoke when \`command-dispatch: "tool"\` |
|
|
345
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. |
|
|
346
384
|
|
|
347
385
|
---
|
|
348
386
|
|
|
@@ -386,7 +424,7 @@ command-tool: "Bash"
|
|
|
386
424
|
|-------|----------|----------|-------------|
|
|
387
425
|
| \`name\` | No | All | Display name \u2014 lowercase, numbers, hyphens only (max 64 chars). Defaults to directory name. |
|
|
388
426
|
| \`description\` | Yes | All | Shown in \`/\` menu. Claude uses this to decide auto-loading. |
|
|
389
|
-
| \`user-invocable\` | No |
|
|
427
|
+
| \`user-invocable\` | No | Claude Code, OpenClaw | \`false\` = hidden from \`/\` menu, background knowledge only. (default: \`true\`) |
|
|
390
428
|
| \`allowed-tools\` | No | Claude Code | Tools Claude can use without asking. e.g. \`Read\` \`Write\` \`Edit\` \`Bash\` \`Grep\` \`Glob\` |
|
|
391
429
|
| \`argument-hint\` | No | Claude Code | Hint shown during autocomplete. e.g. \`[issue-number]\` |
|
|
392
430
|
| \`model\` | No | Claude Code | Model to use when active. \`sonnet\` or \`haiku\` |
|
|
@@ -398,10 +436,11 @@ command-tool: "Bash"
|
|
|
398
436
|
|
|
399
437
|
### Agents
|
|
400
438
|
|
|
401
|
-
Custom subagent definitions loaded by Claude Code.
|
|
439
|
+
Custom subagent definitions loaded by Claude Code and Cursor.
|
|
402
440
|
|
|
403
441
|
\`\`\`yaml
|
|
404
442
|
---
|
|
443
|
+
# Claude Code
|
|
405
444
|
name: "my-agent"
|
|
406
445
|
description: "What this agent does and when to use it"
|
|
407
446
|
allowed-tools:
|
|
@@ -409,37 +448,87 @@ allowed-tools:
|
|
|
409
448
|
- Bash
|
|
410
449
|
model: sonnet
|
|
411
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
|
|
412
484
|
---
|
|
485
|
+
|
|
486
|
+
Use \`@filename\` to reference files instead of copying content.
|
|
413
487
|
\`\`\`
|
|
414
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
|
+
|
|
415
498
|
| Field | Required | Description |
|
|
416
499
|
|-------|----------|-------------|
|
|
417
|
-
| \`
|
|
418
|
-
| \`
|
|
419
|
-
| \`
|
|
420
|
-
|
|
421
|
-
|
|
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.
|
|
422
507
|
|
|
423
508
|
---
|
|
424
509
|
|
|
425
510
|
### Hooks
|
|
426
511
|
|
|
427
|
-
Lifecycle
|
|
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.
|
|
428
517
|
|
|
429
518
|
\`\`\`yaml
|
|
430
519
|
hooks:
|
|
431
520
|
PreToolUse:
|
|
432
|
-
- matcher: "Bash"
|
|
521
|
+
- matcher: "Bash"
|
|
433
522
|
hooks:
|
|
434
523
|
- type: command
|
|
435
524
|
command: ".claude/hooks/validate.sh"
|
|
436
|
-
timeout: 30
|
|
525
|
+
timeout: 30
|
|
437
526
|
PostToolUse:
|
|
438
527
|
- matcher: "Write|Edit"
|
|
439
528
|
hooks:
|
|
440
529
|
- type: command
|
|
441
530
|
command: "npm run lint"
|
|
442
|
-
async: true
|
|
531
|
+
async: true
|
|
443
532
|
Stop:
|
|
444
533
|
- hooks:
|
|
445
534
|
- type: prompt
|
|
@@ -455,12 +544,10 @@ hooks:
|
|
|
455
544
|
| \`PostToolUseFailure\` | tool name | No | After a tool fails |
|
|
456
545
|
| \`UserPromptSubmit\` | \u2014 | Yes | When user submits a prompt |
|
|
457
546
|
| \`SessionStart\` | \`startup\` \\| \`resume\` \\| \`clear\` \\| \`compact\` | No | Session begins |
|
|
458
|
-
| \`Stop\` | \u2014 | Yes |
|
|
547
|
+
| \`Stop\` | \u2014 | Yes | Agent finishes responding |
|
|
459
548
|
| \`Notification\` | \`permission_prompt\` \\| \`idle_prompt\` | No | Notification fires |
|
|
460
549
|
| \`SubagentStart\` / \`SubagentStop\` | agent type | No / Yes | Subagent spawned / finished |
|
|
461
550
|
|
|
462
|
-
Full event list: [hooks reference](https://code.claude.com/docs/en/hooks)
|
|
463
|
-
|
|
464
551
|
**Handler fields**
|
|
465
552
|
|
|
466
553
|
| Field | Type | Description |
|
|
@@ -469,57 +556,134 @@ Full event list: [hooks reference](https://code.claude.com/docs/en/hooks)
|
|
|
469
556
|
| \`command\` | command only | Shell command to execute. Receives hook JSON on stdin |
|
|
470
557
|
| \`prompt\` | prompt/agent | Prompt text. Use \`$ARGUMENTS\` for the hook JSON input |
|
|
471
558
|
| \`timeout\` | all | Seconds before cancel. Defaults: 600 / 30 / 60 |
|
|
472
|
-
| \`async\` | command only | \`true\` = run in background, cannot block
|
|
559
|
+
| \`async\` | command only | \`true\` = run in background, cannot block |
|
|
473
560
|
| \`once\` | command only | \`true\` = run once per session then remove (skills only) |
|
|
474
561
|
|
|
475
|
-
|
|
562
|
+
Full event list: [Claude Code hooks reference](https://code.claude.com/docs/en/hooks)
|
|
476
563
|
|
|
477
|
-
|
|
564
|
+
---
|
|
478
565
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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.
|
|
482
569
|
|
|
483
|
-
|
|
484
|
-
|
|
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
|
+
}
|
|
485
585
|
\`\`\`
|
|
486
586
|
|
|
487
|
-
|
|
587
|
+
**Agent Events**
|
|
488
588
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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 |
|
|
492
609
|
|
|
493
|
-
|
|
494
|
-
set-prompt install https://github.com/you/my-prompts
|
|
495
|
-
set-prompt link
|
|
610
|
+
**Tab Events** (inline autocomplete only)
|
|
496
611
|
|
|
497
|
-
|
|
498
|
-
|
|
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"}'
|
|
499
642
|
\`\`\`
|
|
643
|
+
|
|
500
644
|
`;
|
|
501
645
|
|
|
502
646
|
// src/commands/scaffold-command.ts
|
|
503
|
-
var
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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;
|
|
515
674
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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;
|
|
519
682
|
}
|
|
520
|
-
|
|
683
|
+
fs2.writeFileSync(appJsonPath, JSON.stringify({ apps: {} }, null, 4), { encoding: "utf-8" });
|
|
684
|
+
return true;
|
|
521
685
|
};
|
|
522
|
-
var scaffoldCommand = async (localPath
|
|
686
|
+
var scaffoldCommand = async (localPath) => {
|
|
523
687
|
try {
|
|
524
688
|
let targetPath = null;
|
|
525
689
|
if (localPath != null) {
|
|
@@ -528,61 +692,48 @@ var scaffoldCommand = async (localPath, options = {}) => {
|
|
|
528
692
|
if (configManager.repo_path != null) {
|
|
529
693
|
targetPath = configManager.repo_path;
|
|
530
694
|
} else {
|
|
531
|
-
console.error(
|
|
695
|
+
console.error(chalk3.red("No path provided and no repo registered. Please provide a path."));
|
|
532
696
|
process.exit(1);
|
|
533
697
|
}
|
|
534
698
|
}
|
|
535
699
|
if (fs2.existsSync(targetPath) === false || fs2.statSync(targetPath).isDirectory() === false) {
|
|
536
|
-
console.error(
|
|
700
|
+
console.error(chalk3.red(`Invalid directory path: '${targetPath}'`));
|
|
537
701
|
process.exit(1);
|
|
538
702
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
if (!valid) {
|
|
548
|
-
if (options.force !== true) {
|
|
549
|
-
const proceed = await confirm({
|
|
550
|
-
message: "Some directories are missing. Scaffold them now?",
|
|
551
|
-
default: true
|
|
552
|
-
});
|
|
553
|
-
if (proceed === false) {
|
|
554
|
-
console.log(chalk2.yellow("Scaffold skipped."));
|
|
555
|
-
return false;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
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) {
|
|
559
710
|
const guideMdPath = path2.join(targetPath, "SET_PROMPT_GUIDE.md");
|
|
560
711
|
fs2.writeFileSync(guideMdPath, SET_PROMPT_GUIDE, { encoding: "utf-8", flag: "w" });
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
const gitkeepPath = path2.join(dirPath, ".gitkeep");
|
|
571
|
-
if (!fs2.existsSync(gitkeepPath)) {
|
|
572
|
-
fs2.writeFileSync(gitkeepPath, "", { encoding: "utf-8" });
|
|
573
|
-
if (!created.includes(` ${dirName}/`)) {
|
|
574
|
-
created.push(` ${dirName}/.gitkeep`);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
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}/`);
|
|
577
721
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
722
|
+
const gitkeepPath = path2.join(dirPath, ".gitkeep");
|
|
723
|
+
if (!fs2.existsSync(gitkeepPath)) {
|
|
724
|
+
fs2.writeFileSync(gitkeepPath, "", { encoding: "utf-8" });
|
|
581
725
|
}
|
|
582
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."));
|
|
583
734
|
return true;
|
|
584
735
|
} catch (ex) {
|
|
585
|
-
console.error(
|
|
736
|
+
console.error(chalk3.red(`Failed to scaffold repo structure: ${ex.message}`), ex);
|
|
586
737
|
throw ex;
|
|
587
738
|
}
|
|
588
739
|
};
|
|
@@ -596,13 +747,13 @@ var cloneRepo = async (remoteUrl) => {
|
|
|
596
747
|
backupPath = path3.join(HOME_DIR, `repo.bak.${timestamp}`);
|
|
597
748
|
try {
|
|
598
749
|
fs3.renameSync(localPath, backupPath);
|
|
599
|
-
console.log(
|
|
750
|
+
console.log(chalk4.yellow(" backed up") + chalk4.dim(` existing repo \u2192 ${backupPath}`));
|
|
600
751
|
} catch (ex) {
|
|
601
752
|
if (ex.code === "EPERM") {
|
|
602
|
-
console.error(
|
|
603
|
-
console.log(
|
|
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}`));
|
|
604
755
|
} else {
|
|
605
|
-
console.error(
|
|
756
|
+
console.error(chalk4.red(`\u274C Failed to backup existing repo: ${ex.message}`));
|
|
606
757
|
}
|
|
607
758
|
return false;
|
|
608
759
|
}
|
|
@@ -617,13 +768,13 @@ var cloneRepo = async (remoteUrl) => {
|
|
|
617
768
|
console.log("\u2705 Cloned successfully.");
|
|
618
769
|
if (backupPath != null) {
|
|
619
770
|
fs3.rmSync(backupPath, { recursive: true, force: true });
|
|
620
|
-
console.log(
|
|
771
|
+
console.log(chalk4.red(" removed") + chalk4.dim(` backup \u2192 ${backupPath}`));
|
|
621
772
|
}
|
|
622
773
|
await scaffoldCommand(localPath, { force: true });
|
|
623
774
|
configManager.repo_path = localPath;
|
|
624
775
|
configManager.remote_url = remoteUrl;
|
|
625
776
|
if (configManager.save() === false) {
|
|
626
|
-
console.error(
|
|
777
|
+
console.error(chalk4.red("Failed to save config."));
|
|
627
778
|
return false;
|
|
628
779
|
}
|
|
629
780
|
return true;
|
|
@@ -631,62 +782,54 @@ var cloneRepo = async (remoteUrl) => {
|
|
|
631
782
|
var installCommand = async (target) => {
|
|
632
783
|
try {
|
|
633
784
|
if (isGitUrl(target) === false) {
|
|
634
|
-
console.error(
|
|
635
|
-
console.log(
|
|
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"));
|
|
636
787
|
process.exit(1);
|
|
637
788
|
}
|
|
638
789
|
const normalizeUrl = (url) => url.replace(/\.git$/, "").toLowerCase();
|
|
639
790
|
if (configManager.repo_path != null) {
|
|
640
791
|
if (normalizeUrl(configManager.remote_url ?? "") === normalizeUrl(target)) {
|
|
641
|
-
console.error(
|
|
642
|
-
console.log(
|
|
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."));
|
|
643
794
|
return false;
|
|
644
795
|
}
|
|
645
|
-
console.warn(
|
|
796
|
+
console.warn(chalk4.yellow(`\u26A0 Switching repo: ${configManager.remote_url} \u2192 ${target}`));
|
|
646
797
|
const proceed = await confirm2({ message: "Replace existing installation?", default: false });
|
|
647
798
|
if (!proceed) {
|
|
648
|
-
console.log(
|
|
799
|
+
console.log(chalk4.yellow("Cancelled."));
|
|
649
800
|
return false;
|
|
650
801
|
}
|
|
651
802
|
} else {
|
|
652
803
|
const proceed = await confirm2({ message: `Clone and register "${target}"?`, default: true });
|
|
653
804
|
if (!proceed) {
|
|
654
|
-
console.log(
|
|
805
|
+
console.log(chalk4.yellow("Cancelled."));
|
|
655
806
|
return false;
|
|
656
807
|
}
|
|
657
808
|
}
|
|
658
809
|
return await cloneRepo(target);
|
|
659
810
|
} catch (ex) {
|
|
660
|
-
console.error(
|
|
811
|
+
console.error(chalk4.red(`Unexpected error: ${ex.message}`), ex);
|
|
661
812
|
process.exit(1);
|
|
662
813
|
}
|
|
663
814
|
};
|
|
664
815
|
|
|
665
816
|
// src/commands/link-command.ts
|
|
817
|
+
import chalk11 from "chalk";
|
|
818
|
+
import { checkbox } from "@inquirer/prompts";
|
|
819
|
+
|
|
820
|
+
// src/link/claudecode.ts
|
|
666
821
|
import path4 from "path";
|
|
667
822
|
import fs4 from "fs";
|
|
668
823
|
import os2 from "os";
|
|
669
|
-
import
|
|
670
|
-
import { confirm as confirm3
|
|
671
|
-
import { pathExists } from "fs-extra";
|
|
672
|
-
var resolveRepoPath = () => {
|
|
673
|
-
if (configManager.repo_path == null) {
|
|
674
|
-
console.error(chalk4.red("\u274C No repo installed."));
|
|
675
|
-
console.log(chalk4.yellow("Run: set-prompt install <git-url>"));
|
|
676
|
-
return null;
|
|
677
|
-
}
|
|
678
|
-
return configManager.repo_path;
|
|
679
|
-
};
|
|
680
|
-
var MARKET_NAME = "set-prompt";
|
|
681
|
-
var PLUGIN_NAME = "sppt";
|
|
824
|
+
import chalk5 from "chalk";
|
|
825
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
682
826
|
var linkClaudeCode = async () => {
|
|
683
827
|
const repoPath = resolveRepoPath();
|
|
684
828
|
if (repoPath == null) {
|
|
685
829
|
return;
|
|
686
830
|
}
|
|
687
|
-
const
|
|
831
|
+
const buildMarketplace = () => {
|
|
688
832
|
try {
|
|
689
|
-
fs4.mkdirSync(CLAUDE_CODE_DIR, { recursive: true });
|
|
690
833
|
const marketplaceMetaDir = path4.join(CLAUDE_CODE_DIR, ".claude-plugin");
|
|
691
834
|
fs4.mkdirSync(marketplaceMetaDir, { recursive: true });
|
|
692
835
|
const marketplaceJson = {
|
|
@@ -697,52 +840,23 @@ var linkClaudeCode = async () => {
|
|
|
697
840
|
};
|
|
698
841
|
fs4.writeFileSync(
|
|
699
842
|
path4.join(marketplaceMetaDir, "marketplace.json"),
|
|
700
|
-
JSON.stringify(marketplaceJson, null,
|
|
701
|
-
"utf-8"
|
|
702
|
-
);
|
|
703
|
-
console.log(chalk4.dim(" \u251C\u2500\u2500 .claude-plugin/"));
|
|
704
|
-
console.log(chalk4.dim(" \u2502 \u2514\u2500\u2500 marketplace.json") + chalk4.green(" \u2713"));
|
|
705
|
-
const pluginDir = path4.join(CLAUDE_CODE_DIR, "plugins", PLUGIN_NAME);
|
|
706
|
-
fs4.mkdirSync(pluginDir, { recursive: true });
|
|
707
|
-
console.log(chalk4.dim(" \u2514\u2500\u2500 plugins/"));
|
|
708
|
-
console.log(chalk4.dim(` \u2514\u2500\u2500 ${PLUGIN_NAME}/`));
|
|
709
|
-
const pluginMetaDir = path4.join(pluginDir, ".claude-plugin");
|
|
710
|
-
fs4.mkdirSync(pluginMetaDir, { recursive: true });
|
|
711
|
-
const pluginJson = {
|
|
712
|
-
name: PLUGIN_NAME,
|
|
713
|
-
version: "1.0.0",
|
|
714
|
-
description: "Managed by set-prompt",
|
|
715
|
-
author: { name: path4.basename(repoPath) }
|
|
716
|
-
};
|
|
717
|
-
fs4.writeFileSync(
|
|
718
|
-
path4.join(pluginMetaDir, "plugin.json"),
|
|
719
|
-
JSON.stringify(pluginJson, null, 2),
|
|
843
|
+
JSON.stringify(marketplaceJson, null, 4),
|
|
720
844
|
"utf-8"
|
|
721
845
|
);
|
|
722
|
-
console.log(
|
|
723
|
-
console.log(
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if (await pathExists(src) === false) {
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
731
|
-
if (fs4.existsSync(dest)) {
|
|
732
|
-
fs4.rmSync(dest, { recursive: true, force: true });
|
|
733
|
-
}
|
|
734
|
-
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
735
|
-
fs4.symlinkSync(src, dest, symlinkType);
|
|
736
|
-
linked.push({ dir, src });
|
|
737
|
-
}
|
|
738
|
-
for (const { dir, src } of linked) {
|
|
739
|
-
const isLast = linked[linked.length - 1].dir === dir;
|
|
740
|
-
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
741
|
-
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 });
|
|
742
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"));
|
|
743
857
|
return true;
|
|
744
858
|
} catch (ex) {
|
|
745
|
-
console.error(
|
|
859
|
+
console.error(chalk5.red(`\u274C Failed to build marketplace structure: ${ex.message}`));
|
|
746
860
|
return false;
|
|
747
861
|
}
|
|
748
862
|
};
|
|
@@ -757,11 +871,11 @@ var linkClaudeCode = async () => {
|
|
|
757
871
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
758
872
|
settings = parsed;
|
|
759
873
|
} else {
|
|
760
|
-
console.warn(
|
|
874
|
+
console.warn(chalk5.yellow(" \u26A0 settings.json has unexpected format \u2014 proceeding with caution"));
|
|
761
875
|
}
|
|
762
876
|
} catch {
|
|
763
|
-
console.warn(
|
|
764
|
-
console.error(
|
|
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."));
|
|
765
879
|
return false;
|
|
766
880
|
}
|
|
767
881
|
}
|
|
@@ -780,21 +894,21 @@ var linkClaudeCode = async () => {
|
|
|
780
894
|
try {
|
|
781
895
|
fs4.copyFileSync(claudeSettingsPath, backupPath);
|
|
782
896
|
} catch (ex) {
|
|
783
|
-
console.warn(
|
|
897
|
+
console.warn(chalk5.yellow(` \u26A0 Could not create backup: ${ex.message}`));
|
|
784
898
|
backupPath = null;
|
|
785
899
|
}
|
|
786
900
|
}
|
|
787
901
|
try {
|
|
788
902
|
fs4.mkdirSync(path4.dirname(claudeSettingsPath), { recursive: true });
|
|
789
|
-
fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null,
|
|
903
|
+
fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 4), "utf-8");
|
|
790
904
|
} catch (ex) {
|
|
791
905
|
if (backupPath !== null) {
|
|
792
906
|
try {
|
|
793
907
|
fs4.copyFileSync(backupPath, claudeSettingsPath);
|
|
794
908
|
fs4.unlinkSync(backupPath);
|
|
795
|
-
console.warn(
|
|
909
|
+
console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
|
|
796
910
|
} catch {
|
|
797
|
-
console.error(
|
|
911
|
+
console.error(chalk5.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
|
|
798
912
|
}
|
|
799
913
|
}
|
|
800
914
|
throw ex;
|
|
@@ -806,16 +920,16 @@ var linkClaudeCode = async () => {
|
|
|
806
920
|
}
|
|
807
921
|
}
|
|
808
922
|
console.log(`\u2705 Registered to Claude Code settings.`);
|
|
809
|
-
console.log(
|
|
923
|
+
console.log(chalk5.dim(` ${claudeSettingsPath}`));
|
|
810
924
|
return true;
|
|
811
925
|
} catch (ex) {
|
|
812
|
-
console.error(
|
|
813
|
-
console.log(
|
|
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."));
|
|
814
928
|
return false;
|
|
815
929
|
}
|
|
816
930
|
};
|
|
817
931
|
const patchInstalledPlugins = () => {
|
|
818
|
-
const installPath =
|
|
932
|
+
const installPath = repoPath;
|
|
819
933
|
const installedPluginsPath = path4.join(os2.homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
820
934
|
const pluginKey = `${PLUGIN_NAME}@${MARKET_NAME}`;
|
|
821
935
|
try {
|
|
@@ -839,18 +953,18 @@ var linkClaudeCode = async () => {
|
|
|
839
953
|
}
|
|
840
954
|
];
|
|
841
955
|
fs4.mkdirSync(path4.dirname(installedPluginsPath), { recursive: true });
|
|
842
|
-
fs4.writeFileSync(installedPluginsPath, JSON.stringify(data, null,
|
|
956
|
+
fs4.writeFileSync(installedPluginsPath, JSON.stringify(data, null, 4), "utf-8");
|
|
843
957
|
console.log(`\u2705 Patched installed_plugins.json \u2192 installPath points to source.`);
|
|
844
|
-
console.log(
|
|
958
|
+
console.log(chalk5.dim(` ${installPath}`));
|
|
845
959
|
} catch (ex) {
|
|
846
|
-
console.warn(
|
|
960
|
+
console.warn(chalk5.yellow(` \u26A0 Could not patch installed_plugins.json: ${ex.message}`));
|
|
847
961
|
}
|
|
848
962
|
};
|
|
849
|
-
console.log(
|
|
963
|
+
console.log(chalk5.green(`
|
|
850
964
|
Setting up Claude Code plugin...`));
|
|
851
|
-
console.log(
|
|
852
|
-
const
|
|
853
|
-
if (
|
|
965
|
+
console.log(chalk5.dim(CLAUDE_CODE_DIR));
|
|
966
|
+
const marketplaceOk = buildMarketplace();
|
|
967
|
+
if (marketplaceOk === false) {
|
|
854
968
|
return;
|
|
855
969
|
}
|
|
856
970
|
const settingsOk = registerToClaudeSettings();
|
|
@@ -861,49 +975,154 @@ Setting up Claude Code plugin...`));
|
|
|
861
975
|
configManager.claude_code = { path: CLAUDE_CODE_DIR };
|
|
862
976
|
configManager.save();
|
|
863
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";
|
|
864
1083
|
var linkRooCode = async () => {
|
|
865
1084
|
const repoPath = resolveRepoPath();
|
|
866
1085
|
if (repoPath == null) {
|
|
867
1086
|
return;
|
|
868
1087
|
}
|
|
869
|
-
console.log(
|
|
1088
|
+
console.log(chalk6.green(`
|
|
870
1089
|
Setting up RooCode integration...`));
|
|
871
|
-
console.log(
|
|
1090
|
+
console.log(chalk6.dim(ROO_DIR));
|
|
872
1091
|
const roocodeDirs = AGENT_PROMPT_DIRS["roocode" /* ROOCODE */];
|
|
873
1092
|
const backupExistingRooCodeFiles = async () => {
|
|
874
1093
|
try {
|
|
875
|
-
|
|
1094
|
+
fs5.mkdirSync(ROO_DIR, { recursive: true });
|
|
876
1095
|
const dirsToBackup = [];
|
|
877
1096
|
for (const dir of roocodeDirs) {
|
|
878
|
-
const target =
|
|
879
|
-
if (
|
|
1097
|
+
const target = path5.join(ROO_DIR, dir);
|
|
1098
|
+
if (fs5.existsSync(target) && fs5.lstatSync(target).isSymbolicLink() === false && fs5.readdirSync(target).length > 0) {
|
|
880
1099
|
dirsToBackup.push(dir);
|
|
881
1100
|
}
|
|
882
1101
|
}
|
|
883
1102
|
if (dirsToBackup.length === 0) {
|
|
884
1103
|
return true;
|
|
885
1104
|
}
|
|
886
|
-
console.log(
|
|
1105
|
+
console.log(chalk6.yellow(`
|
|
887
1106
|
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
888
1107
|
for (const dir of dirsToBackup) {
|
|
889
|
-
console.log(
|
|
1108
|
+
console.log(chalk6.dim(` - ${path5.join(ROO_DIR, dir)}`));
|
|
890
1109
|
}
|
|
891
|
-
console.log(
|
|
892
|
-
const ok = await
|
|
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 });
|
|
893
1112
|
if (!ok) {
|
|
894
|
-
console.log(
|
|
1113
|
+
console.log(chalk6.yellow("Skipped RooCode linking."));
|
|
895
1114
|
return false;
|
|
896
1115
|
}
|
|
897
|
-
|
|
1116
|
+
fs5.mkdirSync(ROO_BACKUP_DIR, { recursive: true });
|
|
898
1117
|
for (const dir of dirsToBackup) {
|
|
899
|
-
const src =
|
|
900
|
-
const dest =
|
|
901
|
-
|
|
902
|
-
console.log(
|
|
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}`));
|
|
903
1122
|
}
|
|
904
1123
|
return true;
|
|
905
1124
|
} catch (ex) {
|
|
906
|
-
console.error(
|
|
1125
|
+
console.error(chalk6.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
907
1126
|
return false;
|
|
908
1127
|
}
|
|
909
1128
|
};
|
|
@@ -911,26 +1130,26 @@ Setting up RooCode integration...`));
|
|
|
911
1130
|
try {
|
|
912
1131
|
const linked = [];
|
|
913
1132
|
for (const dir of roocodeDirs) {
|
|
914
|
-
const src =
|
|
915
|
-
const dest =
|
|
1133
|
+
const src = path5.join(repoPath, dir);
|
|
1134
|
+
const dest = path5.join(ROO_DIR, dir);
|
|
916
1135
|
if (await pathExists(src) === false) {
|
|
917
1136
|
continue;
|
|
918
1137
|
}
|
|
919
|
-
if (
|
|
920
|
-
|
|
1138
|
+
if (fs5.existsSync(dest)) {
|
|
1139
|
+
fs5.rmSync(dest, { recursive: true, force: true });
|
|
921
1140
|
}
|
|
922
1141
|
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
923
|
-
|
|
1142
|
+
fs5.symlinkSync(src, dest, symlinkType);
|
|
924
1143
|
linked.push({ dir, src });
|
|
925
1144
|
}
|
|
926
1145
|
for (const { dir, src } of linked) {
|
|
927
1146
|
const isLast = linked[linked.length - 1].dir === dir;
|
|
928
1147
|
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
929
|
-
console.log(
|
|
1148
|
+
console.log(chalk6.dim(` ${branch} `) + chalk6.bold(`${dir}/`) + chalk6.dim(` \u2192 ${src}`) + chalk6.green(" \u2713"));
|
|
930
1149
|
}
|
|
931
1150
|
return true;
|
|
932
1151
|
} catch (ex) {
|
|
933
|
-
console.error(
|
|
1152
|
+
console.error(chalk6.red(`\u274C Failed to create symlinks: ${ex.message}`));
|
|
934
1153
|
return false;
|
|
935
1154
|
}
|
|
936
1155
|
};
|
|
@@ -945,49 +1164,97 @@ Setting up RooCode integration...`));
|
|
|
945
1164
|
configManager.roocode = { path: ROO_DIR, backup_path: ROO_BACKUP_DIR };
|
|
946
1165
|
configManager.save();
|
|
947
1166
|
};
|
|
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
|
+
}
|
|
1177
|
+
}
|
|
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)) {
|
|
1190
|
+
try {
|
|
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";
|
|
948
1215
|
var linkOpenclaw = async () => {
|
|
949
1216
|
const repoPath = resolveRepoPath();
|
|
950
1217
|
if (repoPath == null) {
|
|
951
1218
|
return;
|
|
952
1219
|
}
|
|
953
|
-
console.log(
|
|
1220
|
+
console.log(chalk7.green(`
|
|
954
1221
|
Setting up OpenClaw integration...`));
|
|
955
|
-
console.log(
|
|
1222
|
+
console.log(chalk7.dim(OPENCLAW_DIR));
|
|
956
1223
|
const openclawDirs = AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */];
|
|
957
1224
|
const backupExistingOpenclawFiles = async () => {
|
|
958
1225
|
try {
|
|
959
|
-
|
|
1226
|
+
fs6.mkdirSync(OPENCLAW_DIR, { recursive: true });
|
|
960
1227
|
const dirsToBackup = [];
|
|
961
1228
|
for (const dir of openclawDirs) {
|
|
962
|
-
const target =
|
|
963
|
-
if (
|
|
1229
|
+
const target = path6.join(OPENCLAW_DIR, dir);
|
|
1230
|
+
if (fs6.existsSync(target) && fs6.lstatSync(target).isSymbolicLink() === false && fs6.readdirSync(target).length > 0) {
|
|
964
1231
|
dirsToBackup.push(dir);
|
|
965
1232
|
}
|
|
966
1233
|
}
|
|
967
1234
|
if (dirsToBackup.length === 0) {
|
|
968
1235
|
return true;
|
|
969
1236
|
}
|
|
970
|
-
console.log(
|
|
1237
|
+
console.log(chalk7.yellow(`
|
|
971
1238
|
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
972
1239
|
for (const dir of dirsToBackup) {
|
|
973
|
-
console.log(
|
|
1240
|
+
console.log(chalk7.dim(` - ${path6.join(OPENCLAW_DIR, dir)}`));
|
|
974
1241
|
}
|
|
975
|
-
console.log(
|
|
976
|
-
const ok = await
|
|
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 });
|
|
977
1244
|
if (!ok) {
|
|
978
|
-
console.log(
|
|
1245
|
+
console.log(chalk7.yellow("Skipped OpenClaw linking."));
|
|
979
1246
|
return false;
|
|
980
1247
|
}
|
|
981
|
-
|
|
1248
|
+
fs6.mkdirSync(OPENCLAW_BACKUP_DIR, { recursive: true });
|
|
982
1249
|
for (const dir of dirsToBackup) {
|
|
983
|
-
const src =
|
|
984
|
-
const dest =
|
|
985
|
-
|
|
986
|
-
console.log(
|
|
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}`));
|
|
987
1254
|
}
|
|
988
1255
|
return true;
|
|
989
1256
|
} catch (ex) {
|
|
990
|
-
console.error(
|
|
1257
|
+
console.error(chalk7.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
991
1258
|
return false;
|
|
992
1259
|
}
|
|
993
1260
|
};
|
|
@@ -995,26 +1262,26 @@ Setting up OpenClaw integration...`));
|
|
|
995
1262
|
try {
|
|
996
1263
|
const linked = [];
|
|
997
1264
|
for (const dir of openclawDirs) {
|
|
998
|
-
const src =
|
|
999
|
-
const dest =
|
|
1000
|
-
if (await
|
|
1265
|
+
const src = path6.join(repoPath, dir);
|
|
1266
|
+
const dest = path6.join(OPENCLAW_DIR, dir);
|
|
1267
|
+
if (await pathExists2(src) === false) {
|
|
1001
1268
|
continue;
|
|
1002
1269
|
}
|
|
1003
|
-
if (
|
|
1004
|
-
|
|
1270
|
+
if (fs6.existsSync(dest)) {
|
|
1271
|
+
fs6.rmSync(dest, { recursive: true, force: true });
|
|
1005
1272
|
}
|
|
1006
1273
|
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1007
|
-
|
|
1274
|
+
fs6.symlinkSync(src, dest, symlinkType);
|
|
1008
1275
|
linked.push({ dir, src });
|
|
1009
1276
|
}
|
|
1010
1277
|
for (const { dir, src } of linked) {
|
|
1011
1278
|
const isLast = linked[linked.length - 1].dir === dir;
|
|
1012
1279
|
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
1013
|
-
console.log(
|
|
1280
|
+
console.log(chalk7.dim(` ${branch} `) + chalk7.bold(`${dir}/`) + chalk7.dim(` \u2192 ${src}`) + chalk7.green(" \u2713"));
|
|
1014
1281
|
}
|
|
1015
1282
|
return true;
|
|
1016
1283
|
} catch (ex) {
|
|
1017
|
-
console.error(
|
|
1284
|
+
console.error(chalk7.red(`\u274C Failed to create symlinks: ${ex.message}`));
|
|
1018
1285
|
return false;
|
|
1019
1286
|
}
|
|
1020
1287
|
};
|
|
@@ -1029,49 +1296,97 @@ Setting up OpenClaw integration...`));
|
|
|
1029
1296
|
configManager.openclaw = { path: OPENCLAW_DIR, backup_path: OPENCLAW_BACKUP_DIR };
|
|
1030
1297
|
configManager.save();
|
|
1031
1298
|
};
|
|
1299
|
+
var unlinkOpenclaw = async (force = false) => {
|
|
1300
|
+
if (!force) {
|
|
1301
|
+
const ok = await confirm5({
|
|
1302
|
+
message: `Remove OpenClaw symlinks from ${OPENCLAW_DIR}?`,
|
|
1303
|
+
default: false
|
|
1304
|
+
});
|
|
1305
|
+
if (!ok) {
|
|
1306
|
+
console.log(chalk7.yellow("Cancelled."));
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
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}`));
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if (fs6.existsSync(backupPath)) {
|
|
1322
|
+
try {
|
|
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)) {
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
fs6.renameSync(src, dest);
|
|
1330
|
+
console.log(chalk7.green(" restored") + chalk7.dim(`: ${dir}/`));
|
|
1331
|
+
}
|
|
1332
|
+
fs6.rmdirSync(backupPath);
|
|
1333
|
+
} catch (ex) {
|
|
1334
|
+
console.error(chalk7.red(` \u274C Failed to restore OpenClaw backup: ${ex.message}`));
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
configManager.openclaw = null;
|
|
1338
|
+
configManager.save();
|
|
1339
|
+
};
|
|
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";
|
|
1032
1347
|
var linkAntigravity = async () => {
|
|
1033
1348
|
const repoPath = resolveRepoPath();
|
|
1034
1349
|
if (repoPath == null) {
|
|
1035
1350
|
return;
|
|
1036
1351
|
}
|
|
1037
|
-
console.log(
|
|
1352
|
+
console.log(chalk8.green(`
|
|
1038
1353
|
Setting up Antigravity integration...`));
|
|
1039
|
-
console.log(
|
|
1354
|
+
console.log(chalk8.dim(ANTIGRAVITY_DIR));
|
|
1040
1355
|
const antigravityDirs = AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */];
|
|
1041
1356
|
const backupExistingAntigravityFiles = async () => {
|
|
1042
1357
|
try {
|
|
1043
|
-
|
|
1358
|
+
fs7.mkdirSync(ANTIGRAVITY_DIR, { recursive: true });
|
|
1044
1359
|
const dirsToBackup = [];
|
|
1045
1360
|
for (const dir of antigravityDirs) {
|
|
1046
|
-
const target =
|
|
1047
|
-
if (
|
|
1361
|
+
const target = path7.join(ANTIGRAVITY_DIR, dir);
|
|
1362
|
+
if (fs7.existsSync(target) && fs7.lstatSync(target).isSymbolicLink() === false && fs7.readdirSync(target).length > 0) {
|
|
1048
1363
|
dirsToBackup.push(dir);
|
|
1049
1364
|
}
|
|
1050
1365
|
}
|
|
1051
1366
|
if (dirsToBackup.length === 0) {
|
|
1052
1367
|
return true;
|
|
1053
1368
|
}
|
|
1054
|
-
console.log(
|
|
1369
|
+
console.log(chalk8.yellow(`
|
|
1055
1370
|
\u26A0 The following existing directories will be replaced by symlinks:`));
|
|
1056
1371
|
for (const dir of dirsToBackup) {
|
|
1057
|
-
console.log(
|
|
1372
|
+
console.log(chalk8.dim(` - ${path7.join(ANTIGRAVITY_DIR, dir)}`));
|
|
1058
1373
|
}
|
|
1059
|
-
console.log(
|
|
1060
|
-
const ok = await
|
|
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 });
|
|
1061
1376
|
if (!ok) {
|
|
1062
|
-
console.log(
|
|
1377
|
+
console.log(chalk8.yellow("Skipped Antigravity linking."));
|
|
1063
1378
|
return false;
|
|
1064
1379
|
}
|
|
1065
|
-
|
|
1380
|
+
fs7.mkdirSync(ANTIGRAVITY_BACKUP_DIR, { recursive: true });
|
|
1066
1381
|
for (const dir of antigravityDirs) {
|
|
1067
|
-
const src =
|
|
1068
|
-
const dest =
|
|
1069
|
-
|
|
1070
|
-
console.log(
|
|
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}`));
|
|
1071
1386
|
}
|
|
1072
1387
|
return true;
|
|
1073
1388
|
} catch (ex) {
|
|
1074
|
-
console.error(
|
|
1389
|
+
console.error(chalk8.red(`\u274C Failed to backup existing directories: ${ex.message}`));
|
|
1075
1390
|
return false;
|
|
1076
1391
|
}
|
|
1077
1392
|
};
|
|
@@ -1079,26 +1394,26 @@ Setting up Antigravity integration...`));
|
|
|
1079
1394
|
try {
|
|
1080
1395
|
const linked = [];
|
|
1081
1396
|
for (const dir of antigravityDirs) {
|
|
1082
|
-
const src =
|
|
1083
|
-
const dest =
|
|
1084
|
-
if (await
|
|
1397
|
+
const src = path7.join(repoPath, dir);
|
|
1398
|
+
const dest = path7.join(ANTIGRAVITY_DIR, dir);
|
|
1399
|
+
if (await pathExists3(src) === false) {
|
|
1085
1400
|
continue;
|
|
1086
1401
|
}
|
|
1087
|
-
if (
|
|
1088
|
-
|
|
1402
|
+
if (fs7.existsSync(dest)) {
|
|
1403
|
+
fs7.rmSync(dest, { recursive: true, force: true });
|
|
1089
1404
|
}
|
|
1090
1405
|
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1091
|
-
|
|
1406
|
+
fs7.symlinkSync(src, dest, symlinkType);
|
|
1092
1407
|
linked.push({ dir, src });
|
|
1093
1408
|
}
|
|
1094
1409
|
for (const { dir, src } of linked) {
|
|
1095
1410
|
const isLast = linked[linked.length - 1].dir === dir;
|
|
1096
1411
|
const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
1097
|
-
console.log(
|
|
1412
|
+
console.log(chalk8.dim(` ${branch} `) + chalk8.bold(`${dir}/`) + chalk8.dim(` \u2192 ${src}`) + chalk8.green(" \u2713"));
|
|
1098
1413
|
}
|
|
1099
1414
|
return true;
|
|
1100
1415
|
} catch (ex) {
|
|
1101
|
-
console.error(
|
|
1416
|
+
console.error(chalk8.red(`\u274C Failed to create symlinks: ${ex.message}`));
|
|
1102
1417
|
return false;
|
|
1103
1418
|
}
|
|
1104
1419
|
};
|
|
@@ -1113,403 +1428,412 @@ Setting up Antigravity integration...`));
|
|
|
1113
1428
|
configManager.antigravity = { path: ANTIGRAVITY_DIR, backup_path: ANTIGRAVITY_BACKUP_DIR };
|
|
1114
1429
|
configManager.save();
|
|
1115
1430
|
};
|
|
1116
|
-
var
|
|
1117
|
-
|
|
1118
|
-
|
|
1431
|
+
var unlinkAntigravity = async (force = false) => {
|
|
1432
|
+
if (!force) {
|
|
1433
|
+
const ok = await confirm6({
|
|
1434
|
+
message: `Remove Antigravity symlinks from ${ANTIGRAVITY_DIR}?`,
|
|
1435
|
+
default: false
|
|
1436
|
+
});
|
|
1437
|
+
if (!ok) {
|
|
1438
|
+
console.log(chalk8.yellow("Cancelled."));
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
console.log(chalk8.red(`
|
|
1443
|
+
Removing Antigravity integration...`));
|
|
1444
|
+
console.log(chalk8.dim(ANTIGRAVITY_DIR));
|
|
1445
|
+
const backupPath = configManager.antigravity?.backup_path ?? ANTIGRAVITY_BACKUP_DIR;
|
|
1446
|
+
for (const dir of AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */]) {
|
|
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}`));
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (fs7.existsSync(backupPath)) {
|
|
1454
|
+
try {
|
|
1455
|
+
for (const dir of AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */]) {
|
|
1456
|
+
const src = path7.join(backupPath, dir);
|
|
1457
|
+
const dest = path7.join(ANTIGRAVITY_DIR, dir);
|
|
1458
|
+
if (!fs7.existsSync(src)) {
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
fs7.renameSync(src, dest);
|
|
1462
|
+
console.log(chalk8.green(" restored") + chalk8.dim(`: ${dir}/`));
|
|
1463
|
+
}
|
|
1464
|
+
fs7.rmdirSync(backupPath);
|
|
1465
|
+
} catch (ex) {
|
|
1466
|
+
console.error(chalk8.red(` \u274C Failed to restore Antigravity backup: ${ex.message}`));
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
configManager.antigravity = null;
|
|
1470
|
+
configManager.save();
|
|
1119
1471
|
};
|
|
1120
|
-
|
|
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 () => {
|
|
1121
1484
|
const repoPath = resolveRepoPath();
|
|
1122
1485
|
if (repoPath == null) {
|
|
1123
1486
|
return;
|
|
1124
1487
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1488
|
+
ensureCodexPluginManifest(repoPath);
|
|
1489
|
+
ensureMcpJson(repoPath);
|
|
1490
|
+
ensureAppJson(repoPath);
|
|
1491
|
+
const registerToMarketplace = () => {
|
|
1492
|
+
const marketplacePath = path8.join(CODEX_AGENTS_DIR, "marketplace.json");
|
|
1128
1493
|
try {
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
owner: { name: os2.userInfo().username },
|
|
1134
|
-
metadata: { description: "Managed by set-prompt" },
|
|
1135
|
-
plugins: [{ name: CURSOR_PLUGIN_NAME, source: `./plugins/${CURSOR_LOCAL_DIR}/${CURSOR_PLUGIN_NAME}`, description: "Managed by set-prompt" }]
|
|
1494
|
+
let marketplace = {
|
|
1495
|
+
name: CODEX_MARKETPLACE_NAME,
|
|
1496
|
+
interface: { displayName: "Local Repository" },
|
|
1497
|
+
plugins: []
|
|
1136
1498
|
};
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
name: CURSOR_PLUGIN_NAME,
|
|
1150
|
-
displayName: "set-prompt",
|
|
1151
|
-
version: "1.0.0",
|
|
1152
|
-
description: "Managed by set-prompt"
|
|
1153
|
-
};
|
|
1154
|
-
for (const dir of cursorDirs) {
|
|
1155
|
-
pluginJson[dir] = `./${dir}/`;
|
|
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;
|
|
1510
|
+
}
|
|
1156
1511
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1512
|
+
if (!Array.isArray(marketplace.plugins)) {
|
|
1513
|
+
marketplace.plugins = [];
|
|
1514
|
+
}
|
|
1515
|
+
marketplace.plugins = marketplace.plugins.filter(
|
|
1516
|
+
(p) => p?.name !== PLUGIN_NAME
|
|
1161
1517
|
);
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
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;
|
|
1172
1539
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
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
|
+
}
|
|
1175
1553
|
}
|
|
1176
|
-
|
|
1177
|
-
fs4.symlinkSync(src, dest, symlinkType2);
|
|
1178
|
-
linked.push({ dir, src });
|
|
1554
|
+
throw ex;
|
|
1179
1555
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1556
|
+
if (backupPath !== null) {
|
|
1557
|
+
try {
|
|
1558
|
+
fs8.unlinkSync(backupPath);
|
|
1559
|
+
} catch {
|
|
1560
|
+
}
|
|
1184
1561
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
1188
|
-
fs4.symlinkSync(pluginDir, CURSOR_PLUGIN_DIR, symlinkType);
|
|
1189
|
-
console.log(chalk4.green(`
|
|
1190
|
-
\u2705 Installed to Cursor plugins.`));
|
|
1191
|
-
console.log(chalk4.dim(` ${CURSOR_PLUGIN_DIR}`));
|
|
1562
|
+
console.log(`\u2705 Registered to marketplace.json`);
|
|
1563
|
+
console.log(chalk9.dim(` ${marketplacePath}`));
|
|
1192
1564
|
return true;
|
|
1193
1565
|
} catch (ex) {
|
|
1194
|
-
console.error(
|
|
1566
|
+
console.error(chalk9.red(`\u274C Failed to update marketplace.json: ${ex.message}`));
|
|
1195
1567
|
return false;
|
|
1196
1568
|
}
|
|
1197
1569
|
};
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1570
|
+
const patchPluginCache = () => {
|
|
1571
|
+
const cachePath = path8.join(CODEX_CACHE_DIR, CODEX_MARKETPLACE_NAME, PLUGIN_NAME, "1.0.0");
|
|
1572
|
+
try {
|
|
1573
|
+
fs8.mkdirSync(path8.dirname(cachePath), { recursive: true });
|
|
1574
|
+
if (fs8.existsSync(cachePath)) {
|
|
1575
|
+
fs8.rmSync(cachePath, { recursive: true, force: true });
|
|
1576
|
+
}
|
|
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"));
|
|
1592
|
+
}
|
|
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}`));
|
|
1601
|
+
} catch (ex) {
|
|
1602
|
+
console.warn(chalk9.yellow(` \u26A0 Could not update config.toml: ${ex.message}`));
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
console.log(chalk9.green(`
|
|
1606
|
+
Setting up Codex plugin...`));
|
|
1607
|
+
const marketplaceOk = registerToMarketplace();
|
|
1608
|
+
if (marketplaceOk === false) {
|
|
1203
1609
|
return;
|
|
1204
1610
|
}
|
|
1205
|
-
|
|
1611
|
+
patchPluginCache();
|
|
1612
|
+
enableInConfig();
|
|
1613
|
+
configManager.codex = { path: CODEX_DIR };
|
|
1206
1614
|
configManager.save();
|
|
1207
1615
|
};
|
|
1208
|
-
var
|
|
1616
|
+
var unlinkCodex = async (force = false) => {
|
|
1209
1617
|
if (!force) {
|
|
1210
|
-
const ok = await
|
|
1211
|
-
message: `Remove
|
|
1618
|
+
const ok = await confirm7({
|
|
1619
|
+
message: `Remove Codex plugin and marketplace entries?`,
|
|
1212
1620
|
default: false
|
|
1213
1621
|
});
|
|
1214
1622
|
if (!ok) {
|
|
1215
|
-
console.log(
|
|
1623
|
+
console.log(chalk9.yellow("Cancelled."));
|
|
1216
1624
|
return;
|
|
1217
1625
|
}
|
|
1218
1626
|
}
|
|
1219
|
-
console.log(
|
|
1220
|
-
Removing
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
if (fs4.existsSync(claudeSettingsPath)) {
|
|
1224
|
-
try {
|
|
1225
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1226
|
-
const backupPath = `${claudeSettingsPath}.bak.${timestamp}`;
|
|
1227
|
-
fs4.copyFileSync(claudeSettingsPath, backupPath);
|
|
1228
|
-
const settings = JSON.parse(fs4.readFileSync(claudeSettingsPath, "utf-8"));
|
|
1229
|
-
if (settings?.extraKnownMarketplaces?.[MARKET_NAME] !== void 0) {
|
|
1230
|
-
delete settings.extraKnownMarketplaces[MARKET_NAME];
|
|
1231
|
-
}
|
|
1232
|
-
if (settings?.enabledPlugins?.[`${PLUGIN_NAME}@${MARKET_NAME}`] !== void 0) {
|
|
1233
|
-
delete settings.enabledPlugins[`${PLUGIN_NAME}@${MARKET_NAME}`];
|
|
1234
|
-
}
|
|
1235
|
-
try {
|
|
1236
|
-
fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
1237
|
-
fs4.unlinkSync(backupPath);
|
|
1238
|
-
console.log(chalk4.red(" removed") + chalk4.dim(` set-prompt entries from: ${claudeSettingsPath}`));
|
|
1239
|
-
} catch (ex) {
|
|
1240
|
-
fs4.copyFileSync(backupPath, claudeSettingsPath);
|
|
1241
|
-
fs4.unlinkSync(backupPath);
|
|
1242
|
-
console.warn(chalk4.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
|
|
1243
|
-
}
|
|
1244
|
-
} catch (ex) {
|
|
1245
|
-
console.error(chalk4.red(` \u274C Failed to clean up settings.json: ${ex.message}`));
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
const claudePluginsDir = path4.join(os2.homedir(), ".claude", "plugins");
|
|
1249
|
-
const installedPluginsPath = path4.join(claudePluginsDir, "installed_plugins.json");
|
|
1250
|
-
if (fs4.existsSync(installedPluginsPath)) {
|
|
1627
|
+
console.log(chalk9.red(`
|
|
1628
|
+
Removing Codex plugin...`));
|
|
1629
|
+
const marketplacePath = path8.join(CODEX_AGENTS_DIR, "marketplace.json");
|
|
1630
|
+
if (fs8.existsSync(marketplacePath)) {
|
|
1251
1631
|
try {
|
|
1252
1632
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1253
|
-
const backupPath = `${
|
|
1254
|
-
|
|
1255
|
-
const
|
|
1256
|
-
if (
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
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
|
+
);
|
|
1262
1640
|
}
|
|
1263
1641
|
try {
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
console.log(
|
|
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}`));
|
|
1267
1645
|
} catch (ex) {
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
console.warn(
|
|
1646
|
+
fs8.copyFileSync(backupPath, marketplacePath);
|
|
1647
|
+
fs8.unlinkSync(backupPath);
|
|
1648
|
+
console.warn(chalk9.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
|
|
1271
1649
|
}
|
|
1272
1650
|
} catch (ex) {
|
|
1273
|
-
console.error(
|
|
1651
|
+
console.error(chalk9.red(` \u274C Failed to clean up marketplace.json: ${ex.message}`));
|
|
1274
1652
|
}
|
|
1275
1653
|
}
|
|
1276
|
-
const
|
|
1277
|
-
if (
|
|
1654
|
+
const configPath = path8.join(os3.homedir(), ".codex", "config.toml");
|
|
1655
|
+
if (fs8.existsSync(configPath)) {
|
|
1278
1656
|
try {
|
|
1279
|
-
const
|
|
1280
|
-
const
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
}
|
|
1286
|
-
try {
|
|
1287
|
-
fs4.writeFileSync(knownMarketplacesPath, JSON.stringify(marketplaces, null, 2), "utf-8");
|
|
1288
|
-
fs4.unlinkSync(backupPath);
|
|
1289
|
-
console.log(chalk4.red(" removed") + chalk4.dim(` set-prompt entry from: ${knownMarketplacesPath}`));
|
|
1290
|
-
} catch (ex) {
|
|
1291
|
-
fs4.copyFileSync(backupPath, knownMarketplacesPath);
|
|
1292
|
-
fs4.unlinkSync(backupPath);
|
|
1293
|
-
console.warn(chalk4.yellow(" \u26A0 Write failed \u2014 rolled back known_marketplaces.json."));
|
|
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}`));
|
|
1294
1663
|
}
|
|
1295
1664
|
} catch (ex) {
|
|
1296
|
-
console.error(
|
|
1665
|
+
console.error(chalk9.red(` \u274C Failed to clean up config.toml: ${ex.message}`));
|
|
1297
1666
|
}
|
|
1298
1667
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
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}`));
|
|
1302
1672
|
}
|
|
1303
|
-
configManager.
|
|
1673
|
+
configManager.codex = null;
|
|
1304
1674
|
configManager.save();
|
|
1305
1675
|
};
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
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) {
|
|
1687
|
+
return;
|
|
1316
1688
|
}
|
|
1317
|
-
console.log(
|
|
1318
|
-
|
|
1319
|
-
console.log(
|
|
1320
|
-
const
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
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);
|
|
1326
1698
|
}
|
|
1327
1699
|
}
|
|
1328
|
-
if (
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
if (!fs4.existsSync(src)) {
|
|
1334
|
-
continue;
|
|
1335
|
-
}
|
|
1336
|
-
fs4.renameSync(src, dest);
|
|
1337
|
-
console.log(chalk4.green(" restored") + chalk4.dim(`: ${dir}/`));
|
|
1338
|
-
}
|
|
1339
|
-
fs4.rmdirSync(backupPath);
|
|
1340
|
-
} catch (ex) {
|
|
1341
|
-
console.error(chalk4.red(` \u274C Failed to restore RooCode backup: ${ex.message}`));
|
|
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)}`));
|
|
1342
1705
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
configManager.save();
|
|
1346
|
-
};
|
|
1347
|
-
var unlinkOpenclaw = async (force = false) => {
|
|
1348
|
-
if (!force) {
|
|
1349
|
-
const ok = await confirm3({
|
|
1350
|
-
message: `Remove OpenClaw symlinks from ${OPENCLAW_DIR}?`,
|
|
1351
|
-
default: false
|
|
1352
|
-
});
|
|
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 });
|
|
1353
1708
|
if (!ok) {
|
|
1354
|
-
console.log(
|
|
1709
|
+
console.log(chalk10.yellow("Skipped Cursor linking."));
|
|
1355
1710
|
return;
|
|
1356
1711
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
const target = path4.join(OPENCLAW_DIR, dir);
|
|
1364
|
-
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink()) {
|
|
1365
|
-
fs4.unlinkSync(target);
|
|
1366
|
-
console.log(chalk4.red(" removed symlink") + chalk4.dim(`: ${target}`));
|
|
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}`));
|
|
1367
1718
|
}
|
|
1368
1719
|
}
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
}
|
|
1377
|
-
fs4.renameSync(src, dest);
|
|
1378
|
-
console.log(chalk4.green(" restored") + chalk4.dim(`: ${dir}/`));
|
|
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;
|
|
1379
1727
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
configManager.save();
|
|
1387
|
-
};
|
|
1388
|
-
var unlinkAntigravity = async (force = false) => {
|
|
1389
|
-
if (!force) {
|
|
1390
|
-
const ok = await confirm3({
|
|
1391
|
-
message: `Remove Antigravity symlinks from ${ANTIGRAVITY_DIR}?`,
|
|
1392
|
-
default: false
|
|
1393
|
-
});
|
|
1394
|
-
if (!ok) {
|
|
1395
|
-
console.log(chalk4.yellow("Cancelled."));
|
|
1396
|
-
return;
|
|
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 });
|
|
1397
1734
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
Removing Antigravity integration...`));
|
|
1401
|
-
console.log(chalk4.dim(ANTIGRAVITY_DIR));
|
|
1402
|
-
const backupPath = configManager.antigravity?.backup_path ?? ANTIGRAVITY_BACKUP_DIR;
|
|
1403
|
-
for (const dir of AGENT_PROMPT_DIRS["antigravity" /* ANTIGRAVITY */]) {
|
|
1404
|
-
const target = path4.join(ANTIGRAVITY_DIR, dir);
|
|
1405
|
-
if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink()) {
|
|
1406
|
-
fs4.unlinkSync(target);
|
|
1407
|
-
console.log(chalk4.red(" removed symlink") + chalk4.dim(`: ${target}`));
|
|
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"));
|
|
1408
1737
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
}
|
|
1418
|
-
fs4.renameSync(src, dest);
|
|
1419
|
-
console.log(chalk4.green(" restored") + chalk4.dim(`: ${dir}/`));
|
|
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}`));
|
|
1420
1746
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
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"));
|
|
1424
1752
|
}
|
|
1753
|
+
} catch (ex) {
|
|
1754
|
+
console.error(chalk10.red(`\u274C Failed to set up Cursor: ${ex.message}`));
|
|
1755
|
+
return;
|
|
1425
1756
|
}
|
|
1426
|
-
configManager.
|
|
1757
|
+
configManager.cursor = { path: CURSOR_DIR, backup_path: CURSOR_BACKUP_DIR };
|
|
1427
1758
|
configManager.save();
|
|
1428
1759
|
};
|
|
1429
|
-
var
|
|
1760
|
+
var unlinkCursor = async (force = false) => {
|
|
1430
1761
|
if (!force) {
|
|
1431
|
-
const ok = await
|
|
1432
|
-
message: `Remove
|
|
1762
|
+
const ok = await confirm8({
|
|
1763
|
+
message: `Remove Cursor symlinks from ${CURSOR_DIR}?`,
|
|
1433
1764
|
default: false
|
|
1434
1765
|
});
|
|
1435
1766
|
if (!ok) {
|
|
1436
|
-
console.log(
|
|
1767
|
+
console.log(chalk10.yellow("Cancelled."));
|
|
1437
1768
|
return;
|
|
1438
1769
|
}
|
|
1439
1770
|
}
|
|
1440
|
-
console.log(
|
|
1441
|
-
Removing
|
|
1442
|
-
console.log(
|
|
1443
|
-
const backupPath = configManager.
|
|
1444
|
-
for (const dir of AGENT_PROMPT_DIRS["
|
|
1445
|
-
const target =
|
|
1446
|
-
if (
|
|
1447
|
-
|
|
1448
|
-
console.log(
|
|
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}`));
|
|
1449
1780
|
}
|
|
1450
1781
|
}
|
|
1451
|
-
|
|
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)) {
|
|
1452
1788
|
try {
|
|
1453
|
-
for (const dir of AGENT_PROMPT_DIRS["
|
|
1454
|
-
const src =
|
|
1455
|
-
const dest =
|
|
1456
|
-
if (!
|
|
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)) {
|
|
1457
1793
|
continue;
|
|
1458
1794
|
}
|
|
1459
|
-
|
|
1460
|
-
console.log(
|
|
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`));
|
|
1461
1802
|
}
|
|
1462
|
-
|
|
1803
|
+
fs9.rmSync(backupPath, { recursive: true, force: true });
|
|
1463
1804
|
} catch (ex) {
|
|
1464
|
-
console.error(
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
configManager.codex = null;
|
|
1468
|
-
configManager.save();
|
|
1469
|
-
};
|
|
1470
|
-
var unlinkCursor = async (force = false) => {
|
|
1471
|
-
if (!force) {
|
|
1472
|
-
const ok = await confirm3({
|
|
1473
|
-
message: `Remove Cursor plugin dir (${CURSOR_PLUGIN_DIR}) and plugin structure?`,
|
|
1474
|
-
default: false
|
|
1475
|
-
});
|
|
1476
|
-
if (!ok) {
|
|
1477
|
-
console.log(chalk4.yellow("Cancelled."));
|
|
1478
|
-
return;
|
|
1805
|
+
console.error(chalk10.red(` \u274C Failed to restore Cursor backup: ${ex.message}`));
|
|
1479
1806
|
}
|
|
1480
1807
|
}
|
|
1481
|
-
console.log(chalk4.red(`
|
|
1482
|
-
Removing Cursor plugin...`));
|
|
1483
|
-
console.log(chalk4.dim(CURSOR_PLUGIN_DIR));
|
|
1484
|
-
fs4.rmSync(CURSOR_PLUGIN_DIR, { recursive: true, force: true });
|
|
1485
|
-
console.log(chalk4.red(" removed") + chalk4.dim(`: ${CURSOR_PLUGIN_DIR}`));
|
|
1486
|
-
if (fs4.existsSync(CURSOR_DIR)) {
|
|
1487
|
-
fs4.rmSync(CURSOR_DIR, { recursive: true, force: true });
|
|
1488
|
-
console.log(chalk4.red(" removed") + chalk4.dim(`: ${CURSOR_DIR}`));
|
|
1489
|
-
}
|
|
1490
1808
|
configManager.cursor = null;
|
|
1491
1809
|
configManager.save();
|
|
1492
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
|
+
};
|
|
1493
1829
|
var linkCommand = async (tool) => {
|
|
1494
1830
|
if (tool != null) {
|
|
1495
1831
|
const known = ALL_AGENTS.some((a) => a.value === tool);
|
|
1496
1832
|
if (!known) {
|
|
1497
|
-
console.log(
|
|
1833
|
+
console.log(chalk11.red(`Unknown vendor: ${tool}`));
|
|
1498
1834
|
process.exit(1);
|
|
1499
1835
|
}
|
|
1500
|
-
|
|
1501
|
-
await linkClaudeCode();
|
|
1502
|
-
} else if (tool === "roocode" /* ROOCODE */) {
|
|
1503
|
-
await linkRooCode();
|
|
1504
|
-
} else if (tool === "openclaw" /* OPENCLAW */) {
|
|
1505
|
-
await linkOpenclaw();
|
|
1506
|
-
} else if (tool === "codex" /* CODEX */) {
|
|
1507
|
-
await linkCodex();
|
|
1508
|
-
} else if (tool === "antigravity" /* ANTIGRAVITY */) {
|
|
1509
|
-
await linkAntigravity();
|
|
1510
|
-
} else if (tool === "cursor" /* CURSOR */) {
|
|
1511
|
-
await linkCursor();
|
|
1512
|
-
}
|
|
1836
|
+
await LINK_MAP[tool]();
|
|
1513
1837
|
return;
|
|
1514
1838
|
}
|
|
1515
1839
|
const prevLinked = {
|
|
@@ -1523,7 +1847,7 @@ var linkCommand = async (tool) => {
|
|
|
1523
1847
|
const selected = await checkbox({
|
|
1524
1848
|
message: "Which AI agent do you want to integrate?",
|
|
1525
1849
|
choices: ALL_AGENTS.map((a) => ({
|
|
1526
|
-
name: prevLinked[a.value] ? `${a.name} ${
|
|
1850
|
+
name: prevLinked[a.value] ? `${a.name} ${chalk11.dim("(applied)")}` : a.name,
|
|
1527
1851
|
value: a.value,
|
|
1528
1852
|
checked: prevLinked[a.value]
|
|
1529
1853
|
}))
|
|
@@ -1532,10 +1856,10 @@ var linkCommand = async (tool) => {
|
|
|
1532
1856
|
const toUnlink = ALL_AGENTS.filter((a) => prevLinked[a.value] && !selected.includes(a.value));
|
|
1533
1857
|
console.log();
|
|
1534
1858
|
if (toLink.length > 0) {
|
|
1535
|
-
console.log(
|
|
1859
|
+
console.log(chalk11.green(" Link ") + chalk11.dim("\u2192 ") + toLink.map((a) => chalk11.bold(a.name)).join(chalk11.dim(", ")));
|
|
1536
1860
|
}
|
|
1537
1861
|
if (toUnlink.length > 0) {
|
|
1538
|
-
console.log(
|
|
1862
|
+
console.log(chalk11.red(" Unlink ") + chalk11.dim("\u2192 ") + toUnlink.map((a) => chalk11.bold(a.name)).join(chalk11.dim(", ")));
|
|
1539
1863
|
}
|
|
1540
1864
|
console.log();
|
|
1541
1865
|
if (toLink.length === 0 && toUnlink.length === 0) {
|
|
@@ -1545,47 +1869,23 @@ var linkCommand = async (tool) => {
|
|
|
1545
1869
|
const was = prevLinked[a.value];
|
|
1546
1870
|
const now = selected.includes(a.value);
|
|
1547
1871
|
if (!was && now) {
|
|
1548
|
-
|
|
1549
|
-
await linkClaudeCode();
|
|
1550
|
-
} else if (a.value === "roocode" /* ROOCODE */) {
|
|
1551
|
-
await linkRooCode();
|
|
1552
|
-
} else if (a.value === "openclaw" /* OPENCLAW */) {
|
|
1553
|
-
await linkOpenclaw();
|
|
1554
|
-
} else if (a.value === "codex" /* CODEX */) {
|
|
1555
|
-
await linkCodex();
|
|
1556
|
-
} else if (a.value === "antigravity" /* ANTIGRAVITY */) {
|
|
1557
|
-
await linkAntigravity();
|
|
1558
|
-
} else if (a.value === "cursor" /* CURSOR */) {
|
|
1559
|
-
await linkCursor();
|
|
1560
|
-
}
|
|
1872
|
+
await LINK_MAP[a.value]();
|
|
1561
1873
|
}
|
|
1562
1874
|
if (was && !now) {
|
|
1563
|
-
|
|
1564
|
-
await unlinkClaudeCode(true);
|
|
1565
|
-
} else if (a.value === "roocode" /* ROOCODE */) {
|
|
1566
|
-
await unlinkRooCode(true);
|
|
1567
|
-
} else if (a.value === "openclaw" /* OPENCLAW */) {
|
|
1568
|
-
await unlinkOpenclaw(true);
|
|
1569
|
-
} else if (a.value === "codex" /* CODEX */) {
|
|
1570
|
-
await unlinkCodex(true);
|
|
1571
|
-
} else if (a.value === "antigravity" /* ANTIGRAVITY */) {
|
|
1572
|
-
await unlinkAntigravity(true);
|
|
1573
|
-
} else if (a.value === "cursor" /* CURSOR */) {
|
|
1574
|
-
await unlinkCursor(true);
|
|
1575
|
-
}
|
|
1875
|
+
await UNLINK_MAP[a.value](true);
|
|
1576
1876
|
}
|
|
1577
1877
|
}
|
|
1578
1878
|
};
|
|
1579
1879
|
|
|
1580
1880
|
// src/commands/uninstall-command.ts
|
|
1581
|
-
import
|
|
1582
|
-
import
|
|
1583
|
-
import { confirm as
|
|
1881
|
+
import fs10 from "fs";
|
|
1882
|
+
import chalk12 from "chalk";
|
|
1883
|
+
import { confirm as confirm9 } from "@inquirer/prompts";
|
|
1584
1884
|
var uninstallCommand = async () => {
|
|
1585
1885
|
const targets = [
|
|
1586
|
-
{ label: `Config file ${
|
|
1587
|
-
{ label: `Home dir ${
|
|
1588
|
-
].filter((t) =>
|
|
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));
|
|
1589
1889
|
const hasClaudeCode = configManager.isClaudeCodeEnabled();
|
|
1590
1890
|
const hasRooCode = configManager.isRooCodeEnabled();
|
|
1591
1891
|
const hasOpenclaw = configManager.isOpenclawEnabled();
|
|
@@ -1593,32 +1893,32 @@ var uninstallCommand = async () => {
|
|
|
1593
1893
|
const hasCodex = configManager.isCodexEnabled();
|
|
1594
1894
|
const hasCursor = configManager.isCursorEnabled();
|
|
1595
1895
|
if (targets.length === 0 && !hasClaudeCode && !hasRooCode && !hasOpenclaw && !hasAntigravity && !hasCodex && !hasCursor) {
|
|
1596
|
-
console.log(
|
|
1896
|
+
console.log(chalk12.yellow("Nothing to remove."));
|
|
1597
1897
|
return;
|
|
1598
1898
|
}
|
|
1599
|
-
console.log(
|
|
1899
|
+
console.log(chalk12.red("\nThe following will be removed:"));
|
|
1600
1900
|
targets.forEach((t) => console.log(` ${t.label}`));
|
|
1601
1901
|
if (hasClaudeCode) {
|
|
1602
|
-
console.log(` Claude Code plugin dir ${
|
|
1902
|
+
console.log(` Claude Code plugin dir ${chalk12.dim(CLAUDE_CODE_DIR)}`);
|
|
1603
1903
|
}
|
|
1604
1904
|
if (hasRooCode) {
|
|
1605
|
-
console.log(` RooCode symlinks ${
|
|
1905
|
+
console.log(` RooCode symlinks ${chalk12.dim("(backup will be restored)")}`);
|
|
1606
1906
|
}
|
|
1607
1907
|
if (hasOpenclaw) {
|
|
1608
|
-
console.log(` OpenClaw symlinks ${
|
|
1908
|
+
console.log(` OpenClaw symlinks ${chalk12.dim("(backup will be restored)")}`);
|
|
1609
1909
|
}
|
|
1610
1910
|
if (hasAntigravity) {
|
|
1611
|
-
console.log(` Antigravity symlinks ${
|
|
1911
|
+
console.log(` Antigravity symlinks ${chalk12.dim("(backup will be restored)")}`);
|
|
1612
1912
|
}
|
|
1613
1913
|
if (hasCodex) {
|
|
1614
|
-
console.log(` Codex symlinks ${
|
|
1914
|
+
console.log(` Codex symlinks ${chalk12.dim("(backup will be restored)")}`);
|
|
1615
1915
|
}
|
|
1616
1916
|
if (hasCursor) {
|
|
1617
|
-
console.log(` Cursor plugin dir ${
|
|
1917
|
+
console.log(` Cursor plugin dir ${chalk12.dim("(symlink will be removed)")}`);
|
|
1618
1918
|
}
|
|
1619
|
-
const ok = await
|
|
1919
|
+
const ok = await confirm9({ message: "Proceed?", default: false });
|
|
1620
1920
|
if (!ok) {
|
|
1621
|
-
console.log(
|
|
1921
|
+
console.log(chalk12.yellow("Cancelled."));
|
|
1622
1922
|
return;
|
|
1623
1923
|
}
|
|
1624
1924
|
if (hasClaudeCode) {
|
|
@@ -1640,26 +1940,26 @@ var uninstallCommand = async () => {
|
|
|
1640
1940
|
await unlinkCursor(true);
|
|
1641
1941
|
}
|
|
1642
1942
|
for (const t of targets) {
|
|
1643
|
-
|
|
1644
|
-
console.log(
|
|
1943
|
+
fs10.rmSync(t.path, { recursive: true, force: true });
|
|
1944
|
+
console.log(chalk12.dim(` removed: ${t.path}`));
|
|
1645
1945
|
}
|
|
1646
|
-
console.log(
|
|
1946
|
+
console.log(chalk12.green("\nUninstalled."));
|
|
1647
1947
|
};
|
|
1648
1948
|
|
|
1649
1949
|
// src/commands/status-command.ts
|
|
1650
|
-
import
|
|
1950
|
+
import chalk13 from "chalk";
|
|
1651
1951
|
var statusCommand = () => {
|
|
1652
1952
|
if (configManager.repo_path == null) {
|
|
1653
|
-
console.log(
|
|
1654
|
-
console.log(
|
|
1953
|
+
console.log(chalk13.yellow("\u274C No repo installed."));
|
|
1954
|
+
console.log(chalk13.dim(` Run: set-prompt install <repo-url>`));
|
|
1655
1955
|
return;
|
|
1656
1956
|
}
|
|
1657
|
-
console.log(
|
|
1658
|
-
console.log(`${TAB}path ${
|
|
1957
|
+
console.log(chalk13.bold("\nRepo"));
|
|
1958
|
+
console.log(`${TAB}path ${chalk13.cyan(configManager.repo_path)}`);
|
|
1659
1959
|
if (configManager.remote_url != null) {
|
|
1660
|
-
console.log(`${TAB}remote ${
|
|
1960
|
+
console.log(`${TAB}remote ${chalk13.dim(configManager.remote_url)}`);
|
|
1661
1961
|
}
|
|
1662
|
-
console.log(
|
|
1962
|
+
console.log(chalk13.bold("\nLinked agents"));
|
|
1663
1963
|
for (const agent of ALL_AGENTS) {
|
|
1664
1964
|
let linked = false;
|
|
1665
1965
|
let agentPath = null;
|
|
@@ -1679,8 +1979,8 @@ var statusCommand = () => {
|
|
|
1679
1979
|
linked = configManager.isAntigravityEnabled();
|
|
1680
1980
|
agentPath = configManager.antigravity?.path;
|
|
1681
1981
|
}
|
|
1682
|
-
const label = linked ?
|
|
1683
|
-
const pathStr = linked && agentPath ?
|
|
1982
|
+
const label = linked ? chalk13.green("linked") : chalk13.dim("not linked");
|
|
1983
|
+
const pathStr = linked && agentPath ? chalk13.dim(` \u2192 ${agentPath}`) : "";
|
|
1684
1984
|
console.log(`${TAB}${agent.name.padEnd(12)} ${label}${pathStr}`);
|
|
1685
1985
|
}
|
|
1686
1986
|
console.log("");
|
|
@@ -1688,69 +1988,69 @@ var statusCommand = () => {
|
|
|
1688
1988
|
|
|
1689
1989
|
// src/commands/update-command.ts
|
|
1690
1990
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1691
|
-
import
|
|
1991
|
+
import chalk14 from "chalk";
|
|
1692
1992
|
var updateCommand = () => {
|
|
1693
1993
|
const repoPath = configManager.repo_path;
|
|
1694
1994
|
if (repoPath == null) {
|
|
1695
|
-
console.error(
|
|
1696
|
-
console.log(
|
|
1995
|
+
console.error(chalk14.red("\u274C No repo installed."));
|
|
1996
|
+
console.log(chalk14.yellow("Run: set-prompt install <git-url>"));
|
|
1697
1997
|
return;
|
|
1698
1998
|
}
|
|
1699
1999
|
if (configManager.remote_url == null) {
|
|
1700
|
-
console.error(
|
|
2000
|
+
console.error(chalk14.red("\u274C No remote URL registered. Cannot update."));
|
|
1701
2001
|
return;
|
|
1702
2002
|
}
|
|
1703
|
-
console.log(
|
|
1704
|
-
console.log(
|
|
2003
|
+
console.log(chalk14.green("\nUpdating prompt repo..."));
|
|
2004
|
+
console.log(chalk14.dim(repoPath));
|
|
1705
2005
|
const fetch = spawnSync2("git", ["fetch"], { cwd: repoPath, stdio: "inherit" });
|
|
1706
2006
|
if (fetch.status !== 0) {
|
|
1707
|
-
console.error(
|
|
2007
|
+
console.error(chalk14.red("\u274C git fetch failed."));
|
|
1708
2008
|
return;
|
|
1709
2009
|
}
|
|
1710
2010
|
const pull = spawnSync2("git", ["pull"], { cwd: repoPath, stdio: "inherit" });
|
|
1711
2011
|
if (pull.status !== 0) {
|
|
1712
|
-
console.error(
|
|
2012
|
+
console.error(chalk14.red("\u274C git pull failed."));
|
|
1713
2013
|
return;
|
|
1714
2014
|
}
|
|
1715
|
-
console.log(
|
|
2015
|
+
console.log(chalk14.green("\u2705 Repo updated."));
|
|
1716
2016
|
};
|
|
1717
2017
|
|
|
1718
2018
|
// src/index.ts
|
|
1719
2019
|
process.on("SIGINT", () => {
|
|
1720
|
-
console.log(
|
|
2020
|
+
console.log(chalk15.yellow("\nCancelled."));
|
|
1721
2021
|
process.exit(0);
|
|
1722
2022
|
});
|
|
1723
2023
|
process.on("unhandledRejection", (reason) => {
|
|
1724
2024
|
if (reason instanceof Error && reason.name === "ExitPromptError") {
|
|
1725
|
-
console.log(
|
|
2025
|
+
console.log(chalk15.yellow("\nCancelled."));
|
|
1726
2026
|
process.exit(0);
|
|
1727
2027
|
}
|
|
1728
2028
|
throw reason;
|
|
1729
2029
|
});
|
|
1730
|
-
var __dirname =
|
|
1731
|
-
var pkg = JSON.parse(
|
|
2030
|
+
var __dirname = path10.dirname(fileURLToPath(import.meta.url));
|
|
2031
|
+
var pkg = JSON.parse(fs11.readFileSync(path10.join(__dirname, "../package.json"), "utf-8"));
|
|
1732
2032
|
configManager.init();
|
|
1733
2033
|
var program = new Command();
|
|
1734
|
-
var banner =
|
|
2034
|
+
var banner = chalk15.cyan(figlet.textSync("Set-Prompt", { horizontalLayout: "full" }));
|
|
1735
2035
|
program.name("set-prompt").description(pkg.description).version(pkg.version).addHelpText("beforeAll", banner + "\n").action(() => {
|
|
1736
2036
|
program.help();
|
|
1737
2037
|
});
|
|
1738
|
-
program.command("install").description(`\u{1F4E6} Clone a ${
|
|
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) => {
|
|
1739
2039
|
await installCommand(source);
|
|
1740
2040
|
});
|
|
1741
|
-
program.command("link").description(`\u{1F517} Symlink your prompt repo into an ${
|
|
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) => {
|
|
1742
2042
|
await linkCommand(agent);
|
|
1743
2043
|
});
|
|
1744
|
-
program.command("scaffold").description(`\u{1F6E0}\uFE0F Verify and create ${
|
|
1745
|
-
await scaffoldCommand(localPath
|
|
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);
|
|
1746
2046
|
});
|
|
1747
|
-
program.command("status").description(`\u{1F4CB} Show registered ${
|
|
2047
|
+
program.command("status").description(`\u{1F4CB} Show registered ${chalk15.cyan("repo")} and which ${chalk15.cyan("agents")} are linked`).action(() => {
|
|
1748
2048
|
statusCommand();
|
|
1749
2049
|
});
|
|
1750
|
-
program.command("update").description(`\u{1F504} Fetch and pull the latest changes from the ${
|
|
2050
|
+
program.command("update").description(`\u{1F504} Fetch and pull the latest changes from the ${chalk15.cyan("remote repo")}`).action(() => {
|
|
1751
2051
|
updateCommand();
|
|
1752
2052
|
});
|
|
1753
|
-
program.command("uninstall").description(`\u{1F5D1}\uFE0F Remove all set-prompt data ${
|
|
2053
|
+
program.command("uninstall").description(`\u{1F5D1}\uFE0F Remove all set-prompt data ${chalk15.dim("(~/.set-prompt/, plugin dirs, settings entries)")}`).action(async () => {
|
|
1754
2054
|
await uninstallCommand();
|
|
1755
2055
|
});
|
|
1756
2056
|
program.parse(process.argv);
|