@cavuno/board 2.0.5 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +12 -3
  3. package/dist/{_spec-BKtVFcV9.d.mts → _spec-nkWAraS0.d.mts} +4 -18
  4. package/dist/{_spec-BKtVFcV9.d.ts → _spec-nkWAraS0.d.ts} +4 -18
  5. package/dist/bin.mjs +40 -57
  6. package/dist/{board-3Xj_Y-Tx.d.ts → board-NE98PlwN.d.ts} +2 -3
  7. package/dist/{board-pNN710Zc.d.mts → board-muu8BI8P.d.mts} +2 -3
  8. package/dist/doctor.js +21 -47
  9. package/dist/doctor.mjs +24 -50
  10. package/dist/filters.d.mts +2 -2
  11. package/dist/filters.d.ts +2 -2
  12. package/dist/format.d.mts +3 -3
  13. package/dist/format.d.ts +3 -3
  14. package/dist/index.d.mts +11 -27
  15. package/dist/index.d.ts +11 -27
  16. package/dist/index.js +3 -3
  17. package/dist/index.mjs +3 -3
  18. package/dist/{jobs-C4qRfCdP.d.ts → jobs-BiEvL9d7.d.ts} +1 -1
  19. package/dist/{jobs-k84C0v_8.d.mts → jobs-lVvKCBCU.d.mts} +1 -1
  20. package/dist/{salaries--J5mTylN.d.ts → salaries-BahBpxWT.d.ts} +2 -2
  21. package/dist/{salaries-C9YLO5NG.d.mts → salaries-ChSqDBAM.d.mts} +2 -2
  22. package/dist/{search-CE_qQiM7.d.mts → search-CCyFWPi3.d.mts} +1 -1
  23. package/dist/{search-CLRQ_El9.d.ts → search-CyRvVMUp.d.ts} +1 -1
  24. package/dist/seo.d.mts +4 -4
  25. package/dist/seo.d.ts +4 -4
  26. package/dist/server.d.mts +5 -5
  27. package/dist/server.d.ts +5 -5
  28. package/dist/sitemap.d.mts +5 -5
  29. package/dist/sitemap.d.ts +5 -5
  30. package/dist/skills.d.mts +15 -4
  31. package/dist/skills.d.ts +15 -4
  32. package/dist/skills.js +30 -7
  33. package/dist/skills.mjs +30 -8
  34. package/dist/suggest.d.mts +2 -2
  35. package/dist/suggest.d.ts +2 -2
  36. package/package.json +1 -11
  37. package/skills/cavuno-board-account/SKILL.md +58 -66
  38. package/skills/cavuno-board-api-client/SKILL.md +78 -0
  39. package/skills/cavuno-board-applications/SKILL.md +42 -52
  40. package/skills/cavuno-board-auth/SKILL.md +39 -60
  41. package/skills/cavuno-board-blog/SKILL.md +64 -49
  42. package/skills/cavuno-board-companies/SKILL.md +75 -53
  43. package/skills/cavuno-board-errors/SKILL.md +32 -45
  44. package/skills/cavuno-board-filters/SKILL.md +68 -75
  45. package/skills/cavuno-board-format/SKILL.md +65 -66
  46. package/skills/cavuno-board-i18n/SKILL.md +43 -68
  47. package/skills/cavuno-board-job-alerts/SKILL.md +72 -57
  48. package/skills/cavuno-board-jobs/SKILL.md +67 -55
  49. package/skills/cavuno-board-messaging/SKILL.md +57 -59
  50. package/skills/cavuno-board-paywall/SKILL.md +52 -54
  51. package/skills/cavuno-board-post-a-job/SKILL.md +122 -0
  52. package/skills/cavuno-board-salaries/SKILL.md +77 -50
  53. package/skills/cavuno-board-search-suggestions/SKILL.md +86 -0
  54. package/skills/cavuno-board-seo/BLOG.md +40 -0
  55. package/skills/cavuno-board-seo/JOB_AND_BREADCRUMBS.md +49 -0
  56. package/skills/cavuno-board-seo/LISTING.md +36 -0
  57. package/skills/cavuno-board-seo/SALARY.md +61 -0
  58. package/skills/cavuno-board-seo/SKILL.md +34 -161
  59. package/skills/cavuno-board-server-sessions/SKILL.md +147 -0
  60. package/skills/cavuno-board-setup/SKILL.md +46 -79
  61. package/skills/cavuno-board-sitemap/SKILL.md +78 -91
  62. package/skills/cavuno-board-smoke-test/SKILL.md +38 -69
  63. package/skills/manifest.json +43 -57
  64. package/dist/theme.d.mts +0 -115
  65. package/dist/theme.d.ts +0 -115
  66. package/dist/theme.js +0 -195
  67. package/dist/theme.mjs +0 -174
  68. package/skills/cavuno-board-client/SKILL.md +0 -89
  69. package/skills/cavuno-board-job-posting/SKILL.md +0 -116
  70. package/skills/cavuno-board-server/SKILL.md +0 -229
  71. package/skills/cavuno-board-suggest/SKILL.md +0 -119
  72. package/skills/cavuno-board-theme/SKILL.md +0 -57
  73. package/skills/flavors/tanstack-start/SKILL.md +0 -102
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
3
3
  This changelog records changes that affect Board API compatibility, exported
