pruff-skills 0.1.0 → 0.2.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/README.md +9 -0
- package/dist/cli.mjs +198 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,15 @@ The catalog also ships **Claude Code subagents**. `find` lists them alongside sk
|
|
|
38
38
|
npx pruff-skills agents add <name> # → .claude/agents/ (add -g for ~/.claude/agents/)
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
It also ships **coding-convention rules** for the Pruff repos, each tagged by scope
|
|
42
|
+
(`client` / `server` / `common`, where `common` applies to both). `find` lists them tagged
|
|
43
|
+
`[rule]`. Install one by name, or a whole repo's set at once:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx pruff-skills rules add <name> # → .claude/rules/ (add -g for ~/.claude/rules/)
|
|
47
|
+
npx pruff-skills rules add --scope server # all server + common rules (client/server pull in common)
|
|
48
|
+
```
|
|
49
|
+
|
|
41
50
|
## Use a Skill Without Installing
|
|
42
51
|
|
|
43
52
|
Generate a prompt for one skill, or start a supported coding agent interactively:
|
package/dist/cli.mjs
CHANGED
|
@@ -434,7 +434,7 @@ const CLONE_TIMEOUT_MS = (() => {
|
|
|
434
434
|
const parsed = Number.parseInt(raw, 10);
|
|
435
435
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CLONE_TIMEOUT_MS;
|
|
436
436
|
})();
|
|
437
|
-
const execFileAsync$
|
|
437
|
+
const execFileAsync$3 = promisify(execFile);
|
|
438
438
|
var GitCloneError = class extends Error {
|
|
439
439
|
url;
|
|
440
440
|
isTimeout;
|
|
@@ -513,7 +513,7 @@ async function resetTempDir(dir) {
|
|
|
513
513
|
async function tryGhClone(repo, tempDir, ref) {
|
|
514
514
|
let cloneTarget = repo.slug;
|
|
515
515
|
try {
|
|
516
|
-
const { stdout, stderr } = await execFileAsync$
|
|
516
|
+
const { stdout, stderr } = await execFileAsync$3("gh", [
|
|
517
517
|
"auth",
|
|
518
518
|
"status",
|
|
519
519
|
"-h",
|
|
@@ -530,7 +530,7 @@ async function tryGhClone(repo, tempDir, ref) {
|
|
|
530
530
|
} catch {
|
|
531
531
|
return false;
|
|
532
532
|
}
|
|
533
|
-
await execFileAsync$
|
|
533
|
+
await execFileAsync$3("gh", [
|
|
534
534
|
"repo",
|
|
535
535
|
"clone",
|
|
536
536
|
cloneTarget,
|
|
@@ -3997,7 +3997,7 @@ function parseAddOptions(args) {
|
|
|
3997
3997
|
options
|
|
3998
3998
|
};
|
|
3999
3999
|
}
|
|
4000
|
-
const execFileAsync$
|
|
4000
|
+
const execFileAsync$2 = promisify(execFile);
|
|
4001
4001
|
const claudeAgentsDir = (global) => global ? join(process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude"), "agents") : join(process.cwd(), ".claude", "agents");
|
|
4002
4002
|
async function fetchAgentFile(source, name) {
|
|
4003
4003
|
const rel = `agents/${name}.md`;
|
|
@@ -4006,7 +4006,7 @@ async function fetchAgentFile(source, name) {
|
|
|
4006
4006
|
return existsSync(file) ? readFile(file, "utf8") : null;
|
|
4007
4007
|
}
|
|
4008
4008
|
if (/^[^/]+\/[^/]+$/.test(source)) try {
|
|
4009
|
-
const { stdout } = await execFileAsync$
|
|
4009
|
+
const { stdout } = await execFileAsync$2("gh", [
|
|
4010
4010
|
"api",
|
|
4011
4011
|
`repos/${source}/contents/${rel}`,
|
|
4012
4012
|
"--jq",
|
|
@@ -4103,6 +4103,152 @@ async function runAgents(args) {
|
|
|
4103
4103
|
console.log(`Unknown agents command: ${sub}`);
|
|
4104
4104
|
console.log(AGENTS_HELP);
|
|
4105
4105
|
}
|
|
4106
|
+
const execFileAsync$1 = promisify(execFile);
|
|
4107
|
+
const RULE_SCOPES = [
|
|
4108
|
+
"client",
|
|
4109
|
+
"server",
|
|
4110
|
+
"common"
|
|
4111
|
+
];
|
|
4112
|
+
const claudeRulesDir = (global) => global ? join(process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude"), "rules") : join(process.cwd(), ".claude", "rules");
|
|
4113
|
+
function stripFrontmatter(md) {
|
|
4114
|
+
const m = md.match(/^---\n[\s\S]*?\n---\n+/);
|
|
4115
|
+
return m ? md.slice(m[0].length) : md;
|
|
4116
|
+
}
|
|
4117
|
+
async function fetchFromCatalog(source, rel) {
|
|
4118
|
+
if (source.startsWith(".") || isAbsolute(source)) {
|
|
4119
|
+
const file = join(source, rel);
|
|
4120
|
+
return existsSync(file) ? readFile(file, "utf8") : null;
|
|
4121
|
+
}
|
|
4122
|
+
if (/^[^/]+\/[^/]+$/.test(source)) try {
|
|
4123
|
+
const { stdout } = await execFileAsync$1("gh", [
|
|
4124
|
+
"api",
|
|
4125
|
+
`repos/${source}/contents/${rel}`,
|
|
4126
|
+
"--jq",
|
|
4127
|
+
".content"
|
|
4128
|
+
], { env: {
|
|
4129
|
+
...process.env,
|
|
4130
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
4131
|
+
} });
|
|
4132
|
+
return Buffer.from(stdout.trim(), "base64").toString("utf8");
|
|
4133
|
+
} catch {
|
|
4134
|
+
return null;
|
|
4135
|
+
}
|
|
4136
|
+
try {
|
|
4137
|
+
const file = join(await cloneRepo(source), rel);
|
|
4138
|
+
return existsSync(file) ? readFile(file, "utf8") : null;
|
|
4139
|
+
} catch {
|
|
4140
|
+
return null;
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
const fetchRuleFile = (source, name) => fetchFromCatalog(source, `rules/${name}.md`);
|
|
4144
|
+
async function fetchRuleIndex(source) {
|
|
4145
|
+
const raw = await fetchFromCatalog(source, ".well-known/agent-skills/index.json");
|
|
4146
|
+
if (!raw) return [];
|
|
4147
|
+
try {
|
|
4148
|
+
return JSON.parse(raw).rules ?? [];
|
|
4149
|
+
} catch {
|
|
4150
|
+
return [];
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
function expandScopes(scopes) {
|
|
4154
|
+
const set = new Set(scopes);
|
|
4155
|
+
if (set.has("client") || set.has("server")) set.add("common");
|
|
4156
|
+
return set;
|
|
4157
|
+
}
|
|
4158
|
+
function parseRulesOptions(args) {
|
|
4159
|
+
const names = [];
|
|
4160
|
+
const options = {};
|
|
4161
|
+
const pushScope = (value) => {
|
|
4162
|
+
if (value && RULE_SCOPES.includes(value)) (options.scopes ??= []).push(value);
|
|
4163
|
+
else M.warn(`Ignoring invalid --scope value: ${import_picocolors.default.yellow(value ?? "(missing)")}`);
|
|
4164
|
+
};
|
|
4165
|
+
for (let i = 0; i < args.length; i++) {
|
|
4166
|
+
const arg = args[i];
|
|
4167
|
+
if (arg === "-g" || arg === "--global") options.global = true;
|
|
4168
|
+
else if (arg === "-y" || arg === "--yes") options.yes = true;
|
|
4169
|
+
else if (arg === "--all") options.all = true;
|
|
4170
|
+
else if (arg === "-s" || arg === "--scope") pushScope(args[++i]);
|
|
4171
|
+
else if (arg.startsWith("--scope=")) pushScope(arg.slice(8));
|
|
4172
|
+
else if (!arg.startsWith("-")) names.push(arg);
|
|
4173
|
+
}
|
|
4174
|
+
return {
|
|
4175
|
+
names,
|
|
4176
|
+
options
|
|
4177
|
+
};
|
|
4178
|
+
}
|
|
4179
|
+
async function addRules(names, options) {
|
|
4180
|
+
const source = PRUFF_CATALOG;
|
|
4181
|
+
const targetDir = claudeRulesDir(options.global ?? false);
|
|
4182
|
+
Ie(import_picocolors.default.cyan("pruff-skills rules"));
|
|
4183
|
+
let toInstall = names.filter(Boolean);
|
|
4184
|
+
if (options.all || options.scopes?.length) {
|
|
4185
|
+
const index = await fetchRuleIndex(source);
|
|
4186
|
+
if (index.length === 0) {
|
|
4187
|
+
M.error(`Could not read the rules catalog (${source}).`);
|
|
4188
|
+
process.exit(1);
|
|
4189
|
+
}
|
|
4190
|
+
const wanted = options.all ? null : expandScopes(options.scopes ?? []);
|
|
4191
|
+
const matched = index.filter((r) => wanted ? !!r.scope && wanted.has(r.scope) : true).map((r) => r.name);
|
|
4192
|
+
toInstall = Array.from(new Set([...toInstall, ...matched]));
|
|
4193
|
+
}
|
|
4194
|
+
if (toInstall.length === 0) {
|
|
4195
|
+
M.error("Specify a rule name, a --scope, or --all.");
|
|
4196
|
+
M.message(import_picocolors.default.dim("e.g. `pruff-skills rules add service-patterns`"));
|
|
4197
|
+
M.message(import_picocolors.default.dim("or `pruff-skills rules add --scope server`"));
|
|
4198
|
+
M.message(import_picocolors.default.dim("Browse them with `npx pruff-skills find`."));
|
|
4199
|
+
process.exit(1);
|
|
4200
|
+
}
|
|
4201
|
+
await mkdir(targetDir, { recursive: true });
|
|
4202
|
+
const installed = [];
|
|
4203
|
+
for (const name of toInstall) {
|
|
4204
|
+
if (!isBareSkillName(name)) {
|
|
4205
|
+
M.warn(`Skipping invalid rule name: ${import_picocolors.default.yellow(name)}`);
|
|
4206
|
+
continue;
|
|
4207
|
+
}
|
|
4208
|
+
const content = await fetchRuleFile(source, name);
|
|
4209
|
+
if (!content) {
|
|
4210
|
+
M.error(`Rule ${import_picocolors.default.yellow(name)} not found in the catalog (${source}).`);
|
|
4211
|
+
continue;
|
|
4212
|
+
}
|
|
4213
|
+
const dest = join(targetDir, `${sanitizeName(name)}.md`);
|
|
4214
|
+
await writeFile(dest, stripFrontmatter(content));
|
|
4215
|
+
installed.push(dest);
|
|
4216
|
+
M.success(`${import_picocolors.default.green(name)} → ${dest}`);
|
|
4217
|
+
}
|
|
4218
|
+
if (installed.length === 0) {
|
|
4219
|
+
Se(import_picocolors.default.red("No rules installed."));
|
|
4220
|
+
process.exit(1);
|
|
4221
|
+
}
|
|
4222
|
+
Se(`Installed ${installed.length} rule${installed.length === 1 ? "" : "s"} into ${import_picocolors.default.dim(targetDir)}.`);
|
|
4223
|
+
}
|
|
4224
|
+
const RULES_HELP = `${import_picocolors.default.bold("Usage:")} pruff-skills rules <command> [options]
|
|
4225
|
+
|
|
4226
|
+
${import_picocolors.default.bold("Commands:")}
|
|
4227
|
+
add <name...> Install one or more rules into .claude/rules/
|
|
4228
|
+
|
|
4229
|
+
${import_picocolors.default.bold("Options:")}
|
|
4230
|
+
-s, --scope <s> Install every rule in a scope: client | server | common
|
|
4231
|
+
(client/server also pull in common). Repeatable.
|
|
4232
|
+
--all Install every rule in the catalog
|
|
4233
|
+
-g, --global Install to ~/.claude/rules/ instead of the project
|
|
4234
|
+
-y, --yes Skip prompts
|
|
4235
|
+
|
|
4236
|
+
${import_picocolors.default.bold("Examples:")}
|
|
4237
|
+
pruff-skills rules add service-patterns bigint-handling
|
|
4238
|
+
pruff-skills rules add --scope server ${import_picocolors.default.dim("# all server + common rules")}
|
|
4239
|
+
pruff-skills rules add --scope client -g
|
|
4240
|
+
pruff-skills rules add --all
|
|
4241
|
+
|
|
4242
|
+
Browse available rules with ${import_picocolors.default.cyan("npx pruff-skills find")}.`;
|
|
4243
|
+
async function runRules(args) {
|
|
4244
|
+
const sub = args[0];
|
|
4245
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
4246
|
+
console.log(RULES_HELP);
|
|
4247
|
+
return;
|
|
4248
|
+
}
|
|
4249
|
+
const { names, options } = parseRulesOptions(sub === "add" || sub === "a" || sub === "install" || sub === "i" ? args.slice(1) : args);
|
|
4250
|
+
await addRules(names, options);
|
|
4251
|
+
}
|
|
4106
4252
|
const execFileAsync = promisify(execFile);
|
|
4107
4253
|
const RESET$3 = "\x1B[0m";
|
|
4108
4254
|
const BOLD$3 = "\x1B[1m";
|
|
@@ -4111,6 +4257,7 @@ const TEXT$2 = "\x1B[38;5;145m";
|
|
|
4111
4257
|
const CYAN$1 = "\x1B[36m";
|
|
4112
4258
|
const GREEN = "\x1B[32m";
|
|
4113
4259
|
const MAGENTA = "\x1B[35m";
|
|
4260
|
+
const BLUE$1 = "\x1B[38;2;110;139;255m";
|
|
4114
4261
|
const CATALOG_SOURCE = process.env.PRUFF_SKILLS_CATALOG || "Pruff-Tech/pruff-skills";
|
|
4115
4262
|
async function fetchCatalogIndex(source) {
|
|
4116
4263
|
const rel = ".well-known/agent-skills/index.json";
|
|
@@ -4149,6 +4296,10 @@ function agentInstalled(name) {
|
|
|
4149
4296
|
const globalClaude = process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude");
|
|
4150
4297
|
return existsSync(join(process.cwd(), ".claude", "agents", `${name}.md`)) || existsSync(join(globalClaude, "agents", `${name}.md`));
|
|
4151
4298
|
}
|
|
4299
|
+
function ruleInstalled(name) {
|
|
4300
|
+
const globalClaude = process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude");
|
|
4301
|
+
return existsSync(join(process.cwd(), ".claude", "rules", `${name}.md`)) || existsSync(join(globalClaude, "rules", `${name}.md`));
|
|
4302
|
+
}
|
|
4152
4303
|
let catalogCache = null;
|
|
4153
4304
|
async function loadCatalog() {
|
|
4154
4305
|
if (catalogCache) return catalogCache;
|
|
@@ -4157,6 +4308,8 @@ async function loadCatalog() {
|
|
|
4157
4308
|
const installedSkills = await installedSkillNames();
|
|
4158
4309
|
const toEntry = (e, kind) => {
|
|
4159
4310
|
const name = sanitizeMetadata(e.name);
|
|
4311
|
+
const badge = kind === "rule" ? e.scope : e.category;
|
|
4312
|
+
const installed = kind === "agent" ? agentInstalled(name) : kind === "rule" ? ruleInstalled(name) : installedSkills.has(name);
|
|
4160
4313
|
return {
|
|
4161
4314
|
name,
|
|
4162
4315
|
slug: name,
|
|
@@ -4164,11 +4317,15 @@ async function loadCatalog() {
|
|
|
4164
4317
|
installs: 0,
|
|
4165
4318
|
description: sanitizeMetadata(e.description || ""),
|
|
4166
4319
|
kind,
|
|
4167
|
-
category:
|
|
4168
|
-
installed
|
|
4320
|
+
category: badge ? sanitizeMetadata(badge) : void 0,
|
|
4321
|
+
installed
|
|
4169
4322
|
};
|
|
4170
4323
|
};
|
|
4171
|
-
catalogCache = [
|
|
4324
|
+
catalogCache = [
|
|
4325
|
+
...(index.skills ?? []).map((e) => toEntry(e, "skill")),
|
|
4326
|
+
...(index.agents ?? []).map((e) => toEntry(e, "agent")),
|
|
4327
|
+
...(index.rules ?? []).map((e) => toEntry(e, "rule"))
|
|
4328
|
+
];
|
|
4172
4329
|
} catch {
|
|
4173
4330
|
catalogCache = [];
|
|
4174
4331
|
}
|
|
@@ -4199,7 +4356,7 @@ async function runSearchPrompt(kind, initialQuery = "") {
|
|
|
4199
4356
|
let query = initialQuery;
|
|
4200
4357
|
let loading = false;
|
|
4201
4358
|
let lastRenderedLines = 0;
|
|
4202
|
-
const noun = kind === "agent" ? "subagents" : "skills";
|
|
4359
|
+
const noun = kind === "agent" ? "subagents" : kind === "rule" ? "rules" : "skills";
|
|
4203
4360
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
4204
4361
|
readline.emitKeypressEvents(process.stdin);
|
|
4205
4362
|
process.stdin.resume();
|
|
@@ -4316,10 +4473,10 @@ ${DIM$3} 2) npx pruff-skills add <skill>${RESET$3}`;
|
|
|
4316
4473
|
console.log(`${DIM$3}Nothing in the catalog matches "${query}"${RESET$3}`);
|
|
4317
4474
|
return;
|
|
4318
4475
|
}
|
|
4319
|
-
console.log(`${DIM$3}Install a skill with${RESET$3} npx pruff-skills add <name>${DIM$3}, a subagent with${RESET$3} npx pruff-skills agents add <name>`);
|
|
4476
|
+
console.log(`${DIM$3}Install a skill with${RESET$3} npx pruff-skills add <name>${DIM$3}, a subagent with${RESET$3} npx pruff-skills agents add <name>${DIM$3}, a rule with${RESET$3} npx pruff-skills rules add <name>`);
|
|
4320
4477
|
console.log();
|
|
4321
4478
|
for (const skill of results.slice(0, 10)) {
|
|
4322
|
-
const kindBadge = skill.kind === "agent" ? ` ${MAGENTA}[subagent]${RESET$3}` : ` ${CYAN$1}[skill]${RESET$3}`;
|
|
4479
|
+
const kindBadge = skill.kind === "agent" ? ` ${MAGENTA}[subagent]${RESET$3}` : skill.kind === "rule" ? ` ${BLUE$1}[rule]${RESET$3}` : ` ${CYAN$1}[skill]${RESET$3}`;
|
|
4323
4480
|
console.log(`${TEXT$2}${skill.name}${RESET$3}${kindBadge}${rowBadge(skill)}`);
|
|
4324
4481
|
if (skill.description) console.log(`${DIM$3}└ ${skill.description}${RESET$3}`);
|
|
4325
4482
|
console.log();
|
|
@@ -4335,19 +4492,28 @@ ${DIM$3} 2) npx pruff-skills add <skill>${RESET$3}`;
|
|
|
4335
4492
|
const all = await searchSkillsAPI("");
|
|
4336
4493
|
const skillCount = all.filter((s) => s.kind === "skill").length;
|
|
4337
4494
|
const agentCount = all.filter((s) => s.kind === "agent").length;
|
|
4495
|
+
const ruleCount = all.filter((s) => s.kind === "rule").length;
|
|
4338
4496
|
let selected = "back";
|
|
4339
4497
|
while (selected === "back") {
|
|
4340
4498
|
const kindChoice = await ve({
|
|
4341
4499
|
message: "What are you looking for?",
|
|
4342
|
-
options: [
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4500
|
+
options: [
|
|
4501
|
+
{
|
|
4502
|
+
value: "skill",
|
|
4503
|
+
label: "Skills",
|
|
4504
|
+
hint: `${skillCount} available`
|
|
4505
|
+
},
|
|
4506
|
+
{
|
|
4507
|
+
value: "agent",
|
|
4508
|
+
label: "Subagents",
|
|
4509
|
+
hint: `${agentCount} available`
|
|
4510
|
+
},
|
|
4511
|
+
{
|
|
4512
|
+
value: "rule",
|
|
4513
|
+
label: "Rules",
|
|
4514
|
+
hint: `${ruleCount} available`
|
|
4515
|
+
}
|
|
4516
|
+
]
|
|
4351
4517
|
});
|
|
4352
4518
|
if (pD(kindChoice)) {
|
|
4353
4519
|
console.log(`${DIM$3}Search cancelled${RESET$3}`);
|
|
@@ -4365,6 +4531,7 @@ ${DIM$3} 2) npx pruff-skills add <skill>${RESET$3}`;
|
|
|
4365
4531
|
const skillName = selected.name;
|
|
4366
4532
|
console.log();
|
|
4367
4533
|
if (selected.kind === "agent") await addAgents([skillName], {});
|
|
4534
|
+
else if (selected.kind === "rule") await addRules([skillName], {});
|
|
4368
4535
|
else {
|
|
4369
4536
|
const pkg = selected.source || selected.slug;
|
|
4370
4537
|
console.log(`${TEXT$2}Installing ${BOLD$3}${skillName}${RESET$3} from ${DIM$3}${pkg}${RESET$3}...`);
|
|
@@ -6012,6 +6179,7 @@ async function showBanner() {
|
|
|
6012
6179
|
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills list${RESET} ${DIM}List installed skills${RESET}`);
|
|
6013
6180
|
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills find ${DIM}[query]${RESET} ${DIM}Search the Pruff catalog${RESET}`);
|
|
6014
6181
|
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills agents add ${DIM}<name>${RESET} ${DIM}Add a Pruff subagent${RESET}`);
|
|
6182
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills rules add ${DIM}<name>${RESET} ${DIM}Add a Pruff rule (.claude/rules/)${RESET}`);
|
|
6015
6183
|
console.log();
|
|
6016
6184
|
console.log(` ${DIM}$${RESET} ${TEXT}npx pruff-skills update${RESET} ${DIM}Update installed skills${RESET}`);
|
|
6017
6185
|
console.log();
|
|
@@ -6039,6 +6207,11 @@ ${BOLD}Manage Skills:${RESET}
|
|
|
6039
6207
|
${BOLD}Subagents:${RESET}
|
|
6040
6208
|
agents add <name> Install a Claude Code subagent into .claude/agents/ (-g for global)
|
|
6041
6209
|
|
|
6210
|
+
${BOLD}Rules:${RESET}
|
|
6211
|
+
rules add <name> Install a coding-convention rule into .claude/rules/ (-g for global)
|
|
6212
|
+
rules add --scope <client|server|common>
|
|
6213
|
+
Install a whole repo's rule set (client/server also pull in common)
|
|
6214
|
+
|
|
6042
6215
|
${BOLD}Updates:${RESET}
|
|
6043
6216
|
update [skills...] Update skills to latest versions (alias: upgrade)
|
|
6044
6217
|
|
|
@@ -6239,6 +6412,11 @@ async function main() {
|
|
|
6239
6412
|
if (!inAgent) await showLogo();
|
|
6240
6413
|
await runAgents(restArgs);
|
|
6241
6414
|
break;
|
|
6415
|
+
case "rules":
|
|
6416
|
+
case "rule":
|
|
6417
|
+
if (!inAgent) await showLogo();
|
|
6418
|
+
await runRules(restArgs);
|
|
6419
|
+
break;
|
|
6242
6420
|
case "remove":
|
|
6243
6421
|
case "rm":
|
|
6244
6422
|
case "r":
|