Skip to content

feat: marketplace.json as central manifest with code generation#2

Merged
Adam Poulemanos (bashandbone) merged 6 commits into
mainfrom
copilot/extend-versioning-system
Apr 10, 2026
Merged

feat: marketplace.json as central manifest with code generation#2
Adam Poulemanos (bashandbone) merged 6 commits into
mainfrom
copilot/extend-versioning-system

Conversation

Copilot AI commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Adding a plugin required manual edits to four separate files (marketplace.json, .commitlintrc.json, release.yml, plugin.json, package.json) with no enforcement that they stayed in sync.

Changes

  • Expanded .claude-plugin/marketplace.json — added $schema pointer, shared block (author, npmScope, repository, homepage, license, private), and per-plugin fields (version, description, per-plugin homepage overrides, merged keywords, mcpServers). Plugin-manifest-only fields not permitted in PluginEntry (e.g. userConfig) live in a root-level pluginManifestExtensions block keyed by plugin name — the root schema has no additionalProperties: false, so this is valid.

  • scripts/generate.mjs (Node ESM, zero new deps) — reads the manifest and writes all derived files:

    • .commitlintrc.jsonscope-enum from plugin names
    • .github/workflows/release.ymlmatrix.plugin from plugin names (surgical line replacement, rest of file untouched)
    • plugins/<name>/.claude-plugin/plugin.json — merges pluginManifestExtensions[name] (extensions spread first so canonical fields always win) so fields like userConfig still land in the generated output
    • plugins/<name>/package.json — adds shared metadata fields; release block is preserved verbatim; parent directories are created automatically if absent

    Flags: --check (exits 1 on drift, used by npm run validate) and --new <name> (scaffolds a new plugin directory after its entry is added to the manifest; errors clearly if name is missing or invalid)

  • npm run validate now chains validate-marketplace.sh && generate --check

  • validate.yml gains a generated-files-in-sync job — runs the generator in write mode then asserts git diff --exit-code (making the drift check meaningful rather than a no-op)

  • release.yml version-sync step updated — after semantic-release bumps package.json, the new version is synced back into marketplace.json and the generator is re-run to regenerate all derived files (marketplace.json + plugin.json + package.json are committed together), preventing drift between the central manifest and generated outputs

Schema compliance

The PluginEntry definition in the official marketplace schema has "additionalProperties": false. The manifest uses only schema-valid fields inside the plugins array. Any plugin-manifest-only data that has no PluginEntry counterpart (e.g. userConfig) is stored in the root-level pluginManifestExtensions object and merged into the generated plugin.json by the generator.

Adding a plugin going forward

# 1. Add entry to .claude-plugin/marketplace.json
# 2. Scaffold + regenerate derived files
npm run generate -- --new my-plugin
# 3. Add content to plugins/my-plugin/

That's it — no manual edits to commitlintrc, release.yml, or the JSON manifests.

Copilot AI and others added 2 commits April 10, 2026 13:18
@bashandbone

Copy link
Copy Markdown
Contributor

