From moonbit-skills
Extracts formal API specs using declare stubs and organized test suites (valid/invalid) from existing MoonBit implementations for spec-driven testing.
npx claudepluginhub moonbitlang/skills --plugin moonbit-skillsThis skill uses the workspace's default tool permissions.
Reverse-engineer a formal API contract (`<pkg>_spec.mbt`) and comprehensive test suites from an existing MoonBit implementation. This enables spec-driven testing for already-written code.
Provides MoonBit best practices for code generation, navigation with moon ide/doc, and fixes for common AI syntax pitfalls like type params, raise, macros, and loops.
Guides MoonBit development by solving language, code, compiler diagnostics, package, toolchain, backend, FFI, and test questions using fresh sources like moon ide and docs.
Generates tests from Allium specifications including boundary exposure, actor restrictions, guarantees, property-based, and state machine tests. Use for propagating specs to test files or checking coverage.
Share bugs, ideas, or general feedback.
Reverse-engineer a formal API contract (<pkg>_spec.mbt) and comprehensive test suites from an existing MoonBit implementation. This enables spec-driven testing for already-written code.
Identify public API surface:
pub and pub(all) types, functions, traitsExamine test patterns (if tests exist):
*_test.mbt, *_wbtest.mbt)<pkg>_spec.mbt)Create formal API contract:
declare keyword for all public functions/typesderive(Show, Eq, ToJson) for testabilitydeclare do not have a bodypub(all) for types that need construction in testspub for opaque typesExample spec structure:
///|
/// Error type for parsing operations
pub(all) suberror ParseError {
InvalidFormat(String)
UnexpectedEof
} derive(Show, Eq, ToJson)
///|
/// Main data type
declare pub(all) type Toml
///|
/// Convert to test JSON format
declare pub fn Toml::to_test_json(self : Toml) -> Json
///|
/// Parse TOML from string
declare pub fn parse(input : StringView) -> Result[Toml, ParseError]
Categorize tests by validity (happy path vs error path):
Valid tests (<pkg>_valid_test.mbt):
Invalid tests (<pkg>_invalid_test.mbt):
For valid input tests:
///|
test "valid/category/test-name" {
let toml_text =
#|key = "value"
#|
let toml = match @pkg.parse(toml_text) {
Ok(toml) => toml
Err(error) => fail("Failed to parse: " + error.to_string())
}
json_inspect(toml.to_test_json(), content={
"key": { "type": "string", "value": "value" }
})
}
For invalid input tests:
///|
test "invalid/category/test-name" {
let toml_text =
#|invalid syntax here
#|
match @pkg.parse(toml_text) {
Ok(toml) => {
let json = toml.to_test_json()
fail("Expected parse to fail, got \{json.stringify(indent=2)}")
}
Err(_) => ()
}
}
Type-check the spec:
moon check
Run the tests:
moon test # Run all tests
moon test -u # Update snapshot tests
Verify coverage:
declare keyword (no body needed)to_test_json() for inspection@pkg.function)json_inspect() for complex values<pkg>_valid_test.mbt for happy path, <pkg>_invalid_test.mbt for error path)///| to delimit test blockspub type Config
pub fn Config::parse(text : String) -> Result[Config, String] {
// implementation
}
pub fn Config::get_value(self : Config, key : String) -> String? {
// implementation
}
///|
declare pub type Config
///|
declare pub fn Config::parse(text : String) -> Result[Config, String]
///|
declare pub fn Config::get_value(self : Config, key : String) -> String?
///|
test "parse valid config" {
let result = try? Config::parse("key=value")
match result {
Ok(cfg) => {
inspect(cfg.get_value("key"), content="Some(\"value\")")
}
Err(e) => fail("Parse failed: \{e}")
}
}
///|
test "parse invalid config returns error" {
let result = try? Config::parse("invalid")
match result {
Ok(_) => fail("Should have failed to parse")
Err(_) => ()
}
}
moon check frequently to catch type errors earlymoon test -u when output format changeslet result : Result[T, Error] = try? function_call(...)
match result {
Ok(value) => inspect(value, content="...")
Err(e) => fail("Unexpected error: \{e}")
}
match try? function_call(...) {
Ok(v) => fail("Expected error, got \{v}")
Err(_) => () // Success - error was expected
}
json_inspect(value.to_test_json(), content={
"field": { "type": "integer", "value": "42" }
})