@agentrules/cli 0.3.0 → 0.3.2
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 +2 -1
- package/dist/index.js +527 -421
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -193,8 +193,9 @@ agentrules publish [path] [options]
|
|
|
193
193
|
# Publish current directory
|
|
194
194
|
agentrules publish
|
|
195
195
|
|
|
196
|
-
# Publish a specific
|
|
196
|
+
# Publish a specific directory or file (interactive if no agentrules.json)
|
|
197
197
|
agentrules publish ./my-rule
|
|
198
|
+
agentrules publish .claude/commands/deploy.md
|
|
198
199
|
|
|
199
200
|
# Publish to major version 2
|
|
200
201
|
agentrules publish --version 2
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { chmod, constants as constants$1 } from "fs";
|
|
|
9
9
|
import * as client from "openid-client";
|
|
10
10
|
import { promisify } from "util";
|
|
11
11
|
import { exec } from "child_process";
|
|
12
|
+
import * as p$2 from "@clack/prompts";
|
|
12
13
|
import * as p$1 from "@clack/prompts";
|
|
13
14
|
import * as p from "@clack/prompts";
|
|
14
15
|
|
|
@@ -34,6 +35,11 @@ const RULE_TYPE_TUPLE = [
|
|
|
34
35
|
|
|
35
36
|
//#endregion
|
|
36
37
|
//#region ../core/src/platform/config.ts
|
|
38
|
+
/**
|
|
39
|
+
* Placeholder for user's home directory in path templates.
|
|
40
|
+
* Consumers (e.g., CLI) are responsible for expanding this at runtime.
|
|
41
|
+
*/
|
|
42
|
+
const USER_HOME_DIR_PLACEHOLDER = "{userHomeDir}";
|
|
37
43
|
const PLATFORM_IDS = PLATFORM_ID_TUPLE;
|
|
38
44
|
/**
|
|
39
45
|
* Platform configuration including supported types and install paths.
|
|
@@ -42,7 +48,7 @@ const PLATFORMS = {
|
|
|
42
48
|
opencode: {
|
|
43
49
|
label: "OpenCode",
|
|
44
50
|
platformDir: ".opencode",
|
|
45
|
-
globalDir: "
|
|
51
|
+
globalDir: "{userHomeDir}/.config/opencode",
|
|
46
52
|
types: {
|
|
47
53
|
instruction: {
|
|
48
54
|
description: "Project instructions",
|
|
@@ -74,7 +80,7 @@ const PLATFORMS = {
|
|
|
74
80
|
claude: {
|
|
75
81
|
label: "Claude Code",
|
|
76
82
|
platformDir: ".claude",
|
|
77
|
-
globalDir: "
|
|
83
|
+
globalDir: "{userHomeDir}/.claude",
|
|
78
84
|
types: {
|
|
79
85
|
instruction: {
|
|
80
86
|
description: "Project instructions",
|
|
@@ -101,7 +107,7 @@ const PLATFORMS = {
|
|
|
101
107
|
cursor: {
|
|
102
108
|
label: "Cursor",
|
|
103
109
|
platformDir: ".cursor",
|
|
104
|
-
globalDir: "
|
|
110
|
+
globalDir: "{userHomeDir}/.cursor",
|
|
105
111
|
types: {
|
|
106
112
|
instruction: {
|
|
107
113
|
description: "Project instructions",
|
|
@@ -128,7 +134,7 @@ const PLATFORMS = {
|
|
|
128
134
|
codex: {
|
|
129
135
|
label: "Codex",
|
|
130
136
|
platformDir: ".codex",
|
|
131
|
-
globalDir: "
|
|
137
|
+
globalDir: "{userHomeDir}/.codex",
|
|
132
138
|
types: {
|
|
133
139
|
instruction: {
|
|
134
140
|
description: "Project instructions",
|
|
@@ -182,7 +188,7 @@ function getInstallPath({ platform, type, name, scope = "project" }) {
|
|
|
182
188
|
* Returns the path relative to the platform's root directory.
|
|
183
189
|
*
|
|
184
190
|
* Example: For codex instruction with scope="global", returns "AGENTS.md"
|
|
185
|
-
* (not
|
|
191
|
+
* (not the full path with {userHomeDir})
|
|
186
192
|
*/
|
|
187
193
|
function getRelativeInstallPath({ platform, type, name, scope = "project" }) {
|
|
188
194
|
const typeConfig = getTypeConfig(platform, type);
|
|
@@ -1989,7 +1995,7 @@ const $ZodUnion = /* @__PURE__ */ $constructor("$ZodUnion", (inst, def) => {
|
|
|
1989
1995
|
defineLazy(inst._zod, "pattern", () => {
|
|
1990
1996
|
if (def.options.every((o) => o._zod.pattern)) {
|
|
1991
1997
|
const patterns = def.options.map((o) => o._zod.pattern);
|
|
1992
|
-
return new RegExp(`^(${patterns.map((p$
|
|
1998
|
+
return new RegExp(`^(${patterns.map((p$3) => cleanRegex(p$3.source)).join("|")})$`);
|
|
1993
1999
|
}
|
|
1994
2000
|
return void 0;
|
|
1995
2001
|
});
|
|
@@ -2368,9 +2374,9 @@ var $ZodRegistry = class {
|
|
|
2368
2374
|
return this;
|
|
2369
2375
|
}
|
|
2370
2376
|
get(schema) {
|
|
2371
|
-
const p$
|
|
2372
|
-
if (p$
|
|
2373
|
-
const pm = { ...this.get(p$
|
|
2377
|
+
const p$3 = schema._zod.parent;
|
|
2378
|
+
if (p$3) {
|
|
2379
|
+
const pm = { ...this.get(p$3) ?? {} };
|
|
2374
2380
|
delete pm.id;
|
|
2375
2381
|
const f = {
|
|
2376
2382
|
...pm,
|
|
@@ -3504,6 +3510,21 @@ const COMMON_LICENSES = [
|
|
|
3504
3510
|
];
|
|
3505
3511
|
const licenseSchema = string().trim().min(1, "License is required").max(128, "License must be 128 characters or less");
|
|
3506
3512
|
const pathSchema = string().trim().min(1);
|
|
3513
|
+
/**
|
|
3514
|
+
* Validate a bundle file path for security.
|
|
3515
|
+
* Rejects paths that could escape the install directory.
|
|
3516
|
+
*
|
|
3517
|
+
* @returns true if path is safe, false otherwise
|
|
3518
|
+
*/
|
|
3519
|
+
function isValidBundlePath(path$1) {
|
|
3520
|
+
if (path$1.includes("..")) return false;
|
|
3521
|
+
if (path$1.startsWith("/")) return false;
|
|
3522
|
+
if (path$1.startsWith("~")) return false;
|
|
3523
|
+
if (path$1.includes("/~/") || path$1.includes("\\~\\")) return false;
|
|
3524
|
+
if (path$1.includes("{userHomeDir}")) return false;
|
|
3525
|
+
return true;
|
|
3526
|
+
}
|
|
3527
|
+
const bundlePathSchema = string().min(1).refine(isValidBundlePath, { message: "Path must be relative without traversal (no .., ~, absolute paths, or {userHomeDir})" });
|
|
3507
3528
|
const ignorePatternSchema = string().trim().min(1, "Ignore pattern cannot be empty");
|
|
3508
3529
|
const ignoreSchema = array(ignorePatternSchema).max(50, "Maximum 50 ignore patterns allowed");
|
|
3509
3530
|
/**
|
|
@@ -3548,7 +3569,7 @@ const ruleConfigSchema = object({
|
|
|
3548
3569
|
platforms: array(platformEntrySchema).min(1, "At least one platform is required")
|
|
3549
3570
|
}).strict();
|
|
3550
3571
|
const bundledFileSchema = object({
|
|
3551
|
-
path:
|
|
3572
|
+
path: bundlePathSchema,
|
|
3552
3573
|
size: number().int().nonnegative(),
|
|
3553
3574
|
checksum: string().length(64),
|
|
3554
3575
|
content: string()
|
|
@@ -4375,8 +4396,8 @@ function command(cmd) {
|
|
|
4375
4396
|
return theme.command(cmd);
|
|
4376
4397
|
}
|
|
4377
4398
|
/** Format a file path */
|
|
4378
|
-
function path(p$
|
|
4379
|
-
return theme.path(p$
|
|
4399
|
+
function path(p$3) {
|
|
4400
|
+
return theme.path(p$3);
|
|
4380
4401
|
}
|
|
4381
4402
|
/** Format as muted/secondary text */
|
|
4382
4403
|
function muted(text) {
|
|
@@ -4694,6 +4715,22 @@ function fileTree(files, options) {
|
|
|
4694
4715
|
});
|
|
4695
4716
|
return lines.join("\n");
|
|
4696
4717
|
}
|
|
4718
|
+
function rulePreview(options) {
|
|
4719
|
+
const lines = [];
|
|
4720
|
+
lines.push(header(options.header));
|
|
4721
|
+
if (options.path) lines.push(keyValue(options.pathLabel ?? "Path", path(options.path)));
|
|
4722
|
+
lines.push(keyValue("Name", code(options.name)));
|
|
4723
|
+
lines.push(keyValue("Title", options.title));
|
|
4724
|
+
lines.push(keyValue("Description", options.description || dim("—")));
|
|
4725
|
+
lines.push(keyValue("Platforms", options.platforms.join(", ")));
|
|
4726
|
+
if (options.type) lines.push(keyValue("Type", options.type));
|
|
4727
|
+
if (options.tags && options.tags.length > 0) lines.push(keyValue("Tags", options.tags.join(", ")));
|
|
4728
|
+
else if (options.showHints) lines.push(keyValue("Tags", dim("— (add to improve discoverability)")));
|
|
4729
|
+
if (options.features && options.features.length > 0) lines.push(keyValue("Features", options.features.join(", ")));
|
|
4730
|
+
else if (options.showHints) lines.push(keyValue("Features", dim("— (highlight what this rule does)")));
|
|
4731
|
+
if (options.license) lines.push(keyValue("License", options.license));
|
|
4732
|
+
return lines.join("\n");
|
|
4733
|
+
}
|
|
4697
4734
|
const ui = {
|
|
4698
4735
|
theme,
|
|
4699
4736
|
symbols,
|
|
@@ -4730,6 +4767,7 @@ const ui = {
|
|
|
4730
4767
|
fileCounts,
|
|
4731
4768
|
hint,
|
|
4732
4769
|
link,
|
|
4770
|
+
rulePreview,
|
|
4733
4771
|
stripAnsi,
|
|
4734
4772
|
truncate,
|
|
4735
4773
|
formatBytes: formatBytes$1,
|
|
@@ -5593,7 +5631,7 @@ function resolveInstallTarget(platform, type, name, options) {
|
|
|
5593
5631
|
const { platformDir, globalDir } = PLATFORMS[platform];
|
|
5594
5632
|
if (options.global) {
|
|
5595
5633
|
if (!globalDir) throw new Error(`Platform "${platform}" does not support global installation`);
|
|
5596
|
-
const globalRoot = resolve(
|
|
5634
|
+
const globalRoot = resolve(expandUserHomeDir(globalDir));
|
|
5597
5635
|
return {
|
|
5598
5636
|
root: globalRoot,
|
|
5599
5637
|
mode: "global",
|
|
@@ -5605,7 +5643,7 @@ function resolveInstallTarget(platform, type, name, options) {
|
|
|
5605
5643
|
label: `global path ${globalRoot}`
|
|
5606
5644
|
};
|
|
5607
5645
|
}
|
|
5608
|
-
const projectRoot = options.directory ? resolve(
|
|
5646
|
+
const projectRoot = options.directory ? resolve(expandUserHomeDir(options.directory)) : process.cwd();
|
|
5609
5647
|
const label = options.directory ? `directory ${projectRoot}` : `project root ${projectRoot}`;
|
|
5610
5648
|
return {
|
|
5611
5649
|
root: projectRoot,
|
|
@@ -5705,15 +5743,20 @@ function ensureWithinRoot(candidate, root) {
|
|
|
5705
5743
|
if (candidate === root) return;
|
|
5706
5744
|
if (!candidate.startsWith(normalizedRoot)) throw new Error(`Refusing to write outside of ${root}. Derived path: ${candidate}`);
|
|
5707
5745
|
}
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5746
|
+
/**
|
|
5747
|
+
* Expand home directory references in a path string.
|
|
5748
|
+
* Handles both {userHomeDir} placeholder and ~ prefix.
|
|
5749
|
+
*/
|
|
5750
|
+
function expandUserHomeDir(path$1) {
|
|
5751
|
+
const home = process.env.HOME || homedir();
|
|
5752
|
+
if (path$1.includes(USER_HOME_DIR_PLACEHOLDER)) return path$1.replace(USER_HOME_DIR_PLACEHOLDER, home);
|
|
5753
|
+
if (path$1.startsWith("~")) {
|
|
5754
|
+
const remainder = path$1.slice(1);
|
|
5712
5755
|
if (!remainder) return home;
|
|
5713
5756
|
if (remainder.startsWith("/") || remainder.startsWith("\\")) return `${home}${remainder}`;
|
|
5714
5757
|
return `${home}/${remainder}`;
|
|
5715
5758
|
}
|
|
5716
|
-
return
|
|
5759
|
+
return path$1;
|
|
5717
5760
|
}
|
|
5718
5761
|
|
|
5719
5762
|
//#endregion
|
|
@@ -5882,6 +5925,20 @@ async function directoryExists(path$1) {
|
|
|
5882
5925
|
}
|
|
5883
5926
|
}
|
|
5884
5927
|
|
|
5928
|
+
//#endregion
|
|
5929
|
+
//#region src/lib/zod-validator.ts
|
|
5930
|
+
/**
|
|
5931
|
+
* Creates a validator function from a Zod schema.
|
|
5932
|
+
* Returns error message if invalid, undefined if valid.
|
|
5933
|
+
*/
|
|
5934
|
+
function check(schema) {
|
|
5935
|
+
return (value) => {
|
|
5936
|
+
const result = schema.safeParse(value);
|
|
5937
|
+
if (!result.success) return result.error.issues[0]?.message;
|
|
5938
|
+
return;
|
|
5939
|
+
};
|
|
5940
|
+
}
|
|
5941
|
+
|
|
5885
5942
|
//#endregion
|
|
5886
5943
|
//#region src/lib/rule-utils.ts
|
|
5887
5944
|
const SKILL_FILENAME = "SKILL.md";
|
|
@@ -6005,7 +6062,7 @@ async function loadConfig(inputPath, overrides) {
|
|
|
6005
6062
|
platforms
|
|
6006
6063
|
};
|
|
6007
6064
|
const configDir = dirname(configPath);
|
|
6008
|
-
const platformNames = platforms.map((
|
|
6065
|
+
const platformNames = platforms.map((plat) => plat.platform).join(", ");
|
|
6009
6066
|
log.debug(`Loaded config: ${config$1.name}, platforms: ${platformNames}`);
|
|
6010
6067
|
return {
|
|
6011
6068
|
configPath,
|
|
@@ -6191,18 +6248,270 @@ async function readFirstMatch(dir, filenames) {
|
|
|
6191
6248
|
}
|
|
6192
6249
|
return;
|
|
6193
6250
|
}
|
|
6251
|
+
/**
|
|
6252
|
+
* Detect if directory contains SKILL.md and extract frontmatter defaults.
|
|
6253
|
+
*/
|
|
6254
|
+
async function detectSkillDirectory$1(directory) {
|
|
6255
|
+
const skillPath = join(directory, SKILL_FILENAME);
|
|
6256
|
+
if (!await fileExists(skillPath)) return;
|
|
6257
|
+
const content = await readFile(skillPath, "utf8");
|
|
6258
|
+
const frontmatter = parseSkillFrontmatter(content);
|
|
6259
|
+
return {
|
|
6260
|
+
name: frontmatter.name,
|
|
6261
|
+
license: frontmatter.license
|
|
6262
|
+
};
|
|
6263
|
+
}
|
|
6264
|
+
function parseTags(input) {
|
|
6265
|
+
if (typeof input !== "string") return [];
|
|
6266
|
+
if (input.trim().length === 0) return [];
|
|
6267
|
+
return input.split(",").map((tag) => tag.trim().toLowerCase()).filter((tag) => tag.length > 0);
|
|
6268
|
+
}
|
|
6269
|
+
function checkTags(value) {
|
|
6270
|
+
const tags = parseTags(value);
|
|
6271
|
+
const result = tagsSchema.safeParse(tags);
|
|
6272
|
+
if (!result.success) return result.error.issues[0]?.message;
|
|
6273
|
+
}
|
|
6274
|
+
/**
|
|
6275
|
+
* Collect rule inputs via interactive prompts or defaults.
|
|
6276
|
+
*
|
|
6277
|
+
* Handles:
|
|
6278
|
+
* - Skill detection with SKILL.md frontmatter
|
|
6279
|
+
* - Platform multiselect with keyboard hints
|
|
6280
|
+
* - Per-platform path prompting (for non-skill multi-platform)
|
|
6281
|
+
* - Name, title, description, tags, license prompts
|
|
6282
|
+
*/
|
|
6283
|
+
async function collectRuleInputs(options) {
|
|
6284
|
+
const { directory, defaults = {}, nonInteractive = false } = options;
|
|
6285
|
+
const skillInfo = await detectSkillDirectory$1(directory);
|
|
6286
|
+
let isSkill = false;
|
|
6287
|
+
if (skillInfo && !nonInteractive) {
|
|
6288
|
+
const confirm = await p$2.confirm({
|
|
6289
|
+
message: `Detected SKILL.md${skillInfo.name ? ` (${skillInfo.name})` : ""}. Initialize as skill?`,
|
|
6290
|
+
initialValue: true
|
|
6291
|
+
});
|
|
6292
|
+
if (p$2.isCancel(confirm)) throw new Error("Cancelled");
|
|
6293
|
+
isSkill = confirm;
|
|
6294
|
+
} else if (skillInfo && nonInteractive) isSkill = true;
|
|
6295
|
+
const defaultName = isSkill && skillInfo?.name ? normalizeName(skillInfo.name) : defaults.name ?? "my-rule";
|
|
6296
|
+
const defaultLicense = isSkill && skillInfo?.license ? skillInfo.license : defaults.license ?? "MIT";
|
|
6297
|
+
const validatedPlatforms = [];
|
|
6298
|
+
if (defaults.platforms) for (const platform of defaults.platforms) {
|
|
6299
|
+
if (!isSupportedPlatform(platform)) throw new Error(`Unknown platform "${platform}"`);
|
|
6300
|
+
validatedPlatforms.push(platform);
|
|
6301
|
+
}
|
|
6302
|
+
const selectedPlatforms = validatedPlatforms.length > 0 ? validatedPlatforms : await (async () => {
|
|
6303
|
+
if (nonInteractive) throw new Error("Missing --platform in non-interactive mode");
|
|
6304
|
+
const platformChoices = await p$2.multiselect({
|
|
6305
|
+
message: `Platforms ${ui.dim("(space to toggle, 'a' to select all)")}`,
|
|
6306
|
+
options: PLATFORM_IDS.map((id) => ({
|
|
6307
|
+
value: id,
|
|
6308
|
+
label: id
|
|
6309
|
+
})),
|
|
6310
|
+
required: true
|
|
6311
|
+
});
|
|
6312
|
+
if (p$2.isCancel(platformChoices)) throw new Error("Cancelled");
|
|
6313
|
+
return platformChoices;
|
|
6314
|
+
})();
|
|
6315
|
+
const platformPaths = {};
|
|
6316
|
+
if (selectedPlatforms.length > 1 && !isSkill && !nonInteractive) {
|
|
6317
|
+
const hasCompletePathMapping = selectedPlatforms.every((platform) => {
|
|
6318
|
+
const value = defaults.platformPaths?.[platform];
|
|
6319
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
6320
|
+
});
|
|
6321
|
+
if (hasCompletePathMapping && defaults.platformPaths) for (const platform of selectedPlatforms) {
|
|
6322
|
+
const pathVal = defaults.platformPaths[platform]?.trim();
|
|
6323
|
+
if (pathVal && pathVal !== ".") platformPaths[platform] = pathVal;
|
|
6324
|
+
}
|
|
6325
|
+
else for (const platform of selectedPlatforms) {
|
|
6326
|
+
const mappedPath = defaults.platformPaths?.[platform]?.trim();
|
|
6327
|
+
const suggestedPath = mappedPath ?? (await directoryExists(join(directory, platform)) ? platform : ".");
|
|
6328
|
+
const input = await p$2.text({
|
|
6329
|
+
message: `Folder for ${platform} files ('.' = same folder as agentrules.json)`,
|
|
6330
|
+
placeholder: suggestedPath,
|
|
6331
|
+
defaultValue: suggestedPath
|
|
6332
|
+
});
|
|
6333
|
+
if (p$2.isCancel(input)) throw new Error("Cancelled");
|
|
6334
|
+
const trimmed = input.trim();
|
|
6335
|
+
const resolvedPath = trimmed.length > 0 ? trimmed : suggestedPath;
|
|
6336
|
+
if (resolvedPath !== ".") platformPaths[platform] = resolvedPath;
|
|
6337
|
+
}
|
|
6338
|
+
} else if (defaults.platformPaths) for (const platform of selectedPlatforms) {
|
|
6339
|
+
const pathVal = defaults.platformPaths[platform]?.trim();
|
|
6340
|
+
if (pathVal && pathVal !== ".") platformPaths[platform] = pathVal;
|
|
6341
|
+
}
|
|
6342
|
+
if (nonInteractive) {
|
|
6343
|
+
const name = normalizeName(defaults.name ?? defaultName);
|
|
6344
|
+
const nameCheck = nameSchema.safeParse(name);
|
|
6345
|
+
if (!nameCheck.success) throw new Error(nameCheck.error.issues[0]?.message ?? "Invalid name");
|
|
6346
|
+
return {
|
|
6347
|
+
platforms: selectedPlatforms,
|
|
6348
|
+
platformPaths,
|
|
6349
|
+
name,
|
|
6350
|
+
title: defaults.title ?? toTitleCase(name),
|
|
6351
|
+
description: defaults.description ?? "",
|
|
6352
|
+
tags: defaults.tags ?? [],
|
|
6353
|
+
license: defaultLicense,
|
|
6354
|
+
isSkill,
|
|
6355
|
+
ruleType: isSkill ? "skill" : defaults.ruleType
|
|
6356
|
+
};
|
|
6357
|
+
}
|
|
6358
|
+
const result = await p$2.group({
|
|
6359
|
+
name: () => {
|
|
6360
|
+
const normalizedDefault = normalizeName(defaultName);
|
|
6361
|
+
return p$2.text({
|
|
6362
|
+
message: "Rule name",
|
|
6363
|
+
placeholder: normalizedDefault,
|
|
6364
|
+
defaultValue: normalizedDefault,
|
|
6365
|
+
validate: (value) => {
|
|
6366
|
+
if (!value || value.trim() === "") return;
|
|
6367
|
+
return check(nameSchema)(value);
|
|
6368
|
+
}
|
|
6369
|
+
});
|
|
6370
|
+
},
|
|
6371
|
+
title: ({ results }) => {
|
|
6372
|
+
const defaultTitle = defaults.title ?? toTitleCase(results.name ?? defaultName);
|
|
6373
|
+
return p$2.text({
|
|
6374
|
+
message: "Title",
|
|
6375
|
+
defaultValue: defaultTitle,
|
|
6376
|
+
placeholder: defaultTitle
|
|
6377
|
+
});
|
|
6378
|
+
},
|
|
6379
|
+
description: () => p$2.text({
|
|
6380
|
+
message: "Description",
|
|
6381
|
+
placeholder: "Describe what this rule does...",
|
|
6382
|
+
defaultValue: defaults.description,
|
|
6383
|
+
validate: check(descriptionSchema)
|
|
6384
|
+
}),
|
|
6385
|
+
tags: () => p$2.text({
|
|
6386
|
+
message: "Tags (comma-separated, optional)",
|
|
6387
|
+
placeholder: "e.g., typescript, testing, react",
|
|
6388
|
+
validate: checkTags
|
|
6389
|
+
}),
|
|
6390
|
+
license: async () => {
|
|
6391
|
+
const choice = await p$2.select({
|
|
6392
|
+
message: "License",
|
|
6393
|
+
options: [...COMMON_LICENSES.map((id) => ({
|
|
6394
|
+
value: id,
|
|
6395
|
+
label: id
|
|
6396
|
+
})), {
|
|
6397
|
+
value: "__other__",
|
|
6398
|
+
label: "Other (enter SPDX identifier)"
|
|
6399
|
+
}],
|
|
6400
|
+
initialValue: defaultLicense
|
|
6401
|
+
});
|
|
6402
|
+
if (p$2.isCancel(choice)) throw new Error("Cancelled");
|
|
6403
|
+
if (choice === "__other__") {
|
|
6404
|
+
const custom = await p$2.text({
|
|
6405
|
+
message: "License (SPDX identifier)",
|
|
6406
|
+
placeholder: "e.g., MPL-2.0, AGPL-3.0-only",
|
|
6407
|
+
validate: check(licenseSchema)
|
|
6408
|
+
});
|
|
6409
|
+
if (p$2.isCancel(custom)) throw new Error("Cancelled");
|
|
6410
|
+
return custom;
|
|
6411
|
+
}
|
|
6412
|
+
return choice;
|
|
6413
|
+
}
|
|
6414
|
+
}, { onCancel: () => {
|
|
6415
|
+
throw new Error("Cancelled");
|
|
6416
|
+
} });
|
|
6417
|
+
const tags = parseTags(result.tags);
|
|
6418
|
+
return {
|
|
6419
|
+
platforms: selectedPlatforms,
|
|
6420
|
+
platformPaths,
|
|
6421
|
+
name: result.name,
|
|
6422
|
+
title: result.title.trim() || toTitleCase(result.name),
|
|
6423
|
+
description: result.description ?? "",
|
|
6424
|
+
tags,
|
|
6425
|
+
license: result.license,
|
|
6426
|
+
isSkill,
|
|
6427
|
+
ruleType: isSkill ? "skill" : defaults.ruleType
|
|
6428
|
+
};
|
|
6429
|
+
}
|
|
6194
6430
|
|
|
6195
6431
|
//#endregion
|
|
6196
|
-
//#region src/
|
|
6432
|
+
//#region src/commands/rule/init.ts
|
|
6433
|
+
/** Default rule name when none specified */
|
|
6434
|
+
const DEFAULT_RULE_NAME = "my-rule";
|
|
6197
6435
|
/**
|
|
6198
|
-
*
|
|
6199
|
-
* Returns error message if invalid, undefined if valid.
|
|
6436
|
+
* Detect if directory contains SKILL.md and extract frontmatter defaults.
|
|
6200
6437
|
*/
|
|
6201
|
-
function
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6438
|
+
async function detectSkillDirectory(directory) {
|
|
6439
|
+
const skillPath = join(directory, SKILL_FILENAME);
|
|
6440
|
+
if (!await fileExists(skillPath)) return;
|
|
6441
|
+
const content = await readFile(skillPath, "utf8");
|
|
6442
|
+
const frontmatter = parseSkillFrontmatter(content);
|
|
6443
|
+
return {
|
|
6444
|
+
name: frontmatter.name,
|
|
6445
|
+
license: frontmatter.license
|
|
6446
|
+
};
|
|
6447
|
+
}
|
|
6448
|
+
/**
|
|
6449
|
+
* Initialize a rule in a directory (rule root).
|
|
6450
|
+
*
|
|
6451
|
+
* Structure:
|
|
6452
|
+
* - ruleDir/agentrules.json - rule config
|
|
6453
|
+
* - ruleDir/* - rule files (collected by default)
|
|
6454
|
+
* - ruleDir/README.md, ruleDir/LICENSE.md, ruleDir/INSTALL.txt - optional metadata (not bundled)
|
|
6455
|
+
*/
|
|
6456
|
+
async function initRule(options) {
|
|
6457
|
+
const ruleDir = options.directory ?? process.cwd();
|
|
6458
|
+
log.debug(`Initializing rule in: ${ruleDir}`);
|
|
6459
|
+
const skillInfo = await detectSkillDirectory(ruleDir);
|
|
6460
|
+
const isSkillDirectory = skillInfo !== void 0;
|
|
6461
|
+
const inferredPlatform = getPlatformFromDir(basename(ruleDir));
|
|
6462
|
+
const platformInputs = options.platforms ?? (inferredPlatform ? [inferredPlatform] : []);
|
|
6463
|
+
if (platformInputs.length === 0) throw new Error(`Cannot determine platform. Specify --platform (${PLATFORM_IDS.join(", ")}).`);
|
|
6464
|
+
const platforms = platformInputs.map(normalizePlatformEntryInput);
|
|
6465
|
+
const name = normalizeName(options.name ?? skillInfo?.name ?? DEFAULT_RULE_NAME);
|
|
6466
|
+
const title = options.title ?? toTitleCase(name);
|
|
6467
|
+
const description = options.description ?? "";
|
|
6468
|
+
const license = options.license ?? skillInfo?.license ?? "MIT";
|
|
6469
|
+
const ruleType = isSkillDirectory ? "skill" : options.type;
|
|
6470
|
+
const platformLabels = platforms.map((p$3) => typeof p$3 === "string" ? p$3 : p$3.platform).join(", ");
|
|
6471
|
+
log.debug(`Rule name: ${name}, platforms: ${platformLabels}`);
|
|
6472
|
+
const configPath = join(ruleDir, RULE_CONFIG_FILENAME);
|
|
6473
|
+
if (!options.force && await fileExists(configPath)) throw new Error(`${RULE_CONFIG_FILENAME} already exists. Use --force to overwrite.`);
|
|
6474
|
+
const config$1 = {
|
|
6475
|
+
$schema: RULE_SCHEMA_URL,
|
|
6476
|
+
name,
|
|
6477
|
+
...ruleType && { type: ruleType },
|
|
6478
|
+
title,
|
|
6479
|
+
version: 1,
|
|
6480
|
+
description,
|
|
6481
|
+
tags: options.tags ?? [],
|
|
6482
|
+
license,
|
|
6483
|
+
platforms
|
|
6484
|
+
};
|
|
6485
|
+
let createdDir;
|
|
6486
|
+
if (await directoryExists(ruleDir)) log.debug(`Directory exists: ${ruleDir}`);
|
|
6487
|
+
else {
|
|
6488
|
+
await mkdir(ruleDir, { recursive: true });
|
|
6489
|
+
createdDir = ruleDir;
|
|
6490
|
+
log.debug(`Created directory: ${ruleDir}`);
|
|
6491
|
+
}
|
|
6492
|
+
const content = `${JSON.stringify(config$1, null, 2)}\n`;
|
|
6493
|
+
await writeFile(configPath, content, "utf8");
|
|
6494
|
+
log.debug(`Wrote config file: ${configPath}`);
|
|
6495
|
+
log.debug("Rule initialization complete.");
|
|
6496
|
+
return {
|
|
6497
|
+
configPath,
|
|
6498
|
+
rule: config$1,
|
|
6499
|
+
createdDir
|
|
6500
|
+
};
|
|
6501
|
+
}
|
|
6502
|
+
function normalizePlatform(input) {
|
|
6503
|
+
const normalized = input.toLowerCase();
|
|
6504
|
+
if (!isSupportedPlatform(normalized)) throw new Error(`Unknown platform "${input}". Supported: ${PLATFORM_IDS.join(", ")}`);
|
|
6505
|
+
return normalized;
|
|
6506
|
+
}
|
|
6507
|
+
function normalizePlatformEntryInput(input) {
|
|
6508
|
+
if (typeof input === "string") return normalizePlatform(input);
|
|
6509
|
+
const platform = normalizePlatform(input.platform);
|
|
6510
|
+
const path$1 = typeof input.path === "string" ? input.path.trim() : "";
|
|
6511
|
+
if (path$1.length === 0 || path$1 === ".") return platform;
|
|
6512
|
+
return {
|
|
6513
|
+
platform,
|
|
6514
|
+
path: path$1
|
|
6206
6515
|
};
|
|
6207
6516
|
}
|
|
6208
6517
|
|
|
@@ -6210,14 +6519,46 @@ function check(schema) {
|
|
|
6210
6519
|
//#region src/commands/publish.ts
|
|
6211
6520
|
/** Maximum size per variant/platform bundle in bytes (1MB) */
|
|
6212
6521
|
const MAX_VARIANT_SIZE_BYTES = 1 * 1024 * 1024;
|
|
6213
|
-
/** Schema for parsing comma-separated tags input */
|
|
6214
|
-
const tagsInputSchema = string().transform((input) => input.split(",").map((t) => t.trim().toLowerCase()).filter((t) => t.length > 0)).pipe(tagsSchema);
|
|
6215
6522
|
function formatBytes(bytes) {
|
|
6216
6523
|
if (bytes < 1024) return `${bytes} B`;
|
|
6217
6524
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
6218
6525
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
6219
6526
|
}
|
|
6220
6527
|
/**
|
|
6528
|
+
* Prompts to create agentrules.json after quick publish (or dry run).
|
|
6529
|
+
* With --yes, creates without prompting.
|
|
6530
|
+
*/
|
|
6531
|
+
async function promptToCreateConfig(quickPublish, yes) {
|
|
6532
|
+
if (!quickPublish) return false;
|
|
6533
|
+
let createConfig = yes ?? false;
|
|
6534
|
+
if (!yes && process.stdin.isTTY) {
|
|
6535
|
+
log.print("");
|
|
6536
|
+
const answer = await p$1.confirm({
|
|
6537
|
+
message: "Create agentrules.json for future publishes?",
|
|
6538
|
+
initialValue: true
|
|
6539
|
+
});
|
|
6540
|
+
createConfig = !p$1.isCancel(answer) && answer;
|
|
6541
|
+
}
|
|
6542
|
+
if (createConfig) {
|
|
6543
|
+
const sourceDir = quickPublish.source.type === "directory" ? quickPublish.source.path : void 0;
|
|
6544
|
+
const configDir = sourceDir ?? (quickPublish.source.path.replace(/[/\\][^/\\]+$/, "") || ".");
|
|
6545
|
+
await initRule({
|
|
6546
|
+
directory: configDir,
|
|
6547
|
+
name: quickPublish.name,
|
|
6548
|
+
title: quickPublish.title,
|
|
6549
|
+
description: quickPublish.description || void 0,
|
|
6550
|
+
platforms: quickPublish.platforms,
|
|
6551
|
+
type: quickPublish.ruleType,
|
|
6552
|
+
tags: quickPublish.tags,
|
|
6553
|
+
license: quickPublish.license,
|
|
6554
|
+
force: false
|
|
6555
|
+
});
|
|
6556
|
+
log.success(`Created ${ui.path(join(configDir, "agentrules.json"))}`);
|
|
6557
|
+
return true;
|
|
6558
|
+
}
|
|
6559
|
+
return false;
|
|
6560
|
+
}
|
|
6561
|
+
/**
|
|
6221
6562
|
* Publishes a rule to the registry.
|
|
6222
6563
|
*
|
|
6223
6564
|
* Supports:
|
|
@@ -6287,7 +6628,9 @@ async function publish(options = {}) {
|
|
|
6287
6628
|
dryRun,
|
|
6288
6629
|
version: version$2,
|
|
6289
6630
|
spinner: fileSpinner,
|
|
6290
|
-
ctx
|
|
6631
|
+
ctx,
|
|
6632
|
+
quickPublish: resolved,
|
|
6633
|
+
yes
|
|
6291
6634
|
});
|
|
6292
6635
|
}
|
|
6293
6636
|
if (quickDir) {
|
|
@@ -6303,8 +6646,7 @@ async function publish(options = {}) {
|
|
|
6303
6646
|
license
|
|
6304
6647
|
}, {
|
|
6305
6648
|
type: "directory",
|
|
6306
|
-
path: quickDir
|
|
6307
|
-
entryFile: quickDir.entryFile
|
|
6649
|
+
path: quickDir
|
|
6308
6650
|
}, {
|
|
6309
6651
|
dryRun,
|
|
6310
6652
|
yes
|
|
@@ -6338,7 +6680,9 @@ async function publish(options = {}) {
|
|
|
6338
6680
|
dryRun,
|
|
6339
6681
|
version: version$2,
|
|
6340
6682
|
spinner: dirSpinner,
|
|
6341
|
-
ctx
|
|
6683
|
+
ctx,
|
|
6684
|
+
quickPublish: resolved,
|
|
6685
|
+
yes
|
|
6342
6686
|
});
|
|
6343
6687
|
}
|
|
6344
6688
|
const spinner$1 = await log.spinner("Validating rule...");
|
|
@@ -6435,14 +6779,7 @@ async function getQuickPublishDirectory(inputPath) {
|
|
|
6435
6779
|
if (!pathStat?.isDirectory()) return;
|
|
6436
6780
|
const configStat = await stat(`${inputPath}/${RULE_CONFIG_FILENAME}`).catch(() => null);
|
|
6437
6781
|
if (configStat?.isFile()) return;
|
|
6438
|
-
|
|
6439
|
-
const skillStat = await stat(skillPath).catch(() => null);
|
|
6440
|
-
if (skillStat?.isFile()) return {
|
|
6441
|
-
path: inputPath,
|
|
6442
|
-
type: "skill",
|
|
6443
|
-
entryFile: skillPath
|
|
6444
|
-
};
|
|
6445
|
-
return;
|
|
6782
|
+
return inputPath;
|
|
6446
6783
|
}
|
|
6447
6784
|
function normalizePathForInference(value) {
|
|
6448
6785
|
return value.replace(/\\/g, "/");
|
|
@@ -6469,17 +6806,6 @@ function inferFileDefaults(filePath) {
|
|
|
6469
6806
|
}
|
|
6470
6807
|
return result;
|
|
6471
6808
|
}
|
|
6472
|
-
async function inferDirectoryDefaults(_dirPath, entryFile, dirType) {
|
|
6473
|
-
const result = {};
|
|
6474
|
-
if (dirType === "skill") {
|
|
6475
|
-
result.ruleType = "skill";
|
|
6476
|
-
const content = await readFile(entryFile, "utf8");
|
|
6477
|
-
const frontmatter = parseSkillFrontmatter(content);
|
|
6478
|
-
if (frontmatter.name) result.name = normalizeName(frontmatter.name);
|
|
6479
|
-
if (frontmatter.license) result.license = frontmatter.license;
|
|
6480
|
-
}
|
|
6481
|
-
return result;
|
|
6482
|
-
}
|
|
6483
6809
|
function buildConfigPublishOverrides(options) {
|
|
6484
6810
|
const overrides = {};
|
|
6485
6811
|
if (options.name !== void 0) overrides.name = options.name;
|
|
@@ -6492,37 +6818,23 @@ function buildConfigPublishOverrides(options) {
|
|
|
6492
6818
|
return Object.keys(overrides).length > 0 ? overrides : void 0;
|
|
6493
6819
|
}
|
|
6494
6820
|
async function resolveQuickPublishInputs(options, source, ctx) {
|
|
6495
|
-
const inferred = source.type === "file" ? inferFileDefaults(source.path) : await inferDirectoryDefaults(source.path, source.entryFile, "skill");
|
|
6496
6821
|
const isInteractive = !ctx.yes && process.stdin.isTTY;
|
|
6497
6822
|
const isDirectory = source.type === "directory";
|
|
6498
|
-
const
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
const selection = await p$1.select({
|
|
6509
|
-
message: "Platform",
|
|
6510
|
-
options: PLATFORM_IDS.map((id) => ({
|
|
6511
|
-
value: id,
|
|
6512
|
-
label: id
|
|
6513
|
-
}))
|
|
6514
|
-
});
|
|
6515
|
-
if (p$1.isCancel(selection)) throw new Error("Cancelled");
|
|
6516
|
-
selectedPlatform = selection;
|
|
6517
|
-
}
|
|
6518
|
-
let selectedType = options.type ?? inferred.ruleType;
|
|
6519
|
-
if (!selectedType) {
|
|
6520
|
-
if (!isInteractive) throw new Error("Missing --type");
|
|
6521
|
-
const candidates = getValidTypes(selectedPlatform).filter((t) => supportsInstallPath({
|
|
6522
|
-
platform: selectedPlatform,
|
|
6823
|
+
const isFile = source.type === "file";
|
|
6824
|
+
const fileInferred = isFile ? inferFileDefaults(source.path) : {};
|
|
6825
|
+
const parsedPlatforms = options.platform ? parsePlatformSelection(options.platform).map(normalizePlatformInput) : void 0;
|
|
6826
|
+
let selectedType = options.type ?? fileInferred.ruleType;
|
|
6827
|
+
const platformsForTypeCheck = parsedPlatforms && parsedPlatforms.length > 0 ? parsedPlatforms : fileInferred.platform ? [fileInferred.platform] : [];
|
|
6828
|
+
if (isFile && !selectedType) {
|
|
6829
|
+
if (!isInteractive) throw new Error("Publishing a single file in non-interactive mode requires --name, --platform, and --type.");
|
|
6830
|
+
if (platformsForTypeCheck.length === 0) throw new Error("Missing --platform");
|
|
6831
|
+
const candidateSets = platformsForTypeCheck.map((plat) => getValidTypes(plat).filter((t) => supportsInstallPath({
|
|
6832
|
+
platform: plat,
|
|
6523
6833
|
type: t,
|
|
6524
6834
|
scope: "project"
|
|
6525
|
-
}));
|
|
6835
|
+
})));
|
|
6836
|
+
const candidates = candidateSets.length === 1 ? candidateSets[0] : candidateSets.reduce((acc, set) => acc.filter((t) => set.includes(t)));
|
|
6837
|
+
if (candidates.length === 0) throw new Error(`No common type supports all selected platforms: ${platformsForTypeCheck.join(", ")}`);
|
|
6526
6838
|
const selection = await p$1.select({
|
|
6527
6839
|
message: "Type",
|
|
6528
6840
|
options: candidates.map((t) => ({
|
|
@@ -6533,64 +6845,48 @@ async function resolveQuickPublishInputs(options, source, ctx) {
|
|
|
6533
6845
|
if (p$1.isCancel(selection)) throw new Error("Cancelled");
|
|
6534
6846
|
selectedType = selection;
|
|
6535
6847
|
}
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
const input = await p$1.text({
|
|
6565
|
-
message: "Description (optional)",
|
|
6566
|
-
placeholder: "Describe what this rule does...",
|
|
6567
|
-
validate: check(descriptionSchema)
|
|
6568
|
-
});
|
|
6569
|
-
if (p$1.isCancel(input)) throw new Error("Cancelled");
|
|
6570
|
-
return input;
|
|
6571
|
-
})();
|
|
6572
|
-
const finalTags = await (async () => {
|
|
6573
|
-
if (options.tags) return tagsSchema.parse(options.tags);
|
|
6574
|
-
if (!isInteractive) return [];
|
|
6575
|
-
const input = await p$1.text({
|
|
6576
|
-
message: "Tags (optional)",
|
|
6577
|
-
placeholder: "comma-separated, e.g. typescript, react",
|
|
6578
|
-
validate: check(tagsInputSchema)
|
|
6579
|
-
});
|
|
6580
|
-
if (p$1.isCancel(input)) throw new Error("Cancelled");
|
|
6581
|
-
return tagsInputSchema.parse(input);
|
|
6582
|
-
})();
|
|
6583
|
-
const finalLicense = options.license ?? inferred.license ?? "MIT";
|
|
6848
|
+
if (options.type && platformsForTypeCheck.length > 0) {
|
|
6849
|
+
const ruleType = selectedType;
|
|
6850
|
+
for (const platform of platformsForTypeCheck) if (!supportsInstallPath({
|
|
6851
|
+
platform,
|
|
6852
|
+
type: ruleType,
|
|
6853
|
+
scope: "project"
|
|
6854
|
+
})) throw new Error(`Type "${ruleType}" is not supported for project installs on platform "${platform}".`);
|
|
6855
|
+
}
|
|
6856
|
+
const collected = await collectRuleInputs({
|
|
6857
|
+
directory: isDirectory ? source.path : dirname(source.path),
|
|
6858
|
+
defaults: {
|
|
6859
|
+
name: options.name ?? fileInferred.name,
|
|
6860
|
+
title: options.title,
|
|
6861
|
+
description: options.description,
|
|
6862
|
+
platforms: parsedPlatforms,
|
|
6863
|
+
license: options.license ?? fileInferred.license,
|
|
6864
|
+
tags: options.tags,
|
|
6865
|
+
ruleType: selectedType
|
|
6866
|
+
},
|
|
6867
|
+
nonInteractive: !isInteractive,
|
|
6868
|
+
detectType: isDirectory
|
|
6869
|
+
});
|
|
6870
|
+
const finalRuleType = isFile ? selectedType : collected.ruleType ?? (collected.isSkill ? "skill" : "instruction");
|
|
6871
|
+
for (const platform of collected.platforms) if (!supportsInstallPath({
|
|
6872
|
+
platform,
|
|
6873
|
+
type: finalRuleType,
|
|
6874
|
+
scope: "project"
|
|
6875
|
+
})) throw new Error(`Type "${finalRuleType}" is not supported for project installs on platform "${platform}".`);
|
|
6584
6876
|
if (isInteractive && !ctx.dryRun) {
|
|
6585
6877
|
log.print("");
|
|
6586
|
-
log.print(ui.
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6878
|
+
log.print(ui.rulePreview({
|
|
6879
|
+
header: "Quick publish",
|
|
6880
|
+
path: source.path,
|
|
6881
|
+
pathLabel: isDirectory ? "Directory" : "File",
|
|
6882
|
+
name: collected.name,
|
|
6883
|
+
title: collected.title,
|
|
6884
|
+
description: collected.description,
|
|
6885
|
+
platforms: collected.platforms,
|
|
6886
|
+
type: finalRuleType,
|
|
6887
|
+
tags: collected.tags,
|
|
6888
|
+
showHints: true
|
|
6889
|
+
}));
|
|
6594
6890
|
log.print("");
|
|
6595
6891
|
const confirm = await p$1.confirm({
|
|
6596
6892
|
message: isDirectory ? "Publish this directory?" : "Publish this file?",
|
|
@@ -6600,16 +6896,24 @@ async function resolveQuickPublishInputs(options, source, ctx) {
|
|
|
6600
6896
|
}
|
|
6601
6897
|
return {
|
|
6602
6898
|
source,
|
|
6603
|
-
name:
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6899
|
+
name: collected.name,
|
|
6900
|
+
platforms: collected.platforms,
|
|
6901
|
+
platformPaths: isFile ? {} : collected.platformPaths,
|
|
6902
|
+
ruleType: finalRuleType,
|
|
6903
|
+
title: collected.title,
|
|
6904
|
+
description: collected.description,
|
|
6905
|
+
tags: collected.tags,
|
|
6906
|
+
license: collected.license
|
|
6610
6907
|
};
|
|
6611
6908
|
}
|
|
6612
6909
|
async function buildRuleInputFromQuickPublish(inputs) {
|
|
6910
|
+
const platformEntries = inputs.platforms.map((platform) => {
|
|
6911
|
+
const path$1 = inputs.platformPaths[platform];
|
|
6912
|
+
return path$1 ? {
|
|
6913
|
+
platform,
|
|
6914
|
+
path: path$1
|
|
6915
|
+
} : { platform };
|
|
6916
|
+
});
|
|
6613
6917
|
const config$1 = {
|
|
6614
6918
|
$schema: RULE_SCHEMA_URL,
|
|
6615
6919
|
name: inputs.name,
|
|
@@ -6618,34 +6922,38 @@ async function buildRuleInputFromQuickPublish(inputs) {
|
|
|
6618
6922
|
description: inputs.description,
|
|
6619
6923
|
license: inputs.license,
|
|
6620
6924
|
tags: inputs.tags,
|
|
6621
|
-
platforms:
|
|
6925
|
+
platforms: platformEntries
|
|
6622
6926
|
};
|
|
6623
6927
|
if (inputs.source.type === "file") {
|
|
6624
6928
|
const content = await readFile(inputs.source.path);
|
|
6625
|
-
const
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
platform: inputs.platform,
|
|
6929
|
+
const platformFiles$1 = [];
|
|
6930
|
+
for (const platform of inputs.platforms) {
|
|
6931
|
+
const bundlePath = getInstallPath({
|
|
6932
|
+
platform,
|
|
6933
|
+
type: inputs.ruleType,
|
|
6934
|
+
name: inputs.name,
|
|
6935
|
+
scope: "project"
|
|
6936
|
+
});
|
|
6937
|
+
if (!bundlePath) throw new Error(`Type "${inputs.ruleType}" is not supported for project installs on platform "${platform}".`);
|
|
6938
|
+
platformFiles$1.push({
|
|
6939
|
+
platform,
|
|
6637
6940
|
files: [{
|
|
6638
6941
|
path: bundlePath,
|
|
6639
6942
|
content
|
|
6640
6943
|
}]
|
|
6641
|
-
}
|
|
6944
|
+
});
|
|
6945
|
+
}
|
|
6946
|
+
return {
|
|
6947
|
+
name: inputs.name,
|
|
6948
|
+
config: config$1,
|
|
6949
|
+
platformFiles: platformFiles$1
|
|
6642
6950
|
};
|
|
6643
6951
|
}
|
|
6644
6952
|
const loadedConfig = {
|
|
6645
6953
|
configPath: `${inputs.source.path}/agentrules.json`,
|
|
6646
6954
|
config: {
|
|
6647
6955
|
...config$1,
|
|
6648
|
-
platforms:
|
|
6956
|
+
platforms: platformEntries
|
|
6649
6957
|
},
|
|
6650
6958
|
configDir: inputs.source.path
|
|
6651
6959
|
};
|
|
@@ -6657,7 +6965,7 @@ async function buildRuleInputFromQuickPublish(inputs) {
|
|
|
6657
6965
|
};
|
|
6658
6966
|
}
|
|
6659
6967
|
async function finalizePublish(options) {
|
|
6660
|
-
const { publishInput, dryRun, version: version$2, spinner: spinner$1, ctx } = options;
|
|
6968
|
+
const { publishInput, dryRun, version: version$2, spinner: spinner$1, ctx, quickPublish, yes } = options;
|
|
6661
6969
|
const totalFileCount = publishInput.variants.reduce((sum, v) => sum + v.files.length, 0);
|
|
6662
6970
|
const platformList = publishInput.variants.map((v) => v.platform).join(", ");
|
|
6663
6971
|
let totalSize = 0;
|
|
@@ -6696,6 +7004,7 @@ async function finalizePublish(options) {
|
|
|
6696
7004
|
log.print("");
|
|
6697
7005
|
}
|
|
6698
7006
|
log.print(ui.hint("Run without --dry-run to publish."));
|
|
7007
|
+
await promptToCreateConfig(quickPublish, yes);
|
|
6699
7008
|
return {
|
|
6700
7009
|
success: true,
|
|
6701
7010
|
preview: {
|
|
@@ -6743,6 +7052,7 @@ async function finalizePublish(options) {
|
|
|
6743
7052
|
}
|
|
6744
7053
|
log.info("");
|
|
6745
7054
|
log.info(ui.keyValue("Now live at", ui.link(data.url)));
|
|
7055
|
+
await promptToCreateConfig(quickPublish, yes);
|
|
6746
7056
|
return {
|
|
6747
7057
|
success: true,
|
|
6748
7058
|
rule: {
|
|
@@ -6833,196 +7143,15 @@ async function discoverRuleDirs(inputDir) {
|
|
|
6833
7143
|
return ruleDirs.sort();
|
|
6834
7144
|
}
|
|
6835
7145
|
|
|
6836
|
-
//#endregion
|
|
6837
|
-
//#region src/commands/rule/init.ts
|
|
6838
|
-
/** Default rule name when none specified */
|
|
6839
|
-
const DEFAULT_RULE_NAME$1 = "my-rule";
|
|
6840
|
-
/**
|
|
6841
|
-
* Detect if directory contains SKILL.md and extract frontmatter defaults.
|
|
6842
|
-
*/
|
|
6843
|
-
async function detectSkillDirectory(directory) {
|
|
6844
|
-
const skillPath = join(directory, SKILL_FILENAME);
|
|
6845
|
-
if (!await fileExists(skillPath)) return;
|
|
6846
|
-
const content = await readFile(skillPath, "utf8");
|
|
6847
|
-
const frontmatter = parseSkillFrontmatter(content);
|
|
6848
|
-
return {
|
|
6849
|
-
name: frontmatter.name,
|
|
6850
|
-
license: frontmatter.license
|
|
6851
|
-
};
|
|
6852
|
-
}
|
|
6853
|
-
/**
|
|
6854
|
-
* Initialize a rule in a directory (rule root).
|
|
6855
|
-
*
|
|
6856
|
-
* Structure:
|
|
6857
|
-
* - ruleDir/agentrules.json - rule config
|
|
6858
|
-
* - ruleDir/* - rule files (collected by default)
|
|
6859
|
-
* - ruleDir/README.md, ruleDir/LICENSE.md, ruleDir/INSTALL.txt - optional metadata (not bundled)
|
|
6860
|
-
*/
|
|
6861
|
-
async function initRule(options) {
|
|
6862
|
-
const ruleDir = options.directory ?? process.cwd();
|
|
6863
|
-
log.debug(`Initializing rule in: ${ruleDir}`);
|
|
6864
|
-
const skillInfo = await detectSkillDirectory(ruleDir);
|
|
6865
|
-
const isSkillDirectory = skillInfo !== void 0;
|
|
6866
|
-
const inferredPlatform = getPlatformFromDir(basename(ruleDir));
|
|
6867
|
-
const platformInputs = options.platforms ?? (inferredPlatform ? [inferredPlatform] : []);
|
|
6868
|
-
if (platformInputs.length === 0) throw new Error(`Cannot determine platform. Specify --platform (${PLATFORM_IDS.join(", ")}).`);
|
|
6869
|
-
const platforms = platformInputs.map(normalizePlatformEntryInput);
|
|
6870
|
-
const name = normalizeName(options.name ?? skillInfo?.name ?? DEFAULT_RULE_NAME$1);
|
|
6871
|
-
const title = options.title ?? toTitleCase(name);
|
|
6872
|
-
const description = options.description ?? "";
|
|
6873
|
-
const license = options.license ?? skillInfo?.license ?? "MIT";
|
|
6874
|
-
const ruleType = isSkillDirectory ? "skill" : options.type;
|
|
6875
|
-
const platformLabels = platforms.map((p$2) => typeof p$2 === "string" ? p$2 : p$2.platform).join(", ");
|
|
6876
|
-
log.debug(`Rule name: ${name}, platforms: ${platformLabels}`);
|
|
6877
|
-
const configPath = join(ruleDir, RULE_CONFIG_FILENAME);
|
|
6878
|
-
if (!options.force && await fileExists(configPath)) throw new Error(`${RULE_CONFIG_FILENAME} already exists. Use --force to overwrite.`);
|
|
6879
|
-
const config$1 = {
|
|
6880
|
-
$schema: RULE_SCHEMA_URL,
|
|
6881
|
-
name,
|
|
6882
|
-
...ruleType && { type: ruleType },
|
|
6883
|
-
title,
|
|
6884
|
-
version: 1,
|
|
6885
|
-
description,
|
|
6886
|
-
tags: options.tags ?? [],
|
|
6887
|
-
license,
|
|
6888
|
-
platforms
|
|
6889
|
-
};
|
|
6890
|
-
let createdDir;
|
|
6891
|
-
if (await directoryExists(ruleDir)) log.debug(`Directory exists: ${ruleDir}`);
|
|
6892
|
-
else {
|
|
6893
|
-
await mkdir(ruleDir, { recursive: true });
|
|
6894
|
-
createdDir = ruleDir;
|
|
6895
|
-
log.debug(`Created directory: ${ruleDir}`);
|
|
6896
|
-
}
|
|
6897
|
-
const content = `${JSON.stringify(config$1, null, 2)}\n`;
|
|
6898
|
-
await writeFile(configPath, content, "utf8");
|
|
6899
|
-
log.debug(`Wrote config file: ${configPath}`);
|
|
6900
|
-
log.debug("Rule initialization complete.");
|
|
6901
|
-
return {
|
|
6902
|
-
configPath,
|
|
6903
|
-
rule: config$1,
|
|
6904
|
-
createdDir
|
|
6905
|
-
};
|
|
6906
|
-
}
|
|
6907
|
-
function normalizePlatform(input) {
|
|
6908
|
-
const normalized = input.toLowerCase();
|
|
6909
|
-
if (!isSupportedPlatform(normalized)) throw new Error(`Unknown platform "${input}". Supported: ${PLATFORM_IDS.join(", ")}`);
|
|
6910
|
-
return normalized;
|
|
6911
|
-
}
|
|
6912
|
-
function normalizePlatformEntryInput(input) {
|
|
6913
|
-
if (typeof input === "string") return normalizePlatform(input);
|
|
6914
|
-
const platform = normalizePlatform(input.platform);
|
|
6915
|
-
const path$1 = typeof input.path === "string" ? input.path.trim() : "";
|
|
6916
|
-
if (path$1.length === 0 || path$1 === ".") return platform;
|
|
6917
|
-
return {
|
|
6918
|
-
platform,
|
|
6919
|
-
path: path$1
|
|
6920
|
-
};
|
|
6921
|
-
}
|
|
6922
|
-
|
|
6923
7146
|
//#endregion
|
|
6924
7147
|
//#region src/commands/rule/init-interactive.ts
|
|
6925
|
-
const DEFAULT_RULE_NAME = "my-rule";
|
|
6926
|
-
/**
|
|
6927
|
-
* Parse comma-separated tags string into array.
|
|
6928
|
-
*/
|
|
6929
|
-
function parseTags(input) {
|
|
6930
|
-
if (typeof input !== "string") return [];
|
|
6931
|
-
if (input.trim().length === 0) return [];
|
|
6932
|
-
return input.split(",").map((tag) => tag.trim().toLowerCase()).filter((tag) => tag.length > 0);
|
|
6933
|
-
}
|
|
6934
|
-
/**
|
|
6935
|
-
* Validator for comma-separated tags input.
|
|
6936
|
-
*/
|
|
6937
|
-
function checkTags(value) {
|
|
6938
|
-
const tags = parseTags(value);
|
|
6939
|
-
const result = tagsSchema.safeParse(tags);
|
|
6940
|
-
if (!result.success) return result.error.issues[0]?.message;
|
|
6941
|
-
}
|
|
6942
7148
|
/**
|
|
6943
7149
|
* Run interactive init flow with clack prompts.
|
|
6944
7150
|
*/
|
|
6945
7151
|
async function initInteractive(options) {
|
|
6946
|
-
const { directory,
|
|
7152
|
+
const { directory, platformPaths } = options;
|
|
6947
7153
|
let { force } = options;
|
|
6948
7154
|
p.intro("Create a new rule");
|
|
6949
|
-
const skillInfo = await detectSkillDirectory(directory);
|
|
6950
|
-
let useSkillDefaults = false;
|
|
6951
|
-
if (skillInfo) {
|
|
6952
|
-
const confirm = await p.confirm({
|
|
6953
|
-
message: `Detected SKILL.md${skillInfo.name ? ` (${skillInfo.name})` : ""}. Initialize as skill?`,
|
|
6954
|
-
initialValue: true
|
|
6955
|
-
});
|
|
6956
|
-
if (p.isCancel(confirm)) {
|
|
6957
|
-
p.cancel("Cancelled");
|
|
6958
|
-
process.exit(0);
|
|
6959
|
-
}
|
|
6960
|
-
useSkillDefaults = confirm;
|
|
6961
|
-
}
|
|
6962
|
-
const defaultName = useSkillDefaults && skillInfo?.name ? skillInfo.name : nameOption ?? DEFAULT_RULE_NAME;
|
|
6963
|
-
const defaultLicense = useSkillDefaults && skillInfo?.license ? skillInfo.license : licenseOption ?? "MIT";
|
|
6964
|
-
const validatedPlatforms = [];
|
|
6965
|
-
if (platformsOption) for (const platform of platformsOption) {
|
|
6966
|
-
if (!isSupportedPlatform(platform)) {
|
|
6967
|
-
p.cancel(`Unknown platform "${platform}"`);
|
|
6968
|
-
process.exit(1);
|
|
6969
|
-
}
|
|
6970
|
-
validatedPlatforms.push(platform);
|
|
6971
|
-
}
|
|
6972
|
-
const selectedPlatforms = validatedPlatforms.length > 0 ? validatedPlatforms : await (async () => {
|
|
6973
|
-
const platformChoices = await p.multiselect({
|
|
6974
|
-
message: "Platforms (select one or more)",
|
|
6975
|
-
options: PLATFORM_IDS.map((id) => ({
|
|
6976
|
-
value: id,
|
|
6977
|
-
label: id
|
|
6978
|
-
})),
|
|
6979
|
-
required: true
|
|
6980
|
-
});
|
|
6981
|
-
if (p.isCancel(platformChoices)) {
|
|
6982
|
-
p.cancel("Cancelled");
|
|
6983
|
-
process.exit(0);
|
|
6984
|
-
}
|
|
6985
|
-
return platformChoices;
|
|
6986
|
-
})();
|
|
6987
|
-
const platformEntries = await (async () => {
|
|
6988
|
-
if (selectedPlatforms.length === 0) return [];
|
|
6989
|
-
if (useSkillDefaults) return selectedPlatforms;
|
|
6990
|
-
const hasCompletePathMapping = selectedPlatforms.every((platform) => {
|
|
6991
|
-
const value = platformPaths?.[platform];
|
|
6992
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
6993
|
-
});
|
|
6994
|
-
if (hasCompletePathMapping) return selectedPlatforms.map((platform) => {
|
|
6995
|
-
const path$1 = platformPaths?.[platform]?.trim();
|
|
6996
|
-
if (!path$1 || path$1 === ".") return platform;
|
|
6997
|
-
return {
|
|
6998
|
-
platform,
|
|
6999
|
-
path: path$1
|
|
7000
|
-
};
|
|
7001
|
-
});
|
|
7002
|
-
if (selectedPlatforms.length === 1) return selectedPlatforms;
|
|
7003
|
-
const entries = [];
|
|
7004
|
-
for (const platform of selectedPlatforms) {
|
|
7005
|
-
const mappedPath = platformPaths?.[platform]?.trim();
|
|
7006
|
-
const suggestedPath = mappedPath ?? (await directoryExists(join(directory, platform)) ? platform : ".");
|
|
7007
|
-
const input = await p.text({
|
|
7008
|
-
message: `Folder for ${platform} files ('.' = same folder as agentrules.json)`,
|
|
7009
|
-
placeholder: suggestedPath,
|
|
7010
|
-
defaultValue: suggestedPath
|
|
7011
|
-
});
|
|
7012
|
-
if (p.isCancel(input)) {
|
|
7013
|
-
p.cancel("Cancelled");
|
|
7014
|
-
process.exit(0);
|
|
7015
|
-
}
|
|
7016
|
-
const trimmed = input.trim();
|
|
7017
|
-
const resolvedPath = trimmed.length > 0 ? trimmed : suggestedPath;
|
|
7018
|
-
if (resolvedPath === ".") entries.push(platform);
|
|
7019
|
-
else entries.push({
|
|
7020
|
-
platform,
|
|
7021
|
-
path: resolvedPath
|
|
7022
|
-
});
|
|
7023
|
-
}
|
|
7024
|
-
return entries;
|
|
7025
|
-
})();
|
|
7026
7155
|
const configPath = join(directory, RULE_CONFIG_FILENAME);
|
|
7027
7156
|
if (!force && await fileExists(configPath)) {
|
|
7028
7157
|
const overwrite = await p.confirm({
|
|
@@ -7035,81 +7164,58 @@ async function initInteractive(options) {
|
|
|
7035
7164
|
}
|
|
7036
7165
|
force = true;
|
|
7037
7166
|
}
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
}
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
placeholder: defaultTitle
|
|
7057
|
-
});
|
|
7058
|
-
},
|
|
7059
|
-
description: () => p.text({
|
|
7060
|
-
message: "Description",
|
|
7061
|
-
placeholder: "Describe what this rule does...",
|
|
7062
|
-
defaultValue: descriptionOption,
|
|
7063
|
-
validate: check(descriptionSchema)
|
|
7064
|
-
}),
|
|
7065
|
-
tags: () => p.text({
|
|
7066
|
-
message: "Tags (comma-separated, optional)",
|
|
7067
|
-
placeholder: "e.g., typescript, testing, react",
|
|
7068
|
-
validate: checkTags
|
|
7069
|
-
}),
|
|
7070
|
-
license: async () => {
|
|
7071
|
-
const choice = await p.select({
|
|
7072
|
-
message: "License",
|
|
7073
|
-
options: [...COMMON_LICENSES.map((id) => ({
|
|
7074
|
-
value: id,
|
|
7075
|
-
label: id
|
|
7076
|
-
})), {
|
|
7077
|
-
value: "__other__",
|
|
7078
|
-
label: "Other (enter SPDX identifier)"
|
|
7079
|
-
}],
|
|
7080
|
-
initialValue: defaultLicense
|
|
7081
|
-
});
|
|
7082
|
-
if (p.isCancel(choice)) {
|
|
7083
|
-
p.cancel("Cancelled");
|
|
7084
|
-
process.exit(0);
|
|
7085
|
-
}
|
|
7086
|
-
if (choice === "__other__") {
|
|
7087
|
-
const custom = await p.text({
|
|
7088
|
-
message: "License (SPDX identifier)",
|
|
7089
|
-
placeholder: "e.g., MPL-2.0, AGPL-3.0-only",
|
|
7090
|
-
validate: check(licenseSchema)
|
|
7091
|
-
});
|
|
7092
|
-
if (p.isCancel(custom)) {
|
|
7093
|
-
p.cancel("Cancelled");
|
|
7094
|
-
process.exit(0);
|
|
7095
|
-
}
|
|
7096
|
-
return custom;
|
|
7097
|
-
}
|
|
7098
|
-
return choice;
|
|
7167
|
+
let collected;
|
|
7168
|
+
try {
|
|
7169
|
+
collected = await collectRuleInputs({
|
|
7170
|
+
directory,
|
|
7171
|
+
defaults: {
|
|
7172
|
+
name: options.name,
|
|
7173
|
+
title: options.title,
|
|
7174
|
+
description: options.description,
|
|
7175
|
+
platforms: options.platforms,
|
|
7176
|
+
platformPaths,
|
|
7177
|
+
license: options.license
|
|
7178
|
+
},
|
|
7179
|
+
nonInteractive: false
|
|
7180
|
+
});
|
|
7181
|
+
} catch (error$2) {
|
|
7182
|
+
if (error$2 instanceof Error && error$2.message === "Cancelled") {
|
|
7183
|
+
p.cancel("Cancelled");
|
|
7184
|
+
process.exit(0);
|
|
7099
7185
|
}
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7186
|
+
throw error$2;
|
|
7187
|
+
}
|
|
7188
|
+
const platformEntries = collected.platforms.map((platform) => {
|
|
7189
|
+
const path$1 = collected.platformPaths[platform];
|
|
7190
|
+
return path$1 ? {
|
|
7191
|
+
platform,
|
|
7192
|
+
path: path$1
|
|
7193
|
+
} : platform;
|
|
7194
|
+
});
|
|
7195
|
+
log.print("");
|
|
7196
|
+
log.print(ui.rulePreview({
|
|
7197
|
+
header: "Rule configuration",
|
|
7198
|
+
path: directory,
|
|
7199
|
+
pathLabel: "Directory",
|
|
7200
|
+
name: collected.name,
|
|
7201
|
+
title: collected.title || toTitleCase(collected.name),
|
|
7202
|
+
description: collected.description,
|
|
7203
|
+
platforms: collected.platforms,
|
|
7204
|
+
type: collected.isSkill ? "skill" : void 0,
|
|
7205
|
+
tags: collected.tags,
|
|
7206
|
+
license: collected.license,
|
|
7207
|
+
showHints: true
|
|
7208
|
+
}));
|
|
7209
|
+
log.print("");
|
|
7104
7210
|
const initOptions = {
|
|
7105
7211
|
directory,
|
|
7106
|
-
name:
|
|
7107
|
-
type:
|
|
7108
|
-
title:
|
|
7109
|
-
description:
|
|
7110
|
-
tags:
|
|
7212
|
+
name: collected.name,
|
|
7213
|
+
type: collected.isSkill ? "skill" : void 0,
|
|
7214
|
+
title: collected.title || void 0,
|
|
7215
|
+
description: collected.description,
|
|
7216
|
+
tags: collected.tags,
|
|
7111
7217
|
platforms: platformEntries,
|
|
7112
|
-
license:
|
|
7218
|
+
license: collected.license,
|
|
7113
7219
|
force
|
|
7114
7220
|
};
|
|
7115
7221
|
const initResult = await initRule(initOptions);
|
|
@@ -7582,7 +7688,7 @@ program.command("add <item>").description("Download and install a rule from the
|
|
|
7582
7688
|
program.command("init").description("Initialize a new rule").argument("[directory]", "Directory to initialize (created if it doesn't exist)").option("-y, --yes", "Accept defaults without prompting").option("-n, --name <name>", "Rule name").option("-t, --title <title>", "Display title").option("--description <text>", "Rule description").option("-p, --platform <platform>", "Target platform(s). Repeatable, accepts comma-separated. Supports <platform>=<path> mappings.", (value, previous) => previous ? [...previous, value] : [value]).option("-l, --license <license>", "License (e.g., MIT)").option("-f, --force", "Overwrite existing agentrules.json").action(handle(async (directory, options) => {
|
|
7583
7689
|
const targetDir = directory ?? process.cwd();
|
|
7584
7690
|
const defaultName = directory ? basename(directory) : void 0;
|
|
7585
|
-
const platformInputs = options.platform?.flatMap((p$
|
|
7691
|
+
const platformInputs = options.platform?.flatMap((p$3) => p$3.split(",").map((s) => s.trim())).filter((p$3) => p$3.length > 0);
|
|
7586
7692
|
const platformIds = [];
|
|
7587
7693
|
const platformPaths = {};
|
|
7588
7694
|
if (platformInputs) for (const input of platformInputs) {
|
|
@@ -7623,7 +7729,7 @@ program.command("init").description("Initialize a new rule").argument("[director
|
|
|
7623
7729
|
}
|
|
7624
7730
|
const nextSteps$1 = [
|
|
7625
7731
|
"Add your rule files in this directory",
|
|
7626
|
-
"Add tags
|
|
7732
|
+
"Add tags and features to agentrules.json",
|
|
7627
7733
|
`Run ${ui.command("agentrules publish")} to publish your rule`
|
|
7628
7734
|
];
|
|
7629
7735
|
log.print(`\n${ui.header("Next steps")}`);
|
|
@@ -7647,7 +7753,7 @@ program.command("init").description("Initialize a new rule").argument("[director
|
|
|
7647
7753
|
}
|
|
7648
7754
|
const nextSteps = [
|
|
7649
7755
|
"Add your rule files in this directory",
|
|
7650
|
-
"Add tags
|
|
7756
|
+
"Add tags and features to agentrules.json",
|
|
7651
7757
|
`Run ${ui.command("agentrules publish")} to publish your rule`
|
|
7652
7758
|
];
|
|
7653
7759
|
log.print(`\n${ui.header("Next steps")}`);
|
|
@@ -7656,13 +7762,13 @@ program.command("init").description("Initialize a new rule").argument("[director
|
|
|
7656
7762
|
program.command("validate").description("Validate an agentrules.json configuration").argument("[path]", "Path to agentrules.json or directory").action(handle(async (path$1) => {
|
|
7657
7763
|
const result = await validateRule({ path: path$1 });
|
|
7658
7764
|
if (result.valid && result.rule) {
|
|
7659
|
-
const p$
|
|
7660
|
-
const platforms = p$
|
|
7661
|
-
log.success(p$
|
|
7662
|
-
if (p$
|
|
7663
|
-
log.print(ui.keyValue("License", p$
|
|
7765
|
+
const p$3 = result.rule;
|
|
7766
|
+
const platforms = p$3.platforms.map((entry) => entry.platform).join(", ");
|
|
7767
|
+
log.success(p$3.title);
|
|
7768
|
+
if (p$3.description) log.print(ui.keyValue("Description", p$3.description));
|
|
7769
|
+
log.print(ui.keyValue("License", p$3.license));
|
|
7664
7770
|
log.print(ui.keyValue("Platforms", platforms));
|
|
7665
|
-
if (p$
|
|
7771
|
+
if (p$3.tags?.length) log.print(ui.keyValue("Tags", p$3.tags.join(", ")));
|
|
7666
7772
|
} else if (!result.valid) log.error(`Invalid: ${ui.path(result.configPath)}`);
|
|
7667
7773
|
if (result.errors.length > 0) {
|
|
7668
7774
|
log.print("");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentrules/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"author": "Brian Cheung <bcheung.dev@gmail.com> (https://github.com/bcheung)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://agentrules.directory",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"clean": "rm -rf node_modules dist .turbo"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@agentrules/core": "0.3.
|
|
56
|
+
"@agentrules/core": "0.3.1",
|
|
57
57
|
"@clack/prompts": "^0.11.0",
|
|
58
58
|
"chalk": "^5.4.1",
|
|
59
59
|
"commander": "^12.1.0",
|