Skip to content

langstage-cli init is all-or-nothing: if EITHER my_agent.py or langstage.toml exists it refuses to write the OTHER (missing) file too — can't repair a half-scaffolded project without --force clobbering the surviving file #134

Description

@dkedar7

Summary

langstage-cli init scaffolds two files — my_agent.py and langstage.toml. But its overwrite guard is global, not per-file: if either target already exists (and --force isn't passed) it refuses to write both, so the missing file is never created. A real user who loses just one of the two (a stray git clean, an accidental rm, an edit gone wrong) can't run init to regenerate the missing half — it just errors and writes nothing, leaving a half-scaffolded, non-runnable project.

The docstring/README frame the guard as protecting existing files ("refuses to overwrite an existing my_agent.py / langstage.toml unless --force"), but the implementation over-refuses: it also declines to create a file that doesn't exist. And the error names only the existing file, so the user is never told the file they actually needed wasn't written either.

Severity: minor (a recovery-path UX gap; deterministic).

Environment

  • langstage-cli 0.6.30 (from PyPI, clean venv)
  • langstage-core 1.0.34, Python 3.11, Linux

Minimal repro

mkdir proj && cd proj
langstage-cli init            # writes my_agent.py + langstage.toml  (✓)
rm my_agent.py                # lose ONLY the agent file
langstage-cli init            # try to regenerate the missing my_agent.py
#   -> Error: refusing to overwrite existing langstage.toml.
#      (writes NOTHING — my_agent.py is still missing)
langstage-cli "hello"         # project is now broken
#   -> Error: FileNotFoundError: Agent file not found: .../proj/my_agent.py

The reverse is symmetric — keep my_agent.py, delete langstage.toml, run init: Error: refusing to overwrite existing my_agent.py. and the missing langstage.toml is not written.

Evidence (literal output, ANSI stripped)

$ langstage-cli init                # in a dir that has langstage.toml but NOT my_agent.py
Error: refusing to overwrite existing langstage.toml.
Re-run with --force to overwrite.
$ ls
langstage.toml                      # my_agent.py NOT created
$ langstage-cli "hello"
Error: FileNotFoundError: Agent file not found: .../proj/my_agent.py

Expected vs actual

  • Expected: init writes the file(s) that don't exist (here, my_agent.py), and only refuses to overwrite the file(s) that do exist — matching the documented wording. The project is runnable again after re-running init.
  • Actual: because one file exists, init writes neither — the needed, missing file is never created — and the error mentions only the existing file, so the broken state is silent.

Why --force isn't an adequate workaround

--force writes both files unconditionally, so recovering the missing my_agent.py also overwrites the surviving langstage.toml back to the stock default (scaffold_init does an unconditional toml_path.write_text(_INIT_TOML)). A user who customized langstage.toml (a different [agent] spec, [ui] verbose, a pinned [configurable] thread_id) loses those edits. So today there is no way to regenerate just the missing default file without clobbering the customized surviving one.

Root cause

scaffold_init (langstage_cli/cli.py, ~L2010) computes the existing set and refuses globally, then writes both unconditionally:

if not force:
    existing = [p.name for p in (agent_path, toml_path) if p.exists()]
    if existing:
        _status(f"... refusing to overwrite existing {', '.join(existing)}.")
        return 1
...
agent_path.write_text(_INIT_AGENT_PY, encoding="utf-8")   # always both
toml_path.write_text(_INIT_TOML, encoding="utf-8")

The guard keys off "any file exists", not "this file exists", and the two writes are unconditional after it.

Suggested fix

Make it per-file: write each of my_agent.py / langstage.toml only if it's missing (or --force); skip the ones that already exist, reporting exactly what was written vs skipped. E.g.:

$ langstage-cli init   # langstage.toml exists, my_agent.py missing
✓ Wrote my_agent.py
• Skipped langstage.toml (already exists — use --force to overwrite)

That matches the documented "refuses to overwrite an existing …" contract, lets init repair a partially-missing scaffold, and keeps --force as the only way to clobber a file that genuinely exists. (If all-or-nothing is intentional, at minimum the error should state that the missing file was not created, so the user isn't left with a silently broken project.)


Filed by the nightly dogfood routine. Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingroutineFiled by the daily power-user dogfooding routine

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions