@jarel/myskills 0.1.0-alpha.2 → 0.1.0-alpha.3

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 -2
  2. package/dist/index.js +175 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -49,7 +49,13 @@ myskills config reset api-url
49
49
  myskills config list
50
50
  myskills submit --path <file-directory-or-zip> [--api-url <url>] [--token <token>]
51
51
  myskills review submissions [--api-url <url>] [--token <token>]
52
- myskills review action <submission-id> --action <approve|publish> [--reason <text>]
52
+ myskills review action <submission-id> --action <approve|request-changes|reject|publish> [--reason <text>] [--api-url <url>] [--token <token>]
53
+ myskills submissions list [--api-url <url>] [--token <token>]
54
+ myskills submissions withdraw <submission-id> [--reason <text>] [--api-url <url>] [--token <token>]
55
+ myskills skills edit <skill-slug> [--title <text>] [--summary <text>] [--visibility <scope>] [--tag <tag>] [--reason <text>] [--api-url <url>] [--token <token>]
56
+ myskills skills archive|restore|delete <skill-slug> [--reason <text>] [--api-url <url>] [--token <token>]
57
+ myskills releases list <skill-slug> [--api-url <url>] [--token <token>]
58
+ myskills releases deprecate|unpublish|revoke|restore|delete <skill-slug>@<version> [--reason <text>] [--replacement <version>] [--api-url <url>] [--token <token>]
53
59
  myskills teams list|skills [--api-url <url>] [--token <token>]
54
60
  myskills teams create <team-name> [--name <team-name>] [--api-url <url>] [--token <token>]
55
61
  myskills teams invite <team-id> --email <email> [--api-url <url>] [--token <token>]
@@ -90,7 +96,9 @@ npm install -g @jarel/myskills@alpha
90
96
 
91
97
  `config get api-url`, `config set api-url <url>`, `config reset api-url`, and `config list` manage the saved API URL. `doctor` checks the CLI version, Node version, resolved API URL, `/health`, auth status, token-store backend, install-directory writability, and `/v1/capabilities`. If the CLI is pointed at the web app instead of the API, or a newer command is sent to an older server, command errors include concrete next steps and `--json` returns structured error codes.
92
98
 
93
- `submit` validates and scans locally before sending package directories as normalized text entries or `.zip` packages as base64 archive uploads for server-side extraction. `export` downloads server-authorized bundle content, verifies byte size and SHA-256 against release metadata, and writes normalized package paths under the requested output directory. `install` uses the same verified bundle path, writes into `--dir`, `MYSKILLS_INSTALL_DIR`, or the user data directory, and records local state in `.myskills-app/installed.json`; `update` preserves a rollback snapshot before replacing files, and `rollback` restores the most recent snapshot. `token create` prints the plaintext API token only once and does not overwrite the stored login session. Browser/device login, platform-specific install adapters, and archive creation are still planned.
99
+ `submit` validates and scans locally before sending package directories as normalized text entries or `.zip` packages as base64 archive uploads for server-side extraction. Authors can inspect their submitted versions with `submissions list` and withdraw unreviewed or changes-requested submissions with `submissions withdraw`. Maintainers can approve, request changes, reject, and publish submitted versions through `review action`.
100
+
101
+ Published artifacts remain immutable. `skills edit` changes mutable skill metadata only, while `releases deprecate`, `releases unpublish`, `releases revoke`, `releases restore`, and `releases delete` update server-owned lifecycle state for a specific version. Deprecated releases remain visible and installable; unpublished, revoked, archived, and deleted releases are hidden from install/export queries. `export` downloads server-authorized bundle content, verifies byte size and SHA-256 against release metadata, and writes normalized package paths under the requested output directory. `install` uses the same verified bundle path, writes into `--dir`, `MYSKILLS_INSTALL_DIR`, or the user data directory, and records local state in `.myskills-app/installed.json`; `update` preserves a rollback snapshot before replacing files, and `rollback` restores the most recent snapshot. `token create` prints the plaintext API token only once and does not overwrite the stored login session. Browser/device login, platform-specific install adapters, and archive creation are still planned.
94
102
 
95
103
  Common scopes:
96
104
 
