@przeprogramowani/10x-cli 1.7.0 → 1.8.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.
Files changed (3) hide show
  1. package/README.md +14 -0
  2. package/dist/index.mjs +64 -13
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -69,6 +69,7 @@ Once installed, just tell your agent to **set up 10x-cli** and it will pick up t
69
69
  | `--name <name>` | Filter by artifact name (requires `--type`) |
70
70
  | `--dry-run` | Show what would be written without touching the filesystem |
71
71
  | `--course <slug>` | Override the course slug (default: `10xdevs3`) |
72
+ | `--no-course-rules` | Skip the course rules block in your rules file (`CLAUDE.md`/`AGENTS.md`); strips an existing one. Use `--course-rules` to re-enable. |
72
73
 
73
74
  #### Examples
74
75
 
@@ -88,8 +89,21 @@ Once installed, just tell your agent to **set up 10x-cli** and it will pick up t
88
89
 
89
90
  # Use with a different AI coding tool
90
91
  10x get m1l1 --tool cursor
92
+
93
+ # Skip the course rules block (use only your rules). Persisted across runs;
94
+ # a previously-applied block is stripped. Re-enable later with --course-rules.
95
+ 10x get m1l1 --no-course-rules
96
+ 10x get m1l2 --course-rules
97
+
98
+ # An explicit rules request always applies, even with the opt-out persisted
99
+ 10x get m1l1 --type rules
91
100
  ```
92
101
 
102
+ > The `--no-course-rules` / `--course-rules` choice is saved as `courseRules`
103
+ > in `config.json` and applies to subsequent plain `10x get` runs. An explicit
104
+ > `--type rules` request overrides the opt-out for that run. Skills, prompts,
105
+ > and config-templates are unaffected.
106
+
93
107
  ### Global Flags
94
108
 
95
109
  - `--json` — Machine-readable JSON output (auto-detected when piped)
package/dist/index.mjs CHANGED
@@ -2271,7 +2271,7 @@ var cac = (name = "") => new CAC(name);
2271
2271
  // package.json
2272
2272
  var package_default = {
2273
2273
  name: "@przeprogramowani/10x-cli",
2274
- version: "1.7.0",
2274
+ version: "1.8.0",
2275
2275
  description: "Open-source CLI for 10xDevs course content",
2276
2276
  repository: {
2277
2277
  type: "git",
@@ -2992,6 +2992,9 @@ function readToolConfig() {
2992
2992
  if (parsed.acknowledgedOrphans !== undefined && (!Array.isArray(parsed.acknowledgedOrphans) || !parsed.acknowledgedOrphans.every((x2) => typeof x2 === "string"))) {
2993
2993
  delete parsed.acknowledgedOrphans;
2994
2994
  }
2995
+ if (parsed.courseRules !== undefined && typeof parsed.courseRules !== "boolean") {
2996
+ delete parsed.courseRules;
2997
+ }
2995
2998
  return parsed;
2996
2999
  } catch {
2997
3000
  return null;
@@ -3004,6 +3007,14 @@ function saveToolConfig(config) {
3004
3007
  }
3005
3008
  writeJsonAtomic(toolConfigPath(), config);
3006
3009
  }
3010
+ function updateToolConfig(patch) {
3011
+ const current = readToolConfig();
3012
+ const merged = { ...current, ...patch };
3013
+ if (typeof merged.tool !== "string") {
3014
+ throw new Error("updateToolConfig: refusing to persist a config without a `tool` — " + "no prior config exists and the patch did not seed one.");
3015
+ }
3016
+ saveToolConfig(merged);
3017
+ }
3007
3018
 
3008
3019
  // src/lib/api-client.ts
3009
3020
  var DEFAULT_API_BASE = "https://10x-toolkit-api.przeprogramowani.workers.dev";
@@ -4563,6 +4574,7 @@ async function applyBundle(bundle, projectRoot, options = {}) {
4563
4574
  const course = options.course ?? DEFAULT_COURSE;
4564
4575
  const profile = options.profile ?? PROFILES[DEFAULT_TOOL];
4565
4576
  const onConflict = options.onConflict;
4577
+ const applyCourseRules = options.applyCourseRules !== false;
4566
4578
  const manifestDir = join5(projectRoot, profile.manifestDir);
4567
4579
  const prevManifest = readManifest(manifestDir);
4568
4580
  for (const skill of bundle.skills) {
@@ -4672,7 +4684,17 @@ async function applyBundle(bundle, projectRoot, options = {}) {
4672
4684
  const rulesFilePath = join5(projectRoot, profile.rulesFile);
4673
4685
  const existingRules = readFileOrEmpty(rulesFilePath);
4674
4686
  let rulesAction;
4675
- if (bundle.rules.length === 0) {
4687
+ if (!applyCourseRules) {
4688
+ const { content: stripped, removed: removed2 } = removeRulesBlockWithMarkers(existingRules, profile.sentinelBegin, profile.sentinelEnd);
4689
+ if (removed2 && stripped !== existingRules) {
4690
+ rulesAction = "removed";
4691
+ if (!dryRun) {
4692
+ writeFileAt(rulesFilePath, stripped);
4693
+ }
4694
+ } else {
4695
+ rulesAction = "unchanged";
4696
+ }
4697
+ } else if (bundle.rules.length === 0) {
4676
4698
  rulesAction = "unchanged";
4677
4699
  } else {
4678
4700
  const rulesBody = bundle.rules.map((r2) => r2.content.trim()).join(`
