modify template
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
# Updating Existing Local Plugins
|
||||
|
||||
Use this reference when a plugin already exists and the request is about updating the plugin during
|
||||
local development.
|
||||
|
||||
All scripts here are specified relative to the skill root. Update the path for running the scripts
|
||||
depending on your current working directory.
|
||||
|
||||
## When To Use This Flow
|
||||
|
||||
Use this flow when all of the following are true:
|
||||
|
||||
- the plugin already exists locally
|
||||
- the marketplace entry already points at the plugin source you are editing
|
||||
- the user wants Codex to see the updated plugin without manually editing marketplace files
|
||||
|
||||
If the user still needs the initial plugin entry or marketplace structure created, use the scaffold
|
||||
flow first and only then switch to this reinstall flow.
|
||||
|
||||
## Update Loop
|
||||
|
||||
1. Update the plugin manifest to a single Codex cachebuster suffix:
|
||||
|
||||
```bash
|
||||
python3 scripts/update_plugin_cachebuster.py \
|
||||
<plugin-path>
|
||||
```
|
||||
|
||||
Prefer the default helper behavior here. If you omit `--cachebuster`, the helper uses a UTC
|
||||
timestamp down to seconds, which is the recommended path for routine local iteration.
|
||||
|
||||
Only use a manual cachebuster override when the user explicitly asks for one or when a workflow
|
||||
outside Codex depends on a specific token:
|
||||
|
||||
```bash
|
||||
python3 scripts/update_plugin_cachebuster.py \
|
||||
<plugin-path> \
|
||||
--cachebuster local-20260519-184516
|
||||
```
|
||||
|
||||
2. For the default scaffolded flow, read the marketplace name from the personal marketplace file:
|
||||
|
||||
```bash
|
||||
python3 scripts/read_marketplace_name.py
|
||||
```
|
||||
|
||||
Here, "personal marketplace" means the marketplace whose file is at
|
||||
`~/.agents/plugins/marketplace.json`. On Windows, use the equivalent path under the user profile.
|
||||
The helper uses Python's home-directory resolution and prints the marketplace name to use when
|
||||
constructing the install command.
|
||||
|
||||
To read the name from a different marketplace file, pass the path directly:
|
||||
|
||||
```bash
|
||||
python3 scripts/read_marketplace_name.py --marketplace-path <path-to-marketplace.json>
|
||||
```
|
||||
|
||||
3. Reinstall from that marketplace name:
|
||||
|
||||
```bash
|
||||
codex plugin add <plugin-name>@<marketplace-name-from-marketplace-json>
|
||||
```
|
||||
|
||||
The default personal marketplace is discovered implicitly from
|
||||
`~/.agents/plugins/marketplace.json`. You do not need `codex plugin marketplace add` for that
|
||||
path, and `codex plugin marketplace list` is not the right check for whether that default
|
||||
marketplace exists.
|
||||
|
||||
4. If the plugin is not using the personal marketplace file, check which configured local
|
||||
marketplace is actually surfacing that plugin:
|
||||
|
||||
```bash
|
||||
codex plugin list
|
||||
```
|
||||
|
||||
If the plugin is not in the personal marketplace file, confirm which marketplace entry points at
|
||||
the plugin source you are editing and make sure that marketplace is still local. If it is a
|
||||
different local marketplace, reinstall from that marketplace name instead of forcing the personal
|
||||
marketplace flow. If it is not local, stop and help the user resolve the mismatch before
|
||||
continuing.
|
||||
|
||||
5. If the plugin lives in a different confirmed local marketplace, substitute that marketplace
|
||||
name:
|
||||
|
||||
```bash
|
||||
codex plugin add <plugin-name>@<local-marketplace>
|
||||
```
|
||||
|
||||
6. Prompt the user to use a new thread to try the updated plugin, so that Codex picks up new skills
|
||||
and tools.
|
||||
|
||||
## Cachebuster Policy
|
||||
|
||||
- Preserve the existing version prefix and replace only the suffix.
|
||||
- Treat the preserved prefix as everything before `+`.
|
||||
- Use the format:
|
||||
|
||||
```text
|
||||
<base-version>+codex.<cachebuster>
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
- `0.1.0` → `0.1.0+codex.local-20260519-184516`
|
||||
- `0.1.0+codex.old-token` → `0.1.0+codex.local-20260519-184516`
|
||||
- `1.2.3-beta.1+codex.prev` → `1.2.3-beta.1+codex.local-20260519-184516`
|
||||
- `dev-build+other-tag` → `dev-build+codex.local-20260519-184516`
|
||||
|
||||
Replace the existing Codex cachebuster instead of appending another one. Do not keep incrementing
|
||||
numeric version components just to trigger reinstall behavior.
|
||||
|
||||
## Marketplace Rules
|
||||
|
||||
- Marketplace manipulation should happen through commands, not by hand-editing `marketplace.json`
|
||||
or `config.toml` during this update/reinstall flow.
|
||||
- Prefer the personal marketplace file for the default scaffolded flow.
|
||||
- Read the personal marketplace name with
|
||||
`python3 scripts/read_marketplace_name.py` and use the printed value when constructing
|
||||
`codex plugin add <plugin-name>@<marketplace-name>`.
|
||||
- For non-default marketplace files, use
|
||||
`python3 scripts/read_marketplace_name.py --marketplace-path <path-to-marketplace.json>` to read
|
||||
the name before constructing reinstall commands.
|
||||
- Do not tell the user to run `codex plugin marketplace add` for the default personal-marketplace
|
||||
flow. That marketplace is discovered implicitly by Codex.
|
||||
- If the user specified a different marketplace path, make sure that marketplace is installed
|
||||
before giving install or reinstall instructions. Non-default marketplace paths are not
|
||||
discovered implicitly.
|
||||
- Use `codex plugin list` when the plugin lives in a different configured marketplace and you need
|
||||
to confirm which marketplace is surfacing that plugin.
|
||||
- If a non-default local marketplace has not been configured yet, install it with
|
||||
`codex plugin marketplace add <path-to-marketplace-root>` before telling the user to run
|
||||
`codex plugin add <plugin-name>@<marketplace-name>`.
|
||||
- If the plugin is not in the personal marketplace file, confirm that the selected marketplace is
|
||||
local before telling the user to reinstall from it.
|
||||
- If the selected marketplace is not local, stop and help the user resolve that mismatch rather
|
||||
than pretending the normal local reinstall flow applies.
|
||||
- If the plugin source is not already the source referenced by the chosen marketplace entry, stop
|
||||
and fix that first. This update flow does not rewrite marketplace entries.
|
||||
|
||||
## After Reinstall
|
||||
|
||||
After reinstalling, prompt the user to start a new thread for testing. That is the safe boundary for
|
||||
picking up the updated plugin and its MCP tools.
|
||||
@@ -0,0 +1,194 @@
|
||||
# Plugin JSON sample spec
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "plugin-name",
|
||||
"version": "1.2.0",
|
||||
"description": "Brief plugin description",
|
||||
"author": {
|
||||
"name": "Author Name",
|
||||
"email": "author@example.com",
|
||||
"url": "https://github.com/author"
|
||||
},
|
||||
"homepage": "https://docs.example.com/plugin",
|
||||
"repository": "https://github.com/author/plugin",
|
||||
"license": "MIT",
|
||||
"keywords": ["keyword1", "keyword2"],
|
||||
"skills": "./skills/",
|
||||
"hooks": "./hooks.json",
|
||||
"mcpServers": "./.mcp.json",
|
||||
"apps": "./.app.json",
|
||||
"interface": {
|
||||
"displayName": "Plugin Display Name",
|
||||
"shortDescription": "Short description for subtitle",
|
||||
"longDescription": "Long description for details page",
|
||||
"developerName": "OpenAI",
|
||||
"category": "Productivity",
|
||||
"capabilities": ["Interactive", "Write"],
|
||||
"websiteURL": "https://openai.com/",
|
||||
"privacyPolicyURL": "https://openai.com/policies/row-privacy-policy/",
|
||||
"termsOfServiceURL": "https://openai.com/policies/row-terms-of-use/",
|
||||
"defaultPrompt": [
|
||||
"Summarize my inbox and draft replies for me.",
|
||||
"Find open bugs and turn them into Linear tickets.",
|
||||
"Review today's meetings and flag scheduling gaps."
|
||||
],
|
||||
"brandColor": "#3B82F6",
|
||||
"composerIcon": "./assets/icon.png",
|
||||
"logo": "./assets/logo.png",
|
||||
"screenshots": [
|
||||
"./assets/screenshot1.png",
|
||||
"./assets/screenshot2.png",
|
||||
"./assets/screenshot3.png"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Field guide
|
||||
|
||||
### Top-level fields
|
||||
|
||||
- `name` (`string`): Plugin identifier (kebab-case, no spaces). Required if `plugin.json` is provided and used as manifest name and component namespace.
|
||||
- `version` (`string`): Plugin semantic version.
|
||||
- `description` (`string`): Short purpose summary.
|
||||
- `author` (`object`): Publisher identity.
|
||||
- `name` (`string`): Author or team name.
|
||||
- `email` (`string`): Contact email.
|
||||
- `url` (`string`): Author/team homepage or profile URL.
|
||||
- `homepage` (`string`): Documentation URL for plugin usage.
|
||||
- `repository` (`string`): Source code URL.
|
||||
- `license` (`string`): License identifier (for example `MIT`, `Apache-2.0`).
|
||||
- `keywords` (`array` of `string`): Search/discovery tags.
|
||||
- `skills` (`string`): Relative path to skill directories/files.
|
||||
- `hooks` (`string`): Hook config path.
|
||||
- `mcpServers` (`string`): MCP config path.
|
||||
- `apps` (`string`): App manifest path for plugin integrations.
|
||||
- `interface` (`object`): Interface/UX metadata block for plugin presentation.
|
||||
|
||||
### `interface` fields
|
||||
|
||||
- `displayName` (`string`): User-facing title shown for the plugin.
|
||||
- `shortDescription` (`string`): Brief subtitle used in compact views.
|
||||
- `longDescription` (`string`): Longer description used on details screens.
|
||||
- `developerName` (`string`): Human-readable publisher name.
|
||||
- `category` (`string`): Plugin category bucket.
|
||||
- `capabilities` (`array` of `string`): Capability list from implementation.
|
||||
- `websiteURL` (`string`): Public website for the plugin.
|
||||
- `privacyPolicyURL` (`string`): Privacy policy URL.
|
||||
- `termsOfServiceURL` (`string`): Terms of service URL.
|
||||
- `defaultPrompt` (`array` of `string`): Starter prompts shown in composer/UX context.
|
||||
- Include at most 3 strings. Entries after the first 3 are ignored and will not be included.
|
||||
- Each string is capped at 128 characters. Longer entries are truncated.
|
||||
- Prefer short starter prompts around 50 characters so they scan well in the UI.
|
||||
- `brandColor` (`string`): Theme color for the plugin card.
|
||||
- `composerIcon` (`string`): Path to icon asset.
|
||||
- `logo` (`string`): Path to logo asset.
|
||||
- `screenshots` (`array` of `string`): List of screenshot asset paths.
|
||||
- Screenshot entries must be PNG filenames and stored under `./assets/`.
|
||||
- Keep file paths relative to plugin root.
|
||||
|
||||
### Path conventions and defaults
|
||||
|
||||
- Path values should be relative and begin with `./`.
|
||||
- `skills`, `hooks`, and `mcpServers` are supplemented on top of default component discovery; they do not replace defaults.
|
||||
- Custom path values must follow the plugin root convention and naming/namespacing rules.
|
||||
- This repo’s scaffold writes `.codex-plugin/plugin.json`; treat that as the manifest location this skill generates.
|
||||
|
||||
# Marketplace JSON sample spec
|
||||
|
||||
`marketplace.json` depends on where the plugin should live. New plugin creation defaults to the
|
||||
personal marketplace unless the caller explicitly requests a repo-local destination:
|
||||
|
||||
- Personal plugin: `~/.agents/plugins/marketplace.json`
|
||||
- Repo/team plugin: `<repo-root>/.agents/plugins/marketplace.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "openai-curated",
|
||||
"interface": {
|
||||
"displayName": "ChatGPT Official"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "linear",
|
||||
"source": {
|
||||
"source": "local",
|
||||
"path": "./plugins/linear"
|
||||
},
|
||||
"policy": {
|
||||
"installation": "AVAILABLE",
|
||||
"authentication": "ON_INSTALL"
|
||||
},
|
||||
"category": "Productivity"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Marketplace field guide
|
||||
|
||||
### Top-level fields
|
||||
|
||||
- `name` (`string`): Marketplace identifier or catalog name.
|
||||
- `interface` (`object`, optional): Marketplace presentation metadata.
|
||||
- `plugins` (`array`): Ordered plugin entries. This order determines how Codex renders plugins.
|
||||
|
||||
### `interface` fields
|
||||
|
||||
- `displayName` (`string`, optional): User-facing marketplace title.
|
||||
|
||||
### Plugin entry fields
|
||||
|
||||
- `name` (`string`): Plugin identifier. Match the plugin folder name and `plugin.json` `name`.
|
||||
- `source` (`object`): Plugin source descriptor.
|
||||
- `source` (`string`): Use `local` for this repo workflow.
|
||||
- `path` (`string`): Relative plugin path based on the marketplace root.
|
||||
- Personal plugin in `~/.agents/plugins/marketplace.json`: `./plugins/<plugin-name>`
|
||||
- Repo/team plugin: `./plugins/<plugin-name>`
|
||||
- The same relative path convention is used for both personal and repo/team marketplaces.
|
||||
- Example: with `~/.agents/plugins/marketplace.json`, `./plugins/<plugin-name>` resolves to
|
||||
`~/plugins/<plugin-name>`.
|
||||
- `policy` (`object`): Marketplace policy block. Always include it.
|
||||
- `installation` (`string`): Availability policy.
|
||||
- Allowed values: `NOT_AVAILABLE`, `AVAILABLE`, `INSTALLED_BY_DEFAULT`
|
||||
- Default for new entries: `AVAILABLE`
|
||||
- `authentication` (`string`): Authentication timing policy.
|
||||
- Allowed values: `ON_INSTALL`, `ON_USE`
|
||||
- Default for new entries: `ON_INSTALL`
|
||||
- `products` (`array` of `string`, optional): Product override for this plugin entry. Omit it unless product gating is explicitly requested.
|
||||
- `category` (`string`): Display category bucket. Always include it.
|
||||
|
||||
### Marketplace generation rules
|
||||
|
||||
- `displayName` belongs under the top-level `interface` object, not individual plugin entries.
|
||||
- When creating a new marketplace file from scratch, seed `interface.displayName` alongside top-level `name`.
|
||||
- Always include `policy.installation`, `policy.authentication`, and `category` on every generated or updated plugin entry.
|
||||
- Treat `policy.products` as an override and omit it unless explicitly requested.
|
||||
- Append new entries unless the user explicitly requests reordering.
|
||||
- Replace an existing entry for the same plugin only when overwrite is intentional.
|
||||
- Default new plugin creation to the personal marketplace.
|
||||
- Use a repo/team marketplace only when the user specifically requests that destination.
|
||||
- Only override the marketplace `name` when the default `personal` name is already taken or
|
||||
installed and you need to seed a different new marketplace file.
|
||||
- Choose marketplace location to match the selected destination:
|
||||
- Personal plugin: `~/.agents/plugins/marketplace.json`
|
||||
- Repo/team plugin: `<repo-root>/.agents/plugins/marketplace.json`
|
||||
|
||||
### Plugin validation notes
|
||||
|
||||
- The validator mirrors the workspace plugin ingestion schema so generated plugins follow the same
|
||||
manifest contract from the start.
|
||||
- Plugin manifests must include real values for `name`, `version`, `description`,
|
||||
`author.name`, and the required `interface` fields.
|
||||
- `version` must use strict semver.
|
||||
- `websiteURL`, `privacyPolicyURL`, and `termsOfServiceURL` must be absolute `https://` URLs when
|
||||
present.
|
||||
- `composerIcon`, `logo`, and `screenshots` must point to real files inside the plugin archive when
|
||||
present.
|
||||
- `apps` and `mcpServers` should appear in `plugin.json` only when `.app.json` and `.mcp.json`
|
||||
actually exist.
|
||||
- Validation rejects unsupported manifest fields such as `hooks`, so the scaffold keeps them out of
|
||||
generated manifests.
|
||||
- Run `scripts/validate_plugin.py <plugin-path>` before handing back a generated plugin. It adds one
|
||||
intentional preflight check that rejects leftover `[TODO: ...]` placeholders.
|
||||
Reference in New Issue
Block a user