@hasna/skills 0.1.63 → 0.1.64

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 (38) hide show
  1. package/README.md +28 -6
  2. package/bin/index.js +1593 -228
  3. package/bin/mcp.js +290 -24
  4. package/bin/migrate.js +229 -33
  5. package/bin/server.js +774 -116
  6. package/bin/worker.js +558 -79
  7. package/dist/cli/commands/registry-reconcile.d.ts +2 -0
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +559 -251
  10. package/dist/lib/agent-sync.d.ts +26 -6
  11. package/dist/lib/auth-store.d.ts +37 -0
  12. package/dist/lib/config.d.ts +51 -0
  13. package/dist/lib/home-census.d.ts +4 -0
  14. package/dist/lib/home-migration.d.ts +8 -9
  15. package/dist/lib/native-storage.d.ts +1 -1
  16. package/dist/lib/portable-skills.d.ts +35 -6
  17. package/dist/lib/pull.d.ts +31 -0
  18. package/dist/lib/registry-reconcile.d.ts +114 -0
  19. package/dist/lib/registry.d.ts +7 -4
  20. package/dist/lib/remote-client.d.ts +118 -1
  21. package/dist/lib/remote-registry.d.ts +26 -0
  22. package/dist/lib/revision.d.ts +29 -0
  23. package/dist/sdk/index.js +1351 -353
  24. package/dist/server/app.d.ts +1 -1
  25. package/dist/server/config.d.ts +12 -2
  26. package/dist/server/rows.d.ts +2 -1
  27. package/dist/server/skills-api.d.ts +108 -6
  28. package/dist/server/sqlite-store.d.ts +20 -3
  29. package/dist/server/store.d.ts +31 -5
  30. package/dist/server/types.d.ts +98 -3
  31. package/dist/storage.js +7 -2
  32. package/migrations/postgres/0004_hosted_pins.sql +28 -0
  33. package/migrations/postgres/0005_revision_tombstone_registry.sql +37 -0
  34. package/migrations/postgres/0005_tag_projection.sql +36 -0
  35. package/migrations/sqlite/0004_hosted_pins.sql +21 -0
  36. package/migrations/sqlite/0005_revision_tombstone_registry.sql +18 -0
  37. package/migrations/sqlite/0005_tag_projection.sql +25 -0
  38. package/package.json +3 -2
package/bin/mcp.js CHANGED
@@ -6727,6 +6727,9 @@ function normalizeConfigValue(key, value) {
6727
6727
  return value.trim() ? value : undefined;
6728
6728
  return;
6729
6729
  }