@@ -5346,9 +5368,16 @@ var ARTIFACT_TYPES = ["skills", "prompts", "rules", "configs"];
5346
5368
  function isArtifactType(s) {
5347
5369
  return ARTIFACT_TYPES.includes(s);
5348
5370
  }
5371
+ function resolveCourseRulesFlag(argv) {
5372
+ if (argv.includes("--no-course-rules"))
5373
+ return false;
5374
+ if (argv.includes("--course-rules"))
5375
+ return true;
5376
+ return;
5377
+ }
5349
5378
  var DEFAULT_COURSE2 = "10xdevs3";
5350
5379
  function registerGetCommand(cli) {
5351
- cli.command("get <ref>", "Fetch and apply a lesson pack").option("--dry-run", "Show what would be written without touching the filesystem").option("--course <course>", "Override the course slug (default: 10xdevs3)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex, windsurf, gemini, generic)").option("--print", "Print artifact content to stdout instead of writing to files").option("--type <type>", "Artifact type filter: skills, prompts, rules, configs").option("--name <name>", "Artifact name filter (requires --type)").option("--lang <lang>", "Content language: en (default) or pl").action(async (ref, options) => {
5380
+ cli.command("get <ref>", "Fetch and apply a lesson pack").option("--dry-run", "Show what would be written without touching the filesystem").option("--course <course>", "Override the course slug (default: 10xdevs3)").option("--tool <tool>", "AI coding tool (claude-code, cursor, copilot, codex, windsurf, gemini, generic)").option("--print", "Print artifact content to stdout instead of writing to files").option("--type <type>", "Artifact type filter: skills, prompts, rules, configs").option("--name <name>", "Artifact name filter (requires --type)").option("--lang <lang>", "Content language: en (default) or pl").option("--no-course-rules", "Skip applying the course rules block to your rules file (CLAUDE.md/AGENTS.md)").action(async (ref, options) => {
5352
5381
  const ctx = resolveContext(options);
5353
5382
  await runGet(ctx, ref, options);
5354
5383
  });
@@ -5371,10 +5400,18 @@ async function runGet(ctx, ref, options) {
5371
5400
  const auth = await requireAuth(ctx);
5372
5401
  const course = options.course ?? DEFAULT_COURSE2;
5373
5402
  const profile = await resolveToolProfile(options.tool, process.cwd());
5403
+ const dryRun = options.dryRun === true;
5374
5404
  const lang = options.lang ?? readToolConfig()?.lang ?? "en";
5375
- if (options.lang) {
5376
- const existing = readToolConfig();
5377
- saveToolConfig({ tool: existing?.tool ?? profile.toolId, lang: options.lang });
5405
+ const explicitCourseRules = resolveCourseRulesFlag(process.argv);
5406
+ const resolvedCourseRules = explicitCourseRules ?? readToolConfig()?.courseRules ?? true;
5407
+ if (!dryRun && (options.lang || explicitCourseRules !== undefined)) {
5408
+ const tool = readToolConfig()?.tool ?? profile.toolId;
5409
+ const patch = { tool };
5410
+ if (options.lang)
5411
+ patch.lang = options.lang;
5412
+ if (explicitCourseRules !== undefined)
5413
+ patch.courseRules = explicitCourseRules;
5414
+ updateToolConfig(patch);
5378
5415
  }
5379
5416
  if (options.print) {
5380
5417
  await runPrintMode(ctx, parsed.lessonId, course, profile, auth.access_token, lang, options);
@@ -5405,15 +5442,18 @@ async function runGet(ctx, ref, options) {
5405
5442
  if (prevManifest && prevManifest.manifestVersion === 2) {
5406
5443
  showUpgradeNotice(isTTY);
5407
5444
  }
5445
+ const applyCourseRules = isFiltered ? true : resolvedCourseRules;
5408
5446
  const writeResult = await applyBundle(bundle, process.cwd(), {
5409
- dryRun: options.dryRun === true,
5447
+ dryRun,
5410
5448
  profile,
5411
5449
  partial: isFiltered,
5412
- onConflict: createConflictResolver(isTTY)
5450
+ onConflict: createConflictResolver(isTTY),
5451
+ applyCourseRules
5413
5452
  });
5414
- renderGetResult(ctx, bundle, writeResult, options.dryRun === true, profile, {
5453
+ renderGetResult(ctx, bundle, writeResult, dryRun, profile, {
5415
5454
  language: contentLang ?? lang,
5416
- languageFallback: isFallback
5455
+ languageFallback: isFallback,
5456
+ applyCourseRules
5417
5457
  });
5418
5458
  }
5419
5459
  function filterBundle(ctx, bundle, options) {
@@ -5553,7 +5593,12 @@ function formatAction(action) {
5553
5593
  return action;
5554
5594
  }
5555
5595
  }
5556
- function renderGetResult(ctx, bundle, writeResult, dryRun, profile, langMeta = { language: "en", languageFallback: false }) {
5596
+ function renderGetResult(ctx, bundle, writeResult, dryRun, profile, langMeta = {
5597
+ language: "en",
5598
+ languageFallback: false,
5599
+ applyCourseRules: true
5600
+ }) {
5601
+ const applyCourseRules = langMeta.applyCourseRules;
5557
5602
  const totalRemovals = writeResult.removals.skills.length + writeResult.removals.prompts.length + writeResult.removals.configs.length;
5558
5603
  if (ctx.json) {
5559
5604
  output(ctx, "", {
@@ -5574,7 +5619,7 @@ function renderGetResult(ctx, bundle, writeResult, dryRun, profile, langMeta = {
5574
5619
  counts: {
5575
5620
  skills: writeResult.skills.length,
5576
5621
  prompts: writeResult.prompts.length,
5577
- rules: bundle.rules.length,
5622
+ rules: applyCourseRules ? bundle.rules.length : 0,
5578
5623
  configs: writeResult.configs.length,
5579
5624
  removals: totalRemovals
5580
5625
  }
@@ -5602,7 +5647,13 @@ function renderGetResult(ctx, bundle, writeResult, dryRun, profile, langMeta = {
5602
5647
  for (const prompt of writeResult.prompts) {
5603
5648
  lines.push(` [${formatAction(prompt.action)}] prompt ${prompt.path}`);
5604
5649
  }
5605
- if (bundle.rules.length > 0) {
5650
+ if (!applyCourseRules) {
5651
+ if (writeResult.rules.action === "removed") {
5652
+ lines.push(` [removed] rules ${profile.rulesFile}`);
5653
+ } else {
5654
+ verbose(ctx, `course rules disabled — ${profile.rulesFile} left untouched`);
5655
+ }
5656
+ } else if (bundle.rules.length > 0) {
5606
5657
  lines.push(` [${writeResult.rules.action}] rules ${profile.rulesFile} (${bundle.rules.length} block${bundle.rules.length === 1 ? "" : "s"})`);
5607
5658
  }
5608
5659
  for (const config of writeResult.configs) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@przeprogramowani/10x-cli",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Open-source CLI for 10xDevs course content",
5
5
  "repository": {
6
6
  "type": "git",