Merge pull request #6 from Molecule-AI/feat/cli-full-command-tree
feat(cli): implement full CLI command tree
This commit is contained in:
commit
38d7618085
157
CLAUDE.md
157
CLAUDE.md
@ -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.
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -284,5 +284,6 @@ func runWorkspaceDelegate(cmd *cobra.Command, args []string) error {
|
||||
} else {
|
||||
fmt.Printf("Delegation sent to %q.\n", targetID)
|
||||
}
|
||||
_ = workspaceID
|
||||
return nil
|
||||
}
|
||||
@ -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`.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user