set-prompt 0.0.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1244 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+ import chalk8 from "chalk";
6
+ import figlet from "figlet";
7
+ import fs6 from "fs";
8
+ import path6 from "path";
9
+ import { fileURLToPath } from "url";
10
+
11
+ // src/commands/install-command.ts
12
+ import fs3 from "fs";
13
+ import path3 from "path";
14
+ import { spawnSync } from "child_process";
15
+ import chalk3 from "chalk";
16
+ import { confirm as confirm2 } from "@inquirer/prompts";
17
+
18
+ // src/_defs/index.ts
19
+ import path from "path";
20
+ import os from "os";
21
+ var TAB = ` `;
22
+ var HOME_DIR = path.join(os.homedir(), ".set-prompt");
23
+ var CONFIG_PATH = path.join(HOME_DIR, "config.json");
24
+ var REPO_DIR = path.join(HOME_DIR, "repo");
25
+ var CLAUDE_CODE_DIR = path.join(HOME_DIR, "claude-code");
26
+ var ROO_DIR = path.join(os.homedir(), ".roo");
27
+ var ROO_BACKUP_DIR = path.join(ROO_DIR, "SET_PROMPT_BACKUP");
28
+ var OPENCLAW_DIR = path.join(os.homedir(), ".openclaw", "workspace");
29
+ var OPENCLAW_BACKUP_DIR = path.join(OPENCLAW_DIR, "SET_PROMPT_BACKUP");
30
+ var PROMPT_DIR_NAMES = ["skills", "commands", "hooks", "agents"];
31
+ var AGENT_PROMPT_DIRS = {
32
+ ["claudecode" /* CLAUDECODE */]: ["skills", "commands", "hooks", "agents"],
33
+ ["roocode" /* ROOCODE */]: ["skills", "commands"],
34
+ ["openclaw" /* OPENCLAW */]: ["skills"],
35
+ ["codex" /* CODEX */]: ["skills", "commands"],
36
+ ["antigravity" /* ANTIGRAVITY */]: ["skills", "commands"]
37
+ };
38
+ var ALL_AGENTS = [
39
+ { name: "Claude Code", value: "claudecode" /* CLAUDECODE */ },
40
+ { name: "RooCode", value: "roocode" /* ROOCODE */ },
41
+ { name: "OpenClaw", value: "openclaw" /* OPENCLAW */ },
42
+ { name: "Codex", value: "codex" /* CODEX */ },
43
+ { name: "Antigravity", value: "antigravity" /* ANTIGRAVITY */ }
44
+ ];
45
+
46
+ // src/_libs/config.ts
47
+ import fs from "fs";
48
+ import chalk from "chalk";
49
+
50
+ // src/_types/index.ts
51
+ import { z } from "zod";
52
+ var ClaudeCodeConfigSchema = z.object({
53
+ path: z.string().nullable()
54
+ });
55
+ var RoocodeConfigSchema = z.object({
56
+ path: z.string().nullable(),
57
+ // ~/.roo
58
+ backup_path: z.string().nullish().optional()
59
+ // ~/.roo/SET_PROMPT_BACKUP
60
+ });
61
+ var OpenclawConfigSchema = z.object({
62
+ path: z.string().nullable(),
63
+ backup_path: z.string().nullish().optional()
64
+ });
65
+ var CodexConfigSchema = z.object({
66
+ path: z.string().nullable()
67
+ });
68
+ var AntigravityConfigSchema = z.object({
69
+ path: z.string().nullable()
70
+ });
71
+ var GlobalConfigSchema = z.object({
72
+ repo_path: z.string(),
73
+ remote_url: z.string().nullable(),
74
+ claude_code: ClaudeCodeConfigSchema.nullable(),
75
+ roocode: RoocodeConfigSchema.nullable(),
76
+ openclaw: OpenclawConfigSchema.nullable(),
77
+ codex: CodexConfigSchema.nullish().optional(),
78
+ antigravity: AntigravityConfigSchema.nullish().optional()
79
+ });
80
+
81
+ // src/_libs/config.ts
82
+ var ConfigManager = class {
83
+ constructor() {
84
+ this._repo_path = null;
85
+ this._remote_url = null;
86
+ this._claude_code = null;
87
+ this._roocode = null;
88
+ this._openclaw = null;
89
+ this._codex = null;
90
+ this._antigravity = null;
91
+ }
92
+ get repo_path() {
93
+ return this._repo_path;
94
+ }
95
+ get remote_url() {
96
+ return this._remote_url;
97
+ }
98
+ get claude_code() {
99
+ return this._claude_code;
100
+ }
101
+ get roocode() {
102
+ return this._roocode;
103
+ }
104
+ get openclaw() {
105
+ return this._openclaw;
106
+ }
107
+ get codex() {
108
+ return this._codex;
109
+ }
110
+ get antigravity() {
111
+ return this._antigravity;
112
+ }
113
+ set repo_path(v) {
114
+ this._repo_path = v;
115
+ }
116
+ set remote_url(v) {
117
+ this._remote_url = v;
118
+ }
119
+ set claude_code(v) {
120
+ this._claude_code = v;
121
+ }
122
+ set roocode(v) {
123
+ this._roocode = v;
124
+ }
125
+ set openclaw(v) {
126
+ this._openclaw = v;
127
+ }
128
+ set codex(v) {
129
+ this._codex = v;
130
+ }
131
+ set antigravity(v) {
132
+ this._antigravity = v;
133
+ }
134
+ init() {
135
+ this._loadFromDisk();
136
+ if (this._repo_path != null) {
137
+ console.log(chalk.dim(`Config loaded from ${CONFIG_PATH}`));
138
+ }
139
+ }
140
+ save() {
141
+ if (this._repo_path == null) {
142
+ console.error(chalk.red("Cannot save config: repo_path is not set."));
143
+ return false;
144
+ }
145
+ try {
146
+ fs.mkdirSync(HOME_DIR, { recursive: true });
147
+ const configStr = JSON.stringify({
148
+ repo_path: this._repo_path,
149
+ remote_url: this._remote_url,
150
+ claude_code: this._claude_code,
151
+ roocode: this._roocode,
152
+ openclaw: this._openclaw,
153
+ codex: this._codex,
154
+ antigravity: this._antigravity
155
+ }, null, 4);
156
+ fs.writeFileSync(CONFIG_PATH, configStr, "utf-8");
157
+ console.log(chalk.green(`Config saved`) + chalk.dim(` \u2192 ${CONFIG_PATH}`));
158
+ return true;
159
+ } catch (ex) {
160
+ console.error(chalk.red(`Failed to save config at '${CONFIG_PATH}'`), ex.message);
161
+ return false;
162
+ }
163
+ }
164
+ reload() {
165
+ this._loadFromDisk();
166
+ }
167
+ exists() {
168
+ return fs.existsSync(CONFIG_PATH);
169
+ }
170
+ isRepoSet() {
171
+ return this._repo_path != null;
172
+ }
173
+ isClaudeCodeEnabled() {
174
+ return this._claude_code != null;
175
+ }
176
+ isRooCodeEnabled() {
177
+ return this._roocode != null;
178
+ }
179
+ isOpenclawEnabled() {
180
+ return this._openclaw != null;
181
+ }
182
+ isCodexEnabled() {
183
+ return this._codex != null;
184
+ }
185
+ isAntigravityEnabled() {
186
+ return this._antigravity != null;
187
+ }
188
+ _assign(config) {
189
+ this._repo_path = config.repo_path;
190
+ this._remote_url = config.remote_url;
191
+ this._claude_code = config.claude_code;
192
+ this._roocode = config.roocode;
193
+ this._openclaw = config.openclaw;
194
+ this._codex = config.codex ?? null;
195
+ this._antigravity = config.antigravity ?? null;
196
+ }
197
+ _loadFromDisk() {
198
+ if (fs.existsSync(CONFIG_PATH) === false) {
199
+ return;
200
+ }
201
+ try {
202
+ const textData = fs.readFileSync(CONFIG_PATH, "utf-8");
203
+ const json = JSON.parse(textData);
204
+ const config = GlobalConfigSchema.parse(json);
205
+ this._assign(config);
206
+ } catch (ex) {
207
+ console.error(chalk.red(`Failed to parse config at '${CONFIG_PATH}'`), ex.message);
208
+ }
209
+ }
210
+ };
211
+ var configManager = new ConfigManager();
212
+
213
+ // src/_libs/index.ts
214
+ 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://"));
215
+
216
+ // src/commands/scaffold-command.ts
217
+ import fs2 from "fs";
218
+ import path2 from "path";
219
+ import chalk2 from "chalk";
220
+ import { confirm } from "@inquirer/prompts";
221
+
222
+ // src/_libs/templates.ts
223
+ var SET_PROMPT_GUIDE = `# Set Prompt Repository Guide
224
+
225
+ > Managed by [set-prompt](https://github.com/alkemic-studio/set-prompt)
226
+
227
+ ## Structure
228
+
229
+ \`\`\`
230
+ \u251C\u2500\u2500 set-prompt.toml # Repository configuration
231
+ \u251C\u2500\u2500 skills/
232
+ \u2502 \u2514\u2500\u2500 <skill-name>/
233
+ \u2502 \u251C\u2500\u2500 SKILL.md # Platform-specific frontmatter + prompt content
234
+ \u2502 \u2514\u2500\u2500 ... # Supporting files (scripts, configs, etc.)
235
+ \u251C\u2500\u2500 commands/
236
+ \u2502 \u2514\u2500\u2500 <command-name>/
237
+ \u2502 \u251C\u2500\u2500 COMMAND.md # Platform-specific frontmatter + prompt content
238
+ \u2502 \u2514\u2500\u2500 ... # Supporting files
239
+ \u251C\u2500\u2500 hooks/ # Lifecycle shell hooks
240
+ \u2514\u2500\u2500 agents/ # Agent definitions (Claude Code)
241
+ \u2514\u2500\u2500 <agent-name>/
242
+ \u2514\u2500\u2500 AGENT.md
243
+ \`\`\`
244
+
245
+ ## Frontmatter Reference
246
+
247
+ ### Skills
248
+
249
+ Auto-loadable AI behaviors.
250
+
251
+ \`\`\`yaml
252
+ ---
253
+ # Common
254
+ name: "my-skill"
255
+ description: "What this skill does and when to use it"
256
+
257
+ # Claude Code
258
+ allowed-tools:
259
+ - Read
260
+ - Bash
261
+ disable-model-invocation: false
262
+ model: sonnet
263
+ context: fork
264
+ agent: general-purpose
265
+ hooks:
266
+ PreToolUse:
267
+ - matcher: "Bash"
268
+ hooks:
269
+ - type: command
270
+ command: ".claude/hooks/validate.sh"
271
+
272
+ # RooCode
273
+ slug: my-mode
274
+ name: "\u{1F527} My Mode"
275
+ roleDefinition: "You are a specialist in..."
276
+ groups:
277
+ - read
278
+ - edit
279
+ - command
280
+ whenToUse: "Use when you need to..."
281
+ customInstructions: "Additional behavior guidelines..."
282
+
283
+ # OpenClaw
284
+ homepage: "https://github.com/you/my-skill"
285
+ metadata: {"openclaw":{"emoji":"\u{1F527}","os":["darwin","linux"]}}
286
+ ---
287
+ \`\`\`
288
+
289
+ | Field | Required | Platform | Description |
290
+ |-------|----------|----------|-------------|
291
+ | \`name\` | Yes | All | Display name. Claude Code: lowercase, numbers, hyphens only (max 64 chars). RooCode: emoji allowed. |
292
+ | \`description\` | Yes | All | What it does and when to use it. Claude uses this to decide auto-loading. |
293
+ | \`disable-model-invocation\` | No | CC, OpenClaw | \`true\` = prevent auto-loading, manual \`/name\` only. (default: \`false\`) |
294
+ | \`allowed-tools\` | No | Claude Code | Tools Claude can use without asking. e.g. \`Read\` \`Write\` \`Edit\` \`Bash\` \`Grep\` \`Glob\` |
295
+ | \`model\` | No | Claude Code | Model to use when active. \`sonnet\` or \`haiku\` |
296
+ | \`context\` | No | Claude Code | \`fork\` = run in a forked subagent context |
297
+ | \`agent\` | No | Claude Code | Subagent type when \`context: fork\`. e.g. \`general-purpose\` \`Explore\` \`Plan\` |
298
+ | \`hooks\` | No | Claude Code | Lifecycle hooks for pre/post processing. |
299
+ | \`slug\` | Yes | RooCode | Unique ID \u2014 \`[a-zA-Z0-9-]\` only |
300
+ | \`roleDefinition\` | Yes | RooCode | Core role and expertise definition |
301
+ | \`groups\` | Yes | RooCode | Tool permissions: \`read\` \`edit\` \`command\` \`mcp\` \`browser\` |
302
+ | \`whenToUse\` | No | RooCode | Guide for auto mode selection |
303
+ | \`customInstructions\` | No | RooCode | Additional behavior guidelines |
304
+ | \`homepage\` | No | OpenClaw | Website URL shown in the Skills UI |
305
+ | \`metadata\` | No | OpenClaw | Single-line JSON for platform gating. e.g. \`os\`, \`requires.bins\`, \`requires.env\` |
306
+
307
+ RooCode file-restricted edit example:
308
+ \`\`\`yaml
309
+ groups:
310
+ - read
311
+ - [edit, {fileRegex: '\\.(md|ts)$', description: "Markdown and TS only"}]
312
+ \`\`\`
313
+
314
+ ---
315
+
316
+ ### Commands
317
+
318
+ User-invocable slash commands (\`/command-name\`).
319
+
320
+ > RooCode does not have a separate command concept \u2014 use **Skills (modes)** instead.
321
+
322
+ \`\`\`yaml
323
+ ---
324
+ # Common
325
+ name: "my-command"
326
+ description: "What this command does"
327
+
328
+ # Claude Code
329
+ allowed-tools:
330
+ - Read
331
+ - Bash
332
+ argument-hint: "[filename] [format]"
333
+ user-invocable: true
334
+ model: sonnet
335
+ context: fork
336
+ agent: general-purpose
337
+ hooks:
338
+ PostToolUse:
339
+ - matcher: "Write|Edit"
340
+ hooks:
341
+ - type: command
342
+ command: "npm run lint"
343
+ async: true
344
+
345
+ # OpenClaw
346
+ user-invocable: true
347
+ command-dispatch: "tool" # bypass model, dispatch directly to a tool
348
+ command-tool: "Bash"
349
+ ---
350
+ \`\`\`
351
+
352
+ | Field | Required | Platform | Description |
353
+ |-------|----------|----------|-------------|
354
+ | \`name\` | No | All | Display name \u2014 lowercase, numbers, hyphens only (max 64 chars). Defaults to directory name. |
355
+ | \`description\` | Yes | All | Shown in \`/\` menu. Claude uses this to decide auto-loading. |
356
+ | \`user-invocable\` | No | CC, OpenClaw | \`false\` = hidden from \`/\` menu, background knowledge only. (default: \`true\`) |
357
+ | \`allowed-tools\` | No | Claude Code | Tools Claude can use without asking. e.g. \`Read\` \`Write\` \`Edit\` \`Bash\` \`Grep\` \`Glob\` |
358
+ | \`argument-hint\` | No | Claude Code | Hint shown during autocomplete. e.g. \`[issue-number]\` |
359
+ | \`model\` | No | Claude Code | Model to use when active. \`sonnet\` or \`haiku\` |
360
+ | \`context\` | No | Claude Code | \`fork\` = run in a forked subagent context |
361
+ | \`agent\` | No | Claude Code | Subagent type when \`context: fork\`. e.g. \`general-purpose\` \`Explore\` \`Plan\` |
362
+ | \`hooks\` | No | Claude Code | Lifecycle hooks for pre/post processing. |
363
+ | \`command-dispatch\` | No | OpenClaw | \`"tool"\` = bypass model, dispatch directly to a tool |
364
+ | \`command-tool\` | No | OpenClaw | Tool to invoke when \`command-dispatch: "tool"\` |
365
+ | \`command-arg-mode\` | No | OpenClaw | How arguments are forwarded to the tool. (default: \`"raw"\`) |
366
+
367
+ ---
368
+
369
+ ### Agents
370
+
371
+ Custom subagent definitions loaded by Claude Code.
372
+
373
+ \`\`\`yaml
374
+ ---
375
+ name: "my-agent"
376
+ description: "What this agent does and when to use it"
377
+ allowed-tools:
378
+ - Read
379
+ - Bash
380
+ model: sonnet
381
+ context: fork
382
+ ---
383
+ \`\`\`
384
+
385
+ | Field | Required | Description |
386
+ |-------|----------|-------------|
387
+ | \`name\` | Yes | Display name \u2014 lowercase, numbers, hyphens only (max 64 chars) |
388
+ | \`description\` | Yes | When and how to use this agent. Claude uses this to decide when to spawn it. |
389
+ | \`allowed-tools\` | No | Tools this agent can use without asking |
390
+ | \`model\` | No | Model override. \`sonnet\` or \`haiku\` |
391
+ | \`context\` | No | \`fork\` = run in isolated subagent context |
392
+
393
+ ---
394
+
395
+ ### Hooks
396
+
397
+ Lifecycle shell commands (or LLM prompts) that fire at specific points. Hooks in skill/command frontmatter are scoped to that component \u2014 active while it runs, removed when it finishes.
398
+
399
+ \`\`\`yaml
400
+ hooks:
401
+ PreToolUse:
402
+ - matcher: "Bash" # regex matched against tool name
403
+ hooks:
404
+ - type: command
405
+ command: ".claude/hooks/validate.sh"
406
+ timeout: 30 # seconds (default: 600)
407
+ PostToolUse:
408
+ - matcher: "Write|Edit"
409
+ hooks:
410
+ - type: command
411
+ command: "npm run lint"
412
+ async: true # run in background, non-blocking
413
+ Stop:
414
+ - hooks:
415
+ - type: prompt
416
+ prompt: "Verify all tasks are complete: $ARGUMENTS"
417
+ \`\`\`
418
+
419
+ **Events**
420
+
421
+ | Event | Matcher | Can Block | When it fires |
422
+ |-------|---------|:---------:|---------------|
423
+ | \`PreToolUse\` | tool name | Yes | Before a tool runs |
424
+ | \`PostToolUse\` | tool name | No | After a tool succeeds |
425
+ | \`PostToolUseFailure\` | tool name | No | After a tool fails |
426
+ | \`UserPromptSubmit\` | \u2014 | Yes | When user submits a prompt |
427
+ | \`SessionStart\` | \`startup\` \\| \`resume\` \\| \`clear\` \\| \`compact\` | No | Session begins |
428
+ | \`Stop\` | \u2014 | Yes | Claude finishes responding |
429
+ | \`Notification\` | \`permission_prompt\` \\| \`idle_prompt\` | No | Notification fires |
430
+ | \`SubagentStart\` / \`SubagentStop\` | agent type | No / Yes | Subagent spawned / finished |
431
+
432
+ Full event list: [hooks reference](https://code.claude.com/docs/en/hooks)
433
+
434
+ **Handler fields**
435
+
436
+ | Field | Type | Description |
437
+ |-------|------|-------------|
438
+ | \`type\` | required | \`command\`, \`prompt\`, or \`agent\` |
439
+ | \`command\` | command only | Shell command to execute. Receives hook JSON on stdin |
440
+ | \`prompt\` | prompt/agent | Prompt text. Use \`$ARGUMENTS\` for the hook JSON input |
441
+ | \`timeout\` | all | Seconds before cancel. Defaults: 600 / 30 / 60 |
442
+ | \`async\` | command only | \`true\` = run in background, cannot block Claude |
443
+ | \`once\` | command only | \`true\` = run once per session then remove (skills only) |
444
+
445
+ **Decision control** (command hooks)
446
+
447
+ Exit \`0\` = allow. Exit \`2\` = block (stderr is fed to Claude as the reason). Print JSON to stdout for richer control:
448
+
449
+ \`\`\`bash
450
+ # PreToolUse: deny via JSON
451
+ echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"reason"}}'
452
+
453
+ # Stop/PostToolUse: block via JSON
454
+ echo '{"decision":"block","reason":"Tests must pass first"}'
455
+ \`\`\`
456
+
457
+ ## Usage
458
+
459
+ \`\`\`bash
460
+ # Scaffold this repo's directory structure
461
+ set-prompt scaffold .
462
+
463
+ # Install from remote and link to AI tools
464
+ set-prompt install https://github.com/you/my-prompts
465
+ set-prompt link
466
+
467
+ # Pull latest changes
468
+ set-prompt update
469
+ \`\`\`
470
+ `;
471
+
472
+ // src/commands/scaffold-command.ts
473
+ var REQUIRED_DIRS = ["skills", "commands"];
474
+ var OPTIONAL_DIRS = ["hooks", "agents"];
475
+ var printStructure = async (localPath) => {
476
+ let valid = true;
477
+ for (const dir of REQUIRED_DIRS) {
478
+ const exists = fs2.existsSync(path2.join(localPath, dir));
479
+ if (exists) {
480
+ console.log(`${TAB}\u2705 ${dir}/`);
481
+ } else {
482
+ console.log(`${TAB}\u274C ${dir}/ ${chalk2.red("(missing)")}`);
483
+ valid = false;
484
+ }
485
+ }
486
+ for (const dir of OPTIONAL_DIRS) {
487
+ const exists = fs2.existsSync(path2.join(localPath, dir));
488
+ console.log(`${TAB}${chalk2.dim(exists ? "\u2713" : "\u25CB")} ${dir}/ ${exists ? "" : chalk2.dim("(optional)")}`);
489
+ }
490
+ return valid;
491
+ };
492
+ var scaffoldCommand = async (localPath, options = {}) => {
493
+ try {
494
+ let targetPath = null;
495
+ if (localPath != null) {
496
+ targetPath = localPath;
497
+ } else {
498
+ if (configManager.repo_path != null) {
499
+ targetPath = configManager.repo_path;
500
+ } else {
501
+ console.error(chalk2.red("No path provided and no repo registered. Please provide a path."));
502
+ process.exit(1);
503
+ }
504
+ }
505
+ if (fs2.existsSync(targetPath) === false || fs2.statSync(targetPath).isDirectory() === false) {
506
+ console.error(chalk2.red(`Invalid directory path: '${targetPath}'`));
507
+ process.exit(1);
508
+ }
509
+ if (options.force !== true) {
510
+ console.log(chalk2.dim(`Checking repo structure at: ${targetPath}`));
511
+ }
512
+ const valid = options.force === true ? false : await printStructure(targetPath);
513
+ if (valid) {
514
+ console.log(chalk2.green("Repo structure is valid."));
515
+ return true;
516
+ }
517
+ if (!valid) {
518
+ if (options.force !== true) {
519
+ const proceed = await confirm({
520
+ message: "Some directories are missing. Scaffold them now?",
521
+ default: true
522
+ });
523
+ if (proceed === false) {
524
+ console.log(chalk2.yellow("Scaffold skipped."));
525
+ return false;
526
+ }
527
+ }
528
+ const created = [];
529
+ const guideMdPath = path2.join(targetPath, "SET_PROMPT_GUIDE.md");
530
+ if (options.force === true || fs2.existsSync(guideMdPath) === false) {
531
+ fs2.writeFileSync(guideMdPath, SET_PROMPT_GUIDE, { encoding: "utf-8", flag: "w" });
532
+ created.push(" SET_PROMPT_GUIDE.md");
533
+ }
534
+ for (const dirName of PROMPT_DIR_NAMES) {
535
+ const dirPath = path2.join(targetPath, dirName);
536
+ if (fs2.existsSync(dirPath)) {
537
+ console.warn(chalk2.yellow(`Directory already exists: '${dirName}' (skipping)`));
538
+ continue;
539
+ }
540
+ fs2.mkdirSync(dirPath, { recursive: true });
541
+ created.push(` ${dirName}/`);
542
+ }
543
+ if (created.length > 0) {
544
+ console.log(chalk2.green("Created:"));
545
+ created.forEach((line) => console.log(line));
546
+ }
547
+ }
548
+ return true;
549
+ } catch (ex) {
550
+ console.error(chalk2.red(`Failed to scaffold repo structure: ${ex.message}`), ex);
551
+ throw ex;
552
+ }
553
+ };
554
+
555
+ // src/commands/install-command.ts
556
+ var cloneRepo = async (remoteUrl) => {
557
+ const proceed = await confirm2({
558
+ message: `Clone and register "${remoteUrl}"?`,
559
+ default: true
560
+ });
561
+ if (proceed == false) {
562
+ console.log(chalk3.yellow("Cancelled."));
563
+ return false;
564
+ }
565
+ const localPath = path3.join(HOME_DIR, "repo");
566
+ if (fs3.existsSync(localPath) == true) {
567
+ console.warn(chalk3.yellow(`Existing repo found. Backing up before proceeding.`));
568
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
569
+ const backupPath = path3.join(HOME_DIR, `repo.bak.${timestamp}`);
570
+ fs3.renameSync(localPath, backupPath);
571
+ console.log(chalk3.dim(` Backed up to: ${backupPath}`));
572
+ }
573
+ fs3.mkdirSync(path3.dirname(localPath), { recursive: true });
574
+ console.log(`Cloning ${remoteUrl}...`);
575
+ const result = spawnSync("git", ["clone", remoteUrl, localPath], { stdio: "inherit" });
576
+ if (result.status !== 0) {
577
+ console.log("\u274C Failed to clone. Check the URL and your git credentials.");
578
+ process.exit(1);
579
+ }
580
+ console.log("\u2705 Cloned successfully.");
581
+ await scaffoldCommand(localPath, { force: true });
582
+ configManager.repo_path = localPath;
583
+ configManager.remote_url = remoteUrl;
584
+ if (configManager.save() === false) {
585
+ console.error(chalk3.red("Failed to save config."));
586
+ return false;
587
+ }
588
+ return true;
589
+ };
590
+ var installCommand = async (target) => {
591
+ try {
592
+ if (isGitUrl(target) === false) {
593
+ console.error(chalk3.red("\u274C Only remote git URLs are supported."));
594
+ console.log(chalk3.dim(" Example: set-prompt install https://github.com/you/my-prompts"));
595
+ process.exit(1);
596
+ }
597
+ return await cloneRepo(target);
598
+ } catch (ex) {
599
+ console.error(chalk3.red(`Unexpected error: ${ex.message}`), ex);
600
+ process.exit(1);
601
+ }
602
+ };
603
+
604
+ // src/commands/link-command.ts
605
+ import path4 from "path";
606
+ import fs4 from "fs";
607
+ import os2 from "os";
608
+ import chalk4 from "chalk";
609
+ import { checkbox } from "@inquirer/prompts";
610
+ import { pathExists } from "fs-extra";
611
+ var resolveRepoPath = () => {
612
+ if (configManager.repo_path == null) {
613
+ console.error(chalk4.red("\u274C No repo installed."));
614
+ console.log(chalk4.yellow("Run: set-prompt install <git-url>"));
615
+ return null;
616
+ }
617
+ return configManager.repo_path;
618
+ };
619
+ var PLUGIN_NAME = "set-prompt";
620
+ var linkClaudeCode = async () => {
621
+ const repoPath = resolveRepoPath();
622
+ if (repoPath == null) {
623
+ return;
624
+ }
625
+ const setClaudeCodeAssets = async () => {
626
+ try {
627
+ fs4.mkdirSync(CLAUDE_CODE_DIR, { recursive: true });
628
+ const marketplaceMetaDir = path4.join(CLAUDE_CODE_DIR, ".claude-plugin");
629
+ fs4.mkdirSync(marketplaceMetaDir, { recursive: true });
630
+ const marketplaceJson = {
631
+ name: PLUGIN_NAME,
632
+ owner: { name: os2.userInfo().username },
633
+ metadata: { description: "Managed by set-prompt", version: "1.0.0" },
634
+ plugins: [{ name: PLUGIN_NAME, source: `./plugins/${PLUGIN_NAME}`, description: "Managed by set-prompt" }]
635
+ };
636
+ fs4.writeFileSync(
637
+ path4.join(marketplaceMetaDir, "marketplace.json"),
638
+ JSON.stringify(marketplaceJson, null, 2),
639
+ "utf-8"
640
+ );
641
+ console.log(chalk4.dim(" \u251C\u2500\u2500 .claude-plugin/"));
642
+ console.log(chalk4.dim(" \u2502 \u2514\u2500\u2500 marketplace.json") + chalk4.green(" \u2713"));
643
+ const pluginDir = path4.join(CLAUDE_CODE_DIR, "plugins", PLUGIN_NAME);
644
+ fs4.mkdirSync(pluginDir, { recursive: true });
645
+ console.log(chalk4.dim(" \u2514\u2500\u2500 plugins/"));
646
+ console.log(chalk4.dim(` \u2514\u2500\u2500 ${PLUGIN_NAME}/`));
647
+ const pluginMetaDir = path4.join(pluginDir, ".claude-plugin");
648
+ fs4.mkdirSync(pluginMetaDir, { recursive: true });
649
+ const pluginJson = {
650
+ name: PLUGIN_NAME,
651
+ version: "1.0.0",
652
+ description: "Managed by set-prompt",
653
+ author: { name: path4.basename(repoPath) }
654
+ };
655
+ fs4.writeFileSync(
656
+ path4.join(pluginMetaDir, "plugin.json"),
657
+ JSON.stringify(pluginJson, null, 2),
658
+ "utf-8"
659
+ );
660
+ console.log(chalk4.dim(" \u251C\u2500\u2500 .claude-plugin/"));
661
+ console.log(chalk4.dim(" \u2502 \u2514\u2500\u2500 plugin.json") + chalk4.green(" \u2713"));
662
+ const linked = [];
663
+ for (const dir of AGENT_PROMPT_DIRS["claudecode" /* CLAUDECODE */]) {
664
+ const src = path4.join(repoPath, dir);
665
+ const dest = path4.join(pluginDir, dir);
666
+ if (await pathExists(src) === false) {
667
+ continue;
668
+ }
669
+ if (fs4.existsSync(dest)) {
670
+ fs4.rmSync(dest, { recursive: true, force: true });
671
+ }
672
+ const symlinkType = process.platform === "win32" ? "junction" : "dir";
673
+ fs4.symlinkSync(src, dest, symlinkType);
674
+ linked.push({ dir, src });
675
+ }
676
+ for (const { dir, src } of linked) {
677
+ const isLast = linked[linked.length - 1].dir === dir;
678
+ const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
679
+ console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
680
+ }
681
+ return true;
682
+ } catch (ex) {
683
+ console.error(chalk4.red(`\u274C Failed to build plugin structure: ${ex.message}`));
684
+ return false;
685
+ }
686
+ };
687
+ const registerToClaudeSettings = () => {
688
+ const claudeSettingsPath = path4.join(os2.homedir(), ".claude", "settings.json");
689
+ try {
690
+ let settings = {};
691
+ if (fs4.existsSync(claudeSettingsPath)) {
692
+ const raw = fs4.readFileSync(claudeSettingsPath, "utf-8");
693
+ try {
694
+ const parsed = JSON.parse(raw);
695
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
696
+ settings = parsed;
697
+ } else {
698
+ console.warn(chalk4.yellow(" \u26A0 settings.json has unexpected format \u2014 proceeding with caution"));
699
+ }
700
+ } catch {
701
+ console.warn(chalk4.yellow(" \u26A0 Failed to parse settings.json \u2014 will not overwrite existing file"));
702
+ console.error(chalk4.red("\u274C Could not register plugin. Please add manually."));
703
+ return false;
704
+ }
705
+ }
706
+ settings.extraKnownMarketplaces = {
707
+ ...settings.extraKnownMarketplaces,
708
+ [PLUGIN_NAME]: { source: { source: "directory", path: CLAUDE_CODE_DIR } }
709
+ };
710
+ settings.enabledPlugins = {
711
+ ...settings.enabledPlugins,
712
+ [`${PLUGIN_NAME}@${PLUGIN_NAME}`]: true
713
+ };
714
+ let backupPath = null;
715
+ if (fs4.existsSync(claudeSettingsPath)) {
716
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
717
+ backupPath = `${claudeSettingsPath}.bak.${timestamp}`;
718
+ try {
719
+ fs4.copyFileSync(claudeSettingsPath, backupPath);
720
+ } catch (ex) {
721
+ console.warn(chalk4.yellow(` \u26A0 Could not create backup: ${ex.message}`));
722
+ backupPath = null;
723
+ }
724
+ }
725
+ try {
726
+ fs4.mkdirSync(path4.dirname(claudeSettingsPath), { recursive: true });
727
+ fs4.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2), "utf-8");
728
+ } catch (ex) {
729
+ if (backupPath !== null) {
730
+ try {
731
+ fs4.copyFileSync(backupPath, claudeSettingsPath);
732
+ fs4.unlinkSync(backupPath);
733
+ console.warn(chalk4.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
734
+ } catch {
735
+ console.error(chalk4.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
736
+ }
737
+ }
738
+ throw ex;
739
+ }
740
+ if (backupPath !== null) {
741
+ try {
742
+ fs4.unlinkSync(backupPath);
743
+ } catch {
744
+ }
745
+ }
746
+ console.log(`\u2705 Registered to Claude Code settings.`);
747
+ console.log(chalk4.dim(` ${claudeSettingsPath}`));
748
+ return true;
749
+ } catch (ex) {
750
+ console.error(chalk4.red(`\u274C Failed to update settings.json: ${ex.message}`));
751
+ console.log(chalk4.dim(" Please add the plugin manually via Claude Code /plugins."));
752
+ return false;
753
+ }
754
+ };
755
+ console.log(chalk4.green(`
756
+ Setting up Claude Code plugin...`));
757
+ console.log(chalk4.dim(CLAUDE_CODE_DIR));
758
+ const structureOk = await setClaudeCodeAssets();
759
+ if (structureOk === false) {
760
+ return;
761
+ }
762
+ const settingsOk = registerToClaudeSettings();
763
+ if (settingsOk === false) {
764
+ return;
765
+ }
766
+ configManager.claude_code = { path: CLAUDE_CODE_DIR };
767
+ configManager.save();
768
+ };
769
+ var linkRooCode = async () => {
770
+ const repoPath = resolveRepoPath();
771
+ if (repoPath == null) {
772
+ return;
773
+ }
774
+ console.log(chalk4.green(`
775
+ Setting up RooCode integration...`));
776
+ console.log(chalk4.dim(ROO_DIR));
777
+ const roocodeDirs = AGENT_PROMPT_DIRS["roocode" /* ROOCODE */];
778
+ const backupExistingRooCodeFiles = async () => {
779
+ try {
780
+ fs4.mkdirSync(ROO_DIR, { recursive: true });
781
+ const dirsToBackup = [];
782
+ for (const dir of roocodeDirs) {
783
+ const target = path4.join(ROO_DIR, dir);
784
+ if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false) {
785
+ dirsToBackup.push(dir);
786
+ }
787
+ }
788
+ if (dirsToBackup.length === 0) {
789
+ return true;
790
+ }
791
+ fs4.mkdirSync(ROO_BACKUP_DIR, { recursive: true });
792
+ for (const dir of dirsToBackup) {
793
+ const src = path4.join(ROO_DIR, dir);
794
+ const dest = path4.join(ROO_BACKUP_DIR, dir);
795
+ fs4.renameSync(src, dest);
796
+ console.log(chalk4.dim(` backed up: ${dir}/ \u2192 ${dest}`));
797
+ }
798
+ return true;
799
+ } catch (ex) {
800
+ console.error(chalk4.red(`\u274C Failed to backup existing directories: ${ex.message}`));
801
+ return false;
802
+ }
803
+ };
804
+ const setRooCodeAssets = async () => {
805
+ try {
806
+ const linked = [];
807
+ for (const dir of roocodeDirs) {
808
+ const src = path4.join(repoPath, dir);
809
+ const dest = path4.join(ROO_DIR, dir);
810
+ if (await pathExists(src) === false) {
811
+ continue;
812
+ }
813
+ if (fs4.existsSync(dest)) {
814
+ fs4.rmSync(dest, { recursive: true, force: true });
815
+ }
816
+ const symlinkType = process.platform === "win32" ? "junction" : "dir";
817
+ fs4.symlinkSync(src, dest, symlinkType);
818
+ linked.push({ dir, src });
819
+ }
820
+ for (const { dir, src } of linked) {
821
+ const isLast = linked[linked.length - 1].dir === dir;
822
+ const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
823
+ console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
824
+ }
825
+ return true;
826
+ } catch (ex) {
827
+ console.error(chalk4.red(`\u274C Failed to create symlinks: ${ex.message}`));
828
+ return false;
829
+ }
830
+ };
831
+ const backupOk = await backupExistingRooCodeFiles();
832
+ if (backupOk === false) {
833
+ return;
834
+ }
835
+ const linkOk = await setRooCodeAssets();
836
+ if (linkOk === false) {
837
+ return;
838
+ }
839
+ configManager.roocode = { path: ROO_DIR, backup_path: ROO_BACKUP_DIR };
840
+ configManager.save();
841
+ };
842
+ var linkOpenclaw = async () => {
843
+ const repoPath = resolveRepoPath();
844
+ if (repoPath == null) {
845
+ return;
846
+ }
847
+ console.log(chalk4.green(`
848
+ Setting up OpenClaw integration...`));
849
+ console.log(chalk4.dim(OPENCLAW_DIR));
850
+ const openclawDirs = AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */];
851
+ const backupExistingOpenclawFiles = async () => {
852
+ try {
853
+ fs4.mkdirSync(OPENCLAW_DIR, { recursive: true });
854
+ const dirsToBackup = [];
855
+ for (const dir of openclawDirs) {
856
+ const target = path4.join(OPENCLAW_DIR, dir);
857
+ if (fs4.existsSync(target) && fs4.lstatSync(target).isSymbolicLink() === false) {
858
+ dirsToBackup.push(dir);
859
+ }
860
+ }
861
+ if (dirsToBackup.length === 0) {
862
+ return true;
863
+ }
864
+ fs4.mkdirSync(OPENCLAW_BACKUP_DIR, { recursive: true });
865
+ for (const dir of dirsToBackup) {
866
+ const src = path4.join(OPENCLAW_DIR, dir);
867
+ const dest = path4.join(OPENCLAW_BACKUP_DIR, dir);
868
+ fs4.renameSync(src, dest);
869
+ console.log(chalk4.dim(` backed up: ${dir}/ \u2192 ${dest}`));
870
+ }
871
+ return true;
872
+ } catch (ex) {
873
+ console.error(chalk4.red(`\u274C Failed to backup existing directories: ${ex.message}`));
874
+ return false;
875
+ }
876
+ };
877
+ const setOpenclawAssets = async () => {
878
+ try {
879
+ const linked = [];
880
+ for (const dir of openclawDirs) {
881
+ const src = path4.join(repoPath, dir);
882
+ const dest = path4.join(OPENCLAW_DIR, dir);
883
+ if (await pathExists(src) === false) {
884
+ continue;
885
+ }
886
+ if (fs4.existsSync(dest)) {
887
+ fs4.rmSync(dest, { recursive: true, force: true });
888
+ }
889
+ const symlinkType = process.platform === "win32" ? "junction" : "dir";
890
+ fs4.symlinkSync(src, dest, symlinkType);
891
+ linked.push({ dir, src });
892
+ }
893
+ for (const { dir, src } of linked) {
894
+ const isLast = linked[linked.length - 1].dir === dir;
895
+ const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
896
+ console.log(chalk4.dim(` ${branch} `) + chalk4.bold(`${dir}/`) + chalk4.dim(` \u2192 ${src}`) + chalk4.green(" \u2713"));
897
+ }
898
+ return true;
899
+ } catch (ex) {
900
+ console.error(chalk4.red(`\u274C Failed to create symlinks: ${ex.message}`));
901
+ return false;
902
+ }
903
+ };
904
+ const backupOk = await backupExistingOpenclawFiles();
905
+ if (backupOk === false) {
906
+ return;
907
+ }
908
+ const linkOk = await setOpenclawAssets();
909
+ if (linkOk === false) {
910
+ return;
911
+ }
912
+ configManager.openclaw = { path: OPENCLAW_DIR, backup_path: OPENCLAW_BACKUP_DIR };
913
+ configManager.save();
914
+ };
915
+ var linkCodex = async () => {
916
+ if (resolveRepoPath() == null) {
917
+ return;
918
+ }
919
+ console.log(chalk4.yellow("Codex integration is not yet implemented."));
920
+ };
921
+ var linkAntigravity = async () => {
922
+ if (resolveRepoPath() == null) {
923
+ return;
924
+ }
925
+ console.log(chalk4.yellow("Antigravity integration is not yet implemented."));
926
+ };
927
+ var linkCommand = async (tool) => {
928
+ if (tool != null) {
929
+ const known = ALL_AGENTS.some((a) => a.value === tool);
930
+ if (known === false) {
931
+ console.log(chalk4.red(`Unknown vendor: ${tool}`));
932
+ process.exit(1);
933
+ }
934
+ if (tool === "claudecode" /* CLAUDECODE */) {
935
+ await linkClaudeCode();
936
+ } else if (tool === "roocode" /* ROOCODE */) {
937
+ await linkRooCode();
938
+ } else if (tool === "openclaw" /* OPENCLAW */) {
939
+ await linkOpenclaw();
940
+ } else if (tool === "codex" /* CODEX */) {
941
+ await linkCodex();
942
+ } else if (tool === "antigravity" /* ANTIGRAVITY */) {
943
+ await linkAntigravity();
944
+ }
945
+ return;
946
+ }
947
+ const selected = await checkbox({
948
+ message: "Which AI agent do you want to integrate?",
949
+ choices: ALL_AGENTS.map((a) => {
950
+ const applied = a.value === "claudecode" /* CLAUDECODE */ ? configManager.isClaudeCodeEnabled() : a.value === "roocode" /* ROOCODE */ ? configManager.isRooCodeEnabled() : a.value === "openclaw" /* OPENCLAW */ ? configManager.isOpenclawEnabled() : a.value === "codex" /* CODEX */ ? configManager.isCodexEnabled() : a.value === "antigravity" /* ANTIGRAVITY */ ? configManager.isAntigravityEnabled() : false;
951
+ return {
952
+ name: applied ? `${a.name} ${chalk4.dim("(applied)")}` : a.name,
953
+ value: a.value,
954
+ checked: applied
955
+ };
956
+ })
957
+ });
958
+ for (const a of selected) {
959
+ if (a === "claudecode" /* CLAUDECODE */) {
960
+ await linkClaudeCode();
961
+ } else if (a === "roocode" /* ROOCODE */) {
962
+ await linkRooCode();
963
+ } else if (a === "openclaw" /* OPENCLAW */) {
964
+ await linkOpenclaw();
965
+ } else if (a === "codex" /* CODEX */) {
966
+ await linkCodex();
967
+ } else if (a === "antigravity" /* ANTIGRAVITY */) {
968
+ await linkAntigravity();
969
+ }
970
+ }
971
+ };
972
+
973
+ // src/commands/uninstall-command.ts
974
+ import fs5 from "fs";
975
+ import path5 from "path";
976
+ import os3 from "os";
977
+ import chalk5 from "chalk";
978
+ import { confirm as confirm4 } from "@inquirer/prompts";
979
+ var PLUGIN_NAME2 = "set-prompt";
980
+ var rollbackClaudeCode = () => {
981
+ const claudeSettingsPath = path5.join(os3.homedir(), ".claude", "settings.json");
982
+ if (fs5.existsSync(claudeSettingsPath) === false) {
983
+ return;
984
+ }
985
+ try {
986
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
987
+ const backupPath = `${claudeSettingsPath}.bak.${timestamp}`;
988
+ try {
989
+ fs5.copyFileSync(claudeSettingsPath, backupPath);
990
+ } catch (ex) {
991
+ console.warn(chalk5.yellow(` \u26A0 Could not create backup: ${ex.message} \u2014 skipping cleanup`));
992
+ return;
993
+ }
994
+ const raw = fs5.readFileSync(claudeSettingsPath, "utf-8");
995
+ let settings;
996
+ try {
997
+ const parsed = JSON.parse(raw);
998
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
999
+ console.warn(chalk5.yellow(" \u26A0 settings.json has unexpected format \u2014 skipping cleanup"));
1000
+ fs5.unlinkSync(backupPath);
1001
+ return;
1002
+ }
1003
+ settings = parsed;
1004
+ } catch {
1005
+ console.warn(chalk5.yellow(" \u26A0 Failed to parse settings.json \u2014 skipping cleanup"));
1006
+ fs5.unlinkSync(backupPath);
1007
+ return;
1008
+ }
1009
+ if (settings.extraKnownMarketplaces?.[PLUGIN_NAME2] !== void 0) {
1010
+ delete settings.extraKnownMarketplaces[PLUGIN_NAME2];
1011
+ }
1012
+ if (settings.enabledPlugins?.[`${PLUGIN_NAME2}@${PLUGIN_NAME2}`] !== void 0) {
1013
+ delete settings.enabledPlugins[`${PLUGIN_NAME2}@${PLUGIN_NAME2}`];
1014
+ }
1015
+ try {
1016
+ fs5.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2), "utf-8");
1017
+ } catch (ex) {
1018
+ try {
1019
+ fs5.copyFileSync(backupPath, claudeSettingsPath);
1020
+ fs5.unlinkSync(backupPath);
1021
+ console.warn(chalk5.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
1022
+ } catch {
1023
+ console.error(chalk5.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
1024
+ }
1025
+ throw ex;
1026
+ }
1027
+ try {
1028
+ fs5.unlinkSync(backupPath);
1029
+ } catch {
1030
+ }
1031
+ console.log(chalk5.dim(` removed set-prompt entries from: ${claudeSettingsPath}`));
1032
+ } catch (ex) {
1033
+ console.error(chalk5.red(` \u274C Failed to clean up settings.json: ${ex.message}`));
1034
+ }
1035
+ };
1036
+ var rollbackRooCode = () => {
1037
+ const backupPath = configManager.roocode?.backup_path ?? ROO_BACKUP_DIR;
1038
+ for (const dir of AGENT_PROMPT_DIRS["roocode" /* ROOCODE */]) {
1039
+ const target = path5.join(ROO_DIR, dir);
1040
+ if (fs5.existsSync(target) && fs5.lstatSync(target).isSymbolicLink()) {
1041
+ fs5.unlinkSync(target);
1042
+ console.log(chalk5.dim(` removed symlink: ${target}`));
1043
+ }
1044
+ }
1045
+ if (fs5.existsSync(backupPath) === false) {
1046
+ return;
1047
+ }
1048
+ try {
1049
+ for (const dir of AGENT_PROMPT_DIRS["roocode" /* ROOCODE */]) {
1050
+ const src = path5.join(backupPath, dir);
1051
+ const dest = path5.join(ROO_DIR, dir);
1052
+ if (fs5.existsSync(src) === false) {
1053
+ continue;
1054
+ }
1055
+ fs5.renameSync(src, dest);
1056
+ console.log(chalk5.dim(` restored: ${dir}/`));
1057
+ }
1058
+ fs5.rmdirSync(backupPath);
1059
+ } catch (ex) {
1060
+ console.error(chalk5.red(` \u274C Failed to restore RooCode backup: ${ex.message}`));
1061
+ }
1062
+ };
1063
+ var rollbackOpenclaw = () => {
1064
+ const backupPath = configManager.openclaw?.backup_path ?? OPENCLAW_BACKUP_DIR;
1065
+ for (const dir of AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */]) {
1066
+ const target = path5.join(OPENCLAW_DIR, dir);
1067
+ if (fs5.existsSync(target) && fs5.lstatSync(target).isSymbolicLink()) {
1068
+ fs5.unlinkSync(target);
1069
+ console.log(chalk5.dim(` removed symlink: ${target}`));
1070
+ }
1071
+ }
1072
+ if (fs5.existsSync(backupPath) === false) {
1073
+ return;
1074
+ }
1075
+ try {
1076
+ for (const dir of AGENT_PROMPT_DIRS["openclaw" /* OPENCLAW */]) {
1077
+ const src = path5.join(backupPath, dir);
1078
+ const dest = path5.join(OPENCLAW_DIR, dir);
1079
+ if (fs5.existsSync(src) === false) {
1080
+ continue;
1081
+ }
1082
+ fs5.renameSync(src, dest);
1083
+ console.log(chalk5.dim(` restored: ${dir}/`));
1084
+ }
1085
+ fs5.rmdirSync(backupPath);
1086
+ } catch (ex) {
1087
+ console.error(chalk5.red(` \u274C Failed to restore OpenClaw backup: ${ex.message}`));
1088
+ }
1089
+ };
1090
+ var uninstallCommand = async () => {
1091
+ const targets = [
1092
+ { label: `Claude Code plugin dir ${chalk5.dim(CLAUDE_CODE_DIR)}`, path: CLAUDE_CODE_DIR },
1093
+ { label: `Config file ${chalk5.dim(CONFIG_PATH)}`, path: CONFIG_PATH },
1094
+ { label: `Home dir ${chalk5.dim(HOME_DIR)}`, path: HOME_DIR }
1095
+ ].filter((t) => fs5.existsSync(t.path));
1096
+ const hasClaudeCodeSettings = configManager.isClaudeCodeEnabled();
1097
+ const hasRooCode = configManager.isRooCodeEnabled();
1098
+ const hasOpenclaw = configManager.isOpenclawEnabled();
1099
+ if (targets.length === 0 && hasClaudeCodeSettings === false && hasRooCode === false && hasOpenclaw === false) {
1100
+ console.log(chalk5.yellow("Nothing to remove."));
1101
+ return;
1102
+ }
1103
+ console.log(chalk5.red("\nThe following will be removed:"));
1104
+ targets.forEach((t) => console.log(` ${t.label}`));
1105
+ if (hasClaudeCodeSettings) {
1106
+ const claudeSettingsPath = path5.join(os3.homedir(), ".claude", "settings.json");
1107
+ console.log(` set-prompt entries in ${chalk5.dim(claudeSettingsPath)}`);
1108
+ }
1109
+ if (hasRooCode) {
1110
+ console.log(` RooCode symlinks in ${chalk5.dim(ROO_DIR)} ${chalk5.dim("(backup will be restored)")}`);
1111
+ }
1112
+ if (hasOpenclaw) {
1113
+ console.log(` OpenClaw symlinks in ${chalk5.dim(OPENCLAW_DIR)} ${chalk5.dim("(backup will be restored)")}`);
1114
+ }
1115
+ const ok = await confirm4({ message: "Proceed?", default: false });
1116
+ if (ok === false) {
1117
+ console.log(chalk5.yellow("Cancelled."));
1118
+ return;
1119
+ }
1120
+ if (hasClaudeCodeSettings) {
1121
+ rollbackClaudeCode();
1122
+ }
1123
+ if (hasRooCode) {
1124
+ rollbackRooCode();
1125
+ }
1126
+ if (hasOpenclaw) {
1127
+ rollbackOpenclaw();
1128
+ }
1129
+ for (const t of targets) {
1130
+ fs5.rmSync(t.path, { recursive: true, force: true });
1131
+ console.log(chalk5.dim(` removed: ${t.path}`));
1132
+ }
1133
+ console.log(chalk5.green("\nUninstalled."));
1134
+ };
1135
+
1136
+ // src/commands/status-command.ts
1137
+ import chalk6 from "chalk";
1138
+ var statusCommand = () => {
1139
+ if (configManager.repo_path == null) {
1140
+ console.log(chalk6.yellow("\u274C No repo installed."));
1141
+ console.log(chalk6.dim(` Run: set-prompt install <repo-url>`));
1142
+ return;
1143
+ }
1144
+ console.log(chalk6.bold("\nRepo"));
1145
+ console.log(`${TAB}path ${chalk6.cyan(configManager.repo_path)}`);
1146
+ if (configManager.remote_url != null) {
1147
+ console.log(`${TAB}remote ${chalk6.dim(configManager.remote_url)}`);
1148
+ }
1149
+ console.log(chalk6.bold("\nLinked agents"));
1150
+ for (const agent of ALL_AGENTS) {
1151
+ let linked = false;
1152
+ let agentPath = null;
1153
+ if (agent.value === "claudecode" /* CLAUDECODE */) {
1154
+ linked = configManager.isClaudeCodeEnabled();
1155
+ agentPath = configManager.claude_code?.path;
1156
+ } else if (agent.value === "roocode" /* ROOCODE */) {
1157
+ linked = configManager.isRooCodeEnabled();
1158
+ agentPath = configManager.roocode?.path;
1159
+ } else if (agent.value === "openclaw" /* OPENCLAW */) {
1160
+ linked = configManager.isOpenclawEnabled();
1161
+ agentPath = configManager.openclaw?.path;
1162
+ } else if (agent.value === "codex" /* CODEX */) {
1163
+ linked = configManager.isCodexEnabled();
1164
+ agentPath = configManager.codex?.path;
1165
+ } else if (agent.value === "antigravity" /* ANTIGRAVITY */) {
1166
+ linked = configManager.isAntigravityEnabled();
1167
+ agentPath = configManager.antigravity?.path;
1168
+ }
1169
+ const label = linked ? chalk6.green("linked") : chalk6.dim("not linked");
1170
+ const pathStr = linked && agentPath ? chalk6.dim(` \u2192 ${agentPath}`) : "";
1171
+ console.log(`${TAB}${agent.name.padEnd(12)} ${label}${pathStr}`);
1172
+ }
1173
+ console.log("");
1174
+ };
1175
+
1176
+ // src/commands/update-command.ts
1177
+ import { spawnSync as spawnSync2 } from "child_process";
1178
+ import chalk7 from "chalk";
1179
+ var updateCommand = () => {
1180
+ const repoPath = configManager.repo_path;
1181
+ if (repoPath == null) {
1182
+ console.error(chalk7.red("\u274C No repo installed."));
1183
+ console.log(chalk7.yellow("Run: set-prompt install <git-url>"));
1184
+ return;
1185
+ }
1186
+ if (configManager.remote_url == null) {
1187
+ console.error(chalk7.red("\u274C No remote URL registered. Cannot update."));
1188
+ return;
1189
+ }
1190
+ console.log(chalk7.green("\nUpdating prompt repo..."));
1191
+ console.log(chalk7.dim(repoPath));
1192
+ const fetch = spawnSync2("git", ["fetch"], { cwd: repoPath, stdio: "inherit" });
1193
+ if (fetch.status !== 0) {
1194
+ console.error(chalk7.red("\u274C git fetch failed."));
1195
+ return;
1196
+ }
1197
+ const pull = spawnSync2("git", ["pull"], { cwd: repoPath, stdio: "inherit" });
1198
+ if (pull.status !== 0) {
1199
+ console.error(chalk7.red("\u274C git pull failed."));
1200
+ return;
1201
+ }
1202
+ console.log(chalk7.green("\u2705 Repo updated."));
1203
+ };
1204
+
1205
+ // src/index.ts
1206
+ process.on("SIGINT", () => {
1207
+ console.log(chalk8.yellow("\nCancelled."));
1208
+ process.exit(0);
1209
+ });
1210
+ process.on("unhandledRejection", (reason) => {
1211
+ if (reason instanceof Error && reason.name === "ExitPromptError") {
1212
+ console.log(chalk8.yellow("\nCancelled."));
1213
+ process.exit(0);
1214
+ }
1215
+ throw reason;
1216
+ });
1217
+ var __dirname = path6.dirname(fileURLToPath(import.meta.url));
1218
+ var pkg = JSON.parse(fs6.readFileSync(path6.join(__dirname, "../package.json"), "utf-8"));
1219
+ configManager.init();
1220
+ var program = new Command();
1221
+ var banner = chalk8.cyan(figlet.textSync("Set-Prompt", { horizontalLayout: "full" }));
1222
+ program.name("set-prompt").description(pkg.description).version(pkg.version).addHelpText("beforeAll", banner + "\n").action(() => {
1223
+ program.help();
1224
+ });
1225
+ program.command("install").description(`\u{1F4E6} Clone a ${chalk8.cyan("git repo")} into ${chalk8.dim("~/.set-prompt/repo/")} and register it as your prompt source`).argument("<url>", "remote git URL").action(async (source) => {
1226
+ await installCommand(source);
1227
+ });
1228
+ program.command("link").description(`\u{1F517} Symlink your prompt repo into an ${chalk8.cyan("AI agent")} plugin dir ${chalk8.dim("(claudecode | roocode | openclaw | codex | antigravity)")}`).argument("[agent]", `target agent ${chalk8.dim("(omit for interactive selection)")}`).action(async (agent) => {
1229
+ await linkCommand(agent);
1230
+ });
1231
+ program.command("scaffold").description(`\u{1F6E0}\uFE0F Verify and create ${chalk8.cyan("required directories")} in a prompt repo ${chalk8.dim("(-f to force overwrite)")}`).argument("[path]", `path to repo ${chalk8.dim("(defaults to installed source)")}`).option("-f, --force", "overwrite existing files without prompting").action(async (localPath, options) => {
1232
+ await scaffoldCommand(localPath, options);
1233
+ });
1234
+ program.command("status").description(`\u{1F4CB} Show registered ${chalk8.cyan("repo")} and which ${chalk8.cyan("agents")} are linked`).action(() => {
1235
+ statusCommand();
1236
+ });
1237
+ program.command("update").description(`\u{1F504} Fetch and pull the latest changes from the ${chalk8.cyan("remote repo")}`).action(() => {
1238
+ updateCommand();
1239
+ });
1240
+ program.command("uninstall").description(`\u{1F5D1}\uFE0F Remove all set-prompt data ${chalk8.dim("(~/.set-prompt/, plugin dirs, settings entries)")}`).action(async () => {
1241
+ await uninstallCommand();
1242
+ });
1243
+ program.parse(process.argv);
1244
+ //# sourceMappingURL=index.js.map