@przeprogramowani/10x-cli 1.27.0 → 1.29.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 +10 -5
  2. package/dist/index.mjs +108 -48
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -309,13 +309,18 @@ releases and suggests running `10x sync`.
309
309
  10x changelog --since 2026-10-01 --limit 50
310
310
  ```
311
311
 
312
- Releases are listed newest first as `vX.Y.Z — <date>` followed by their notes, or
313
- `No toolkit changes since <baseline>.` Exit code is `0` whether or not there are
314
- new releases; an invalid `--since`/`--limit` exits `2`, a missing login `3`. Against
312
+ The output starts with where the project stands (for example `This project is on
313
+ toolkit v2.59.2 (recorded by 10x sync on 2026-10-05) — 2 newer releases:`), then
314
+ lists releases newest first: `vX.Y.Z — <date>`, the release highlights as bullets,
315
+ and the changed skills, prompts, rules and config templates with a status mark
316
+ (`+` new, `~` changed, `-` removed, `>` renamed) and a one-line summary. Colours are
317
+ used only on an interactive terminal and respect `NO_COLOR`. Exit code is `0`
318
+ whether or not there are new releases; an invalid `--since`/`--limit` exits `2`, a
319
+ missing login `3`, and an account without course access `4`. Against
315
320
  a backend that does not serve the changelog yet the command fails with
316
321
  `changelog_unsupported` (exit `1`); `get` and `sync` keep working. JSON output
317
- (`--json` or piped) is `{ baseline: { source, version?, date? }, newEntries,
318
- entries }`, where `source` is `flag`, `sync`, `applied` or `none`.
322
+ (`--json` or piped) is `{ baseline: { source, version?, date?, recordedAt? },
323
+ newEntries, entries }`, where `source` is `flag`, `sync`, `applied` or `none`.
319
324
 
320
325
  ### `10x bench`
321
326
 
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.27.0",
3186
+ version: "1.29.0",
3187
3187
  description: "Open-source CLI for 10xDevs course content",
3188
3188
  repository: {
3189
3189
  type: "git",
@@ -3929,7 +3929,7 @@ function isChangelogEntry(value) {
3929
3929
  if (!value || typeof value !== "object" || Array.isArray(value))
3930
3930
  return false;
3931
3931
  const e2 = value;
3932
- if (e2["schemaVersion"] !== undefined && typeof e2["schemaVersion"] !== "number")
3932
+ if (e2["schemaVersion"] !== 1)
3933
3933
  return false;
3934
3934
  if (typeof e2["version"] !== "string" || !e2["version"])
3935
3935
  return false;
@@ -3939,6 +3939,8 @@ function isChangelogEntry(value) {
3939
3939
  return false;
3940
3940
  if (e2["model"] !== null && typeof e2["model"] !== "string")
3941
3941
  return false;
3942
+ if (!Array.isArray(e2["highlights"]) || !e2["highlights"].every((h2) => typeof h2 === "string"))
3943
+ return false;
3942
3944
  const artifacts = e2["artifacts"];
3943
3945
  if (!artifacts || typeof artifacts !== "object" || Array.isArray(artifacts))
3944
3946
  return false;
@@ -3948,7 +3950,7 @@ function isChangelogEntry(value) {
3948
3950
  if (!item || typeof item !== "object")
3949
3951
  return false;
3950
3952
  const change = item;
3951
- return typeof change["name"] === "string" && typeof change["status"] === "string" && CHANGELOG_STATUSES.includes(change["status"]);
3953
+ return typeof change["name"] === "string" && typeof change["status"] === "string" && CHANGELOG_STATUSES.includes(change["status"]) && (change["summary"] === null || typeof change["summary"] === "string");
3952
3954
  });
3953
3955
  });
3954
3956
  }
@@ -4565,6 +4567,38 @@ function formatReleaseAt(iso, now = new Date) {
4565
4567
  return `${absolute} (tomorrow)`;
4566
4568
  return `${absolute} (in ${days} days)`;
4567
4569
  }
4570
+ function colorsEnabled() {
4571
+ return process.env["NO_COLOR"] === undefined && process.stderr.isTTY === true;
4572
+ }
4573
+ var STYLE = {
4574
+ bold: "1",
4575
+ dim: "2",
4576
+ red: "31",
4577
+ green: "32",
4578
+ yellow: "33",
4579
+ cyan: "36"
4580
+ };
4581
+ function paint(style, text) {
4582
+ if (!colorsEnabled())
4583
+ return text;
4584
+ return `\x1B[${style}m${text}\x1B[0m`;
4585
+ }
4586
+ function wrapText(text, width, prefix, indent = " ".repeat(prefix.length)) {
4587
+ const lines = [];
4588
+ let line = prefix;
4589
+ let empty = true;
4590
+ for (const word of text.split(/\s+/).filter(Boolean)) {
4591
+ if (!empty && line.length + 1 + word.length > width) {
4592
+ lines.push(line);
4593
+ line = indent + word;
4594
+ } else {
4595
+ line += (empty ? "" : " ") + word;
4596
+ }
4597
+ empty = false;
4598
+ }
4599
+ lines.push(line);
4600
+ return lines;
4601
+ }
4568
4602
 
4569
4603
  // src/commands/bench.ts
4570
4604
  function registerBenchCommand(cli) {
@@ -4604,22 +4638,6 @@ function handleBenchError(ctx, status, code, error) {
4604
4638
  }
4605
4639
  outputError(ctx, code || "bench_failed", "Failed to load the leaderboard.", ExitCodes.ERROR, error ? `Server said: ${error}` : undefined);
4606
4640
  }
4607
- function colorsEnabled() {
4608
- return process.env["NO_COLOR"] === undefined && process.stderr.isTTY === true;
4609
- }
4610
- var STYLE = {
4611
- bold: "1",
4612
- dim: "2",
4613
- red: "31",
4614
- green: "32",
4615
- yellow: "33",
4616
- cyan: "36"
4617
- };
4618
- function paint(style, text) {
4619
- if (!colorsEnabled())
4620
- return text;
4621
- return `\x1B[${style}m${text}\x1B[0m`;
4622
- }
4623
4641
  function scoreStyle(percentage) {
4624
4642
  if (percentage >= 90)
4625
4643
  return STYLE.green;
@@ -5830,7 +5848,7 @@ function resolveManifestBaseline(ctx, projectRoot) {
5830
5848
  const { profile, manifest } = found;
5831
5849
  verbose(ctx, `using manifest in ${profile.manifestDir}/`);
5832
5850
  if (manifest.toolkit)
5833
- return { source: "sync", version: manifest.toolkit.version };
5851
+ return { source: "sync", version: manifest.toolkit.version, recordedAt: manifest.toolkit.recordedAt };
5834
5852
  const applied = newestAppliedAt(manifest);
5835
5853
  if (applied)
5836
5854
  return { source: "applied", date: applied };
@@ -5854,6 +5872,9 @@ function handleChangelogError(ctx, status, code, error) {
5854
5872
  if (code === "changelog_unsupported") {
5855
5873
  outputError(ctx, code, "The 10x-toolkit API does not support the toolkit changelog yet.", ExitCodes.ERROR, "The changelog endpoint has not been deployed yet. Try again later; '10x sync' still updates your files.");
5856
5874
  }
5875
+ if (status === 403) {
5876
+ outputError(ctx, "course_access_denied", "Your account has no active course access, so there is no toolkit changelog to show.", ExitCodes.FORBIDDEN);
5877
+ }
5857
5878
  if (status === 401) {
5858
5879
  outputError(ctx, "auth_required", "Your session is no longer valid.", ExitCodes.AUTH_REQUIRED, "Run '10x auth' to log in again.");
5859
5880
  }
@@ -5862,48 +5883,87 @@ function handleChangelogError(ctx, status, code, error) {
5862
5883
  }
5863
5884
  outputError(ctx, code || "changelog_failed", error || "Failed to load the toolkit changelog.", ExitCodes.ERROR);
5864
5885
  }
5865
- function describeBaseline(baseline) {
5866
- return baseline.version ?? baseline.date ?? "";
5867
- }
5868
- function describeSource(baseline) {
5886
+ var day = (iso) => (iso ?? "").slice(0, 10);
5887
+ var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
5888
+ function header(baseline, count) {
5889
+ const v3 = paint(STYLE.bold, baseline.version ?? "");
5869
5890
  switch (baseline.source) {
5891
+ case "sync":
5892
+ return `This project is on toolkit ${v3} ${paint(STYLE.dim, `(recorded by 10x sync on ${day(baseline.recordedAt)})`)}${count ? ` — ${plural(count, "newer release")}:` : ""}`;
5870
5893
  case "flag":
5871
- return "from --since";
5894
+ return baseline.version ? `Toolkit changes since ${v3}:` : `Toolkit changes since ${paint(STYLE.bold, day(baseline.date))}:`;
5895
+ case "applied":
5896
+ return `No toolkit version recorded in this project — showing changes since your last lesson apply (${day(baseline.date)}):`;
5897
+ case "none":
5898
+ return `No toolkit version recorded in this project — showing the latest ${plural(count, "release")}:`;
5899
+ }
5900
+ }
5901
+ function emptyMessage(baseline) {
5902
+ switch (baseline.source) {
5872
5903
  case "sync":
5873
- return "recorded by your last 10x sync";
5904
+ return "You're up to date — no toolkit changes since then.";
5905
+ case "flag":
5906
+ return `No toolkit changes since ${baseline.version ?? day(baseline.date)}.`;
5874
5907
  case "applied":
5875
- return "last lesson applied in this project";
5908
+ return `No toolkit changes since ${day(baseline.date)}.`;
5876
5909
  case "none":
5877
- return "no baseline recorded";
5910
+ return "No toolkit changes published yet.";
5878
5911
  }
5879
5912
  }
5913
+ var MAX_WIDTH = 100;
5914
+ var CATEGORIES = [
5915
+ ["skills", "Skills"],
5916
+ ["prompts", "Prompts"],
5917
+ ["rules", "Rules"],
5918
+ ["configTemplates", "Config templates"]
5919
+ ];
5920
+ var STATUS = {
5921
+ added: { mark: "+", style: STYLE.green, label: "new" },
5922
+ modified: { mark: "~", style: STYLE.yellow },
5923
+ removed: { mark: "-", style: STYLE.red, label: "removed" },
5924
+ renamed: { mark: ">", style: STYLE.cyan, label: "renamed" }
5925
+ };
5926
+ function renderEntry(entry, width) {
5927
+ const lines = [`${paint(STYLE.bold, paint(STYLE.cyan, entry.version))} ${paint(STYLE.dim, `— ${entry.releasedAt.slice(0, 10)}`)}`];
5928
+ for (const highlight of entry.highlights)
5929
+ lines.push(...wrapText(sanitize(highlight), width, " • "));
5930
+ for (const [category, label] of CATEGORIES) {
5931
+ const items = entry.artifacts[category];
5932
+ if (items.length === 0)
5933
+ continue;
5934
+ lines.push("", ` ${paint(STYLE.bold, label)}`);
5935
+ for (const item of items) {
5936
+ const status = STATUS[item.status];
5937
+ const suffix = status.label ? ` ${paint(STYLE.dim, `(${status.label})`)}` : "";
5938
+ lines.push(` ${paint(status.style, status.mark)} ${paint(STYLE.bold, sanitize(item.name))}${suffix}`);
5939
+ if (item.summary)
5940
+ lines.push(...wrapText(sanitize(item.summary), width, " "));
5941
+ }
5942
+ }
5943
+ return lines;
5944
+ }
5945
+ function pushSyncHint(lines, baseline) {
5946
+ if (baseline.source !== "none" && baseline.source !== "applied")
5947
+ return;
5948
+ lines.push("", paint(STYLE.dim, "Run 10x sync to record this project's toolkit version; 10x changelog will then show only what changed since."));
5949
+ }
5880
5950
  function render(ctx, baseline, entries) {
5881
5951
  if (ctx.json) {
5882
5952
  output(ctx, "", { baseline, newEntries: entries.length, entries });
5883
5953
  return;
5884
5954
  }
5885
- const lines = [];
5886
- if (baseline.source === "none") {
5887
- lines.push(`Latest toolkit changes (${describeSource(baseline)})`);
5888
- } else {
5889
- lines.push(`Toolkit changes since ${describeBaseline(baseline)} (${describeSource(baseline)})`);
5890
- }
5891
- lines.push("");
5892
5955
  if (entries.length === 0) {
5893
- lines.push(baseline.source === "none" ? "No toolkit changes published yet." : `No toolkit changes since ${describeBaseline(baseline)}.`);
5894
- } else {
5895
- entries.forEach((entry, index) => {
5896
- if (index > 0)
5897
- lines.push("");
5898
- lines.push(`${entry.version} — ${entry.releasedAt.slice(0, 10)}`);
5899
- lines.push("");
5900
- lines.push(entry.markdown.trimEnd());
5901
- });
5902
- }
5903
- if (baseline.source === "none") {
5904
- lines.push("");
5905
- lines.push("Run 10x sync to record your baseline.");
5956
+ const lines2 = baseline.source === "sync" ? [header(baseline, 0), emptyMessage(baseline)] : [emptyMessage(baseline)];
5957
+ pushSyncHint(lines2, baseline);
5958
+ output(ctx, lines2.join(`
5959
+ `), undefined);
5960
+ return;
5906
5961
  }
5962
+ const width = Math.min(process.stderr.columns || 80, MAX_WIDTH);
5963
+ const lines = [header(baseline, entries.length)];
5964
+ for (const entry of entries)
5965
+ lines.push("", ...renderEntry(entry, width));
5966
+ pushSyncHint(lines, baseline);
5907
5967
  output(ctx, lines.join(`
5908
5968
  `), undefined);
5909
5969
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@przeprogramowani/10x-cli",
3
- "version": "1.27.0",
3
+ "version": "1.29.0",
4
4
  "description": "Open-source CLI for 10xDevs course content",
5
5
  "repository": {
6
6
  "type": "git",