package/dist/index.js CHANGED
@@ -16321,7 +16321,7 @@ function decodePackageText(buffer, relativePath) {
16321
16321
 
16322
16322
  // src/cli.ts
16323
16323
  var DEFAULT_API_URL = "http://localhost:3001";
16324
- var CLI_VERSION = "0.1.0-alpha.2";
16324
+ var CLI_VERSION = "0.1.0-alpha.3";
16325
16325
  var CLI_VISIBILITY_SCOPES = ["public", "authenticated", "organization", "team", "private", "explicit-users"];
16326
16326
  var LOGIN_AUTH_METHODS = ["password", "api-key"];
16327
16327
  async function runCli(argv, runtime) {
@@ -16363,6 +16363,12 @@ async function runCli(argv, runtime) {
16363
16363
  return await submitCommand(parsed, runtime);
16364
16364
  case "review":
16365
16365
  return await reviewCommand(parsed, runtime);
16366
+ case "submissions":
16367
+ return await submissionsCommand(parsed, runtime);
16368
+ case "skills":
16369
+ return await skillsCommand(parsed, runtime);
16370
+ case "releases":
16371
+ return await releasesCommand(parsed, runtime);
16366
16372
  case "teams":
16367
16373
  return await teamsCommand(parsed, runtime);
16368
16374
  case "sharing":
@@ -16774,11 +16780,11 @@ async function reviewCommand(parsed, runtime) {
16774
16780
  if (subcommand === "action") {
16775
16781
  const submissionId = parsed.args[1];
16776
16782
  if (!submissionId) {
16777
- throw new CliError("Usage: myskills review action <submission-id> --action <approve|publish>", 2);
16783
+ throw new CliError("Usage: myskills review action <submission-id> --action <approve|request-changes|reject|publish> [--reason <text>] [--api-url <url>] [--token <token>]", 2);
16778
16784
  }
16779
16785
  const action = stringOption(parsed, "action");
16780
- if (action !== "approve" && action !== "publish") {
16781
- throw new CliError("--action must be approve or publish.", 2);
16786
+ if (action !== "approve" && action !== "request-changes" && action !== "reject" && action !== "publish") {
16787
+ throw new CliError("--action must be approve, request-changes, reject, or publish.", 2);
16782
16788
  }
16783
16789
  const reason = optionalStringOption(parsed, "reason");
16784
16790
  const response = await apiPost(`/v1/review/submissions/${encodeURIComponent(submissionId)}/actions`, {
@@ -16793,7 +16799,144 @@ async function reviewCommand(parsed, runtime) {
16793
16799
  }
16794
16800
  return 0;
16795
16801
  }
16796
- throw new CliError("Usage: myskills review submissions | review action <submission-id> --action <approve|publish>", 2);
16802
+ throw new CliError("Usage: myskills review submissions | review action <submission-id> --action <approve|request-changes|reject|publish> [--reason <text>]", 2);
16803
+ }
16804
+ async function submissionsCommand(parsed, runtime) {
16805
+ const token = await requireToken(parsed, runtime);
16806
+ const subcommand = parsed.args[0];
16807
+ if (subcommand === "list" || subcommand === "mine") {
16808
+ const response = await apiGet("/v1/submissions/mine", parsed, runtime, token);
16809
+ if (parsed.options.json) {
16810
+ runtime.io.stdout(JSON.stringify(response, null, 2));
16811
+ } else {
16812
+ const submissions = arrayField(response, "submissions");
16813
+ if (submissions.length === 0) {
16814
+ runtime.io.stdout("No submissions.");
16815
+ } else {
16816
+ for (const value of submissions) {
16817
+ const submission = recordField(value, "submission");
16818
+ runtime.io.stdout([
16819
+ requiredRecordString(submission, "id", "Submission response is missing id."),
16820
+ `${requiredRecordString(submission, "slug", "Submission response is missing slug.")}@${requiredRecordString(submission, "version", "Submission response is missing version.")}`,
16821
+ optionalRecordString(submission, "reviewStatus") ?? "-",
16822
+ optionalRecordString(submission, "lifecycleStatus") ?? "-",
16823
+ optionalRecordString(submission, "securityStatus") ?? "-"
16824
+ ].join(" "));
16825
+ }
16826
+ }
16827
+ }
16828
+ return 0;
16829
+ }
16830
+ if (subcommand === "withdraw") {
16831
+ const submissionId = parsed.args[1];
16832
+ if (!submissionId) {
16833
+ throw new CliError("Usage: myskills submissions withdraw <submission-id> [--reason <text>] [--api-url <url>] [--token <token>]", 2);
16834
+ }
16835
+ const response = await apiPost(`/v1/submissions/${encodeURIComponent(submissionId)}/actions`, {
16836
+ action: "withdraw",
16837
+ ...reasonPayload(parsed)
16838
+ }, parsed, runtime, token);
16839
+ printNamedRecord(response, "submission", runtime.io, ["id", "slug", "version", "reviewStatus", "lifecycleStatus"]);
16840
+ return 0;
16841
+ }
16842
+ throw new CliError("Usage: myskills submissions list | submissions withdraw <submission-id> [--reason <text>] [--api-url <url>] [--token <token>]", 2);
16843
+ }
16844
+ async function skillsCommand(parsed, runtime) {
16845
+ const token = await requireToken(parsed, runtime);
16846
+ const subcommand = parsed.args[0];
16847
+ const slug = parsed.args[1];
16848
+ if (subcommand === "edit") {
16849
+ if (!slug) {
16850
+ throw new CliError("Usage: myskills skills edit <skill-slug> [--title <text>] [--summary <text>] [--visibility <scope>] [--tag <tag>] [--reason <text>] [--api-url <url>] [--token <token>]", 2);
16851
+ }
16852
+ const payload = {
16853
+ ...reasonPayload(parsed)
16854
+ };
16855
+ const title = optionalStringOption(parsed, "title");
16856
+ const summary = optionalStringOption(parsed, "summary");
16857
+ const visibility = optionalStringOption(parsed, "visibility");
16858
+ const tags = stringListOption(parsed, "tag");
16859
+ if (title !== void 0) {
16860
+ payload.title = title;
16861
+ }
16862
+ if (summary !== void 0) {
16863
+ payload.summary = summary;
16864
+ }
16865
+ if (visibility !== void 0) {
16866
+ if (!CLI_VISIBILITY_SCOPES.includes(visibility)) {
16867
+ throw new CliError(`--visibility must be one of: ${CLI_VISIBILITY_SCOPES.join(", ")}.`, 2);
16868
+ }
16869
+ payload.visibility = visibility;
16870
+ }
16871
+ if (tags.length > 0) {
16872
+ payload.tags = tags;
16873
+ }
16874
+ if (Object.keys(payload).every((key) => key === "reason")) {
16875
+ throw new CliError("At least one metadata option is required.", 2);
16876
+ }
16877
+ const response = await apiPut(`/v1/skills/${encodeURIComponent(parseInstallSlug(slug))}`, payload, parsed, runtime, token);
16878
+ printNamedRecord(response, "skill", runtime.io, ["slug", "title", "lifecycleStatus", "visibility"]);
16879
+ return 0;
16880
+ }
16881
+ if (subcommand === "archive" || subcommand === "restore" || subcommand === "delete") {
16882
+ if (!slug) {
16883
+ throw new CliError("Usage: myskills skills archive|restore|delete <skill-slug> [--reason <text>] [--api-url <url>] [--token <token>]", 2);
16884
+ }
16885
+ const response = await apiPost(`/v1/skills/${encodeURIComponent(parseInstallSlug(slug))}/actions`, {
16886
+ action: subcommand,
16887
+ ...reasonPayload(parsed)
16888
+ }, parsed, runtime, token);
16889
+ printNamedRecord(response, "skill", runtime.io, ["slug", "title", "lifecycleStatus", "visibility"]);
16890
+ return 0;
16891
+ }
16892
+ throw new CliError("Usage: myskills skills edit|archive|restore|delete <skill-slug> [--api-url <url>] [--token <token>]", 2);
16893
+ }
16894
+ async function releasesCommand(parsed, runtime) {
16895
+ const subcommand = parsed.args[0];
16896
+ if (subcommand === "list") {
16897
+ const slug = parsed.args[1];
16898
+ if (!slug) {
16899
+ throw new CliError("Usage: myskills releases list <skill-slug>", 2);
16900
+ }
16901
+ const token = await tokenOption(parsed, runtime) ?? void 0;
16902
+ const response = await apiGet(`/v1/skills/${encodeURIComponent(parseInstallSlug(slug))}/releases`, parsed, runtime, token);
16903
+ if (parsed.options.json) {
16904
+ runtime.io.stdout(JSON.stringify(response, null, 2));
16905
+ } else {
16906
+ const releases = arrayField(response, "releases");
16907
+ if (releases.length === 0) {
16908
+ runtime.io.stdout("No releases.");
16909
+ } else {
16910
+ for (const value of releases) {
16911
+ const release = recordField(value, "release");
16912
+ runtime.io.stdout([
16913
+ `${requiredRecordString(release, "slug", "Release response is missing slug.")}@${requiredRecordString(release, "version", "Release response is missing version.")}`,
16914
+ optionalRecordString(release, "lifecycleStatus") ?? "-",
16915
+ optionalRecordString(release, "reviewStatus") ?? "-",
16916
+ optionalRecordString(release, "securityStatus") ?? "-",
16917
+ `published=${optionalRecordString(release, "publishedAt") ?? "-"}`
16918
+ ].join(" "));
16919
+ }
16920
+ }
16921
+ }
16922
+ return 0;
16923
+ }
16924
+ if (["deprecate", "unpublish", "revoke", "restore", "delete"].includes(subcommand ?? "")) {
16925
+ const target = parsed.args[1];
16926
+ if (!target) {
16927
+ throw new CliError("Usage: myskills releases deprecate|unpublish|revoke|restore|delete <skill-slug>@<version> [--reason <text>] [--replacement <version>] [--api-url <url>] [--token <token>]", 2);
16928
+ }
16929
+ const { slug, version: version2 } = parseReleaseTarget(target);
16930
+ const token = await requireToken(parsed, runtime);
16931
+ const response = await apiPost(`/v1/skills/${encodeURIComponent(slug)}/releases/${encodeURIComponent(version2)}/actions`, {
16932
+ action: subcommand,
16933
+ ...reasonPayload(parsed),
16934
+ ...optionalStringOption(parsed, "replacement") ? { replacement: optionalStringOption(parsed, "replacement") } : {}
16935
+ }, parsed, runtime, token);
16936
+ printNamedRecord(response, "release", runtime.io, ["slug", "version", "lifecycleStatus", "reviewStatus", "securityStatus"]);
16937
+ return 0;
16938
+ }
16939
+ throw new CliError("Usage: myskills releases list <skill-slug> | releases deprecate|unpublish|revoke|restore|delete <skill-slug>@<version> [--api-url <url>] [--token <token>]", 2);
16797
16940
  }
16798
16941
  async function teamsCommand(parsed, runtime) {
16799
16942
  const token = await requireToken(parsed, runtime);
@@ -17508,6 +17651,26 @@ function recordField(input, label) {
17508
17651
  }
17509
17652
  return input;
17510
17653
  }
17654
+ function printNamedRecord(response, key, io, fields) {
17655
+ const record2 = recordField(response[key], key);
17656
+ io.stdout(fields.map((field) => optionalRecordString(record2, field) ?? "-").join(" "));
17657
+ }
17658
+ function reasonPayload(parsed) {
17659
+ const reason = optionalStringOption(parsed, "reason");
17660
+ return reason ? { reason } : {};
17661
+ }
17662
+ function parseReleaseTarget(target) {
17663
+ const separator = target.lastIndexOf("@");
17664
+ if (separator <= 0 || separator === target.length - 1) {
17665
+ throw new CliError("Release target must be <skill-slug>@<version>.", 2);
17666
+ }
17667
+ const slug = parseInstallSlug(target.slice(0, separator));
17668
+ const version2 = target.slice(separator + 1);
17669
+ if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version2)) {
17670
+ throw new CliError("Release version is invalid.", 2);
17671
+ }
17672
+ return { slug, version: version2 };
17673
+ }
17511
17674
  function arrayField(record2, key) {
17512
17675
  const value = record2[key];
17513
17676
  return Array.isArray(value) ? value : [];
@@ -18147,7 +18310,13 @@ function helpText() {
18147
18310
  " config list",
18148
18311
  " submit --path <file-directory-or-zip> [--api-url <url>] [--token <token>]",
18149
18312
  " review submissions [--api-url <url>] [--token <token>]",
18150
- " review action <submission-id> --action <approve|publish> [--reason <text>]",
18313
+ " review action <submission-id> --action <approve|request-changes|reject|publish> [--reason <text>] [--api-url <url>] [--token <token>]",
18314
+ " submissions list [--api-url <url>] [--token <token>]",
18315
+ " submissions withdraw <submission-id> [--reason <text>] [--api-url <url>] [--token <token>]",
18316
+ " skills edit <skill-slug> [--title <text>] [--summary <text>] [--visibility <scope>] [--tag <tag>] [--reason <text>] [--api-url <url>] [--token <token>]",
18317
+ " skills archive|restore|delete <skill-slug> [--reason <text>] [--api-url <url>] [--token <token>]",
18318
+ " releases list <skill-slug> [--api-url <url>] [--token <token>]",
18319
+ " releases deprecate|unpublish|revoke|restore|delete <skill-slug>@<version> [--reason <text>] [--replacement <version>] [--api-url <url>] [--token <token>]",
18151
18320
  " teams list|skills [--api-url <url>] [--token <token>]",
18152
18321
  " teams create <team-name> [--name <team-name>] [--api-url <url>] [--token <token>]",
18153
18322
  " teams invite <team-id> --email <email> [--api-url <url>] [--token <token>]",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jarel/myskills",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.3",
4
4
  "description": "Command-line client for publishing, discovering, installing, updating, and rolling back MySkills packages.",
5
5
  "license": "Apache-2.0",
6
6
  "private": false,