@agentrules/cli 0.2.0 → 0.2.1

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.
Files changed (2) hide show
  1. package/dist/index.js +43 -13
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -162,6 +162,21 @@ function getInstallPath({ platform, type, name, scope = "project" }) {
162
162
  if (template.includes("{name}") && !name) throw new Error(`Missing name for install path: platform="${platform}" type="${type}" scope="${scope}"`);
163
163
  return template.replace("{platformDir}", rootDir).replace("{name}", name ?? "");
164
164
  }
165
+ /**
166
+ * Get the relative install path for a type (without the root directory prefix).
167
+ * Returns the path relative to the platform's root directory.
168
+ *
169
+ * Example: For codex instruction with scope="global", returns "AGENTS.md"
170
+ * (not "~/.codex/AGENTS.md")
171
+ */
172
+ function getRelativeInstallPath({ platform, type, name, scope = "project" }) {
173
+ const typeConfig = getTypeConfig(platform, type);
174
+ if (!typeConfig) return null;
175
+ const template = scope === "project" ? typeConfig.project : typeConfig.global;
176
+ if (!template) return null;
177
+ if (template.includes("{name}") && !name) throw new Error(`Missing name for install path: platform="${platform}" type="${type}" scope="${scope}"`);
178
+ return template.replace("{platformDir}/", "").replace("{platformDir}", "").replace("{name}", name ?? "");
179
+ }
165
180
 
166
181
  //#endregion
167
182
  //#region ../core/src/platform/utils.ts
@@ -3757,7 +3772,7 @@ function normalizeBundleBase(base) {
3757
3772
  }
3758
3773
  function getBundlePath(base, slug, platform, version$2) {
3759
3774
  const prefix = base ? `${base}/` : "";
3760
- return `${prefix}${STATIC_BUNDLE_DIR}/${slug}/${platform}/${version$2}`;
3775
+ return `${prefix}${STATIC_BUNDLE_DIR}/${slug}/${version$2}/${platform}.json`;
3761
3776
  }
3762
3777
  function ensureKnownPlatform(platform, slug) {
3763
3778
  if (!isSupportedPlatform(platform)) throw new Error(`Unknown platform "${platform}" in ${slug}. Supported: ${PLATFORM_IDS.join(", ")}`);
@@ -5534,7 +5549,7 @@ function resolveInstallTarget(platform, type, name, options) {
5534
5549
  mode: "global",
5535
5550
  platform,
5536
5551
  platformDir,
5537
- globalDir,
5552
+ globalDir: globalRoot,
5538
5553
  type,
5539
5554
  name,
5540
5555
  label: `global path ${globalRoot}`
@@ -5558,17 +5573,20 @@ function resolveInstallTarget(platform, type, name, options) {
5558
5573
  *
5559
5574
  * - Project scope: use file.path directly
5560
5575
  * - Global scope:
5561
- * - If path starts with platformDir: transform to globalDir
5562
- * - If type is "multi" and path doesn't start with platformDir: skip (return null)
5563
- * - Otherwise: use getInstallPath for root-level files (e.g., instruction)
5576
+ * - If path starts with platformDir: strip prefix (target.root is already globalDir)
5577
+ * - If typed bundle and root file: use type template to get relative path
5578
+ * - If freeform bundle and root file: use path as-is (relative to globalDir)
5579
+ *
5580
+ * IMPORTANT: For global scope, this returns RELATIVE paths only.
5581
+ * The caller combines with target.root (which is the expanded globalDir).
5564
5582
  */
5565
5583
  function resolvePath(filePath, target) {
5566
- const { platform, platformDir, globalDir, type, name, mode } = target;
5584
+ const { platform, platformDir, type, name, mode } = target;
5567
5585
  if (mode === "project") return filePath;
5568
5586
  const platformDirPrefix = `${platformDir}/`;
5569
- if (filePath.startsWith(platformDirPrefix)) return `${globalDir}/${filePath.slice(platformDirPrefix.length)}`;
5570
- if (!type) return null;
5571
- return getInstallPath({
5587
+ if (filePath.startsWith(platformDirPrefix)) return filePath.slice(platformDirPrefix.length);
5588
+ if (!type) return filePath;
5589
+ return getRelativeInstallPath({
5572
5590
  platform,
5573
5591
  type,
5574
5592
  name,
@@ -5578,12 +5596,22 @@ function resolvePath(filePath, target) {
5578
5596
  function computeDestinationPath(pathInput, target) {
5579
5597
  const normalized = normalizeBundlePath(pathInput);
5580
5598
  if (!normalized) throw new Error(`Unable to derive destination for ${pathInput}. The computed relative path is empty.`);
5599
+ validateBundlePath(normalized, pathInput);
5581
5600
  const resolvedPath = resolvePath(normalized, target);
5582
5601
  if (resolvedPath === null) return null;
5583
5602
  const destination = resolve(target.root, resolvedPath);
5584
5603
  ensureWithinRoot(destination, target.root);
5585
5604
  return destination;
5586
5605
  }
5606
+ /**
5607
+ * Validate bundle path for dangerous patterns.
5608
+ * Throws if path contains traversal or home directory references.
5609
+ */
5610
+ function validateBundlePath(normalized, original) {
5611
+ if (normalized.includes("..")) throw new Error(`Refusing to install file with path traversal: ${original}`);
5612
+ if (normalized.startsWith("~")) throw new Error(`Refusing to install file with home directory reference: ${original}`);
5613
+ if (normalized.includes("/~/") || normalized.includes("\\~\\")) throw new Error(`Refusing to install file with embedded home directory reference: ${original}`);
5614
+ }
5587
5615
  function parseInput(input, explicitPlatform, explicitVersion) {
5588
5616
  let normalized = input.toLowerCase().trim();
5589
5617
  let parsedVersion;
@@ -6563,11 +6591,13 @@ async function buildRegistry(options) {
6563
6591
  await mkdir(outputDir, { recursive: true });
6564
6592
  const indent$1 = compact ? void 0 : 2;
6565
6593
  for (const bundle of result.bundles) {
6566
- const bundleDir = join(outputDir, STATIC_BUNDLE_DIR, bundle.slug, bundle.platform);
6567
- await mkdir(bundleDir, { recursive: true });
6568
6594
  const bundleJson = JSON.stringify(bundle, null, indent$1);
6569
- await writeFile(join(bundleDir, bundle.version), bundleJson);
6570
- await writeFile(join(bundleDir, LATEST_VERSION), bundleJson);
6595
+ const versionedDir = join(outputDir, STATIC_BUNDLE_DIR, bundle.slug, bundle.version);
6596
+ await mkdir(versionedDir, { recursive: true });
6597
+ await writeFile(join(versionedDir, `${bundle.platform}.json`), bundleJson);
6598
+ const latestDir = join(outputDir, STATIC_BUNDLE_DIR, bundle.slug, LATEST_VERSION);
6599
+ await mkdir(latestDir, { recursive: true });
6600
+ await writeFile(join(latestDir, `${bundle.platform}.json`), bundleJson);
6571
6601
  }
6572
6602
  for (const rule of result.rules) {
6573
6603
  const ruleJson = JSON.stringify(rule, null, indent$1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentrules/cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
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.2.0",
56
+ "@agentrules/core": "0.2.1",
57
57
  "@clack/prompts": "^0.11.0",
58
58
  "chalk": "^5.4.1",
59
59
  "commander": "^12.1.0",