@copilot did you validate the new json? I can't read schema very well with the naked eye, but I suspect the changes don't comply:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://catalog.lintel.tools/schemas/claude-code/marketplace/latest.json",
  "title": "Claude Code Marketplace",
  "description": "Schema for [Claude Code marketplace](https://code.claude.com/docs/en/plugin-marketplaces) catalog (`.claude-plugin/marketplace.json`). Marketplaces distribute plugins to teams and communities, providing centralized discovery, version tracking, and automatic updates. Each marketplace lists plugins with their sources (local paths, GitHub repos, git URLs, npm packages, or pip packages). Users add marketplaces via `/plugin marketplace add` and install individual plugins with `/plugin install plugin-name@marketplace-name`.",
  "x-lintel": {
    "source": "https://raw.githubusercontent.com/lintel-rs/catalog/master/schemas/claude-code/marketplace.json",
    "sourceSha256": "e384a8b15aea368420746f6f677c735c071650a85e61d941ffa2b3f1bfa4fe06",
    "fileMatch": [
      "**/.claude-plugin/marketplace.json",
      ".claude-plugin/marketplace.json"
    ],
    "parsers": [
      "json"
    ],
    "catalogDescription": "Claude Code plugin marketplace catalog."
  },
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Marketplace identifier (kebab-case, no spaces). This is public-facing — users see it when installing plugins (e.g. `/plugin install my-tool@your-marketplace`). **Reserved names** (cannot be used): `claude-code-marketplace`, `claude-code-plugins`, `claude-plugins-official`, `anthropic-marketplace`, `anthropic-plugins`, `agent-skills`, `life-sciences`. Names that impersonate official marketplaces are also blocked.",
      "pattern": "^[a-z0-9][a-z0-9-]*$",
      "examples": [
        "acme-tools",
        "company-tools",
        "my-plugins"
      ]
    },
    "owner": {
      "$ref": "#/$defs/Owner",
      "description": "Marketplace maintainer information."
    },
    "plugins": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/PluginEntry"
      },
      "description": "List of available plugins in this marketplace. Each entry needs at minimum a `name` and `source`. Plugin entries can also include any field from the [plugin manifest schema](https://code.claude.com/docs/en/plugins-reference#plugin-manifest-schema) (`description`, `version`, `author`, `commands`, `hooks`, etc.)."
    },
    "metadata": {
      "$ref": "#/$defs/Metadata",
      "description": "Optional marketplace metadata."
    }
  },
  "$defs": {
    "Owner": {
      "type": "object",
      "description": "Marketplace maintainer information.",
      "properties": {
        "name": {
          "type": "string",
          "description": "Name of the maintainer or team.",
          "examples": [
            "DevTools Team",
            "Jane Doe"
          ]
        },
        "email": {
          "type": "string",
          "description": "Contact email for the maintainer.",
          "examples": [
            "devtools@example.com"
          ]
        }
      },
      "required": [
        "name"
      ],
      "additionalProperties": false
    },
    "Metadata": {
      "type": "object",
      "description": "Optional marketplace-level metadata.",
      "properties": {
        "description": {
          "type": "string",
          "description": "Brief marketplace description.",
          "examples": [
            "Internal tools for the DevOps team"
          ]
        },
        "version": {
          "type": "string",
          "description": "Marketplace catalog version.",
          "examples": [
            "1.0.0"
          ]
        },
        "pluginRoot": {
          "type": "string",
          "description": "Base directory prepended to relative plugin source paths. For example, setting this to `./plugins` lets you write `\"source\": \"formatter\"` instead of `\"source\": \"./plugins/formatter\"`.",
          "examples": [
            "./plugins"
          ]
        }
      },
      "additionalProperties": false
    },
    "PluginEntry": {
      "type": "object",
      "description": "A plugin listed in the marketplace. Includes marketplace-specific fields (`source`, `category`, `tags`, `strict`) plus any field from the [plugin manifest schema](https://code.claude.com/docs/en/plugins-reference#plugin-manifest-schema) (`description`, `version`, `author`, `commands`, `hooks`, etc.).",
      "properties": {
        "name": {
          "type": "string",
          "description": "Plugin identifier (kebab-case, no spaces). This is public-facing — users see it when installing (e.g. `/plugin install my-plugin@marketplace`).",
          "pattern": "^[a-z0-9][a-z0-9-]*$",
          "examples": [
            "code-formatter",
            "deployment-tools",
            "review-plugin"
          ]
        },
        "source": {
          "$ref": "#/$defs/PluginSource",
          "description": "Where to fetch the plugin from. Can be a relative path within the marketplace repo (starting with `./`) or an object specifying a remote source (`github`, `url`, `npm`, or `pip`). **Note**: Relative paths only work when users add the marketplace via Git — they won't resolve for URL-based marketplaces. See [plugin sources](https://code.claude.com/docs/en/plugin-marketplaces#plugin-sources)."
        },
        "description": {
          "type": "string",
          "description": "Brief plugin description shown when browsing the marketplace.",
          "examples": [
            "Automatic code formatting on save"
          ]
        },
        "version": {
          "type": "string",
          "description": "Plugin version. If the plugin also has a version in its `plugin.json`, the `plugin.json` version **takes priority**. For relative-path plugins, set the version here. For remote plugins, set it in `plugin.json`. See [version resolution](https://code.claude.com/docs/en/plugin-marketplaces#version-resolution-and-release-channels).",
          "examples": [
            "1.0.0",
            "2.1.0"
          ]
        },
        "author": {
          "$ref": "https://catalog.lintel.tools/schemas/claude-code/author/latest.json",
          "description": "Plugin author information."
        },
        "homepage": {
          "type": "string",
          "description": "Plugin homepage or documentation URL.",
          "examples": [
            "https://docs.example.com/plugins/my-tool"
          ]
        },
        "repository": {
          "type": "string",
          "description": "Source code repository URL.",
          "examples": [
            "https://github.com/company/my-plugin"
          ]
        },
        "license": {
          "type": "string",
          "description": "[SPDX license identifier](https://spdx.org/licenses/).",
          "examples": [
            "MIT",
            "Apache-2.0"
          ]
        },
        "keywords": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Tags for plugin discovery and categorization.",
          "examples": [
            [
              "enterprise",
              "workflow",
              "automation"
            ]
          ]
        },
        "category": {
          "type": "string",
          "description": "Plugin category for organization.",
          "examples": [
            "productivity",
            "code-quality",
            "deployment"
          ]
        },
        "tags": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Tags for searchability.",
          "examples": [
            [
              "lint",
              "format",
              "style"
            ]
          ]
        },
        "strict": {
          "type": "boolean",
          "description": "Controls whether `plugin.json` is the authority for component definitions. When `true` (default), `plugin.json` is authoritative and the marketplace entry can supplement it with additional components (both are merged). When `false`, the marketplace entry is the **entire definition** — if the plugin also has a `plugin.json` that declares components, that's a conflict and the plugin fails to load. See [strict mode](https://code.claude.com/docs/en/plugin-marketplaces#strict-mode).",
          "default": true
        },
        "commands": {
          "$ref": "https://catalog.lintel.tools/schemas/claude-code/path-or-paths/latest.json",
          "description": "Custom paths to command files or directories, relative to the plugin root."
        },
        "agents": {
          "$ref": "https://catalog.lintel.tools/schemas/claude-code/path-or-paths/latest.json",
          "description": "Custom paths to agent files, relative to the plugin root."
        },
        "skills": {
          "$ref": "https://catalog.lintel.tools/schemas/claude-code/path-or-paths/latest.json",
          "description": "Custom paths to skill directories, relative to the plugin root."
        },
        "hooks": {
          "description": "[Hook](https://code.claude.com/docs/en/hooks) configuration — paths to JSON config files or inline definitions.",
          "oneOf": [
            {
              "type": "string",
              "description": "Path to a hooks JSON config file."
            },
            {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Array of paths."
            },
            {
              "$ref": "https://catalog.lintel.tools/schemas/claude-code/hooks/latest.json",
              "description": "Inline hook definitions."
            }
          ]
        },
        "mcpServers": {
          "description": "[MCP server](https://code.claude.com/docs/en/mcp) configuration — paths to JSON config files or inline definitions.",
          "oneOf": [
            {
              "type": "string",
              "description": "Path to an MCP config JSON file."
            },
            {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Array of paths."
            },
            {
              "type": "object",
              "description": "Inline MCP server definitions.",
              "additionalProperties": {
                "oneOf": [
                  {
                    "type": "string",
                    "description": "Reference to an already-configured server."
                  },
                  {
                    "$ref": "https://catalog.lintel.tools/schemas/claude-code/mcp-server-config/latest.json",
                    "description": "Inline definition."
                  }
                ]
              }
            }
          ]
        },
        "outputStyles": {
          "$ref": "https://catalog.lintel.tools/schemas/claude-code/path-or-paths/latest.json",
          "description": "Custom paths to output style files or directories."
        },
        "lspServers": {
          "description": "[LSP server](https://code.claude.com/docs/en/plugins-reference#lsp-servers) configuration — paths to JSON config files or inline definitions.",
          "oneOf": [
            {
              "type": "string",
              "description": "Path to an LSP config JSON file."
            },
            {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Array of paths."
            },
            {
              "type": "object",
              "description": "Inline LSP server definitions.",
              "additionalProperties": {
                "$ref": "https://catalog.lintel.tools/schemas/claude-code/lsp-server-config/latest.json"
              }
            }
          ]
        }
      },
      "required": [
        "name",
        "source"
      ],
      "additionalProperties": false
    },
    "PluginSource": {
      "oneOf": [
        {
          "type": "string",
          "description": "Relative path within the marketplace repo. Must start with `./`. Only works when users add the marketplace via Git (not URL-based).",
          "examples": [
            "./plugins/my-plugin"
          ]
        },
        {
          "$ref": "#/$defs/GitHubSource"
        },
        {
          "$ref": "#/$defs/UrlSource"
        },
        {
          "$ref": "#/$defs/NpmSource"
        },
        {
          "$ref": "#/$defs/PipSource"
        }
      ]
    },
    "GitHubSource": {
      "type": "object",
      "description": "Fetch the plugin from a [GitHub](https://github.com) repository. Supports pinning to a branch, tag, or exact commit SHA.",
      "properties": {
        "source": {
          "const": "github"
        },
        "repo": {
          "type": "string",
          "description": "GitHub repository in `owner/repo` format.",
          "pattern": "^[^/]+/[^/]+$",
          "examples": [
            "owner/plugin-repo",
            "company/enterprise-plugin"
          ]
        },
        "ref": {
          "type": "string",
          "description": "Git branch or tag. Defaults to the repository's default branch.",
          "examples": [
            "main",
            "v2.0.0",
            "stable"
          ]
        },
        "sha": {
          "type": "string",
          "description": "Full 40-character git commit SHA to pin to an exact version.",
          "pattern": "^[0-9a-f]{40}$",
          "examples": [
            "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
          ]
        }
      },
      "required": [
        "source",
        "repo"
      ],
      "additionalProperties": false
    },
    "UrlSource": {
      "type": "object",
      "description": "Fetch the plugin from a git URL (GitHub, GitLab, Bitbucket, self-hosted, etc.).",
      "properties": {
        "source": {
          "const": "url"
        },
        "url": {
          "type": "string",
          "description": "Full git repository URL (must end with `.git`).",
          "pattern": "\\.git$",
          "examples": [
            "https://gitlab.com/team/plugin.git"
          ]
        },
        "ref": {
          "type": "string",
          "description": "Git branch or tag. Defaults to the repository's default branch.",
          "examples": [
            "main",
            "v1.0.0"
          ]
        },
        "sha": {
          "type": "string",
          "description": "Full 40-character git commit SHA to pin to an exact version.",
          "pattern": "^[0-9a-f]{40}$"
        }
      },
      "required": [
        "source",
        "url"
      ],
      "additionalProperties": false
    },
    "NpmSource": {
      "type": "object",
      "description": "Install the plugin from an [npm](https://www.npmjs.com/) registry via `npm install`.",
      "properties": {
        "source": {
          "const": "npm"
        },
        "package": {
          "type": "string",
          "description": "npm package name.",
          "examples": [
            "@company/mcp-server",
            "my-claude-plugin"
          ]
        },
        "version": {
          "type": "string",
          "description": "Version range or exact version.",
          "examples": [
            "1.0.0",
            "^2.0.0"
          ]
        },
        "registry": {
          "type": "string",
          "description": "Custom npm registry URL.",
          "examples": [
            "https://npm.company.com"
          ]
        }
      },
      "required": [
        "source",
        "package"
      ],
      "additionalProperties": false
    },
    "PipSource": {
      "type": "object",
      "description": "Install the plugin via [pip](https://pip.pypa.io/).",
      "properties": {
        "source": {
          "const": "pip"
        },
        "package": {
          "type": "string",
          "description": "Python package name.",
          "examples": [
            "my-claude-plugin"
          ]
        },
        "version": {
          "type": "string",
          "description": "Version specifier.",
          "examples": [
            "1.0.0",
            ">=2.0"
          ]
        },
        "registry": {
          "type": "string",
          "description": "Custom PyPI registry URL.",
          "examples": [
            "https://pypi.company.com/simple"
          ]
        }
      },
      "required": [
        "source",
        "package"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "name",
    "owner",
    "plugins"
  ]
}

