@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.
- package/CHANGELOG.md +22 -0
- package/README.md +12 -3
- package/dist/{_spec-BKtVFcV9.d.mts → _spec-nkWAraS0.d.mts} +4 -18
- package/dist/{_spec-BKtVFcV9.d.ts → _spec-nkWAraS0.d.ts} +4 -18
- package/dist/bin.mjs +40 -57
- package/dist/{board-3Xj_Y-Tx.d.ts → board-NE98PlwN.d.ts} +2 -3
- package/dist/{board-pNN710Zc.d.mts → board-muu8BI8P.d.mts} +2 -3
- package/dist/doctor.js +21 -47
- package/dist/doctor.mjs +24 -50
- package/dist/filters.d.mts +2 -2
- package/dist/filters.d.ts +2 -2
- package/dist/format.d.mts +3 -3
- package/dist/format.d.ts +3 -3
- package/dist/index.d.mts +11 -27
- package/dist/index.d.ts +11 -27
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/dist/{jobs-C4qRfCdP.d.ts → jobs-BiEvL9d7.d.ts} +1 -1
- package/dist/{jobs-k84C0v_8.d.mts → jobs-lVvKCBCU.d.mts} +1 -1
- package/dist/{salaries--J5mTylN.d.ts → salaries-BahBpxWT.d.ts} +2 -2
- package/dist/{salaries-C9YLO5NG.d.mts → salaries-ChSqDBAM.d.mts} +2 -2
- package/dist/{search-CE_qQiM7.d.mts → search-CCyFWPi3.d.mts} +1 -1
- package/dist/{search-CLRQ_El9.d.ts → search-CyRvVMUp.d.ts} +1 -1
- package/dist/seo.d.mts +4 -4
- package/dist/seo.d.ts +4 -4
- package/dist/server.d.mts +5 -5
- package/dist/server.d.ts +5 -5
- package/dist/sitemap.d.mts +5 -5
- package/dist/sitemap.d.ts +5 -5
- package/dist/skills.d.mts +15 -4
- package/dist/skills.d.ts +15 -4
- package/dist/skills.js +30 -7
- package/dist/skills.mjs +30 -8
- package/dist/suggest.d.mts +2 -2
- package/dist/suggest.d.ts +2 -2
- package/package.json +1 -11
- package/skills/cavuno-board-account/SKILL.md +58 -66
- package/skills/cavuno-board-api-client/SKILL.md +78 -0
- package/skills/cavuno-board-applications/SKILL.md +42 -52
- package/skills/cavuno-board-auth/SKILL.md +39 -60
- package/skills/cavuno-board-blog/SKILL.md +64 -49
- package/skills/cavuno-board-companies/SKILL.md +75 -53
- package/skills/cavuno-board-errors/SKILL.md +32 -45
- package/skills/cavuno-board-filters/SKILL.md +68 -75
- package/skills/cavuno-board-format/SKILL.md +65 -66
- package/skills/cavuno-board-i18n/SKILL.md +43 -68
- package/skills/cavuno-board-job-alerts/SKILL.md +72 -57
- package/skills/cavuno-board-jobs/SKILL.md +67 -55
- package/skills/cavuno-board-messaging/SKILL.md +57 -59
- package/skills/cavuno-board-paywall/SKILL.md +52 -54
- package/skills/cavuno-board-post-a-job/SKILL.md +122 -0
- package/skills/cavuno-board-salaries/SKILL.md +77 -50
- package/skills/cavuno-board-search-suggestions/SKILL.md +86 -0
- package/skills/cavuno-board-seo/BLOG.md +40 -0
- package/skills/cavuno-board-seo/JOB_AND_BREADCRUMBS.md +49 -0
- package/skills/cavuno-board-seo/LISTING.md +36 -0
- package/skills/cavuno-board-seo/SALARY.md +61 -0
- package/skills/cavuno-board-seo/SKILL.md +34 -161
- package/skills/cavuno-board-server-sessions/SKILL.md +147 -0
- package/skills/cavuno-board-setup/SKILL.md +46 -79
- package/skills/cavuno-board-sitemap/SKILL.md +78 -91
- package/skills/cavuno-board-smoke-test/SKILL.md +38 -69
- package/skills/manifest.json +43 -57
- package/dist/theme.d.mts +0 -115
- package/dist/theme.d.ts +0 -115
- package/dist/theme.js +0 -195
- package/dist/theme.mjs +0 -174
- package/skills/cavuno-board-client/SKILL.md +0 -89
- package/skills/cavuno-board-job-posting/SKILL.md +0 -116
- package/skills/cavuno-board-server/SKILL.md +0 -229
- package/skills/cavuno-board-suggest/SKILL.md +0 -119
- package/skills/cavuno-board-theme/SKILL.md +0 -57
- 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,
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
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 =
|
|
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/
|
|
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) =>
|
|
489
|
-
(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 =
|
|
503
|
-
if (!
|
|
477
|
+
const copied = join2(root, skill.name, "SKILL.md");
|
|
478
|
+
if (!existsSync2(copied)) continue;
|
|
504
479
|
seen.add(skill.name);
|
|
505
|
-
if (
|
|
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
|
|
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 (!
|
|
565
|
-
const pkg = JSON.parse(
|
|
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) =>
|
|
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-
|
|
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
|
|
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-
|
|
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
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
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/
|
|
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,
|
|
517
|
-
(root) => (0,
|
|
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,
|
|
531
|
-
if (!(0,
|
|
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,
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
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 =
|
|
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/
|
|
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) =>
|
|
487
|
-
(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 =
|
|
501
|
-
if (!
|
|
475
|
+
const copied = join2(root, skill.name, "SKILL.md");
|
|
476
|
+
if (!existsSync2(copied)) continue;
|
|
502
477
|
seen.add(skill.name);
|
|
503
|
-
if (
|
|
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
|
);
|
package/dist/filters.d.mts
CHANGED
|
@@ -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-
|
|
3
|
-
import './_spec-
|
|
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-
|
|
3
|
-
import './_spec-
|
|
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-
|
|
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-
|
|
5
|
-
import './_spec-
|
|
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,
|