4
4
  types, runtime behavior, or supported integration patterns.
5
5
 
6
+ ## 3.1.0 — 2026-08-03
7
+
8
+ - Added direct skills.sh installation guidance and repository grouping for all
9
+ 22 Cavuno Board SDK skills.
10
+ - Linked every independently installable skill to the Cavuno SDK documentation.
11
+
12
+ ## 3.0.0 — 2026-08-03
13
+
14
+ - Removed runtime theme data from `board.context()` and removed the
15
+ `PublicBoardTheme` type and `@cavuno/board/theme` export. Applications now
16
+ own their presentation tokens and styling independently of Board API data.
17
+ - Retired the Cavuno theme skill and the bundled TanStack Start flavor. The
18
+ framework-neutral SDK skills were rewritten around concrete integration
19
+ tasks, with clearer names for API client setup, server sessions, search
20
+ suggestions, and public job posting.
21
+
22
+ ## 2.1.0 — 2026-08-01
23
+
24
+ - Removed private starter-repository links from the public SDK documentation.
25
+ - Repaired the audited public-repository export metadata and standalone
26
+ package lockfile used by trusted publishing.
27
+
6
28
  ## 2.0.5 — 2026-07-30
7
29
 
8
30
  - Added the optional `correction` object to board job catalog responses. It is
package/README.md CHANGED
@@ -19,6 +19,17 @@ other compatible coding agents. It teaches them how to wire a board correctly
19
19
  auth and session ownership, pagination, gating, error handling, and a
20
20
  runtime smoke test:
21
21
 
22
+ Install the latest skills directly from the public SDK repository:
23
+
24
+ ```bash
25
+ npx skills add wollemiahq/cavuno-sdk --skill cavuno-board-setup
26
+ # Or install the complete Cavuno skill set:
27
+ npx skills add wollemiahq/cavuno-sdk --skill '*'
28
+ ```
29
+
30
+ For skills matched exactly to an installed SDK version, copy them from the
31
+ package instead:
32
+
22
33
  ```bash
23
34
  npm install @cavuno/board # or: pnpm add / yarn add / bun add
24
35
  npx @cavuno/board setup # copies version-matched Agent Skills
@@ -38,7 +49,7 @@ const board = createBoardClient({
38
49
  board: process.env.PUBLIC_CAVUNO_BOARD!, // pk_… publishable key
39
50
  });
40
51
 
41
- const { name, theme, features } = await board.context();
52
+ const { name, language, features } = await board.context();
42
53
  const page = await board.jobs.list({ limit: 20 });
43
54
  const job = await board.jobs.retrieve('senior-chef');
44
55
 
@@ -94,8 +105,6 @@ SDK doesn't cover yet.
94
105
  - OpenAPI document: `GET https://api.cavuno.com/v1/openapi.json`
95
106
  - Source: <https://github.com/wollemiahq/cavuno-sdk>
96
107
  - Issues: <https://github.com/wollemiahq/cavuno-sdk/issues>
97
- - Conformance starter (TanStack Start on Cloudflare Workers):
98
- <https://github.com/wollemiahq/cavuno-board-starter>
99
108
  - Clone-and-own TanStack Start + shadcn/ui template:
100
109
  <https://github.com/wollemiahq/cavuno-tanstack-start-shadcn-job-board-template>
101
110
 
@@ -1674,22 +1674,6 @@ interface components {
1674
1674
  linkedInPartnerId: string | null;
1675
1675
  cookieConsentRequired: boolean;
1676
1676
  };
1677
- theme: {
1678
- mode: string;
1679
- schemeId: string;
1680
- typography: {
1681
- fontSans: string;
1682
- fontHeading?: string | null;
1683
- };
1684
- colors: {
1685
- light: {
1686
- [key: string]: unknown;
1687
- };
1688
- dark: {
1689
- [key: string]: unknown;
1690
- };
1691
- };
1692
- } | null;
1693
1677
  /** @description Operator-defined custom job-field definitions, in display order. Board-wide; the frontend uses these to render and localize each job's opaque `customFieldValues`. Empty when the board defines none. Display-only: not filterable or searchable in v1. */
1694
1678
  customFields: components["schemas"]["CustomFieldDefinition"][];
1695
1679
  /** @description Stored operator label overrides by config group (`jobCardLabels`, `navLabels`, `breadcrumbsLabels`, …): plain-text chrome copy to merge over the `@cavuno/board` `uiCopy(language)` catalog via `uiCopy(language, labels)`. Empty object when the board stores none. */
