Skip to content

Repository files navigation

claude-oss

Run an open-source repo as its maintainer. Triage the tracker, delegate the work, review hard, merge on green.

Version Python OS License


The problem

A maintainer loop written as prose gets copied between repos, and the copies drift. Fixing a triage rule means editing it in three places and remembering the third. Repos that never got the copy run no loop at all.

This packages the loop once: one skill, the agents it delegates to, a handful of commands. Everything that differs between repos — default branch, label spellings, version sites, test command — lives in a config file the plugin writes by probing the repo, not in the prose.

Install

/plugin marketplace add Digital-Process-Tools/claude-marketplace
/plugin install oss@dpt-plugins

Then run /reload-plugins, or restart Claude Code. Plugin registrations are read once at session start, and installing mid-session leaves the agent registry as it was.

That step is not cosmetic, because its failure does not look like a missing step. In a session that installed the plugin and never reloaded, all seven oss: skills resolve and none of the four oss: agents does — which reads as a plugin that is installed and working with a broken agents/ directory, and has already produced two wrong bug reports against this repo (#140). If a spawn comes back Agent type 'oss:...' not found, reload before concluding anything about the files.

Installing pulls in supertool, remember and claude-jit-context automatically — they are declared dependencies and resolve from the same marketplace.

After cloning a repo you maintain

Install the launcher once, from this plugin's own checkout:

ln -sf "$PWD/bin/oss-workspace" ~/.local/bin/oss-workspace

Then, in any repo you maintain:

cd the-repo
oss-workspace

Without that symlink the launcher is only on the path it ships at, so run it as bin/oss-workspace from this checkout.

That opens a session over the repo you are standing in, with the maintainer loop already running — or with /oss:setup if the repo has no .oss.json yet, because a tick against guessed values merges into the wrong place confidently.

Setup is not the whole onboarding. It deliberately writes nothing tracked, which is what makes it safe to run anywhere and also what leaves the repo half-furnished — configured, and still without a CLAUDE.md, a security policy, issue templates or a changelog gate. /oss:scaffold is the second step, and it is separate because it writes tracked files: they want a branch, a diff and a review. A repo that goes straight from setup to ticking never fails; it just runs the loop against furniture nobody added.

So setup does not merely recommend the second step: it ends by running scaffold's own read-only plan and relaying it. Nothing is written — the gap arrives as a measured per-file list (create, present, replace, decline) rather than as a sentence suggesting you go and look, and a plan that could not run is reported as unmeasured rather than as nothing to do. Writing still happens only under /oss:scaffold.

It does not set up a board. The session is started able to receive watch-channel events, but radar reads its tiers from that repo's own .supertool.json, and a fresh clone has none. The launcher says which of the two is missing rather than reporting the session as armed — a channel nobody publishes to looks exactly like a quiet board.

The channel's name gets the same treatment, because a name that does not arrive intact puts the session on the socket shared with every repo that declares none, which renders as a quiet board too. One rule decides what a name may be, and both roads reach it: a watch_name declared in the repo's .supertool.json and a name derived from repo in .oss.json are checked by the same function, and a value that cannot be used as a path component is refused out loud rather than exported. A refusal names the channel the session actually landed on rather than assuming the shared one: an already exported SUPERTOOL_WATCH_NAME wins over both roads, so a refusal there costs nothing and the session stays on that channel, and the receipt says so. There is a third road out of the declared route too — a name a repo declares and this console's encoding cannot carry. The name is read back through the launcher's own stdout, so an unencodable one cannot arrive at all; it is reported as declared-and- unrenderable rather than printed mangled or silently derived over, because a declaration that exists is not a declaration that is absent. What that rule deliberately does not decide is whether supertool will accept the name — it has its own pattern, with a length cap. Rather than carry a copy of it here to go stale, the launcher reads that rule out of the installed supertool and reports what it finds, in three states: accepted says nothing, a name the consumer will discard is named with its length and the rule that refused it, and not being able to ask — no rule where the launcher looks, a module that will not load — is said just as loudly, because silence there is indistinguishable from acceptance.

Before the session starts working, the launcher runs /oss:doctor's diagnostic over the repo it just resolved, so a broken setup surfaces at second zero rather than after a tick has been spent against it. The verdict is parsed, never the exit status: the diagnostic exits 0 always, by contract, so doctor.sh || warn reads a pass on not usable -- 4 failure(s) exactly as loudly as on ok. A healthy repo costs one line; anything else relays the diagnostic's whole output, because once the answer is not ok a launcher has no standing to decide which line you needed. It never refuses to open — a maintainer whose config is broken is exactly the person who needs a session in which to fix it.

Six answers, and the last three are why this is not a one-liner: ok, usable with gaps, not usable, could not run, a verdict word this launcher does not recognise, and no verdict line at all. That last one splits again by whether the diagnostic printed nothing or could not be started, which the launcher tells apart and says. A check that never fired and a check that found nothing print the same thing otherwise.

It costs what the diagnostic costs. Measured on macOS against a repo with supertool, gh and node on PATH: the launcher opens in 0.45 s without it and 2.5 s with it, most of that the dependency-version check reaching the network — which is bounded at 25 s per declared dependency and 20 s per probed binary, so an offline or hung network is slower than that, not faster. Set OSS_WORKSPACE_SKIP_DOCTOR=1 (any non-empty value) to skip it — the skip is announced, with the state of the repo reported as unknown rather than fine. The run is also announced before it starts, carrying that variable's name, because an escape hatch you can only read about after the wait is one nobody waiting has.

Install the launcher once:

ln -sf "$PWD/bin/oss-workspace" ~/.local/bin/oss-workspace

The working directory is the selection: it opens that repo, never this plugin's checkout.

Commands

Command What it does
/oss:tick One pass of the maintainer loop: board, decide, delegate, review, merge on green.
/oss:setup Probes the repo and writes .oss.json. Measures; never assumes — a version site is a file read and found to carry a version, and every label that matched no pattern is named.
/oss:scaffold Adds the missing repo furniture. Never overwrites; shows before it writes. Reports what it will not do: create a label, guess a required-check count, or generate a test workflow. Its receipt reads both halves of the board question, so a repo it scaffolded before the preset was added — tiers registered, no route to the op that reads them, and unreachable by a template fix because .supertool.json is never replaced — is reported rather than called clean.
/oss:triage One triage sweep — priority, lane, milestone, the clusters one change would fix, the cohort burn-down with the limit it was counted under, and what the board is lying about.
/oss:changelog Checks changelog fragments, or folds them for a release.
/oss:release Gates, version sites, tag, and — where .oss.json says so — the GitHub Release, notes and all.
/oss:doctor Config, dependencies, clone, worktree root, state file — including whether /oss:tick can actually read it, which is not the same question as whether it is there — which watch channel this repo resolves to, which decides whether its board is its own fleet or somebody else's, and whether anything publishes to that board at all: a registered radar tier is one half, a route to the op that reads it is the other, and a channel with neither renders exactly like a healthy one. Which process holds the socket is not established, and the output says so. Also which copy of this plugin answered the invocation — compared by content, because two copies a whole release cycle apart declare the same manifest version, and reported for one command rather than for the session. Exits 0 always.

Status

Tested, not proven. The claim this section used to make — that no real issue had gone from triage through to a merge — stopped being true some time before anyone edited it: the loop now maintains this repository, and the triage-to-merge round trip has run many times over, including the releases cut with /oss:release.

What that does not establish is the part most users care about. Almost everything this plugin claims about a repository it has scaffolded still rests on tests and scratch runs rather than on a repo somebody maintains through it; owned files are known to have gone stale in the field, in every repository carrying them, with no observed repair in any of them.

The measured version of that, with each claim graded observed or reasoned and dated to the commit it was taken at, is What is not proven yet — re-derived at each release rather than edited. It is deliberately not restated here: a second copy is the one that drifts, and this section is the proof of it.

Installing this plugin does not put a maintainer loop in your repository. The workflow it installs is a changelog gate that fires on a pull request; every other step of the loop is a slash command somebody types. The one thing it starts on a clock is a .github/dependabot.yml, seeded once if you do not already have one and yours to delete — and nothing here reviews or merges what that opens. Nothing schedules a tick, a re-scaffold or an update of an owned file, so a repository that installed the plugin and was never ticked again looks, from here, exactly like a healthy one. What that would take is recorded in Autonomy: what the loop reaches, and what it does not, which is a record of the gap and deliberately not a design.

Development

python3 -m pytest tests/ -q

CI runs the suite on ubuntu, macOS and Windows across Python 3.9-3.12. A green macOS run is not evidence on its own.

A separate ubuntu leg runs bash -n and shellcheck -S warning over every tracked shell source. Which files those are is derived rather than listed — python3 scripts/shell_sources.py prints the list, selecting by extension or by shebang so an extensionless script is covered by the commit that adds it. It exits non-zero when it matches nothing, so an empty selection fails the leg instead of linting no files and passing.

License

Community License — see LICENSE. Source-available, not open source: no commercial redistribution, no competing use.

Built by Digital Process Tools in Toulouse, France.

About

Run an open-source repo as its maintainer: triage the tracker, delegate the work, review hard, merge on green. One loop, one version, and per-repo differences in config instead of prose.

Topics

Resources

Code of conduct

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages