@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.
- package/LICENSE +21 -0
- package/README.md +292 -0
- package/dist/directives.d.ts +44 -0
- package/dist/directives.d.ts.map +1 -0
- package/dist/directives.js +99 -0
- package/dist/directives.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +2 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +870 -0
- package/dist/install.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +203 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +43 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +117 -0
- package/dist/session.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/flags.yaml +569 -0
- package/hooks/superflag.py +192 -0
- package/package.json +59 -0
package/dist/install.js
ADDED
|
@@ -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
|