@hasna/skills 0.7.3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +161 -19
  2. package/bin/index.js +5321 -4850
  3. package/bin/mcp.js +7841 -7538
  4. package/bin/migrate.js +22 -60
  5. package/bin/server.js +73576 -20339
  6. package/bin/worker.js +65159 -19251
  7. package/dist/admin-contract.js +8 -8
  8. package/dist/cli/commands/recurring.d.ts +2 -0
  9. package/dist/index.js +2617 -2747
  10. package/dist/lib/canonical-json.d.ts +8 -0
  11. package/dist/lib/cli-mcp-parity.d.ts +1 -1
  12. package/dist/lib/config.d.ts +3 -23
  13. package/dist/lib/installer.d.ts +1 -1
  14. package/dist/lib/mcp-contracts.d.ts +1 -1
  15. package/dist/lib/portable-skills.d.ts +1 -1
  16. package/dist/lib/recurring-customer.d.ts +29 -0
  17. package/dist/lib/recurring-recovery.d.ts +82 -0
  18. package/dist/lib/recurring-surface.d.ts +252 -0
  19. package/dist/lib/registry-data/index.d.ts +1 -0
  20. package/dist/lib/registry-types.d.ts +2 -1
  21. package/dist/lib/registry.d.ts +2 -12
  22. package/dist/lib/release-dependencies.d.ts +3 -0
  23. package/dist/lib/remote-client.d.ts +13 -0
  24. package/dist/lib/remote-customer-operations.d.ts +1 -0
  25. package/dist/lib/remote-invitations.d.ts +1 -1
  26. package/dist/lib/remote-recurring-contract.d.ts +165 -0
  27. package/dist/lib/remote-recurring.d.ts +95 -0
  28. package/dist/lib/skill-aliases.d.ts +2 -3
  29. package/dist/mcp/mcp-test-client.d.ts +2 -2
  30. package/dist/mcp/remote-recurring-tools.d.ts +2 -0
  31. package/dist/sdk/executor.d.ts +5 -5
  32. package/dist/sdk/index.d.ts +4 -0
  33. package/dist/sdk/index.js +114864 -46432
  34. package/dist/sdk/registry.d.ts +3 -3
  35. package/dist/server/config.d.ts +1 -2
  36. package/dist/server/handlers.d.ts +5 -2
  37. package/dist/server/registry.d.ts +6 -3
  38. package/dist/server/skills-api.d.ts +6 -38
  39. package/dist/storage.js +5 -42
  40. package/package.json +3 -2
  41. package/dist/lib/registry-data/business-marketing.d.ts +0 -2
  42. package/dist/lib/registry-data/communication.d.ts +0 -2
  43. package/dist/lib/registry-data/content-generation.d.ts +0 -2
  44. package/dist/lib/registry-data/data-analysis.d.ts +0 -2
  45. package/dist/lib/registry-data/design-branding.d.ts +0 -2
  46. package/dist/lib/registry-data/development-tools.d.ts +0 -2
  47. package/dist/lib/registry-data/education-learning.d.ts +0 -2
  48. package/dist/lib/registry-data/event-management.d.ts +0 -2
  49. package/dist/lib/registry-data/finance-compliance.d.ts +0 -2
  50. package/dist/lib/registry-data/health-wellness.d.ts +0 -2
  51. package/dist/lib/registry-data/media-processing.d.ts +0 -2
  52. package/dist/lib/registry-data/productivity-organization.d.ts +0 -2
  53. package/dist/lib/registry-data/project-management.d.ts +0 -2
  54. package/dist/lib/registry-data/research-writing.d.ts +0 -2
  55. package/dist/lib/registry-data/science-academic.d.ts +0 -2
  56. package/dist/lib/registry-data/travel-lifestyle.d.ts +0 -2
  57. package/dist/lib/registry-data/web-browser.d.ts +0 -2
  58. package/dist/server/seed-bundled.d.ts +0 -20
package/bin/migrate.js CHANGED
@@ -2,12 +2,12 @@
2
2
  // @bun
3
3
 
4
4
  // src/server/migrate.ts
