Skip to content

feat: publish Konductor as a Claude Code plugin marketplace - #28

Merged
knihit merged 30 commits into
mainfrom
feat/claude-plugin-marketplace
Oct 14, 2026
Merged

knihit merged 30 commits into
mainfrom
feat/claude-plugin-marketplace

Conversation

@knihit

@knihit knihit commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Description

Plugin marketplace:

  • Added .claude-plugin/marketplace.json, pointing the konductor plugin entry at the release/plugins branch (ref: "release/plugins"), not main.
  • Added docs/user-guide/tasks/install-claude-plugin-marketplace.md, covering /plugin marketplace add aws-solutions/konductor then /plugin install konductor@konductor, plus the aws:ViaAWSMCPService/aws:CalledViaAWSMCP IAM condition keys for scoping AWS MCP access.
  • Added generated/claude-plugin/README.md and the generation scripts under scripts/ (generate-claude-plugin.sh, assemble-claude-plugin-branch.sh, copy-skills-with-collision-guard.sh, render-claude-plugin-json.py, rewrite-claude-plugin-mcp-tool-names.py, read-bundled-mcp-servers.sh, claude-plugin.template.json). The plugin layout is a single flat tree under generated/claude-plugin/ and the assembled candidate branch; there's no separate repo-root plugin.json/.mcp.json.
  • New Makefile targets: claude-plugin regenerates the flat tree, claude-plugin-check regenerates and validates it (claude plugin validate --strict) with no commits or pushes. If the claude CLI isn't on PATH, it skips validation and reports that instead of a false pass. All generated output is gitignored.

Release and publish flow (release.yml):

  • New publish-claude-plugin job runs after publish succeeds on a push to main. It assembles the flat plugin tree, builds a candidate commit on top of release/plugins' current tip (scripts/build-plugin-candidate-commit.sh), pushes it to a fresh plugin-candidate/... branch (scripts/derive-plugin-candidate-branch.sh), and opens a draft pull request into the protected release/plugins branch.
  • If the assembled plugin tree already matches release/plugins' tip exactly (for example, a re-release of unchanged plugin content), the job skips the push and draft-PR steps and ends green with a notice instead of pushing an empty branch.
  • No force-push anywhere in this path. release/plugins itself is protected (branch ruleset blocks creation/update/deletion/non-fast-forward, zero bypass actors) and the org-wide ruleset requires two approvals, so a human has to merge the candidate PR.
  • tag-claude-plugin-release.yml creates the immutable claude-plugin-vX.Y.Z tag. It's manual workflow_dispatch, run from main with the merged PR's number as input. It verifies the PR is actually merged, checks out the merge commit, confirms the published artifacts are present, and tags that commit. A concurrency group serializes dispatches so two runs can't race the same tag.
  • build-plugin-candidate-commit.sh, derive-plugin-candidate-branch.sh, and validate-plugin-publish-preconditions.sh now share one protected-branch check, is_protected_branch in scripts/lib/protected-branches.sh, instead of each keeping its own copy of the case statement.
  • There's no dry-run workflow. The earlier plugin-publish-dry-run.yml was removed; validate-pr.yml now runs make claude-plugin-check (built with the same pinned claude CLI version release.yml uses) on every PR, giving strict manifest validation without ever pushing a branch or opening a PR.

AWS MCP bundling:

  • konductor synth takes a new --claude-bundled-mcp-servers flag: a comma-separated allowlist of dependencies.mcpRegistry server names the Claude Code transformer may merge into an agent's rendered mcpServers: frontmatter. Omitted, the allowlist is empty and nothing gets merged; this is opt-in by design.
  • scripts/claude-plugin-mcp-servers.json is this repo's own choice: aws-mcp only (not playwright-mcp).
  • k-architect and k-developer's agent specs now pin mcp-proxy-for-aws-cli==1.7.0 directly in dependencies.mcpRegistry, replacing the earlier moving-version launch command. Both agents still grant the full AWS MCP tool surface.

Docs:

  • docs/user-guide/README.md, docs/user-guide/agents.md, docs/user-guide/reference.md, docs/user-guide/tasks/README.md, docs/user-guide/tasks/install-claude-code.md: updated for the pinned MCP proxy and the plugin marketplace path. agents.md now spells out the Kiro CLI vs. Claude Code split for k-architect/k-developer's AWS MCP grant: Kiro CLI's allowedTools grants five tools (doc search, doc read, skill retrieval, region list, regional availability), while Claude Code grants the whole server via mcp__aws-mcp__* (8 tools, including the three AWS-API-acting ones). reference.md also gains a flags-table row for the new synth --claude-bundled-mcp-servers flag, linked from the marketplace install guide's AWS MCP section.
  • README.md's "Optional integrations" section was restructured: AWS MCP gets its own subsection describing the pinned proxy and the IAM condition keys; Playwright, Slack, and Asana integrations are listed separately as user-configured.
  • docs/user-guide/tasks/install-claude-plugin-marketplace.md is new, as noted above.