@@ -1842,12 +1826,12 @@ interface components {
1842
1826
  company: components["schemas"]["JobCompany"];
1843
1827
  /** @description Physical office locations associated with the job. */
1844
1828
  officeLocations: components["schemas"]["JobOfficeLocation"][];
1845
- /** @description Resolved job categories (slug + board display name): same shape as the card; names joined server-side. */
1829
+ /** @description Resolved job categories (slug + board display name): same shape as the card; names joined server-side. Guaranteed resolvable: every emitted slug resolves via `GET /v1/boards/:identifier/categories/:slug` — consumers never need to re-verify before linking. */
1846
1830
  categories: {
1847
1831
  slug: string;
1848
1832
  name: string;
1849
1833
  }[];
1850
- /** @description Resolved job skills (slug + board display name). */
1834
+ /** @description Resolved job skills (slug + board display name). Guaranteed resolvable: every emitted slug resolves via `GET /v1/boards/:identifier/skills/:slug` — consumers never need to re-verify before linking. */
1851
1835
  skills: {
1852
1836
  slug: string;
1853
1837
  name: string;
@@ -1947,10 +1931,12 @@ interface components {
1947
1931
  name: string;
1948
1932
  logoUrl: string | null;
1949
1933
  } | null;
1934
+ /** @description Job categories (slug + board display name). Guaranteed resolvable: every emitted slug resolves via `GET /v1/boards/:identifier/categories/:slug`. */
1950
1935
  categories: {
1951
1936
  slug: string;
1952
1937
  name: string;
1953
1938
  }[];
1939
+ /** @description Job skills (slug + board display name). Guaranteed resolvable: every emitted slug resolves via `GET /v1/boards/:identifier/skills/:slug`. */
1954
1940
  skills: {
1955
1941
  slug: string;
1956
1942
  name: string;
@@ -1674,22 +1674,6 @@ interface components {
1674
1674
  linkedInPartnerId: string | null;
1675
1675
  cookieConsentRequired: boolean;
1676
1676
  };
1677
- theme: {
1678
- mode: string;
1679
- schemeId: string;
1680
- typography: {
1681
- fontSans: string;
1682
- fontHeading?: string | null;
1683
- };
1684
- colors: {
1685
- light: {
1686
- [key: string]: unknown;
1687
- };
1688
- dark: {
1689
- [key: string]: unknown;
1690
- };
1691
- };
1692
- } | null;
1693
1677
  /** @description Operator-defined custom job-field definitions, in display order. Board-wide; the frontend uses these to render and localize each job's opaque `customFieldValues`. Empty when the board defines none. Display-only: not filterable or searchable in v1. */
1694
1678
  customFields: components["schemas"]["CustomFieldDefinition"][];
1695
1679
  /** @description Stored operator label overrides by config group (`jobCardLabels`, `navLabels`, `breadcrumbsLabels`, …): plain-text chrome copy to merge over the `@cavuno/board` `uiCopy(language)` catalog via `uiCopy(language, labels)`. Empty object when the board stores none. */
@@ -1842,12 +1826,12 @@ interface components {
1842
1826
  company: components["schemas"]["JobCompany"];
1843
1827
  /** @description Physical office locations associated with the job. */
1844
1828
  officeLocations: components["schemas"]["JobOfficeLocation"][];
1845
- /** @description Resolved job categories (slug + board display name): same shape as the card; names joined server-side. */
1829
+ /** @description Resolved job categories (slug + board display name): same shape as the card; names joined server-side. Guaranteed resolvable: every emitted slug resolves via `GET /v1/boards/:identifier/categories/:slug` — consumers never need to re-verify before linking. */
1846
1830
  categories: {
1847
1831
  slug: string;
1848
1832
  name: string;
1849
1833
  }[];
1850
- /** @description Resolved job skills (slug + board display name). */
1834
+ /** @description Resolved job skills (slug + board display name). Guaranteed resolvable: every emitted slug resolves via `GET /v1/boards/:identifier/skills/:slug` — consumers never need to re-verify before linking. */
1851
1835
  skills: {
1852
1836
  slug: string;
1853
1837
  name: string;
@@ -1947,10 +1931,12 @@ interface components {
1947
1931
  name: string;
1948
1932
  logoUrl: string | null;
1949
1933
  } | null;
1934
+ /** @description Job categories (slug + board display name). Guaranteed resolvable: every emitted slug resolves via `GET /v1/boards/:identifier/categories/:slug`. */
1950
1935
  categories: {
1951
1936
  slug: string;
1952
1937
  name: string;
1953
1938
  }[];
1939
+ /** @description Job skills (slug + board display name). Guaranteed resolvable: every emitted slug resolves via `GET /v1/boards/:identifier/skills/:slug`. */
1954
1940
  skills: {
1955
1941
  slug: string;
1956
1942
  name: string;
package/dist/bin.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  var DEFAULT_CAVUNO_API_URL = "https://api.cavuno.com";
5
5
 
6
6
  // src/skills.ts
7
- import { readFileSync } from "fs";
7
+ import { readdirSync, readFileSync } from "fs";
8
8
  import { dirname, resolve } from "path";
9
9
  import { fileURLToPath } from "url";
10
10
  function packageRoot() {
@@ -21,13 +21,21 @@ function loadSkillCorpus(baseDir) {
21
21
  const manifest = loadSkillManifest(baseDir);
22
22
  return {
23
23
  version: manifest.version,
24
- skills: manifest.skills.map((skill) => ({
25
- ...skill,
26
- content: readFileSync(
27
- resolveFromPackageRoot(skill.path, baseDir),
28
- "utf8"
29
- )
30
- }))
24
+ skills: manifest.skills.map((skill) => {
25
+ const skillPath = resolveFromPackageRoot(skill.path, baseDir);
26
+ const skillDir = dirname(skillPath);
27
+ const references = readdirSync(skillDir, { withFileTypes: true }).filter(
28
+ (entry) => entry.isFile() && entry.name !== "SKILL.md" && entry.name.endsWith(".md")
29
+ ).map((entry) => ({
30
+ path: entry.name,
31
+ content: readFileSync(resolve(skillDir, entry.name), "utf8")
32
+ })).sort((a, b) => a.path.localeCompare(b.path));
33
+ return {
34
+ ...skill,
35
+ content: readFileSync(skillPath, "utf8"),
36
+ references
37
+ };
38
+ })
31
39
  };
32
40
  }
33
41
 
@@ -105,7 +113,7 @@ function summarize(results) {
105
113
  }
106
114
 
107
115
  // src/doctor/cookie-conformance.ts
108
- import { existsSync, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
116
+ import { existsSync, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync } from "fs";
109
117
  import { join, relative } from "path";
110
118
  var COOKIE = record("static.cookie-codec", 1);
111
119
  var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|astro|svelte|vue)$/;
@@ -211,7 +219,7 @@ function hasGeneratedBanner(text) {
211
219
  function walkSourceFiles(dir, out) {
212
220
  let entries;
213
221
  try {
214
- entries = readdirSync(dir);
222
+ entries = readdirSync2(dir);
215
223
  } catch {
216
224
  return;
217
225
  }
@@ -394,42 +402,9 @@ async function runReadProbes(fetchImpl, frontendUrl) {
394
402
  ];
395
403
  }
396
404
 
397
- // src/doctor/theme.ts
398
- import { createHash } from "crypto";
405
+ // src/doctor/run.ts
399
406
  import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
400
407
  import { join as join2 } from "path";
401
- var THEME = record("static.theme", 1);
402
- function checkThemeFreshness(projectRoot) {
403
- const tokensPath = join2(projectRoot, "src/tokens.css");
404
- if (!existsSync2(tokensPath)) {
405
- return [
406
- THEME(
407
- "skip",
408
- "no src/tokens.css \u2014 local theme freshness check not applicable"
409
- )
410
- ];
411
- }
412
- const tokensHash = createHash("sha256").update(readFileSync3(tokensPath, "utf8"), "utf8").digest("hex");
413
- const resolvedPath = join2(projectRoot, "src/theme/resolved.ts");
414
- const resolvedHash = existsSync2(resolvedPath) ? readFileSync3(resolvedPath, "utf8").match(
415
- /tokensHash = '([0-9a-f]{64})'/
416
- )?.[1] ?? null : null;
417
- if (resolvedHash !== tokensHash) {
418
- return [
419
- THEME(
420
- "fail",
421
- `src/theme/resolved.ts is ${resolvedHash ? "stale" : "missing"} \u2014 run \`npm run gen:theme\` (OG images render from it)`
422
- )
423
- ];
424
- }
425
- return [
426
- THEME("pass", `tokens.css \u21C4 resolved module (${tokensHash.slice(0, 12)}\u2026)`)
427
- ];
428
- }
429
-
430
- // src/doctor/run.ts
431
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
432
- import { join as join3 } from "path";
433
408
  var STATIC_API = record("static.api", 1);
434
409
  var STATIC_BOARD = record("static.board", 1);
435
410
  var STATIC_SKILLS = record("static.skills", 1);
@@ -485,8 +460,8 @@ var SKILL_ROOTS = [
485
460
  ".cursor/skills"
486
461
  ];
487
462
  function checkSkillsFreshness(projectRoot) {
488
- const roots = SKILL_ROOTS.map((root) => join3(projectRoot, root)).filter(
489
- (root) => existsSync3(root)
463
+ const roots = SKILL_ROOTS.map((root) => join2(projectRoot, root)).filter(
464
+ (root) => existsSync2(root)
490
465
  );
491
466
  if (roots.length === 0) {
492
467
  return STATIC_SKILLS(
@@ -499,10 +474,10 @@ function checkSkillsFreshness(projectRoot) {
499
474
  const seen = /* @__PURE__ */ new Set();
500
475
  for (const root of roots) {
501
476
  for (const skill of corpus.skills) {
502
- const copied = join3(root, skill.name, "SKILL.md");
503
- if (!existsSync3(copied)) continue;
477
+ const copied = join2(root, skill.name, "SKILL.md");
478
+ if (!existsSync2(copied)) continue;
504
479
  seen.add(skill.name);
505
- if (readFileSync4(copied, "utf8") !== skill.content) {
480
+ if (readFileSync3(copied, "utf8") !== skill.content) {
506
481
  stale.add(skill.name);
507
482
  }
508
483
  }
@@ -546,7 +521,6 @@ async function runDoctor(options) {
546
521
  results.push(STATIC_BOARD("skip", "env checks failed \u2014 fix them first"));
547
522
  }
548
523
  results.push(checkSkillsFreshness(options.projectRoot ?? process.cwd()));
549
- results.push(...checkThemeFreshness(options.projectRoot ?? process.cwd()));
550
524
  results.push(
551
525
  ...checkCookieCodecConformance(options.projectRoot ?? process.cwd())
552
526
  );
@@ -557,12 +531,20 @@ async function runDoctor(options) {
557
531
  }
558
532
 
559
533
  // src/setup/run.ts
560
- import { cpSync, existsSync as existsSync4, mkdirSync, readFileSync as readFileSync5 } from "fs";
534
+ import { cpSync, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, rmSync } from "fs";
561
535
  import { dirname as dirname2, resolve as resolve2 } from "path";
536
+ var RETIRED_SKILL_NAMES = [
537
+ "cavuno-board-theme",
538
+ "cavuno-board-tanstack-start",
539
+ "cavuno-board-client",
540
+ "cavuno-board-server",
541
+ "cavuno-board-suggest",
542
+ "cavuno-board-job-posting"
543
+ ];
562
544
  function detectFramework(cwd) {
563
545
  const pkgPath = resolve2(cwd, "package.json");
564
- if (!existsSync4(pkgPath)) return null;
565
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
546
+ if (!existsSync3(pkgPath)) return null;
547
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
566
548
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
567
549
  if (deps["@tanstack/react-start"]) return "tanstack-start";
568
550
  return null;
@@ -570,20 +552,21 @@ function detectFramework(cwd) {
570
552
  function runSetup(cwd = process.cwd()) {
571
553
  const manifest = loadSkillManifest();
572
554
  const framework = detectFramework(cwd);
573
- const chosen = manifest.skills.filter(
574
- (skill) => skill.category === "core" || skill.framework === framework
575
- );
555
+ const chosen = manifest.skills.filter((skill) => skill.category === "core");
576
556
  const roots = [
577
557
  resolve2(cwd, ".agents", "skills"),
578
558
  resolve2(cwd, ".claude", "skills"),
579
559
  resolve2(cwd, ".codex", "skills"),
580
560
  resolve2(cwd, ".cursor", "skills")
581
561
  ];
582
- const existing = roots.filter((root) => existsSync4(root));
562
+ const existing = roots.filter((root) => existsSync3(root));
583
563
  const targetDirs = existing.length > 0 ? existing : [roots[0]];
584
564
  const copied = [];
585
565
  for (const targetDir of targetDirs) {
586
566
  mkdirSync(targetDir, { recursive: true });
567
+ for (const name of RETIRED_SKILL_NAMES) {
568
+ rmSync(resolve2(targetDir, name), { recursive: true, force: true });
569
+ }
587
570
  for (const skill of chosen) {
588
571
  const sourceDir = dirname2(resolveFromPackageRoot(skill.path));
589
572
  const destDir = resolve2(targetDir, skill.name);
@@ -1,9 +1,8 @@
1
- import { S as Schemas } from './_spec-BKtVFcV9.js';
1
+ import { S as Schemas } from './_spec-nkWAraS0.js';
2
2
 
3
3
  type PublicBoard = Schemas['PublicBoardContext'];
4
4
  type PublicBoardFeatures = PublicBoard['features'];
5
5
  type PublicBoardAnalytics = PublicBoard['analytics'];
6
- type PublicBoardTheme = NonNullable<PublicBoard['theme']>;
7
6
  /**
8
7
  * An operator-defined custom job-field definition. Board-wide;
9
8
  * use it to render and localize a job's opaque `customFieldValues` (resolve
@@ -14,4 +13,4 @@ type CustomFieldDefinition = Schemas['CustomFieldDefinition'];
14
13
  type CustomFieldType = CustomFieldDefinition['type'];
15
14
  type CustomFieldOption = Schemas['CustomFieldOption'];
16
15
 
17
- export type { CustomFieldDefinition as C, PublicBoard as P, CustomFieldOption as a, CustomFieldType as b, PublicBoardAnalytics as c, PublicBoardFeatures as d, PublicBoardTheme as e };
16
+ export type { CustomFieldDefinition as C, PublicBoard as P, CustomFieldOption as a, CustomFieldType as b, PublicBoardAnalytics as c, PublicBoardFeatures as d };
@@ -1,9 +1,8 @@
1
- import { S as Schemas } from './_spec-BKtVFcV9.mjs';
1
+ import { S as Schemas } from './_spec-nkWAraS0.mjs';
2
2
 
3
3
  type PublicBoard = Schemas['PublicBoardContext'];
4
4
  type PublicBoardFeatures = PublicBoard['features'];
5
5
  type PublicBoardAnalytics = PublicBoard['analytics'];
6
- type PublicBoardTheme = NonNullable<PublicBoard['theme']>;
7
6
  /**
8
7
  * An operator-defined custom job-field definition. Board-wide;
9
8
  * use it to render and localize a job's opaque `customFieldValues` (resolve
@@ -14,4 +13,4 @@ type CustomFieldDefinition = Schemas['CustomFieldDefinition'];
14
13
  type CustomFieldType = CustomFieldDefinition['type'];
15
14
  type CustomFieldOption = Schemas['CustomFieldOption'];
16
15
 
17
- export type { CustomFieldDefinition as C, PublicBoard as P, CustomFieldOption as a, CustomFieldType as b, PublicBoardAnalytics as c, PublicBoardFeatures as d, PublicBoardTheme as e };
16
+ export type { CustomFieldDefinition as C, PublicBoard as P, CustomFieldOption as a, CustomFieldType as b, PublicBoardAnalytics as c, PublicBoardFeatures as d };
package/dist/doctor.js CHANGED
@@ -49,13 +49,21 @@ function loadSkillCorpus(baseDir) {
49
49
  const manifest = loadSkillManifest(baseDir);
50
50
  return {
51
51
  version: manifest.version,
52
- skills: manifest.skills.map((skill) => ({
53
- ...skill,
54
- content: (0, import_node_fs.readFileSync)(
55
- resolveFromPackageRoot(skill.path, baseDir),
56
- "utf8"
57
- )
58
- }))
52
+ skills: manifest.skills.map((skill) => {
53
+ const skillPath = resolveFromPackageRoot(skill.path, baseDir);
54
+ const skillDir = (0, import_node_path.dirname)(skillPath);
55
+ const references = (0, import_node_fs.readdirSync)(skillDir, { withFileTypes: true }).filter(
56
+ (entry) => entry.isFile() && entry.name !== "SKILL.md" && entry.name.endsWith(".md")
57
+ ).map((entry) => ({
58
+ path: entry.name,
59
+ content: (0, import_node_fs.readFileSync)((0, import_node_path.resolve)(skillDir, entry.name), "utf8")
60
+ })).sort((a, b) => a.path.localeCompare(b.path));
61
+ return {
62
+ ...skill,
63
+ content: (0, import_node_fs.readFileSync)(skillPath, "utf8"),
64
+ references
65
+ };
66
+ })
59
67
  };
60
68
  }
61
69
 
@@ -422,42 +430,9 @@ async function runReadProbes(fetchImpl, frontendUrl) {
422
430
  ];
423
431
  }
424
432
 
425
- // src/doctor/theme.ts
426
- var import_node_crypto = require("crypto");
433
+ // src/doctor/run.ts
427
434
  var import_node_fs3 = require("fs");
428
435
  var import_node_path3 = require("path");
429
- var THEME = record("static.theme", 1);
430
- function checkThemeFreshness(projectRoot) {
431
- const tokensPath = (0, import_node_path3.join)(projectRoot, "src/tokens.css");
432
- if (!(0, import_node_fs3.existsSync)(tokensPath)) {
433
- return [
434
- THEME(
435
- "skip",
436
- "no src/tokens.css \u2014 local theme freshness check not applicable"
437
- )
438
- ];
439
- }
440
- const tokensHash = (0, import_node_crypto.createHash)("sha256").update((0, import_node_fs3.readFileSync)(tokensPath, "utf8"), "utf8").digest("hex");
441
- const resolvedPath = (0, import_node_path3.join)(projectRoot, "src/theme/resolved.ts");
442
- const resolvedHash = (0, import_node_fs3.existsSync)(resolvedPath) ? (0, import_node_fs3.readFileSync)(resolvedPath, "utf8").match(
443
- /tokensHash = '([0-9a-f]{64})'/
444
- )?.[1] ?? null : null;
445
- if (resolvedHash !== tokensHash) {
446
- return [
447
- THEME(
448
- "fail",
449
- `src/theme/resolved.ts is ${resolvedHash ? "stale" : "missing"} \u2014 run \`npm run gen:theme\` (OG images render from it)`
450
- )
451
- ];
452
- }
453
- return [
454
- THEME("pass", `tokens.css \u21C4 resolved module (${tokensHash.slice(0, 12)}\u2026)`)
455
- ];
456
- }
457
-
458
- // src/doctor/run.ts
459
- var import_node_fs4 = require("fs");
460
- var import_node_path4 = require("path");
461
436
  var STATIC_API = record("static.api", 1);
462
437
  var STATIC_BOARD = record("static.board", 1);
463
438
  var STATIC_SKILLS = record("static.skills", 1);
@@ -513,8 +488,8 @@ var SKILL_ROOTS = [
513
488
  ".cursor/skills"
514
489
  ];
515
490
  function checkSkillsFreshness(projectRoot) {
516
- const roots = SKILL_ROOTS.map((root) => (0, import_node_path4.join)(projectRoot, root)).filter(
517
- (root) => (0, import_node_fs4.existsSync)(root)
491
+ const roots = SKILL_ROOTS.map((root) => (0, import_node_path3.join)(projectRoot, root)).filter(
492
+ (root) => (0, import_node_fs3.existsSync)(root)
518
493
  );
519
494
  if (roots.length === 0) {
520
495
  return STATIC_SKILLS(
@@ -527,10 +502,10 @@ function checkSkillsFreshness(projectRoot) {
527
502
  const seen = /* @__PURE__ */ new Set();
528
503
  for (const root of roots) {
529
504
  for (const skill of corpus.skills) {
530
- const copied = (0, import_node_path4.join)(root, skill.name, "SKILL.md");
531
- if (!(0, import_node_fs4.existsSync)(copied)) continue;
505
+ const copied = (0, import_node_path3.join)(root, skill.name, "SKILL.md");
506
+ if (!(0, import_node_fs3.existsSync)(copied)) continue;
532
507
  seen.add(skill.name);
533
- if ((0, import_node_fs4.readFileSync)(copied, "utf8") !== skill.content) {
508
+ if ((0, import_node_fs3.readFileSync)(copied, "utf8") !== skill.content) {
534
509
  stale.add(skill.name);
535
510
  }
536
511
  }
@@ -574,7 +549,6 @@ async function runDoctor(options) {
574
549
  results.push(STATIC_BOARD("skip", "env checks failed \u2014 fix them first"));
575
550
  }
576
551
  results.push(checkSkillsFreshness(options.projectRoot ?? process.cwd()));
577
- results.push(...checkThemeFreshness(options.projectRoot ?? process.cwd()));
578
552
  results.push(
579
553
  ...checkCookieCodecConformance(options.projectRoot ?? process.cwd())
580
554
  );
package/dist/doctor.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  var DEFAULT_CAVUNO_API_URL = "https://api.cavuno.com";
3
3
 
4
4
  // src/skills.ts
5
- import { readFileSync } from "fs";
5
+ import { readdirSync, readFileSync } from "fs";
6
6
  import { dirname, resolve } from "path";
7
7
  import { fileURLToPath } from "url";
8
8
  function packageRoot() {
@@ -19,13 +19,21 @@ function loadSkillCorpus(baseDir) {
19
19
  const manifest = loadSkillManifest(baseDir);
20
20
  return {
21
21
  version: manifest.version,
22
- skills: manifest.skills.map((skill) => ({
23
- ...skill,
24
- content: readFileSync(
25
- resolveFromPackageRoot(skill.path, baseDir),
26
- "utf8"
27
- )
28
- }))
22
+ skills: manifest.skills.map((skill) => {
23
+ const skillPath = resolveFromPackageRoot(skill.path, baseDir);
24
+ const skillDir = dirname(skillPath);
25
+ const references = readdirSync(skillDir, { withFileTypes: true }).filter(
26
+ (entry) => entry.isFile() && entry.name !== "SKILL.md" && entry.name.endsWith(".md")
27
+ ).map((entry) => ({
28
+ path: entry.name,
29
+ content: readFileSync(resolve(skillDir, entry.name), "utf8")
30
+ })).sort((a, b) => a.path.localeCompare(b.path));
31
+ return {
32
+ ...skill,
33
+ content: readFileSync(skillPath, "utf8"),
34
+ references
35
+ };
36
+ })
29
37
  };
30
38
  }
31
39
 
@@ -103,7 +111,7 @@ function summarize(results) {
103
111
  }
104
112
 
105
113
  // src/doctor/cookie-conformance.ts
106
- import { existsSync, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
114
+ import { existsSync, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync } from "fs";
107
115
  import { join, relative } from "path";
108
116
  var COOKIE = record("static.cookie-codec", 1);
109
117
  var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|astro|svelte|vue)$/;
@@ -209,7 +217,7 @@ function hasGeneratedBanner(text) {
209
217
  function walkSourceFiles(dir, out) {
210
218
  let entries;
211
219
  try {
212
- entries = readdirSync(dir);
220
+ entries = readdirSync2(dir);
213
221
  } catch {
214
222
  return;
215
223
  }
@@ -392,42 +400,9 @@ async function runReadProbes(fetchImpl, frontendUrl) {
392
400
  ];
393
401
  }
394
402
 
395
- // src/doctor/theme.ts
396
- import { createHash } from "crypto";
403
+ // src/doctor/run.ts
397
404
  import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
398
405
  import { join as join2 } from "path";
399
- var THEME = record("static.theme", 1);
400
- function checkThemeFreshness(projectRoot) {
401
- const tokensPath = join2(projectRoot, "src/tokens.css");
402
- if (!existsSync2(tokensPath)) {
403
- return [
404
- THEME(
405
- "skip",
406
- "no src/tokens.css \u2014 local theme freshness check not applicable"
407
- )
408
- ];
409
- }
410
- const tokensHash = createHash("sha256").update(readFileSync3(tokensPath, "utf8"), "utf8").digest("hex");
411
- const resolvedPath = join2(projectRoot, "src/theme/resolved.ts");
412
- const resolvedHash = existsSync2(resolvedPath) ? readFileSync3(resolvedPath, "utf8").match(
413
- /tokensHash = '([0-9a-f]{64})'/
414
- )?.[1] ?? null : null;
415
- if (resolvedHash !== tokensHash) {
416
- return [
417
- THEME(
418
- "fail",
419
- `src/theme/resolved.ts is ${resolvedHash ? "stale" : "missing"} \u2014 run \`npm run gen:theme\` (OG images render from it)`
420
- )
421
- ];
422
- }
423
- return [
424
- THEME("pass", `tokens.css \u21C4 resolved module (${tokensHash.slice(0, 12)}\u2026)`)
425
- ];
426
- }
427
-
428
- // src/doctor/run.ts
429
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
430
- import { join as join3 } from "path";
431
406
  var STATIC_API = record("static.api", 1);
432
407
  var STATIC_BOARD = record("static.board", 1);
433
408
  var STATIC_SKILLS = record("static.skills", 1);
@@ -483,8 +458,8 @@ var SKILL_ROOTS = [
483
458
  ".cursor/skills"
484
459
  ];
485
460
  function checkSkillsFreshness(projectRoot) {
486
- const roots = SKILL_ROOTS.map((root) => join3(projectRoot, root)).filter(
487
- (root) => existsSync3(root)
461
+ const roots = SKILL_ROOTS.map((root) => join2(projectRoot, root)).filter(
462
+ (root) => existsSync2(root)
488
463
  );
489
464
  if (roots.length === 0) {
490
465
  return STATIC_SKILLS(
@@ -497,10 +472,10 @@ function checkSkillsFreshness(projectRoot) {
497
472
  const seen = /* @__PURE__ */ new Set();
498
473
  for (const root of roots) {
499
474
  for (const skill of corpus.skills) {
500
- const copied = join3(root, skill.name, "SKILL.md");
501
- if (!existsSync3(copied)) continue;
475
+ const copied = join2(root, skill.name, "SKILL.md");
476
+ if (!existsSync2(copied)) continue;
502
477
  seen.add(skill.name);
503
- if (readFileSync4(copied, "utf8") !== skill.content) {
478
+ if (readFileSync3(copied, "utf8") !== skill.content) {
504
479
  stale.add(skill.name);
505
480
  }
506
481
  }
@@ -544,7 +519,6 @@ async function runDoctor(options) {
544
519
  results.push(STATIC_BOARD("skip", "env checks failed \u2014 fix them first"));
545
520
  }
546
521
  results.push(checkSkillsFreshness(options.projectRoot ?? process.cwd()));
547
- results.push(...checkThemeFreshness(options.projectRoot ?? process.cwd()));
548
522
  results.push(
549
523
  ...checkCookieCodecConformance(options.projectRoot ?? process.cwd())
550
524
  );
@@ -1,6 +1,6 @@
1
1
  import { B as BoardLabelOverrides } from './ui-copy-CCkz6CVF.mjs';
2
- import { J as JobSort, E as EmploymentType, R as RemoteOption, S as Seniority } from './jobs-k84C0v_8.mjs';
3
- import './_spec-BKtVFcV9.mjs';
2
+ import { J as JobSort, E as EmploymentType, R as RemoteOption, S as Seniority } from './jobs-lVvKCBCU.mjs';
3
+ import './_spec-nkWAraS0.mjs';
4
4
 
5
5
  declare const REMOTE_OPTIONS: readonly RemoteOption[];
6
6
  /**
package/dist/filters.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { B as BoardLabelOverrides } from './ui-copy-CCkz6CVF.js';
2
- import { J as JobSort, E as EmploymentType, R as RemoteOption, S as Seniority } from './jobs-C4qRfCdP.js';
3
- import './_spec-BKtVFcV9.js';
2
+ import { J as JobSort, E as EmploymentType, R as RemoteOption, S as Seniority } from './jobs-BiEvL9d7.js';
3
+ import './_spec-nkWAraS0.js';
4
4
 
5
5
  declare const REMOTE_OPTIONS: readonly RemoteOption[];
6
6
  /**
package/dist/format.d.mts CHANGED
@@ -1,8 +1,8 @@
1
- import { P as PublicJob, a as PublicJobCard, C as CustomFieldValues } from './jobs-k84C0v_8.mjs';
1
+ import { P as PublicJob, a as PublicJobCard, C as CustomFieldValues } from './jobs-lVvKCBCU.mjs';
2
2
  import { B as BoardLabelOverrides } from './ui-copy-CCkz6CVF.mjs';
3
3
  export { A as AlertsCopy, a as ApplyCopy, b as BlogCopy, c as BreadcrumbsCopy, C as CopyLinkCopy, E as EntityCopy, F as FooterCopy, J as JobCardCopy, d as JobDetailCopy, e as JobSearchCopy, N as NavCopy, P as PUBLIC_LABEL_GROUPS, f as PaginationCopy, S as SalaryCopy, U as UiCopy, u as uiCopy } from './ui-copy-CCkz6CVF.mjs';
4
- import { C as CustomFieldDefinition } from './board-pNN710Zc.mjs';
5
- import './_spec-BKtVFcV9.mjs';
4
+ import { C as CustomFieldDefinition } from './board-muu8BI8P.mjs';
5
+ import './_spec-nkWAraS0.mjs';
6
6
 
7
7
  /**
8
8
  * Date display helpers in the board language (required leading parameter,