Merge pull request #6 from Molecule-AI/feat/cli-full-command-tree

feat(cli): implement full CLI command tree
This commit is contained in:
Hongming Wang 2026-04-24 13:25:23 -07:00 committed by GitHub
commit 38d7618085
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 44 additions and 178 deletions

157
CLAUDE.md
View File

@ -20,16 +20,18 @@ This CLI is the primary user-facing tool for interacting with the Molecule AI pl
## 2. Build, Test, and Local Run
```bash
# Build the binary to ./bin/molecule
# Build the binary to ./bin/molecule (or $GOBIN/molecule)
go build -o bin/molecule ./cmd/molecule
# Run the test suite (24 integration tests)
# Run tests (none yet; add as commands are implemented)
go test ./...
# Run the CLI
# Run the CLI locally (requires platform env vars — see Section 5)
./bin/molecule --help
```
There is no `main.go` or `cmd/molecule/main.go` yet. Creating it is the first implementation task. The module path will be auto-detected from `go.mod`.
## 3. Go Module Conventions
**Module path:** `github.com/Molecule-AI/molecule-cli` (from `go.mod`)
@ -120,148 +122,17 @@ See `known-issues.md` at the repo root for the full tracked list.
**Policy:** File a GitHub issue before patching silently. Do not merge a workaround without a linked issue.
## 8. Command Reference
Full `molecule` command tree. All subcommands follow `molecule <resource> <verb> [flags]` pattern.
### Workspace Commands
```
molecule workspace create [--name <name>] [--tier <1-4>] [--template <template-id>]
molecule workspace list
molecule workspace inspect <workspace-id>
molecule workspace delete <workspace-id>
molecule workspace restart <workspace-id>
molecule workspace delegate <workspace-id> <target-id> <task>
molecule workspace audit
```
### Agent Commands
```
molecule agent list [workspace-id]
molecule agent inspect <agent-id>
molecule agent send <agent-id> <message>
molecule agent peers <workspace-id>
```
### Platform Commands
```
molecule platform audit
molecule platform health
```
### Config Commands
```
molecule init # Bootstrap molecule.yaml in the current directory
molecule config list # Show current config
molecule config set <key> <value>
molecule config get <key>
molecule config init # Alias for molecule init
molecule config view # Print config file path and current values
```
### Global Flags
| Flag | Description |
|------|-------------|
| `--api-url <url>` | Platform API base URL (env: MOLECULE_API_URL) |
| `--output`, `-o` | Output format: `table` (default), `json`, `yaml` |
| `--verbose`, `-v` | Enable verbose (DEBUG-level) output to stderr |
| `--config <path>` | Path to config file (default: `~/.config/molecule.yaml` or `./molecule.yaml`) |
| `--help`, `-h` | Show help for any command |
### Error Codes
All errors go to stderr with exit codes:
- **0** — success
- **1** — runtime error (platform API error, file system error)
- **2** — usage error (missing required flag, bad argument, unknown subcommand)
Error format: `[resource] [verb]: [specific message]`
Examples:
```
molecule workspace delete abc123: workspace not found
molecule agent send xyz: workspace_id unknown for agent "xyz"
molecule: unknown subcommand "agen inspect"
```
### Output Format Examples
**text (default):**
```
Workspace: my-workspace
ID: 550e8400-e29b-41d4-a716-446655440000
Status: online
Tier: 2
Created: 2026-04-01T12:00:00Z
```
**json:**
```json
{"id": "550e8400-e29b-41d4-a716-446655440000", "name": "my-workspace", "status": "online", "tier": 2}
```
**yaml:**
```yaml
id: 550e8400-e29b-41d4-a716-446655440000
name: my-workspace
status: online
tier: 2
```
## 9. Homebrew Tap Release
Releases are published to the Molecule-AI/homebrew-tap tap. The GitHub Actions workflow handles the formula update automatically when a `v*` tag is pushed.
To release via Homebrew tap:
1. Push a `v*` tag to GitHub
2. The GitHub Release workflow attaches a `molecule_*_darwin_arm64.tar.gz` and `molecule_*_darwin_amd64.tar.gz` to the release
3. The `brew формула` is updated by the workflow to point at the new release assets
4. Users install via: `brew install molecule-ai/tap/molecule`
Do not manually edit the Homebrew formula. Let the workflow manage it.
## 10. Cross-Platform Binary Build Notes
GoReleaser builds for these targets by default (see `.goreleaser.yml`):
- `darwin/amd64` — Intel macOS
- `darwin/arm64` — Apple Silicon macOS
- `linux/amd64` — Linux x86_64
- `linux/arm64` — Linux ARM64
- `windows/amd64` — Windows x86_64 (.exe)
Each target produces a compressed archive (`.tar.gz` on Unix, `.zip` on Windows) with:
- `molecule` (or `molecule.exe`) binary
- `completions/` dir with shell completion scripts (`bash`, `zsh`, `fish`, `powershell`)
Install shell completions:
```bash
# bash
source <(molecule completion bash)
# zsh
molecule completion zsh > "${fpath[1]}/_molecule"
# fish
molecule completion fish | source
```
## 11. Implementation Status (as of 2026-04-22)
The CLI has a full command tree and 24 integration tests. Remaining items:
## 8. Implemented
- [x] `cmd/molecule/main.go` — entry point with root command
- [x] Root command and global flags (`--verbose`, `--output`, `--config`)
- [x] `workspace create`, `workspace list`, `workspace delete` subcommands
- [x] `agent inspect`, `agent list` subcommands
- [x] Control plane API client (initialized with `MOLECULE_API_URL`)
- [ ] Workspace runtime client (for dev/proxy mode)
- [ ] Workspace template config (`~/.config/molecule.yaml` scaffold by default)
- [ ] `molecule completion` shell completion subcommands
- [ ] `molecule workspace restart` — confirm API endpoint / status code handling
- [ ] Cross-compile CI matrix (`.github/workflows/release.yml` currently uses plain `go build`)
Done:
- [x] `cmd/molecule/main.go` — entry point with Cobra root command
- [x] Root command and global flags (`--verbose`, `--output`, `--config`, `--api-url`)
- [x] `workspace create`, `workspace list`, `workspace inspect`, `workspace delete`, `workspace restart`, `workspace audit`, `workspace delegate` subcommands
- [x] `agent list`, `agent inspect`, `agent send`, `agent peers` subcommands
- [x] `platform audit`, `platform health` subcommands
- [x] `init`, `config list`, `config get`, `config set`, `config init`, `config view` subcommands
- [x] Control plane API client (`internal/client/platform.go`)
- [x] `go test ./...` — 24 integration tests with httptest mock server
- [x] `.goreleaser.yaml` with all 6 targets wired up
- [ ] Configuration file (e.g., `~/.config/molecule/cli.yaml`) — workspace template per platform rules
- [ ] Unit tests for core command logic
- [ ] `molecule init` (bootstrap local workspace config)
**Platform constraint reminders (from `constraints-and-rules.md`):**
- Postgres is the source of truth. CLI commands that mutate state ultimately write to Postgres via the control plane.

View File

@ -98,16 +98,16 @@ func runConfigSet(cmd *cobra.Command, args []string) error {
}
configFile := filepath.Join(configDir, "molecule.yaml")
// Create a fresh viper instance scoped to the target config file.
// Read existing values (if any), set the new key, then atomically write.
v := viper.New()
v.SetConfigFile(configFile)
_ = v.ReadInConfig() // ignore not-found; we write only the new key below
_ = v.ReadInConfig() // ignore not-found
v.Set(key, value)
if err := v.SafeWriteConfig(); err != nil {
return fmt.Errorf("config set: write %s: %w", configFile, err)
if err := v.WriteConfig(); err != nil {
if err2 := v.SafeWriteConfig(); err2 != nil {
return fmt.Errorf("config set: write %s: %w (tried WriteConfig then SafeWriteConfig)", configFile, err)
}
}
fmt.Printf("Set %s=%q in %s\n", key, value, configFile)
fmt.Printf("Set %s=%q in %s\n", key, value, v.ConfigFileUsed())
return nil
}

View File

@ -284,5 +284,6 @@ func runWorkspaceDelegate(cmd *cobra.Command, args []string) error {
} else {
fmt.Printf("Delegation sent to %q.\n", targetID)
}
_ = workspaceID
return nil
}

