mirror of
https://github.com/CharlesWiltgen/Axiom.git
synced 2026-09-20 19:58:20 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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' },
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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}`);
|
||||
Reference in New Issue
Block a user