Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"github"
],
"dependencies": [
"supertool",
"supertool-cli",
"remember",
"claude-jit-context"
]
Expand Down
4 changes: 2 additions & 2 deletions bin/oss-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -1258,7 +1258,7 @@ if not isinstance(plugins, dict):
installs = []
malformed = ""
for key, entries in plugins.items():
if key.split("@")[0] != "supertool":
if key.split("@")[0] not in ("supertool-cli", "supertool"):
continue
if entries and not isinstance(entries, list):
malformed = "%s's supertool entry is not a JSON array" % registry
Expand Down Expand Up @@ -1499,7 +1499,7 @@ if not isinstance(plugins, dict):
installs = []
malformed = ""
for key, entries in plugins.items():
if key.split("@")[0] != "supertool":
if key.split("@")[0] not in ("supertool-cli", "supertool"):
continue
if entries and not isinstance(entries, list):
malformed = "%s's supertool entry is not a JSON array" % registry
Expand Down
1 change: 1 addition & 0 deletions changelog.d/1806.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- The manifest now declares its supertool dependency as `supertool-cli`, the name the dpt-plugins marketplace publishes it under, so installing oss no longer shows `Dependency "supertool@dpt-plugins" is not installed` and leaves `/oss:run` unregistered (#1806). `/oss:doctor` and `oss-workspace` recognise an install registered under either name.
23 changes: 20 additions & 3 deletions scripts/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,18 @@ def check_tool(name, probe):
# mid-session, has none -- and no diagnostic said so.
SUPERTOOL_ENTRY = "supertool"

#: The supertool plugin's marketplace names, current first. dpt-plugins renamed
#: it from `supertool` to `supertool-cli`; an install registered before the
#: rename keeps the old key in `installed_plugins.json` and the plugin cache, so
#: every lookup by plugin name accepts both.
SUPERTOOL_PLUGIN_NAMES = ("supertool-cli", "supertool")


def is_supertool_plugin_key(key):
"""True when an `installed_plugins.json` key (`name@marketplace`) is supertool."""
return str(key).split("@")[0] in SUPERTOOL_PLUGIN_NAMES


#: The file the hook links to, and the file a supertool checkout carries at its own
#: root. Both spellings are the same name on purpose; which one is meant is decided by
#: what else is beside it.
Expand Down Expand Up @@ -1751,11 +1763,15 @@ def _supertool_tree_identity_confirmed(directory, dependency_repos=None, run=Non
confirm" rather than reading both as the same silence.
"""
repos = (
dependency_repositories(["supertool"])
dependency_repositories(list(SUPERTOOL_PLUGIN_NAMES))
if dependency_repos is None
else dependency_repos
)
expected = repos.get("supertool") if isinstance(repos, dict) else None
expected = (
next((repos[n] for n in SUPERTOOL_PLUGIN_NAMES if repos.get(n)), None)
if isinstance(repos, dict)
else None
)
if not expected:
return False, "no installed supertool dependency to compare against"
match = re.search(r"github\.com[:/]+([^/]+/[^/]+?)(?:\.git)?/?$", str(expected))
Expand Down Expand Up @@ -4480,7 +4496,7 @@ def _supertool_installs():
installs = [
entry.get("installPath")
for key, entries in plugins.items()
if key.split("@")[0] == "supertool"
if is_supertool_plugin_key(key)
for entry in (entries if isinstance(entries, list) else [])
if isinstance(entry, dict) and entry.get("installPath")
]
Expand Down Expand Up @@ -6371,6 +6387,7 @@ def published_versions(repos):
#: already is. A fourth dependency with a fourth shape lands in `could-not-run`
#: below until it is added here.
DEPENDENCY_DIAGNOSTICS = {
"supertool-cli": {"kind": "op", "argv": ["doctor"]},
"supertool": {"kind": "op", "argv": ["doctor"]},
"remember": {"kind": "script", "rel": ("scripts", "doctor.sh")},
JIT_PLUGIN: {"kind": "script", "rel": ("scripts", "jit-doctor.sh")},
Expand Down
70 changes: 70 additions & 0 deletions tests/test_supertool_cli_rename.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""The supertool plugin is published as `supertool-cli` in the dpt-plugins marketplace.

The marketplace renamed it from `supertool` (dpt-plugins PR #7). A manifest still
declaring `supertool` asks the harness for `supertool@dpt-plugins`, which no longer
exists, and every install of this plugin then shows a dependency error. Installs
registered under the old key must still be recognised, so every name check here
accepts both spellings.
"""

import json
from pathlib import Path

import doctor

ROOT = Path(__file__).resolve().parent.parent
REPO = "https://github.com/Digital-Process-Tools/claude-supertool"


def _declared():
manifest = ROOT / ".claude-plugin" / "plugin.json"
return json.loads(manifest.read_text(encoding="utf-8"))["dependencies"]


def test_manifest_declares_the_published_name():
deps = _declared()
assert "supertool-cli" in deps, deps
assert "supertool" not in deps, deps


def test_every_declared_dependency_has_a_diagnostic_route():
missing = [
name for name in _declared() if name not in doctor.DEPENDENCY_DIAGNOSTICS
]
assert missing == [], missing


def test_both_names_are_supertool_plugin_keys():
assert doctor.is_supertool_plugin_key("supertool-cli@dpt-plugins")
assert doctor.is_supertool_plugin_key("supertool@dpt-plugins")
assert not doctor.is_supertool_plugin_key("supertool-extra@dpt-plugins")
assert not doctor.is_supertool_plugin_key("remember@dpt-plugins")


def _fake_git_remote(argv, **kwargs):
class Result:
returncode = 0
stdout = "[email protected]:Digital-Process-Tools/claude-supertool.git\n"
stderr = ""

return Result()


def test_identity_check_reads_the_new_name(tmp_path):
confirmed, reason = doctor._supertool_tree_identity_confirmed(
tmp_path, dependency_repos={"supertool-cli": REPO}, run=_fake_git_remote
)
assert (confirmed, reason) == (True, None)


def test_identity_check_still_reads_the_old_name(tmp_path):
confirmed, reason = doctor._supertool_tree_identity_confirmed(
tmp_path, dependency_repos={"supertool": REPO}, run=_fake_git_remote
)
assert (confirmed, reason) == (True, None)


def test_launcher_accepts_both_registry_keys():
text = (ROOT / "bin" / "oss-workspace").read_text(encoding="utf-8")
assert 'key.split("@")[0] != "supertool"' not in text
assert text.count('key.split("@")[0] not in ("supertool-cli", "supertool")') == 2
4 changes: 3 additions & 1 deletion tests/test_version_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,6 @@ def test_manifest_declares_its_plugin_dependencies():
turns a documented capability into a missing tool at runtime.
"""
dependencies = _manifest()["dependencies"]
assert dependencies == ["supertool", "remember", "claude-jit-context"], dependencies
assert dependencies == ["supertool-cli", "remember", "claude-jit-context"], (
dependencies
)
Loading