WIP adds doc site

This commit is contained in:
Raven Security
2026-02-02 11:22:37 -06:00
parent 7bbd06a09e
commit 015647f90c
14 changed files with 7686 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
export default new Map();
+1
View File
@@ -0,0 +1 @@
export default new Map();
+199
View File
@@ -0,0 +1,199 @@
declare module 'astro:content' {
export interface RenderResult {
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
headings: import('astro').MarkdownHeading[];
remarkPluginFrontmatter: Record<string, any>;
}
interface Render {
'.md': Promise<RenderResult>;
}
export interface RenderedContent {
html: string;
metadata?: {
imagePaths: Array<string>;
[key: string]: unknown;
};
}
}
declare module 'astro:content' {
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
export type CollectionKey = keyof AnyEntryMap;
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
export type ContentCollectionKey = keyof ContentEntryMap;
export type DataCollectionKey = keyof DataEntryMap;
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
ContentEntryMap[C]
>['slug'];
export type ReferenceDataEntry<
C extends CollectionKey,
E extends keyof DataEntryMap[C] = string,
> = {
collection: C;
id: E;
};
export type ReferenceContentEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}) = string,
> = {
collection: C;
slug: E;
};
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
collection: C;
id: string;
};
/** @deprecated Use `getEntry` instead. */
export function getEntryBySlug<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
// Note that this has to accept a regular string too, for SSR
entrySlug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
/** @deprecated Use `getEntry` instead. */
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
collection: C,
entryId: E,
): Promise<CollectionEntry<C>>;
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
collection: C,
filter?: (entry: CollectionEntry<C>) => entry is E,
): Promise<E[]>;
export function getCollection<C extends keyof AnyEntryMap>(
collection: C,
filter?: (entry: CollectionEntry<C>) => unknown,
): Promise<CollectionEntry<C>[]>;
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter?: LiveLoaderCollectionFilterType<C>,
): Promise<
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
entry: ReferenceContentEntry<C, E>,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
entry: ReferenceDataEntry<C, E>,
): E extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
slug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
collection: C,
id: E,
): E extends keyof DataEntryMap[C]
? string extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]> | undefined
: Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter: string | LiveLoaderEntryFilterType<C>,
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
/** Resolve an array of entry references from the same collection */
export function getEntries<C extends keyof ContentEntryMap>(
entries: ReferenceContentEntry<C, ValidContentEntrySlug<C>>[],
): Promise<CollectionEntry<C>[]>;
export function getEntries<C extends keyof DataEntryMap>(
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
): Promise<CollectionEntry<C>[]>;
export function render<C extends keyof AnyEntryMap>(
entry: AnyEntryMap[C][string],
): Promise<RenderResult>;
export function reference<C extends keyof AnyEntryMap>(
collection: C,
): import('astro/zod').ZodEffects<
import('astro/zod').ZodString,
C extends keyof ContentEntryMap
? ReferenceContentEntry<C, ValidContentEntrySlug<C>>
: ReferenceDataEntry<C, keyof DataEntryMap[C]>
>;
// Allow generic `string` to avoid excessive type errors in the config
// if `dev` is not running to update as you edit.
// Invalid collection names will be caught at build time.
export function reference<C extends string>(
collection: C,
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
>;
type ContentEntryMap = {
};
type DataEntryMap = {
};
type AnyEntryMap = ContentEntryMap & DataEntryMap;
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
infer TData,
infer TEntryFilter,
infer TCollectionFilter,
infer TError
>
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
: { data: never; entryFilter: never; collectionFilter: never; error: never };
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
LiveContentConfig['collections'][C]['schema'] extends undefined
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
: import('astro/zod').infer<
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
>;
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
LiveContentConfig['collections'][C]['loader']
>;
export type ContentConfig = typeof import("../src/content.config.mjs");
export type LiveContentConfig = never;
}
+2
View File
@@ -0,0 +1,2 @@
/// <reference types="astro/client" />
/// <reference path="content.d.ts" />
+13
View File
@@ -34,3 +34,16 @@ yarn-error.log*
# Awesome list submission workspace
submits/
# Astro docs site
site/node_modules/
site/dist/
site/.astro/
site/src/content/docs/skills/
site/src/content/docs/guides/
site/src/content/docs/workflows/
site/src/content/docs/getting-started.md
site/src/content/docs/skills-guide.md
site/src/content/docs/contributing.md
site/src/content/docs/changelog.md
site/src/content/docs/roadmap.md
+7 -1
View File
@@ -1,4 +1,4 @@
.PHONY: dev-link dev-unlink validate test
.PHONY: dev-link dev-unlink validate test site-dev site-build
PLUGIN_NAME := fullstack-dev-skills
VERSION := $(shell python -c "import json; print(json.load(open('version.json'))['version'])")
@@ -47,3 +47,9 @@ validate:
test:
bash scripts/test-makefile.sh
site-dev:
cd site && npm run dev
site-build:
cd site && npm run build
+113
View File
@@ -0,0 +1,113 @@
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
export default defineConfig({
integrations: [
starlight({
title: 'Claude Skills',
description:
'65 specialized skills for Claude Code — progressive disclosure, context engineering, and full-stack coverage.',
customCss: ['./src/styles/custom.css'],
social: [
{
icon: 'github',
label: 'GitHub',
href: 'https://github.com/jeffallan/claude-skills',
},
],
sidebar: [
{ label: 'Home', link: '/' },
{ label: 'Getting Started', link: '/getting-started/' },
{ label: 'Skills Guide', link: '/skills-guide/' },
{
label: 'Guides',
items: [
{ label: 'Workflow Commands', link: '/guides/workflow-commands/' },
{ label: 'Common Ground', link: '/guides/common-ground/' },
{
label: 'Atlassian MCP Setup',
link: '/guides/atlassian-mcp-setup/',
},
{
label: 'Local Development',
link: '/guides/local-development/',
},
],
},
{
label: 'Workflows',
collapsed: true,
autogenerate: { directory: 'workflows' },
},
{
label: 'Language',
collapsed: true,
autogenerate: { directory: 'skills/language' },
},
{
label: 'Backend Frameworks',
collapsed: true,
autogenerate: { directory: 'skills/backend' },
},
{
label: 'Frontend & Mobile',
collapsed: true,
autogenerate: { directory: 'skills/frontend' },
},
{
label: 'Infrastructure & Cloud',
collapsed: true,
autogenerate: { directory: 'skills/infrastructure' },
},
{
label: 'API & Architecture',
collapsed: true,
autogenerate: { directory: 'skills/api-architecture' },
},
{
label: 'Quality & Testing',
collapsed: true,
autogenerate: { directory: 'skills/quality' },
},
{
label: 'DevOps & Operations',
collapsed: true,
autogenerate: { directory: 'skills/devops' },
},
{
label: 'Security',
collapsed: true,
autogenerate: { directory: 'skills/security' },
},
{
label: 'Data & ML',
collapsed: true,
autogenerate: { directory: 'skills/data-ml' },
},
{
label: 'Platform',
collapsed: true,
autogenerate: { directory: 'skills/platform' },
},
{
label: 'Specialized',
collapsed: true,
autogenerate: { directory: 'skills/specialized' },
},
{
label: 'Workflow Skills',
collapsed: true,
autogenerate: { directory: 'skills/workflow' },
},
{
label: 'Project',
items: [
{ label: 'Contributing', link: '/contributing/' },
{ label: 'Changelog', link: '/changelog/' },
{ label: 'Roadmap', link: '/roadmap/' },
],
},
],
}),
],
});
+6857
View File
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
{
"name": "claude-skills-docs",
"type": "module",
"version": "0.0.1",
"scripts": {
"sync": "node scripts/sync-content.mjs",
"dev": "npm run sync && astro dev",
"build": "npm run sync && astro build",
"preview": "astro preview"
},
"dependencies": {
"astro": "^5.7.10",
"@astrojs/starlight": "^0.34.1",
"sharp": "^0.33.5",
"js-yaml": "^4.1.0"
}
}
+382
View File
@@ -0,0 +1,382 @@
#!/usr/bin/env node
/**
* sync-content.mjs
*
* Pre-build script that transforms repo-root content into Starlight-compatible
* pages under site/src/content/docs/. Run via `npm run sync`.
*/
import fs from 'node:fs';
import path from 'node:path';
import yaml from 'js-yaml';
const ROOT = path.resolve(import.meta.dirname, '..', '..');
const DOCS_DIR = path.resolve(import.meta.dirname, '..', 'src', 'content', 'docs');
const GITHUB_BLOB = 'https://github.com/jeffallan/claude-skills/blob/main';
// ─── Domain label mapping ───────────────────────────────────────────
const DOMAIN_LABELS = {
language: 'Language',
backend: 'Backend Frameworks',
frontend: 'Frontend & Mobile',
infrastructure: 'Infrastructure & Cloud',
'api-architecture': 'API & Architecture',
quality: 'Quality & Testing',
devops: 'DevOps & Operations',
security: 'Security',
'data-ml': 'Data & ML',
platform: 'Platform',
specialized: 'Specialized',
workflow: 'Workflow Skills',
};
// ─── Role → sidebar badge ───────────────────────────────────────────
const ROLE_BADGES = {
specialist: { text: 'Specialist', variant: 'default' },
expert: { text: 'Expert', variant: 'success' },
architect: { text: 'Architect', variant: 'caution' },
engineer: { text: 'Engineer', variant: 'note' },
};
// ─── Core docs mapping (source relative to ROOT → dest relative to DOCS_DIR)
const CORE_DOCS = [
{ src: 'QUICKSTART.md', dest: 'getting-started.md', title: 'Getting Started' },
{ src: 'SKILLS_GUIDE.md', dest: 'skills-guide.md', title: 'Skills Guide' },
{ src: 'CONTRIBUTING.md', dest: 'contributing.md', title: 'Contributing' },
{ src: 'CHANGELOG.md', dest: 'changelog.md', title: 'Changelog' },
{ src: 'ROADMAP.md', dest: 'roadmap.md', title: 'Roadmap' },
];
// ─── Guide docs mapping ─────────────────────────────────────────────
const GUIDE_DOCS = [
{ src: 'docs/WORKFLOW_COMMANDS.md', dest: 'guides/workflow-commands.md', title: 'Workflow Commands' },
{ src: 'docs/COMMON_GROUND.md', dest: 'guides/common-ground.md', title: 'Common Ground' },
{ src: 'docs/ATLASSIAN_MCP_SETUP.md', dest: 'guides/atlassian-mcp-setup.md', title: 'Atlassian MCP Setup' },
{ src: 'docs/local_skill_development.md', dest: 'guides/local-development.md', title: 'Local Development' },
];
// ─── Link rewrite map (built during sync) ───────────────────────────
const linkMap = new Map();
function buildLinkMap() {
// Core docs
for (const { src, dest } of CORE_DOCS) {
const slug = dest.replace(/\.md$/, '');
addLinkVariants(src, `/${slug}/`);
}
// Guide docs
for (const { src, dest } of GUIDE_DOCS) {
const slug = dest.replace(/\.md$/, '');
addLinkVariants(src, `/${slug}/`);
}
}
function addLinkVariants(srcPath, siteUrl) {
const variants = [
srcPath,
`./${srcPath}`,
path.basename(srcPath),
];
for (const v of variants) {
linkMap.set(v, siteUrl);
}
}
// ─── Helpers ─────────────────────────────────────────────────────────
function ensureDir(dirPath) {
fs.mkdirSync(dirPath, { recursive: true });
}
function stripFrontmatter(content) {
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
if (match) return { frontmatter: match[1], body: match[2] };
return { frontmatter: null, body: content };
}
function parseFrontmatter(content) {
const { frontmatter, body } = stripFrontmatter(content);
const data = frontmatter ? yaml.load(frontmatter) : {};
return { data, body };
}
function extractH1(body) {
const match = body.match(/^#\s+(.+)$/m);
return match ? match[1].trim() : null;
}
function removeH1(body) {
return body.replace(/^#\s+.+\n*/m, '');
}
function starlightFrontmatter(fields) {
const fm = yaml.dump(fields, { lineWidth: -1, quotingType: '"' });
return `---\n${fm}---\n`;
}
function rewriteLinks(body) {
// Rewrite markdown links [text](url) using linkMap
return body.replace(/\[([^\]]*)\]\(([^)]+)\)/g, (_match, text, url) => {
// Skip external URLs and anchors
if (url.startsWith('http') || url.startsWith('#')) return _match;
// Strip anchor from URL for lookup, preserve anchor
const [urlPath, anchor] = url.split('#');
const resolved = linkMap.get(urlPath) || linkMap.get(urlPath.replace(/^\.\//, ''));
if (resolved) {
const suffix = anchor ? `#${anchor}` : '';
return `[${text}](${resolved}${suffix})`;
}
return _match;
});
}
function stripHtmlCommentTags(body) {
// Remove <!-- SKILL_COUNT -->65<!-- /SKILL_COUNT --> style tags, keep inner text
return body.replace(/<!--\s*\w+\s*-->(\w+)<!--\s*\/\w+\s*-->/g, '$1');
}
// ─── Clean synced content ────────────────────────────────────────────
function cleanSyncedContent() {
const syncedDirs = ['skills', 'guides', 'workflows'];
for (const dir of syncedDirs) {
const full = path.join(DOCS_DIR, dir);
if (fs.existsSync(full)) {
fs.rmSync(full, { recursive: true });
}
}
const syncedFiles = CORE_DOCS.map((d) => d.dest);
for (const file of syncedFiles) {
const full = path.join(DOCS_DIR, file);
if (fs.existsSync(full)) {
fs.unlinkSync(full);
}
}
}
// ─── Sync core docs ─────────────────────────────────────────────────
function syncCoreDocs() {
for (const { src, dest, title } of CORE_DOCS) {
const srcPath = path.join(ROOT, src);
if (!fs.existsSync(srcPath)) {
console.warn(` SKIP ${src} (not found)`);
continue;
}
let content = fs.readFileSync(srcPath, 'utf-8');
const { body: rawBody } = stripFrontmatter(content);
let body = removeH1(rawBody);
body = stripHtmlCommentTags(body);
body = rewriteLinks(body);
// Remove GitHub-specific HTML (badges, images, typing SVGs)
body = body.replace(/<p align="center">[\s\S]*?<\/p>/g, '');
const fm = starlightFrontmatter({ title });
const destPath = path.join(DOCS_DIR, dest);
ensureDir(path.dirname(destPath));
fs.writeFileSync(destPath, fm + '\n' + body.trim() + '\n');
console.log(` ${src}${dest}`);
}
}
// ─── Sync guide docs ────────────────────────────────────────────────
function syncGuideDocs() {
for (const { src, dest, title } of GUIDE_DOCS) {
const srcPath = path.join(ROOT, src);
if (!fs.existsSync(srcPath)) {
console.warn(` SKIP ${src} (not found)`);
continue;
}
let content = fs.readFileSync(srcPath, 'utf-8');
const { body: rawBody } = stripFrontmatter(content);
let body = removeH1(rawBody);
body = stripHtmlCommentTags(body);
body = rewriteLinks(body);
const fm = starlightFrontmatter({ title });
const destPath = path.join(DOCS_DIR, dest);
ensureDir(path.dirname(destPath));
fs.writeFileSync(destPath, fm + '\n' + body.trim() + '\n');
console.log(` ${src}${dest}`);
}
}
// ─── Sync workflow docs ─────────────────────────────────────────────
function syncWorkflowDocs() {
const workflowDir = path.join(ROOT, 'docs', 'workflow');
if (!fs.existsSync(workflowDir)) {
console.warn(' SKIP docs/workflow/ (not found)');
return;
}
const files = fs.readdirSync(workflowDir).filter((f) => f.endsWith('.md'));
for (const file of files) {
const srcPath = path.join(workflowDir, file);
let content = fs.readFileSync(srcPath, 'utf-8');
const { body: rawBody } = stripFrontmatter(content);
const h1 = extractH1(rawBody);
let body = removeH1(rawBody);
body = stripHtmlCommentTags(body);
body = rewriteLinks(body);
const title = h1 || file.replace(/\.md$/, '').replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
const fm = starlightFrontmatter({ title });
const destPath = path.join(DOCS_DIR, 'workflows', file);
ensureDir(path.dirname(destPath));
fs.writeFileSync(destPath, fm + '\n' + body.trim() + '\n');
console.log(` docs/workflow/${file} → workflows/${file}`);
}
}
// ─── Build skill domain index (for related-skills linking) ──────────
function buildSkillIndex() {
const index = new Map(); // name → { domain, title }
const skillsDir = path.join(ROOT, 'skills');
const dirs = fs.readdirSync(skillsDir).filter((d) =>
fs.statSync(path.join(skillsDir, d)).isDirectory()
);
for (const dir of dirs) {
const skillPath = path.join(skillsDir, dir, 'SKILL.md');
if (!fs.existsSync(skillPath)) continue;
const content = fs.readFileSync(skillPath, 'utf-8');
const { data, body } = parseFrontmatter(content);
const domain = data.metadata?.domain || 'specialized';
const title = extractH1(body) || dir.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
index.set(dir, { domain, title });
}
return index;
}
// ─── Sync skill pages ───────────────────────────────────────────────
function syncSkillPages(skillIndex) {
const skillsDir = path.join(ROOT, 'skills');
const dirs = fs.readdirSync(skillsDir).filter((d) =>
fs.statSync(path.join(skillsDir, d)).isDirectory()
);
let count = 0;
for (const dir of dirs) {
const skillPath = path.join(skillsDir, dir, 'SKILL.md');
if (!fs.existsSync(skillPath)) continue;
const content = fs.readFileSync(skillPath, 'utf-8');
const { data, body: rawBody } = parseFrontmatter(content);
const domain = data.metadata?.domain || 'specialized';
const role = data.metadata?.role || 'specialist';
const scope = data.metadata?.scope || '';
const outputFormat = data.metadata?.['output-format'] || '';
const triggers = data.metadata?.triggers || '';
const relatedSkills = data.metadata?.['related-skills'] || '';
const description = data.description || '';
const h1 = extractH1(rawBody);
const title = h1 || dir.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
let body = removeH1(rawBody);
// Build metadata table
const metaRows = [];
if (domain) metaRows.push(`| **Domain** | ${DOMAIN_LABELS[domain] || domain} |`);
if (role) metaRows.push(`| **Role** | ${role} |`);
if (scope) metaRows.push(`| **Scope** | ${scope} |`);
if (outputFormat) metaRows.push(`| **Output** | ${outputFormat} |`);
let metaBlock = '';
if (metaRows.length) {
metaBlock = `| | |\n|---|---|\n${metaRows.join('\n')}\n\n`;
}
// Triggers
let triggersBlock = '';
if (triggers) {
triggersBlock = `**Triggers:** ${triggers}\n\n`;
}
// Related skills with links
let relatedBlock = '';
if (relatedSkills) {
const names = relatedSkills.split(',').map((s) => s.trim()).filter(Boolean);
const links = names.map((name) => {
const info = skillIndex.get(name);
if (info) {
return `[${info.title}](/skills/${info.domain}/${name}/)`;
}
return name;
});
relatedBlock = `> **Related Skills:** ${links.join(' · ')}\n\n`;
}
// Rewrite reference table links to GitHub blob URLs
body = body.replace(
/`references\/([^`]+)`/g,
(_match, refPath) =>
`[references/${refPath}](${GITHUB_BLOB}/skills/${dir}/references/${refPath})`
);
body = rewriteLinks(body);
// Build sidebar badge
const badge = ROLE_BADGES[role] || ROLE_BADGES.specialist;
// Assemble frontmatter
const fm = starlightFrontmatter({
title,
description,
sidebar: { badge },
});
// Assemble page
const page = fm + '\n' + metaBlock + triggersBlock + relatedBlock + body.trim() + '\n';
const destPath = path.join(DOCS_DIR, 'skills', domain, `${dir}.md`);
ensureDir(path.dirname(destPath));
fs.writeFileSync(destPath, page);
count++;
}
console.log(` ${count} skill pages synced`);
}
// ─── Main ────────────────────────────────────────────────────────────
function main() {
console.log('sync-content: starting...');
buildLinkMap();
console.log('Cleaning synced content...');
cleanSyncedContent();
console.log('Syncing core docs...');
syncCoreDocs();
console.log('Syncing guide docs...');
syncGuideDocs();
console.log('Syncing workflow docs...');
syncWorkflowDocs();
console.log('Building skill index...');
const skillIndex = buildSkillIndex();
console.log('Syncing skill pages...');
syncSkillPages(skillIndex);
console.log('sync-content: done.');
}
main();
+7
View File
@@ -0,0 +1,7 @@
import { defineCollection } from 'astro:content';
import { docsLoader } from '@astrojs/starlight/loaders';
import { docsSchema } from '@astrojs/starlight/schema';
export const collections = {
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
};
+44
View File
@@ -0,0 +1,44 @@
---
title: Claude Skills
description: 65 specialized skills for Claude Code — progressive disclosure, context engineering, and full-stack coverage.
template: splash
hero:
tagline: Transform Claude Code into your expert pair programmer across the entire development stack.
actions:
- text: Get Started
link: /getting-started/
icon: right-arrow
variant: primary
- text: Browse Skills
link: /skills-guide/
variant: minimal
---
import { Card, CardGrid } from '@astrojs/starlight/components';
<CardGrid stagger>
<Card title="65 Skills" icon="puzzle">
Specialized skills covering 12 domains — languages, frameworks, infrastructure, security, DevOps, data/ML, and more.
</Card>
<Card title="9 Workflows" icon="rocket">
Project workflow commands managing epics from discovery through retrospectives, with Jira and Confluence integration.
</Card>
<Card title="357 References" icon="open-book">
Deep-dive reference files loaded on-demand for surgical precision when context requires it.
</Card>
<Card title="Progressive Disclosure" icon="magnifier">
Lean 80-line skill cores with routing tables to detailed references — 50% token reduction, faster initial responses.
</Card>
</CardGrid>
## Quick Install
```bash
/plugin marketplace add jeffallan/claude-skills
```
```bash
/plugin install fullstack-dev-skills@jeffallan
```
See the [Getting Started guide](/getting-started/) for all installation methods and first steps.
+40
View File
@@ -0,0 +1,40 @@
/* Purple-to-cyan theme matching social preview */
:root {
--sl-color-accent-low: #ede9fe;
--sl-color-accent: #7c3aed;
--sl-color-accent-high: #4c1d95;
--sl-color-white: #1a1a2e;
--sl-color-gray-1: #3d3d5c;
--sl-color-gray-2: #53536e;
--sl-color-gray-3: #8888a0;
--sl-color-gray-4: #b8b8cc;
--sl-color-gray-5: #d4d4e0;
--sl-color-gray-6: #ededf4;
--sl-color-black: #fafafe;
}
:root[data-theme='dark'] {
--sl-color-accent-low: #1e1048;
--sl-color-accent: #8b5cf6;
--sl-color-accent-high: #c4b5fd;
--sl-color-white: #fafafe;
--sl-color-gray-1: #ededf4;
--sl-color-gray-2: #d4d4e0;
--sl-color-gray-3: #8888a0;
--sl-color-gray-4: #53536e;
--sl-color-gray-5: #3d3d5c;
--sl-color-gray-6: #1a1a2e;
--sl-color-black: #0f0f1a;
}
/* Gradient accent bar under site header */
header.header {
border-bottom: 2px solid transparent;
border-image: linear-gradient(90deg, #7c3aed, #6366f1, #3b82f6, #0ea5e9) 1;
}
/* Skill metadata tables: tighter layout */
.sl-markdown-content table:first-of-type {
margin-top: 0.5rem;
}
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "astro/tsconfigs/strict"
}