@hasna/skills 0.10.77 → 0.10.78

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/bin/mcp.js CHANGED
@@ -23,7 +23,7 @@ var package_default;
23
23
  var init_package = __esm(() => {
24
24
  package_default = {
25
25
  name: "@hasna/skills",
26
- version: "0.10.77",
26
+ version: "0.10.78",
27
27
  description: "Skills library for AI coding agents",
28
28
  type: "module",
29
29
  bin: {
@@ -22805,7 +22805,8 @@ var init_codex_native_compatibility = __esm(() => {
22805
22805
  "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
22806
22806
  "codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
22807
22807
  "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
22808
- "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
22808
+ "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
22809
+ "codex-cli 0.163.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
22809
22810
  };
22810
22811
  SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version2) => supportsCodexNativeCapability(version2, "hooks")));
22811
22812
  });
@@ -39120,6 +39121,7 @@ function normalizeRemoteSkill(skill) {
39120
39121
  if (!name)
39121
39122
  throw new Error("Remote skill requires name or slug");
39122
39123
  return {
39124
+ ...skill.id !== undefined ? { id: skill.id } : {},
39123
39125
  name,
39124
39126
  displayName: skill.displayName || titleize(name),
39125
39127
  description: skill.description || "",
@@ -39224,11 +39226,12 @@ var init_remote_registry = __esm(() => {
39224
39226
  details: exports_external.array(exports_external.string()).optional()
39225
39227
  }).passthrough();
39226
39228
  remoteSkillSchema = exports_external.object({
39229
+ id: exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/).optional(),
39227
39230
  name: exports_external.string().min(1).optional(),
39228
39231
  slug: exports_external.string().min(1).optional(),
39229
39232
  displayName: exports_external.string().optional(),
39230
39233
  description: exports_external.string().optional(),
39231
- category: exports_external.string().optional(),
39234
+ category: exports_external.string().nullable().optional(),
39232
39235
  tags: exports_external.array(exports_external.string()).optional(),
39233
39236
  dependencies: exports_external.array(exports_external.string()).optional(),
39234
39237
  version: exports_external.string().optional(),
@@ -41617,6 +41620,24 @@ var init_remote_invitation_recovery = __esm(() => {
41617
41620
 
41618
41621
  // src/lib/remote-private-publications.ts
41619
41622
  import { createHash as createHash26 } from "crypto";
41623
+ function checkedPrivateSkillCreate(value) {
41624
+ const refuse5 = (field3, accepted) => {
41625
+ throw new PrivatePublicationError("INVALID_SKILL_CREATE_INPUT", `Invalid ${field3}. ${accepted}`);
41626
+ };
41627
+ if (!record10(value) || !Object.keys(value).every((key2) => ["slug", "displayName", "description", "visibility", "confirm"].includes(key2)))
41628
+ return refuse5("create input", "Provide only slug, displayName, description, visibility and confirm.");
41629
+ if (value.confirm !== true)
41630
+ throw new PrivatePublicationError("SKILL_CREATE_CONFIRM_REQUIRED", "Explicit confirmation is required to create this skill.");
41631
+ if (typeof value.slug !== "string" || value.slug.length > 128 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value.slug))
41632
+ return refuse5("slug", "Use lowercase kebab-case, at most 128 characters.");
41633
+ if (typeof value.displayName !== "string" || !value.displayName.trim() || value.displayName.length > 256 || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value.displayName))
41634
+ return refuse5("displayName", "Use nonempty text, at most 256 characters, without control characters.");
41635
+ if (value.description !== undefined && (typeof value.description !== "string" || Buffer.byteLength(value.description) > 8192 || /[\p{Cc}\p{Cs}]/u.test(value.description.replace(/[\r\n\t]/g, ""))))
41636
+ return refuse5("description", "Use text of at most 8192 UTF-8 bytes, without control characters other than tabs and newlines.");
41637
+ if (value.visibility !== undefined && value.visibility !== "private" && value.visibility !== "team")
41638
+ return refuse5("visibility", "Use private or team.");
41639
+ return Object.freeze({ slug: value.slug, displayName: value.displayName.trim(), description: value.description ?? "", visibility: value.visibility ?? "private" });
41640
+ }
41620
41641
  function checkedPublicationDeclaration(value) {
41621
41642
  if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash4(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
41622
41643
  return bad();
@@ -41636,11 +41657,11 @@ function checkedPublicationView(value, skillId, intentId, declaration) {
41636
41657
  return invalid4();
41637
41658
  return Object.freeze({ ...value });
41638
41659
  }
41639
- async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal) {
41660
+ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal, create = false) {
41640
41661
  const controller = new AbortController;
41641
41662
  let reader;
41642
41663
  let response;
41643
- const timeout2 = new PrivatePublicationError("PUBLICATION_UNCONFIRMED", "No confirmed publication result. Inspect the saved intent before retrying.", mutation);
41664
+ const timeout2 = new PrivatePublicationError(create ? "SKILL_CREATE_UNCONFIRMED" : "PUBLICATION_UNCONFIRMED", create ? "Skill creation could not be confirmed. Inspect the workspace for this slug before another create; do not retry automatically." : "No confirmed publication result. Inspect the saved intent before retrying.", mutation);
41644
41665
  let timer;
41645
41666
  let abort;
41646
41667
  try {
@@ -41680,11 +41701,19 @@ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal)
41680
41701
  return invalid4();
41681
41702
  }
41682
41703
  if (!response.ok) {
41683
- const code = record10(body) && typeof body.code === "string" && Object.hasOwn(failures2, body.code) ? body.code : null;
41684
- if (code && failures2[code][0] === response.status)
41685
- throw new PrivatePublicationError(code, failures2[code][1], mutation && code === "PUBLICATION_UNCERTAIN", response.status);
41704
+ const known = create ? skillCreateFailures : failures2;
41705
+ const code = record10(body) && typeof body.code === "string" && Object.hasOwn(known, body.code) ? body.code : null;
41706
+ if (code && known[code][0] === response.status)
41707
+ throw new PrivatePublicationError(code, known[code][1], mutation && code === "PUBLICATION_UNCERTAIN", response.status);
41708
+ if (create) {
41709
+ if (response.status >= 500)
41710
+ throw timeout2;
41711
+ throw new PrivatePublicationError("SKILL_CREATE_REQUEST_FAILED", "The server refused skill creation.", false, response.status);
41712
+ }
41686
41713
  throw new PrivatePublicationError("PUBLICATION_REQUEST_FAILED", "The publication request was refused. Inspect its status before retrying.", mutation && response.status >= 500, response.status);
41687
41714
  }
41715
+ if (create && response.status !== 201)
41716
+ return invalid4();
41688
41717
  return body;
41689
41718
  })(), new Promise((_, reject) => {
41690
41719
  abort = () => {
@@ -41731,6 +41760,15 @@ class RemotePrivatePublicationsClient {
41731
41760
  this.membershipId = checked.user.membershipId;
41732
41761
  Object.freeze(this);
41733
41762
  }
41763
+ async createSkill(input2, options = {}) {
41764
+ const body = checkedPrivateSkillCreate(input2);
41765
+ if (options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 15000))
41766
+ throw new PrivatePublicationError("INVALID_SKILL_CREATE_INPUT", "Invalid timeoutMs. Use an integer from 1 to 15000.");
41767
+ const value = await boundedJson(skillsApiRequestUrl(this.apiOrigin, "/api/v1/skills"), { method: "POST", body: JSON.stringify(body) }, this.#token, true, options.timeoutMs, options.signal, true);
41768
+ if (!record10(value) || !publicationUuid(value.id) || value.slug !== body.slug || value.displayName !== body.displayName || value.description !== body.description || value.visibility !== body.visibility || value.sourceType !== "private_hosted" || !record10(value.metadata) || value.metadata.ownerOrganizationId !== this.organizationId)
41769
+ throw new PrivatePublicationError("SKILL_CREATE_UNCONFIRMED", "Skill creation returned an unbound result. Inspect the workspace for this slug before another create; do not retry automatically.", true);
41770
+ return Object.freeze({ id: value.id, slug: body.slug, visibility: body.visibility, sourceType: "private_hosted", ownerOrganizationId: this.organizationId });
41771
+ }
41734
41772
  async getCapability(options = {}) {
41735
41773
  if (options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 15000))
41736
41774
  return bad();
@@ -41873,7 +41911,7 @@ class RemotePrivatePublicationsClient {
41873
41911
  }
41874
41912
  }
41875
41913
  }
41876
- var PRIVATE_PUBLICATION_MAX_BYTES, failures2, PrivatePublicationError, bad = () => {
41914
+ var PRIVATE_PUBLICATION_MAX_BYTES, skillCreateFailures, failures2, PrivatePublicationError, bad = () => {
41877
41915
  throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
41878
41916
  }, invalid4 = () => {
41879
41917
  throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
@@ -41883,6 +41921,16 @@ var init_remote_private_publications = __esm(() => {
41883
41921
  init_remote_workspace_selection();
41884
41922
  init_skill_contract();
41885
41923
  PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
41924
+ skillCreateFailures = {
41925
+ INVALID_SLUG: [400, "Provide a lowercase kebab-case skill slug."],
41926
+ INVALID_DISPLAY_NAME: [400, "Provide a nonempty skill display name."],
41927
+ INVALID_VISIBILITY: [400, "Use private or team visibility for source publication."],
41928
+ SESSION_EXPIRED: [401, "Sign in again before creating a private skill."],
41929
+ INTERACTIVE_SESSION_REQUIRED: [403, "An interactive account session is required to create a private skill."],
41930
+ ACCOUNT_UNAVAILABLE: [403, "The current account membership cannot create a private skill."],
41931
+ PLAN_REQUIRED: [403, "Private and team skills require the Pro plan."],
41932
+ SLUG_CONFLICT: [409, "This skill slug already exists. Inspect the existing skill before publishing."]
41933
+ };
41886
41934
  failures2 = {
41887
41935
  INVALID_REQUEST: [400, "The publication request is invalid."],
41888
41936
  SESSION_EXPIRED: [401, "Sign in again to manage this publication."],
@@ -48312,15 +48360,42 @@ var init_remote_invitation_tools = __esm(() => {
48312
48360
  });
48313
48361
 
48314
48362
  // src/lib/private-publication-customer.ts
48363
+ function preparePrivatePublicationSession(requested) {
48364
+ const selectors = [
48365
+ "HASNA_PROFILE",
48366
+ "HASNA_HOME",
48367
+ "HASNA_CONFIG_HOME",
48368
+ "HASNA_STATION",
48369
+ "HASNA_SKILLS_API_URL",
48370
+ "SKILLS_API_URL",
48371
+ "HASNA_SKILLS_API_KEY_OVERRIDE",
48372
+ "HASNA_SKILLS_API_KEY_REF",
48373
+ "HASNA_SKILLS_API_KEY",
48374
+ "SKILLS_API_KEY"
48375
+ ];
48376
+ const before = new Map(selectors.map((key2) => [key2, process.env[key2]]));
48377
+ const profile = selectedSkillsProfile(process.env), pending = prepareProfileWorkspace("Manage private publications");
48378
+ const captured = requested ? { ...requested } : undefined;
48379
+ const unchanged3 = () => {
48380
+ if (selectors.some((key2) => process.env[key2] !== before.get(key2)) || selectedSkillsProfile(process.env) !== profile)
48381
+ throw new PrivatePublicationError("PUBLICATION_TARGET_CHANGED", "The selected profile or server changed during verification. Start again with a stable target; no skill action was sent.");
48382
+ };
48383
+ return { origin: pending.origin, async verify(email5, code2) {
48384
+ unchanged3();
48385
+ const target = await pending.resolve();
48386
+ const context2 = captured ?? target.context;
48387
+ if (!context2 || !publicationUuid(context2.userId) || !publicationUuid(context2.membershipId) || target.context && (context2.userId !== target.context.userId || context2.membershipId !== target.context.membershipId))
48388
+ throw new PrivatePublicationError("PUBLICATION_CONTEXT_REQUIRED", "Provide the observed user and membership IDs, or select an enrolled workspace profile.");
48389
+ unchanged3();
48390
+ target.unchanged();
48391
+ const client = await new RemoteSkillsAuthClient(target.origin).openPrivatePublications(email5, code2, context2);
48392
+ unchanged3();
48393
+ target.unchanged();
48394
+ return client;
48395
+ } };
48396
+ }
48315
48397
  async function privatePublicationSession(email5, code2, requested) {
48316
- const target = await captureProfileWorkspace("Manage private publications");
48317
- const context2 = requested ?? target.context;
48318
- if (!context2 || !publicationUuid(context2.userId) || !publicationUuid(context2.membershipId) || target.context && (context2.userId !== target.context.userId || context2.membershipId !== target.context.membershipId))
48319
- throw new PrivatePublicationError("PUBLICATION_CONTEXT_REQUIRED", "Provide the observed user and membership IDs, or select an enrolled workspace profile.");
48320
- target.unchanged();
48321
- const client = await new RemoteSkillsAuthClient(target.origin).openPrivatePublications(email5, code2, context2);
48322
- target.unchanged();
48323
- return client;
48398
+ return preparePrivatePublicationSession(requested).verify(email5, code2);
48324
48399
  }
48325
48400
  function privatePublicationCustomerError(error61) {
48326
48401
  return error61 instanceof PrivatePublicationError ? { error: error61.message, code: error61.code, uncertain: error61.uncertain } : { error: "The publication action could not be confirmed. Preserve the recovery directory and inspect the same intent; credentials were not changed.", code: "PUBLICATION_ACTION_UNCONFIRMED", uncertain: true };
@@ -48329,6 +48404,7 @@ var init_private_publication_customer = __esm(() => {
48329
48404
  init_remote_auth();
48330
48405
  init_remote_private_publications();
48331
48406
  init_workspace_profile();
48407
+ init_instance_credentials();
48332
48408
  });
48333
48409
 
48334
48410
  // src/lib/private-publication-recovery.ts
package/bin/migrate.js CHANGED
@@ -25,7 +25,7 @@ import { join as join5 } from "path";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@hasna/skills",
28
- version: "0.10.77",
28
+ version: "0.10.78",
29
29
  description: "Skills library for AI coding agents",
30
30
  type: "module",
31
31
  bin: {
package/bin/server.js CHANGED
@@ -24140,7 +24140,7 @@ restoreLaunchCwd();
24140
24140
  // package.json
24141
24141
  var package_default = {
24142
24142
  name: "@hasna/skills",
24143
- version: "0.10.77",
24143
+ version: "0.10.78",
24144
24144
  description: "Skills library for AI coding agents",
24145
24145
  type: "module",
24146
24146
  bin: {
package/bin/worker.js CHANGED
@@ -24143,7 +24143,7 @@ import { randomUUID as randomUUID6 } from "crypto";
24143
24143
  // package.json
24144
24144
  var package_default = {
24145
24145
  name: "@hasna/skills",
24146
- version: "0.10.77",
24146
+ version: "0.10.78",
24147
24147
  description: "Skills library for AI coding agents",
24148
24148
  type: "module",
24149
24149
  bin: {
@@ -1,4 +1,5 @@
1
1
  export declare function handleMcp(options: {
2
2
  register?: string;
3
3
  json: boolean;
4
+ apiUrl?: string;
4
5
  }): Promise<void>;
package/dist/index.d.ts CHANGED
@@ -53,7 +53,7 @@ export { WorkspaceLeaveInputError, RemoteWorkspaceLeaveError, RemoteWorkspaceLea
53
53
  export type { RemoteWorkspaceInvitation, RemoteWorkspaceInvitationsPage, RemoteWorkspaceInvitationResult, RemoteWorkspaceInvitationAcceptance, ListRemoteWorkspaceInvitations, IssueRemoteWorkspaceInvitation, ResendRemoteWorkspaceInvitation, RevokeRemoteWorkspaceInvitation, AcceptRemoteWorkspaceInvitation, RemoteWorkspaceInvitationErrorCode } from "./lib/remote-invitations.js";
54
54
  export { WorkspaceInvitationInputError, RemoteWorkspaceInvitationError, RemoteWorkspaceInvitationUnconfirmedError, RemoteWorkspaceInvitationReadError } from "./lib/remote-invitations.js";
55
55
  export { InvitationEmailInputError, RemoteInvitationEmailError, RemoteInvitationEmailUnconfirmedError, type RequestInvitationEmailChallenge, type AcceptInvitationEmailChallenge, type RemoteInvitationEmailChallenge, type RemoteInvitationEmailAcceptance, type RemoteInvitationEmailErrorCode } from "./lib/remote-invitation-recovery.js";
56
- export { RemotePrivatePublicationsClient, PrivatePublicationError, PRIVATE_PUBLICATION_MAX_BYTES, type PrivatePublicationDeclaration, type PrivatePublicationView, type PrivatePublicationState, type PrivatePublishingCapability } from "./lib/remote-private-publications.js";
56
+ export { RemotePrivatePublicationsClient, PrivatePublicationError, PRIVATE_PUBLICATION_MAX_BYTES, type CreatePrivateSkillInput, type CreatedPrivateSkill, type PrivatePublicationDeclaration, type PrivatePublicationView, type PrivatePublicationState, type PrivatePublishingCapability } from "./lib/remote-private-publications.js";
57
57
  export { preparePrivatePublication, readPrivatePublicationRecovery, continuePrivatePublication, inspectPrivatePublication, type PrivatePublicationRecovery, type PrivatePublicationResult } from "./lib/private-publication-recovery.js";
58
58
  export { HttpProfileClient, createProfileClient, type ProfileClient, type AuthenticatedProfilePrincipal, type AuthenticatedProfileRole } from "./lib/profile-client.js";
59
59
  export type { SkillSelection, SkillProfile, ResolvedSkillSelection, ResolvedSkillProfile, StationSkillStateInput, StationSkillState } from "./types/skill-selection.js";
package/dist/index.js CHANGED
@@ -9405,7 +9405,8 @@ var MEASURED_CODEX_CAPABILITIES = {
9405
9405
  "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
9406
9406
  "codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
9407
9407
  "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
9408
- "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
9408
+ "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
9409
+ "codex-cli 0.163.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
9409
9410
  };
9410
9411
  function supportsCodexNativeCapability(version, capability) {
9411
9412
  return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
@@ -15446,7 +15447,7 @@ import { basename as basename13, dirname as dirname24, isAbsolute as isAbsolute2
15446
15447
  import { homedir as homedir5 } from "os";
15447
15448
  import { isAbsolute as isAbsolute25, join as join36, resolve as resolve24 } from "path";
15448
15449
  // package.json
15449
- var version = "0.10.77";
15450
+ var version = "0.10.78";
15450
15451
  var package_default = {
15451
15452
  name: "@hasna/skills",
15452
15453
  version,
@@ -44843,11 +44844,12 @@ var remoteAvailabilitySchema = exports_external.object({
44843
44844
  details: exports_external.array(exports_external.string()).optional()
44844
44845
  }).passthrough();
44845
44846
  var remoteSkillSchema = exports_external.object({
44847
+ id: exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/).optional(),
44846
44848
  name: exports_external.string().min(1).optional(),
44847
44849
  slug: exports_external.string().min(1).optional(),
44848
44850
  displayName: exports_external.string().optional(),
44849
44851
  description: exports_external.string().optional(),
44850
- category: exports_external.string().optional(),
44852
+ category: exports_external.string().nullable().optional(),
44851
44853
  tags: exports_external.array(exports_external.string()).optional(),
44852
44854
  dependencies: exports_external.array(exports_external.string()).optional(),
44853
44855
  version: exports_external.string().optional(),
@@ -44908,6 +44910,7 @@ function normalizeRemoteSkill(skill) {
44908
44910
  if (!name)
44909
44911
  throw new Error("Remote skill requires name or slug");
44910
44912
  return {
44913
+ ...skill.id !== undefined ? { id: skill.id } : {},
44911
44914
  name,
44912
44915
  displayName: skill.displayName || titleize(name),
44913
44916
  description: skill.description || "",
@@ -48385,6 +48388,16 @@ async function requestInvitationEmail(origin, action, input2) {
48385
48388
  // src/lib/remote-private-publications.ts
48386
48389
  import { createHash as createHash34 } from "crypto";
48387
48390
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
48391
+ var skillCreateFailures = {
48392
+ INVALID_SLUG: [400, "Provide a lowercase kebab-case skill slug."],
48393
+ INVALID_DISPLAY_NAME: [400, "Provide a nonempty skill display name."],
48394
+ INVALID_VISIBILITY: [400, "Use private or team visibility for source publication."],
48395
+ SESSION_EXPIRED: [401, "Sign in again before creating a private skill."],
48396
+ INTERACTIVE_SESSION_REQUIRED: [403, "An interactive account session is required to create a private skill."],
48397
+ ACCOUNT_UNAVAILABLE: [403, "The current account membership cannot create a private skill."],
48398
+ PLAN_REQUIRED: [403, "Private and team skills require the Pro plan."],
48399
+ SLUG_CONFLICT: [409, "This skill slug already exists. Inspect the existing skill before publishing."]
48400
+ };
48388
48401
  var failures2 = {
48389
48402
  INVALID_REQUEST: [400, "The publication request is invalid."],
48390
48403
  SESSION_EXPIRED: [401, "Sign in again to manage this publication."],
@@ -48431,6 +48444,24 @@ var record11 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
48431
48444
  var exact = (v, keys2) => record11(v) && Object.keys(v).sort().join(",") === keys2.sort().join(",");
48432
48445
  var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
48433
48446
  var publicationSha256 = (bytes) => createHash34("sha256").update(bytes).digest("hex");
48447
+ function checkedPrivateSkillCreate(value) {
48448
+ const refuse6 = (field3, accepted) => {
48449
+ throw new PrivatePublicationError("INVALID_SKILL_CREATE_INPUT", `Invalid ${field3}. ${accepted}`);
48450
+ };
48451
+ if (!record11(value) || !Object.keys(value).every((key2) => ["slug", "displayName", "description", "visibility", "confirm"].includes(key2)))
48452
+ return refuse6("create input", "Provide only slug, displayName, description, visibility and confirm.");
48453
+ if (value.confirm !== true)
48454
+ throw new PrivatePublicationError("SKILL_CREATE_CONFIRM_REQUIRED", "Explicit confirmation is required to create this skill.");
48455
+ if (typeof value.slug !== "string" || value.slug.length > 128 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value.slug))
48456
+ return refuse6("slug", "Use lowercase kebab-case, at most 128 characters.");
48457
+ if (typeof value.displayName !== "string" || !value.displayName.trim() || value.displayName.length > 256 || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value.displayName))
48458
+ return refuse6("displayName", "Use nonempty text, at most 256 characters, without control characters.");
48459
+ if (value.description !== undefined && (typeof value.description !== "string" || Buffer.byteLength(value.description) > 8192 || /[\p{Cc}\p{Cs}]/u.test(value.description.replace(/[\r\n\t]/g, ""))))
48460
+ return refuse6("description", "Use text of at most 8192 UTF-8 bytes, without control characters other than tabs and newlines.");
48461
+ if (value.visibility !== undefined && value.visibility !== "private" && value.visibility !== "team")
48462
+ return refuse6("visibility", "Use private or team.");
48463
+ return Object.freeze({ slug: value.slug, displayName: value.displayName.trim(), description: value.description ?? "", visibility: value.visibility ?? "private" });
48464
+ }
48434
48465
  function checkedPublicationDeclaration(value) {
48435
48466
  if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash4(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
48436
48467
  return bad();
@@ -48450,11 +48481,11 @@ function checkedPublicationView(value, skillId, intentId, declaration) {
48450
48481
  return invalid4();
48451
48482
  return Object.freeze({ ...value });
48452
48483
  }
48453
- async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal) {
48484
+ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal, create = false) {
48454
48485
  const controller = new AbortController;
48455
48486
  let reader;
48456
48487
  let response;
48457
- const timeout2 = new PrivatePublicationError("PUBLICATION_UNCONFIRMED", "No confirmed publication result. Inspect the saved intent before retrying.", mutation);
48488
+ const timeout2 = new PrivatePublicationError(create ? "SKILL_CREATE_UNCONFIRMED" : "PUBLICATION_UNCONFIRMED", create ? "Skill creation could not be confirmed. Inspect the workspace for this slug before another create; do not retry automatically." : "No confirmed publication result. Inspect the saved intent before retrying.", mutation);
48458
48489
  let timer;
48459
48490
  let abort;
48460
48491
  try {
@@ -48494,11 +48525,19 @@ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal)
48494
48525
  return invalid4();
48495
48526
  }
48496
48527
  if (!response.ok) {
48497
- const code = record11(body) && typeof body.code === "string" && Object.hasOwn(failures2, body.code) ? body.code : null;
48498
- if (code && failures2[code][0] === response.status)
48499
- throw new PrivatePublicationError(code, failures2[code][1], mutation && code === "PUBLICATION_UNCERTAIN", response.status);
48528
+ const known = create ? skillCreateFailures : failures2;
48529
+ const code = record11(body) && typeof body.code === "string" && Object.hasOwn(known, body.code) ? body.code : null;
48530
+ if (code && known[code][0] === response.status)
48531
+ throw new PrivatePublicationError(code, known[code][1], mutation && code === "PUBLICATION_UNCERTAIN", response.status);
48532
+ if (create) {
48533
+ if (response.status >= 500)
48534
+ throw timeout2;
48535
+ throw new PrivatePublicationError("SKILL_CREATE_REQUEST_FAILED", "The server refused skill creation.", false, response.status);
48536
+ }
48500
48537
  throw new PrivatePublicationError("PUBLICATION_REQUEST_FAILED", "The publication request was refused. Inspect its status before retrying.", mutation && response.status >= 500, response.status);
48501
48538
  }
48539
+ if (create && response.status !== 201)
48540
+ return invalid4();
48502
48541
  return body;
48503
48542
  })(), new Promise((_, reject) => {
48504
48543
  abort = () => {
@@ -48545,6 +48584,15 @@ class RemotePrivatePublicationsClient {
48545
48584
  this.membershipId = checked.user.membershipId;
48546
48585
  Object.freeze(this);
48547
48586
  }
48587
+ async createSkill(input2, options = {}) {
48588
+ const body = checkedPrivateSkillCreate(input2);
48589
+ if (options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 15000))
48590
+ throw new PrivatePublicationError("INVALID_SKILL_CREATE_INPUT", "Invalid timeoutMs. Use an integer from 1 to 15000.");
48591
+ const value = await boundedJson(skillsApiRequestUrl(this.apiOrigin, "/api/v1/skills"), { method: "POST", body: JSON.stringify(body) }, this.#token, true, options.timeoutMs, options.signal, true);
48592
+ if (!record11(value) || !publicationUuid(value.id) || value.slug !== body.slug || value.displayName !== body.displayName || value.description !== body.description || value.visibility !== body.visibility || value.sourceType !== "private_hosted" || !record11(value.metadata) || value.metadata.ownerOrganizationId !== this.organizationId)
48593
+ throw new PrivatePublicationError("SKILL_CREATE_UNCONFIRMED", "Skill creation returned an unbound result. Inspect the workspace for this slug before another create; do not retry automatically.", true);
48594
+ return Object.freeze({ id: value.id, slug: body.slug, visibility: body.visibility, sourceType: "private_hosted", ownerOrganizationId: this.organizationId });
48595
+ }
48548
48596
  async getCapability(options = {}) {
48549
48597
  if (options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 15000))
48550
48598
  return bad();
@@ -0,0 +1,2 @@
1
+ /** Safe selectors for a later MCP process. Credentials still resolve in that process. */
2
+ export declare function skillsMcpRegistrationEnvironment(env?: Record<string, string | undefined>, origin?: string): Record<string, string>;
@@ -1,7 +1,11 @@
1
1
  import { RemotePrivatePublicationsClient } from "./remote-private-publications.js";
2
2
  import type { RemoteWorkspaceContext } from "./remote-workspace-selection.js";
3
- /** Capture host profile before fresh verification. Neither the session nor a
4
- * signed upload capability is written to the host's credentials. */
3
+ /** Capture before code input, so a later profile/origin change cannot retarget consent. */
4
+ export declare function preparePrivatePublicationSession(requested?: RemoteWorkspaceContext): {
5
+ origin: string;
6
+ verify(email: string, code: string): Promise<RemotePrivatePublicationsClient>;
7
+ };
8
+ /** Neither the session nor a signed upload capability is saved to host credentials. */
5
9
  export declare function privatePublicationSession(email: string, code: string, requested?: RemoteWorkspaceContext): Promise<RemotePrivatePublicationsClient>;
6
10
  export declare function privatePublicationCustomerError(error: unknown): {
7
11
  error: string;
@@ -5,6 +5,8 @@ export type SkillKind = "executable" | "instruction";
5
5
  */
6
6
  export type SkillSource = "official" | "custom" | "remote" | "private" | "private-hosted" | "upstream" | "extension";
7
7
  export interface SkillMeta {
8
+ /** Immutable server UUID, when supplied and validated by a remote metadata read. */
9
+ id?: string;
8
10
  name: string;
9
11
  displayName: string;
10
12
  description: string;
@@ -1,5 +1,20 @@
1
1
  import { type RemoteWorkspaceSession } from "./remote-workspace-selection.js";
2
2
  export declare const PRIVATE_PUBLICATION_MAX_BYTES: number;
3
+ export interface CreatePrivateSkillInput {
4
+ slug: string;
5
+ displayName: string;
6
+ description?: string;
7
+ visibility?: "private" | "team";
8
+ confirm: true;
9
+ }
10
+ /** Only validated identifiers and enums are returned; server free text stays private. */
11
+ export interface CreatedPrivateSkill {
12
+ id: string;
13
+ slug: string;
14
+ visibility: "private" | "team";
15
+ sourceType: "private_hosted";
16
+ ownerOrganizationId: string;
17
+ }
3
18
  export type PrivatePublicationState = "awaiting_upload" | "queued" | "verifying" | "needs_attention" | "committed" | "rejected" | "cancelled" | "expired";
4
19
  export interface PrivatePublicationDeclaration {
5
20
  idempotencyKey: string;
@@ -45,6 +60,8 @@ export declare class PrivatePublicationError extends Error {
45
60
  }
46
61
  export declare const publicationUuid: (v: unknown) => v is string;
47
62
  export declare const publicationSha256: (bytes: Uint8Array | string) => string;
63
+ /** Validate and capture before verification or any await. Confirmation is local only. */
64
+ export declare function checkedPrivateSkillCreate(value: unknown): Omit<Required<CreatePrivateSkillInput>, "confirm">;
48
65
  export declare function checkedPublicationDeclaration(value: unknown): PrivatePublicationDeclaration;
49
66
  export declare function checkedPublicationView(value: unknown, skillId: string, intentId?: string, declaration?: PrivatePublicationDeclaration): PrivatePublicationView;
50
67
  /** Explicit hosted contract, unrelated to registry publishSkill and its content revisions.
@@ -56,6 +73,11 @@ export declare class RemotePrivatePublicationsClient {
56
73
  readonly userId: string;
57
74
  readonly membershipId: string;
58
75
  constructor(apiUrl: string, session: RemoteWorkspaceSession);
76
+ /** One session-only POST. An uncertain result must be inspected, never replayed. */
77
+ createSkill(input: CreatePrivateSkillInput, options?: {
78
+ timeoutMs?: number;
79
+ signal?: AbortSignal;
80
+ }): Promise<CreatedPrivateSkill>;
59
81
  getCapability(options?: {
60
82
  timeoutMs?: number;
61
83
  signal?: AbortSignal;
@@ -49,7 +49,7 @@ var __require = import.meta.require;
49
49
  // src/runtime-prerequisites.ts
50
50
  import { isAbsolute as isAbsolute7 } from "path";
51
51
  // package.json
52
- var version = "0.10.77";
52
+ var version = "0.10.78";
53
53
  var package_default = {
54
54
  name: "@hasna/skills",
55
55
  version,
@@ -1955,7 +1955,8 @@ var MEASURED_CODEX_CAPABILITIES = {
1955
1955
  "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
1956
1956
  "codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
1957
1957
  "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
1958
- "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
1958
+ "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
1959
+ "codex-cli 0.163.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
1959
1960
  };
1960
1961
  function supportsCodexNativeCapability(version2, capability) {
1961
1962
  return typeof version2 === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version2) && MEASURED_CODEX_CAPABILITIES[version2].includes(capability);
@@ -60,7 +60,7 @@ export { WorkspaceLeaveInputError, RemoteWorkspaceLeaveError, RemoteWorkspaceLea
60
60
  export type { RemoteWorkspaceInvitation, RemoteWorkspaceInvitationsPage, RemoteWorkspaceInvitationResult, RemoteWorkspaceInvitationAcceptance, ListRemoteWorkspaceInvitations, IssueRemoteWorkspaceInvitation, ResendRemoteWorkspaceInvitation, RevokeRemoteWorkspaceInvitation, AcceptRemoteWorkspaceInvitation, RemoteWorkspaceInvitationErrorCode } from "../lib/remote-invitations.js";
61
61
  export { WorkspaceInvitationInputError, RemoteWorkspaceInvitationError, RemoteWorkspaceInvitationUnconfirmedError, RemoteWorkspaceInvitationReadError } from "../lib/remote-invitations.js";
62
62
  export { InvitationEmailInputError, RemoteInvitationEmailError, RemoteInvitationEmailUnconfirmedError, type RequestInvitationEmailChallenge, type AcceptInvitationEmailChallenge, type RemoteInvitationEmailChallenge, type RemoteInvitationEmailAcceptance, type RemoteInvitationEmailErrorCode } from "../lib/remote-invitation-recovery.js";
63
- export { RemotePrivatePublicationsClient, PrivatePublicationError, PRIVATE_PUBLICATION_MAX_BYTES, type PrivatePublicationDeclaration, type PrivatePublicationView, type PrivatePublicationState, type PrivatePublishingCapability } from "../lib/remote-private-publications.js";
63
+ export { RemotePrivatePublicationsClient, PrivatePublicationError, PRIVATE_PUBLICATION_MAX_BYTES, type CreatePrivateSkillInput, type CreatedPrivateSkill, type PrivatePublicationDeclaration, type PrivatePublicationView, type PrivatePublicationState, type PrivatePublishingCapability } from "../lib/remote-private-publications.js";
64
64
  export { preparePrivatePublication, readPrivatePublicationRecovery, continuePrivatePublication, inspectPrivatePublication, type PrivatePublicationRecovery, type PrivatePublicationResult } from "../lib/private-publication-recovery.js";
65
65
  export { createSkillOperationClient, SkillOperationClientError, SKILL_OPERATION_LIMITS, type SkillOperationClient, type SkillOperationTransport, type SkillOperationRequest, type SkillOperationResult, type SkillOperationJson, type SkillOperationRefusal, type SkillOperationClientErrorCode } from "./operations.js";
66
66
  export type { RemoteCreditCheckoutOptions, RemoteCreditCheckout, RemoteCreditCheckoutErrorCode } from "../lib/remote-credit-checkout.js";
package/dist/sdk/index.js CHANGED
@@ -32371,7 +32371,7 @@ var init_event_sink = __esm(() => {
32371
32371
  init_dist();
32372
32372
  });
32373
32373
  // package.json
32374
- var version = "0.10.77";
32374
+ var version = "0.10.78";
32375
32375
  var package_default = {
32376
32376
  name: "@hasna/skills",
32377
32377
  version,
@@ -39103,7 +39103,8 @@ var MEASURED_CODEX_CAPABILITIES = {
39103
39103
  "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
39104
39104
  "codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
39105
39105
  "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
39106
- "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
39106
+ "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
39107
+ "codex-cli 0.163.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
39107
39108
  };
39108
39109
  function supportsCodexNativeCapability(version2, capability) {
39109
39110
  return typeof version2 === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version2) && MEASURED_CODEX_CAPABILITIES[version2].includes(capability);
@@ -98372,6 +98373,16 @@ async function requestInvitationEmail(origin, action, input2) {
98372
98373
  // src/lib/remote-private-publications.ts
98373
98374
  import { createHash as createHash45 } from "crypto";
98374
98375
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
98376
+ var skillCreateFailures = {
98377
+ INVALID_SLUG: [400, "Provide a lowercase kebab-case skill slug."],
98378
+ INVALID_DISPLAY_NAME: [400, "Provide a nonempty skill display name."],
98379
+ INVALID_VISIBILITY: [400, "Use private or team visibility for source publication."],
98380
+ SESSION_EXPIRED: [401, "Sign in again before creating a private skill."],
98381
+ INTERACTIVE_SESSION_REQUIRED: [403, "An interactive account session is required to create a private skill."],
98382
+ ACCOUNT_UNAVAILABLE: [403, "The current account membership cannot create a private skill."],
98383
+ PLAN_REQUIRED: [403, "Private and team skills require the Pro plan."],
98384
+ SLUG_CONFLICT: [409, "This skill slug already exists. Inspect the existing skill before publishing."]
98385
+ };
98375
98386
  var failures2 = {
98376
98387
  INVALID_REQUEST: [400, "The publication request is invalid."],
98377
98388
  SESSION_EXPIRED: [401, "Sign in again to manage this publication."],
@@ -98418,6 +98429,24 @@ var record12 = (v2) => !!v2 && typeof v2 === "object" && !Array.isArray(v2);
98418
98429
  var exact = (v2, keys4) => record12(v2) && Object.keys(v2).sort().join(",") === keys4.sort().join(",");
98419
98430
  var date5 = (v2) => typeof v2 === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v2) && Number.isFinite(Date.parse(v2));
98420
98431
  var publicationSha256 = (bytes) => createHash45("sha256").update(bytes).digest("hex");
98432
+ function checkedPrivateSkillCreate(value) {
98433
+ const refuse8 = (field3, accepted) => {
98434
+ throw new PrivatePublicationError("INVALID_SKILL_CREATE_INPUT", `Invalid ${field3}. ${accepted}`);
98435
+ };
98436
+ if (!record12(value) || !Object.keys(value).every((key2) => ["slug", "displayName", "description", "visibility", "confirm"].includes(key2)))
98437
+ return refuse8("create input", "Provide only slug, displayName, description, visibility and confirm.");
98438
+ if (value.confirm !== true)
98439
+ throw new PrivatePublicationError("SKILL_CREATE_CONFIRM_REQUIRED", "Explicit confirmation is required to create this skill.");
98440
+ if (typeof value.slug !== "string" || value.slug.length > 128 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value.slug))
98441
+ return refuse8("slug", "Use lowercase kebab-case, at most 128 characters.");
98442
+ if (typeof value.displayName !== "string" || !value.displayName.trim() || value.displayName.length > 256 || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value.displayName))
98443
+ return refuse8("displayName", "Use nonempty text, at most 256 characters, without control characters.");
98444
+ if (value.description !== undefined && (typeof value.description !== "string" || Buffer.byteLength(value.description) > 8192 || /[\p{Cc}\p{Cs}]/u.test(value.description.replace(/[\r\n\t]/g, ""))))
98445
+ return refuse8("description", "Use text of at most 8192 UTF-8 bytes, without control characters other than tabs and newlines.");
98446
+ if (value.visibility !== undefined && value.visibility !== "private" && value.visibility !== "team")
98447
+ return refuse8("visibility", "Use private or team.");
98448
+ return Object.freeze({ slug: value.slug, displayName: value.displayName.trim(), description: value.description ?? "", visibility: value.visibility ?? "private" });
98449
+ }
98421
98450
  function checkedPublicationDeclaration(value) {
98422
98451
  if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash5(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
98423
98452
  return bad();
@@ -98437,11 +98466,11 @@ function checkedPublicationView(value, skillId, intentId, declaration) {
98437
98466
  return invalid7();
98438
98467
  return Object.freeze({ ...value });
98439
98468
  }
98440
- async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal) {
98469
+ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal, create = false) {
98441
98470
  const controller = new AbortController;
98442
98471
  let reader;
98443
98472
  let response;
98444
- const timeout2 = new PrivatePublicationError("PUBLICATION_UNCONFIRMED", "No confirmed publication result. Inspect the saved intent before retrying.", mutation);
98473
+ const timeout2 = new PrivatePublicationError(create ? "SKILL_CREATE_UNCONFIRMED" : "PUBLICATION_UNCONFIRMED", create ? "Skill creation could not be confirmed. Inspect the workspace for this slug before another create; do not retry automatically." : "No confirmed publication result. Inspect the saved intent before retrying.", mutation);
98445
98474
  let timer;
98446
98475
  let abort;
98447
98476
  try {
@@ -98481,11 +98510,19 @@ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal)
98481
98510
  return invalid7();
98482
98511
  }
98483
98512
  if (!response.ok) {
98484
- const code = record12(body4) && typeof body4.code === "string" && Object.hasOwn(failures2, body4.code) ? body4.code : null;
98485
- if (code && failures2[code][0] === response.status)
98486
- throw new PrivatePublicationError(code, failures2[code][1], mutation && code === "PUBLICATION_UNCERTAIN", response.status);
98513
+ const known = create ? skillCreateFailures : failures2;
98514
+ const code = record12(body4) && typeof body4.code === "string" && Object.hasOwn(known, body4.code) ? body4.code : null;
98515
+ if (code && known[code][0] === response.status)
98516
+ throw new PrivatePublicationError(code, known[code][1], mutation && code === "PUBLICATION_UNCERTAIN", response.status);
98517
+ if (create) {
98518
+ if (response.status >= 500)
98519
+ throw timeout2;
98520
+ throw new PrivatePublicationError("SKILL_CREATE_REQUEST_FAILED", "The server refused skill creation.", false, response.status);
98521
+ }
98487
98522
  throw new PrivatePublicationError("PUBLICATION_REQUEST_FAILED", "The publication request was refused. Inspect its status before retrying.", mutation && response.status >= 500, response.status);
98488
98523
  }
98524
+ if (create && response.status !== 201)
98525
+ return invalid7();
98489
98526
  return body4;
98490
98527
  })(), new Promise((_, reject) => {
98491
98528
  abort = () => {
@@ -98532,6 +98569,15 @@ class RemotePrivatePublicationsClient {
98532
98569
  this.membershipId = checked.user.membershipId;
98533
98570
  Object.freeze(this);
98534
98571
  }
98572
+ async createSkill(input2, options = {}) {
98573
+ const body4 = checkedPrivateSkillCreate(input2);
98574
+ if (options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 15000))
98575
+ throw new PrivatePublicationError("INVALID_SKILL_CREATE_INPUT", "Invalid timeoutMs. Use an integer from 1 to 15000.");
98576
+ const value = await boundedJson(skillsApiRequestUrl(this.apiOrigin, "/api/v1/skills"), { method: "POST", body: JSON.stringify(body4) }, this.#token, true, options.timeoutMs, options.signal, true);
98577
+ if (!record12(value) || !publicationUuid(value.id) || value.slug !== body4.slug || value.displayName !== body4.displayName || value.description !== body4.description || value.visibility !== body4.visibility || value.sourceType !== "private_hosted" || !record12(value.metadata) || value.metadata.ownerOrganizationId !== this.organizationId)
98578
+ throw new PrivatePublicationError("SKILL_CREATE_UNCONFIRMED", "Skill creation returned an unbound result. Inspect the workspace for this slug before another create; do not retry automatically.", true);
98579
+ return Object.freeze({ id: value.id, slug: body4.slug, visibility: body4.visibility, sourceType: "private_hosted", ownerOrganizationId: this.organizationId });
98580
+ }
98535
98581
  async getCapability(options = {}) {
98536
98582
  if (options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 15000))
98537
98583
  return bad();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.10.77",
3
+ "version": "0.10.78",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {