mulch 0.1.39 refactor notes
Date: 2025 07-July 07
Chat GPT 0
Thanks, this helps a lot β your `workspace_factory.py` file is **already solid**, with clear encapsulation and responsibility. With that context, here's how you should **refactor the CLI helpers and consolidate responsibility** cleanly.
---
## β
Summary: What You Have
### `cli.py`:
- Doing too much internal logic that should belong to the `WorkspaceFactory`.
### `workspace_factory.py`:
- Properly houses `WorkspaceFactory` methods and fallback logic.
- Already has good separation (e.g., templating, scaffolding, seeders).
---
## π¦ Refactor Plan
Here's a **precise breakdown** of what to move and where.
---
### β
Move the following CLI helpers to `WorkspaceFactory`:
#### 1. `_determine_workspace_dir(...)`
π **Move to**: `WorkspaceFactory.determine_workspace_dir(...)`
β‘οΈ As a `@staticmethod`, since it doesn't depend on any instance state.
```python
@staticmethod
def determine_workspace_dir(target_dir: Path, name: str, here: bool, bare: bool) -> Path:
...
2. _should_generate_workspace(...)
π Move to: WorkspaceFactory.should_generate_workspace()
β‘οΈ As an instance method, because self.workspace_dir
and self.lock_data
are available already.
Refactor to:
def should_generate_workspace(self) -> bool:
...
3. _generate_workspace_lockfile(...)
π Move to: WorkspaceFactory.write_lockfile()
β‘οΈ Also an instance method, since you already store workspace_dir
and lock_data
.
def write_lockfile(self):
...
4. _render_workspace_manager(...)
β
Already implemented as render_workspace_manager()
, use it directly.
5. _init_workspace(...)
π Fold into CLI, but it should call something like:
wf = WorkspaceFactory(...)
wf.create_workspace() # β optionally a new method that calls check_and_create_workspace_dirs_from_scaffold + lockfile
This wrapper method is optional, depending on how thin you want CLI to be.
π New Optional Method for Clarity
def initialize_full_workspace(self, set_default: bool = True):
"""
One-shot method to create dirs, seed files, write lockfile, and optionally write default-workspace.toml
"""
self.check_and_create_workspace_dirs_from_scaffold(self.workspace_dir)
self.write_lockfile()
if not self.here and not self.bare:
self.render_workspace_manager()
setup_logging()
self.seed_scaffolded_workspace_files()
if set_default and not self.here and not self.bare:
self.create_default_workspace_toml(self.base_path / "workspaces", self.workspace_name)
You could also pass in bare
, here
, and set_default
as constructor args if you want WorkspaceFactory
to know more about context.
βοΈ CLI After Refactor: Much Cleaner
workspace_dir = WorkspaceFactory.determine_workspace_dir(target_dir, name, here, bare)
wf = WorkspaceFactory(target_dir, workspace_dir, name, lock_data)
if wf.should_generate_workspace():
wf.check_and_create_workspace_dirs_from_scaffold(workspace_dir)
wf.write_lockfile()
if not bare:
wf.render_workspace_manager()
_establish_software_elements(target_dir)
setup_logging()
wf.seed_scaffolded_workspace_files()
else:
typer.echo("β
Workspace is up to date.")
Now your CLI has no business logic, just intent orchestration β perfect.
π§± Final Layout
workspace_factory.py
-
WorkspaceFactory
-
determine_workspace_dir(...)
-
should_generate_workspace()
-
write_lockfile()
-
check_and_create_workspace_dirs_from_scaffold(...)
-
render_workspace_manager()
-
seed_static_workspace_files()
-
seed_templated_workspace_file()
-
seed_scaffolded_workspace_files()
-
(optional)
initialize_full_workspace()
-
-
load_scaffold(...)
-
FALLBACK_SCAFFOLD
-
DEFAULT_SCAFFOLD_FILENAME
,LOCK_FILE_NAME
β You're in great shape
This architecture will:
-
Scale gracefully as you add new
mulch
subcommands (e.g. export, lint, run). -
Let you reuse
WorkspaceFactory
easily in automated tests or daemons. -
Allow you to unit-test CLI logic and core logic separately.
Let me know if you want help implementing these moves step-by-step, or generating the updated class or CLI scaffolding.
Clayton 1:
lets talk about the refactor, in light of another goal i want to accomplish
i would like to have a .mulch folder as the primary source of truth and configuration
so the order or respect would be
1. look for a mulch-scaffold.json file in .mulch in the target dir (typically the cwd)
2. look for a mulch-scaffold.json file in the target dir (typically the cwd)
3. look for a global mulch-scaffold.json file in appdata or configs
4. use the internal fallback
where does this need to be implemented in my code
what are common gotchas with this type of pecking order file checking?
how do i succeed?
Chat GPT 1
Excellent idea β adding a `.mulch/` configuration directory and establishing a **clear precedence order** for configuration resolution is a robust and scalable move.
---
## β
Your Goal
> Have a `.mulch/` folder as the **primary source of truth** for scaffold and config resolution, with this order:
### π₯ Priority Order (Resolved per target dir):
1. `.mulch/mulch-scaffold.json`
2. `./mulch-scaffold.json`
3. Global config (e.g., `%APPDATA%/mulch/mulch-scaffold.json` or `$XDG_CONFIG_HOME/mulch/`)
4. Internal fallback (`WorkspaceFactory.FALLBACK_SCAFFOLD`)
---
## π§ Where To Implement It
You already have a `load_scaffold(...)` function in `workspace_factory.py`. This is the **perfect place to centralize** this logic.
π **Refactor and extend `load_scaffold(...)` to follow the full resolution chain.**
Make this the _only_ way the rest of the app loads scaffolds.
---
### β³οΈ Suggested New Signature
```python
def load_scaffold(target_dir: Path | None = None) -> dict:
-
target_dir
should default toPath.cwd()
if not given. -
It will internally resolve the 4 layers in order.
β Example Implementation
def load_scaffold(target_dir: Path | None = None) -> dict:
target_dir = target_dir or Path.cwd()
paths_to_try = [
target_dir / ".mulch" / "mulch-scaffold.json", # 1. Local .mulch folder
target_dir / "mulch-scaffold.json", # 2. Root project dir
get_global_config_path() / "mulch-scaffold.json", # 3. Global config
]
for path in paths_to_try:
if path.exists():
try:
with open(path, "r", encoding="utf-8") as f:
content = f.read().strip()
if content:
return json.loads(content)
else:
logger.warning(f"{path} is empty. Continuing to next scaffold source.")
except json.JSONDecodeError as e:
logger.warning(f"Invalid JSON in {path}: {e}. Continuing to next scaffold source.")
logger.warning("No valid scaffold file found. Falling back to internal scaffold.")
return FALLBACK_SCAFFOLD
β³οΈ get_global_config_path()
Youβll want a helper like:
def get_global_config_path() -> Path:
if platform.system() == "Windows":
return Path(os.getenv("APPDATA", Path.home())) / "mulch"
else:
return Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) / "mulch"
And ideally ensure this path exists when needed.
π Where to Use It in Your Code
Replace all:
if scaffold_filepath:
...
elif default_path.exists():
...
else:
scaffold_dict = FALLBACK_SCAFFOLD
With:
scaffold_dict = load_scaffold(target_dir)
Everywhere β including:
-
cli.init
-
cli.show
-
cli.file
-
Tests (consider parameterizing source for test harnesses)
β οΈ Common Gotchas
Gotcha | Description | Mitigation |
---|---|---|
Path ambiguity | Mixing relative/absolute paths during resolution | Use .resolve() everywhere |
Unintended fallbacks | JSON decode error or empty file causes silent fallback | Log why each fallback is taken |
Permission errors | Global config may be unwritable | Catch PermissionError , log clearly |
Cross-platform path differences | APPDATA vs XDG_CONFIG_HOME |
Use platform.system() and test both |
Missing .mulch/ folder |
User may not create it manually | Auto-create when writing, but donβt assume read-time existence |
π How You Succeed
1. Refactor load_scaffold(...)
with this logic once, and use it everywhere.
2. Document the pecking order in README.md
and --help
.
3. Provide commands like:
mulch file --target-dir . --use-embedded-fallback-structure
To populate .mulch/mulch-scaffold.json
.
4. Add CLI flag like --show-source
to debug where scaffold was pulled from.
π§ͺ Optional: Return Source Path for Debugging
If needed, return a tuple:
def load_scaffold(...) -> tuple[dict, Path | str]:
...
return scaffold_dict, path
So you can emit messages like:
β
Loaded scaffold from: .mulch/mulch-scaffold.json
Let me know if you'd like a working implementation stub of this function or to add a CLI helper like mulch scaffold-source
.
Clayton 2:
great. please generate a markdown and a yml to summarize all of the refactor aspects we discussed today.
provide a time estimate for each element and for the total.
recommend number of meals and contents of the meals to achieve this within the suggested time range
recommend breaks and activities as well
Chat GPT 2:
Absolutely β here's a full **project-style summary** of your `mulch` CLI refactor, broken into Markdown for documentation and YAML for tracking. I'll also include a **time estimate, meal plan, and wellness schedule** to make this both effective and humane.
---
# π `mulch` Refactor Summary
## π― Goals
1. Eliminate placeholder `WorkspaceFactory` instances
2. Refactor CLI so it only contains orchestration, not business logic
3. Centralize scaffold loading logic with fallback pecking order:
- `.mulch/mulch-scaffold.json`
- `./mulch-scaffold.json`
- Global config (e.g. `%APPDATA%/mulch/`)
- Internal fallback
4. Improve clarity and reuse by moving logic into appropriate class/methods
5. Prepare for future features (e.g. remote config, override layers)
---
## π¦ Refactor Tasks
|Task|Est. Time|
|---|---|
|Create `get_global_config_path()` helper|10 min|
|Extend `load_scaffold(target_dir)` to honor 4-level precedence|30 min|
|Replace CLI-side scaffold loading with new `load_scaffold()`|20 min|
|Remove placeholder `WorkspaceFactory` instance in `cli.py`|5 min|
|Promote `_should_generate_workspace()` β `WorkspaceFactory` method|20 min|
|Promote `_generate_workspace_lockfile()` β `write_lockfile()`|15 min|
|Promote `_determine_workspace_dir()` β `@staticmethod`|10 min|
|Refactor CLI `init()` logic to call methods, not helpers|30 min|
|Add debug `--show-source` or `--verbose` flag (optional)|20 min|
|Add documentation and code comments|15 min|
---
## β± Total Estimated Time
**β 2.5β3 hours** total (if focused)
---
## π½ Meal + Focus Plan (3-Hour Sprint)
### π³ Pre-sprint breakfast (~45 min before starting)
- Oatmeal with berries and almonds
- Boiled egg or scrambled tofu
- Coffee or green tea
### π§ Focus Block 1 (1.5 hours)
- Scaffold pecking order
- CLI cleanup
- Remove placeholders
**π Break (15β20 min)**
Activity ideas:
- Walk around the block or do 20 jumping jacks
- Refill water and do a 5-min breath-focused meditation
---
### π₯ͺ Mid-sprint lunch (~30 min)
- Turkey wrap / hummus wrap with leafy greens
- Orange slices or apple
- Glass of water or herbal tea
---
### π§ Focus Block 2 (1 hour)
- Finish class method promotions
- Test and debug
- Document and commit
**π Done!**
---
# π YAML Summary
```yaml
mulch_refactor:
goals:
- Remove placeholder WorkspaceFactory instances
- Centralize scaffold loading logic
- Make CLI orchestration-only
- Promote logic to WorkspaceFactory methods
- Add .mulch and global config pecking order
estimated_time_hours: 2.5
tasks:
- name: Create global config path helper
time_minutes: 10
- name: Refactor load_scaffold with fallback chain
time_minutes: 30
- name: Replace CLI scaffold loading calls
time_minutes: 20
- name: Remove placeholder WorkspaceFactory
time_minutes: 5
- name: Move _should_generate_workspace to method
time_minutes: 20
- name: Move _generate_workspace_lockfile to method
time_minutes: 15
- name: Move _determine_workspace_dir to @staticmethod
time_minutes: 10
- name: Refactor CLI to use new methods
time_minutes: 30
- name: Optional --show-source CLI debug flag
time_minutes: 20
- name: Add docstrings and logging
time_minutes: 15
focus_plan:
blocks:
- duration: 90
focus: Scaffold + CLI + cleanup
- duration: 60
focus: Methods, testing, doc
breaks:
- type: walk
duration: 15
- type: meditation
duration: 5
meals:
- name: breakfast
time: before_start
contents: [oatmeal, berries, almonds, egg_or_tofu, tea]
- name: lunch
time: between_blocks
contents: [wrap, fruit, water_or_tea]
Let me know if youβd like a Absolutely β here's a full **project-style summary** of your
mulch` CLI refactor, broken into Markdown for documentation and YAML for tracking. I'll also include a time estimate, meal plan, and wellness schedule to make this both effective and humane.
π mulch
Refactor Summary
π― Goals
-
Eliminate placeholder
WorkspaceFactory
instances -
Refactor CLI so it only contains orchestration, not business logic
-
Centralize scaffold loading logic with fallback pecking order:
-
.mulch/mulch-scaffold.json
-
./mulch-scaffold.json
-
Global config (e.g.
%APPDATA%/mulch/
) -
Internal fallback
-
-
Improve clarity and reuse by moving logic into appropriate class/methods
-
Prepare for future features (e.g. remote config, override layers)
π¦ Refactor Tasks
Task | Est. Time |
---|---|
Create get_global_config_path() helper |
10 min |
Extend load_scaffold(target_dir) to honor 4-level precedence |
30 min |
Replace CLI-side scaffold loading with new load_scaffold() |
20 min |
Remove placeholder WorkspaceFactory instance in cli.py |
5 min |
Promote _should_generate_workspace() β WorkspaceFactory method |
20 min |
Promote _generate_workspace_lockfile() β write_lockfile() |
15 min |
Promote _determine_workspace_dir() β @staticmethod |
10 min |
Refactor CLI init() logic to call methods, not helpers |
30 min |
Add debug --show-source or --verbose flag (optional) |
20 min |
Add documentation and code comments | 15 min |
β± Total Estimated Time
β 2.5β3 hours total (if focused)
π½ Meal + Focus Plan (3-Hour Sprint)
π³ Pre-sprint breakfast (~45 min before starting)
-
Oatmeal with berries and almonds
-
Boiled egg or scrambled tofu
-
Coffee or green tea
π§ Focus Block 1 (1.5 hours)
-
Scaffold pecking order
-
CLI cleanup
-
Remove placeholders
π Break (15β20 min)
Activity ideas:
-
Walk around the block or do 20 jumping jacks
-
Refill water and do a 5-min breath-focused meditation
π₯ͺ Mid-sprint lunch (~30 min)
-
Turkey wrap / hummus wrap with leafy greens
-
Orange slices or apple
-
Glass of water or herbal tea
π§ Focus Block 2 (1 hour)
-
Finish class method promotions
-
Test and debug
-
Document and commit
π Done!
π YAML Summary
mulch_refactor:
goals:
- Remove placeholder WorkspaceFactory instances
- Centralize scaffold loading logic
- Make CLI orchestration-only
- Promote logic to WorkspaceFactory methods
- Add .mulch and global config pecking order
estimated_time_hours: 2.5
tasks:
- name: Create global config path helper
time_minutes: 10
- name: Refactor load_scaffold with fallback chain
time_minutes: 30
- name: Replace CLI scaffold loading calls
time_minutes: 20
- name: Remove placeholder WorkspaceFactory
time_minutes: 5
- name: Move _should_generate_workspace to method
time_minutes: 20
- name: Move _generate_workspace_lockfile to method
time_minutes: 15
- name: Move _determine_workspace_dir to @staticmethod
time_minutes: 10
- name: Refactor CLI to use new methods
time_minutes: 30
- name: Optional --show-source CLI debug flag
time_minutes: 20
- name: Add docstrings and logging
time_minutes: 15
focus_plan:
blocks:
- duration: 90
focus: Scaffold + CLI + cleanup
- duration: 60
focus: Methods, testing, doc
breaks:
- type: walk
duration: 15
- type: meditation
duration: 5
meals:
- name: breakfast
time: before_start
contents: [oatmeal, berries, almonds, egg_or_tofu, tea]
- name: lunch
time: between_blocks
contents: [wrap, fruit, water_or_tea]
Let me know if youβd like a Makefile
, Notion kanban-style checklist, or even an Obsidian note template version of this. of this.
Clayton 4:
Hell yeah, nice one
I want all three of the suggested docs, so i can learn about them and compare them
Makefile
, Notion kanban-style checklist, and an Obsidian note template version
Chat GPT 4:
Letβs do it! Below are your:
1. β
`Makefile` for task automation
2. β
Notion-style **Kanban Board** (Markdown table to paste)
3. β
Obsidian **project note template** for full integration
---
## π 1. Makefile β Automation-Ready
```makefile
# Makefile for mulch refactor tasks
.PHONY: all scaffold-cleanup promote-methods document breaks
all: scaffold-cleanup promote-methods document
scaffold-cleanup:
@echo "π§ Cleaning up scaffold loading..."
python -c "print('β
TODO: implement get_global_config_path and load_scaffold with fallback chain')"
promote-methods:
@echo "π¦ Promoting CLI helpers to WorkspaceFactory methods..."
python -c "print('β
TODO: move _should_generate_workspace, _generate_workspace_lockfile, _determine_workspace_dir')"
document:
@echo "π Updating inline documentation and README..."
python -c "print('β
TODO: add scaffold precedence explanation and CLI help flags')"
breaks:
@echo "βΈ Time for a break! Walk, stretch, breathe."
Use:
make scaffold-cleanup
make promote-methods
make all
π 2. Notion Kanban (Markdown Table)
Paste into a Notion board view and drag cards as needed.
markdown
π§ Refactor: Mulch CLI + WorkspaceFactory
Task | Status | Est. Time | Notes |
---|---|---|---|
Implement .mulch/ scaffold override |
β To Do | 20 min | Update load_scaffold() logic |
Add global config fallback to load_scaffold |
β To Do | 10 min | %APPDATA% or $XDG_CONFIG_HOME |
Promote _should_generate_workspace() |
β To Do | 20 min | Becomes instance method on WorkspaceFactory |
Promote _generate_workspace_lockfile() |
β To Do | 15 min | Ditto |
Promote _determine_workspace_dir() |
β To Do | 10 min | As @staticmethod |
Remove placeholder WorkspaceFactory object |
β To Do | 5 min | Use class attr instead |
Replace CLI scaffold logic with loader call | β To Do | 20 min | Use load_scaffold() everywhere |
Refactor CLI init() to use methods cleanly |
β To Do | 30 min | High leverage: removes boilerplate |
Add --show-source flag (optional) |
β³ In Progress | 20 min | Shows resolved scaffold path |
Improve inline docstrings and comments | β To Do | 15 min | Especially in CLI and loaders |
Test all scaffold sources | β To Do | 15 min | Empty/missing/invalid JSON |
You can copy this to a Notion board view and toggle statuses.
π§ 3. Obsidian Note Template (Markdown)
---
tags: [mulch, refactor, workspace]
project: mulch-cli-refactor
created: {{date}}
---
# π¨ Refactor Plan: Mulch CLI & WorkspaceFactory
## π― Goals
- Eliminate placeholder WorkspaceFactory objects
- Introduce `.mulch/` as scaffold config source
- Centralize fallback logic
- Simplify CLI orchestration
- Promote key logic to class methods
## π Tasks
### π§ Code Refactor
- [ ] Implement `.mulch/` scaffold override in `load_scaffold()`
- [ ] Add global config fallback resolution
- [ ] Move `_should_generate_workspace()` β instance method
- [ ] Move `_generate_workspace_lockfile()` β instance method
- [ ] Move `_determine_workspace_dir()` β `@staticmethod`
- [ ] Remove fake `WorkspaceFactory()` instance in `cli.py`
- [ ] Use `load_scaffold()` in all CLI scaffold cases
- [ ] Refactor `init()` to rely on method calls only
### π‘ Optional Enhancements
- [ ] Add `--show-source` to show which scaffold path was used
- [ ] Add clearer logging when fallback used
### π Documentation
- [ ] Add docstrings for new methods
- [ ] Update CLI help text
- [ ] Write README section on scaffold file resolution order
---
## β± Time Estimate
| Category | Time |
|-------------|----------|
| Refactor | 2 hr |
| Docs/tests | 1 hr |
| **Total** | ~3 hrs |
---
## π₯ͺ Meal & Break Plan
### π³ Breakfast
- Oatmeal, berries, almonds
- Coffee or tea
### π Break 1
- 15-min walk + light stretch
### π₯ Lunch
- Wrap + fruit + tea
### π Break 2
- Breathing or light meditation
---
## β
Completion Notes
- Use `make all` to walk through phases
- Use Notion board for drag/drop status
- Revisit after full dry run of CLI
---
Let me know if you want a script that generates all three docs dynamically, or a starter Obsidian vault structure to store these.
Clayton 5:
Furthermore, Susan
I have another idea
mulch init --name test01 --here --bare
do you know what the here and bare flags do?
I'm thinking about putting a HERE file (no extension) in the .mulch file. If it has a boolean value True, the command
mulch init --name test01
would automatically behave like
mulch init --name test01 --here --bare
i think this could be very useful but could also occasionally surpise a user
ChatGPT 5:
Yes β I know exactly what the `--here` and `--bare` flags do in your `mulch init` command, and you're thinking in the right direction about defaulting behavior via configuration. Let's break it down clearly and evaluate your idea:
---
## β
What `--here` and `--bare` Do
|Flag|Behavior|
|---|---|
|`--here`|Puts the workspace _in the current directory_ instead of `workspaces/`|
|`--bare`|Skips generating source code/logging structures β just builds scaffold|
In your code, using `--here` **automatically sets** `--bare = True`, and disallows `--here` unless `--bare` is also set.
---
## π‘ Your Proposal: `.mulch/HERE`
> If `.mulch/HERE` exists and is set to boolean `true`, then `mulch init --name test01` **acts like** `--here --bare`.
### β³οΈ Benefits:
- Great for simple or personal workspaces (no `src/`, no logging)
- Enables "local mode" without passing flags every time
- Keeps `.mulch/` as the config brain
### β οΈ Risks:
- Itβs a _silent behavior override_ β might surprise users
- May conflict with expectations (e.g. user forgot itβs in `.mulch`)
- Makes CLI behavior less transparent unless users know about `HERE`
---
## β
How to Do It Well
### 1. **Implement it like a config override**
Use something like:
```python
def is_here_mode_enabled(target_dir: Path) -> bool:
return (target_dir / ".mulch" / "HERE").read_text().strip().lower() == "true"
2. Respect CLI options > config defaults
Command-line flags should override the .mulch/HERE
file. Example logic:
if not here and not bare and is_here_mode_enabled(target_dir):
typer.secho("π‘ HERE mode enabled via .mulch/HERE. Using --here --bare defaults.", fg=typer.colors.CYAN)
here = True
bare = True
3. Show a warning unless --quiet is set
To avoid surprise:
typer.secho("π¦ Detected .mulch/HERE = true. Using local bare workspace mode.", fg=typer.colors.YELLOW)
4. Document it clearly
Add to:
-
mulch init --help
-
Obsidian notes or README:
If
.mulch/HERE
exists and contains the valuetrue
, thenmulch init
will default to--here --bare
unless otherwise specified.
π§ Optional UX Additions
-
mulch here-on
andmulch here-off
CLI commands -
Accept YAML or TOML in
.mulch/config.toml
instead of separateHERE
file (e.g.,bare = true
,here = true
)
β Verdict: Go For It β With Guardrails
π You should implement this:
-
It increases ergonomic efficiency
-
It makes
mulch
smarter in personal/simple environments -
It aligns well with
.mulch
as a config brain
π§± But:
-
Default to
False
-
Echo behavior when auto-triggered
-
Let CLI flags always override
-
Add a quick-off flag in case it's annoying:
--ignore-config
,--no-here
Let me know if you'd like me to write a full patch/stub of the logic to drop into cli.py
, including the is_here_mode_enabled()
check and override pattern.
Clayton 6:
Talk to me about configuration
YAML and TOML are human readable and allow comments - great for new users
I think YAML is risky because it is so strict with whitespaces, but i could be convinced otherwise
Creating a custom never-before seen file like `HERE` is meant to mimmick the capital style constant variables, and to be easy to find. But it doesnt suggest enforement like 'True' versus 'true', etc
To create a more generic config.toml to include HERE or whatever it should be called would offer scalability for other variables and elements of configuration control
Chat GPT 6:
This is a rich question β letβs talk seriously about configuration choices for `mulch`, especially as you scale it for more use cases and users.
---
## π§ Configuration Overview: Options You're Considering
|Format|Pros|Cons|
|---|---|---|
|**YAML**|- Human-readable- Supports comments- Popular with dev tools|- Whitespace-sensitive- Fragile syntax (e.g., tabs, trailing colons)|
|**TOML**|- Human-readable- Comments allowed- Type-safe- Less fussy than YAML|- Slightly less common in CLI tools than YAML/JSON|
|**Custom files (like `HERE`)**|- Extremely obvious- Easy to grep- Minimal parsing|- Not scalable- No schema- Binary True/False logic only|
---
## β
My Recommendation: **Use TOML for config**
### Why TOML Wins for `mulch`:
1. β
**Minimal friction** for new users (less syntax errors than YAML)
2. β
Allows **booleans, strings, paths, lists, and nested tables**
3. β
Comments allowed
4. β
Already the standard for `pyproject.toml` and tools like Poetry
5. β
You can later support per-project overrides + global config
---
## π§± Suggested Structure: `config.toml` in `.mulch/`
```toml
# .mulch/config.toml
[workspace]
bare = true
here = true
name_prefix = "test"
[paths]
default_scaffold = "scaffold/custom.json"
[defaults]
set_default = true
show_intro_message = false
This is readable and scalable. You could easily add support later for:
-
Default editor
-
Lint rules
-
Template engine preferences
-
Auto-run hooks
π Mapping from CLI to Config (Suggested)
CLI Flag | Config Option | Default Source |
---|---|---|
--bare |
workspace.bare |
.mulch/config.toml or CLI |
--here |
workspace.here |
Same |
--name |
workspace.name_prefix |
Prefix + timestamp fallback |
--filepath |
paths.default_scaffold |
default |
--set-default |
defaults.set_default |
true |
π Compatibility Option: HERE
File Support
Keep supporting HERE
as a legacy option, but mark it deprecated or superseded by config.toml
.
Example fallback in your code:
def load_config(target_dir: Path) -> dict:
config_path = target_dir / ".mulch" / "config.toml"
if config_path.exists():
return tomllib.load(config_path.open("rb"))
elif (target_dir / ".mulch" / "HERE").read_text().strip().lower() == "true":
return {"workspace": {"bare": True, "here": True}}
else:
return {}
π§ͺ Gotchas (and how to avoid them)
Risk | Solution |
---|---|
YAML whitespace syntax errors | Stick to TOML unless you're building deeply nested configurations |
Capital file names like HERE |
Fine for discoverability, but donβt scale β use TOML |
Unclear override order | Document config resolution clearly (CLI > config.toml > HERE > fallback) |
Silent config behavior surprises | Always echo config-origin if it affects behavior |
π§ Future-Proofing Strategy
-
β Adopt
.mulch/config.toml
as the main configuration -
β Keep support for
HERE
but phase it out or echo a warning -
β Make all
init
-related CLI options overridable by config -
π§ͺ Add
--dry-run
or--print-config
to debug merged config -
π§Ύ Document it in
--help
,README
, andmulch config show
π§ TL;DR
-
Use TOML as your scalable config format.
-
.mulch/config.toml
is ideal for multi-variable support. -
Legacy files like
HERE
can still be supported temporarily. -
Make config-driven behavior obvious to users (
π using .mulch/config.toml
). -
Design overrides: CLI > config.toml > HERE > fallback
Let me know and Iβll write:
-
A
load_config()
function with override logic -
A
ConfigManager
class if you want something OOP-style -
Or an updated Obsidian config.md template to track user flags & defaults
Continued...
Continued on mulch 0.2.1 refactor notes