Skip to content

Commit 8b5e6f2

Browse files
Coding-Dev-ToolsDevForge Engineer
andauthored
cowork-bot: automated improvements (cowork/improve-devforge-cli) (#12)
* cowork-bot: fix dispatch silent-failure + install-all extra + remove builtins alias - dispatch: add _is_tool_installed() pre-flight check (importlib.util.find_spec) so 'devforge guard ...' on an uninstalled tool shows a clear 'not installed — run pip install devforge[guard]' message instead of silently exiting 1 with a raw Python ModuleNotFoundError trace. The previous except FileNotFoundError was dead code: the error occurs inside the subprocess, not at Popen launch time. - install all: use canonical devforge[all] extra instead of joining all tool keys into a comma-separated extras string (fragile; diverges if TOOLS and pyproject.toml [all] ever drift). - Remove 'import builtins as _builtins' workaround; no builtin shadowing exists, so list() is fine throughout. - Remove unused ctx: typer.Context parameter from dispatch inner function. - Tests: 17/17 green; new TestIsToolInstalled + dispatch install-hint + install-all-extra assertion tests cover the fixed paths. * cowork-bot: seed cowork-auto-pr workflow for automated PR creation * cowork-bot: merge origin/main into cowork/improve-devforge-cli; resolve cli.py merge conflicts keeping the install-all + dispatch improvements * fix(install): replace broken bare 'pip install devforge-tools[...]' with verified-working git+ form devforge-tools is NOT on public PyPI (verified 404 on pypi.org), so the previous bare 'pip install devforge-tools[all/guard/...]' commands failed for every user. README + AGENTS now lead with the git+ GitHub-source form and an honest 'not on public PyPI' note; the 'devforge install <tool>' command and the not-installed dispatch hint now build git+ URLs from each tool's repo URL; false PyPI badges removed from README. Tests updated to assert the corrected hint. Marketing-growth-agent run — conversion-surface repair (highest-ROI rung). NOT pushed (W's call per OPS_CONTRACT). * cowork-bot: forward tool flags in dispatch subcommands (fix silent-failure trap) The per-tool subcommands (guard, sql, deploy, ...) used a typer Argument for args, which made typer reject any token beginning with `-` as an unknown option BEFORE the underlying tool ever ran. So `devforge guard --config x.yaml` failed with "No such option" and the tool silently never executed — the hub's known silent-failure/observability trap. Register each dispatch command with ignore_unknown_options + allow_extra_args and forward ctx.args to the underlying `python -m <pkg>` invocation. Positional args and flags now reach the tool. Added a regression test (test_dispatch_forwards_tool_flags) and the cowork-auto-pr workflow so the improvement is delivered as a PR. * ci: add CODEOWNERS, dependabot, CI/CD workflows (auto-PR, publish) * fix(cli): use consistent devforge-cli.git URL in dispatch install hint + prevent rich line-wrap breaking the URL * fix(tests): resolve merge conflict in test_cli.py (keep upstream test_dispatch_forwards_tool_flags) * style(tests): fix E501 line-too-long in test_cli.py assertion (ruff lint) * style: ruff format (fix CI lint failure) --------- Co-authored-by: DevForge Engineer <engineer@devforge.dev>
1 parent ef84c87 commit 8b5e6f2

4 files changed

Lines changed: 27 additions & 27 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
DevForge CLI meta-package that installs all 11 developer tools in one command. Provides a unified `devforge` CLI entry point delegating to sub-tools: api-contract-guardian, json2sql, deploydiff, configdrift, apighost, apiauth, envault, schemaforge, click-to-mcp, and deadcode.
55

66
## Build & Test Commands
7-
- Install: `pip install -e .[all]` or `pip install devforge-tools`
8-
- Install all tools: `pip install devforge-tools[all]`
7+
- Install: `pip install -e ".[all]"` or `pip install "git+https://github.com/Coding-Dev-Tools/devforge-cli.git[all]"` (NOTE: `devforge-tools` is not on public PyPI — use the `git+` form)
98
- Test: `pytest tests/` (or `python -m pytest tests/ -v --tb=short`)
109
- Lint: `ruff check .`
1110
- Build: `pip install build twine && python -m build && twine check dist/*`

README.md

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
**The `devforge` command — one install, ten developer CLI tools.**
66

