Skip to content

Commit f9bc659

Browse files
authored
Merge pull request #14 from dmoliveira/feat/doctor-json-ci-make-release
Add doctor JSON output, CI checks, and Makefile workflow
2 parents 02cab4c + c38ba08 commit f9bc659

File tree

8 files changed

+162
-21
lines changed

8 files changed

+162
-21
lines changed

.github/workflows/ci.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
validate:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
15+
- name: Set up Python
16+
uses: actions/setup-python@v5
17+
with:
18+
python-version: "3.12"
19+
20+
- name: Validate scripts and config
21+
run: |
22+
python -m py_compile scripts/*.py
23+
python -m json.tool opencode.json > /dev/null
24+
25+
- name: Installer smoke test
26+
run: |
27+
TMP_HOME="$(mktemp -d)"
28+
HOME="$TMP_HOME" REPO_URL="$GITHUB_WORKSPACE" REPO_REF="$GITHUB_SHA" ./install.sh --skip-self-check
29+
HOME="$TMP_HOME" python "$TMP_HOME/.config/opencode/my_opencode/scripts/mcp_command.py" status
30+
HOME="$TMP_HOME" python "$TMP_HOME/.config/opencode/my_opencode/scripts/plugin_command.py" profile lean
31+
HOME="$TMP_HOME" python "$TMP_HOME/.config/opencode/my_opencode/scripts/plugin_command.py" doctor --json

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
__pycache__/
2+
*.pyc
3+
.DS_Store

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
All notable changes to this project are documented in this file.
44

5+
## v0.1.1 - 2026-02-12
6+
7+
### Adds
8+
- Added `/plugin doctor --json` output for automation and CI integrations.
9+
- Added plugin command next-step suggestions and `/plugin help` guidance output.
10+
- Added autocomplete-friendly shortcut commands like `/plugin-enable-supermemory` and `/plugin-profile-stable`.
11+
- Added GitHub Actions CI workflow for script/config validation and installer smoke tests.
12+
- Added `Makefile` with `help`, `validate`, `doctor`, `doctor-json`, `install-test`, and `release` targets.
13+
14+
### Changes
15+
- Expanded README with maintenance commands and machine-readable diagnostics usage.
16+
17+
### Fixes
18+
- Added `.gitignore` entries for Python cache artifacts.
19+
520
## v0.1.0 - 2026-02-11
621

722
### Adds

Makefile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.DEFAULT_GOAL := help
2+
3+
.PHONY: help validate doctor doctor-json install-test release
4+
5+
help: ## Show available targets
6+
@awk 'BEGIN {FS = ":.*##"} /^[a-zA-Z_-]+:.*##/ {printf "%-14s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
7+
8+
validate: ## Validate scripts and JSON config
9+
python3 -m py_compile scripts/*.py
10+
python3 -m json.tool opencode.json >/dev/null
11+
12+
doctor: ## Run plugin diagnostics (human-readable)
13+
python3 scripts/plugin_command.py doctor
14+
15+
doctor-json: ## Run plugin diagnostics (JSON)
16+
python3 scripts/plugin_command.py doctor --json
17+
18+
install-test: ## Run installer smoke test in temp HOME
19+
@TMP_HOME="$$(mktemp -d)"; \
20+
HOME="$$TMP_HOME" REPO_URL="$(PWD)" REPO_REF="$$(git rev-parse --abbrev-ref HEAD)" ./install.sh --skip-self-check; \
21+
HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/mcp_command.py" status; \
22+
HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/plugin_command.py" profile lean; \
23+
HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/plugin_command.py" doctor --json
24+
25+
release: ## Create and publish release (VERSION=0.1.1)
26+
@test -n "$(VERSION)" || (echo "VERSION is required, eg: make release VERSION=0.1.1" && exit 2)
27+
git tag -a "v$(VERSION)" -m "v$(VERSION)"
28+
git push origin "v$(VERSION)"
29+
gh release create "v$(VERSION)" --generate-notes

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Use these directly in OpenCode:
9090
/plugin status
9191
/plugin help
9292
/plugin doctor
93+
/plugin doctor --json
9394
/setup-keys
9495
/plugin enable supermemory
9596
/plugin disable supermemory
@@ -114,6 +115,7 @@ Autocomplete-friendly shortcuts:
114115
/plugin-profile-lean
115116
/plugin-profile-stable
116117
/plugin-profile-experimental
118+
/plugin-doctor-json
117119
```
118120

119121
Supported plugin names: `notifier`, `supermemory`, `morph`, `worktree`, `wakatime`.
@@ -122,6 +124,8 @@ Supported plugin names: `notifier`, `supermemory`, `morph`, `worktree`, `wakatim
122124

123125
`/plugin doctor` checks the current plugin setup and reports missing prerequisites before you enable additional plugins.
124126

127+
`/plugin doctor --json` (or `/plugin-doctor-json`) prints machine-readable diagnostics for automation.
128+
125129
`/setup-keys` prints exact environment/file snippets for missing API keys.
126130

127131
Profiles:
@@ -139,5 +143,20 @@ For WakaTime, configure `~/.wakatime.cfg` with your `api_key` before enabling `w
139143
- `scripts/mcp_command.py` - backend script for `/mcp`
140144
- `scripts/plugin_command.py` - backend script for `/plugin`
141145
- `install.sh` - one-step installer/updater
146+
- `Makefile` - common maintenance commands (`make help`)
147+
- `.github/workflows/ci.yml` - CI checks and installer smoke test
148+
149+
## Maintenance commands 🛠️
150+
151+
```bash
152+
make help
153+
make validate
154+
make doctor
155+
make doctor-json
156+
make install-test
157+
make release VERSION=0.1.1
158+
```
159+
160+
Tip: for local branch testing, installer accepts `REPO_REF`.
142161

143162
Happy shipping! 😄

install.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
set -euo pipefail
33

44
REPO_URL="${REPO_URL:-https://github.com/dmoliveira/my_opencode.git}"
5+
REPO_REF="${REPO_REF:-}"
56
INSTALL_DIR="${INSTALL_DIR:-$HOME/.config/opencode/my_opencode}"
67
CONFIG_DIR="$HOME/.config/opencode"
78
CONFIG_PATH="$CONFIG_DIR/opencode.json"
@@ -53,6 +54,12 @@ else
5354
git clone --depth 1 "$REPO_URL" "$INSTALL_DIR"
5455
fi
5556

57+
if [ -n "$REPO_REF" ]; then
58+
printf "Checking out repo ref %s\n" "$REPO_REF"
59+
git -C "$INSTALL_DIR" fetch --all --prune >/dev/null 2>&1 || true
60+
git -C "$INSTALL_DIR" checkout "$REPO_REF"
61+
fi
62+
5663
chmod +x "$INSTALL_DIR/scripts/mcp_command.py" "$INSTALL_DIR/scripts/plugin_command.py"
5764
ln -sfn "$INSTALL_DIR/opencode.json" "$CONFIG_PATH"
5865

opencode.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
"description": "Show plugin command usage and examples",
4343
"template": "!`python3 \"$HOME/.config/opencode/my_opencode/scripts/plugin_command.py\" help`\nShow only the command output."
4444
},
45+
"plugin-doctor-json": {
46+
"description": "Show plugin diagnostics in JSON",
47+
"template": "!`python3 \"$HOME/.config/opencode/my_opencode/scripts/plugin_command.py\" doctor --json`\nShow only the command output."
48+
},
4549
"plugin-enable-notifier": {
4650
"description": "Enable notifier plugin",
4751
"template": "!`python3 \"$HOME/.config/opencode/my_opencode/scripts/plugin_command.py\" enable notifier`\nShow only the command output."

scripts/plugin_command.py

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def set_plugins(data: dict, plugins: list[str]) -> None:
5151

5252
def usage() -> int:
5353
print(
54-
"usage: /plugin status | /plugin doctor | /plugin setup-keys | /plugin profile <lean|stable|experimental> | /plugin enable <name|all> | /plugin disable <name|all>"
54+
"usage: /plugin status | /plugin doctor [--json] | /plugin setup-keys | /plugin profile <lean|stable|experimental> | /plugin enable <name|all> | /plugin disable <name|all>"
5555
)
5656
print("names: notifier, supermemory, morph, worktree, wakatime")
5757
print("note: 'all' applies stable plugins only: notifier, supermemory, wakatime")
@@ -111,24 +111,24 @@ def has_wakatime_key() -> bool:
111111
return bool(re.search(r"(?im)^\s*api_key\s*=\s*\S+", content))
112112

113113

114-
def print_doctor(plugins: list[str]) -> int:
114+
def collect_doctor(plugins: list[str]) -> dict:
115115
problems: list[str] = []
116116
warnings: list[str] = []
117-
118-
print("plugin doctor")
119-
print("-------------")
120-
print(f"config: {CONFIG_PATH}")
121-
print(f"python: {sys.executable}")
122-
123-
if not CONFIG_PATH.exists():
124-
problems.append(f"missing config file: {CONFIG_PATH}")
117+
plugin_states: dict[str, dict[str, str]] = {}
125118

126119
for alias in PLUGIN_ORDER:
127120
package = KNOWN_PLUGINS[alias]
128121
enabled = package in plugins
129122
status = "enabled" if enabled else "disabled"
130123
kind = "stable" if alias in STABLE_ALIASES else "experimental"
131-
print(f"- {alias}: {status} [{kind}]")
124+
plugin_states[alias] = {
125+
"status": status,
126+
"kind": kind,
127+
"package": package,
128+
}
129+
130+
if not CONFIG_PATH.exists():
131+
problems.append(f"missing config file: {CONFIG_PATH}")
132132

133133
if KNOWN_PLUGINS["supermemory"] in plugins and not has_supermemory_key():
134134
problems.append(
@@ -157,21 +157,51 @@ def print_doctor(plugins: list[str]) -> int:
157157
if not cache_dir.exists():
158158
warnings.append("plugin cache not found yet (~/.cache/opencode/node_modules)")
159159

160-
if warnings:
160+
return {
161+
"result": "PASS" if not problems else "FAIL",
162+
"config": str(CONFIG_PATH),
163+
"python": sys.executable,
164+
"plugins": plugin_states,
165+
"warnings": warnings,
166+
"problems": problems,
167+
"quick_fixes": [
168+
"set SUPERMEMORY_API_KEY and/or create ~/.config/opencode/supermemory.jsonc",
169+
"add api_key to ~/.wakatime.cfg",
170+
"disable unmet plugins with: /plugin disable <name>",
171+
]
172+
if problems
173+
else [],
174+
}
175+
176+
177+
def print_doctor(plugins: list[str], json_output: bool = False) -> int:
178+
report = collect_doctor(plugins)
179+
180+
if json_output:
181+
print(json.dumps(report, indent=2))
182+
return 0 if report["result"] == "PASS" else 1
183+
184+
print("plugin doctor")
185+
print("-------------")
186+
print(f"config: {report['config']}")
187+
print(f"python: {report['python']}")
188+
189+
for alias in PLUGIN_ORDER:
190+
state = report["plugins"][alias]
191+
print(f"- {alias}: {state['status']} [{state['kind']}]")
192+
193+
if report["warnings"]:
161194
print("\nwarnings:")
162-
for item in warnings:
195+
for item in report["warnings"]:
163196
print(f"- {item}")
164197

165-
if problems:
198+
if report["problems"]:
166199
print("\nproblems:")
167-
for item in problems:
200+
for item in report["problems"]:
168201
print(f"- {item}")
169202
print("\nquick fixes:")
170-
print(
171-
"- set SUPERMEMORY_API_KEY and/or create ~/.config/opencode/supermemory.jsonc"
172-
)
173-
print("- add api_key to ~/.wakatime.cfg")
174-
print("- disable unmet plugins with: /plugin disable <name>")
203+
for item in report["quick_fixes"]:
204+
print(f"- {item}")
175205
print("\nresult: FAIL")
176206
return 1
177207

@@ -253,7 +283,10 @@ def main(argv: list[str]) -> int:
253283
return 0
254284

255285
if argv[0] == "doctor":
256-
return print_doctor(plugins)
286+
json_output = len(argv) > 1 and argv[1] == "--json"
287+
if len(argv) > 1 and not json_output:
288+
return usage()
289+
return print_doctor(plugins, json_output=json_output)
257290

258291
if argv[0] == "setup-keys":
259292
return print_setup_keys(plugins)

0 commit comments

Comments
 (0)