@agentrules/cli 0.0.12 → 0.0.13

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 +346 -3
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "module";
3
- import { AGENT_RULES_DIR, API_ENDPOINTS, COMMON_LICENSES, LATEST_VERSION, PLATFORMS, PLATFORM_IDS, PRESET_CONFIG_FILENAME, PRESET_SCHEMA_URL, STATIC_BUNDLE_DIR, buildPresetPublishInput, buildPresetRegistry, createDiffPreview, decodeBundledFile, descriptionSchema, fetchBundle, getPlatformFromDir, isLikelyText, isPlatformDir, isSupportedPlatform, licenseSchema, normalizeBundlePath, normalizePlatformInput, resolvePreset, slugSchema, titleSchema, toUtf8String, validatePresetConfig, verifyBundledFileChecksum } from "@agentrules/core";
3
+ import { AGENT_RULES_DIR, API_ENDPOINTS, COMMON_LICENSES, LATEST_VERSION, PLATFORMS, PLATFORM_IDS, PRESET_CONFIG_FILENAME, PRESET_SCHEMA_URL, STATIC_BUNDLE_DIR, buildPresetPublishInput, buildPresetRegistry, createDiffPreview, decodeBundledFile, descriptionSchema, fetchBundle, getPlatformFromDir, isLikelyText, isPlatformDir, isSupportedPlatform, licenseSchema, normalizeBundlePath, normalizePlatformInput, resolvePreset, slugSchema, tagsSchema, titleSchema, toUtf8String, validatePresetConfig, verifyBundledFileChecksum } from "@agentrules/core";
4
4
  import { Command } from "commander";
5
5
  import { basename, dirname, join, relative, resolve, sep } from "path";
6
6
  import { exec } from "child_process";
@@ -315,6 +315,49 @@ function relativeTime(date) {
315
315
  if (minutes > 0) return `${minutes}m ago`;
316
316
  return "just now";
317
317
  }