Other:

  • The Claude Code install summary now reports converted SOPs as "converted N SOP(s) into sop-* skill(s)" instead of the old "skipped N SOP(s)" wording, since SOPs install as invoke-only skills (disable-model-invocation: true) on Claude Code rather than being skipped.

Type of Change

  • CLI (cli/) change, Rust
  • Infrastructure / pipeline
  • Documentation
  • Dependency change (mcp-proxy-for-aws-cli pin in dependencies.mcpRegistry)

Testing

  • python3 -m pytest tests/ -q: 140 tests, 136 passed, 4 skipped.
  • cargo test --release -- --test-threads=1 (in cli/konductor-rs, summed across every test binary): 1776 passed, 0 failed.
  • make claude-plugin-check: both the marketplace manifest and the assembled flat plugin tree pass claude plugin validate --strict.
  • actionlint against .github/workflows/*.yml: 0 findings.
  • cd cli && make lint (clippy + fmt): clean.

Checklist

  • My code follows the style guidelines of this project (see AGENTS.md's Code Style & Conventions)
  • I have performed a self-review of my own changes
  • I have commented my code where necessary
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings: build is clean, and cd cli && make lint passes
  • I have added or updated tests that prove my change works (see Testing above)

Agents Affected

Note: Names below are the post-Konductor-rebrand agent identifiers.

  • k-architect: dependencies.mcpRegistry now pins mcp-proxy-for-aws-cli==1.7.0 for aws-mcp
  • k-developer: same pin

Notes for Reviewers

  • release/plugins currently mirrors main's tree. The first candidate PR opened against it will replace that entire tree with the assembled plugin files (agents, skills, README, license, rendered .claude-plugin/plugin.json/.mcp.json), so its diff will show the source tree being deleted. That's expected, not a mistake in the candidate-build step.
  • No VERSION bump in this PR; CHANGELOG.md entries stay under [Unreleased].

…w docs and script headers

CHANGELOG.md's [Unreleased] entry is now crisp bullets in final-state form,
matching the [1.1.0] entry's style. generated/claude-plugin/README.md,
install-claude-plugin-marketplace.md, README.md's Optional Integrations
section, and the six new scripts' header comments/docstrings no longer use
em dashes or "--" as a prose separator.
…ease

A final compliance pass over the whole committed tree (not just this PR's
diff hunks) found several internal-only references left over from earlier
scrub rounds, all in files this PR ships:

- CHANGELOG.md and README.md's Project Structure diagram named the
  internal package ("ASDLC Core AI Capabilities") and a phantom
  internal-tooling config file ("aim.json") that doesn't exist in this repo.
- cli.rs cited an internal "Engineering Design" doc section with no public
  equivalent.
- synth/claude.rs had four comments/test strings grounding behavior against
  "AIM" (an internal build tool) or an internal-only "aim-agent-authoring"
  skill; rewritten to cite Claude Code's own public hooks schema and this
  package's own installed output instead.
- synth/mod.rs and install.rs test fixtures used "asdlc-plan"/"asdlc-verify"
  SOP names; renamed to "k-plan"/"k-verify", matching this repo's own
  public SOP naming convention.
- BUILD_USER_GUIDE_PROMPT.md spelled out the internal package name in a
  forbidden-pattern warning; genericized to the same effect.

No functional behavior changes; verified with pytest, cargo test --release,
and make claude-plugin-check.
7475ba9 fixed 10 internal Amazon-tool references found in a compliance
sweep, but did so against the whole committed tree rather than this
PR's own feature diff (origin/main...787f4e4). A pre-existing internal
reference is only this PR's job to fix if the PR's own changes made it
stale or inaccurate; otherwise it belongs to the separate, dedicated
repo-wide cleanup already flagged for a future PR.

Re-reverted 6 of the 10 fixes whose surrounding content the PR's own
diff never touches, keeping the 4 that were already fixed for good
reason:

- CHANGELOG.md's file header ("All notable changes to X will be
  documented here.") is a one-time line unrelated to the PR's own
  [Unreleased] entries added a few lines below it. Reverted.
- README.md's Project Structure tree is byte-identical between
  origin/main and 787f4e4; the PR never edits that list. Reverted the
  aim.json line removal.
- cli.rs's exit-code contract comment sits nowhere near the PR's one
  hunk (new synth CLI args, ~460 lines away). Reverted the "(Engineering
  Design paragraph 6)" removal.
- synth/claude.rs: the module-level doc comment is substantially
  extended by this PR with new paragraphs documenting the very
  mcpServers/mcpRegistry behavior being added, so removing "AIM's own"
  from that same continuous doc block is defensible -- kept. The other
  three claude.rs locations (a doc comment for a function the PR never
  touches, and two test assertions where the PR's only nearby change is
  an unrelated `&BTreeMap::new()` parameter added for an unrelated
  signature change) are reverted; the PR's edits there have no bearing
  on what those comments describe.
- synth/mod.rs and install.rs: asdlc-plan/asdlc-verify test fixture
  names are pre-existing tests with no PR hunk anywhere near them.
  Reverted both.
- BUILD_USER_GUIDE_PROMPT.md's naming-convention warning is ~60 lines
  from the PR's one hunk (a page-alias table row) and covers an
  unrelated topic. Reverted.

Verified with pytest, cargo test --release (cli/mcp/shared workspaces),
and make claude-plugin-check.
Comment thread .github/workflows/release.yml Outdated
…n on PRs instead

release.yml's workflow_dispatch trigger let any branch run the full
release pipeline. check-version, build, and publish had no branch guard
and never consulted the dry-run flag, so a branch with an unreleased
VERSION bump could trigger a real `gh release create`, and by default,
publish-claude-plugin would then force-push that branch's build to the
public claude-plugin branch every marketplace install pulls from.

release.yml now triggers only on push to main, with no manual-dispatch
path to the publish/release logic anywhere in the workflow.

validate-pr.yml installs the claude CLI so its existing
`make claude-plugin-check` step runs the real `claude plugin validate
--strict` check against the assembled plugin tree on every PR, matching
what release.yml's publish-claude-plugin job validates before publishing.
This is read-only: validate-pr.yml has no publish, release, or push step
anywhere in its job graph.
@knihit
knihit requested a review from ihmaws October 1, 2026 15:17
knihit added 2 commits October 1, 2026 18:28
…rketplace

# Conflicts:
#	CHANGELOG.md
#	cli/konductor-rs/src/cli/install.rs
install.rs's format_install_summary doc comment still called its SOP
clause the "SOP-skip note" after sop_clause gained a "converted ..."
wording for harnesses that render SOPs as skills -- the comment half
described the Kiro case and missed the Claude one.

release.yml's publish-claude-plugin job carried a comment that just
restated the if: condition on the next line; removed it.
knihit added 5 commits October 6, 2026 03:23
Pin the credential-bearing AWS proxy, reject missing bundled MCP configuration, and publish the plugin branch with its immutable tag in one atomic push. Require the plugin branch to be provisioned before release.\n\nAlso shorten the affected public setup and generated-output documentation, and add regression coverage for exact proxy pinning and missing bundled configuration.
Keep user docs focused on installation, updates, and security boundaries. Move implementation detail out of public guides, remove duplicated rationale from generator scripts, and eliminate stale version claims and long comments that repeated code behavior.
Keep the generated plugin README concise, but retain the generated-tree layout, MCP namespacing, publication, rollback, and Kiro configuration guidance needed to maintain the release path.
Keep comments for plugin runtime constraints, compatibility behavior, release atomicity, and schema validation. Remove narration that duplicates commands, argument help, or maintainer documentation.\n\nAlso make Rust synthesis reject bundled MCP configuration that omits the required bundled object, matching the plugin renderer.
ihmaws
ihmaws previously approved these changes Oct 6, 2026
Comment thread docs/user-guide/tasks/install-claude-plugin-marketplace.md
Comment thread cli/konductor-rs/src/cli.rs Outdated
Comment thread .gitignore Outdated
Comment thread docs/user-guide/tasks/install-claude-plugin-marketplace.md
Comment thread .github/workflows/release.yml Outdated
Pin the AWS MCP proxy directly in k-architect's and k-developer's own
dependencies.mcpRegistry entries instead of routing it through a
separate --claude-bundled-mcp-config override file and CLI flag. Remove
that flag, its parsing, and the config-driven override path from
konductor synth; scripts/claude-plugin-mcp-servers.json now only lists
bundled server names for the allowlist.

Drop the repo-root plugin.json/.mcp.json generation path. The flat
release tree assembled by scripts/assemble-claude-plugin-branch.sh is
the only Claude Code plugin layout this repository ships; make
claude-plugin-check validates marketplace.json and that flat tree only.
Restore the plugin-scoped MCP tool-name rewrite in that assembly script,
which the repo-root removal had dropped, re-introducing a bare
mcp__aws-mcp__* grant in released output.

Document the session-wide MCP credential boundary and the plugin-scoped
permission glob (mcp__plugin_<name>_aws-mcp__*) needed to configure
Claude Code permissions for this install path.

Add an end-to-end regression test asserting the assembled tree's
rewritten grants, replace the config-override unit tests with coverage
for the direct-pin behavior, and update Makefile/README comments to
match the simplified pipeline.
knihit added 2 commits October 7, 2026 06:00
release.yml's publish-claude-plugin job no longer force-pushes the
assembled plugin tree directly to a public branch. It now pushes a
fresh, collision-resistant candidate branch (release version plus
workflow run identifier) and opens a draft pull request into the
protected release/plugins branch, using GITHUB_TOKEN scoped to
contents: write and pull-requests: write only.

The plugins branch ruleset blocks creation, update, deletion, and
non-fast-forward on release/plugins with no bypass actors, and the
org-wide ruleset requires two approvals before any merge -- so the
actual publish is a human-reviewed action, not something this
workflow can push through on its own. tag-claude-plugin-release.yml
creates the immutable claude-plugin-vX.Y.Z tag only after that PR
merges, driven by compute-plugin-release-tag.sh's merged/base-ref/
head-ref guards.

validate-plugin-publish-preconditions.sh gates the push itself:
confirms the checkout matches the expected release ref by commit SHA,
and that neither the derived candidate branch nor the release tag
already exists on the remote, failing closed on any inconclusive
remote lookup.

A new opt-in rehearsal workflow, plugin-publish-dry-run.yml, exercises
the same push-candidate-branch/open-draft-PR path under a distinct
branch prefix so it can never produce a real tag, gated behind a typed
confirmation input.

marketplace.json's source ref moves from the old claude-plugin branch
to release/plugins to match.

Adds unit tests for all three new scripts (28 cases): tag-computation
guards, candidate-branch derivation, and the four publish-precondition
checks (ref match, protected-name refusal, branch collision, tag
collision), with the collision checks run against a disposable local
bare repo rather than the real remote.
Updates CHANGELOG.md, the plugin README, and the two install guides to
describe the candidate-branch-plus-draft-PR publish path instead of
the old force-push-to-claude-plugin description, including the dry-run
rehearsal workflow and the recovery guidance for a bad release.

@ihmaws ihmaws left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Final questions about the workflow. Should the plugins branches be orphaned from the main trunk?

Also, should their be harness specific variants (claude-code, kiro, etc)?

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/tag-claude-plugin-release.yml Outdated
Comment thread .github/workflows/plugin-publish-dry-run.yml Outdated
Both the release and dry-run publish jobs built their candidate commit
with `git init` in a throwaway directory, so the candidate shared no
history with release/plugins and `gh pr create` had no common ancestor
to diff against.

scripts/build-plugin-candidate-commit.sh replaces that: it checks out
the candidate branch directly on top of origin/<base-branch>, clears
every previously tracked path, and overlays the already-assembled flat
artifact before committing. The resulting commit's parent is
release/plugins' own tip, giving GitHub a real merge base. Both
workflows now call this one script instead of each inlining their own
git init.
knihit added 2 commits October 7, 2026 22:43
Bind confirm_dry_run through a step-scoped env: block instead of
interpolating the raw input into the shell script body, closing a
shell-injection vector on a free-form workflow_dispatch string.

Scope GH_TOKEN to only the two steps that call the GitHub API
(precondition validation, draft PR creation) instead of job-wide,
so unrelated steps (checkout, build, npm install) never see it.

Run the cleanup-identifiers step unconditionally (if: always()) and
report the candidate branch independently of PR outputs, so a run
that fails after pushing a branch but before opening a PR still
prints something a reviewer can act on.

Add regression tests asserting these properties against the parsed
workflow YAML.
…t.closed

release/plugins is artifact-only, so a pull_request-triggered workflow
on that branch never finds a copy of itself to run. Switch
tag-claude-plugin-release.yml to a workflow_dispatch that takes a merged
PR number, verifies it live via the GitHub API (merged, correct base,
head prefix, merge commit SHA), and fails closed on a non-main dispatch
ref before trusting any helper script. Read the release version from
the published .claude-plugin/plugin.json instead of a VERSION file,
which release/plugins never carries. Scope GH_TOKEN to only the steps
that call the GitHub API or push the tag.
ihmaws
ihmaws previously approved these changes Oct 13, 2026
validate-pr.yml already builds and strictly validates the Claude Code
plugin on every PR, making the manual dry-run rehearsal redundant. It
also pushed a real branch and opened a real draft PR into
release/plugins with contents:write and pull-requests:write, which is
unnecessary exposure for a rehearsal step.

Removes the workflow and the scaffolding that existed only to support
it: the dry-run-only --prefix option on derive-plugin-candidate-branch.sh
and its tests, the workflow's own regression test file, and the test
asserting both workflows called the shared candidate-commit script.
Rewords comments and docstrings in validate-plugin-publish-preconditions.sh,
compute-plugin-release-tag.sh, build-plugin-candidate-commit.sh, and the
remaining tests that referenced the removed workflow, without changing
their guard logic. Trims the CHANGELOG entry to match.

The real publish path (candidate branch + draft PR into release/plugins,
tagged via tag-claude-plugin-release.yml) is unchanged.
Point the .gitignore and validate-pr.yml comments at the current
publish path (release/plugins via a candidate branch and draft PR)
instead of the old claude-plugin branch, and drop the now-false
"both shapes" claim since the repo-root plugin layout was removed.

Also corrects the k-architect MCP row in agents.md: the five-tool
allowedTools grant only applies on Kiro CLI. On Claude Code the
agent's frontmatter grants the whole aws-mcp server via
mcp__aws-mcp__*, which includes three additional AWS-API-acting
tools.
build-plugin-candidate-commit.sh now reports committed=true|false (via
GITHUB_OUTPUT and stdout), and release.yml's publish-claude-plugin job
gates the branch push and draft-PR steps on it, with a new step that
prints a clear notice and ends the job green when the assembled plugin
tree already matches release/plugins' tip exactly. Before, those steps
ran unconditionally and gh pr create failed with "No commits between",
leaving a dangling pushed branch.

Also binds every step output used in that job's run bodies through env
instead of interpolating raw steps.*.outputs.* expressions directly,
matching tag-claude-plugin-release.yml's existing convention.
…entials

The manually built AUTHORIZATION header on the tag push was dead: the
merge-commit checkout step already persists write-scoped credentials
(actions/checkout's default persist-credentials behavior, enabled by
this job's contents: write permission), so the header was ignored in
favor of that persisted credential. Drops it and the now-unused
GH_TOKEN binding on that step.

Also adds a concurrency group to the job so two operator-triggered
dispatches can't both pass "tag does not already exist" and then race
to push the same immutable tag ref.
claude-plugin-check printed its success line even when both claude
plugin validate calls were skipped because the CLI isn't on PATH. It
now tracks whether validation actually ran and prints a distinct
"validation skipped" notice instead of a false success claim. CI is
unaffected: validate-pr.yml and release.yml both install the claude
CLI first, so this only changes the message a developer sees locally
without the CLI installed.
Adds the same [ -d "$d" ] || continue guard that
copy-skills-with-collision-guard.sh already uses, so an unmatched
sop-*/ glob doesn't get treated as a literal directory name.
simonkrol
simonkrol previously approved these changes Oct 13, 2026
Comment thread cli/konductor-rs/src/cli/synth/mod.rs
Comment thread scripts/derive-plugin-candidate-branch.sh Outdated
ihmaws
ihmaws previously approved these changes Oct 13, 2026
Add the flag to reference.md's per-command flags table and link to it
from the plugin marketplace install guide's AWS MCP section, where the
plugin build already relies on it to select which dependencies.mcpRegistry
entries get merged into rendered Claude Code agent frontmatter.
derive-plugin-candidate-branch.sh, build-plugin-candidate-commit.sh, and
validate-plugin-publish-preconditions.sh each had their own copy of the
main|mainline|release/plugins case statement. Extract it to a shared
is_protected_branch helper in scripts/lib/protected-branches.sh that each
script sources; each caller keeps its own error message, exit code, and
the point in its flow where the check runs.

Add test_protected_branches_lib.py for the helper itself.
@knihit
knihit dismissed stale reviews from ihmaws and simonkrol via 26a141a October 13, 2026 23:05
@knihit
knihit merged commit f744a7c into main Oct 14, 2026
7 checks passed
@knihit
knihit deleted the feat/claude-plugin-marketplace branch October 14, 2026 13:44
knihit added a commit that referenced this pull request Oct 16, 2026
doctor/power-cli ordering, harness-scoped uninstall cleanup, malformed-
tag rejection, and binary --version validation all fix konductor-setup
code that is part of this same unreleased [1.1.0] Power and never
shipped on its own. Remove the entries instead of describing a fix to
something nobody ever saw broken. PR #28's Claude plugin entries are
unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants