heymark 1.1.3 → 2.0.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/scripts/cli.js ADDED
@@ -0,0 +1,375 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const { CONFIG_RELATIVE, DEFAULT_BRANCH, loadConfig, writeConfig } = require("./lib/config");
7
+ const { loadRules } = require("./lib/parser");
8
+ const { CACHE_DIR_NAME, getLinkedRulesDir, sanitizeRepoName } = require("./lib/repo");
9
+
10
+ const SCRIPT_DIR = __dirname;
11
+ const PROJECT_ROOT = process.cwd();
12
+
13
+ const COMMAND_LINK = "link";
14
+ const COMMAND_SYNC = "sync";
15
+ const COMMAND_CLEAN = "clean";
16
+ const COMMAND_STATUS = "status";
17
+ const COMMAND_HELP = "help";
18
+
19
+ const OPTION_BRANCH = "--branch";
20
+ const OPTION_FOLDER = "--folder";
21
+ const OPTION_SAMPLES = "--samples";
22
+ const SHORT_BRANCH = "-b";
23
+ const SHORT_FOLDER = "-f";
24
+ const TARGET_ALL = "all";
25
+ const TARGET_DOT = ".";
26
+ const LATEST_VERSION_COMMAND = "npx heymark@latest";
27
+ const SAMPLES_REPO_URL = "https://github.com/MosslandOpenDevs/heymark.git";
28
+ const SAMPLES_FOLDER = "samples";
29
+
30
+ function exitWithError(message, details = []) {
31
+ console.error(`[Error] ${message}`);
32
+ details.forEach((detail) => console.error(` ${detail}`));
33
+ process.exit(1);
34
+ }
35
+
36
+ function discoverTools() {
37
+ const toolsDir = path.join(SCRIPT_DIR, "tools");
38
+ const registry = {};
39
+
40
+ fs.readdirSync(toolsDir)
41
+ .filter((fileName) => fileName.endsWith(".js"))
42
+ .sort()
43
+ .forEach((file) => {
44
+ const key = path.basename(file, ".js");
45
+ registry[key] = require(path.join(toolsDir, file));
46
+ });
47
+
48
+ return registry;
49
+ }
50
+
51
+ function showHelp(tools) {
52
+ const toolLines = Object.entries(tools)
53
+ .map(([toolKey, toolDefinition]) => {
54
+ const paddedKey = toolKey.padEnd(10);
55
+ const paddedName = toolDefinition.name.padEnd(16);
56
+ return ` ${paddedKey} ${paddedName} -> ${toolDefinition.output}`;
57
+ })
58
+ .join("\n");
59
+
60
+ console.log(`
61
+ Heymark CLI
62
+
63
+ Reads *.md from a GitHub repository (public or private) and generates
64
+ tool-specific configuration files for various AI coding assistants.
65
+ Same rules everywhere: A computer, B computer, same remote repo.
66
+
67
+ Usage:
68
+ heymark help
69
+ heymark link <repo-url> [--branch <name>] [--folder <path>]
70
+ heymark link --samples
71
+ heymark sync [.|<tool>...]
72
+ heymark clean [.|<tool>...]
73
+ heymark status
74
+ heymark
75
+
76
+ Options:
77
+ --branch, -b <name> Branch name (used with 'link')
78
+ --folder, -f <path> Subdirectory path in repository (used with 'link')
79
+ --samples Use built-in sample Skill repository
80
+
81
+ Targets:
82
+ . All available tools
83
+ <tool>... Space-separated tool names (no commas)
84
+
85
+ Available tools:
86
+ ${toolLines}
87
+
88
+ Examples:
89
+ heymark help
90
+ heymark link --samples
91
+ heymark link https://github.com/org/my-rules.git
92
+ heymark link https://github.com/org/my-rules.git --folder rules --branch main
93
+ heymark sync .
94
+ heymark sync cursor claude
95
+ heymark clean .
96
+ heymark status
97
+ heymark
98
+
99
+ Latest version:
100
+ ${LATEST_VERSION_COMMAND}
101
+ `);
102
+ }
103
+
104
+ function parseLinkArgs(args) {
105
+ if (args.length === 1 && args[0] === OPTION_SAMPLES) {
106
+ return {
107
+ repoUrl: SAMPLES_REPO_URL,
108
+ branch: DEFAULT_BRANCH,
109
+ folder: SAMPLES_FOLDER,
110
+ };
111
+ }
112
+
113
+ if (args.includes(OPTION_SAMPLES)) {
114
+ exitWithError("--samples cannot be combined with other link arguments.", [
115
+ "Use: heymark link --samples",
116
+ ]);
117
+ }
118
+
119
+ const repoUrl = args[0];
120
+ if (!repoUrl || repoUrl.startsWith("--")) {
121
+ exitWithError("link requires a GitHub repository URL.", [
122
+ "Example: heymark link https://github.com/org/my-rules.git",
123
+ "Example: heymark link git@github.com:org/my-rules.git",
124
+ "Optional: --branch <branch> --folder <subdir> (e.g. --folder rules)",
125
+ ]);
126
+ }
127
+
128
+ let branch = DEFAULT_BRANCH;
129
+ let folder = "";
130
+
131
+ for (let i = 1; i < args.length; i++) {
132
+ const arg = args[i];
133
+
134
+ if (arg === OPTION_BRANCH || arg === SHORT_BRANCH) {
135
+ const value = args[++i];
136
+ if (!value) {
137
+ exitWithError("--branch requires a branch name.");
138
+ }
139
+ branch = value.trim();
140
+ continue;
141
+ }
142
+
143
+ if (arg === OPTION_FOLDER || arg === SHORT_FOLDER) {
144
+ const value = args[++i];
145
+ if (!value) {
146
+ exitWithError("--folder requires a folder path.");
147
+ }
148
+ folder = value.trim();
149
+ continue;
150
+ }
151
+
152
+ exitWithError(`Unknown option for link: ${arg}`);
153
+ }
154
+
155
+ return { repoUrl: repoUrl.trim(), branch, folder };
156
+ }
157
+
158
+ function runLink(args) {
159
+ const config = parseLinkArgs(args);
160
+ const configPath = writeConfig(PROJECT_ROOT, config);
161
+
162
+ console.log(
163
+ `[Link] Linked repository saved to ${path.relative(PROJECT_ROOT, configPath) || configPath}`
164
+ );
165
+ console.log(` repoUrl: ${config.repoUrl}`);
166
+ if (config.branch !== DEFAULT_BRANCH) {
167
+ console.log(` branch: ${config.branch}`);
168
+ }
169
+ if (config.folder) {
170
+ console.log(` folder: ${config.folder}`);
171
+ }
172
+ console.log("");
173
+ console.log("Run 'heymark sync .' to fetch rules and generate tool configs.");
174
+ }
175
+
176
+ function loadLinkedConfigOrExit() {
177
+ const linkedConfig = loadConfig(PROJECT_ROOT);
178
+ if (linkedConfig) {
179
+ return linkedConfig;
180
+ }
181
+
182
+ exitWithError("No linked repository found.", [
183
+ `Run: heymark ${COMMAND_LINK} <github-repo-url>`,
184
+ `Config: ${CONFIG_RELATIVE}`,
185
+ ]);
186
+ }
187
+
188
+ function resolveSelectedTools(toolArgs, availableTools) {
189
+ const availableToolKeys = Object.keys(availableTools);
190
+ if (toolArgs.length === 0) {
191
+ return availableToolKeys;
192
+ }
193
+
194
+ const selectedTools = toolArgs.map((tool) => tool.trim().toLowerCase()).filter(Boolean);
195
+ if (selectedTools.length === 0) {
196
+ return availableToolKeys;
197
+ }
198
+
199
+ if (selectedTools.some((tool) => tool.includes(","))) {
200
+ exitWithError("Tool names must be space-separated (no commas).", [
201
+ "Example: heymark sync cursor claude",
202
+ ]);
203
+ }
204
+
205
+ const hasDotToken = selectedTools.includes(TARGET_DOT);
206
+ const hasAllToken = selectedTools.includes(TARGET_ALL);
207
+
208
+ if (hasAllToken) {
209
+ exitWithError("'all' is not supported. Use '.' for all tools.");
210
+ }
211
+
212
+ if (hasDotToken) {
213
+ if (selectedTools.length > 1) {
214
+ exitWithError(`'${TARGET_DOT}' cannot be combined with tool names.`);
215
+ }
216
+ return availableToolKeys;
217
+ }
218
+
219
+ const invalidTools = selectedTools.filter((tool) => !availableTools[tool]);
220
+ if (invalidTools.length > 0) {
221
+ exitWithError(`Unknown tool(s): ${invalidTools.join(", ")}`, [
222
+ `Available: ${availableToolKeys.join(", ")}`,
223
+ ]);
224
+ }
225
+
226
+ return Array.from(new Set(selectedTools));
227
+ }
228
+
229
+ function cleanGeneratedFiles(tools, selectedTools, ruleNames, onlyPrintWhenDeleted) {
230
+ for (const toolKey of selectedTools) {
231
+ const cleanedPaths = tools[toolKey].clean(ruleNames, PROJECT_ROOT);
232
+ if (onlyPrintWhenDeleted && cleanedPaths.length === 0) {
233
+ continue;
234
+ }
235
+
236
+ cleanedPaths.forEach((filePath) => {
237
+ console.log(` Deleted: ${filePath}`);
238
+ });
239
+ }
240
+ }
241
+
242
+ function loadRulesFromLinkedRepo() {
243
+ const linkedConfig = loadLinkedConfigOrExit();
244
+ const rulesDir = getLinkedRulesDir(PROJECT_ROOT, linkedConfig);
245
+ const rules = loadRules(rulesDir);
246
+ return { linkedConfig, rulesDir, rules };
247
+ }
248
+
249
+ function printSyncContext(selectedTools, rulesDir) {
250
+ const rulesRelPath = path.relative(PROJECT_ROOT, rulesDir) || ".";
251
+ console.log("[Sync] Starting convention sync...");
252
+ console.log(` Source: ${rulesRelPath} (from linked repo)`);
253
+ console.log(` Target: ${PROJECT_ROOT}`);
254
+ console.log(` Tools: ${selectedTools.join(", ")}`);
255
+ console.log("");
256
+ }
257
+
258
+ function runSync(toolArgs, tools) {
259
+ const selectedTools = resolveSelectedTools(toolArgs, tools);
260
+ const { rulesDir, rules } = loadRulesFromLinkedRepo();
261
+ printSyncContext(selectedTools, rulesDir);
262
+
263
+ console.log(`[Load] ${rules.length} rule(s): ${rules.map((rule) => rule.name).join(", ")}`);
264
+ console.log("");
265
+
266
+ const ruleNames = rules.map((rule) => rule.name);
267
+
268
+ // Ensure regenerated output is always fresh.
269
+ console.log("[Clean] Removing existing generated files...");
270
+ cleanGeneratedFiles(tools, selectedTools, ruleNames, true);
271
+ console.log("");
272
+
273
+ console.log("[Generate]");
274
+ for (const toolKey of selectedTools) {
275
+ const toolDefinition = tools[toolKey];
276
+ const count = toolDefinition.generate(rules, PROJECT_ROOT);
277
+ const summary = `${toolDefinition.name.padEnd(16)} -> ${toolDefinition.output}`;
278
+ console.log(` ${summary} (${count} rules)`);
279
+ }
280
+
281
+ console.log("");
282
+ console.log(`[Done] ${selectedTools.length} tool(s) synced successfully.`);
283
+ }
284
+
285
+ function runClean(toolArgs, tools) {
286
+ const selectedTools = resolveSelectedTools(toolArgs, tools);
287
+ const { rulesDir, rules } = loadRulesFromLinkedRepo();
288
+ const rulesRelPath = path.relative(PROJECT_ROOT, rulesDir) || ".";
289
+
290
+ console.log("[Clean] Removing generated files...");
291
+ console.log(` Source: ${rulesRelPath} (from linked repo)`);
292
+ console.log(` Tools: ${selectedTools.join(", ")}`);
293
+ console.log("");
294
+
295
+ const ruleNames = rules.map((rule) => rule.name);
296
+ cleanGeneratedFiles(tools, selectedTools, ruleNames, false);
297
+
298
+ console.log("");
299
+ console.log(`[Done] ${selectedTools.length} tool(s) cleaned successfully.`);
300
+ }
301
+
302
+ function runStatus(tools) {
303
+ const linkedConfig = loadConfig(PROJECT_ROOT);
304
+ const toolKeys = Object.keys(tools);
305
+
306
+ console.log("[Status] Heymark");
307
+ console.log(` Project: ${PROJECT_ROOT}`);
308
+ console.log(` Config: ${CONFIG_RELATIVE}`);
309
+ console.log(` Tools: ${toolKeys.join(", ")}`);
310
+ console.log(` Latest: ${LATEST_VERSION_COMMAND}`);
311
+ console.log("");
312
+
313
+ if (!linkedConfig) {
314
+ console.log("No repository is linked yet.");
315
+ console.log(`Run: heymark ${COMMAND_LINK} <github-repo-url>`);
316
+ return;
317
+ }
318
+
319
+ const cachePath = path.join(
320
+ PROJECT_ROOT,
321
+ CACHE_DIR_NAME,
322
+ sanitizeRepoName(linkedConfig.repoUrl)
323
+ );
324
+ const cacheState = fs.existsSync(cachePath) ? "ready" : "not-fetched";
325
+
326
+ console.log("Linked repository:");
327
+ console.log(` repoUrl: ${linkedConfig.repoUrl}`);
328
+ console.log(` branch: ${linkedConfig.branch || DEFAULT_BRANCH}`);
329
+ console.log(` folder: ${linkedConfig.folder || "(root)"}`);
330
+ console.log(` cache: ${cacheState}`);
331
+ }
332
+
333
+ function main() {
334
+ const tools = discoverTools();
335
+ const args = process.argv.slice(2);
336
+
337
+ if (args.length === 0) {
338
+ runStatus(tools);
339
+ return;
340
+ }
341
+
342
+ const command = args[0];
343
+ const commandArgs = args.slice(1);
344
+
345
+ if (command === COMMAND_LINK) {
346
+ runLink(commandArgs);
347
+ return;
348
+ }
349
+ if (command === COMMAND_SYNC) {
350
+ runSync(commandArgs, tools);
351
+ return;
352
+ }
353
+ if (command === COMMAND_CLEAN) {
354
+ runClean(commandArgs, tools);
355
+ return;
356
+ }
357
+ if (command === COMMAND_STATUS) {
358
+ if (commandArgs.length > 0) {
359
+ exitWithError("status does not accept arguments.");
360
+ }
361
+ runStatus(tools);
362
+ return;
363
+ }
364
+ if (command === COMMAND_HELP) {
365
+ if (commandArgs.length > 0) {
366
+ exitWithError("help does not accept arguments.");
367
+ }
368
+ showHelp(tools);
369
+ return;
370
+ }
371
+
372
+ exitWithError(`Unknown command: ${command}`, ["Use 'heymark help' for usage information."]);
373
+ }
374
+
375
+ main();
@@ -9,7 +9,7 @@ const DEFAULT_BRANCH = "main";
9
9
  const CONFIG_RELATIVE = path.join(CONFIG_DIR, CONFIG_FILENAME);
10
10
 
11
11
  /**
12
- * @typedef {{ rulesSource: string, branch?: string, rulesSourceDir?: string }} RuleBookConfig
12
+ * @typedef {{ repoUrl: string, branch?: string, folder?: string }} THeymarkConfig
13
13
  */
14
14
 
15
15
  /**
@@ -24,35 +24,51 @@ function getConfigPath(projectRoot) {
24
24
  /**
25
25
  * Normalize and validate config payload.
26
26
  * @param {unknown} value
27
- * @returns {RuleBookConfig | null}
27
+ * @returns {THeymarkConfig | null}
28
28
  */
29
29
  function normalizeConfig(value) {
30
30
  if (!value || typeof value !== "object") {
31
31
  return null;
32
32
  }
33
33
 
34
- const raw =
35
- /** @type {{ rulesSource?: unknown, branch?: unknown, rulesSourceDir?: unknown }} */ (
36
- value
37
- );
38
- if (typeof raw.rulesSource !== "string" || !raw.rulesSource.trim()) {
34
+ const raw = /** @type {{
35
+ * repoUrl?: unknown,
36
+ * branch?: unknown,
37
+ * folder?: unknown,
38
+ * rulesSource?: unknown,
39
+ * rulesSourceDir?: unknown
40
+ * }} */ (value);
41
+
42
+ const repoUrl =
43
+ typeof raw.repoUrl === "string" && raw.repoUrl.trim()
44
+ ? raw.repoUrl.trim()
45
+ : typeof raw.rulesSource === "string" && raw.rulesSource.trim()
46
+ ? raw.rulesSource.trim()
47
+ : "";
48
+
49
+ if (!repoUrl) {
39
50
  return null;
40
51
  }
41
52
 
42
53
  return {
43
- rulesSource: raw.rulesSource.trim(),
54
+ repoUrl,
44
55
  branch:
45
56
  typeof raw.branch === "string" && raw.branch.trim()
46
57
  ? raw.branch.trim()
47
58
  : DEFAULT_BRANCH,
48
- rulesSourceDir: typeof raw.rulesSourceDir === "string" ? raw.rulesSourceDir.trim() : "",
59
+ folder:
60
+ typeof raw.folder === "string"
61
+ ? raw.folder.trim()
62
+ : typeof raw.rulesSourceDir === "string"
63
+ ? raw.rulesSourceDir.trim()
64
+ : "",
49
65
  };
50
66
  }
51
67
 
52
68
  /**
53
69
  * Read config from project root.
54
70
  * @param {string} projectRoot
55
- * @returns {RuleBookConfig | null}
71
+ * @returns {THeymarkConfig | null}
56
72
  */
57
73
  function loadConfig(projectRoot) {
58
74
  const configPath = getConfigPath(projectRoot);
@@ -71,7 +87,7 @@ function loadConfig(projectRoot) {
71
87
  /**
72
88
  * Create initial config file in .heymark/config.json.
73
89
  * @param {string} projectRoot
74
- * @param {RuleBookConfig} config
90
+ * @param {THeymarkConfig} config
75
91
  * @returns {string}
76
92
  */
77
93
  function writeConfig(projectRoot, config) {
@@ -87,11 +103,11 @@ function writeConfig(projectRoot, config) {
87
103
 
88
104
  const configPath = path.join(configDir, CONFIG_FILENAME);
89
105
  const toWrite = {
90
- rulesSource: normalized.rulesSource,
106
+ repoUrl: normalized.repoUrl,
91
107
  branch: normalized.branch || DEFAULT_BRANCH,
92
108
  };
93
- if (normalized.rulesSourceDir) {
94
- toWrite.rulesSourceDir = normalized.rulesSourceDir;
109
+ if (normalized.folder) {
110
+ toWrite.folder = normalized.folder;
95
111
  }
96
112
 
97
113
  fs.writeFileSync(configPath, JSON.stringify(toWrite, null, 2), "utf8");
@@ -30,24 +30,24 @@ function sanitizeRepoName(repoUrl) {
30
30
  return normalized.replace(/[^a-zA-Z0-9._-]/g, "-") || "repo";
31
31
  }
32
32
 
33
- function cloneRulesRepo(projectRoot, clonePath, branch, repoUrl) {
33
+ function cloneLinkedRepo(projectRoot, clonePath, branch, repoUrl) {
34
34
  execSync(`git clone --depth 1 --branch "${branch}" "${repoUrl}" "${clonePath}"`, {
35
35
  stdio: "inherit",
36
36
  cwd: projectRoot,
37
37
  });
38
38
  }
39
39
 
40
- function updateRulesRepo(clonePath, branch) {
40
+ function updateLinkedRepo(clonePath, branch) {
41
41
  execSync(`git fetch origin && git checkout --quiet . && git pull --quiet origin "${branch}"`, {
42
42
  stdio: "pipe",
43
43
  cwd: clonePath,
44
44
  });
45
45
  }
46
46
 
47
- function resolveRulesDirectory(clonePath, rulesSourceDir) {
48
- const rulesDir = rulesSourceDir ? path.join(clonePath, rulesSourceDir) : clonePath;
47
+ function resolveLinkedFolder(clonePath, folder) {
48
+ const rulesDir = folder ? path.join(clonePath, folder) : clonePath;
49
49
  if (!fs.existsSync(rulesDir) || !fs.statSync(rulesDir).isDirectory()) {
50
- console.error(`[Error] Rules directory not found in repo: ${rulesSourceDir || "(root)"}`);
50
+ console.error(`[Error] Rules directory not found in repo: ${folder || "(root)"}`);
51
51
  process.exit(1);
52
52
  }
53
53
  return rulesDir;
@@ -57,13 +57,13 @@ function resolveRulesDirectory(clonePath, rulesSourceDir) {
57
57
  * Clone or update remote repository and return local rules directory.
58
58
  * Private repositories require user git credentials (SSH key or token).
59
59
  * @param {string} projectRoot
60
- * @param {{ rulesSource: string, branch?: string, rulesSourceDir?: string }} config
60
+ * @param {{ repoUrl: string, branch?: string, folder?: string }} config
61
61
  * @returns {string}
62
62
  */
63
- function getRulesDirFromRepo(projectRoot, config) {
64
- const repoUrl = config.rulesSource;
63
+ function getLinkedRulesDir(projectRoot, config) {
64
+ const repoUrl = config.repoUrl;
65
65
  const branch = config.branch || DEFAULT_BRANCH;
66
- const rulesSourceDir = config.rulesSourceDir || "";
66
+ const folder = config.folder || "";
67
67
 
68
68
  const cacheBase = path.join(projectRoot, CACHE_DIR_NAME);
69
69
  const repoName = sanitizeRepoName(repoUrl);
@@ -72,26 +72,26 @@ function getRulesDirFromRepo(projectRoot, config) {
72
72
  if (!fs.existsSync(clonePath)) {
73
73
  fs.mkdirSync(cacheBase, { recursive: true });
74
74
  try {
75
- cloneRulesRepo(projectRoot, clonePath, branch, repoUrl);
75
+ cloneLinkedRepo(projectRoot, clonePath, branch, repoUrl);
76
76
  } catch {
77
77
  console.error("[Error] Failed to clone rules repository.");
78
78
  console.error(" For private repos, ensure you have access (SSH key or HTTPS token).");
79
- console.error(" Example: heymark init https://github.com/org/repo.git");
79
+ console.error(" Example: heymark link https://github.com/org/repo.git");
80
80
  process.exit(1);
81
81
  }
82
82
  } else {
83
83
  try {
84
- updateRulesRepo(clonePath, branch);
84
+ updateLinkedRepo(clonePath, branch);
85
85
  } catch {
86
86
  // Continue with cached clone when fetch or pull fails.
87
87
  }
88
88
  }
89
89
 
90
- return resolveRulesDirectory(clonePath, rulesSourceDir);
90
+ return resolveLinkedFolder(clonePath, folder);
91
91
  }
92
92
 
93
93
  module.exports = {
94
94
  CACHE_DIR_NAME,
95
- getRulesDirFromRepo,
95
+ getLinkedRulesDir,
96
96
  sanitizeRepoName,
97
97
  };
@@ -38,16 +38,12 @@ module.exports = {
38
38
  },
39
39
 
40
40
  clean(ruleNames, projectRoot) {
41
- const cleaned = [];
42
-
43
- for (const ruleName of ruleNames) {
44
- const skillDir = getSkillDir(projectRoot, ruleName);
45
- if (fs.existsSync(skillDir)) {
46
- fs.rmSync(skillDir, { recursive: true });
47
- cleaned.push(path.join(SKILLS_DIR, ruleName));
48
- }
41
+ const skillsDirPath = path.join(projectRoot, SKILLS_DIR);
42
+ if (!fs.existsSync(skillsDirPath)) {
43
+ return [];
49
44
  }
50
45
 
51
- return cleaned;
46
+ fs.rmSync(skillsDirPath, { recursive: true, force: true });
47
+ return [SKILLS_DIR];
52
48
  },
53
49
  };
@@ -38,16 +38,12 @@ module.exports = {
38
38
  },
39
39
 
40
40
  clean(ruleNames, projectRoot) {
41
- const cleaned = [];
42
-
43
- for (const ruleName of ruleNames) {
44
- const skillDir = getSkillDir(projectRoot, ruleName);
45
- if (fs.existsSync(skillDir)) {
46
- fs.rmSync(skillDir, { recursive: true });
47
- cleaned.push(path.join(SKILLS_DIR, ruleName));
48
- }
41
+ const skillsDirPath = path.join(projectRoot, SKILLS_DIR);
42
+ if (!fs.existsSync(skillsDirPath)) {
43
+ return [];
49
44
  }
50
45
 
51
- return cleaned;
46
+ fs.rmSync(skillsDirPath, { recursive: true, force: true });
47
+ return [SKILLS_DIR];
52
48
  },
53
49
  };
@@ -38,16 +38,12 @@ module.exports = {
38
38
  },
39
39
 
40
40
  clean(ruleNames, projectRoot) {
41
- const cleaned = [];
42
-
43
- for (const ruleName of ruleNames) {
44
- const skillDir = getSkillDir(projectRoot, ruleName);
45
- if (fs.existsSync(skillDir)) {
46
- fs.rmSync(skillDir, { recursive: true });
47
- cleaned.push(path.join(SKILLS_DIR, ruleName));
48
- }
41
+ const skillsDirPath = path.join(projectRoot, SKILLS_DIR);
42
+ if (!fs.existsSync(skillsDirPath)) {
43
+ return [];
49
44
  }
50
45
 
51
- return cleaned;
46
+ fs.rmSync(skillsDirPath, { recursive: true, force: true });
47
+ return [SKILLS_DIR];
52
48
  },
53
49
  };
@@ -50,16 +50,12 @@ module.exports = {
50
50
  },
51
51
 
52
52
  clean(ruleNames, projectRoot) {
53
- const cleaned = [];
54
-
55
- for (const ruleName of ruleNames) {
56
- const filePath = getInstructionPath(projectRoot, ruleName);
57
- if (fs.existsSync(filePath)) {
58
- fs.unlinkSync(filePath);
59
- cleaned.push(path.join(INSTRUCTIONS_DIR, `${ruleName}${FILE_SUFFIX}`));
60
- }
53
+ const instructionsDirPath = path.join(projectRoot, INSTRUCTIONS_DIR);
54
+ if (!fs.existsSync(instructionsDirPath)) {
55
+ return [];
61
56
  }
62
57
 
63
- return cleaned;
58
+ fs.rmSync(instructionsDirPath, { recursive: true, force: true });
59
+ return [INSTRUCTIONS_DIR];
64
60
  },
65
61
  };
@@ -37,16 +37,12 @@ module.exports = {
37
37
  },
38
38
 
39
39
  clean(ruleNames, projectRoot) {
40
- const cleaned = [];
41
-
42
- for (const ruleName of ruleNames) {
43
- const filePath = getRulePath(projectRoot, ruleName);
44
- if (fs.existsSync(filePath)) {
45
- fs.unlinkSync(filePath);
46
- cleaned.push(path.join(RULES_DIR, `${ruleName}${FILE_SUFFIX}`));
47
- }
40
+ const rulesDirPath = path.join(projectRoot, RULES_DIR);
41
+ if (!fs.existsSync(rulesDirPath)) {
42
+ return [];
48
43
  }
49
44
 
50
- return cleaned;
45
+ fs.rmSync(rulesDirPath, { recursive: true, force: true });
46
+ return [RULES_DIR];
51
47
  },
52
48
  };