6730
+ function isOwnerLayoutMigrated(appDir) {
6731
+ return existsSync(join(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD));
6732
+ }
6730
6733
  function getDataDir() {
6731
6734
  const override = process.env[DATA_DIR_ENV];
6732
6735
  if (override) {
@@ -6750,6 +6753,33 @@ function getDataDir() {
6750
6753
  }
6751
6754
  return newDir;
6752
6755
  }
6756
+ function getDataDirReadOnly() {
6757
+ const override = process.env[DATA_DIR_ENV];
6758
+ if (override)
6759
+ return override;
6760
+ return join(process.env["HOME"] || process.env["USERPROFILE"] || homedir(), ".hasna", "skills");
6761
+ }
6762
+ function getConfigPathReadOnly(scope) {
6763
+ if (scope === "global")
6764
+ return join(getDataDirReadOnly(), "config.json");
6765
+ return join(process.cwd(), "skills.config.json");
6766
+ }
6767
+ function loadConfigReadOnly() {
6768
+ const canonicalConfigPath = getConfigPathReadOnly("global");
6769
+ let globalConfig2;
6770
+ if (existsSync(canonicalConfigPath)) {
6771
+ globalConfig2 = readConfigFile(canonicalConfigPath);
6772
+ } else if (process.env[DATA_DIR_ENV] !== undefined) {
6773
+ globalConfig2 = {};
6774
+ } else {
6775
+ globalConfig2 = readConfigFile(legacyConfigFilePath());
6776
+ }
6777
+ const projectConfig = readConfigFile(getConfigPathReadOnly("project"));
6778
+ return { ...globalConfig2, ...projectConfig };
6779
+ }
6780
+ function legacyConfigFilePath() {
6781
+ return join(process.env["HOME"] || process.env["USERPROFILE"] || homedir(), ".skillsrc");
6782
+ }
6753
6783
  function getConfigPath(scope) {
6754
6784
  if (scope === "global") {
6755
6785
  return join(getDataDir(), "config.json");
@@ -6781,7 +6811,7 @@ function loadConfig() {
6781
6811
  const projectConfig = readConfigFile(getConfigPath("project"));
6782
6812
  return { ...globalConfig2, ...projectConfig };
6783
6813
  }
6784
- var ENUM_KEYS, STRING_KEYS, DATA_DIR_ENV = "HASNA_SKILLS_DIR", INSTALLED_SKILLS_DIRNAME = "installed";
6814
+ var ENUM_KEYS, STRING_KEYS, DATA_DIR_ENV = "HASNA_SKILLS_DIR", INSTALLED_SKILLS_DIRNAME = "installed", SKILLS_CACHE_DIRNAME = "skills", LAYOUT_MIGRATION_RECORD = ".layout-migration.json";
6785
6815
  var init_config = __esm(() => {
6786
6816
  init_retired_settings();
6787
6817
  ENUM_KEYS = {
@@ -6866,19 +6896,33 @@ var exports_auth_store = {};
6866
6896
  __export(exports_auth_store, {
6867
6897
  saveAuthConfig: () => saveAuthConfig,
6868
6898
  normalizeSkillsApiOrigin: () => normalizeSkillsApiOrigin,
6899
+ getAuthFilePathReadOnly: () => getAuthFilePathReadOnly,
6900
+ getAuthFilePath: () => getAuthFilePath,
6901
+ getAuthConfigReadOnly: () => getAuthConfigReadOnly,
6869
6902
  getAuthConfig: () => getAuthConfig,
6870
6903
  getApiUrl: () => getApiUrl,
6904
+ getApiKeyReadOnly: () => getApiKeyReadOnly,
6871
6905
  getApiKey: () => getApiKey,
6872
6906
  clearAuthConfig: () => clearAuthConfig
6873
6907
  });
6874
6908
  import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync6, unlinkSync } from "fs";
6875
- import { join as join11 } from "path";
6909
+ import { dirname as dirname5, join as join11 } from "path";
6876
6910
  import { homedir as homedir3 } from "os";
6911
+ function getAuthFilePath() {
6912
+ return join11(getDataDir(), "auth.json");
6913
+ }
6914
+ function getAuthFilePathReadOnly() {
6915
+ return join11(getDataDirReadOnly(), "auth.json");
6916
+ }
6917
+ function legacyAuthFilePath() {
6918
+ return join11(process.env["HOME"] || process.env["USERPROFILE"] || homedir3(), ".skills", "auth.json");
6919
+ }
6877
6920
  function getAuthConfig() {
6878
6921
  if (cachedConfig !== undefined)
6879
6922
  return cachedConfig;
6880
6923
  try {
6881
- const raw = readFileSync10(existsSync11(AUTH_FILE) ? AUTH_FILE : LEGACY_AUTH_FILE, "utf-8");
6924
+ const file = existsSync11(getAuthFilePath()) ? getAuthFilePath() : legacyAuthFilePath();
6925
+ const raw = readFileSync10(file, "utf-8");
6882
6926
  const config2 = JSON.parse(raw);
6883
6927
  if (!config2.apiKey) {
6884
6928
  cachedConfig = null;
@@ -6892,19 +6936,20 @@ function getAuthConfig() {
6892
6936
  }
6893
6937
  }
6894
6938
  function saveAuthConfig(config2) {
6895
- mkdirSync6(AUTH_DIR, { recursive: true, mode: 448 });
6896
- writeFileSync6(AUTH_FILE, JSON.stringify(config2, null, 2) + `
6939
+ const file = getAuthFilePath();
6940
+ mkdirSync6(dirname5(file), { recursive: true, mode: 448 });
6941
+ writeFileSync6(file, JSON.stringify(config2, null, 2) + `
6897
6942
  `, { mode: 384 });
6898
6943
  cachedConfig = config2;
6899
6944
  }
6900
6945
  function clearAuthConfig() {
6901
6946
  try {
6902
- unlinkSync(AUTH_FILE);
6947
+ unlinkSync(getAuthFilePath());
6903
6948
  } catch {}
6904
6949
  try {
6905
- unlinkSync(LEGACY_AUTH_FILE);
6950
+ unlinkSync(legacyAuthFilePath());
6906
6951
  } catch {}
6907
- cachedConfig = null;
6952
+ cachedConfig = undefined;
6908
6953
  }
6909
6954
  function getApiKey() {
6910
6955
  if (process.env.SKILLS_API_KEY)
@@ -6913,6 +6958,25 @@ function getApiKey() {
6913
6958
  return process.env.SKILL_API_KEY;
6914
6959
  return getAuthConfig()?.apiKey || null;
6915
6960
  }
6961
+ function getAuthConfigReadOnly() {
6962
+ try {
6963
+ const file = existsSync11(getAuthFilePathReadOnly()) ? getAuthFilePathReadOnly() : legacyAuthFilePath();
6964
+ const raw = readFileSync10(file, "utf-8");
6965
+ const config2 = JSON.parse(raw);
6966
+ if (!config2.apiKey)
6967
+ return null;
6968
+ return config2;
6969
+ } catch {
6970
+ return null;
6971
+ }
6972
+ }
6973
+ function getApiKeyReadOnly() {
6974
+ if (process.env.SKILLS_API_KEY)
6975
+ return process.env.SKILLS_API_KEY;
6976
+ if (process.env.SKILL_API_KEY)
6977
+ return process.env.SKILL_API_KEY;
6978
+ return getAuthConfigReadOnly()?.apiKey || null;
6979
+ }
6916
6980
  function normalizeSkillsApiOrigin(apiUrl) {
6917
6981
  const url = new URL(apiUrl);
6918
6982
  const pathname = url.pathname.replace(/\/+$/, "");
@@ -6928,12 +6992,10 @@ function normalizeSkillsApiOrigin(apiUrl) {
6928
6992
  function getApiUrl(action) {
6929
6993
  return normalizeSkillsApiOrigin(requireApiUrl(action));
6930
6994
  }
6931
- var AUTH_DIR, AUTH_FILE, LEGACY_AUTH_FILE, cachedConfig;
6995
+ var cachedConfig;
6932
6996
  var init_auth_store = __esm(() => {
6933
6997
  init_api_url();
6934
- AUTH_DIR = join11(homedir3(), ".hasna", "skills");
6935
- AUTH_FILE = join11(AUTH_DIR, "auth.json");
6936
- LEGACY_AUTH_FILE = join11(homedir3(), ".skills", "auth.json");
6998
+ init_config();
6937
6999
  });
6938
7000
 
6939
7001
  // src/lib/blog-article.ts
@@ -7069,8 +7131,11 @@ var init_blog_article = __esm(() => {
7069
7131
  // src/lib/remote-client.ts
7070
7132
  var exports_remote_client = {};
7071
7133
  __export(exports_remote_client, {
7134
+ createRemoteSkillsClientReadOnly: () => createRemoteSkillsClientReadOnly,
7072
7135
  createRemoteSkillsClient: () => createRemoteSkillsClient,
7073
- RemoteSkillsClient: () => RemoteSkillsClient
7136
+ RemoteSkillsClient: () => RemoteSkillsClient,
7137
+ RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
7138
+ RemoteRequestError: () => RemoteRequestError
7074
7139
  });
7075
7140
 
7076
7141
  class RemoteSkillsClient {
@@ -7090,6 +7155,20 @@ class RemoteSkillsClient {
7090
7155
  }
7091
7156
  });
7092
7157
  }
7158
+ async requestNewRoute(path, options, opts = {}) {
7159
+ const response = await this.request(path, options);
7160
+ const routePath = path.split("?")[0];
7161
+ if (response.status === 404 || response.status === 405) {
7162
+ if (response.status === 404 && opts.domainNotFoundCodes?.length && await responseBodyCarriesCode(response, opts.domainNotFoundCodes)) {
7163
+ return response;
7164
+ }
7165
+ throw new RemoteRouteUnsupportedError(routePath, response.status, this.apiUrl);
7166
+ }
7167
+ if (!response.ok) {
7168
+ throw new RemoteRequestError(routePath, response.status, response.statusText);
7169
+ }
7170
+ return response;
7171
+ }
7093
7172
  async listSkills() {
7094
7173
  const res = await this.request("/api/v1/skills");
7095
7174
  return res.json();
@@ -7106,6 +7185,14 @@ class RemoteSkillsClient {
7106
7185
  return null;
7107
7186
  return res.json();
7108
7187
  }
7188
+ async getSkillStatus(slug) {
7189
+ const res = await this.request(`/api/v1/skills/${encodeURIComponent(slug)}`, { method: "GET" });
7190
+ let body = null;
7191
+ try {
7192
+ body = await res.json();
7193
+ } catch {}
7194
+ return { status: res.status, body };
7195
+ }
7109
7196
  async submitRun(slug, input, args) {
7110
7197
  const res = await this.request(`/api/v1/runs/${slug}`, {
7111
7198
  method: "POST",
@@ -7139,15 +7226,18 @@ class RemoteSkillsClient {
7139
7226
  method: "GET"
7140
7227
  });
7141
7228
  }
7142
- async publishSkill(manifest, bundle) {
7229
+ async publishSkill(manifest, bundle, ifMatch) {
7143
7230
  const form = new FormData;
7144
7231
  form.set("manifest", JSON.stringify(manifest));
7145
7232
  if (bundle) {
7146
7233
  form.set("bundle", new Blob([bundle], { type: "application/gzip" }), `${String(manifest.slug ?? "skill")}.tar.gz`);
7147
7234
  }
7235
+ const headers = { Authorization: `Bearer ${this.apiKey}` };
7236
+ if (ifMatch)
7237
+ headers["If-Match"] = ifMatch;
7148
7238
  return fetch(`${this.apiUrl}/api/v1/skills`, {
7149
7239
  method: "POST",
7150
- headers: { Authorization: `Bearer ${this.apiKey}` },
7240
+ headers,
7151
7241
  body: form
7152
7242
  });
7153
7243
  }
@@ -7163,6 +7253,135 @@ class RemoteSkillsClient {
7163
7253
  return null;
7164
7254
  return response;
7165
7255
  }
7256
+ async listPins() {
7257
+ const response = await this.requestNewRoute("/api/v1/pins");
7258
+ return normalizePinList(await response.json());
7259
+ }
7260
+ async pin(slug, metadata) {
7261
+ const path = `/api/v1/pins/${encodeURIComponent(slug)}`;
7262
+ const response = await this.requestNewRoute(path, {
7263
+ method: "PUT",
7264
+ body: JSON.stringify({ ...metadata ? { metadata } : {} })
7265
+ });
7266
+ return normalizePin(await response.json());
7267
+ }
7268
+ async unpin(slug) {
7269
+ const path = `/api/v1/pins/${encodeURIComponent(slug)}`;
7270
+ const response = await this.requestNewRoute(path, { method: "DELETE" }, { domainNotFoundCodes: ["PIN_NOT_FOUND"] });
7271
+ return response.status !== 404;
7272
+ }
7273
+ async listTags() {
7274
+ const response = await this.requestNewRoute("/api/v1/tags");
7275
+ const payload = await response.json();
7276
+ if (!Array.isArray(payload)) {
7277
+ throw new Error("Remote tags payload did not match the expected contract (expected an array of tag names)");
7278
+ }
7279
+ for (const tag of payload) {
7280
+ if (typeof tag !== "string" || tag.trim().length === 0) {
7281
+ throw new Error("Remote tags payload did not match the expected contract (every element must be a non-empty tag name)");
7282
+ }
7283
+ }
7284
+ return payload;
7285
+ }
7286
+ async skillsByTag(tag) {
7287
+ const path = `/api/v1/tags/${encodeURIComponent(tag)}/skills`;
7288
+ const response = await this.requestNewRoute(path);
7289
+ return normalizeSkillSummaryList(await response.json());
7290
+ }
7291
+ async listUpdatedSince(since, options = {}) {
7292
+ const params = new URLSearchParams({ since });
7293
+ if (options.cursor)
7294
+ params.set("cursor", options.cursor);
7295
+ if (options.limit !== undefined)
7296
+ params.set("limit", String(options.limit));
7297
+ const response = await this.requestNewRoute(`/api/v1/skills/updated?${params.toString()}`);
7298
+ return normalizeUpdatedSincePage(await response.json());
7299
+ }
7300
+ }
7301
+ function requireOptionalString(record3, field) {
7302
+ if (record3[field] === undefined)
7303
+ return;
7304
+ if (typeof record3[field] !== "string") {
7305
+ throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
7306
+ }
7307
+ return record3[field];
7308
+ }
7309
+ function normalizePin(entry) {
7310
+ if (!entry || typeof entry !== "object") {
7311
+ throw new Error("Remote pin payload did not match the expected contract (expected an object)");
7312
+ }
7313
+ const record3 = entry;
7314
+ const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
7315
+ if (!slug) {
7316
+ throw new Error("Remote pin payload did not match the expected contract (missing slug)");
7317
+ }
7318
+ let metadata;
7319
+ if (record3.metadata !== undefined) {
7320
+ if (!record3.metadata || typeof record3.metadata !== "object" || Array.isArray(record3.metadata)) {
7321
+ throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
7322
+ }
7323
+ metadata = record3.metadata;
7324
+ }
7325
+ const pinnedAt = requireOptionalString(record3, "pinnedAt");
7326
+ return {
7327
+ slug,
7328
+ ...pinnedAt !== undefined ? { pinnedAt } : {},
7329
+ ...metadata ? { metadata } : {}
7330
+ };
7331
+ }
7332
+ function normalizePinList(payload) {
7333
+ if (!Array.isArray(payload)) {
7334
+ throw new Error("Remote pins payload did not match the expected contract (expected an array of pins)");
7335
+ }
7336
+ return payload.map(normalizePin);
7337
+ }
7338
+ function normalizeSkillSummary(entry) {
7339
+ if (!entry || typeof entry !== "object") {
7340
+ throw new Error("Remote skill payload did not match the expected contract (expected an object)");
7341
+ }
7342
+ const record3 = entry;
7343
+ const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
7344
+ if (!slug) {
7345
+ throw new Error("Remote skill payload did not match the expected contract (missing slug)");
7346
+ }
7347
+ return {
7348
+ slug,
7349
+ ...requireOptionalString(record3, "name") !== undefined ? { name: requireOptionalString(record3, "name") } : {},
7350
+ ...requireOptionalString(record3, "version") !== undefined ? { version: requireOptionalString(record3, "version") } : {},
7351
+ ...requireOptionalString(record3, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record3, "updatedAt") } : {}
7352
+ };
7353
+ }
7354
+ function normalizeSkillSummaryList(payload) {
7355
+ if (!Array.isArray(payload)) {
7356
+ throw new Error("Remote skills payload did not match the expected contract (expected an array of skills)");
7357
+ }
7358
+ return payload.map(normalizeSkillSummary);
7359
+ }
7360
+ async function responseBodyCarriesCode(response, codes) {
7361
+ try {
7362
+ const payload = await response.clone().json();
7363
+ if (!payload || typeof payload !== "object")
7364
+ return false;
7365
+ const code = payload.code;
7366
+ return typeof code === "string" && codes.includes(code);
7367
+ } catch {
7368
+ return false;
7369
+ }
7370
+ }
7371
+ function normalizeUpdatedSincePage(payload) {
7372
+ if (!payload || typeof payload !== "object") {
7373
+ throw new Error("Updated-since payload did not match the expected contract (expected an object)");
7374
+ }
7375
+ const record3 = payload;
7376
+ if (!Array.isArray(record3.skills)) {
7377
+ throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
7378
+ }
7379
+ const skills = record3.skills.map(normalizeSkillSummary);
7380
+ const nextCursor = record3.nextCursor === undefined || record3.nextCursor === null ? null : record3.nextCursor;
7381
+ if (nextCursor !== null && typeof nextCursor !== "string") {
7382
+ throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
7383
+ }
7384
+ return { skills, nextCursor };
7166
7385
  }
7167
7386
  function createRemoteSkillsClient() {
7168
7387
  const apiKey = getApiKey();
@@ -7170,8 +7389,42 @@ function createRemoteSkillsClient() {
7170
7389
  return null;
7171
7390
  return new RemoteSkillsClient(apiKey);
7172
7391
  }
7392
+ function createRemoteSkillsClientReadOnly() {
7393
+ const apiKey = getApiKeyReadOnly();
7394
+ if (!apiKey)
7395
+ return null;
7396
+ const apiUrl = resolveApiUrl(loadConfigReadOnly(), process.env);
7397
+ if (!apiUrl)
7398
+ throw new MissingApiUrlError("the cloud group's sync verb (--dry-run)");
7399
+ return new RemoteSkillsClient(apiKey, apiUrl);
7400
+ }
7401
+ var RemoteRouteUnsupportedError, RemoteRequestError;
7173
7402
  var init_remote_client = __esm(() => {
7174
7403
  init_auth_store();
7404
+ init_api_url();
7405
+ init_config();
7406
+ RemoteRouteUnsupportedError = class RemoteRouteUnsupportedError extends Error {
7407
+ path;
7408
+ status;
7409
+ instance;
7410
+ constructor(path, status, instance) {
7411
+ super(`The configured Skills instance does not support ${path} (HTTP ${status}). ` + `The instance at ${instance} predates this client feature \u2014 upgrade the server, or ` + `use a client version that matches it.`);
7412
+ this.path = path;
7413
+ this.status = status;
7414
+ this.instance = instance;
7415
+ this.name = "RemoteRouteUnsupportedError";
7416
+ }
7417
+ };
7418
+ RemoteRequestError = class RemoteRequestError extends Error {
7419
+ path;
7420
+ status;
7421
+ constructor(path, status, statusText) {
7422
+ super(`Remote request to ${path} failed: HTTP ${status}${statusText ? ` ${statusText}` : ""}`);
7423
+ this.path = path;
7424
+ this.status = status;
7425
+ this.name = "RemoteRequestError";
7426
+ }
7427
+ };
7175
7428
  });
7176
7429
 
7177
7430
  // ../../node_modules/.bun/content-type@1.0.5/node_modules/content-type/index.js
@@ -12690,7 +12943,7 @@ class StdioServerTransport {
12690
12943
  // package.json
12691
12944
  var package_default = {
12692
12945
  name: "@hasna/skills",
12693
- version: "0.1.63",
12946
+ version: "0.1.64",
12694
12947
  description: "Skills library for AI coding agents",
12695
12948
  type: "module",
12696
12949
  bin: {
@@ -12764,6 +13017,7 @@ var package_default = {
12764
13017
  license: "Apache-2.0",
12765
13018
  devDependencies: {
12766
13019
  "@types/bun": "latest",
13020
+ "@types/node": "25.2.3",
12767
13021
  "@types/react": "^18.2.0",
12768
13022
  "bun-types": "1.3.14",
12769
13023
  "react-devtools-core": "^7.0.1",
@@ -12772,7 +13026,7 @@ var package_default = {
12772
13026
  dependencies: {
12773
13027
  "@aws-sdk/client-ecs": "^3.1079.0",
12774
13028
  "@aws-sdk/client-s3": "^3.1079.0",
12775
- "@hasna/events": "0.1.7",
13029
+ "@hasna/events": "0.1.16",
12776
13030
  "@modelcontextprotocol/sdk": "^1.26.0",
12777
13031
  chalk: "^5.3.0",
12778
13032
  commander: "^12.1.0",
@@ -20800,7 +21054,7 @@ var DEVELOPMENT_TOOLS_SKILLS = [
20800
21054
  {
20801
21055
  name: "monitor",
20802
21056
  displayName: "Monitor",
20803
- description: "Operate the open-monitor MCP for machine health, processes, cron jobs, and cleanup workflows",
21057
+ description: "Operate the monitor MCP for machine health, processes, cron jobs, and cleanup workflows",
20804
21058
  category: "Development Tools",
20805
21059
  tags: ["monitoring", "mcp", "processes", "operations"]
20806
21060
  },
@@ -20846,6 +21100,14 @@ var DEVELOPMENT_TOOLS_SKILLS = [
20846
21100
  description: "Validate configuration files for syntax and schema compliance",
20847
21101
  category: "Development Tools",
20848
21102
  tags: ["config", "validation", "schema", "linting"]
21103
+ },
21104
+ {
21105
+ name: "session-inject-monitor",
21106
+ displayName: "Session Inject Monitor",
21107
+ description: "Set up a declarative monitor that injects a prompt into a live coding-agent session when a watched source (conversations, email, todos, knowledge, command output) has new content",
21108
+ category: "Development Tools",
21109
+ tags: ["monitor", "session", "injection", "automation", "wake"],
21110
+ kind: "instruction"
20849
21111
  }
20850
21112
  ];
20851
21113
 
@@ -21233,7 +21495,7 @@ var DESIGN_BRANDING_SKILLS = [
21233
21495
  displayName: "Site Analyze",
21234
21496
  description: "Analyze any website's design system \u2014 detects shadcn/ui, Tailwind, extracts colors, typography, and components via Playwright + Claude Vision.",
21235
21497
  category: "Design & Branding",
21236
- tags: ["design", "shadcn", "tailwind", "colors", "typography", "playwright", "analysis", "open-styles"]
21498
+ tags: ["design", "shadcn", "tailwind", "colors", "typography", "playwright", "analysis", "styles"]
21237
21499
  }
21238
21500
  ];
21239
21501
 
@@ -22566,6 +22828,9 @@ function getPortableSkillsRoot(options = {}) {
22566
22828
  if (options.rootDir)
22567
22829
  return options.rootDir;
22568
22830
  const appDir = options.homeDir ? join5(options.homeDir, ".hasna", "skills") : getDataDir();
22831
+ const cache = join5(appDir, SKILLS_CACHE_DIRNAME);
22832
+ if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
22833
+ return cache;
22569
22834
  const installed = join5(appDir, INSTALLED_SKILLS_DIRNAME);
22570
22835
  migrateLegacySkillLayout(appDir, installed);
22571
22836
  return installed;
@@ -23192,6 +23457,7 @@ import { fileURLToPath } from "url";
23192
23457
 
23193
23458
  // src/lib/home-migration.ts
23194
23459
  init_config();
23460
+ init_config();
23195
23461
 
23196
23462
  // src/lib/utils.ts
23197
23463
  function normalizeSkillName(name) {
@@ -25928,14 +26194,14 @@ function compactRunToolPayload(payload, detailHint) {
25928
26194
  // src/lib/feedback.ts
25929
26195
  init_config();
25930
26196
  import { existsSync as existsSync13, mkdirSync as mkdirSync7 } from "fs";
25931
- import { dirname as dirname5, join as join13 } from "path";
26197
+ import { dirname as dirname6, join as join13 } from "path";
25932
26198
  import { Database } from "bun:sqlite";
25933
26199
  function getFeedbackDbPath() {
25934
26200
  return join13(getDataDir(), "skills.db");
25935
26201
  }
25936
26202
  function getFeedbackDb() {
25937
26203
  const dbPath = getFeedbackDbPath();
25938
- const dir = dirname5(dbPath);
26204
+ const dir = dirname6(dbPath);
25939
26205
  if (!existsSync13(dir))
25940
26206
  mkdirSync7(dir, { recursive: true });
25941
26207
  const db = new Database(dbPath);
@@ -26382,7 +26648,7 @@ import {
26382
26648
  statSync as statSync8,
26383
26649
  writeFileSync as writeFileSync8
26384
26650
  } from "fs";
26385
- import { dirname as dirname6, join as join15, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
26651
+ import { dirname as dirname7, join as join15, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
26386
26652
  init_retired_settings();
26387
26653
  var SKILLS_STORAGE_TABLES = [
26388
26654
  "skills_sync_records",
@@ -26450,7 +26716,7 @@ function getSkillsNativeStorageStatus(options = {}) {
26450
26716
  const s3BucketEnv = readStorageEnv(env, "s3Bucket");
26451
26717
  const targetDir = options.targetDir ?? process.cwd();
26452
26718
  return {
26453
- package: "open-skills",
26719
+ package: "skills",
26454
26720
  tables: [...SKILLS_STORAGE_TABLES],
26455
26721
  env: {
26456
26722
  databaseUrl: SKILLS_NATIVE_STORAGE_ENV.databaseUrl,
@@ -26603,7 +26869,7 @@ function registerStorageTools(server) {
26603
26869
  const snapshot = exportSkillsLocalSnapshot(targetDir, { includeFileContents: false });
26604
26870
  const s3Plan = config2.s3Bucket ? planSkillsS3SnapshotUpload(snapshot, { prefix: config2.s3Prefix }) : [];
26605
26871
  return mcpJson({
26606
- package: "open-skills",
26872
+ package: "skills",
26607
26873
  noNetwork: true,
26608
26874
  databaseConfigured: Boolean(config2.databaseUrl),
26609
26875
  s3Configured: Boolean(config2.s3Bucket),