… on PluginEntry)

Agent-Logs-Url: https://github.com/knitli/toolshed/sessions/709d6e9b-431b-482a-b1cd-dff958712cb9

Co-authored-by: bashandbone <89049923+bashandbone@users.noreply.github.com>

Copilot AI commented Apr 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot did you validate the new json? I can't read schema very well with the naked eye, but I suspect the changes don't comply:

...

Good catch — it wasn't compliant. The PluginEntry definition has "additionalProperties": false, so our two extra fields were invalid:

  • pluginDescription — removed; the single description field now serves both marketplace display and plugin.json
  • userConfig — moved out of the plugins array entries to a root-level pluginManifestExtensions block keyed by plugin name (the root schema has no additionalProperties: false, so this is valid). The generator reads that block and spreads it into the generated plugin.json, so codeweaver's userConfig still ends up in the right place.

Also added the $schema pointer. Fixed in d9cd49b.

@bashandbone
Adam Poulemanos (bashandbone) marked this pull request as ready for review April 10, 2026 16:16
Copilot AI review requested due to automatic review settings April 10, 2026 16:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR centralizes plugin metadata in .claude-plugin/marketplace.json and introduces a generator to keep derived repo files (commitlint config, release workflow matrix, per-plugin plugin.json and package.json) automatically in sync, reducing manual edits and drift.

Changes:

  • Expand .claude-plugin/marketplace.json to act as the source-of-truth (shared metadata + per-plugin fields + extensions).
  • Add scripts/generate.mjs to generate/verify derived files (--check, --new).
  • Wire drift checking into npm run validate and GitHub Actions validation.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
scripts/generate.mjs Adds the central manifest generator and drift-check/new-plugin scaffolding logic.
.claude-plugin/marketplace.json Becomes the central manifest with shared metadata and richer plugin entries.
.commitlintrc.json Generated commitlint scope enum derived from plugin names.
.github/workflows/release.yml Plugin matrix updated to reflect manifest ordering.
.github/workflows/validate.yml Adds a job to enforce generated-file drift checks in CI.
package.json Adds generate script and updates validate to include generate --check.
plugins/ctx/.claude-plugin/plugin.json Updates author metadata in the plugin manifest.
plugins/strip-ansi/.claude-plugin/plugin.json Updates author metadata in the plugin manifest.
plugins/codeweaver/.claude-plugin/plugin.json Updates keywords/formatting and preserves userConfig via extensions.
plugins/ctx/package.json Adds shared package metadata fields to the plugin package.
plugins/strip-ansi/package.json Adds shared package metadata fields to the plugin package.
plugins/codeweaver/package.json Adds shared package metadata fields to the plugin package.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/generate.mjs
Comment on lines +111 to +114
for (const plugin of plugins) {
const pluginDir = join(ROOT, plugin.source.replace(/^\.\//, ''));
const pluginJsonPath = join(pluginDir, '.claude-plugin', 'plugin.json');

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--new <name> scaffolding runs after the main generation loops, but the generation step writes plugins/<name>/.claude-plugin/plugin.json and plugins/<name>/package.json without ensuring the directories exist. If the new plugin directory (or .claude-plugin/) isn’t already present, --new will fail before it reaches the mkdir scaffolding section. Consider running scaffolding (mkdirs) before generating for the target plugin, or ensure parent dirs exist before writing generated files.

Copilot uses AI. Check for mistakes.
Comment thread scripts/generate.mjs
Comment on lines +115 to +128
const extensions = pluginManifestExtensions[plugin.name] ?? {};

const pluginJson = {
name: plugin.name,
version: plugin.version,
description: plugin.description,
author: shared.author,
homepage: plugin.homepage ?? shared.homepage,
repository: shared.repository,
license: plugin.license ?? shared.license,
keywords: plugin.keywords,
...(plugin.mcpServers !== undefined && { mcpServers: plugin.mcpServers }),
...extensions,
};

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pluginManifestExtensions is spread last into the generated plugin.json, which means it can override core fields like name, version, repository, etc. That makes the “central manifest is source of truth” invariant easy to accidentally break. Consider either filtering extension keys to an allowlist (e.g., userConfig) or merging extensions first so the canonical fields always win.

Copilot uses AI. Check for mistakes.
Comment thread scripts/generate.mjs Outdated
Comment on lines +27 to +28
const NEW_PLUGIN = newIdx !== -1 ? args[newIdx + 1] : null;

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--new argument parsing doesn’t validate that a name was actually provided (e.g., --new as the last arg). In that case NEW_PLUGIN becomes undefined and the flag is silently ignored. Consider erroring out when --new is present but no plugin name is provided (or when the next token starts with --).

Suggested change
const NEW_PLUGIN = newIdx !== -1 ? args[newIdx + 1] : null;
let NEW_PLUGIN = null;
if (newIdx !== -1) {
const candidate = args[newIdx + 1];
if (!candidate || candidate.startsWith('--')) {
console.error('ERROR: --new requires a plugin name.');
process.exit(1);
}
NEW_PLUGIN = candidate;
}

Copilot uses AI. Check for mistakes.
Comment thread plugins/ctx/.claude-plugin/plugin.json Outdated
"name": "Knitli Inc.",
"email": "hello@knitli.com"
},
"homepage": "https://knitli.com",

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

marketplace.json sets an explicit homepage for ctx (https://knitli.com/ctx), and the generator uses plugin.homepage ?? shared.homepage for plugin.json. This generated plugin.json still has the shared homepage (https://knitli.com), so npm run generate --check will report drift. Please regenerate (or update the file) so the plugin.json homepage matches the manifest override.

Suggested change
"homepage": "https://knitli.com",
"homepage": "https://knitli.com/ctx",

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +32
- name: Fail if any generated file was modified
run: git diff --exit-code

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This job runs node scripts/generate.mjs --check, which never writes to disk, so the subsequent git diff --exit-code step will always be a no-op. If you want a second layer of enforcement, run the generator in write mode and then git diff --exit-code, otherwise consider dropping the diff step to reduce CI noise/time.

Suggested change
- name: Fail if any generated file was modified
run: git diff --exit-code

Copilot uses AI. Check for mistakes.
Comment on lines 14 to 18
runs-on: ubuntu-latest
strategy:
matrix:
plugin: [ctx, codeweaver, strip-ansi]
plugin: [ctx, strip-ansi, codeweaver]
steps:

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the new “marketplace.json is the source of truth + generated plugin.json/package.json” model, this workflow’s version-sync step (updating plugins/<plugin>/.claude-plugin/plugin.json based on package.json) will create drift against the generator output (which sets versions from marketplace.json). Consider updating the release process to sync versions back into .claude-plugin/marketplace.json and re-run the generator, or stop directly editing generated files in the workflow.

Copilot uses AI. Check for mistakes.
@bashandbone

Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

Copilot AI and others added 2 commits April 10, 2026 16:27

Copilot AI commented Apr 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

Applied all changes from that review thread (commit 894b7ea, polish in f8b6d80):

  • generate.mjs--new arg validation: exits with a clear error if --new is the last arg or the next token starts with --
  • generate.mjs — directory creation: mkdirSync({ recursive: true }) before writing both plugin.json and package.json, so --new works even on a completely fresh directory
  • generate.mjs — extensions spread order: extensions are now spread first in the pluginJson object, so canonical fields (name, version, repository, etc.) always win over anything in pluginManifestExtensions
  • validate.yml — CI no-op fix: step now runs node scripts/generate.mjs (write mode) followed by git diff --exit-code — the diff is now meaningful since the generator actually writes files
  • release.yml — generated-file drift fix: replaced the direct plugin.json jq edit with: (1) sync the bumped version from package.json back into marketplace.json, (2) run the generator to regenerate all derived files, (3) commit marketplace.json + plugin.json + package.json together
  • ctx/plugin.json homepage drift: regenerated; now correctly shows https://knitli.com/ctx

@bashandbone
Adam Poulemanos (bashandbone) merged commit 12510e6 into main Apr 10, 2026
5 of 6 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version @knitli/ctx-v1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version @knitli/codeweaver-v1.1.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version @knitli/strip-ansi-v1.1.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants