Skip to main content

max / quasi

Refuse a flag where `quasi new` expects a directory `quasi new foo --path /tmp/x` took `--path` as the directory, generated a full app into a directory of that name, git-inited it, and reported success with next steps pointing at a path that was never written. The mistake wrote to disk and said nothing. `quasi new` has no flags, so a leading dash is now an error naming the argument, alongside USAGE, before anything is created. A third stray argument, silently ignored before, is refused the same way.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-23 20:50 UTC
Signed with PGP, not checked
Commit: 7461ef9b49c3ab7dd30ddc22232f68e2cfb74417
Parent: 4785c13
1 file changed, +21 insertions, -6 deletions
@@ -45,9 +45,8 @@
45 45
46 46 match args.next().as_deref() {
47 47 Some("new") => {
48 - let given = args.next();
49 - let into = args.next();
50 - match new(given.as_deref(), into.as_deref()) {
48 + let rest: Vec<String> = args.collect();
49 + match new(&rest) {
51 50 Ok((root, name)) => {
52 51 report(&root, &name);
53 52 ExitCode::SUCCESS
@@ -94,13 +93,29 @@
94 93 /// The two are not the same thing: `[directory]` moves where it lands, and the
95 94 /// name is what the crates are called. Reporting the directory as the name is
96 95 /// how the printed next step names a package that does not exist.
97 - fn new(given: Option<&str>, into: Option<&str>) -> Result<(PathBuf, Name), String> {
98 - let Some(given) = given else {
96 + fn new(rest: &[String]) -> Result<(PathBuf, Name), String> {
97 + // Before anything is read positionally. `quasi new foo --path /tmp/x` used
98 + // to take `--path` as the directory and generate into a directory of that
99 + // name beside whatever you were working in, reporting success and printing
100 + // next steps for a path that did not exist. `quasi new` has no flags, so
101 + // anything leading with a dash is a mistake rather than a directory.
102 + if let Some(flag) = rest.iter().find(|arg| arg.starts_with('-')) {
103 + return Err(format!(
104 + "unrecognised flag `{flag}`; `quasi new` takes no flags\n\n{USAGE}"
105 + ));
106 + }
107 + if let Some(extra) = rest.get(2) {
108 + return Err(format!("unexpected argument `{extra}`\n\n{USAGE}"));
109 + }
110 +
111 + let Some(given) = rest.first() else {
99 112 return Err(format!("a name is required\n\n{USAGE}"));
100 113 };
101 114 let name = Name::parse(given).map_err(|error| error.to_string())?;
102 115
103 - let root = into.map_or_else(|| PathBuf::from(&name.kebab), PathBuf::from);
116 + let root = rest
117 + .get(1)
118 + .map_or_else(|| PathBuf::from(&name.kebab), PathBuf::from);
104 119
105 120 // Checked before anything is written rather than per-file, so a refusal
106 121 // leaves nothing behind. `create_dir` rather than `create_dir_all` for the