@superclaude-org/superflag 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,870 @@
1
+ import * as fs from "fs/promises";
2
+ import * as fsSync from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
5
+ import chalk from "chalk";
6
+ import * as yaml from "js-yaml";
7
+ import inquirer from "inquirer";
8
+ import { VERSION } from "./version.js";
9
+ import { fileURLToPath } from "url";
10
+ import { dirname } from "path";
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ const PLATFORMS = [
14
+ {
15
+ name: "Claude Code",
16
+ configDir: path.join(os.homedir(), ".claude"),
17
+ mainFile: "CLAUDE.md",
18
+ contextFile: "SUPERFLAG.md",
19
+ type: "markdown"
20
+ },
21
+ {
22
+ name: "Gemini CLI",
23
+ configDir: path.join(os.homedir(), ".gemini"),
24
+ mainFile: "GEMINI.md",
25
+ contextFile: "SUPERFLAG.md",
26
+ type: "markdown"
27
+ },
28
+ {
29
+ name: "Continue",
30
+ configDir: path.join(os.homedir(), ".continue"),
31
+ mainFile: "config.yaml",
32
+ contextFile: "",
33
+ type: "yaml"
34
+ }
35
+ ];
36
+ export async function handleCommand(command, args = []) {
37
+ // Handle version in install command
38
+ if (args.includes("--version") || args.includes("-v")) {
39
+ console.log(VERSION);
40
+ process.exit(0);
41
+ }
42
+ // Check if --target argument is provided for non-interactive mode
43
+ const targetIndex = args.indexOf("--target");
44
+ const hasExplicitTarget = targetIndex !== -1 && args[targetIndex + 1];
45
+ // Also check for direct target argument (e.g., "install cc")
46
+ const directTarget = args.length > 0 && !args[0].startsWith("-") ? args[0] : null;
47
+ const target = hasExplicitTarget ? args[targetIndex + 1] : directTarget;
48
+ if (target) {
49
+ // Non-interactive mode with explicit target
50
+ if (command === "install") {
51
+ await install(target);
52
+ }
53
+ else if (command === "uninstall") {
54
+ await uninstall(target);
55
+ }
56
+ else {
57
+ console.error(chalk.red(`Unknown command: ${command}`));
58
+ showUsage();
59
+ process.exit(1);
60
+ }
61
+ }
62
+ else {
63
+ // Interactive mode - action determined by command
64
+ if (command === "install") {
65
+ await interactiveInstall();
66
+ }
67
+ else if (command === "uninstall") {
68
+ await interactiveUninstall();
69
+ }
70
+ else {
71
+ console.error(chalk.red(`Unknown command: ${command}`));
72
+ showUsage();
73
+ process.exit(1);
74
+ }
75
+ }
76
+ }
77
+ async function interactiveInstall() {
78
+ console.log(chalk.cyan.bold("\n============================================================"));
79
+ console.log(chalk.cyan.bold(` SuperFlag v${VERSION} - Installer`));
80
+ console.log(chalk.cyan.bold("============================================================\n"));
81
+ // Check current installation status
82
+ const installedPlatforms = await checkInstalledPlatforms();
83
+ // Ask which platforms to install
84
+ const platformChoices = PLATFORMS.map(p => ({
85
+ name: installedPlatforms.includes(p.name)
86
+ ? `${p.name} ${chalk.green("(installed)")}`
87
+ : p.name,
88
+ value: p.name,
89
+ checked: !installedPlatforms.includes(p.name) // Pre-select non-installed platforms
90
+ }));
91
+ const platformAnswer = await inquirer.prompt([
92
+ {
93
+ type: "checkbox",
94
+ name: "platforms",
95
+ message: "Select platforms to install:",
96
+ choices: platformChoices,
97
+ validate: (answer) => {
98
+ if (answer.length === 0) {
99
+ return "Please select at least one platform.";
100
+ }
101
+ return true;
102
+ }
103
+ }
104
+ ]);
105
+ if (platformAnswer.platforms.length === 0) {
106
+ console.log(chalk.yellow("No platforms selected. Exiting."));
107
+ process.exit(0);
108
+ }
109
+ // Confirmation
110
+ const confirmAnswer = await inquirer.prompt([
111
+ {
112
+ type: "confirm",
113
+ name: "confirm",
114
+ message: `Install SuperFlag for ${platformAnswer.platforms.join(", ")}?`,
115
+ default: true
116
+ }
117
+ ]);
118
+ if (!confirmAnswer.confirm) {
119
+ console.log(chalk.gray("Operation cancelled."));
120
+ process.exit(0);
121
+ }
122
+ await installInteractive(platformAnswer.platforms);
123
+ }
124
+ async function interactiveUninstall() {
125
+ console.log(chalk.yellow.bold("\n============================================================"));
126
+ console.log(chalk.yellow.bold(` SuperFlag v${VERSION} - Uninstaller`));
127
+ console.log(chalk.yellow.bold("============================================================\n"));
128
+ // Check current installation status
129
+ const installedPlatforms = await checkInstalledPlatforms();
130
+ if (installedPlatforms.length === 0) {
131
+ console.log(chalk.yellow("SuperFlag is not installed on any platform."));
132
+ process.exit(0);
133
+ }
134
+ // Ask which platforms to uninstall
135
+ const platformChoices = PLATFORMS.map(p => ({
136
+ name: installedPlatforms.includes(p.name)
137
+ ? `${p.name} ${chalk.green("(installed)")}`
138
+ : `${p.name} ${chalk.gray("(not installed)")}`,
139
+ value: p.name,
140
+ disabled: !installedPlatforms.includes(p.name),
141
+ checked: installedPlatforms.includes(p.name) // Pre-select installed platforms
142
+ }));
143
+ const platformAnswer = await inquirer.prompt([
144
+ {
145
+ type: "checkbox",
146
+ name: "platforms",
147
+ message: "Select platforms to uninstall:",
148
+ choices: platformChoices,
149
+ validate: (answer) => {
150
+ if (answer.length === 0) {
151
+ return "Please select at least one platform.";
152
+ }
153
+ return true;
154
+ }
155
+ }
156
+ ]);
157
+ if (platformAnswer.platforms.length === 0) {
158
+ console.log(chalk.yellow("No platforms selected. Exiting."));
159
+ process.exit(0);
160
+ }
161
+ // Confirmation
162
+ const confirmAnswer = await inquirer.prompt([
163
+ {
164
+ type: "confirm",
165
+ name: "confirm",
166
+ message: `Uninstall SuperFlag from ${platformAnswer.platforms.join(", ")}?`,
167
+ default: true
168
+ }
169
+ ]);
170
+ if (!confirmAnswer.confirm) {
171
+ console.log(chalk.gray("Operation cancelled."));
172
+ process.exit(0);
173
+ }
174
+ await uninstallInteractive(platformAnswer.platforms);
175
+ }
176
+ async function checkInstalledPlatforms() {
177
+ const installed = [];
178
+ for (const platform of PLATFORMS) {
179
+ try {
180
+ if (platform.type === "markdown") {
181
+ const mainPath = path.join(platform.configDir, platform.mainFile);
182
+ const content = await fs.readFile(mainPath, "utf-8");
183
+ if (content.includes("@SUPERFLAG.md")) {
184
+ installed.push(platform.name);
185
+ }
186
+ }
187
+ else if (platform.type === "yaml") {
188
+ const configPath = path.join(platform.configDir, platform.mainFile);
189
+ const content = await fs.readFile(configPath, "utf-8");
190
+ const config = yaml.load(content);
191
+ if (config.rules?.some((rule) => rule.title === "SuperFlag")) {
192
+ installed.push(platform.name);
193
+ }
194
+ }
195
+ }
196
+ catch {
197
+ // Not installed or file doesn't exist
198
+ }
199
+ }
200
+ return installed;
201
+ }
202
+ async function installInteractive(platformNames) {
203
+ const tasks = [];
204
+ // 1. Setup flags.yaml
205
+ const flagsPath = path.join(os.homedir(), ".superflag", "flags.yaml");
206
+ if (await setupFlagsYaml(flagsPath)) {
207
+ tasks.push({
208
+ name: "Flags config",
209
+ status: "OK",
210
+ detail: "~/.superflag/flags.yaml",
211
+ });
212
+ }
213
+ else {
214
+ tasks.push({
215
+ name: "Flags config",
216
+ status: "SKIP",
217
+ detail: "Already exists",
218
+ });
219
+ }
220
+ // 2. Setup selected platforms
221
+ const selectedPlatforms = PLATFORMS.filter(p => platformNames.includes(p.name));
222
+ for (const platform of selectedPlatforms) {
223
+ const result = await setupPlatform(platform, "install");
224
+ tasks.push(result);
225
+ // Setup Python hooks for Claude only
226
+ if (platform.name === "Claude Code") {
227
+ const hookResult = await setupPythonHooks();
228
+ tasks.push({
229
+ name: "Claude hooks",
230
+ status: hookResult ? "OK" : "SKIP",
231
+ detail: hookResult ? "~/.claude/hooks/" : "Already exists"
232
+ });
233
+ // Register hook in settings.json
234
+ const settingsResult = await registerHookInSettings();
235
+ tasks.push({
236
+ name: "Claude settings",
237
+ status: settingsResult ? "OK" : "SKIP",
238
+ detail: settingsResult ? "Hook registered" : "Already registered"
239
+ });
240
+ }
241
+ }
242
+ // Display results
243
+ displayResults(tasks);
244
+ // Show next steps
245
+ showNextSteps(platformNames);
246
+ }
247
+ async function uninstallInteractive(platformNames) {
248
+ const tasks = [];
249
+ // Remove from selected platforms
250
+ const selectedPlatforms = PLATFORMS.filter(p => platformNames.includes(p.name));
251
+ for (const platform of selectedPlatforms) {
252
+ const result = await setupPlatform(platform, "uninstall");
253
+ tasks.push(result);
254
+ // Remove Python hooks for Claude only
255
+ if (platform.name === "Claude Code") {
256
+ const hookPath = path.join(os.homedir(), ".claude", "hooks", "superflag.py");
257
+ if (await removeFile(hookPath)) {
258
+ tasks.push({
259
+ name: "Claude hooks",
260
+ status: "OK",
261
+ detail: "Removed",
262
+ });
263
+ }
264
+ else {
265
+ tasks.push({
266
+ name: "Claude hooks",
267
+ status: "SKIP",
268
+ detail: "Not found",
269
+ });
270
+ }
271
+ // Remove hook from settings.json
272
+ const settingsResult = await unregisterHookFromSettings();
273
+ tasks.push({
274
+ name: "Claude settings",
275
+ status: settingsResult ? "OK" : "SKIP",
276
+ detail: settingsResult ? "Hook unregistered" : "Not found"
277
+ });
278
+ }
279
+ }
280
+ displayResults(tasks);
281
+ if (platformNames.includes("Claude Code")) {
282
+ console.log(chalk.yellow("\n⚠ Note: Remove MCP server manually with:"));
283
+ console.log(chalk.gray(" claude mcp remove superflag"));
284
+ }
285
+ }
286
+ async function install(targetArg) {
287
+ console.log(chalk.cyan.bold("\n============================================================"));
288
+ console.log(chalk.cyan.bold(` SuperFlag v${VERSION} - Installer`));
289
+ console.log(chalk.cyan.bold("============================================================\n"));
290
+ const tasks = [];
291
+ // 1. Setup flags.yaml
292
+ const flagsPath = path.join(os.homedir(), ".superflag", "flags.yaml");
293
+ if (await setupFlagsYaml(flagsPath)) {
294
+ tasks.push({
295
+ name: "Flags config",
296
+ status: "OK",
297
+ detail: "~/.superflag/flags.yaml",
298
+ });
299
+ }
300
+ else {
301
+ tasks.push({
302
+ name: "Flags config",
303
+ status: "SKIP",
304
+ detail: "Already exists",
305
+ });
306
+ }
307
+ // 2. Setup platforms
308
+ const platforms = getPlatforms(targetArg);
309
+ for (const platform of platforms) {
310
+ const result = await setupPlatform(platform, "install");
311
+ tasks.push(result);
312
+ // Setup Python hooks for Claude only
313
+ if (platform.name === "Claude Code") {
314
+ const hookResult = await setupPythonHooks();
315
+ tasks.push({
316
+ name: "Claude hooks",
317
+ status: hookResult ? "OK" : "SKIP",
318
+ detail: hookResult ? "~/.claude/hooks/" : "Already exists"
319
+ });
320
+ // Register hook in settings.json
321
+ const settingsResult = await registerHookInSettings();
322
+ tasks.push({
323
+ name: "Claude settings",
324
+ status: settingsResult ? "OK" : "SKIP",
325
+ detail: settingsResult ? "Hook registered" : "Already registered"
326
+ });
327
+ }
328
+ }
329
+ // Display results
330
+ displayResults(tasks);
331
+ // Show next steps
332
+ const platformNames = platforms.map(p => p.name);
333
+ showNextSteps(platformNames);
334
+ }
335
+ async function uninstall(targetArg) {
336
+ console.log(chalk.yellow.bold("\n============================================================"));
337
+ console.log(chalk.yellow.bold(` SuperFlag v${VERSION} - Uninstaller`));
338
+ console.log(chalk.yellow.bold("============================================================\n"));
339
+ const tasks = [];
340
+ // Remove from platforms
341
+ const platforms = getPlatforms(targetArg);
342
+ for (const platform of platforms) {
343
+ const result = await setupPlatform(platform, "uninstall");
344
+ tasks.push(result);
345
+ // Remove Python hooks for Claude only
346
+ if (platform.name === "Claude Code") {
347
+ const hookPath = path.join(os.homedir(), ".claude", "hooks", "superflag.py");
348
+ if (await removeFile(hookPath)) {
349
+ tasks.push({
350
+ name: "Claude hooks",
351
+ status: "OK",
352
+ detail: "Removed",
353
+ });
354
+ }
355
+ else {
356
+ tasks.push({
357
+ name: "Claude hooks",
358
+ status: "SKIP",
359
+ detail: "Not found",
360
+ });
361
+ }
362
+ // Remove hook from settings.json
363
+ const settingsResult = await unregisterHookFromSettings();
364
+ tasks.push({
365
+ name: "Claude settings",
366
+ status: settingsResult ? "OK" : "SKIP",
367
+ detail: settingsResult ? "Hook unregistered" : "Not found"
368
+ });
369
+ }
370
+ }
371
+ displayResults(tasks);
372
+ if (targetArg === "all" || targetArg === "claude-code") {
373
+ console.log(chalk.yellow("\n⚠ Note: Remove MCP server manually with:"));
374
+ console.log(chalk.gray(" claude mcp remove superflag"));
375
+ }
376
+ }
377
+ function showNextSteps(platformNames) {
378
+ console.log(chalk.yellow("\nšŸ“ Next Steps:"));
379
+ if (platformNames.includes("Claude Code")) {
380
+ console.log(chalk.white("\nFor Claude Code:"));
381
+ console.log(chalk.cyan(" 1. Register MCP server:"));
382
+ console.log(chalk.gray(` claude mcp add superflag "npx" "-y" "@superclaude-org/superflag" -s user`));
383
+ console.log(chalk.cyan(" 2. Restart Claude Code"));
384
+ }
385
+ if (platformNames.includes("Gemini CLI")) {
386
+ console.log(chalk.white("\nFor Gemini CLI:"));
387
+ console.log(chalk.cyan(" 1. Register MCP server in Gemini settings"));
388
+ console.log(chalk.cyan(" 2. Restart Gemini CLI"));
389
+ }
390
+ if (platformNames.includes("Continue")) {
391
+ console.log(chalk.white("\nFor Continue:"));
392
+ console.log(chalk.cyan(" 1. Restart Continue extension"));
393
+ }
394
+ console.log(chalk.green("\n3. Test with: \"Analyze this code --analyze --strict\""));
395
+ }
396
+ function showUsage() {
397
+ console.log("\nUsage:");
398
+ console.log(" Interactive mode:");
399
+ console.log(" superflag-install install");
400
+ console.log(" superflag-install uninstall");
401
+ console.log("\n Non-interactive mode:");
402
+ console.log(" superflag-install install --target claude-code|gemini-cli|cn|all");
403
+ console.log(" superflag-install uninstall --target claude-code|gemini-cli|cn|all");
404
+ }
405
+ function getPlatforms(targetArg) {
406
+ if (targetArg === "all") {
407
+ return PLATFORMS;
408
+ }
409
+ const platformMap = {
410
+ "claude-code": PLATFORMS[0],
411
+ "cc": PLATFORMS[0], // Add shortcut
412
+ "gemini-cli": PLATFORMS[1],
413
+ "gemini": PLATFORMS[1], // Add shortcut
414
+ "cn": PLATFORMS[2],
415
+ "continue": PLATFORMS[2] // Add full name
416
+ };
417
+ if (!platformMap[targetArg]) {
418
+ console.error(chalk.red(`Error: Invalid target '${targetArg}'`));
419
+ console.log(chalk.yellow("Valid targets: claude-code (cc), gemini-cli (gemini), cn (continue), all"));
420
+ process.exit(1);
421
+ }
422
+ return [platformMap[targetArg]];
423
+ }
424
+ async function setupPlatform(platform, mode) {
425
+ try {
426
+ if (platform.type === "markdown") {
427
+ if (mode === "install") {
428
+ return await installMarkdownPlatform(platform);
429
+ }
430
+ else {
431
+ return await uninstallMarkdownPlatform(platform);
432
+ }
433
+ }
434
+ else if (platform.type === "yaml") {
435
+ if (mode === "install") {
436
+ return await installYamlPlatform(platform);
437
+ }
438
+ else {
439
+ return await uninstallYamlPlatform(platform);
440
+ }
441
+ }
442
+ return {
443
+ name: platform.name,
444
+ status: "FAIL",
445
+ detail: "Unknown platform type"
446
+ };
447
+ }
448
+ catch (error) {
449
+ return {
450
+ name: platform.name,
451
+ status: "FAIL",
452
+ detail: error instanceof Error ? error.message : "Unknown error"
453
+ };
454
+ }
455
+ }
456
+ async function installMarkdownPlatform(platform) {
457
+ // Create directory if needed
458
+ await fs.mkdir(platform.configDir, { recursive: true });
459
+ // 1. Create SUPERFLAG.md
460
+ const contextPath = path.join(platform.configDir, platform.contextFile);
461
+ await fs.writeFile(contextPath, getSuperflagMdContent(), "utf-8");
462
+ // 2. Update main file (CLAUDE.md or GEMINI.md)
463
+ const mainPath = path.join(platform.configDir, platform.mainFile);
464
+ let mainContent = "";
465
+ let fileExists = false;
466
+ try {
467
+ mainContent = await fs.readFile(mainPath, "utf-8");
468
+ fileExists = true;
469
+ }
470
+ catch {
471
+ // File doesn't exist, will create new
472
+ mainContent = "";
473
+ }
474
+ // Check if already has @SUPERFLAG.md
475
+ if (!mainContent.includes("@SUPERFLAG.md")) {
476
+ // Add @SUPERFLAG.md at the end
477
+ if (mainContent && !mainContent.endsWith("\n")) {
478
+ mainContent += "\n";
479
+ }
480
+ mainContent += "@SUPERFLAG.md\n";
481
+ await fs.writeFile(mainPath, mainContent, "utf-8");
482
+ return {
483
+ name: platform.name,
484
+ status: "OK",
485
+ detail: fileExists ? `${platform.mainFile} updated` : `${platform.mainFile} created`
486
+ };
487
+ }
488
+ return {
489
+ name: platform.name,
490
+ status: "SKIP",
491
+ detail: "@SUPERFLAG.md already exists"
492
+ };
493
+ }
494
+ async function uninstallMarkdownPlatform(platform) {
495
+ let hasChanges = false;
496
+ // 1. Remove SUPERFLAG.md file
497
+ const contextPath = path.join(platform.configDir, platform.contextFile);
498
+ if (await removeFile(contextPath)) {
499
+ hasChanges = true;
500
+ }
501
+ // 2. Remove @SUPERFLAG.md from main file
502
+ const mainPath = path.join(platform.configDir, platform.mainFile);
503
+ try {
504
+ let content = await fs.readFile(mainPath, "utf-8");
505
+ // Remove all occurrences of @SUPERFLAG.md (with or without newline)
506
+ const newContent = content.replace(/@SUPERFLAG\.md\n?/g, "");
507
+ if (newContent !== content) {
508
+ await fs.writeFile(mainPath, newContent, "utf-8");
509
+ hasChanges = true;
510
+ }
511
+ }
512
+ catch {
513
+ // File doesn't exist, nothing to remove
514
+ }
515
+ return {
516
+ name: platform.name,
517
+ status: hasChanges ? "OK" : "SKIP",
518
+ detail: hasChanges ? "Removed" : "Not found"
519
+ };
520
+ }
521
+ async function installYamlPlatform(platform) {
522
+ // For Continue platform
523
+ await fs.mkdir(platform.configDir, { recursive: true });
524
+ const configPath = path.join(platform.configDir, platform.mainFile);
525
+ let config = {};
526
+ let fileExists = false;
527
+ try {
528
+ const content = await fs.readFile(configPath, "utf-8");
529
+ config = yaml.load(content) || {};
530
+ fileExists = true;
531
+ }
532
+ catch {
533
+ // File doesn't exist, create new config
534
+ config = {};
535
+ }
536
+ // Ensure rules array exists
537
+ if (!config.rules) {
538
+ config.rules = [];
539
+ }
540
+ // Check if SuperFlag rule already exists
541
+ const hasSuperflag = config.rules.some((rule) => rule.title === "SuperFlag");
542
+ if (!hasSuperflag) {
543
+ // Add SuperFlag rule
544
+ config.rules.push({
545
+ title: "SuperFlag",
546
+ pattern: "--\\w+",
547
+ message: "Flag detected. Execute MCP: get_directives([detected_flags])\n" +
548
+ "Available: --analyze, --strict, --performance, --refactor, --lean, --discover, " +
549
+ "--explain, --save, --parallel, --todo, --seq, --concise, --git, --readonly, " +
550
+ "--load, --collab, --reset, --auto"
551
+ });
552
+ const yamlStr = yaml.dump(config, {
553
+ indent: 2,
554
+ lineWidth: 120,
555
+ noRefs: true
556
+ });
557
+ await fs.writeFile(configPath, yamlStr, "utf-8");
558
+ return {
559
+ name: platform.name,
560
+ status: "OK",
561
+ detail: fileExists ? "Rule added" : "config.yaml created"
562
+ };
563
+ }
564
+ return {
565
+ name: platform.name,
566
+ status: "SKIP",
567
+ detail: "Rule already exists"
568
+ };
569
+ }
570
+ async function uninstallYamlPlatform(platform) {
571
+ const configPath = path.join(platform.configDir, platform.mainFile);
572
+ try {
573
+ const content = await fs.readFile(configPath, "utf-8");
574
+ const config = yaml.load(content);
575
+ if (config.rules) {
576
+ const originalLength = config.rules.length;
577
+ // Remove SuperFlag rule
578
+ config.rules = config.rules.filter((rule) => rule.title !== "SuperFlag");
579
+ if (config.rules.length < originalLength) {
580
+ const yamlStr = yaml.dump(config, {
581
+ indent: 2,
582
+ lineWidth: 120,
583
+ noRefs: true
584
+ });
585
+ await fs.writeFile(configPath, yamlStr, "utf-8");
586
+ return {
587
+ name: platform.name,
588
+ status: "OK",
589
+ detail: "Rule removed"
590
+ };
591
+ }
592
+ }
593
+ }
594
+ catch {
595
+ // File doesn't exist or error reading
596
+ }
597
+ return {
598
+ name: platform.name,
599
+ status: "SKIP",
600
+ detail: "Not found"
601
+ };
602
+ }
603
+ async function setupFlagsYaml(flagsPath) {
604
+ try {
605
+ // Check if already exists
606
+ await fs.access(flagsPath);
607
+ return false; // Already exists
608
+ }
609
+ catch {
610
+ // Create directory
611
+ await fs.mkdir(path.dirname(flagsPath), { recursive: true });
612
+ // Copy from package
613
+ const sourcePath = path.join(__dirname, "..", "flags.yaml");
614
+ try {
615
+ await fs.copyFile(sourcePath, flagsPath);
616
+ }
617
+ catch {
618
+ // Create from bundled content if copy fails
619
+ const defaultContent = getDefaultFlagsYaml();
620
+ await fs.writeFile(flagsPath, defaultContent, "utf-8");
621
+ }
622
+ return true;
623
+ }
624
+ }
625
+ async function unregisterHookFromSettings() {
626
+ const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
627
+ const hookPath = path.join(os.homedir(), ".claude", "hooks", "superflag.py");
628
+ try {
629
+ const settingsContent = await fs.readFile(settingsPath, 'utf-8');
630
+ const settings = JSON.parse(settingsContent);
631
+ if (!settings.hooks || !settings.hooks.UserPromptSubmit) {
632
+ return false; // No hooks to remove
633
+ }
634
+ const hookCommand = `python "${hookPath}"`;
635
+ const initialLength = settings.hooks.UserPromptSubmit.length;
636
+ // Remove SuperFlag hook
637
+ settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter((h) => {
638
+ if (h.hooks && h.hooks.some((hook) => hook.command && hook.command.includes("superflag.py")))
639
+ return false;
640
+ return true;
641
+ });
642
+ if (settings.hooks.UserPromptSubmit.length === initialLength) {
643
+ return false; // Nothing was removed
644
+ }
645
+ // Save updated settings
646
+ await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
647
+ return true;
648
+ }
649
+ catch (error) {
650
+ return false;
651
+ }
652
+ }
653
+ async function registerHookInSettings() {
654
+ const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
655
+ const hookPath = path.join(os.homedir(), ".claude", "hooks", "superflag.py");
656
+ try {
657
+ // Read existing settings or create new
658
+ let settings = {};
659
+ try {
660
+ const settingsContent = await fs.readFile(settingsPath, 'utf-8');
661
+ settings = JSON.parse(settingsContent);
662
+ }
663
+ catch {
664
+ // Create new settings if doesn't exist
665
+ settings = { hooks: { UserPromptSubmit: [] } };
666
+ }
667
+ // Ensure hooks structure exists
668
+ if (!settings.hooks) {
669
+ settings.hooks = {};
670
+ }
671
+ if (!settings.hooks.UserPromptSubmit) {
672
+ settings.hooks.UserPromptSubmit = [];
673
+ }
674
+ // Check if hook already exists
675
+ const hookCommand = `python "${hookPath}"`;
676
+ const existingHook = settings.hooks.UserPromptSubmit.find((h) => h.hooks && h.hooks.some((hook) => hook.command === hookCommand));
677
+ if (existingHook) {
678
+ return false; // Already registered
679
+ }
680
+ // Add hook (matching Python format exactly)
681
+ settings.hooks.UserPromptSubmit.push({
682
+ matcher: "",
683
+ hooks: [{
684
+ type: "command",
685
+ command: hookCommand
686
+ }]
687
+ });
688
+ // Save settings
689
+ await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
690
+ return true;
691
+ }
692
+ catch (error) {
693
+ return false;
694
+ }
695
+ }
696
+ async function setupPythonHooks() {
697
+ const hooksDir = path.join(os.homedir(), ".claude", "hooks");
698
+ try {
699
+ await fs.mkdir(hooksDir, { recursive: true });
700
+ const hookPath = path.join(hooksDir, "superflag.py");
701
+ // Check if already exists
702
+ try {
703
+ await fs.access(hookPath);
704
+ return false; // Already exists
705
+ }
706
+ catch {
707
+ // Copy the actual hook file from hooks directory
708
+ const sourceHookPath = path.join(__dirname, '..', 'hooks', 'superflag.py');
709
+ try {
710
+ const hookContent = await fs.readFile(sourceHookPath, 'utf-8');
711
+ await fs.writeFile(hookPath, hookContent, "utf-8");
712
+ return true;
713
+ }
714
+ catch {
715
+ // Fallback to simple hook if source file not found
716
+ await fs.writeFile(hookPath, getPythonHookContent(), "utf-8");
717
+ return true;
718
+ }
719
+ }
720
+ }
721
+ catch {
722
+ return false;
723
+ }
724
+ }
725
+ async function removeFile(filePath) {
726
+ try {
727
+ await fs.unlink(filePath);
728
+ return true;
729
+ }
730
+ catch {
731
+ return false;
732
+ }
733
+ }
734
+ function displayResults(tasks) {
735
+ console.log(chalk.blue("\nšŸ“‹ Summary:"));
736
+ console.log("─".repeat(60));
737
+ for (const task of tasks) {
738
+ const symbol = task.status === "OK"
739
+ ? chalk.green("āœ“")
740
+ : task.status === "SKIP"
741
+ ? chalk.gray("ā—‹")
742
+ : chalk.red("āœ—");
743
+ const statusColor = task.status === "OK"
744
+ ? chalk.green
745
+ : task.status === "SKIP"
746
+ ? chalk.gray
747
+ : chalk.red;
748
+ console.log(`${symbol} ${task.name.padEnd(20)} ${statusColor(`[${task.status}]`)} ${chalk.gray(task.detail)}`);
749
+ }
750
+ console.log("─".repeat(60));
751
+ }
752
+ function getDefaultFlagsYaml() {
753
+ // Copy from bundled flags.yaml
754
+ const packageFlagsPath = path.join(__dirname, '..', 'flags.yaml');
755
+ try {
756
+ return fsSync.readFileSync(packageFlagsPath, 'utf-8');
757
+ }
758
+ catch (error) {
759
+ throw new Error('Cannot find bundled flags.yaml. Package installation is corrupted.');
760
+ }
761
+ }
762
+ function getSuperflagMdContent() {
763
+ return `# SuperFlag
764
+ MCP Protocol: get_directives([flags])
765
+
766
+ ## Core Workflow
767
+ <core_workflow>
768
+ When flags detected in user input:
769
+ 1. Execute MCP tool: get_directives([detected_flags])
770
+ 2. Apply directives completely and in order
771
+ 3. Verify compliance at checkpoints
772
+ </core_workflow>
773
+
774
+ ## Available Flags
775
+
776
+ ### Analysis & Optimization
777
+ - **--analyze**: Multi-perspective root cause analysis
778
+ - **--performance**: Measure and optimize bottlenecks
779
+ - **--refactor**: Safe code structure improvements
780
+ - **--strict**: Zero-error execution with transparency
781
+ - **--lean**: Eliminate waste with minimal implementation
782
+
783
+ ### Discovery & Documentation
784
+ - **--discover**: Research existing solutions first
785
+ - **--explain**: Progressive disclosure from overview to details
786
+ - **--save**: Create handoff documents for continuity
787
+ - **--load**: Restore context from handoff documents
788
+
789
+ ### Workflow Management
790
+ - **--parallel**: Execute independent tasks simultaneously
791
+ - **--todo**: Track progress with structured task management
792
+ - **--seq**: Sequential step-by-step problem solving
793
+ - **--collab**: Co-develop with quantitative validation
794
+
795
+ ### Output Control
796
+ - **--concise**: Professional, culturally neutral content
797
+ - **--git**: Anonymous, ASCII-only commit messages
798
+ - **--readonly**: Analysis without file modifications
799
+
800
+ ### Meta Control
801
+ - **--reset**: Clear session and force fresh directives
802
+ - **--auto**: Grant autonomous flag selection authority
803
+
804
+ ## Flag Selection Strategy
805
+ <flag_selection_strategy>
806
+ When --auto is used:
807
+ 1. Analyze user intent and task requirements
808
+ 2. Select complementary flags that work together
809
+ 3. Avoid conflicting flags (e.g., --readonly with --git)
810
+ 4. Prioritize based on task type:
811
+ - Bugs: --analyze, --strict, --todo
812
+ - Performance: --performance, --lean
813
+ - Features: --discover, --parallel, --todo
814
+ - Documentation: --explain, --save, --concise
815
+ </flag_selection_strategy>
816
+
817
+ ## Examples
818
+ \`\`\`
819
+ "Fix this bug --analyze --strict"
820
+ "Optimize the code --performance --lean"
821
+ "Refactor safely --refactor --git"
822
+ "Research alternatives --discover --parallel"
823
+ "Track complex task --todo --seq"
824
+ \`\`\`
825
+ `;
826
+ }
827
+ function getPythonHookContent() {
828
+ return `#!/usr/bin/env python3
829
+ """SuperFlag Claude Code Hook - TypeScript Edition Bridge"""
830
+
831
+ import sys
832
+ import json
833
+ import re
834
+
835
+ def main():
836
+ try:
837
+ user_input = sys.stdin.read()
838
+
839
+ # Detect flags in user input
840
+ flags = re.findall(r'--\\w+', user_input)
841
+
842
+ if flags:
843
+ # Output hook message for Claude Code
844
+ message = {
845
+ "type": "system",
846
+ "message": f"Flags detected: {', '.join(flags)}\\n" +
847
+ "Execute: get_directives(" + str(flags) + ") for systematic implementation."
848
+ }
849
+ print(json.dumps(message))
850
+ return 0
851
+
852
+ except Exception:
853
+ pass
854
+
855
+ return 1
856
+
857
+ if __name__ == "__main__":
858
+ sys.exit(main())
859
+ `;
860
+ }
861
+ // Main execution
862
+ async function main() {
863
+ const args = process.argv.slice(2);
864
+ const command = args[0];
865
+ const commandArgs = args.slice(1);
866
+ await handleCommand(command || "", commandArgs);
867
+ }
868
+ // Run if this is the main module
869
+ main().catch(console.error);
870
+ //# sourceMappingURL=install.js.map