7-
[![PyPI](https://img.shields.io/pypi/v/devforge-tools)](https://pypi.org/project/devforge-tools/)
8-
[![Python Versions](https://img.shields.io/pypi/pyversions/devforge-tools)](https://pypi.org/project/devforge-tools/)
97
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
108

119
Ten production-ready CLI tools for API contracts, SQL generation, infrastructure diffs, config drift, API mocking, key management, env syncing, schema conversion, MCP servers, and dead code removal — in a single package. Install one meta-package and get immediate access to all tools via the unified `devforge` command.
@@ -18,31 +16,28 @@ Ten production-ready CLI tools for API contracts, SQL generation, infrastructure
1816

1917
## Why the Suite?
2018

21-
Instead of installing ten separate tools and learning ten different CLIs, `pip install devforge-tools[all]` gives you:
19+
Instead of installing ten separate tools and learning ten different CLIs, the `devforge` meta-package gives you:
2220

2321
- **Single CLI** (`devforge`) to invoke any tool — no context switching
2422
- **Consistent flags, output formats, and help** across all tools
2523
- **Shared configuration** — one install, all tools
2624

2725
## Installation
2826

27+
> **Install note:** `devforge-tools` is **not published on public PyPI**. Install it directly from the GitHub source with the `git+` form below (the only verified-working pip path right now).
28+
2929
```bash
3030
# Install everything (recommended)
31-
pip install devforge-tools[all]
32-
33-
# Or install individual tools
34-
pip install devforge-tools[guard] # API Contract Guardian
35-
pip install devforge-tools[sql] # json2sql
36-
pip install devforge-tools[deploy] # DeployDiff
37-
pip install devforge-tools[drift] # ConfigDrift
38-
pip install devforge-tools[ghost] # APIGhost
39-
pip install devforge-tools[auth] # APIAuth
40-
pip install devforge-tools[envault] # Envault
41-
pip install devforge-tools[schema] # SchemaForge
42-
pip install devforge-tools[mcp] # click-to-mcp
43-
pip install devforge-tools[deadcode] # DeadCode
31+
pip install "git+https://github.com/Coding-Dev-Tools/devforge-cli.git[all]"
32+
33+
# Or clone and install locally
34+
git clone https://github.com/Coding-Dev-Tools/devforge-cli.git
35+
cd devforge-cli
36+
pip install -e ".[all]"
4437
```
4538

39+
Each tool can also be installed on its own from its own repo (e.g. `pip install git+https://github.com/Coding-Dev-Tools/apighost.git`). See each tool's README for details.
40+
4641
## Usage
4742

4843
```bash

src/devforge/cli.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ def list_tools(
7676
)
7777

7878
console.print(table)
79-
console.print("\n[dim]Install individually:[/dim] [green]pip install devforge-tools[guard][/green]")
80-
console.print("[dim]Install all:[/dim] [green]pip install devforge-tools[all][/green]")
79+
console.print(
80+
'\n[dim]Install:[/dim] [green]pip install "git+https://github.com/Coding-Dev-Tools/devforge-cli.git[all]"[/green]'
81+
)
82+
console.print("[dim](devforge-tools is not on public PyPI — use the git+ form above.)[/dim]")
8183

8284

8385
@app.command()
@@ -96,7 +98,9 @@ def install(
9698
console.print(f"Available: {', '.join(TOOLS.keys())}, 'all'")
9799
raise typer.Exit(code=1)
98100

99-
pkg = f"devforge-tools[{extras}]"
101+
# devforge-tools is NOT published on public PyPI — install from GitHub source.
102+
repo_url = "https://github.com/Coding-Dev-Tools/devforge-cli.git"
103+
pkg = f"git+{repo_url}[{extras}]"
100104
console.print(f"[yellow]Installing {pkg}...[/yellow]")
101105
try:
102106
result = subprocess.run([sys.executable, "-m", "pip", "install", pkg], capture_output=True, text=True)
@@ -159,7 +163,8 @@ def dispatch(ctx: typer.Context):
159163
if not _is_tool_installed(module_name):
160164
console.print(
161165
f"[red]Tool '{tool_name}' is not installed.[/red]\n"
162-
f"Run: [green]pip install devforge-tools\\[{tool_name}][/green]"
166+
f'Run: [green]pip install "git+https://github.com/Coding-Dev-Tools/devforge-cli.git\\[{tool_name}]"[/green]',
167+
soft_wrap=True,
163168
)
164169
raise typer.Exit(code=1)
165170

tests/test_cli.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ def test_install_all_uses_all_extra(self, mock_run):
5656
assert "Successfully" in result.stdout
5757
mock_run.assert_called_once()
5858
call_args = mock_run.call_args[0][0] # positional arg: the command list
59-
# Must contain "devforge-tools[all]", not "devforge-tools[guard,sql,...]"
60-
pkg_arg = next((a for a in call_args if a.startswith("devforge-tools[")), None)
61-
assert pkg_arg == "devforge-tools[all]", f"Expected devforge-tools[all], got {pkg_arg}"
59+
# Must contain the git+ URL with [all] extra, not a comma-joined list
60+
pkg_arg = next((a for a in call_args if "devforge-cli.git[" in a), None)
61+
expected = "git+https://github.com/Coding-Dev-Tools/devforge-cli.git[all]"
62+
assert pkg_arg == expected, f"Expected {expected}, got {pkg_arg}"
6263

6364
def test_install_unknown_tool(self):
6465
"""Error on unknown tool name."""
@@ -121,7 +122,7 @@ def test_dispatch_not_installed_shows_install_hint(self, _mock):
121122
result = runner.invoke(app, ["guard"])
122123
assert result.exit_code == 1
123124
assert "not installed" in result.stdout
124-
assert "pip install devforge-tools[guard]" in result.stdout
125+
assert 'pip install "git+https://github.com/Coding-Dev-Tools/devforge-cli.git[guard]"' in result.stdout
125126

126127
@mock.patch("devforge.cli._is_tool_installed", return_value=True)
127128
@mock.patch("devforge.cli.subprocess.run")
@@ -165,7 +166,7 @@ def test_dispatch_install_hint_escapes_extra_brackets(self, _mock):
165166
"""
166167
result = runner.invoke(app, ["guard"])
167168
assert result.exit_code == 1
168-
assert "pip install devforge-tools[guard]" in result.stdout
169+
assert 'pip install "git+https://github.com/Coding-Dev-Tools/devforge-cli.git[guard]"' in result.stdout
169170

170171

171172
class TestHelp:

0 commit comments

Comments
 (0)