@primer/brand-mcp 0.0.0-20260626120119
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/README.md +43 -0
- package/dist/brand/detect-framework.d.ts +13 -0
- package/dist/brand/detect-framework.js +48 -0
- package/dist/brand/detect-framework.js.map +1 -0
- package/dist/brand/docs-source.d.ts +40 -0
- package/dist/brand/docs-source.js +82 -0
- package/dist/brand/docs-source.js.map +1 -0
- package/dist/brand/resolve-assets.d.ts +14 -0
- package/dist/brand/resolve-assets.js +101 -0
- package/dist/brand/resolve-assets.js.map +1 -0
- package/dist/brand/resolve-install.d.ts +25 -0
- package/dist/brand/resolve-install.js +63 -0
- package/dist/brand/resolve-install.js.map +1 -0
- package/dist/catalog/load.d.ts +3 -0
- package/dist/catalog/load.js +18 -0
- package/dist/catalog/load.js.map +1 -0
- package/dist/catalog/types.d.ts +60 -0
- package/dist/catalog/types.js +15 -0
- package/dist/catalog/types.js.map +1 -0
- package/dist/catalog.json +9964 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.js +18 -0
- package/dist/logger.js.map +1 -0
- package/dist/review/rules.d.ts +5 -0
- package/dist/review/rules.js +235 -0
- package/dist/review/rules.js.map +1 -0
- package/dist/review/types.d.ts +15 -0
- package/dist/review/types.js +6 -0
- package/dist/review/types.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +53 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/format.d.ts +15 -0
- package/dist/tools/format.js +23 -0
- package/dist/tools/format.js.map +1 -0
- package/dist/tools/primer-brand-asset/index.d.ts +1 -0
- package/dist/tools/primer-brand-asset/index.js +2 -0
- package/dist/tools/primer-brand-asset/index.js.map +1 -0
- package/dist/tools/primer-brand-asset/primer-brand-asset.d.ts +18 -0
- package/dist/tools/primer-brand-asset/primer-brand-asset.js +43 -0
- package/dist/tools/primer-brand-asset/primer-brand-asset.js.map +1 -0
- package/dist/tools/primer-brand-component/index.d.ts +1 -0
- package/dist/tools/primer-brand-component/index.js +2 -0
- package/dist/tools/primer-brand-component/index.js.map +1 -0
- package/dist/tools/primer-brand-component/primer-brand-component.d.ts +12 -0
- package/dist/tools/primer-brand-component/primer-brand-component.js +72 -0
- package/dist/tools/primer-brand-component/primer-brand-component.js.map +1 -0
- package/dist/tools/primer-brand-docs/index.d.ts +1 -0
- package/dist/tools/primer-brand-docs/index.js +2 -0
- package/dist/tools/primer-brand-docs/index.js.map +1 -0
- package/dist/tools/primer-brand-docs/primer-brand-docs.d.ts +15 -0
- package/dist/tools/primer-brand-docs/primer-brand-docs.js +56 -0
- package/dist/tools/primer-brand-docs/primer-brand-docs.js.map +1 -0
- package/dist/tools/primer-brand-examples/index.d.ts +1 -0
- package/dist/tools/primer-brand-examples/index.js +2 -0
- package/dist/tools/primer-brand-examples/index.js.map +1 -0
- package/dist/tools/primer-brand-examples/primer-brand-examples.d.ts +12 -0
- package/dist/tools/primer-brand-examples/primer-brand-examples.js +48 -0
- package/dist/tools/primer-brand-examples/primer-brand-examples.js.map +1 -0
- package/dist/tools/primer-brand-review/index.d.ts +1 -0
- package/dist/tools/primer-brand-review/index.js +2 -0
- package/dist/tools/primer-brand-review/index.js.map +1 -0
- package/dist/tools/primer-brand-review/primer-brand-review.d.ts +15 -0
- package/dist/tools/primer-brand-review/primer-brand-review.js +63 -0
- package/dist/tools/primer-brand-review/primer-brand-review.js.map +1 -0
- package/dist/tools/primer-brand-setup/index.d.ts +1 -0
- package/dist/tools/primer-brand-setup/index.js +2 -0
- package/dist/tools/primer-brand-setup/index.js.map +1 -0
- package/dist/tools/primer-brand-setup/primer-brand-setup.d.ts +12 -0
- package/dist/tools/primer-brand-setup/primer-brand-setup.js +126 -0
- package/dist/tools/primer-brand-setup/primer-brand-setup.js.map +1 -0
- package/dist/tools/primer-brand-tokens/index.d.ts +1 -0
- package/dist/tools/primer-brand-tokens/index.js +2 -0
- package/dist/tools/primer-brand-tokens/index.js.map +1 -0
- package/dist/tools/primer-brand-tokens/primer-brand-tokens.d.ts +18 -0
- package/dist/tools/primer-brand-tokens/primer-brand-tokens.js +93 -0
- package/dist/tools/primer-brand-tokens/primer-brand-tokens.js.map +1 -0
- package/dist/tools/register.d.ts +3 -0
- package/dist/tools/register.js +45 -0
- package/dist/tools/register.js.map +1 -0
- package/dist/tools/types.d.ts +34 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/util/text.d.ts +8 -0
- package/dist/util/text.js +31 -0
- package/dist/util/text.js.map +1 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @primer/brand-mcp
|
|
2
|
+
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server that helps AI agents use **Primer Brand** (`@primer/react-brand`) correctly when building GitHub marketing and landing pages.
|
|
4
|
+
|
|
5
|
+
It is a version-aware, `stdio` (local) server which reads the docs and metadata of your installed `@primer/react-brand` dependency.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | What it does |
|
|
10
|
+
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| `primer_brand_setup` | Framework-aware setup (Auto-installs `ThemeProvider`, Mona Sans fonts, styles, `'use client'` for RSC) - the parts agents forget. |
|
|
12
|
+
| `primer_brand_component` | List components, or get one component's API: props, allowed values, sub-components, and examples. |
|
|
13
|
+
| `primer_brand_examples` | Ranked, copy-and-adapt examples for a goal, taken from the library's own tested stories. Falls back to a default set. |
|
|
14
|
+
| `primer_brand_tokens` | Resolve design tokens by intent (color, space, typography) to CSS variables and values. |
|
|
15
|
+
| `primer_brand_asset` | Find Octicons (`@primer/octicons-react`) and Octovisuals (`@primer/octovisuals-react`) as code imports. |
|
|
16
|
+
| `primer_brand_docs` | Search and read Primer Brand guidance (principles, accessibility, getting started). |
|
|
17
|
+
| `primer_brand_review` | Check generated JSX/CSS against the design system: unknown components, invalid props, hardcoded values, off-brand tells. |
|
|
18
|
+
|
|
19
|
+
## Getting started
|
|
20
|
+
|
|
21
|
+
Requires Node.js >= 24.
|
|
22
|
+
|
|
23
|
+
### VS Code
|
|
24
|
+
|
|
25
|
+
```jsonc
|
|
26
|
+
{
|
|
27
|
+
"servers": {
|
|
28
|
+
"Primer Brand": {
|
|
29
|
+
"type": "stdio",
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["@primer/brand-mcp@latest"]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Copilot CLI / other stdio clients
|
|
38
|
+
|
|
39
|
+
Run the server with `npx @primer/brand-mcp@latest`.
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type FrameworkId = 'next-app' | 'next-pages' | 'vite' | 'astro' | 'remix' | 'unknown';
|
|
2
|
+
export interface FrameworkInfo {
|
|
3
|
+
id: FrameworkId;
|
|
4
|
+
label: string;
|
|
5
|
+
/** Uses React Server Components, so providers need a `'use client'` boundary. */
|
|
6
|
+
rsc: boolean;
|
|
7
|
+
projectDir: string | null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Best-effort detection of the consumer's framework so setup guidance can be tailored
|
|
11
|
+
* (where providers go, whether a `'use client'` boundary is required, which style import to use).
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectFramework(fromDir?: string): FrameworkInfo;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, sep } from 'node:path';
|
|
3
|
+
/** Nearest `package.json` above `fromDir` that isn't inside `node_modules`. */
|
|
4
|
+
function findProjectDir(fromDir) {
|
|
5
|
+
let dir = fromDir;
|
|
6
|
+
for (;;) {
|
|
7
|
+
if (!dir.split(sep).includes('node_modules') && existsSync(join(dir, 'package.json')))
|
|
8
|
+
return dir;
|
|
9
|
+
const parent = dirname(dir);
|
|
10
|
+
if (parent === dir)
|
|
11
|
+
return null;
|
|
12
|
+
dir = parent;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function readDependencies(projectDir) {
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(readFileSync(join(projectDir, 'package.json'), 'utf8'));
|
|
18
|
+
return { ...parsed.dependencies, ...parsed.devDependencies };
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Best-effort detection of the consumer's framework so setup guidance can be tailored
|
|
26
|
+
* (where providers go, whether a `'use client'` boundary is required, which style import to use).
|
|
27
|
+
*/
|
|
28
|
+
export function detectFramework(fromDir = process.cwd()) {
|
|
29
|
+
const projectDir = findProjectDir(fromDir);
|
|
30
|
+
if (!projectDir)
|
|
31
|
+
return { id: 'unknown', label: 'a React project', rsc: false, projectDir: null };
|
|
32
|
+
const deps = readDependencies(projectDir);
|
|
33
|
+
if (deps.next) {
|
|
34
|
+
const appRouter = existsSync(join(projectDir, 'app')) || existsSync(join(projectDir, 'src', 'app'));
|
|
35
|
+
return appRouter
|
|
36
|
+
? { id: 'next-app', label: 'Next.js (App Router)', rsc: true, projectDir }
|
|
37
|
+
: { id: 'next-pages', label: 'Next.js (Pages Router)', rsc: false, projectDir };
|
|
38
|
+
}
|
|
39
|
+
if (deps['@remix-run/react'] || deps['@remix-run/node']) {
|
|
40
|
+
return { id: 'remix', label: 'Remix', rsc: false, projectDir };
|
|
41
|
+
}
|
|
42
|
+
if (deps.astro)
|
|
43
|
+
return { id: 'astro', label: 'Astro', rsc: false, projectDir };
|
|
44
|
+
if (deps.vite)
|
|
45
|
+
return { id: 'vite', label: 'Vite + React', rsc: false, projectDir };
|
|
46
|
+
return { id: 'unknown', label: 'a React project', rsc: false, projectDir };
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=detect-framework.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-framework.js","sourceRoot":"","sources":["../../src/brand/detect-framework.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAChD,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AAY5C,+EAA+E;AAC/E,SAAS,cAAc,CAAC,OAAe;IACrC,IAAI,GAAG,GAAG,OAAO,CAAA;IACjB,SAAS,CAAC;QACR,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,GAAG,CAAA;QACjG,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAA;QAC/B,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,UAAkB;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAG/E,CAAA;QACD,OAAO,EAAC,GAAG,MAAM,CAAC,YAAY,EAAE,GAAG,MAAM,CAAC,eAAe,EAAC,CAAA;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB,OAAO,CAAC,GAAG,EAAE;IAC7D,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;IAC1C,IAAI,CAAC,UAAU;QAAE,OAAO,EAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAC,CAAA;IAE/F,MAAM,IAAI,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAA;IACzC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;QACnG,OAAO,SAAS;YACd,CAAC,CAAC,EAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,sBAAsB,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAC;YACxE,CAAC,CAAC,EAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;IACjF,CAAC;IACD,IAAI,IAAI,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACxD,OAAO,EAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;IAC9D,CAAC;IACD,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,EAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;IAC5E,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,EAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;IACjF,OAAO,EAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAC,CAAA;AAC1E,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Logger } from '../logger.js';
|
|
2
|
+
import type { BrandInstall } from './resolve-install.js';
|
|
3
|
+
/**
|
|
4
|
+
* One entry in the documentation table of contents (parsed from `llms.txt`).
|
|
5
|
+
*/
|
|
6
|
+
export interface DocEntry {
|
|
7
|
+
label: string;
|
|
8
|
+
/** Path relative to the docs root, e.g. `docs/components/Hero/index.md`. */
|
|
9
|
+
path: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
}
|
|
12
|
+
export type DocOrigin = 'installed' | 'live' | 'none';
|
|
13
|
+
export interface DocResult {
|
|
14
|
+
text: string;
|
|
15
|
+
origin: DocOrigin;
|
|
16
|
+
version: string | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Tiered documentation source.
|
|
20
|
+
*
|
|
21
|
+
* Resolution order, by design:
|
|
22
|
+
* 1. The bundled, version-pinned docs of the installed `@primer/react-brand` (authoritative,
|
|
23
|
+
* offline, matches the consumer's version).
|
|
24
|
+
* 2. The live site's `llms.txt` / pages — best-effort enrichment for guidance that is not
|
|
25
|
+
* bundled, bounded by a short timeout and cached.
|
|
26
|
+
*
|
|
27
|
+
* Version-sensitive facts never depend on the network; this only covers prose guidance.
|
|
28
|
+
*/
|
|
29
|
+
export interface DocsSource {
|
|
30
|
+
origin: DocOrigin;
|
|
31
|
+
version: string | null;
|
|
32
|
+
index(): Promise<DocEntry[]>;
|
|
33
|
+
read(path: string): Promise<DocResult | null>;
|
|
34
|
+
}
|
|
35
|
+
export declare function createDocsSource(brand: BrandInstall, logger: Logger): DocsSource;
|
|
36
|
+
/**
|
|
37
|
+
* Parse the `llms.txt` markdown table of contents into structured entries.
|
|
38
|
+
* Lines look like: `- [Hero](docs/components/Hero/index.md): A prominent banner...`
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseToc(markdown: string): DocEntry[];
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, normalize, resolve, sep } from 'node:path';
|
|
3
|
+
const SITE = 'https://primer.style/brand';
|
|
4
|
+
const FETCH_TIMEOUT_MS = 2500;
|
|
5
|
+
export function createDocsSource(brand, logger) {
|
|
6
|
+
const cache = new Map();
|
|
7
|
+
const hasLocalDocs = Boolean(brand.llmsPath && brand.docsDir);
|
|
8
|
+
const docsRoot = brand.docsDir ? dirname(brand.docsDir) : null;
|
|
9
|
+
async function fetchText(url) {
|
|
10
|
+
const cached = cache.get(url);
|
|
11
|
+
if (cached !== undefined)
|
|
12
|
+
return cached;
|
|
13
|
+
try {
|
|
14
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
15
|
+
if (!response.ok)
|
|
16
|
+
return null;
|
|
17
|
+
const text = await response.text();
|
|
18
|
+
cache.set(url, text);
|
|
19
|
+
return text;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
logger.debug(`live fetch failed for ${url}: ${error.message}`);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function index() {
|
|
27
|
+
if (hasLocalDocs && brand.llmsPath) {
|
|
28
|
+
return parseToc(readFileSync(brand.llmsPath, 'utf8'));
|
|
29
|
+
}
|
|
30
|
+
const live = await fetchText(`${SITE}/llms.txt`);
|
|
31
|
+
return live ? parseToc(live) : [];
|
|
32
|
+
}
|
|
33
|
+
async function read(path) {
|
|
34
|
+
if (hasLocalDocs && docsRoot) {
|
|
35
|
+
const safe = safeJoin(docsRoot, path);
|
|
36
|
+
if (safe && existsSync(safe)) {
|
|
37
|
+
return { text: readFileSync(safe, 'utf8'), origin: 'installed', version: brand.version };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const live = await fetchText(`${SITE}/${path.replace(/^docs\//, '').replace(/\.md$/, '')}`);
|
|
41
|
+
if (live)
|
|
42
|
+
return { text: live, origin: 'live', version: null };
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
origin: hasLocalDocs ? 'installed' : 'live',
|
|
47
|
+
version: brand.version,
|
|
48
|
+
index,
|
|
49
|
+
read,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse the `llms.txt` markdown table of contents into structured entries.
|
|
54
|
+
* Lines look like: `- [Hero](docs/components/Hero/index.md): A prominent banner...`
|
|
55
|
+
*/
|
|
56
|
+
export function parseToc(markdown) {
|
|
57
|
+
const entries = [];
|
|
58
|
+
const linkLine = /^- \[(.+?)\]\((.+?)\)(?::\s*(.*))?$/;
|
|
59
|
+
for (const raw of markdown.split('\n')) {
|
|
60
|
+
const match = linkLine.exec(raw.trim());
|
|
61
|
+
if (!match)
|
|
62
|
+
continue;
|
|
63
|
+
const [, label, path, description] = match;
|
|
64
|
+
if (!label || !path)
|
|
65
|
+
continue;
|
|
66
|
+
entries.push({ label, path, description: description?.trim() || undefined });
|
|
67
|
+
}
|
|
68
|
+
return entries;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Join a user-supplied doc path onto the docs root, refusing anything that escapes it.
|
|
72
|
+
* Prevents path traversal (e.g. `../../etc/passwd`) reaching the filesystem.
|
|
73
|
+
*/
|
|
74
|
+
function safeJoin(root, path) {
|
|
75
|
+
const target = resolve(join(root, normalize(path)));
|
|
76
|
+
const realRoot = realpathSync(root);
|
|
77
|
+
const prefix = realRoot.endsWith(sep) ? realRoot : `${realRoot}${sep}`;
|
|
78
|
+
if (target !== realRoot && !target.startsWith(prefix))
|
|
79
|
+
return null;
|
|
80
|
+
return target;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=docs-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-source.js","sourceRoot":"","sources":["../../src/brand/docs-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CatalogAsset } from '../catalog/types.js';
|
|
2
|
+
export interface InstalledAssets {
|
|
3
|
+
assets: CatalogAsset[];
|
|
4
|
+
sources: Array<{
|
|
5
|
+
module: string;
|
|
6
|
+
version: string | null;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the icon/illustration packages installed in the consumer's project and extract their
|
|
11
|
+
* exported names. This makes `primer_brand_asset` version-accurate: it reflects the Octicons/Octovisuals
|
|
12
|
+
* the project actually has, rather than the snapshot baked into the catalog at build time.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveInstalledAssets(fromDir?: string): InstalledAssets;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
const ASSET_PACKAGES = [
|
|
4
|
+
{ module: '@primer/octicons-react', kind: 'icon' },
|
|
5
|
+
{ module: '@primer/octovisuals-react', kind: 'illustration' },
|
|
6
|
+
];
|
|
7
|
+
/** Walk up from `fromDir` to find an installed package directory. */
|
|
8
|
+
function findPackageDir(packageName, fromDir) {
|
|
9
|
+
let dir = fromDir;
|
|
10
|
+
for (;;) {
|
|
11
|
+
const candidate = join(dir, 'node_modules', ...packageName.split('/'));
|
|
12
|
+
if (existsSync(join(candidate, 'package.json')))
|
|
13
|
+
return candidate;
|
|
14
|
+
const parent = dirname(dir);
|
|
15
|
+
if (parent === dir)
|
|
16
|
+
return null;
|
|
17
|
+
dir = parent;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function readVersion(packageDir) {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8')).version ?? null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Collect exported PascalCase const names from a package's `.d.ts` type declarations. Octicons
|
|
30
|
+
* declare their icons as `declare const AlertIcon: Icon` in a sibling file, so all `.d.ts` files
|
|
31
|
+
* in the package's output directory are scanned, not just `index.d.ts`.
|
|
32
|
+
*/
|
|
33
|
+
function exportedNames(packageDir) {
|
|
34
|
+
const names = new Set();
|
|
35
|
+
for (const sub of ['dist', 'lib', '.']) {
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = readdirSync(join(packageDir, sub));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (!entry.endsWith('.d.ts'))
|
|
45
|
+
continue;
|
|
46
|
+
let source;
|
|
47
|
+
try {
|
|
48
|
+
source = readFileSync(join(packageDir, sub, entry), 'utf8');
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
for (const match of source.matchAll(/(?:export\s+)?declare\s+const\s+([A-Z]\w+)/g)) {
|
|
54
|
+
if (match[1])
|
|
55
|
+
names.add(match[1]);
|
|
56
|
+
}
|
|
57
|
+
for (const match of source.matchAll(/export\s+const\s+([A-Z]\w+)/g)) {
|
|
58
|
+
if (match[1])
|
|
59
|
+
names.add(match[1]);
|
|
60
|
+
}
|
|
61
|
+
for (const match of source.matchAll(/export\s*\{([^}]*)\}/g)) {
|
|
62
|
+
for (const part of (match[1] ?? '').split(',')) {
|
|
63
|
+
const ident = part
|
|
64
|
+
.trim()
|
|
65
|
+
.split(/\s+as\s+/)
|
|
66
|
+
.pop()
|
|
67
|
+
?.trim();
|
|
68
|
+
if (ident && /^[A-Z]\w+$/.test(ident))
|
|
69
|
+
names.add(ident);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return [...names];
|
|
75
|
+
}
|
|
76
|
+
function keepName(name, kind) {
|
|
77
|
+
// Octovisuals components are also named `...Icon`, so they are kept by package, not suffix.
|
|
78
|
+
return kind === 'icon' ? name.endsWith('Icon') : /^[A-Z]/.test(name) && !/(Props|Type|Metadata)$/.test(name);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Resolve the icon/illustration packages installed in the consumer's project and extract their
|
|
82
|
+
* exported names. This makes `primer_brand_asset` version-accurate: it reflects the Octicons/Octovisuals
|
|
83
|
+
* the project actually has, rather than the snapshot baked into the catalog at build time.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveInstalledAssets(fromDir = process.cwd()) {
|
|
86
|
+
const assets = [];
|
|
87
|
+
const sources = [];
|
|
88
|
+
for (const { module, kind } of ASSET_PACKAGES) {
|
|
89
|
+
const packageDir = findPackageDir(module, fromDir);
|
|
90
|
+
if (!packageDir)
|
|
91
|
+
continue;
|
|
92
|
+
const names = exportedNames(packageDir).filter(name => keepName(name, kind));
|
|
93
|
+
if (names.length === 0)
|
|
94
|
+
continue;
|
|
95
|
+
sources.push({ module, version: readVersion(packageDir) });
|
|
96
|
+
for (const name of names)
|
|
97
|
+
assets.push({ name, module, kind });
|
|
98
|
+
}
|
|
99
|
+
return { assets, sources };
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=resolve-assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-assets.js","sourceRoot":"","sources":["../../src/brand/resolve-assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Information about the `@primer/react-brand` install discovered in the consumer's project.
|
|
3
|
+
* This is what makes the server version-aware: tools prefer the docs and version of the
|
|
4
|
+
* package the project actually depends on.
|
|
5
|
+
*/
|
|
6
|
+
export interface BrandInstall {
|
|
7
|
+
found: boolean;
|
|
8
|
+
version: string | null;
|
|
9
|
+
packageDir: string | null;
|
|
10
|
+
/** Bundled version-pinned docs directory, when the installed version ships one. */
|
|
11
|
+
docsDir: string | null;
|
|
12
|
+
/** Bundled `llms.txt` table of contents, when present. */
|
|
13
|
+
llmsPath: string | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Walk up from `fromDir` looking for `node_modules/@primer/react-brand`. We resolve from the
|
|
17
|
+
* working directory (not from this server's own location) so that, when launched inside a
|
|
18
|
+
* consumer project, we read their installed version.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveBrandInstall(fromDir?: string): BrandInstall;
|
|
21
|
+
/**
|
|
22
|
+
* To properly disambiguate competing MCP libraries within Primer, we check if the project depends on `@primer/react` (the product UI library) but not Primer Brand.
|
|
23
|
+
* Used to gently steer agents that may have invoked the wrong design system's tools.
|
|
24
|
+
*/
|
|
25
|
+
export declare function looksLikeProductProject(fromDir?: string): boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
const notFound = {
|
|
4
|
+
found: false,
|
|
5
|
+
version: null,
|
|
6
|
+
packageDir: null,
|
|
7
|
+
docsDir: null,
|
|
8
|
+
llmsPath: null,
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Walk up from `fromDir` looking for `node_modules/@primer/react-brand`. We resolve from the
|
|
12
|
+
* working directory (not from this server's own location) so that, when launched inside a
|
|
13
|
+
* consumer project, we read their installed version.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveBrandInstall(fromDir = process.cwd()) {
|
|
16
|
+
let dir = fromDir;
|
|
17
|
+
for (;;) {
|
|
18
|
+
const packageDir = join(dir, 'node_modules', '@primer', 'react-brand');
|
|
19
|
+
if (existsSync(join(packageDir, 'package.json'))) {
|
|
20
|
+
const docsDir = join(packageDir, 'docs');
|
|
21
|
+
const llmsPath = join(packageDir, 'llms.txt');
|
|
22
|
+
return {
|
|
23
|
+
found: true,
|
|
24
|
+
version: readVersion(join(packageDir, 'package.json')),
|
|
25
|
+
packageDir,
|
|
26
|
+
docsDir: existsSync(docsDir) ? docsDir : null,
|
|
27
|
+
llmsPath: existsSync(llmsPath) ? llmsPath : null,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const parent = dirname(dir);
|
|
31
|
+
if (parent === dir)
|
|
32
|
+
return notFound;
|
|
33
|
+
dir = parent;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* To properly disambiguate competing MCP libraries within Primer, we check if the project depends on `@primer/react` (the product UI library) but not Primer Brand.
|
|
38
|
+
* Used to gently steer agents that may have invoked the wrong design system's tools.
|
|
39
|
+
*/
|
|
40
|
+
export function looksLikeProductProject(fromDir = process.cwd()) {
|
|
41
|
+
if (resolveBrandInstall(fromDir).found)
|
|
42
|
+
return false;
|
|
43
|
+
let dir = fromDir;
|
|
44
|
+
for (;;) {
|
|
45
|
+
if (existsSync(join(dir, 'node_modules', '@primer', 'react', 'package.json')))
|
|
46
|
+
return true;
|
|
47
|
+
const parent = dirname(dir);
|
|
48
|
+
if (parent === dir)
|
|
49
|
+
return false;
|
|
50
|
+
dir = parent;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function readVersion(packageJsonPath) {
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
56
|
+
return parsed.version ?? null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Unreadable manifest -> treat the version as unknown rather than failing discovery.
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=resolve-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-install.js","sourceRoot":"","sources":["../../src/brand/resolve-install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAChD,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,WAAW,CAAA;AAiBvC,MAAM,QAAQ,GAAiB;IAC7B,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,IAAI;CACf,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAkB,OAAO,CAAC,GAAG,EAAE;IACjE,IAAI,GAAG,GAAG,OAAO,CAAA;IACjB,SAAS,CAAC;QACR,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,CAAC,CAAA;QACtE,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;YACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;YAC7C,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;gBACtD,UAAU;gBACV,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBAC7C,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;aACjD,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,QAAQ,CAAA;QACnC,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAkB,OAAO,CAAC,GAAG,EAAE;IACrE,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACpD,IAAI,GAAG,GAAG,OAAO,CAAA;IACjB,SAAS,CAAC;QACR,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;QAC1F,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,KAAK,CAAA;QAChC,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,eAAuB;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAuB,CAAA;QACtF,OAAO,MAAM,CAAC,OAAO,IAAI,IAAI,CAAA;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,qFAAqF;QACrF,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { emptyCatalog } from './types.js';
|
|
4
|
+
export function loadCatalog(logger) {
|
|
5
|
+
// Output next to the compiled output (`dist/catalog.json`) by the build-time generator.
|
|
6
|
+
const path = fileURLToPath(new URL('../catalog.json', import.meta.url));
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
9
|
+
logger.debug(`loaded catalog (${parsed.components.length} components, generated from @primer/react-brand@${parsed.generatedFromVersion})`);
|
|
10
|
+
return parsed;
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
logger.warn(`could not load catalog at ${path}: ${error.message}`);
|
|
14
|
+
// If missing, it falls back to an empty catalog.
|
|
15
|
+
return emptyCatalog();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/catalog/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AACpC,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAA;AAGtC,OAAO,EAAe,YAAY,EAAC,MAAM,YAAY,CAAA;AAErD,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,wFAAwF;IACxF,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACvE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAY,CAAA;QAChE,MAAM,CAAC,KAAK,CACV,mBAAmB,MAAM,CAAC,UAAU,CAAC,MAAM,mDAAmD,MAAM,CAAC,oBAAoB,GAAG,CAC7H,CAAA;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,6BAA6B,IAAI,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAA;QAC7E,iDAAiD;QACjD,OAAO,YAAY,EAAE,CAAA;IACvB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All types associated with the build-generated catalog that ships inside the package.
|
|
3
|
+
* It is the structured, offline source of truth the tools reason over so they never depend on the network for version-sensitive facts.
|
|
4
|
+
*/
|
|
5
|
+
export interface CatalogProp {
|
|
6
|
+
name: string;
|
|
7
|
+
type?: string;
|
|
8
|
+
/** Allowed string-literal values, when the prop is an enum. */
|
|
9
|
+
enum?: string[];
|
|
10
|
+
required?: boolean;
|
|
11
|
+
default?: string;
|
|
12
|
+
/** One-line prop description from its JSDoc, when documented in source. */
|
|
13
|
+
description?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CatalogExample {
|
|
16
|
+
title: string;
|
|
17
|
+
/** Source snippet (JSX), when one could be extracted. */
|
|
18
|
+
code?: string;
|
|
19
|
+
/** Where the snippet came from: an authored JSDoc `@example`, a tested Storybook story, or a docs page. */
|
|
20
|
+
source: 'jsdoc' | 'story' | 'docs';
|
|
21
|
+
/** Storybook story id, when the example comes from a story. */
|
|
22
|
+
storyId?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CatalogComponent {
|
|
25
|
+
/** Public name, e.g. `Hero`. */
|
|
26
|
+
name: string;
|
|
27
|
+
/** Package the component is imported from. */
|
|
28
|
+
module: string;
|
|
29
|
+
/** Compound sub-components, e.g. `['Hero.Heading', 'Hero.Description']`. */
|
|
30
|
+
subcomponents: string[];
|
|
31
|
+
props: CatalogProp[];
|
|
32
|
+
examples: CatalogExample[];
|
|
33
|
+
/** One-line summary for listings, when available. */
|
|
34
|
+
description?: string;
|
|
35
|
+
/** Search keywords from the component's docs page, used to improve relevance ranking. */
|
|
36
|
+
keywords?: string[];
|
|
37
|
+
/** A usage caveat from the component's JSDoc `@remarks` (e.g. a non-obvious prop interaction). */
|
|
38
|
+
note?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface CatalogAsset {
|
|
41
|
+
name: string;
|
|
42
|
+
module: string;
|
|
43
|
+
kind: 'icon' | 'illustration';
|
|
44
|
+
}
|
|
45
|
+
export interface CatalogToken {
|
|
46
|
+
name: string;
|
|
47
|
+
value: string;
|
|
48
|
+
group: string;
|
|
49
|
+
}
|
|
50
|
+
export interface Catalog {
|
|
51
|
+
/** Package whose API this catalog describes. */
|
|
52
|
+
brandPackage: string;
|
|
53
|
+
/** Version of `@primer/react-brand` the catalog was generated from. */
|
|
54
|
+
generatedFromVersion: string;
|
|
55
|
+
generatedAt: string;
|
|
56
|
+
components: CatalogComponent[];
|
|
57
|
+
assets: CatalogAsset[];
|
|
58
|
+
tokens: CatalogToken[];
|
|
59
|
+
}
|
|
60
|
+
export declare function emptyCatalog(): Catalog;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All types associated with the build-generated catalog that ships inside the package.
|
|
3
|
+
* It is the structured, offline source of truth the tools reason over so they never depend on the network for version-sensitive facts.
|
|
4
|
+
*/
|
|
5
|
+
export function emptyCatalog() {
|
|
6
|
+
return {
|
|
7
|
+
brandPackage: '@primer/react-brand',
|
|
8
|
+
generatedFromVersion: 'unknown',
|
|
9
|
+
generatedAt: new Date(0).toISOString(),
|
|
10
|
+
components: [],
|
|
11
|
+
assets: [],
|
|
12
|
+
tokens: [],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/catalog/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+DH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,YAAY,EAAE,qBAAqB;QACnC,oBAAoB,EAAE,SAAS;QAC/B,WAAW,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QACtC,UAAU,EAAE,EAAE;QACd,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,EAAE;KACX,CAAA;AACH,CAAC"}
|