create-ryos-app
Generates the necessary file structure and registration code for new applications in ryOS.
Install
mkdir -p .claude/skills/create-ryos-app && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7453" && unzip -o skill.zip -d .claude/skills/create-ryos-app && rm skill.zipInstalls to .claude/skills/create-ryos-app
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Create new applications for ryOS following established patterns and conventions. Use when building a new app, adding an application to the desktop, creating app components, or scaffolding app structures.Key capabilities
- →Generates standardized directory hierarchy src/apps/
- →Scaffolds main React component and menu bar boilerplate
- →Creates logic hooks for state management
- →Registers new apps in appRegistry.tsx
- →Generates localization JSON structures
How it works
It uses a template-based file generation script to populate required directories and register metadata in the ryOS global app registry.
Inputs & outputs
When to use create-ryos-app
- →Scaffold a new desktop application
- →Add a menu bar to an existing app
- →Register a new component in the app registry
- →Generate localized app files
About this skill
Creating ryOS Applications
Quick Start Checklist
- [ ] 1. Create app directory: src/apps/[app-name]/
- [ ] 2. Create main component: components/[AppName]AppComponent.tsx
- [ ] 3. Create menu bar: components/[AppName]MenuBar.tsx
- [ ] 4. Create logic hook: hooks/use[AppName]Logic.ts
- [ ] 5. Create metadata: metadata.ts (appMetadata + exactly 6 help items)
- [ ] 6. Create app definition: index.tsx (re-export metadata, declare initialData type)
- [ ] 7. Add icon: choose from existing icons or the resource catalogs, then place active assets under `public/icons/<theme>/[app-name].png`
- [ ] 8. Register the app id: add to appIds + appNames in src/config/appRegistryData.ts
- [ ] 9. Register the app: lazy component + registry entry in src/config/appRegistry.tsx
- [ ] 10. Register help key order in src/hooks/useTranslatedHelpItems.ts
- [ ] 11. Add translation keys to src/lib/locales/en/translation.json
- [ ] 12. Localize (last): add en strings, sync locales; use the localize skill to finish
Directory Structure
src/apps/[app-name]/
├── components/
│ ├── [AppName]AppComponent.tsx # Main component (required)
│ └── [AppName]MenuBar.tsx # Menu bar (required)
├── hooks/
│ └── use[AppName]Logic.ts # Logic hook (recommended)
├── metadata.ts # appMetadata + helpItems (required)
└── index.tsx # App definition: re-export metadata, initialData types (required)
Why metadata lives in its own file
appRegistry.tsx imports appMetadata/helpItems eagerly so the dock, About/Help dialogs, and search can show app info without loading the (lazy) component bundle. Keep these in a tiny metadata.ts that imports nothing heavy. Most current apps follow this split (@/apps/<id>/metadata). index.tsx then re-exports from metadata.ts and is the home for initialData types and any app-specific exported types.
1. Metadata (metadata.ts)
Keep app metadata and help items in metadata.ts so the registry can load them eagerly without pulling in the component.
export const appMetadata = {
name: "[App Name]",
version: "1.0.0",
creator: { name: "Ryo Lu", url: "https://ryo.lu" },
github: "https://github.com/ryokun6/ryos",
icon: "/icons/default/[app-name].png",
};
// Always include exactly 6 help items (icon, title, description each).
export const helpItems = [
{ icon: "🚀", title: "Getting Started", description: "How to use this app" },
{ icon: "📂", title: "Open & Save", description: "Open and save files from the File menu" },
{ icon: "✏️", title: "Editing", description: "Use the Edit menu for cut, copy, paste" },
{ icon: "👁️", title: "View Options", description: "Adjust view and layout from the View menu" },
{ icon: "⌨️", title: "Shortcuts", description: "Use keyboard shortcuts for faster workflows" },
{ icon: "❓", title: "Help & About", description: "Open Help from the Help menu for more info" },
];
App icon sourcing
Before creating a new app icon from scratch, check the active icon themes and the historical icon resource catalogs:
- Look for an existing logical icon in
public/icons/default,public/icons/macosx,public/icons/win98, andpublic/icons/xp. - Search the resource catalogs for historically appropriate source art:
- Mac OS X:
public/resources/macos-icon-catalogs/{panther,tiger}/catalog.md - Windows:
public/resources/windows-icon-catalogs/{win98,xp}/catalog.md
- Mac OS X:
- If a catalog asset is the right source, copy or adapt it into the active icon tree (
public/icons/<theme>/...) instead of referencingpublic/resources/...directly from app metadata. - Add at least
public/icons/default/[app-name].png; add theme-specific variants when the catalog has a better era-matched asset. - Run
bun run generate:iconsafter adding or moving active files underpublic/icons.
Keep public/resources/*-icon-catalogs as source libraries. Do not replace unrelated active icons just because a catalog contains a historical equivalent.
App Definition (index.tsx)
Re-export the metadata and declare any initialData type. This is what other files import as @/apps/[app-name].
export { appMetadata, helpItems } from "./metadata";
// Optional: typed startup payload for launchApp("[app-name]", { ... })
export interface [AppName]InitialData {
// e.g. filePath?: string;
}
2. Main Component ([AppName]AppComponent.tsx)
import { WindowFrame } from "@/components/layout/WindowFrame";
import { [AppName]MenuBar } from "./[AppName]MenuBar";
import { AppProps } from "@/apps/base/types";
import { use[AppName]Logic } from "../hooks/use[AppName]Logic";
import { HelpDialog } from "@/components/dialogs/HelpDialog";
import { AboutDialog } from "@/components/dialogs/AboutDialog";
import { appMetadata } from "..";
export function [AppName]AppComponent({
isWindowOpen,
onClose,
isForeground,
skipInitialSound,
instanceId,
}: AppProps) {
const {
t,
translatedHelpItems,
isHelpDialogOpen,
setIsHelpDialogOpen,
isAboutDialogOpen,
setIsAboutDialogOpen,
isWindowsTheme,
} = use[AppName]Logic({ isWindowOpen, isForeground, instanceId });
const menuBar = (
<[AppName]MenuBar
onClose={onClose}
onShowHelp={() => setIsHelpDialogOpen(true)}
onShowAbout={() => setIsAboutDialogOpen(true)}
/>
);
if (!isWindowOpen) return null;
return (
<>
{!isWindowsTheme && isForeground && menuBar}
<WindowFrame
title={t("apps.[app-name].title")}
onClose={onClose}
isForeground={isForeground}
appId="[app-name]"
skipInitialSound={skipInitialSound}
instanceId={instanceId}
menuBar={isWindowsTheme ? menuBar : undefined}
>
<div className="flex flex-col h-full bg-os-window-bg font-os-ui">
{/* App content */}
</div>
</WindowFrame>
<HelpDialog
isOpen={isHelpDialogOpen}
onOpenChange={setIsHelpDialogOpen}
appId="[app-name]"
helpItems={translatedHelpItems}
/>
<AboutDialog
isOpen={isAboutDialogOpen}
onOpenChange={setIsAboutDialogOpen}
metadata={appMetadata}
appId="[app-name]"
/>
</>
);
}
3. Logic Hook (use[AppName]Logic.ts)
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useTranslatedHelpItems } from "@/hooks/useTranslatedHelpItems";
import { useThemeStore } from "@/stores/useThemeStore";
import { helpItems } from "..";
export function use[AppName]Logic({ instanceId }: { instanceId: string }) {
const { t } = useTranslation();
const translatedHelpItems = useTranslatedHelpItems("[app-name]", helpItems);
const currentTheme = useThemeStore((state) => state.current);
const isWindowsTheme = currentTheme === "xp" || currentTheme === "win98";
const [isHelpDialogOpen, setIsHelpDialogOpen] = useState(false);
const [isAboutDialogOpen, setIsAboutDialogOpen] = useState(false);
return {
t,
translatedHelpItems,
isWindowsTheme,
isHelpDialogOpen,
setIsHelpDialogOpen,
isAboutDialogOpen,
setIsAboutDialogOpen,
};
}
4. Menu Bar ([AppName]MenuBar.tsx)
Match existing app menubars: structure, classes, and spacing.
- Wrapper:
<MenuBar inWindowFrame={isWindowsTheme}>— no extra gap between menus (layout usesspace-x-0). - Trigger:
MenubarTrigger className="text-md px-2 py-1 border-none focus-visible:ring-0". - Content:
MenubarContent align="start" sideOffset={1} className="px-0". - Items:
MenubarItem className="text-md h-6 px-3". - Separators:
MenubarSeparator className="h-[2px] bg-black my-1".
import { MenuBar } from "@/components/layout/MenuBar";
import {
MenubarMenu,
MenubarTrigger,
MenubarContent,
MenubarItem,
MenubarSeparator,
} from "@/components/ui/menubar";
import { useThemeStore } from "@/stores/useThemeStore";
import { useTranslation } from "react-i18next";
interface [AppName]MenuBarProps {
onClose: () => void;
onShowHelp: () => void;
onShowAbout: () => void;
}
export function [AppName]MenuBar({ onClose, onShowHelp, onShowAbout }: [AppName]MenuBarProps) {
const { t } = useTranslation();
const currentTheme = useThemeStore((state) => state.current);
const isWindowsTheme = currentTheme === "xp" || currentTheme === "win98";
const isMacOSTheme = currentTheme === "macosx";
return (
<MenuBar inWindowFrame={isWindowsTheme}>
<MenubarMenu>
<MenubarTrigger className="text-md px-2 py-1 border-none focus-visible:ring-0">
{t("common.menu.file")}
</MenubarTrigger>
<MenubarContent align="start" sideOffset={1} className="px-0">
<MenubarSeparator className="h-[2px] bg-black my-1" />
<MenubarItem onClick={onClose} className="text-md h-6 px-3">
{t("common.menu.close")}
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger className="text-md px-2 py-1 border-none focus-visible:ring-0">
{t("common.menu.help")}
</MenubarTrigger>
<MenubarContent align="start" sideOffset={1} className="px-0">
<MenubarItem onClick={onShowHelp} className="text-md h-6 px-3">
{t("apps.[app-name].menu.help")}
</MenubarItem>
{!isMacOSTheme && (
<>
<MenubarSeparator className="h-[2px] bg-black my-1" />
<MenubarItem onClick={onShowAbout} className="text-md h-6 px-3">
{t("apps.[app-name].menu.about")}
</MenubarItem>
</>
)}
</MenubarContent>
</MenubarMenu>
</MenuBar>
);
}
5. Register the App ID (appRegistryData.ts)
The AppId union type, dock/search ordering, and store lookups all derive from src/config/appRegistryData.ts. Add the id here first — otherwise appRegistry.tsx (and everything typed agai
Content truncated.
When not to use it
- →For non-ryOS web application development
- →When creating a simple component that does not require app status
Limitations
- →Hard-coded requirement for exactly 6 help items per app
- →Requires manual localization after scaffolding
- →Limited to the specific ryOS directory pattern
How it compares
It automates the mandatory registry and localization boilerplate that is required for every app to function within the ryOS desktop shell.
Compared to similar skills
create-ryos-app side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| create-ryos-app (this skill) | 1 | 6mo | No flags | Beginner |
| zustand | 113 | 2mo | No flags | Intermediate |
| web-artifacts-builder | 49 | 3mo | Review | Intermediate |
| landing-page-guide-v2 | 48 | 8mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by ryokun6
View all by ryokun6 →You might also like
zustand
lobehub
Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.
web-artifacts-builder
anthropics
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.
landing-page-guide-v2
bear2u
Create distinctive, high-converting landing pages that combine proven conversion elements with exceptional design quality. Build beautiful, memorable landing pages using Next.js 14+ and ShadCN UI that avoid generic AI aesthetics while following the 11 essential elements framework.
react
lobehub
React component development guide. Use when working with React components (.tsx files), creating UI, using @lobehub/ui components, implementing routing, or building frontend features. Triggers on React component creation, modification, layout implementation, or navigation tasks.
frontend-prompt-generator
gharam1234
Generate structured prompts for frontend development tasks following established patterns. Use when the user requests prompts for wireframes, UI implementation, data binding, or routing functionality in React/Next.js projects with specific formatting requirements (Cursor rules, file paths, test-driven development).
react-dev
davila7
This skill should be used when building React components with TypeScript, typing hooks, handling events, or when React TypeScript, React 19, Server Components are mentioned. Covers type-safe patterns for React 18-19 including generic components, proper event typing, and routing integration (TanStack Router, React Router).