devops_utils.agent.install module
Install bundled skills and wire the MCP server into an agent’s config.
Framework-agnostic helpers used by the devops-utils setup CLI. Kept out of
the Click layer so the copy/merge logic stays importable and testable on its own
(mirrors how devops_utils.agent.tools stays free of any framework import).
The bundled skill markdown lives at devops_utils/agent/skills/ — either a
flat *.md or a <dir>/SKILL.md with reference files beside it — and is
read through importlib.resources, so it resolves the same way from a
source checkout and from an installed wheel.
- class devops_utils.agent.install.BundledSkill(name: str, source: str, files: dict[str, str])[source]
Bases:
objectOne skill bundled with the package.
- Variables:
name (str) – Frontmatter
name:(falls back to the file/dir stem).source (str) – Bundled entry name —
foo.mdfor a single-file skill,foofor a directory skill.files (dict[str, str]) –
{relative_path: text}. A single-file skill has one entry,SKILL.md; a directory skill carriesSKILL.mdplus its reference files (e.g.reference/operations.md), relative paths always/-separated.
- devops_utils.agent.install.ConfirmOverwrite
Callback deciding whether an existing target may be overwritten. Returning
Truewrites the new content;Falseleaves the file untouched. The install helpers stay UI-free — the Click layer supplies the prompt.alias of
Callable[[OverwriteRequest],bool]
- devops_utils.agent.install.MARKETPLACE_NAME = 'carneirofc'
Marketplace name users add before installing the plugin (
/plugin install devops-utils@<MARKETPLACE_NAME>).
- devops_utils.agent.install.MCP_COMMAND = 'devops-utils-mcp'
Console script that launches the stdio MCP server (see
[project.scripts]).
- devops_utils.agent.install.MCP_SERVER_NAME = 'devops-utils'
Name under which the MCP server is registered in an agent’s config.
- class devops_utils.agent.install.OverwriteRequest(path: Path, new_text: str, existing_text: str, label: str)[source]
Bases:
objectOne pending overwrite, handed to a
ConfirmOverwritecallback.- Variables:
path (pathlib.Path) – File that would be written.
new_text (str) – Content that would replace what is on disk.
existing_text (str) – Content currently on disk.
label (str) – Human-facing name for the item (usually
str(path), but for a merged JSON config it names the entry, not just the file).
- devops_utils.agent.install.PLUGIN_NAME = 'devops-utils'
Claude Code plugin name. The harness namespaces a plugin’s skills/agents as
<plugin-name>:<component>, so with this name the bundled skills/agents list asdevops-utils:azure-devops-research,devops-utils:azdo-workitem-analyst, etc. — distinguishable at a glance from unrelated skills/agents.
- devops_utils.agent.install.env_template() str[source]
Return an
.envscaffold for the Azure DevOps credentials.
- devops_utils.agent.install.install_agents(dest: Path, force: bool = False, confirm: Callable[[OverwriteRequest], bool] | None = None) tuple[list[Path], list[Path]][source]
Copy the bundled Claude Code agents into
dest/agents/<name>.md.Claude Code discovers subagents as single
.mdfiles in anagents/directory (user scope~/.claude/agents, project scope.claude/agents).- Parameters:
dest – Base directory to install into (e.g.
~/.claude).force – Overwrite existing files instead of skipping them.
confirm – Asked per existing file when
forceis not set;Noneskips existing files outright.
- Returns:
A
(written, skipped)tuple of destination paths.
- devops_utils.agent.install.install_plugin(repo_root: Path, force: bool = False, confirm: Callable[[OverwriteRequest], bool] | None = None) tuple[list[Path], list[Path]][source]
Generate the Claude Code plugin + marketplace tree under
repo_root.Lays the bundled skills/agents out where Claude Code discovers them for a plugin named
PLUGIN_NAME, and writes the two manifests:repo_root/plugins/devops-utils/skills/<name>/SKILL.md(viainstall_skills()with the"claude"layout)repo_root/plugins/devops-utils/agents/<name>.md(viainstall_agents())repo_root/plugins/devops-utils/.claude-plugin/plugin.jsonrepo_root/.claude-plugin/marketplace.json
The skill/agent markdown is copied byte-for-byte from the bundled sources; the
<plugin>:<name>namespace is derived by Claude Code from this layout, so no frontmatter rewriting is needed.- Parameters:
repo_root – Repository root to generate the plugin tree into.
force – Overwrite existing files instead of skipping them.
confirm – Asked per existing file when
forceis not set;Noneskips existing files outright.
- Returns:
A
(written, skipped)tuple of destination paths.
- devops_utils.agent.install.install_skills(dest: Path, layout: str = 'claude', force: bool = False, confirm: Callable[[OverwriteRequest], bool] | None = None) tuple[list[Path], list[Path]][source]
Copy the bundled skills into
dest.- Parameters:
dest – Base directory to install into.
layout –
"claude"writesdest/skills/<name>/SKILL.md(Claude Code discovery layout) plus any reference files beside it;"flat"writes a single-file skill asdest/<filename>and a directory skill asdest/<dir>/....force – Overwrite existing files instead of skipping them.
confirm – Asked per existing file when
forceis not set;Noneskips existing files outright.
- Returns:
A
(written, skipped)tuple of destination paths.
- devops_utils.agent.install.install_tracker(dest: Path, project_name: str, done_state: str = 'Closed', org_url: str | None = None, parent_epic: int | None = None, area_path: str | None = None, default_tags: list[str] | None = None, force: bool = False, confirm: Callable[[OverwriteRequest], bool] | None = None) tuple[list[Path], list[Path]][source]
Write the Azure DevOps tracker config for mattpocock-style skills.
Renders the bundled templates (
agent/trackers/*.md) intodest/docs/agents/, substituting the placeholders computed by_tracker_substitutions(). The resultingissue-tracker.md/triage-labels.mdare the config files skills like mattpocock/skills read to learn how to talk to the issue tracker.- Parameters:
dest – Repository root to install into (files go to
docs/agents/).project_name – Azure DevOps team project name.
done_state –
System.Statevalue that means “closed” in the project’s process template (e.g.Closed,Done,Resolved).org_url – Azure DevOps organization/collection URL recorded in the defaults table (the env var stays authoritative at runtime).
parent_epic – Work-item id of the Epic new items are parented under.
area_path – Default
System.AreaPathfor creates and queries.default_tags – Tags applied to every created item and used to scope queries.
force – Overwrite existing files instead of skipping them.
confirm – Asked per existing file when
forceis not set;Noneskips existing files outright.
- Returns:
A
(written, skipped)tuple of destination paths.
- devops_utils.agent.install.iter_bundled_agents() list[tuple[str, str, str]][source]
List the Claude Code agent files bundled with the package.
- Returns:
A list of
(agent_name, filename, text)tuples, sorted by filename.agent_namecomes from the frontmattername:(falling back to the file stem).
- devops_utils.agent.install.iter_bundled_skills() list[BundledSkill][source]
List the skills bundled with the package, sorted by source name.
A skill is either a flat
<stem>.mdor a<stem>/directory holdingSKILL.mdplus reference files it links to — the progressive-disclosure shape for skills too long to load in one go.
- devops_utils.agent.install.marketplace_manifest() dict[str, object][source]
Return the
.claude-plugin/marketplace.jsoncontents for the repo.Declares a single-plugin marketplace whose
sourceis the relative path./plugins/devops-utils(Claude Code resolves it from the marketplace root, i.e. the repo root). Users install with:/plugin marketplace add carneirofc/devops-utils /plugin install devops-utils@carneirofc
- devops_utils.agent.install.mcp_server_entry(use_uvx: bool = True) dict[str, object][source]
Return the MCP
mcpServersentry for the stdio server.- Parameters:
use_uvx – When
True(default) the entry launches the server throughuvx --from "devops-utils[mcp]" devops-utils-mcp— zero-install, nothing needs to be onPATHbeyonduv. WhenFalsethe entry is the baredevops-utils-mcpconsole script, for environments where the package is installed (pip install "devops-utils[mcp]") anduvis not available.
- devops_utils.agent.install.merge_mcp_config(path: Path, name: str = 'devops-utils', force: bool = False, use_uvx: bool = True, confirm: Callable[[OverwriteRequest], bool] | None = None) tuple[Path, bool][source]
Register the MCP server in a JSON config, preserving other servers.
Loads
pathif it exists, setsmcpServers[name]tomcp_server_entry()without touching sibling servers, and writes it back pretty-printed. Works for both~/.claude.json(user) and.mcp.json(project).- Parameters:
path – Path to the JSON config file (created if missing).
name – Server key to register under.
force – Overwrite an existing entry for
name. WhenFalseand the entry already exists, the file is left unchanged.use_uvx – Passed to
mcp_server_entry()—True(default) writes the zero-installuvxlauncher,Falsethe on-PATHdevops-utils-mcpconsole script.confirm – Asked before replacing an existing entry when
forceis not set;Noneleaves the entry alone. The request compares the entry JSON, not the whole file — sibling servers are never at stake.
- Returns:
A
(path, changed)tuple.changedisFalsewhen an entry fornamealready existed and neitherforcenorconfirmallowed replacing it.
- devops_utils.agent.install.plugin_manifest() dict[str, object][source]
Return the
.claude-plugin/plugin.jsoncontents for the bundled plugin.The plugin name drives Claude Code’s namespacing: skills and agents shipped under it list as
devops-utils:<component>(seePLUGIN_NAME). The MCP server is intentionally not declared here — a plugin-bundled MCP server is re-scoped tomcp__plugin_<plugin>_<server>__<tool>, which would break the agents’tools:frontmatter that targetsmcp__devops-utils__azdo_*. MCP stays wired viamerge_mcp_config()/setup mcpinstead.versionis deliberately omitted (it is optional): the package uses VCS-derived versioning, so a value here would change on every commit and make the committed manifest churn and drift from a regeneration.