Skip to content

Commit c8bc912

Browse files
committed
feat: scaffold crucible cli module
Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
1 parent 55eb6b7 commit c8bc912

3 files changed

Lines changed: 108 additions & 0 deletions

File tree

cmd/crucible/go.mod

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module github.com/stablekernel/crucible/cmd/crucible
2+
3+
go 1.25.11
4+
5+
require (
6+
github.com/stablekernel/crucible/gen v0.0.0
7+
github.com/stablekernel/crucible/state v0.0.0
8+
)
9+
10+
replace github.com/stablekernel/crucible/state => ../../state
11+
12+
replace github.com/stablekernel/crucible/gen => ../../gen

cmd/crucible/main.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Command crucible is a headless command-line tool over the crucible
2+
// state-machine IR. It lints, renders, diffs, validates, and ejects a machine's
3+
// serialized IR JSON without running any behavior.
4+
package main
5+
6+
import (
7+
"fmt"
8+
"io"
9+
"os"
10+
)
11+
12+
// version is the crucible CLI's own version, independent of the state module's
13+
// v1 freeze.
14+
const version = "0.1.0"
15+
16+
// exit codes. 0 is success, 1 a runtime or load error, 2 a usage error, and a
17+
// non-zero code (1) also signals lint findings.
18+
const (
19+
exitOK = 0
20+
exitError = 1
21+
exitUsage = 2
22+
exitFindings = 1
23+
)
24+
25+
func main() {
26+
os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
27+
}
28+
29+
// run dispatches a subcommand and returns its exit code. It is the testable
30+
// seam: every subcommand is a func(args, stdout, stderr) int, so the whole CLI
31+
// is exercised without spawning a process.
32+
func run(args []string, stdout, stderr io.Writer) int {
33+
if len(args) == 0 {
34+
usage(stderr)
35+
return exitUsage
36+
}
37+
38+
cmd, rest := args[0], args[1:]
39+
switch cmd {
40+
case "lint":
41+
return runLint(rest, stdout, stderr)
42+
case "render":
43+
return runRender(rest, stdout, stderr)
44+
case "diff":
45+
return runDiff(rest, stdout, stderr)
46+
case "validate":
47+
return runValidate(rest, stdout, stderr)
48+
case "eject":
49+
return runEject(rest, stdout, stderr)
50+
case "version":
51+
emitln(stdout, version)
52+
return exitOK
53+
case "-version", "--version":
54+
emitln(stdout, version)
55+
return exitOK
56+
case "-h", "--help", "help":
57+
usage(stdout)
58+
return exitOK
59+
default:
60+
emitf(stderr, "crucible: unknown subcommand %q\n\n", cmd)
61+
usage(stderr)
62+
return exitUsage
63+
}
64+
}
65+
66+
// printf, println, and print write diagnostics and command output to an
67+
// io.Writer. A failure writing to stdout or stderr is unrecoverable for a
68+
// command-line tool (the stream the user reads is gone), so the error is
69+
// deliberately ignored here rather than threaded back through every call site.
70+
func emitf(w io.Writer, format string, a ...any) { _, _ = fmt.Fprintf(w, format, a...) }
71+
func emitln(w io.Writer, a ...any) { _, _ = fmt.Fprintln(w, a...) }
72+
func emit(w io.Writer, a ...any) { _, _ = fmt.Fprint(w, a...) }
73+
74+
// usage prints the top-level command listing.
75+
func usage(w io.Writer) {
76+
emit(w, `crucible - headless tooling for the crucible state-machine IR
77+
78+
Usage:
79+
crucible <command> [arguments]
80+
81+
Commands:
82+
lint <ir.json> run static analysis and report findings
83+
render <ir.json> [-format f] render the machine as mermaid (default) or dot
84+
diff <old.json> <new.json> classify changes and recommend a semver bump
85+
validate <ir.json> confirm the IR loads and assembles
86+
eject <ir.json> [-package p] [-o f] generate typed Go behavior stubs
87+
version print the crucible CLI version
88+
89+
Each command reads an IR JSON file path, or - for stdin.
90+
91+
Flags:
92+
-version print the crucible CLI version
93+
-h, --help show this help
94+
`)
95+
}

go.work

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ go 1.25.11
1212
// source modules join the workspace.
1313
use (
1414
./cluster
15+
./cmd/crucible
1516
./durable
1617
./e2e
1718
./examples/dispatch

0 commit comments

Comments
 (0)