@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 +6 -1
- package/dist/index.mjs +82 -57
- package/package.json +1 -1
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.
|
|
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
|
|
6244
|
-
const
|
|
6245
|
-
|
|
6246
|
-
|
|
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;
|