mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
fix(codex): tighten manifest docs and test guards
This commit is contained in:
@@ -26,6 +26,8 @@ codex plugin install affaan-m/everything-claude-code
|
|||||||
|
|
||||||
# Or reference locally during development
|
# Or reference locally during development
|
||||||
codex plugin install ./
|
codex plugin install ./
|
||||||
|
|
||||||
|
Run this from the repository root so `./` points to the repo root and `.mcp.json` resolves correctly.
|
||||||
```
|
```
|
||||||
|
|
||||||
## MCP Servers Included
|
## MCP Servers Included
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
* - .claude-plugin/PLUGIN_SCHEMA_NOTES.md (Claude Code validator rules)
|
* - .claude-plugin/PLUGIN_SCHEMA_NOTES.md (Claude Code validator rules)
|
||||||
* - https://platform.openai.com/docs/codex/plugins (Codex official docs)
|
* - https://platform.openai.com/docs/codex/plugins (Codex official docs)
|
||||||
*
|
*
|
||||||
* Run with: node tests/plugin-manifest.test.js
|
* Run with: node tests/run-all.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -18,7 +18,8 @@ const assert = require('assert');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const repoRoot = path.join(__dirname, '..');
|
const repoRoot = path.resolve(__dirname, '..');
|
||||||
|
const repoRootWithSep = `${repoRoot}${path.sep}`;
|
||||||
|
|
||||||
let passed = 0;
|
let passed = 0;
|
||||||
let failed = 0;
|
let failed = 0;
|
||||||
@@ -35,6 +36,34 @@ function test(name, fn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadJsonObject(filePath, label) {
|
||||||
|
assert.ok(fs.existsSync(filePath), `Expected ${label} to exist`);
|
||||||
|
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||||
|
} catch (error) {
|
||||||
|
assert.fail(`Expected ${label} to contain valid JSON: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
parsed && typeof parsed === 'object' && !Array.isArray(parsed),
|
||||||
|
`Expected ${label} to contain a JSON object`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertSafeRepoRelativePath(relativePath, label) {
|
||||||
|
const normalized = path.posix.normalize(relativePath.replace(/\\/g, '/'));
|
||||||
|
|
||||||
|
assert.ok(!path.isAbsolute(relativePath), `${label} must not be absolute: ${relativePath}`);
|
||||||
|
assert.ok(
|
||||||
|
!normalized.startsWith('../') && !normalized.includes('/../'),
|
||||||
|
`${label} must not traverse directories: ${relativePath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Claude plugin manifest ────────────────────────────────────────────────────
|
// ── Claude plugin manifest ────────────────────────────────────────────────────
|
||||||
console.log('\n=== .claude-plugin/plugin.json ===\n');
|
console.log('\n=== .claude-plugin/plugin.json ===\n');
|
||||||
|
|
||||||
@@ -44,7 +73,7 @@ test('claude plugin.json exists', () => {
|
|||||||
assert.ok(fs.existsSync(claudePluginPath), 'Expected .claude-plugin/plugin.json to exist');
|
assert.ok(fs.existsSync(claudePluginPath), 'Expected .claude-plugin/plugin.json to exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
const claudePlugin = JSON.parse(fs.readFileSync(claudePluginPath, 'utf8'));
|
const claudePlugin = loadJsonObject(claudePluginPath, '.claude-plugin/plugin.json');
|
||||||
|
|
||||||
test('claude plugin.json has version field', () => {
|
test('claude plugin.json has version field', () => {
|
||||||
assert.ok(claudePlugin.version, 'Expected version field');
|
assert.ok(claudePlugin.version, 'Expected version field');
|
||||||
@@ -56,6 +85,7 @@ test('claude plugin.json agents is an array', () => {
|
|||||||
|
|
||||||
test('claude plugin.json agents uses explicit file paths (not directories)', () => {
|
test('claude plugin.json agents uses explicit file paths (not directories)', () => {
|
||||||
for (const agentPath of claudePlugin.agents) {
|
for (const agentPath of claudePlugin.agents) {
|
||||||
|
assertSafeRepoRelativePath(agentPath, 'Agent path');
|
||||||
assert.ok(
|
assert.ok(
|
||||||
agentPath.endsWith('.md'),
|
agentPath.endsWith('.md'),
|
||||||
`Expected explicit .md file path, got: ${agentPath}`,
|
`Expected explicit .md file path, got: ${agentPath}`,
|
||||||
@@ -69,7 +99,12 @@ test('claude plugin.json agents uses explicit file paths (not directories)', ()
|
|||||||
|
|
||||||
test('claude plugin.json all agent files exist', () => {
|
test('claude plugin.json all agent files exist', () => {
|
||||||
for (const agentRelPath of claudePlugin.agents) {
|
for (const agentRelPath of claudePlugin.agents) {
|
||||||
const absolute = path.join(repoRoot, agentRelPath.replace(/^\.\//, ''));
|
assertSafeRepoRelativePath(agentRelPath, 'Agent path');
|
||||||
|
const absolute = path.resolve(repoRoot, agentRelPath);
|
||||||
|
assert.ok(
|
||||||
|
absolute === repoRoot || absolute.startsWith(repoRootWithSep),
|
||||||
|
`Agent path resolves outside repo root: ${agentRelPath}`,
|
||||||
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
fs.existsSync(absolute),
|
fs.existsSync(absolute),
|
||||||
`Agent file missing: ${agentRelPath}`,
|
`Agent file missing: ${agentRelPath}`,
|
||||||
@@ -105,7 +140,7 @@ test('codex plugin.json exists', () => {
|
|||||||
assert.ok(fs.existsSync(codexPluginPath), 'Expected .codex-plugin/plugin.json to exist');
|
assert.ok(fs.existsSync(codexPluginPath), 'Expected .codex-plugin/plugin.json to exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
const codexPlugin = JSON.parse(fs.readFileSync(codexPluginPath, 'utf8'));
|
const codexPlugin = loadJsonObject(codexPluginPath, '.codex-plugin/plugin.json');
|
||||||
|
|
||||||
test('codex plugin.json has name field', () => {
|
test('codex plugin.json has name field', () => {
|
||||||
assert.ok(codexPlugin.name, 'Expected name field');
|
assert.ok(codexPlugin.name, 'Expected name field');
|
||||||
@@ -165,7 +200,7 @@ test('.mcp.json exists at plugin root (not inside .codex-plugin/)', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const mcpConfig = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf8'));
|
const mcpConfig = loadJsonObject(mcpJsonPath, '.mcp.json');
|
||||||
|
|
||||||
test('.mcp.json has mcpServers object', () => {
|
test('.mcp.json has mcpServers object', () => {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
@@ -194,7 +229,7 @@ test('marketplace.json exists at .agents/plugins/', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const marketplace = JSON.parse(fs.readFileSync(marketplacePath, 'utf8'));
|
const marketplace = loadJsonObject(marketplacePath, '.agents/plugins/marketplace.json');
|
||||||
|
|
||||||
test('marketplace.json has name field', () => {
|
test('marketplace.json has name field', () => {
|
||||||
assert.ok(marketplace.name, 'Expected name field');
|
assert.ok(marketplace.name, 'Expected name field');
|
||||||
|
|||||||
Reference in New Issue
Block a user