318
+ /**
319
+ * Formats an array of files as a tree structure
320
+ */
321
+ function fileTree(files) {
322
+ const root = {
323
+ name: "",
324
+ children: new Map()
325
+ };
326
+ for (const file of files) {
327
+ const parts = file.path.split("/");
328
+ let current = root;
329
+ for (let i = 0; i < parts.length; i++) {
330
+ const part = parts[i];
331
+ const isFile = i === parts.length - 1;
332
+ let child = current.children.get(part);
333
+ if (!child) {
334
+ child = {
335
+ name: part,
336
+ size: isFile ? file.size : void 0,
337
+ children: new Map()
338
+ };
339
+ current.children.set(part, child);
340
+ }
341
+ current = child;
342
+ }
343
+ }
344
+ const lines = [];
345
+ function renderNode(node, prefix, isLast) {
346
+ const connector = isLast ? "└── " : "├── ";
347
+ const sizeStr = node.size !== void 0 ? muted(` (${formatBytes$1(node.size)})`) : "";
348
+ lines.push(`${prefix}${connector}${node.name}${sizeStr}`);
349
+ const children = Array.from(node.children.values());
350
+ const newPrefix = prefix + (isLast ? " " : "│ ");
351
+ children.forEach((child, index) => {
352
+ renderNode(child, newPrefix, index === children.length - 1);
353
+ });
354
+ }
355
+ const topLevel = Array.from(root.children.values());
356
+ topLevel.forEach((child, index) => {
357
+ renderNode(child, "", index === topLevel.length - 1);
358
+ });
359
+ return lines.join("\n");
360
+ }
318
361
  const ui = {
319
362
  theme,
320
363
  symbols,
@@ -354,7 +397,8 @@ const ui = {
354
397
  stripAnsi,
355
398
  truncate,
356
399
  formatBytes: formatBytes$1,
357
- relativeTime
400
+ relativeTime,
401
+ fileTree
358
402
  };
359
403
 
360
404
  //#endregion
@@ -1729,6 +1773,7 @@ async function initPreset(options) {
1729
1773
  title,
1730
1774
  version: 1,
1731
1775
  description,
1776
+ tags: options.tags ?? [],
1732
1777
  license,
1733
1778
  platform
1734
1779
  };
@@ -1773,6 +1818,20 @@ function check(schema) {
1773
1818
  //#region src/commands/preset/init-interactive.ts
1774
1819
  const DEFAULT_PRESET_NAME = "my-preset";
1775
1820
  /**
1821
+ * Parse comma-separated tags string into array
1822
+ */
1823
+ function parseTags(input) {
1824
+ return input.split(",").map((tag) => tag.trim().toLowerCase()).filter((tag) => tag.length > 0);
1825
+ }
1826
+ /**
1827
+ * Validator for comma-separated tags input
1828
+ */
1829
+ function checkTags(value) {
1830
+ const tags = parseTags(value);
1831
+ const result = tagsSchema.safeParse(tags);
1832
+ if (!result.success) return result.error.issues[0]?.message;
1833
+ }
1834
+ /**
1776
1835
  * Run interactive init flow with clack prompts.
1777
1836
  *
1778
1837
  * If platformDir is provided, init directly in that directory.
@@ -1855,6 +1914,11 @@ async function initInteractive(options) {
1855
1914
  validate: check(descriptionSchema)
1856
1915
  });
1857
1916
  },
1917
+ tags: () => p.text({
1918
+ message: "Tags (comma-separated, at least one)",
1919
+ placeholder: "e.g., typescript, testing, react",
1920
+ validate: checkTags
1921
+ }),
1858
1922
  license: async () => {
1859
1923
  const defaultLicense = licenseOption ?? "MIT";
1860
1924
  const choice = await p.select({
@@ -1895,6 +1959,7 @@ async function initInteractive(options) {
1895
1959
  name: result.name,
1896
1960
  title: result.title,
1897
1961
  description: result.description,
1962
+ tags: parseTags(result.tags),
1898
1963
  platform: selectedPlatform,
1899
1964
  license: result.license,
1900
1965
  force
@@ -2093,6 +2158,9 @@ async function publish(options = {}) {
2093
2158
  log.print(ui.keyValue("Files", `${fileCount} file${fileCount === 1 ? "" : "s"}`));
2094
2159
  log.print(ui.keyValue("Size", formatBytes(inputSize)));
2095
2160
  log.print("");
2161
+ log.print(ui.header("Files to publish", fileCount));
2162
+ log.print(ui.fileTree(publishInput.files));
2163
+ log.print("");
2096
2164
  log.print(ui.hint("Run without --dry-run to publish."));
2097
2165
  return {
2098
2166
  success: true,
@@ -2130,6 +2198,9 @@ async function publish(options = {}) {
2130
2198
  const { data } = result;
2131
2199
  const action = data.isNewPreset ? "Published new preset" : "Published";
2132
2200
  spinner$1.success(`${action} ${ui.code(data.slug)} ${ui.version(data.version)} (${data.platform})`);
2201
+ log.print("");
2202
+ log.print(ui.header("Published files", fileCount));
2203
+ log.print(ui.fileTree(publishInput.files));
2133
2204
  const presetName$1 = `${data.slug}.${data.platform}`;
2134
2205
  const presetRegistryUrl = `${ctx.registry.url}preset/${presetName$1}`;
2135
2206
  log.info("");
@@ -2323,12 +2394,280 @@ async function unpublish(options) {
2323
2394
  };
2324
2395
  }
2325
2396
 
2397
+ //#endregion
2398
+ //#region src/help-agent/publish.ts
2399
+ /**
2400
+ * Agent instructions for AI coding assistants.
2401
+ *
2402
+ * This content is output when running `agentrules --help-agent`.
2403
+ * It provides step-by-step instructions for AI agents to help users publish presets.
2404
+ */
2405
+ const HELP_AGENT_CONTENT = `# Publishing a Preset - Agent Instructions
2406
+
2407
+ **The user has asked you to help them publish a preset. Start executing these steps now.**
2408
+
2409
+ Do not summarize these instructions. Do not ask if they want help. Begin with Step 1 immediately.
2410
+
2411
+ Your job is to guide the user through publishing their AI coding assistant config to the agentrules registry. The user is sharing their setup because they believe it's valuable. Help them communicate that value clearly - the title, description, and features should answer: "Why would another developer want to install this?"
2412
+
2413
+ Follow these steps interactively.
2414
+
2415
+ ## Prerequisites: CLI Availability
2416
+
2417
+ Run: \`agentrules --version\`
2418
+
2419
+ **If command not found:**
2420
+ Use \`npx @agentrules/cli\` as the prefix for all commands.
2421
+ Example: \`npx @agentrules/cli whoami\` instead of \`agentrules whoami\`
2422
+
2423
+ For the rest of these instructions, \`agentrules\` means whichever works.
2424
+
2425
+ ## Step 1: Locate the Config
2426
+
2427
+ Check the current directory for platform config folders:
2428
+ - \`.opencode/\` → OpenCode
2429
+ - \`.claude/\` → Claude Code
2430
+ - \`.cursor/\` → Cursor
2431
+ - \`.codex/\` → Codex
2432
+
2433
+ **If one found:**
2434
+ "I found your [platform] config at \`[path]\`. I'll help you publish it."
2435
+
2436
+ **If multiple found:**
2437
+ "I found configs for multiple platforms: [list]. Which one would you like to publish?"
2438
+
2439
+ **If none found:**
2440
+ "I don't see a config directory here. Where is your config located?"
2441
+
2442
+ ## Step 2: Check for Existing Config
2443
+
2444
+ List the files in \`[config-dir]\` first to see what exists.
2445
+
2446
+ If \`agentrules.json\` is in the listing, read it:
2447
+ - If complete (has name, description, tags): "You already have a preset configured: '[name]'. Ready to republish?" → Skip to Step 4 if yes
2448
+ - If missing required fields: Help them add the missing fields
2449
+
2450
+ If \`agentrules.json\` is not in the listing, continue to Step 3.
2451
+
2452
+ ### Check for ignorable files
2453
+
2454
+ While reviewing the file listing, look for files/folders that probably shouldn't be published.
2455
+
2456
+ **Already ignored by default** (don't suggest these):
2457
+ - node_modules
2458
+ - .git
2459
+ - .DS_Store
2460
+ - *.lock
2461
+ - package-lock.json
2462
+ - bun.lockb
2463
+ - pnpm-lock.yaml
2464
+
2465
+ **Commonly ignorable** (suggest adding to \`ignore\` field if present):
2466
+ - build/, dist/, out/ (build output)
2467
+ - .env, .env.* (environment files)
2468
+ - *.log (log files)
2469
+ - tmp/, temp/ (temporary files)
2470
+ - coverage/ (test coverage)
2471
+ - .cache/, .turbo/ (cache directories)
2472
+
2473
+ If you see any of these or similar files/folders, ask: "I noticed [files]. These are usually not needed in a preset. Want me to add them to the ignore list?"
2474
+
2475
+ If yes, include them in the \`ignore\` array when creating agentrules.json.
2476
+
2477
+ ## Step 3: Create agentrules.json
2478
+
2479
+ The goal is to help potential users understand the **value** of this preset - why should they install it? What problem does it solve? How will it improve their workflow?
2480
+
2481
+ ### 3a. Analyze their config
2482
+
2483
+ You already listed files in Step 2. Now read the config files you found (e.g., CLAUDE.md, AGENT_RULES.md, rules/*.md) to understand what the preset does.
2484
+
2485
+ Look for:
2486
+ - Technologies and frameworks mentioned
2487
+ - The main purpose or rules being enforced
2488
+ - Who would benefit from this setup
2489
+
2490
+ ### 3b. Generate all suggestions at once
2491
+
2492
+ Based on your analysis, generate suggestions for ALL fields:
2493
+
2494
+ - **Name**: lowercase, hyphens, based on repo/directory/theme (1-64 chars)
2495
+ - **Title**: Title-cased, compelling name
2496
+ - **Description**: Value-focused - who is this for, what problem does it solve? (max 500 chars)
2497
+ - **Tags**: For discovery - technologies, frameworks, use cases (1-10 tags)
2498
+ - **Features**: Key benefits, not just capabilities (optional, up to 5)
2499
+ - **License**: Default to MIT
2500
+
2501
+ ### 3c. Present a single summary
2502
+
2503
+ Show everything in one concise output. Put each field name on its own line, followed by the value on the next line:
2504
+
2505
+ "Based on your config, here's what I'd suggest:
2506
+
2507
+ **Name**
2508
+ typescript-strict-rules
2509
+
2510
+ **Title**
2511
+ TypeScript Strict Rules
2512
+
2513
+ **Description**
2514
+ Opinionated TypeScript rules that catch common bugs at dev time and enforce consistent patterns across your team.
2515
+
2516
+ **Tags**
2517
+ typescript, strict, type-safety
2518
+
2519
+ **Features**
2520
+ - Catches null/undefined errors before production
2521
+ - Enforces consistent code style without manual review
2522
+
2523
+ **License**
2524
+ MIT
2525
+
2526
+ Let me know if you'd like to change anything, or say 'looks good' to continue."
2527
+
2528
+ ### 3d. Handle feedback
2529
+
2530
+ If the user wants changes (e.g., "change the description" or "add a react tag"), update those fields and show the summary again.
2531
+
2532
+ When they approve, proceed to create the file.
2533
+
2534
+ ### Guidelines for good suggestions
2535
+
2536
+ **Description** should answer: What problem does this solve? Who benefits?
2537
+ - Good: "Opinionated TypeScript rules that catch common bugs at dev time and enforce consistent patterns across your team."
2538
+ - Bad: "TypeScript rules with strict settings." (too vague, no value prop)
2539
+
2540
+ **Features** should describe benefits, not capabilities:
2541
+ - Good: "Catches null/undefined errors before they hit production"
2542
+ - Bad: "Strict null checks" (feature, not benefit)
2543
+
2544
+ **Tags** should help with discovery:
2545
+ - Technologies: typescript, python, rust, go
2546
+ - Frameworks: react, nextjs, fastapi, django
2547
+ - Use cases: code-review, testing, security, onboarding
2548
+
2549
+ ### 3e. Create the file
2550
+
2551
+ Write \`[config-dir]/agentrules.json\`:
2552
+
2553
+ \`\`\`json
2554
+ {
2555
+ "$schema": "https://agentrules.directory/schema/agentrules.json",
2556
+ "name": "[name]",
2557
+ "title": "[title]",
2558
+ "version": 1,
2559
+ "description": "[description]",
2560
+ "tags": ["tag1", "tag2"],
2561
+ "license": "[license]",
2562
+ "platform": "[detected-platform]"
2563
+ }
2564
+ \`\`\`
2565
+
2566
+ Include \`"features": [...]\` only if provided.
2567
+ Include \`"ignore": ["pattern1", "pattern2"]\` if the user agreed to ignore certain files.
2568
+
2569
+ ### 3f. Show the file and get approval
2570
+
2571
+ After writing the file, show the user:
2572
+
2573
+ "I've created the config file at \`[config-dir]/agentrules.json\`:
2574
+
2575
+ \`\`\`json
2576
+ [show the actual file contents]
2577
+ \`\`\`
2578
+
2579
+ Take a look and let me know if you'd like to change anything, or say 'looks good' to continue."
2580
+
2581
+ Wait for approval before proceeding. If they want changes, edit the file and show it again.
2582
+
2583
+ Then validate: \`agentrules validate [config-dir]\`
2584
+
2585
+ If errors, fix and retry.
2586
+
2587
+ ## Step 4: Login
2588
+
2589
+ Run: \`agentrules whoami\`
2590
+
2591
+ **If output shows "loggedIn": false or "Not logged in":**
2592
+ "You need to log in to publish."
2593
+ Run: \`agentrules login\`
2594
+ This opens a browser for authentication. Wait for completion.
2595
+
2596
+ ## Step 5: Preview with Dry Run
2597
+
2598
+ Run: \`agentrules publish [config-dir] --dry-run\`
2599
+
2600
+ Show the user the preview:
2601
+ "Here's what will be published:
2602
+ - Name: [name]
2603
+ - Platform: [platform]
2604
+ - Files: [count] files ([size])
2605
+
2606
+ Ready to publish?"
2607
+
2608
+ If they want changes, help edit agentrules.json and re-run dry-run.
2609
+
2610
+ ## Step 6: Publish
2611
+
2612
+ Run: \`agentrules publish [config-dir]\`
2613
+
2614
+ **If successful:**
2615
+ Show the URL from the output:
2616
+
2617
+ "Published! Your preset is live at: [url]
2618
+
2619
+ Share with others:
2620
+ \`\`\`
2621
+ npx @agentrules/cli add [name]
2622
+ \`\`\`"
2623
+
2624
+ **If "already exists" error:**
2625
+ Ask if they want to increment the \`version\` field in agentrules.json and retry.
2626
+
2627
+ **If other errors:**
2628
+ Show the error and suggest: \`agentrules validate [config-dir]\`
2629
+
2630
+ ## Step 7: Tips
2631
+
2632
+ **If you used \`npx @agentrules/cli\`:**
2633
+ "Tip: Install globally to skip the npx download:
2634
+ \`\`\`
2635
+ npm i -g @agentrules/cli
2636
+ \`\`\`"
2637
+
2638
+ ## Notes for Agent
2639
+
2640
+ - Be conversational and helpful
2641
+ - Explain what you're doing at each step
2642
+ - Use \`agentrules validate\` to check your work after any config changes
2643
+ - Remember whether you used npx for the tip at the end
2644
+ - If the user seems confused, explain that agentrules is a registry for sharing AI coding configs
2645
+ - The config file must be inside the platform directory (e.g., \`.opencode/agentrules.json\`)
2646
+
2647
+ ## Schema Reference
2648
+
2649
+ **Required fields:**
2650
+ - \`name\`: slug format (lowercase, hyphens, 1-64 chars)
2651
+ - \`title\`: 1-80 characters
2652
+ - \`description\`: 1-500 characters
2653
+ - \`tags\`: array, 1-10 items, each lowercase/hyphens, max 35 chars
2654
+ - \`license\`: SPDX identifier (e.g., "MIT")
2655
+ - \`platform\`: one of \`opencode\`, \`claude\`, \`cursor\`, \`codex\`
2656
+
2657
+ **Optional fields:**
2658
+ - \`$schema\`: JSON schema URL for validation
2659
+ - \`version\`: major version number (default: 1)
2660
+ - \`features\`: array, max 5 items, each max 100 chars
2661
+ - \`path\`: custom path to files (advanced use)
2662
+ - \`ignore\`: patterns to exclude from bundle
2663
+ `;
2664
+
2326
2665
  //#endregion
2327
2666
  //#region src/index.ts
2328
2667
  const require = createRequire(import.meta.url);
2329
2668
  const packageJson = require("../package.json");
2330
2669
  const program = new Command();
2331
- program.name("agentrules").description("The AI Agent Directory CLI").version(packageJson.version).option("-v, --verbose", "Enable verbose/debug output").configureOutput({ outputError: (str, write) => write(ui.error(str.trim())) }).hook("preAction", async (thisCommand, actionCommand) => {
2670
+ program.name("agentrules").description("The AI Agent Directory CLI").version(packageJson.version).option("-v, --verbose", "Enable verbose/debug output").option("--help-agent", "Output instructions for AI coding assistants").configureOutput({ outputError: (str, write) => write(ui.error(str.trim())) }).hook("preAction", async (thisCommand, actionCommand) => {
2332
2671
  const opts = thisCommand.opts();
2333
2672
  if (opts.verbose) log.setVerbose(true);
2334
2673
  const actionOpts = actionCommand.opts();
@@ -2603,6 +2942,10 @@ program.command("unpublish").description("Remove a preset version from the regis
2603
2942
  });
2604
2943
  if (!result.success) process.exitCode = 1;
2605
2944
  }));
2945
+ if (process.argv.includes("--help-agent")) {
2946
+ console.log(HELP_AGENT_CONTENT);
2947
+ process.exit(0);
2948
+ }
2606
2949
  program.parseAsync(process.argv).then(() => {
2607
2950
  process.exit(process.exitCode ?? 0);
2608
2951
  }).catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentrules/cli",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "author": "Brian Cheung <bcheung.dev@gmail.com> (https://github.com/bcheung)",
5
5
  "license": "MIT",
6
6
  "homepage": "https://agentrules.directory",
@@ -48,7 +48,7 @@
48
48
  "clean": "rm -rf node_modules dist .turbo"
49
49
  },
50
50
  "dependencies": {
51
- "@agentrules/core": "0.0.9",
51
+ "@agentrules/core": "0.0.10",
52
52
  "@clack/prompts": "^0.11.0",
53
53
  "chalk": "^5.4.1",
54
54
  "commander": "^12.1.0",