update todo

This commit is contained in:
Emil Lerch 2026-05-18 17:16:41 -07:00
parent 2f13b16021
commit 8e4dfffc3f
Signed by: lobo
GPG key ID: A7B62D657EF764F8

112
TODO.md
View file

@ -156,77 +156,6 @@ Out of scope for V1: file-format alternatives (SVG, PDF), themed
color overrides for export (always uses the active terminal theme),
non-chart command output as PNG.
## CLI architecture overhaul — priority MEDIUM
The CLI surface has grown to 21 top-level commands and the dispatch
machinery in `src/main.zig` is starting to bloat. Three related gaps,
all best addressed together because they share the same refactor
vehicle:
1. **No per-command `--help`.** `zfin help` shows a wall of every
command and every flag. There's no way to ask "what does
`projections` accept?" without grepping the top-level usage.
Every command should support `zfin <command> --help` printing
just that command's synopsis, flags, and a few examples.
2. **`usage` text is roughly chronological-by-feature.** Re-group it
into workflow sections so users can scan for what they need:
- **Per-symbol lookups:** `perf`, `quote`, `history` (symbol
mode), `divs`, `splits`, `options`, `earnings`, `etf`
- **Portfolio analysis:** `portfolio`, `analysis`, `history`
(portfolio mode), `projections`, `milestones`
- **Time-series & journaling:** `snapshot`, `compare`,
`contributions`
- **Data hygiene:** `audit`, `enrich`, `lookup`
- **Infra:** `cache`, `version`, `interactive`
3. **Per-command flag parsing lives inline in `runCli`.** It's
~500 lines now. The `projections`, `contributions`, and `compare`
branches each carry ~50 lines of inline flag parsing plus their
own conflict-detection. Each command module should own its own
`parseArgs()`; `runCli` should be pure dispatch.
The natural mechanism for all three is a **command registry table**
mirroring the `tab_modules` registry in `src/tui.zig`:
```zig
// src/main.zig
const command_registry = .{
.perf = @import("commands/perf.zig"),
.quote = @import("commands/quote.zig"),
// ...
};
// each command module exposes:
// pub const meta = .{
// .help = "perf <SYMBOL> Show trailing returns ...",
// .group = .symbol_lookup,
// .takes_symbol = true,
// .needs_portfolio_path = false,
// };
// pub fn parseArgs(allocator, args, today) !ParsedArgs
// pub fn run(io, allocator, *DataService, parsed, today, color, *Writer) !void
```
A comptime walk over the registry produces:
- The grouped `usage` text (sort by `meta.group`, render section
headers).
- Per-command help dispatch (`zfin <cmd> --help` prints
`meta.help` plus the parsed flag descriptors).
- The dispatch chain itself (replace the giant `if-else if`).
- The "does this command take a symbol?" / "does it need a
portfolio path?" decisions currently scattered across `runCli`.
**Driver:** the per-command `--help` is the loudest user-facing gap;
the registry refactor is the cleanest way to land it without
scattering help text across more files. Also unblocks `zfin doctor`
(below) and the unified time-range parser (below) by giving them
a clean module shape to plug into.
**Open question for when this is picked up:** how to handle the
`history` command's two modes (symbol vs portfolio) under a single
registry entry. Probably one entry with a parser that branches on
`args[0]`, same as today — but the registry shape should accommodate
that without ceremony.
## `zfin doctor` health-check command — priority LOW
Front-door command for the file constellation and environment.
@ -327,47 +256,6 @@ README still says "six tabs," actual count is eight (Portfolio,
Quote, Performance, Options, Earnings, Analysis, History,
Projections).
## Unified time-range flag parser — priority LOW
`compare`, `contributions`, `projections --vs`, and `projections
--as-of` all accept overlapping but slightly different time-range
inputs. Today there are two helpers in `src/commands/common.zig`
(`parseAsOfDate`, `parseCommitSpec`) and the conflict-detection
logic (`since != null and before_spec != null`, etc.) is duplicated
across each command's flag-parsing block in `main.zig`.
### Sketch
```zig
// src/commands/common.zig
pub const TimeRange = struct {
before: Endpoint,
after: Endpoint,
pub const Endpoint = union(enum) {
date: Date,
commit_spec: CommitSpec,
live,
head,
};
};
pub fn parseTimeRange(args: ...) !TimeRange { ... }
```
Each command consumes a `TimeRange` and decides which endpoint
combinations make sense:
- `compare` rejects `live`-on-both-sides.
- `contributions` rejects `live` entirely (no meaningful "live
contributions diff").
- `projections --vs` accepts `live` on either side.
### Naturally folds into the CLI architecture overhaul
Once each command has its own `parseArgs()` (per the CLI overhaul
entry above), the time-range parser becomes a shared utility those
parsers call. Could either land standalone or be pulled in as part
of the overhaul. If the overhaul lands first, this is one of its
follow-ups; if this lands first, the overhaul inherits it for free.
## Split `audit.zig` into per-broker reconcilers — priority LOW
`src/commands/audit.zig` is 3438 lines — the largest command file