Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions app/changelog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ const REPO = "https://github.com/CommandOSSLabs/ai-devkit";
const SITE = "https://skills.commandoss.com";

const ENTRIES: ChangelogEntry[] = [
{
date: "August 29, 2026",
shortDate: "Aug 29",
title: "A skills map that tells you what to do next",
items: [
"The **Skills catalog** is category-first and much more compact — a card carries the handle, the title and what the skill is for, so choosing a workflow no longer means reading 34 descriptions.",
"Everything now says **uses** and **used by** instead of references and referenced by, on the map, in the list, on the detail page and in the catalog. A skill that uses another is the one pulling it in.",
"Before you pick anything the map offers a way in: **jump to a group**, or start from one of the skills nothing else depends on.",
"**Visualize interactions** is a pan-and-zoom canvas now. Move skills around, trace what each one references and what references it, and reset to the canonical layout whenever you want.",
"The map opens at a size you can actually read, and **`F` gives it the whole screen** — navigation, header and stats out of the way, inspector as an overlay. `Esc` comes back, `R` resets the layout.",
"Every relationship is still available as an accessible **List view**, which is also what smaller screens get by default.",
"**Skill detail, the workspace, Preview, Source, Edit and browser-local drafts** are all still there — this changed how you find a skill, not what you can do with it.",
`[$ gh pr view 24](${REPO}/pull/24)`,
],
},
{
date: "August 14, 2026",
shortDate: "Aug 14",
Expand Down
91 changes: 91 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,20 @@
--skill-edge-in: #B4126B;
}

/* The map is the one surface that has to sit BELOW the page rather than above
it. Everything else here is a panel lifted off the background; a canvas is a
space you look into, and the dot grid that says so only reads against
something close to black. Opaque on purpose: the animated wash behind the
shell was showing through the translucent panel, lifting the surface past
the dots and flattening the whole map. */
.skills-shell {
--skill-canvas: #FFFFFF;
--skill-dot: #CBD5E1;
}

.dark .skills-shell {
--skill-canvas: #050608;
--skill-dot: #2F3742;
--accent: #82AAFF;
--text-tertiary: #7C8593;
--text-disabled: #6E7684;
Expand Down Expand Up @@ -273,3 +286,81 @@
background-color: var(--text-disabled);
}
}

/* React Flow ships a light-mode chrome. These map its controls and minimap
onto the shell's own tokens so the canvas does not become the one panel on
the page with a different set of colours. */
.skills-shell .react-flow__controls {
box-shadow: none;
border: 1px solid var(--border-subtle);
border-radius: 8px;
overflow: hidden;
}

.skills-shell .react-flow__controls-button {
background: var(--bg-surface);
border-bottom: 1px solid var(--border-subtle);
color: var(--text-secondary);
width: 26px;
height: 26px;
}

.skills-shell .react-flow__controls-button:hover {
background: var(--bg-elevated);
color: var(--text-primary);
}

.skills-shell .react-flow__controls-button svg {
fill: currentColor;
}

.skills-shell .react-flow__minimap {
border: 1px solid var(--border-subtle);
border-radius: 8px;
}

/* Styled here rather than through the nodeColor prop: that prop is read once
into an SVG fill and does not follow a theme change, while a class does. */
.skills-shell .react-flow__minimap-node {
fill: var(--skill-node);
stroke: none;
}

.skills-shell .react-flow__attribution {
display: none;
}

/* Canvas focus mode. The graph does not move in the tree and is never
unmounted: the shell's own chrome collapses out of its way from a single
attribute on <html>, so the canvas grows into the padding, the rail and the
header instead of being re-created inside a fixed overlay. Unlayered, so it
beats the Tailwind utilities it is overriding regardless of specificity. */
html[data-skills-focus] .skills-shell {
padding: 0;
gap: 0;
}

html[data-skills-focus] .skills-shell > aside {
display: none;
}