View File

@ -106,54 +106,48 @@ resulting `go.sum`. Add `go mod verify` to CI as a lint step. Ensure
## KI-004 — GoReleaser config may not be aligned with go.mod module path
**File:** `.goreleaser.yaml`
**Status:** ✅ Resolved — `.goreleaser.yaml` added
**Resolved in:** `main` (commit `47b2804` + this branch)
**File:** `.github/workflows/release.yml`
**Status:** ⚠️ Unverified — needs real tag to confirm
**Severity:** Medium
### Symptom
The GoReleaser workflow was wired up but had no `.goreleaser.yaml` config.
A `v*` tag push could produce an empty release or a binary with the wrong name
if `builds[].dir` or `builds[].main` were misconfigured.
The GoReleaser workflow is wired up but has not been tested with a real tag.
The `gomod.alphaSettings` or `builds[].dir` settings in `.goreleaser.yaml`
(if it exists) may not correctly resolve the module root. A real `v*` tag
push could produce an empty release or a binary with the wrong name.
### Resolution
Added `.goreleaser.yaml` with:
- `dir: .` — repo root
- `main: ./cmd/molecule` — main package path
- `binary: molecule` — output binary name
- All 6 targets: linux/darwin × amd64/arm64 + windows × amd64
- `CGO_ENABLED=0` for static binaries
- Checksum files generated for all archives
### Impact
The first release may silently fail or produce a malformed artifact that is
not usable by platform operators.
`release.yml` still uses plain `go build` per matrix target (GoReleaser is
configured but not wired into CI yet — the plain build is sufficient for
v0.1.0). Wire GoReleaser into CI when Homebrew formula + checksum
verification are needed.
### Suggested fix
Before the first release, test goreleaser locally with `goreleaser check`
and `goreleaser snapshot --clean`. Verify the binary name, module path, and
target OS/arch match expectations. Ensure `goreleaser.yaml` `builds[].dir`
is set to `.` (repo root) since the main package is at `cmd/molecule`.
---
## KI-005 — No integration test for the full CLI lifecycle
**File:** `tests/` (does not exist)
**Status:** ✅ Resolved
**Resolved in:** `cmd/molecule/molecule_test.go` — 24 table-driven tests using httptest mock server.
**File:** `tests/` (does not exist)
**Status:** Not yet implemented
**Severity:** Medium
### Symptom
There were no tests at all (per `go test ./...` — no packages match).
As subcommands were built, there was no test harness for end-to-end CLI testing
There are no tests at all (per `go test ./...` — no packages match).
As subcommands are built, there is no test harness for end-to-end CLI testing
(e.g. `molecule workspace create --name test --output json` → verify JSON output).
### Impact
Each subcommand was shipped without regression protection. Manual testing
was required for every release.
Each subcommand will be shipped without regression protection. Manual testing
is required for every release. The absence of a `tests/` directory also means
there is no fixture for CLI integration testing with recorded API responses.
### Suggested fix
Add `tests/` with:
- `cmd/molecule/molecule_test.go` — table-driven tests for each subcommand
using `exec.Command("molecule", ...)` against a built binary
- Use a httptest mock server for offline testing
- Use `molecule-sdk-python` fixture server or recorded API responses for
offline testing
- Add `go test ./...` to CI; require >0 test packages before merge
**✅ Done:** 24 integration tests covering all 18 subcommands, error paths,
and structured output. `go test ./...` passes, CI job added to `release.yml`.