feat: add Codex plugin build script and installation docs

- scripts/build-codex.ts copies 164 skills (excluding routers) and
  generates .codex-plugin/plugin.json + per-skill agents/openai.yaml
- npm run build:codex runs the build
- docs/guide/codex-install.md covers local marketplace installation
- axiom-codex/ output is gitignored (generated artifact)
This commit is contained in:
Charles Wiltgen
2026-03-29 11:22:13 -07:00
parent 6862dd8f8c
commit 2b5d6adbc2
5 changed files with 298 additions and 1 deletions
+3
View File
@@ -25,6 +25,9 @@ axiom-mcp/node_modules/
axiom-mcp/dist/
axiom-mcp/*.tsbuildinfo
# Codex plugin build output (generated by scripts/build-codex.ts)
axiom-codex/
# Node modules and dependencies
node_modules/
yarn.lock
+1
View File
@@ -44,6 +44,7 @@ export default withMermaid(defineConfig({
{ text: 'Overview', link: '/guide/' },
{ text: 'Quick Start', link: '/guide/quick-start' },
{ text: 'MCP Server', link: '/guide/mcp-install' },
{ text: 'Codex Plugin', link: '/guide/codex-install' },
{ text: 'Xcode Integration', link: '/guide/xcode-setup' },
{ text: 'Example Workflows', link: '/guide/workflows' },
{ text: 'Skill Map', link: '/guide/skill-map' },
+125
View File
@@ -0,0 +1,125 @@
# Codex Plugin
Axiom is available as a native plugin for OpenAI Codex, bringing its iOS development skills directly into the Codex CLI, web app, and IDE extensions.
## What You Get
The Codex plugin includes 164 specialized skills covering:
- **SwiftUI** — layout, navigation, animations, performance, architecture, debugging
- **Data** — SwiftData, Core Data, GRDB, CloudKit, migrations, Codable
- **Concurrency** — Swift 6, actors, Sendable, async/await, synchronization
- **Performance** — memory leaks, profiling, energy, Instruments workflows
- **Networking** — URLSession, Network.framework, connection diagnostics
- **Build** — Xcode debugging, code signing, build optimization, SPM
- **Integration** — StoreKit, widgets, push notifications, camera, contacts, haptics
- **Apple Intelligence** — Foundation Models, on-device AI, CoreML
- **Accessibility** — VoiceOver, Dynamic Type, WCAG compliance
## Prerequisites
- **Codex CLI** or Codex web app
## Installation
::: info
The Codex plugin marketplace does not yet support third-party submissions. For now, install Axiom locally using one of the methods below. We'll update this page when marketplace publishing is available.
:::
### Option 1: Personal Marketplace (recommended)
Clone the repo and build the plugin:
```bash
git clone https://github.com/CharlesWiltgen/Axiom.git
cd Axiom
npm run build:codex
```
Add to your personal marketplace at `~/.agents/plugins/marketplace.json`:
```json
{
"name": "axiom-local",
"interface": { "displayName": "Axiom (Local)" },
"plugins": [
{
"name": "axiom",
"source": { "source": "local", "path": "/path/to/Axiom/axiom-codex" },
"policy": { "installation": "INSTALLED_BY_DEFAULT" },
"category": "Development"
}
]
}
```
Replace `/path/to/Axiom` with the actual path where you cloned the repo.
### Option 2: Project-Scoped
To make Axiom available only within a specific project, add a marketplace file at your repo root:
```bash
mkdir -p .agents/plugins
```
Create `.agents/plugins/marketplace.json`:
```json
{
"name": "project-plugins",
"interface": { "displayName": "Project Plugins" },
"plugins": [
{
"name": "axiom",
"source": { "source": "local", "path": "/path/to/Axiom/axiom-codex" },
"policy": { "installation": "INSTALLED_BY_DEFAULT" },
"category": "Development"
}
]
}
```
## Usage
Skills activate automatically based on your questions. Just ask:
```
"I'm getting BUILD FAILED in Xcode"
"How do I fix Swift 6 concurrency errors?"
"My app has memory leaks"
"I need to add a database column safely"
```
You can also invoke skills explicitly with `$skill-name` in Codex.
## Updating
Pull the latest changes and rebuild:
```bash
cd /path/to/Axiom
git pull
npm run build:codex
```
The plugin reads skills from disk, so the update takes effect immediately.
## Differences from Claude Code
The Codex plugin includes the same skill content as the Claude Code plugin, with a few differences:
| Feature | Claude Code | Codex |
|---------|-------------|-------|
| Skills | 164 specialized + 17 routers | 164 specialized (Codex has native routing) |
| Agents | 38 autonomous auditors | Not yet supported in Codex plugins |
| Commands | 12 `/axiom:*` commands | Not yet supported in Codex plugins |
| Installation | `/plugin marketplace add` | Local marketplace |
As the Codex plugin system matures, we'll add support for additional features.
## Also Available
- **[Claude Code](/guide/quick-start)** — Native plugin with full agent and command support
- **[MCP Server](/guide/mcp-install)** — Works with VS Code, Cursor, Gemini CLI, and more
- **[Xcode Integration](/guide/xcode-setup)** — Direct Xcode MCP bridge setup
+2 -1
View File
@@ -9,7 +9,8 @@
"docs:preview": "vitepress preview docs",
"test": "deno run --allow-read --allow-run --allow-env scripts/pre-deploy.ts --static",
"test:full": "deno run --allow-read --allow-run --allow-env scripts/pre-deploy.ts",
"predeploy": "deno run --allow-read --allow-run --allow-env scripts/pre-deploy.ts"
"predeploy": "deno run --allow-read --allow-run --allow-env scripts/pre-deploy.ts",
"build:codex": "deno run --allow-read --allow-write scripts/build-codex.ts"
},
"devDependencies": {
"mermaid": "^11.12.2",
+167
View File
@@ -0,0 +1,167 @@
#!/usr/bin/env -S deno run --allow-read --allow-write
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const root = path.dirname(path.dirname(__filename));
const SOURCE_SKILLS = path.join(root, '.claude-plugin/plugins/axiom/skills');
const OUTPUT_DIR = path.join(root, 'axiom-codex');
const OUTPUT_SKILLS = path.join(OUTPUT_DIR, 'skills');
const OUTPUT_MANIFEST = path.join(OUTPUT_DIR, '.codex-plugin');
// Router skills — Codex has native progressive disclosure, so these are unnecessary
const EXCLUDE_SKILLS = new Set([
'axiom-ios-build',
'axiom-ios-testing',
'axiom-ios-ui',
'axiom-ios-data',
'axiom-ios-concurrency',
'axiom-ios-performance',
'axiom-ios-networking',
'axiom-ios-integration',
'axiom-ios-accessibility',
'axiom-ios-ai',
'axiom-ios-ml',
'axiom-ios-vision',
'axiom-ios-graphics',
'axiom-ios-games',
'axiom-apple-docs',
'axiom-xcode-mcp',
'axiom-shipping',
'axiom-using-axiom', // Claude Code-specific discipline injection
]);
// Read version from Claude Code manifest
const ccManifest = JSON.parse(
fs.readFileSync(path.join(root, '.claude-plugin/plugins/axiom/claude-code.json'), 'utf8')
);
const version = ccManifest.version;
// Clean and recreate output
if (fs.existsSync(OUTPUT_DIR)) {
fs.rmSync(OUTPUT_DIR, { recursive: true });
}
fs.mkdirSync(OUTPUT_SKILLS, { recursive: true });
fs.mkdirSync(OUTPUT_MANIFEST, { recursive: true });
// Parse SKILL.md frontmatter (name, description) without external dependencies
function parseFrontmatter(content: string): Record<string, string> {
const match = content.match(/^---\n([\s\S]*?)\n---/);
if (!match) return {};
const fields: Record<string, string> = {};
for (const line of match[1].split('\n')) {
const m = line.match(/^(\w+):\s*(.+)/);
if (m) fields[m[1]] = m[2];
}
return fields;
}
// Known casing for iOS/Apple terms
const CASE_MAP: Record<string, string> = {
swiftui: 'SwiftUI', swiftdata: 'SwiftData', coredata: 'CoreData',
cloudkit: 'CloudKit', storekit: 'StoreKit', spritekit: 'SpriteKit',
scenekit: 'SceneKit', realitykit: 'RealityKit', uikit: 'UIKit',
appkit: 'AppKit', mapkit: 'MapKit', eventkit: 'EventKit',
textkit: 'TextKit', metalkit: 'MetalKit', cryptokit: 'CryptoKit',
lldb: 'LLDB', grdb: 'GRDB', ios: 'iOS', tvos: 'tvOS',
iap: 'IAP', icloud: 'iCloud', hig: 'HIG', ux: 'UX',
sf: 'SF', mcp: 'MCP', asc: 'ASC', tdd: 'TDD',
ref: 'Reference', diag: 'Diagnostics', objc: 'Obj-C',
avfoundation: 'AVFoundation', xctest: 'XCTest', xctrace: 'xctrace',
xclog: 'xclog', sqlitedata: 'SQLiteData', metrickit: 'MetricKit',
alarmkit: 'AlarmKit',
};
// Derive display name: "axiom-swiftui-performance" → "SwiftUI Performance"
function toDisplayName(skillName: string): string {
return skillName
.replace(/^axiom-/, '')
.split('-')
.map(w => CASE_MAP[w] || w.charAt(0).toUpperCase() + w.slice(1))
.join(' ');
}
// Derive short_description from full description
function toShortDescription(description: string): string {
// Strip "Use when" / "Use for" prefix
let short = description.replace(/^Use (?:when|for)\s*/i, '');
// Take up to first period, em dash, or " - " delimiter — but only if we'd keep 20+ chars
const end = short.search(/\.\s|—|\s-\s/);
if (end >= 20) short = short.slice(0, end);
if (short.length > 120) short = short.slice(0, 117) + '...';
// Escape quotes for YAML and trim
short = short.replace(/"/g, '\\"').trim();
return short.charAt(0).toUpperCase() + short.slice(1);
}
// Copy skills and generate openai.yaml
const skillDirs = fs.readdirSync(SOURCE_SKILLS, { withFileTypes: true })
.filter(d => d.isDirectory() && !EXCLUDE_SKILLS.has(d.name));
let copied = 0;
for (const dir of skillDirs) {
const srcSkill = path.join(SOURCE_SKILLS, dir.name, 'SKILL.md');
if (!fs.existsSync(srcSkill)) continue;
const destDir = path.join(OUTPUT_SKILLS, dir.name);
fs.mkdirSync(destDir, { recursive: true });
fs.copyFileSync(srcSkill, path.join(destDir, 'SKILL.md'));
// Generate agents/openai.yaml from frontmatter
const content = fs.readFileSync(srcSkill, 'utf8');
const fm = parseFrontmatter(content);
if (fm.name && fm.description) {
const agentsDir = path.join(destDir, 'agents');
fs.mkdirSync(agentsDir, { recursive: true });
const yaml = [
'interface:',
` display_name: "${toDisplayName(fm.name)}"`,
` short_description: "${toShortDescription(fm.description)}"`,
'',
].join('\n');
fs.writeFileSync(path.join(agentsDir, 'openai.yaml'), yaml);
}
copied++;
}
// Generate plugin.json
const pluginManifest = {
name: 'axiom',
version,
description: 'Battle-tested skills for modern iOS development — SwiftUI, concurrency, data, performance, networking, accessibility, and more.',
author: {
name: 'Charles Wiltgen',
url: 'https://charleswiltgen.github.io/Axiom/',
},
homepage: 'https://charleswiltgen.github.io/Axiom/',
repository: 'https://github.com/CharlesWiltgen/Axiom',
license: 'MIT',
keywords: ['ios', 'swift', 'swiftui', 'xcode', 'apple', 'mobile', 'development'],
skills: './skills/',
interface: {
displayName: 'Axiom',
shortDescription: 'Battle-tested iOS development skills',
longDescription: 'Axiom gives AI coding assistants deep iOS development expertise — preventing data loss from bad migrations, catching memory leaks, diagnosing build failures, and guiding Swift concurrency, SwiftUI, networking, accessibility, and more.',
developerName: 'Charles Wiltgen',
category: 'Development',
capabilities: ['Read'],
websiteURL: 'https://charleswiltgen.github.io/Axiom/',
defaultPrompt: [
'Check my SwiftUI code for performance issues',
'Help me fix this build failure',
'How do I safely add a database column?',
],
},
};
fs.writeFileSync(
path.join(OUTPUT_MANIFEST, 'plugin.json'),
JSON.stringify(pluginManifest, null, 2) + '\n'
);
// Summary
const skipped = EXCLUDE_SKILLS.size;
console.log(`axiom-codex built: ${copied} skills (${skipped} routers excluded), v${version}`);