@przeprogramowani/10x-cli 1.23.0 → 1.23.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.
package/README.md CHANGED
@@ -7,6 +7,10 @@ prompts, and configs directly into your workspace.
7
7
 
8
8
  - **Node 20+** — this is the only runtime dependency.
9
9
 
10
+ Corporate firewall / proxy allowlist (for security and sysadmin teams):
11
+ Polish [docs/wymagania-sieciowe.md](docs/wymagania-sieciowe.md),
12
+ English [docs/network-requirements.md](docs/network-requirements.md).
13
+
10
14
  ## Install
11
15
 
12
16
  ```bash
@@ -181,7 +185,7 @@ remain visible. Full lesson downloads and other commands remain available below.
181
185
  | `--name <name>` | Filter by artifact name (requires `--type`) |
182
186
  | `--dry-run` | Show what would be written without touching the filesystem |
183
187
  | `--course <slug>` | Select an entitled course ID or slug; default is the project edition or API recommendation |
184
- | `--no-course-rules` | Skip the course rules block in your rules file (`CLAUDE.md`/`AGENTS.md`); removes an unchanged block whose ownership and baseline are known. Use `--course-rules` to re-enable. |
188
+ | `--no-course-rules` | Skip the course rules block in your rules file (`CLAUDE.md`/`AGENTS.md`); removes an unchanged block whose ownership and baseline are known. Broken or duplicate markers are left untouched so skills and prompts can still apply. Use `--course-rules` to re-enable. |
185
189
 
186
190
  #### Examples
187
191
 
@@ -204,6 +208,7 @@ remain visible. Full lesson downloads and other commands remain available below.
204
208
 
205
209
  # Skip the course rules block (use only your rules). Persisted across runs;
206
210
  # an unchanged block with a known baseline is removed. Re-enable later with --course-rules.
211
+ # Also the recovery path when CLAUDE.md markers are broken (orphan/duplicate).
207
212
  10x get m1l1 --no-course-rules
208
213
  10x get m1l2 --course-rules
209
214
 
package/dist/index.mjs CHANGED
@@ -3183,7 +3183,7 @@ var cac = (name = "") => new CAC(name);
3183
3183
  // package.json
3184
3184
  var package_default = {
3185
3185
  name: "@przeprogramowani/10x-cli",
3186
- version: "1.23.0",
3186
+ version: "1.23.1",
3187
3187
  description: "Open-source CLI for 10xDevs course content",
3188
3188
  repository: {
3189
3189
  type: "git",
@@ -5655,6 +5655,62 @@ var defaultDeps = {
5655
5655
  import { existsSync as existsSync5, accessSync, constants } from "node:fs";
5656
5656
  import { join as join6 } from "node:path";
5657
5657
 
5658
+ // src/lib/sentinel-migration.ts
5659
+ var OLD_BEGIN = "<!-- BEGIN @przeprogramowani/10x-toolkit -->";
5660
+ var OLD_END = "<!-- END @przeprogramowani/10x-toolkit -->";
5661
+ class RulesMarkerRepairError extends Error {
5662
+ constructor(detail) {
5663
+ super(`Rules markers need repair: ${detail}`);
5664
+ this.name = "RulesMarkerRepairError";
5665
+ }
5666
+ }
5667
+ function inspectRulesBlock(content, begin, end) {
5668
+ const starts = content.split(begin).length - 1;
5669
+ const ends = content.split(end).length - 1;
5670
+ if (!starts && !ends)
5671
+ return null;
5672
+ const start = content.indexOf(begin);
5673
+ const stop = content.indexOf(end);
5674
+ if (starts !== 1 || ends !== 1 || stop < start) {
5675
+ throw new RulesMarkerRepairError("orphan, duplicate, or out-of-order sentinel. Preserve the file and resolve its markers before continuing.");
5676
+ }
5677
+ const finish = stop + end.length;
5678
+ return { start, finish, text: content.slice(start, finish), body: content.slice(start + begin.length, stop).trim() };
5679
+ }
5680
+ function removeRulesBlockWithMarkers(existing, begin, end) {
5681
+ const block = inspectRulesBlock(existing, begin, end);
5682
+ if (!block)
5683
+ return { content: existing, removed: false };
5684
+ return { content: existing.slice(0, block.start) + existing.slice(block.finish), removed: true };
5685
+ }
5686
+ function applyRulesBlockWithMarkers(existing, body, begin, end) {
5687
+ for (const marker of [OLD_BEGIN, OLD_END, begin, end]) {
5688
+ if (body.includes(marker))
5689
+ throw new Error(`rules body contains a sentinel marker (${JSON.stringify(marker)})`);
5690
+ }
5691
+ const current = inspectRulesBlock(existing, begin, end);
5692
+ const legacy = begin === OLD_BEGIN ? null : inspectRulesBlock(existing, OLD_BEGIN, OLD_END);
5693
+ const fresh = `${begin}
5694
+
5695
+ ${body.trim()}
5696
+
5697
+ ${end}`;
5698
+ const blocks = [current, legacy].filter((entry) => entry !== null).sort((a2, b2) => a2.start - b2.start);
5699
+ if (blocks.length > 1 && blocks[0].finish > blocks[1].start)
5700
+ throw new RulesMarkerRepairError("nested sentinel blocks.");
5701
+ if (!blocks.length)
5702
+ return { content: `${existing}${existing && !existing.endsWith(`
5703
+ `) ? `
5704
+ ` : ""}${fresh}
5705
+ `, warnings: [] };
5706
+ let content = existing;
5707
+ for (let i = blocks.length - 1;i >= 0; i--) {
5708
+ const block = blocks[i];
5709
+ content = content.slice(0, block.start) + (i === 0 ? fresh : "") + content.slice(block.finish);
5710
+ }
5711
+ return { content, warnings: [] };
5712
+ }
5713
+
5658
5714
  // src/lib/course-selection.ts
5659
5715
  class CourseSelectionError extends Error {
5660
5716
  code;
@@ -5696,6 +5752,9 @@ function reportCourseError(ctx, error) {
5696
5752
  if (error instanceof CourseBindingError || error instanceof CourseSelectionError) {
5697
5753
  outputError(ctx, error.code, error.message, error.code === "course_access_denied" ? ExitCodes.FORBIDDEN : error.code === "course_mismatch" ? ExitCodes.USAGE : ExitCodes.ERROR);
5698
5754
  }
5755
+ if (error instanceof RulesMarkerRepairError) {
5756
+ outputError(ctx, "rules_markers_need_repair", error.message, ExitCodes.ERROR, "Keep the file and leave exactly one BEGIN/END pair, in that order. Text before or after the block is allowed. To skip the course rules block and still apply the lesson, run '10x get <lesson> --no-course-rules'.");
5757
+ }
5699
5758
  throw error;
5700
5759
  }
5701
5760
  async function resolveCourseSelection(ctx, options, dependencies = {}) {
@@ -6171,58 +6230,6 @@ function pruneEmptyParents(root, file, boundary) {
6171
6230
  // src/lib/managed-rules.ts
6172
6231
  import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
6173
6232
  import { join as join7 } from "node:path";
6174
-
6175
- // src/lib/sentinel-migration.ts
6176
- var OLD_BEGIN = "<!-- BEGIN @przeprogramowani/10x-toolkit -->";
6177
- var OLD_END = "<!-- END @przeprogramowani/10x-toolkit -->";
6178
- function inspectRulesBlock(content, begin, end) {
6179
- const starts = content.split(begin).length - 1;
6180
- const ends = content.split(end).length - 1;
6181
- if (!starts && !ends)
6182
- return null;
6183
- const start = content.indexOf(begin);
6184
- const stop = content.indexOf(end);
6185
- if (starts !== 1 || ends !== 1 || stop < start) {
6186
- throw new Error("Rules markers need repair: orphan, duplicate, or out-of-order sentinel. Preserve the file and resolve its markers before continuing.");
6187
- }
6188
- const finish = stop + end.length;
6189
- return { start, finish, text: content.slice(start, finish), body: content.slice(start + begin.length, stop).trim() };
6190
- }
6191
- function removeRulesBlockWithMarkers(existing, begin, end) {
6192
- const block = inspectRulesBlock(existing, begin, end);
6193
- if (!block)
6194
- return { content: existing, removed: false };
6195
- return { content: existing.slice(0, block.start) + existing.slice(block.finish), removed: true };
6196
- }
6197
- function applyRulesBlockWithMarkers(existing, body, begin, end) {
6198
- for (const marker of [OLD_BEGIN, OLD_END, begin, end]) {
6199
- if (body.includes(marker))
6200
- throw new Error(`rules body contains a sentinel marker (${JSON.stringify(marker)})`);
6201
- }
6202
- const current = inspectRulesBlock(existing, begin, end);
6203
- const legacy = begin === OLD_BEGIN ? null : inspectRulesBlock(existing, OLD_BEGIN, OLD_END);
6204
- const fresh = `${begin}
6205
-
6206
- ${body.trim()}
6207
-
6208
- ${end}`;
6209
- const blocks = [current, legacy].filter((entry) => entry !== null).sort((a2, b2) => a2.start - b2.start);
6210
- if (blocks.length > 1 && blocks[0].finish > blocks[1].start)
6211
- throw new Error("Rules markers need repair: nested sentinel blocks.");
6212
- if (!blocks.length)
6213
- return { content: `${existing}${existing && !existing.endsWith(`
6214
- `) ? `
6215
- ` : ""}${fresh}
6216
- `, warnings: [] };
6217
- let content = existing;
6218
- for (let i = blocks.length - 1;i >= 0; i--) {
6219
- const block = blocks[i];
6220
- content = content.slice(0, block.start) + (i === 0 ? fresh : "") + content.slice(block.finish);
6221
- }
6222
- return { content, warnings: [] };
6223
- }
6224
-
6225
- // src/lib/managed-rules.ts
6226
6233
  function otherRulesOwners(root, profile) {
6227
6234
  const owners = [];
6228
6235
  for (const candidate of [...Object.values(PROFILES), ...Object.values(LEGACY_PROFILES)]) {
@@ -6236,14 +6243,32 @@ function otherRulesOwners(root, profile) {
6236
6243
  }
6237
6244
  return owners;
6238
6245
  }
6246
+ function inspectExisting(content, begin, end) {
6247
+ try {
6248
+ return { malformed: false, block: inspectRulesBlock(content, begin, end) };
6249
+ } catch {
6250
+ return { malformed: true, block: null };
6251
+ }
6252
+ }
6239
6253
  function planManagedRules(root, profile, body, enabled, baseline) {
6240
6254
  const path = join7(root, profile.rulesFile);
6241
6255
  assertProjectFilePath(root, path);
6242
6256
  const existing = existsSync7(path) ? readFileSync6(path, "utf8") : "";
6243
- const block = inspectRulesBlock(existing, profile.sentinelBegin, profile.sentinelEnd);
6244
- const legacy = inspectRulesBlock(existing, OLD_BEGIN, OLD_END);
6245
- if (block && legacy && Math.max(block.start, legacy.start) < Math.min(block.finish, legacy.finish))
6246
- throw new Error("Rules markers need repair: nested sentinel blocks.");
6257
+ const current = inspectExisting(existing, profile.sentinelBegin, profile.sentinelEnd);
6258
+ const legacyScan = inspectExisting(existing, OLD_BEGIN, OLD_END);
6259
+ const block = current.block;
6260
+ const legacy = legacyScan.block;
6261
+ const nested = !!block && !!legacy && Math.max(block.start, legacy.start) < Math.min(block.finish, legacy.finish);
6262
+ const malformed = current.malformed || legacyScan.malformed || nested;
6263
+ if (malformed) {
6264
+ if (!enabled)
6265
+ return { action: "conflict_skipped", content: existing, isConflict: true, reason: "malformed_markers", next: baseline };
6266
+ if (body === undefined)
6267
+ return { action: "unchanged", content: existing, isConflict: false, next: baseline };
6268
+ if (nested)
6269
+ throw new RulesMarkerRepairError("nested sentinel blocks.");
6270
+ inspectRulesBlock(existing, current.malformed ? profile.sentinelBegin : OLD_BEGIN, current.malformed ? profile.sentinelEnd : OLD_END);
6271
+ }
6247
6272
  if (body === undefined && enabled)
6248
6273
  return { action: "unchanged", content: existing, isConflict: false, next: baseline };
6249
6274
  const validBaseline = baseline?.path === profile.rulesFile && baseline.begin === profile.sentinelBegin && baseline.end === profile.sentinelEnd ? baseline : undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@przeprogramowani/10x-cli",
3
- "version": "1.23.0",
3
+ "version": "1.23.1",
4
4
  "description": "Open-source CLI for 10xDevs course content",
5
5
  "repository": {
6
6
  "type": "git",