/* backdrop-filter on this column makes it the containing block for every
fixed descendant. Dropping it in focus mode is what lets the inspector
drawer position against the viewport, and the column covers the whole
screen by then anyway, so there is nothing left to blur. */
html[data-skills-focus] .skills-column {
border: 0;
border-radius: 0;
backdrop-filter: none;
background: var(--skill-canvas);
}

html[data-skills-focus] .skills-topbar,
html[data-skills-focus] .skills-page-head,
html[data-skills-focus] .skills-backdrop {
display: none;
}

html[data-skills-focus] .skills-content-pad {
padding: 0;
}
6 changes: 3 additions & 3 deletions app/skills/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export default function SkillsLayout({
<SkillsIconRail />
</aside>

<div className="relative z-10 flex min-w-0 flex-1 flex-col overflow-hidden rounded-2xl border border-[var(--border-subtle)] bg-[color-mix(in_srgb,var(--bg-base)_92%,transparent)] backdrop-blur-sm">
<header className="shrink-0 border-b border-[var(--border-subtle)] px-4 py-4 sm:px-6">
<div className="skills-column relative z-10 flex min-w-0 flex-1 flex-col overflow-hidden rounded-2xl border border-[var(--border-subtle)] bg-[color-mix(in_srgb,var(--bg-base)_92%,transparent)] backdrop-blur-sm">
<header className="skills-topbar shrink-0 border-b border-[var(--border-subtle)] px-4 py-4 sm:px-6">
<div className="flex items-center justify-between gap-3">
<Link href="/" className="flex items-center gap-2 lg:hidden">
<div className="flex h-7 w-7 items-center justify-center rounded-md border border-[var(--border-strong)] bg-[var(--bg-surface)]">
Expand Down Expand Up @@ -60,7 +60,7 @@ export default function SkillsLayout({
</header>

<main className="flex min-h-0 flex-1 flex-col overflow-hidden">
<div className="flex min-h-0 flex-1 flex-col p-4 sm:p-6">
<div className="skills-content-pad flex min-h-0 flex-1 flex-col p-4 sm:p-6">
{children}
</div>
</main>
Expand Down
22 changes: 3 additions & 19 deletions app/skills/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { Metadata } from "next";
import { FileText, Layers } from "lucide-react";
import { Layers } from "lucide-react";
import { getCatalogCategories, getSkillCatalog } from "@/lib/skill-catalog";
import { getRepoSnapshot } from "@/lib/repo-snapshot";
import { REPO_SKILLS_TREE } from "@/lib/repo-links";
import { SkillCatalog } from "@/components/skills/skill-catalog";
import { RepoSnapshotChip } from "@/components/skills/repo-snapshot-chip";
import { GooeyTextReveal } from "@/components/motion/gooey-text-reveal";
Expand All @@ -22,7 +21,6 @@ export default function SkillsBrowsePage() {
const skills = getSkillCatalog();
const categories = getCatalogCategories(skills);
const snapshot = getRepoSnapshot();
const fileCount = skills.reduce((n, s) => n + s.files.length, 0);
const handles = Object.fromEntries(skills.map((s) => [s.id, s.handle]));

return (
Expand All @@ -42,19 +40,9 @@ export default function SkillsBrowsePage() {
delay={0.05}
className="flex w-full max-w-[62ch] flex-col gap-1"
>
<h1 className="text-[19px] font-semibold tracking-[-0.02em] text-[var(--text-primary)]">Browse skills</h1>
<h1 className="text-[19px] font-semibold tracking-[-0.02em] text-[var(--text-primary)]">Skills</h1>
<p className="text-[13px] leading-[1.6] text-[var(--text-secondary)]">
Find the right skill for the task you are working on. Every skill in{" "}
<a
href={REPO_SKILLS_TREE}
target="_blank"
rel="noreferrer"
className="text-[color:var(--accent)] underline decoration-[#82AAFF]/30 underline-offset-4 hover:decoration-[#82AAFF]"
>
CommandOSSLabs/ai-devkit
</a>
: what it does, when to reach for it, and what it works with. Open one to read it, or jump straight into its
files.
Find the right workflow, open its files, and adapt it to your repository.
</p>
</GooeyTextReveal>
</div>
Expand All @@ -64,10 +52,6 @@ export default function SkillsBrowsePage() {
<Layers size={13} aria-hidden="true" />
<span>{skills.length} skills</span>
</div>
<div className={metaChipClassName}>
<FileText size={13} aria-hidden="true" />
<span>{fileCount} files</span>
</div>
<RepoSnapshotChip snapshot={snapshot} />
</div>
</div>
Expand Down
58 changes: 18 additions & 40 deletions app/skills/visualize-interactions/page.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,35 @@
import type { Metadata } from "next";
import { Share2, Link2, CircleDot } from "lucide-react";
import { getSkillGraph } from "@/lib/skill-graph";
import { SkillGraphView } from "@/components/skills/skill-graph-view";
import { BlurHighlight } from "@/components/ui/blur-highlight";

export const dynamic = "force-static";

export const metadata: Metadata = {
title: "Visualize interactions · AI DevKit Skills",
title: "How skills connect · AI DevKit Skills",
description:
"How the skills in CommandOSSLabs/ai-devkit reference each other — a graph built from the cmk: handles in every SKILL.md.",
"Which skills to use before, after, or alongside the one you are working on, mapped from the cmk: handles in every SKILL.md.",
};

const metaChipClassName =
"flex h-9 items-center gap-1.5 rounded-lg border border-[var(--border-subtle)] bg-[var(--glass-elevated)] px-3 text-[12.5px] text-[var(--text-secondary)] backdrop-blur-sm";

export default function VisualizeInteractionsPage() {
const graph = getSkillGraph();
const entryPoints = graph.nodes.filter((n) => n.inDegree === 0).length;

return (
<div className="flex h-full min-h-0 flex-col gap-4">
<div className="flex shrink-0 flex-wrap items-center justify-between gap-3">
<div className="min-w-0">
<h1 className="text-lg font-semibold tracking-tight text-[var(--text-primary)]">Visualize interactions</h1>
<BlurHighlight
highlightedBits={["reference each other", "hub-and-spoke"]}
highlightColor="rgba(130, 170, 255, 0.22)"
highlightClassName="rounded-[3px] px-0.5"
blurAmount={6}
blurDuration={0.7}
highlightDelay={0.5}
viewportOptions={{ once: true, amount: 0.4 }}
className="mt-1 text-[13px] leading-6 text-[var(--text-secondary)]"
>
Skills reference each other by their cmk: handle, and the result is hub-and-spoke rather than a flat list.
</BlurHighlight>
</div>

<div className="flex flex-wrap items-center gap-2">
<div className={metaChipClassName}>
<CircleDot size={13} />
<span>{graph.nodes.length} skills</span>
</div>
<div className={metaChipClassName}>
<Link2 size={13} />
<span>{graph.edges.length} references</span>
</div>
<div className={metaChipClassName}>
<Share2 size={13} />
<span>{entryPoints} entry points</span>
</div>
</div>
<div className="flex h-full min-h-0 flex-col gap-3">
{/* Two lines and one row of counts. Three stat cards used to sit above
the map at the same weight as the controls, which is backwards on a
page whose subject is the map. The entry-point count left this line
entirely: it is something to act on, so it lives in the start panel
where it can be clicked, not in a statistic. */}
<div className="skills-page-head shrink-0">
<h1 className="text-lg font-semibold tracking-tight text-[var(--text-primary)]">
How skills connect
</h1>
<p className="mt-1 max-w-[66ch] text-[13px] leading-6 text-[var(--text-secondary)]">
See which skills to use before, after, or alongside the one you are working on.
</p>
<p className="mt-1.5 font-mono text-[11.5px] tabular-nums text-[var(--text-tertiary)]">
{graph.nodes.length} skills · {graph.edges.length} connections
</p>
</div>

<SkillGraphView graph={graph} />
Expand Down
24 changes: 13 additions & 11 deletions components/marketing/roadmap/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ const REPO = "https://github.com/CommandOSSLabs/ai-devkit";

export const ROADMAP_ITEMS: RoadmapItem[] = [
{
id: "browse-skills-nav",
id: "skills-explorer-canvas",
status: "now",
category: "Website",
title: "Browse Skills nav + loading transition",
description: "A dedicated nav button to browse skills, with a TextMorph transition while the list loads.",
pr: `${REPO}/pull/15`,
title: "Minimal skills explorer + skill relationship map",
description: "A quieter category-first catalog and a map of which skills to use before, after or alongside each other, with a full-viewport focus mode for reading it.",
pr: `${REPO}/pull/24`,
},
{
id: "repo-meta-rate-limit",
Expand All @@ -68,13 +68,6 @@ export const ROADMAP_ITEMS: RoadmapItem[] = [
description: "Stops hitting GitHub's rate limit on repo metadata calls and removes a dead WebGL scroll loop left running behind it.",
pr: `${REPO}/pull/13`,
},
{
id: "skill-detail-editor",
status: "now",
category: "Website",
title: "Skill detail view, styled like an editor",
description: "A code-editor-styled view for browsing a skill's own details, instead of the current layout. No PR yet — still in draft.",
},
{
id: "cmk-visualize",
status: "next",
Expand All @@ -83,6 +76,15 @@ export const ROADMAP_ITEMS: RoadmapItem[] = [
description: "Turns content you already have — a design doc, a tracker query, a raw description — into a rendered diagram, slide deck, or animated teaser, instead of a wall of markdown. One target shape: an isometric map of a repo's own infrastructure, dependencies and data paths traced from the real code, citing the files it read.",
link: { label: "reference: isometric repo-map prompt", url: "https://x.com/JayScambler/status/2088356230968287547" },
},
{
id: "skills-dashboard",
status: "shipped",
category: "Website",
shippedDate: "Aug 29",
title: "Skills dashboard and local workspace",
description: "Browse, inspect and locally edit all skills through catalog, detail and code-editor-style workspace views.",
ref: { label: "c29ea93", url: `${REPO}/commit/c29ea93` },
},
{
id: "cmk-interpret",
status: "shipped",
Expand Down
8 changes: 7 additions & 1 deletion components/motion/gooey-text-reveal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,13 @@ export const GooeyTextReveal = React.forwardRef<HTMLDivElement, GooeyTextRevealP
const split = SplitText.create(target, {
type: "lines",
linesClass: "gooey-text-reveal-line",
aria: "auto",
// "auto" puts aria-label on the target and aria-hidden on every
// line it generates. On a heading that is fine; on a <p> the label
// is prohibited on the paragraph role, so assistive technology can
// drop it and then find nothing but hidden children — a paragraph
// that reads as empty. Splitting by line keeps whole words in the
// DOM, so the text is announced correctly with no aria at all.
aria: "none",
});

split.lines.forEach((line) => {
Expand Down
7 changes: 6 additions & 1 deletion components/skills/eval-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ export function EvalView({ cases }: { cases: EvalCase[] }) {
const totalAssertions = cases.reduce((n, c) => n + (c.assertions?.length ?? 0), 0);

return (
<div className="min-h-0 flex-1 overflow-y-auto px-6 py-6 sm:px-10">
<div
tabIndex={0}
role="region"
aria-label="Evaluation scenarios"
className="min-h-0 flex-1 overflow-y-auto px-6 py-6 outline-none focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--accent)] sm:px-10"
>
<div className="mx-auto flex max-w-[760px] flex-col gap-6">
<div className="flex flex-wrap items-center gap-2 text-[11.5px] text-[var(--text-tertiary)]">
<span className="rounded-full bg-[var(--glass-elevated)] px-2.5 py-1">
Expand Down
Loading
Loading