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
31 changes: 31 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Check

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
check:
name: check
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Setup pnpm
uses: pnpm/action-setup@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24.x

- name: Check plugin package
run: pnpm check
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
.DS_Store

node_modules/
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Start a new Codex thread after installing so the plugin skill and MCP server are
```text
.claude-plugin/marketplace.json Claude Code marketplace catalog
.agents/plugins/marketplace.json Codex marketplace catalog
package.json Package metadata and check command
plugins/samebase/ Shared Claude Code and Codex plugin
```

Expand All @@ -99,5 +100,5 @@ declaration, icon, and skill tree, so the workflow cannot drift between Codex an
Validate the complete package with:

```bash
node scripts/check.mjs
pnpm check
```
13 changes: 13 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "samebase-agent-plugins",
"version": "0.2.0",
"private": true,
"type": "module",
"engines": {
"node": ">=24"
},
"packageManager": "pnpm@11.0.8",
"scripts": {
"check": "node ./scripts/check.ts"
}
}
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 49 additions & 19 deletions scripts/check.mjs → scripts/check.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,81 @@
import { existsSync, readFileSync } from "node:fs";
import { existsSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

import codexMarketplace from "../.agents/plugins/marketplace.json" with { type: "json" };
import claudeMarketplace from "../.claude-plugin/marketplace.json" with { type: "json" };
import packageMetadata from "../package.json" with { type: "json" };
import claudeManifest from "../plugins/samebase/.claude-plugin/plugin.json" with {
type: "json",
};
import codexManifest from "../plugins/samebase/.codex-plugin/plugin.json" with {
type: "json",
};
import mcp from "../plugins/samebase/.mcp.json" with { type: "json" };

const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const pluginRoot = resolve(root, "plugins/samebase");

function readJson(path) {
return JSON.parse(readFileSync(path, "utf8"));
}

function check(condition, message) {
function check(condition: unknown, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}

function checkRelativePath(pluginRoot, relativePath, label) {
function checkRelativePath(
baseRoot: string,
relativePath: unknown,
label: string,
): asserts relativePath is string {
check(typeof relativePath === "string", `${label} must be a relative path.`);
check(
existsSync(resolve(pluginRoot, relativePath)),
existsSync(resolve(baseRoot, relativePath)),
`${label} does not resolve: ${relativePath}`,
);
}

const codexManifest = readJson(resolve(pluginRoot, ".codex-plugin/plugin.json"));
const claudeManifest = readJson(resolve(pluginRoot, ".claude-plugin/plugin.json"));
const codexMarketplace = readJson(resolve(root, ".agents/plugins/marketplace.json"));
const claudeMarketplace = readJson(resolve(root, ".claude-plugin/marketplace.json"));
const mcp = readJson(resolve(pluginRoot, ".mcp.json"));

check(codexManifest.version === claudeManifest.version, "Codex and Claude versions must match.");
check(
packageMetadata.version === codexManifest.version,
"The package version must match the Codex manifest.",
);
check(
packageMetadata.version === claudeManifest.version,
"The package version must match the Claude manifest.",
);
check(
packageMetadata.version === claudeMarketplace.plugins[0]?.version,
"The package version must match the Claude marketplace.",
);
check(
codexManifest.version === claudeManifest.version,
"Codex and Claude versions must match.",
);
check(
claudeMarketplace.plugins[0]?.version === claudeManifest.version,
"The Claude marketplace version must match the plugin manifests.",
);
check(mcp.mcpServers?.samebase?.url === "https://api.samebase.com/mcp", "Unexpected MCP endpoint.");
check(
mcp.mcpServers?.samebase?.url === "https://api.samebase.com/mcp",
"Unexpected MCP endpoint.",
);

const codexMarketplaceSource = codexMarketplace.plugins[0]?.source;
check(codexMarketplaceSource?.source === "local", "The Codex marketplace source must be local.");
check(
codexMarketplaceSource?.source === "local",
"The Codex marketplace source must be local.",
);
checkRelativePath(root, codexMarketplaceSource.path, "Codex marketplace source");
checkRelativePath(root, claudeMarketplace.plugins[0]?.source, "Claude marketplace source");
checkRelativePath(
root,
claudeMarketplace.plugins[0]?.source,
"Claude marketplace source",
);
check(
resolve(root, codexMarketplaceSource.path) === pluginRoot,
"The Codex marketplace must install the shared plugin directory.",
);
check(
resolve(root, claudeMarketplace.plugins[0]?.source) === pluginRoot,
resolve(root, claudeMarketplace.plugins[0].source) === pluginRoot,
"The Claude marketplace must install the shared plugin directory.",
);
checkRelativePath(pluginRoot, codexManifest.skills, "Codex skills path");
Expand Down