5
- import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync3 } from "fs";
6
- import { join as join6 } from "path";
5
+ import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
6
+ import { join as join5 } from "path";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@hasna/skills",
10
- version: "0.7.3",
10
+ version: "0.8.1",
11
11
  description: "Skills library for AI coding agents",
12
12
  type: "module",
13
13
  bin: {
@@ -92,7 +92,8 @@ var package_default = {
92
92
  author: "Hasna",
93
93
  license: "Apache-2.0",
94
94
  devDependencies: {
95
- "@hasna/contracts": "1.0.2",
95
+ "@hasna/contracts": "1.1.0",
96
+ "@hasna/secrets": "0.4.2",
96
97
  "@types/bun": "1.3.14",
97
98
  "@types/node": "25.2.3",
98
99
  "@types/react": "^18.2.0",
@@ -189,7 +190,6 @@ function resolveServerConfig(env = process.env) {
189
190
  port,
190
191
  databaseUrl: env[DATABASE_URL_ENV] || env.DATABASE_URL || undefined,
191
192
  bootstrapApiKey: env.HASNA_SKILLS_BOOTSTRAP_API_KEY || undefined,
192
- seedBundledCorpus: (env.HASNA_SKILLS_SEED_BUNDLED_CORPUS ?? "1") !== "0",
193
193
  artifactBucket: env.HASNA_SKILLS_S3_BUCKET || env.SKILLS_S3_BUCKET || undefined,
194
194
  artifactPrefix: normalizePrefix(env.HASNA_SKILLS_S3_PREFIX || env.SKILLS_S3_PREFIX || "skills/artifacts"),
195
195
  runArtifactPrefix: env.HASNA_SKILLS_S3_RUN_PREFIX ? normalizePrefix(env.HASNA_SKILLS_S3_RUN_PREFIX) : undefined,
@@ -216,12 +216,11 @@ function normalizePrefix(value) {
216
216
  }
217
217
 
218
218
  // src/server/database-url.ts
219
- import { isAbsolute, join as join3 } from "path";
219
+ import { isAbsolute, join as join2 } from "path";
220
220
  import { fileURLToPath } from "url";
221
221
 
222
222
  // src/lib/config.ts
223
- import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
224
- import { join as join2, dirname } from "path";
223
+ import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
225
224
 
226
225
  // src/lib/app-home.ts
227
226
  import { existsSync } from "fs";
@@ -281,12 +280,6 @@ function exactDataRoot() {
281
280
  }
282
281
  return;
283
282
  }
284
- function hasExactOverride(env = process.env) {
285
- return Boolean(env[DATA_DIR_ENV]?.trim()) || Boolean(env[HASNA_SKILLS_HOME_ENV]?.trim()) || Boolean(env[SKILLS_HOME_ENV]?.trim());
286
- }
287
- function hasOperatorOverride(env = process.env) {
288
- return hasExactOverride(env) || Boolean(env.HASNA_DATA_HOME?.trim());
289
- }
290
283
  function getDataRoot() {
291
284
  const exact = exactDataRoot();
292
285
  if (exact)
@@ -295,42 +288,11 @@ function getDataRoot() {
295
288
  return adoptResolverDataRoot(resolved) ? resolve(resolved) : resolve(legacyDataRoot());
296
289
  }
297
290
  // src/lib/config.ts
298
- function mergeDirectoryContents(sourceDir, targetDir) {
299
- if (!existsSync2(sourceDir))
300
- return;
301
- mkdirSync(targetDir, { recursive: true });
302
- for (const entry of readdirSync(sourceDir)) {
303
- const sourcePath = join2(sourceDir, entry);
304
- const targetPath = join2(targetDir, entry);
305
- try {
306
- const sourceStat = statSync(sourcePath);
307
- if (sourceStat.isDirectory()) {
308
- mergeDirectoryContents(sourcePath, targetPath);
309
- continue;
310
- }
311
- if (!existsSync2(targetPath))
312
- copyFileSync(sourcePath, targetPath);
313
- } catch {}
314
- }
315
- }
316
291
  function getDataDir() {
317
292
  const root = getDataRoot();
318
293
  try {
319
294
  mkdirSync(root, { recursive: true });
320
295
  } catch {}
321
- if (hasOperatorOverride())
322
- return root;
323
- const home = effectiveHome();
324
- const oldDir = join2(home, ".skills");
325
- const oldConfigFile = join2(home, ".skillsrc");
326
- try {
327
- mergeDirectoryContents(oldDir, root);
328
- } catch {}
329
- if (existsSync2(oldConfigFile) && !existsSync2(join2(root, "config.json"))) {
330
- try {
331
- copyFileSync(oldConfigFile, join2(root, "config.json"));
332
- } catch {}
333
- }
334
296
  return root;
335
297
  }
336
298
 
@@ -342,7 +304,7 @@ var SQLITE_SCHEMES = new Set(["sqlite", "sqlite3", "file"]);
342
304
  var MEMORY_SCHEMES = new Set(["memory"]);
343
305
  var SQLITE_EXTENSIONS = [".db", ".sqlite", ".sqlite3", ".db3"];
344
306
  function defaultSqlitePath() {
345
- return join3(getDataDir(), DEFAULT_SQLITE_FILENAME2);
307
+ return join2(getDataDir(), DEFAULT_SQLITE_FILENAME2);
346
308
  }
347
309
  function resolveDatabaseTarget(raw) {
348
310
  const value = raw?.trim();
@@ -369,7 +331,7 @@ function resolveDatabaseTarget(raw) {
369
331
  throw new Error(`unsupported database scheme "${scheme}:". Supported: postgres://, postgresql://, sqlite:, file:, ` + `an absolute or relative path to a .db/.sqlite file, ":memory:", or "memory:" (non-durable, tests only). ` + `Leave the setting empty to use the default SQLite database at ${defaultSqlitePath()}.`);
370
332
  }
371
333
  if (looksLikeSqlitePath(value)) {
372
- const path = isAbsolute(value) ? value : join3(process.cwd(), value);
334
+ const path = isAbsolute(value) ? value : join2(process.cwd(), value);
373
335
  return { kind: "sqlite", path, durable: true, label: `sqlite (${path})` };
374
336
  }
375
337
  throw new Error(`could not resolve a database backend from "${value}". Use postgres://\u2026, sqlite:\u2026, an absolute or ` + `relative path ending in ${SQLITE_EXTENSIONS.join("/")}, ":memory:", or leave it empty for the ` + `default SQLite database at ${defaultSqlitePath()}.`);
@@ -393,7 +355,7 @@ function sqlitePathFromUrl(value, scheme) {
393
355
  }
394
356
  return rest.slice(2).replace(/^\/\/+/, "/");
395
357
  }
396
- return isAbsolute(rest) ? rest : join3(process.cwd(), rest);
358
+ return isAbsolute(rest) ? rest : join2(process.cwd(), rest);
397
359
  }
398
360
  function looksLikeSqlitePath(value) {
399
361
  if (value.includes("/"))
@@ -403,17 +365,17 @@ function looksLikeSqlitePath(value) {
403
365
 
404
366
  // src/server/migrations-dir.ts
405
367
  import { existsSync as existsSync3 } from "fs";
406
- import { dirname as dirname2, join as join4 } from "path";
368
+ import { dirname, join as join3 } from "path";
407
369
  var MIGRATION_DIALECTS = ["postgres", "sqlite"];
408
370
  var MAX_WALK_UP = 6;
409
371
  function findMigrationsRoot(startDirs = defaultStartDirs()) {
410
372
  for (const start of startDirs) {
411
373
  let dir = start;
412
374
  for (let level = 0;level < MAX_WALK_UP; level += 1) {
413
- const candidate = join4(dir, "migrations");
414
- if (MIGRATION_DIALECTS.some((dialect) => existsSync3(join4(candidate, dialect))))
375
+ const candidate = join3(dir, "migrations");
376
+ if (MIGRATION_DIALECTS.some((dialect) => existsSync3(join3(candidate, dialect))))
415
377
  return candidate;
416
- const parent = dirname2(dir);
378
+ const parent = dirname(dir);
417
379
  if (parent === dir)
418
380
  break;
419
381
  dir = parent;
@@ -425,7 +387,7 @@ function resolveMigrationsDir(dialect, root = findMigrationsRoot()) {
425
387
  if (!root) {
426
388
  throw new Error(`could not locate the migrations directory for dialect "${dialect}". ` + `Expected a migrations/${dialect}/ folder alongside the package root.`);
427
389
  }
428
- const dir = join4(root, dialect);
390
+ const dir = join3(root, dialect);
429
391
  if (!existsSync3(dir)) {
430
392
  throw new Error(`migrations directory not found: ${dir}`);
431
393
  }
@@ -540,8 +502,8 @@ class SqliteSkillSelectionStore {
540
502
 
541
503
  // src/server/sqlite-store.ts
542
504
  import { randomUUID as randomUUID3 } from "crypto";
543
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
544
- import { dirname as dirname3, join as join5 } from "path";
505
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync } from "fs";
506
+ import { dirname as dirname2, join as join4 } from "path";
545
507
 
546
508
  // src/server/auth.ts
547
509
  import { createHash } from "crypto";
@@ -1101,7 +1063,7 @@ class SqliteSkillsStore {
1101
1063
  constructor(path = SQLITE_MEMORY_PATH, options = {}) {
1102
1064
  const inMemory = path === SQLITE_MEMORY_PATH;
1103
1065
  if (!inMemory)
1104
- mkdirSync2(dirname3(path), { recursive: true });
1066
+ mkdirSync2(dirname2(path), { recursive: true });
1105
1067
  this.db = openDatabase(path);
1106
1068
  this.db.exec(`PRAGMA busy_timeout = ${Math.max(0, options.busyTimeoutMs ?? 5000)}`);
1107
1069
  if (!inMemory)
@@ -1654,14 +1616,14 @@ function applySqliteMigrations(db, migrationsDir = resolveMigrationsDir("sqlite"
1654
1616
  version text PRIMARY KEY,
1655
1617
  applied_at text NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
1656
1618
  )`);
1657
- const files = readdirSync2(migrationsDir).filter((name) => name.endsWith(".sql")).sort();
1619
+ const files = readdirSync(migrationsDir).filter((name) => name.endsWith(".sql")).sort();
1658
1620
  if (files.length === 0) {
1659
1621
  throw new Error(`no .sql migrations found in ${migrationsDir}`);
1660
1622
  }
1661
1623
  const appliedNow = [];
1662
1624
  for (const file of files) {
1663
1625
  const version = file.replace(/\.sql$/, "");
1664
- const text = readFileSync2(join5(migrationsDir, file), "utf8");
1626
+ const text = readFileSync2(join4(migrationsDir, file), "utf8");
1665
1627
  const apply = db.transaction(() => {
1666
1628
  const already = db.query("SELECT 1 AS present FROM schema_migrations WHERE version = ? LIMIT 1").get(version);
1667
1629
  if (already)
@@ -1743,14 +1705,14 @@ async function runPostgresMigrations(databaseUrl, migrationsDir) {
1743
1705
  const appliedRows = await sql`SELECT version FROM schema_migrations`;
1744
1706
  const applied = new Set(appliedRows.map((row) => row.version));
1745
1707
  const appliedNow = [];
1746
- const files = readdirSync3(migrationsDir).filter((name) => name.endsWith(".sql")).sort();
1708
+ const files = readdirSync2(migrationsDir).filter((name) => name.endsWith(".sql")).sort();
1747
1709
  if (files.length === 0)
1748
1710
  throw new Error(`no .sql migrations found in ${migrationsDir}`);
1749
1711
  for (const file of files) {
1750
1712
  const version = file.replace(/\.sql$/, "");
1751
1713
  if (applied.has(version))
1752
1714
  continue;
1753
- const sqlText = readFileSync3(join6(migrationsDir, file), "utf8");
1715
+ const sqlText = readFileSync3(join5(migrationsDir, file), "utf8");
1754
1716
  await sql.unsafe("BEGIN");
1755
1717
  try {
1756
1718
  await sql.unsafe(sqlText);