@@ -71,9 +71,21 @@ pub trait InitPrompt {
7171
7272/// Wire up the real prompt and run the generator.
7373pub fn run ( factory : & dyn AdapterFactory , root : & Path , opts : & InitOptions ) -> Result < ( ) > {
74+ print_intro ( ) ;
7475 orchestrate ( factory, & StdinInitPrompt , root, opts)
7576}
7677
78+ /// A short, friendly preamble so a first-time dev knows what `init` will ask and that nothing is
79+ /// locked in — every answer has a default and is editable afterward.
80+ fn print_intro ( ) {
81+ println ! ( "\n otf-release init — configure releases for this repo.\n " ) ;
82+ println ! (
83+ " • Writes release.toml (the editable source of truth) and a GitHub release workflow."
84+ ) ;
85+ println ! ( " • Press Enter to accept the default shown in (parentheses); a hint sits under each prompt." ) ;
86+ println ! ( " • Nothing is permanent — re-run init, edit release.toml by hand, or use `otf-release config`.\n " ) ;
87+ }
88+
7789/// The testable core of `init`.
7890pub fn orchestrate (
7991 factory : & dyn AdapterFactory ,
@@ -192,7 +204,10 @@ fn select_targets(prompt: &str) -> Result<Vec<Target>> {
192204 . collect ( ) ;
193205 let selected = MultiSelect :: new ( prompt, labels)
194206 . with_default ( & defaults)
195- . with_help_message ( MULTI_HELP )
207+ . with_help_message (
208+ "the widely-supported platforms are pre-selected; \
209+ space toggles · enter confirm",
210+ )
196211 . raw_prompt ( ) ?;
197212 Ok ( selected
198213 . iter ( )
@@ -915,9 +930,10 @@ fn configure_generic(
915930 & format ! ( " {name} — mode:" ) ,
916931 vec ! [
917932 "publish (to registry)" ,
918- "build-only (GitHub Release artifacts )" ,
933+ "build-only (standalone binaries on a GitHub Release)" ,
919934 ] ,
920935 )
936+ . with_help_message ( MODE_HELP )
921937 . raw_prompt ( ) ?
922938 . index
923939 {
@@ -926,20 +942,23 @@ fn configure_generic(
926942 } ;
927943
928944 let matrix = Select :: new (
929- & format ! ( " {name} — build across a target matrix ?" ) ,
945+ & format ! ( " {name} — cross-compile a binary per platform ?" ) ,
930946 vec ! [ "Yes" , "No" ] ,
931947 )
948+ . with_help_message ( MATRIX_HELP )
932949 . raw_prompt ( ) ?
933950 . index
934951 == 0 ;
935952 let targets = if matrix {
936- select_targets ( " Target triples :" ) ?
953+ select_targets ( " Target platforms :" ) ?
937954 } else {
938955 Vec :: new ( )
939956 } ;
940957
958+ // `otf-release build` runs `rustup target add {triple}` itself and substitutes the placeholders,
959+ // so the commands here use `{triple}`/`{ext}`/`{bin}`, not GitHub `${{ matrix.* }}` expressions.
941960 let default_cmd = match ( kind, matrix) {
942- ( Some ( "Rust / Cargo" ) , true ) => "rustup target add ${{ matrix.rust_target }} && cargo build --release --target ${{ matrix.rust_target } }" ,
961+ ( Some ( "Rust / Cargo" ) , true ) => "cargo build --release --target {triple }" ,
943962 ( Some ( "Rust / Cargo" ) , false ) => "cargo build --release" ,
944963 ( Some ( "Node / npm" ) , _) => "npm run build" ,
945964 ( Some ( "Deno / JSR" ) , _) => "deno task build" ,
@@ -951,36 +970,44 @@ fn configure_generic(
951970 } ;
952971 let command = Text :: new ( & format ! ( " {name} — build command (optional):" ) )
953972 . with_default ( default_cmd)
973+ . with_help_message ( if matrix {
974+ COMMAND_HELP
975+ } else {
976+ "runs in CI before release; leave blank for none"
977+ } )
954978 . prompt ( ) ?;
955979
956980 let bin_name = if kind == Some ( "Rust / Cargo" ) {
957981 let n = Text :: new ( & format ! ( " {name} — binary name:" ) )
958982 . with_default ( name)
983+ . with_help_message ( BIN_NAME_HELP )
959984 . prompt ( ) ?;
960985 Some ( n)
961986 } else {
962987 None
963988 } ;
964989
965990 let default_artifacts = match ( kind, matrix) {
966- ( Some ( "Rust / Cargo" ) , true ) => format ! (
967- "target/${{{{ matrix.target }}}}/release/{}${{{{ matrix.ext }}}}" ,
968- bin_name. as_deref( ) . unwrap( )
969- ) ,
991+ ( Some ( "Rust / Cargo" ) , true ) => "target/{triple}/release/{bin}{ext}" . to_string ( ) ,
970992 ( Some ( "Rust / Cargo" ) , false ) => format ! ( "target/release/{}" , bin_name. as_deref( ) . unwrap( ) ) ,
971993 ( Some ( "Node / npm" ) , _) => "dist/*" . to_string ( ) ,
972- _ => "" . to_string ( ) ,
994+ _ => String :: new ( ) ,
973995 } ;
974996 let artifacts = Text :: new ( & format ! ( " {name} — artifacts to stage (optional):" ) )
975997 . with_default ( & default_artifacts)
998+ . with_help_message ( if matrix {
999+ ARTIFACTS_HELP
1000+ } else {
1001+ "files to attach/stage on release"
1002+ } )
9761003 . prompt ( ) ?;
9771004
9781005 let publish = if mode == Mode :: Publish {
979- let cmd = Text :: new ( & format ! (
980- " {name} — publish command (e.g. npx jsr publish):"
981- ) )
982- . with_default ( " ")
983- . prompt ( ) ?;
1006+ let cmd = Text :: new ( & format ! ( " {name} — publish command:" ) )
1007+ . with_default ( "" )
1008+ . with_placeholder ( "e.g. npx jsr publish" )
1009+ . with_help_message ( "the command CI runs to push this package to its registry ")
1010+ . prompt ( ) ?;
9841011 ( !cmd. trim ( ) . is_empty ( ) ) . then_some ( cmd)
9851012 } else {
9861013 None
@@ -1006,12 +1033,36 @@ fn configure_generic(
10061033pub struct StdinInitPrompt ;
10071034
10081035const MULTI_HELP : & str = "↑↓ move · space toggle · enter confirm" ;
1036+ const SELECT_HELP : & str = "↑↓ move · enter select" ;
1037+
1038+ const BUILD_PKGS_HELP : & str =
1039+ "a build step compiles/bundles before publish (a Rust binary, a TS bundle, …). \
1040+ Packages you don't pick are published as-is. ↑↓ move · space toggle · enter confirm";
1041+ const MODE_HELP : & str =
1042+ "publish → push to the registry · build-only → standalone binaries on a GitHub Release (no registry)" ;
1043+ const MATRIX_HELP : & str =
1044+ "Yes → cross-compile one binary per OS/arch (Rust, Go, …), staged per platform · No → a single build" ;
1045+ const BIN_NAME_HELP : & str =
1046+ "the compiled executable's base name; staged at bin/<platform>-<arch>/<name> inside the package" ;
1047+ const COMMAND_HELP : & str =
1048+ "runs in CI for each target; {triple} {ext} {bin} are substituted per platform" ;
1049+ const ARTIFACTS_HELP : & str =
1050+ "path to the binary the command produced; {triple} {ext} {bin} expand per target" ;
1051+ const TAG_FORMAT_HELP : & str =
1052+ "e.g. v{version} (single package) or {name}@{version} (per-package tags in a monorepo)" ;
1053+ const CHANGELOG_SCOPE_HELP : & str =
1054+ "Root → one shared CHANGELOG.md · Per-package → each package keeps its own (best for monorepos)" ;
1055+ const NOTES_HELP : & str =
1056+ "how the GitHub Release body is filled: auto (from PRs/commits), your CHANGELOG, or a commit list" ;
10091057
10101058impl InitPrompt for StdinInitPrompt {
10111059 fn select_adapters ( & self ) -> Result < Vec < Ecosystem > > {
10121060 let labels: Vec < & str > = Ecosystem :: ALL . iter ( ) . map ( |e| e. label ( ) ) . collect ( ) ;
10131061 let chosen = MultiSelect :: new ( "Adapters to enable:" , labels)
1014- . with_help_message ( MULTI_HELP )
1062+ . with_help_message (
1063+ "the ecosystems/registries this repo releases to; pick all that apply. \
1064+ space toggles · enter confirm",
1065+ )
10151066 . raw_prompt ( ) ?;
10161067 Ok ( chosen. iter ( ) . map ( |o| Ecosystem :: ALL [ o. index ] ) . collect ( ) )
10171068 }
@@ -1022,7 +1073,7 @@ impl InitPrompt for StdinInitPrompt {
10221073 }
10231074 let labels: Vec < String > = publishable. iter ( ) . map ( |p| p. name . clone ( ) ) . collect ( ) ;
10241075 let chosen = MultiSelect :: new ( "Which packages need a build step before publish?" , labels)
1025- . with_help_message ( MULTI_HELP )
1076+ . with_help_message ( BUILD_PKGS_HELP )
10261077 . raw_prompt ( ) ?;
10271078 Ok ( chosen
10281079 . iter ( )
@@ -1035,7 +1086,9 @@ impl InitPrompt for StdinInitPrompt {
10351086 enabled[ 0 ]
10361087 } else {
10371088 let labels: Vec < & str > = enabled. iter ( ) . map ( |e| e. label ( ) ) . collect ( ) ;
1038- let opt = Select :: new ( & format ! ( "{pkg_name} — adapter:" ) , labels) . raw_prompt ( ) ?;
1089+ let opt = Select :: new ( & format ! ( "{pkg_name} — adapter:" ) , labels)
1090+ . with_help_message ( "which registry/ecosystem this package is released through" )
1091+ . raw_prompt ( ) ?;
10391092 enabled[ opt. index ]
10401093 } ;
10411094
@@ -1053,6 +1106,7 @@ impl InitPrompt for StdinInitPrompt {
10531106 "build-only (standalone binaries on a GitHub Release)" ,
10541107 ] ,
10551108 )
1109+ . with_help_message ( MODE_HELP )
10561110 . raw_prompt ( ) ?
10571111 . index
10581112 {
@@ -1062,9 +1116,10 @@ impl InitPrompt for StdinInitPrompt {
10621116 } ;
10631117
10641118 let matrix = Select :: new (
1065- & format ! ( "{pkg_name} — build across a target matrix ?" ) ,
1119+ & format ! ( "{pkg_name} — cross-compile a binary per platform ?" ) ,
10661120 vec ! [ "Yes" , "No" ] ,
10671121 )
1122+ . with_help_message ( MATRIX_HELP )
10681123 . raw_prompt ( ) ?
10691124 . index
10701125 == 0 ;
@@ -1081,6 +1136,7 @@ impl InitPrompt for StdinInitPrompt {
10811136 let ( bin_name, compress, default_cmd, default_artifacts) = if matrix {
10821137 let bin = Text :: new ( & format ! ( "{pkg_name} — binary name:" ) )
10831138 . with_default ( & slug ( pkg_name) )
1139+ . with_help_message ( BIN_NAME_HELP )
10841140 . prompt ( ) ?;
10851141 let compress = ( adapter == Ecosystem :: Npm ) . then ( || "brotli" . to_string ( ) ) ;
10861142 let cmd = if adapter == Ecosystem :: Generic {
@@ -1104,9 +1160,19 @@ impl InitPrompt for StdinInitPrompt {
11041160 } ;
11051161 let command = Text :: new ( & format ! ( "{pkg_name} — build command:" ) )
11061162 . with_default ( & default_cmd)
1163+ . with_help_message ( if matrix {
1164+ COMMAND_HELP
1165+ } else {
1166+ "runs in CI before publish (e.g. a bundler). Leave blank if no build is needed."
1167+ } )
11071168 . prompt ( ) ?;
11081169 let artifacts = Text :: new ( & format ! ( "{pkg_name} — artifacts to stage:" ) )
11091170 . with_default ( & default_artifacts)
1171+ . with_help_message ( if matrix {
1172+ ARTIFACTS_HELP
1173+ } else {
1174+ "files to include when publishing (e.g. dist/**). Optional."
1175+ } )
11101176 . prompt ( ) ?;
11111177
11121178 Ok ( PackageEntry {
@@ -1153,14 +1219,27 @@ impl InitPrompt for StdinInitPrompt {
11531219 } else {
11541220 "Add another package by hand?"
11551221 } ;
1156- if Select :: new ( question, vec ! [ "Yes" , "No" ] ) . raw_prompt ( ) ?. index == 1 {
1222+ if Select :: new ( question, vec ! [ "Yes" , "No" ] )
1223+ . with_help_message ( SELECT_HELP )
1224+ . raw_prompt ( ) ?
1225+ . index
1226+ == 1
1227+ {
11571228 break ;
11581229 }
1159- let name = Text :: new ( " name:" ) . prompt ( ) ?;
1160- let manifest =
1161- Text :: new ( " manifest file holding the version (e.g. deno.json):" ) . prompt ( ) ?;
1230+ let name = Text :: new ( " name:" )
1231+ . with_placeholder ( "@scope/pkg or my-tool" )
1232+ . with_help_message ( "the package name; also used in tags and the changelog" )
1233+ . prompt ( ) ?;
1234+ let manifest = Text :: new ( " manifest file holding the version:" )
1235+ . with_placeholder ( "deno.json" )
1236+ . with_help_message ( "the file the version is read from and bumped in" )
1237+ . prompt ( ) ?;
11621238 let version_field = Text :: new ( " version field:" )
11631239 . with_default ( DEFAULT_VERSION_FIELD )
1240+ . with_help_message (
1241+ "key inside the manifest; dot-paths like workspace.package.version work" ,
1242+ )
11641243 . prompt ( ) ?;
11651244 out. push ( configure_generic ( & name, & manifest, & version_field, None ) ?) ;
11661245 }
@@ -1172,14 +1251,18 @@ impl InitPrompt for StdinInitPrompt {
11721251 & format ! ( "{} already exists. Overwrite?" , path. display( ) ) ,
11731252 vec ! [ "No" , "Yes" ] ,
11741253 )
1254+ . with_help_message (
1255+ "regenerates this file from your answers; your other files are untouched" ,
1256+ )
11751257 . raw_prompt ( ) ?
11761258 . index
11771259 == 1 )
11781260 }
11791261
11801262 fn tag_format ( & self ) -> Result < String > {
1181- Ok ( Text :: new ( "Git tag format ({version}, optional {name}) :" )
1263+ Ok ( Text :: new ( "Git tag format:" )
11821264 . with_default ( DEFAULT_TAG_FORMAT )
1265+ . with_help_message ( TAG_FORMAT_HELP )
11831266 . prompt ( ) ?)
11841267 }
11851268
@@ -1195,6 +1278,7 @@ impl InitPrompt for StdinInitPrompt {
11951278 "Codeberg (Coming Soon)" ,
11961279 ] ,
11971280 )
1281+ . with_help_message ( "only GitHub is fully supported today" )
11981282 . prompt ( ) ?;
11991283
12001284 if ans == "GitHub" {
@@ -1210,6 +1294,7 @@ impl InitPrompt for StdinInitPrompt {
12101294 "Where should release notes be maintained?" ,
12111295 vec ! [ "Root CHANGELOG.md" , "Per-package CHANGELOG.md files" ] ,
12121296 )
1297+ . with_help_message ( CHANGELOG_SCOPE_HELP )
12131298 . prompt ( ) ?;
12141299
12151300 if ans. starts_with ( "Root" ) {
@@ -1228,6 +1313,7 @@ impl InitPrompt for StdinInitPrompt {
12281313 "Semantic-style commit list since the last matching tag" ,
12291314 ] ,
12301315 )
1316+ . with_help_message ( NOTES_HELP )
12311317 . prompt ( ) ?;
12321318
12331319 if ans. starts_with ( "Copy" ) {
0 commit comments