Commit graph

1173 commits

Author SHA1 Message Date
Bryan Helmkamp
fcd229f3e9 Adopt cli-table for ANSI-aware table rendering and fix fabro ps bugs
Migrate all 7 CLI tables to cli-table, which measures column widths
correctly in the presence of ANSI escape codes, fixing misaligned
columns in `fabro ps`. Also fix DIRECTORY to show ~/relative paths
instead of just the last component, and compute elapsed duration for
running jobs instead of showing "-".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:27:10 -04:00
Bryan Helmkamp
a2ad0e6645 Extract short_run_id helper to deduplicate run ID truncation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:27:09 -04:00
Bryan Helmkamp
0e3063fdee Make fabro ps behave like docker ps
Default to showing only running processes (use -a for all), remove row
limit, display oldest-first, truncate run IDs to 12 chars, add DIRECTORY
column from host_repo_path, and drop STARTED/COST/LABELS columns.

- Add host_repo_path to Manifest and populate from RunConfig
- Add StatusFilter enum (RunningOnly/All) to filter_runs
- Replace --limit with -a/--all flag (docker-ps semantics)
- Add host_repo_path to RunInfo, extract in scan_runs
- New column layout: RUN ID | WORKFLOW | STATUS | DIRECTORY | DURATION

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:27:09 -04:00
Bryan Helmkamp
6c3148ca5f chore: bump snapshot to fabro-v5
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:27:09 -04:00
Bryan Helmkamp
234fc8845f Disable debug info for dependencies in dev builds
[profile.dev.package."*"]
debug = false

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:27:09 -04:00
Bryan Helmkamp
7355c48267 chore: disable incremental compilation in sandbox image
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:27:09 -04:00
Bryan Helmkamp
1b3e983d05 Improve fabro ps output
This PR enriches the `fabro ps` output with colored status indicators,
duration/cost columns, relative timestamps, and pagination controls.
Status values are now color-coded (green for success, red for fail, cyan
for running, dim for unknown), the header row is bolded, and
separators/labels are dimmed.

Duration and total cost are now extracted from `conclusion.json` and
displayed as new columns. Start times are shown as human-friendly
relative strings (e.g., "2m ago", "3h ago") instead of raw RFC 3339
timestamps, with full timestamps preserved in `--json` output.

New `--limit N` (default 10) and `--all` flags cap the displayed output,
with a footer indicating how many runs are shown out of the total.

PR: https://github.com/fabro-sh/fabro/pull/2
2026-03-15 17:27:09 -04:00
Bryan Helmkamp
21cca3572e chore: add gh cli to image 2026-03-15 15:02:58 -04:00
Bryan Helmkamp
1bcb4323e6 Fix --goal-file not expanding ~ to home directory
Move expand_tilde from fabro-config to fabro-util::path so it can be
shared without circular dependencies, and apply it to the goal file
path in resolve_cli_goal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:57:38 -04:00
Bryan Helmkamp
2edf639a64 Deduplicate CheckpointSaved hook, use idiomatic bsha.clone()
Move the identical CheckpointSaved hook block from both git and non-git
checkpoint branches to a single block after the if/else. Replace
bsha.to_string() with bsha.clone() for &String → String conversion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:50:42 -04:00
Bryan Helmkamp
24532c2385 Fold CheckpointSaved into CheckpointCompleted
Remove the separate CheckpointSaved event — CheckpointCompleted now fires in
both git and non-git paths. git_commit_sha is Optional (None when git is
disabled or for start nodes). The CheckpointSaved hook event is preserved
unchanged for backward compat with user hook configs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:43:09 -04:00
Bryan Helmkamp
835132cdae Add granular git events, rename GitCheckpoint → CheckpointCompleted
Rename GitCheckpoint/GitCheckpointFailed to CheckpointCompleted/CheckpointFailed
to separate checkpoint lifecycle from git operations. Add 7 new granular git
events: GitCommit, GitPush, GitBranch, GitWorktreeAdd, GitWorktreeRemove,
GitFetch, GitReset. Emit at all relevant call sites in engine.rs and parallel.rs.
Update push helpers to return bool for GitPush success tracking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:35:17 -04:00
Bryan Helmkamp
92a0582b4a Add RetroStarted, RetroCompleted, RetroFailed events
Replace fake StageStarted/StageCompleted events with dedicated retro
variants so consumers can distinguish retro activity from normal stages.
The resume path now emits retro events instead of silently skipping them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:21:43 -04:00
Bryan Helmkamp
7de9fe04a2 Fix empty-status backward-compat bug, add From<&StageUsage> for Usage
- Fix: empty-string status from old logs now defaults to "success"
  instead of rendering as red/error
- Add From<&StageUsage> for fabro_llm::Usage to centralize conversion
- Simplify usage aggregation: replace collect+reduce+unwrap with
  direct .reduce() on the iterator

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:14:17 -04:00
Bryan Helmkamp
a52bdda2ca Add status + usage to WorkflowRunCompleted, enrich fabro logs --pretty summary
Adds aggregate `status` and `usage` fields to the WorkflowRunCompleted
event so `fabro logs --pretty` can render a complete end-of-run summary
(status, tokens, cache, reasoning) without scanning all StageCompleted
events. Also adds pretty handlers for PullRequestCreated/Failed events.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:04:02 -04:00
Bryan Helmkamp
fd7d302a15 Strip "Plan:" prefix from PR titles
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 13:47:20 -04:00
Bryan Helmkamp
e1b9da9d12 Fix race condition between fabro run --detach and fabro logs -f
Write id.txt and touch empty progress.jsonl in detach_run() before
spawning the child process so that `fabro logs -f ULID` can resolve
the run and tail the file immediately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 13:37:48 -04:00
Bryan Helmkamp
457f0a6d6a Add -p short alias for --pretty in fabro logs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 13:29:34 -04:00
Bryan Helmkamp
b15d697b23 commas 2026-03-15 12:31:08 -04:00
Bryan Helmkamp
cac163f0e4 Fix dry-run bug in API server and deduplicate test helpers
The API server set RunConfig.dry_run but never called
engine.set_dry_run(), so command/script nodes executed for real
during API-served dry runs. Add the missing call.

Also extract EngineServices::test_default() to replace 11 identical
make_services() bodies and 8 inline struct constructions across
handler test modules (-254/+57 lines).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:28:54 -04:00
Bryan Helmkamp
faf5440677 Fix --dry-run pushing branches to remote
--dry-run was not suppressing real git push operations in three places:
pre-run branch sync, post-run auto-PR creation, and engine checkpoint
pushes. Guard all three with dry_run checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:12:35 -04:00
Bryan Helmkamp
42988044f3 docs 2026-03-15 12:11:54 -04:00
Bryan Helmkamp
9388de5754 Fix --dry-run executing command/script nodes instead of simulating them
Command nodes were running for real during dry-run mode because the
dry_run flag only affected LLM-backed handlers. Propagate dry_run
through EngineServices so CommandHandler can skip execution and return
a simulated success.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:07:41 -04:00
Bryan Helmkamp
0cb1fab86e Add progress spinner to run --preflight
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 11:44:22 -04:00
Bryan Helmkamp
f6cf39a6ea cleanup workflows 2026-03-15 11:04:04 -04:00
Bryan Helmkamp
3a4743e10a files-internal -> docs-internal 2026-03-15 10:58:01 -04:00
Bryan Helmkamp
47b17d6541 rm prompts 2026-03-15 10:57:26 -04:00
Bryan Helmkamp
d086811c2f Add fabro inspect to show detailed JSON data for a workflow run
Outputs run_id, run_dir, status, manifest, conclusion, checkpoint, and
sandbox as a JSON array (null for missing files). Resolves runs by ID
prefix or workflow name, matching existing `fabro logs` semantics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 18:19:21 -04:00
Bryan Helmkamp
01e855608c Handle credential-embedded GitHub URLs in parse_github_owner_repo
URLs like https://x-access-token:TOKEN@github.com/owner/repo.git are
used by Daytona sandboxes. Strip the credentials before matching the
github.com prefix so pr_create and other callers work in those envs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 17:47:55 -04:00
Bryan Helmkamp
9dedaa9e39 Fix doctor trycmd test: move env vars to [env.add] section
trycmd's Env struct requires env vars under [env.add], not directly
under [env]. Vars placed directly under [env] are silently ignored by
serde, so the subprocess ran with a fully cleared env. On CI this caused
dirs::home_dir() to fall back to passwd, loading the real cli.toml
(with app_id) but without GITHUB_APP_PRIVATE_KEY → partial config error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 17:28:44 -04:00
Bryan Helmkamp
589a98ec6e Add fabro run --detach to fork workflows as background processes
Pre-generates a ULID in the parent, passes it to the child via hidden
`--run-id` arg, prints the ULID to stdout, and exits immediately.
Child stdout/stderr go to `{run_dir}/detach.log`. Uses `setsid()` on
unix to detach from the controlling terminal. Existing `fabro ps` and
`fabro logs` work with no changes since `run.pid` and `conclusion.json`
are written by the child as usual.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 17:18:50 -04:00
Bryan Helmkamp
87e231a78f Fix doctor dry-run trycmd test on CI by setting HOME to nonexistent path
`inherit = false` clears HOME but `dirs::home_dir()` falls back to the
passwd database, picking up the runner's ~/.fabro/cli.toml. The loaded
app_id without GITHUB_APP_PRIVATE_KEY triggers a partial-config error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 17:06:35 -04:00
Bryan Helmkamp
4e40f5ce3d Add routing context to EdgeSelected event: reason, status, hints
Emit reason (condition/preferred_label/suggested_next/unconditional/
jump/fallback), stage_status, preferred_label, suggested_next_ids,
and is_jump so logs explain why an edge was chosen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:47:28 -04:00
Bryan Helmkamp
1806aa86d6 Add inherit = false to all trycmd test envs to prevent CI flakiness
The doctor dry-run test was failing in CI because it inherited the host
environment. With no LLM API keys set, the doctor reported errors and
exited non-zero. Adding `inherit = false` to all 18 .toml test files
ensures deterministic behavior regardless of the host environment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:43:23 -04:00
Bryan Helmkamp
06f8fcef06 Tweak fabro logs --pretty styling: increase indent, dim model brackets
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:32:03 -04:00
Bryan Helmkamp
b3e763d78d Fix fabro logs --pretty wrapping past terminal edge on assistant messages
Markdown was rendered at full terminal width then indented, pushing lines
past the right edge. Now wraps to terminal_width minus indent first.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:30:03 -04:00
Bryan Helmkamp
263399e0cd Store workflow slug in manifest so resolve_run can match by directory name
When running `fabro run smoke`, the slug "smoke" was used to locate the
workflow but never persisted. If the DOT graph name diverged from the
directory name (e.g. workflows/foo/ contains digraph Bar), resolve_run
couldn't find the run by slug. Now the slug is extracted from the
workflow path, stored in the manifest, and matched in resolve_run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:14:06 -04:00
Bryan Helmkamp
387cee9c10 Add instability warning to run directory docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:11:24 -04:00
Bryan Helmkamp
bd875bc26c Make resolve_run match workflow slugs and display names
Workflow names in manifests are PascalCase (e.g. "LegacyTool") but
users expect to type the slug (e.g. "legacy-tool"). resolve_run now
compares case-insensitively and with hyphens/underscores stripped,
so both forms work.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:00:14 -04:00
Bryan Helmkamp
e510fdc2b8 Add fabro logs command to view workflow run event logs
Supports raw JSONL output (pipeable to jq) and --pretty mode with
colored, formatted output showing stages, tool calls, and assistant
messages. Includes --follow, --since, and --tail filtering options.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:55:58 -04:00
Bryan Helmkamp
b6cc56c17b Add resolve_run() that accepts run ID prefix or workflow name
Subcommands like cp, diff, preview, ssh, and pr previously only
accepted run ID prefixes. The new resolve_run() tries run ID prefix
first, then falls back to workflow name (most recent run), making
these commands more ergonomic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:42:03 -04:00
Bryan Helmkamp
9c7bb2cc32 Reorganize docs nav: merge Server Mode into Deployment, move Comparison and Dark Factory
- Merge core-concepts/server-mode into administration/deploy-server
- Move Comparison from Getting Started to Reference
- Move Dark Factory from Getting Started to Core Concepts
- Update all internal links to server-mode

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:39:08 -04:00
Bryan Helmkamp
8188b47f1f Fix detect_binary test to work on Ubuntu (dash) sandboxes
`sh --version` exits non-zero on dash (Ubuntu default), so the test
only passed on macOS where sh is bash. Use `git` instead which
reliably supports --version on all platforms.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:31:27 -04:00
Bryan Helmkamp
4bb9f935ea Extend smoke workflow to run linting and tests, add python3 to sandbox
Add python3 to the Daytona Dockerfile so MCP integration tests can run
their test server. Update the smoke workflow to verify fmt, clippy,
cargo test, typecheck, and bun test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:28:38 -04:00
Bryan Helmkamp
738d42a7b0 Fix empty run_id on sandbox events in progress.jsonl
The JSONL listener's run_id was initialized to "" and only populated
when WorkflowRunStarted fired, but sandbox events emit before that.
Seed it with the already-generated ULID so all events carry the run_id.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:00:40 -04:00
Bryan Helmkamp
bc0bbc273c Add Daytona sandbox defaults to fabro.toml and smoke workflow
Set Daytona as the project-level default sandbox so workflows that don't
specify their own sandbox config run on Daytona automatically. Add a
smoke workflow that verifies the sandbox toolchain (git, rustc, bun).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:53:53 -04:00
Bryan Helmkamp
5fe1925944 Add fabro workflow create <name> subcommand to scaffold new workflows
Writes a starter workflow.fabro (DOT graph) and workflow.toml into the
project's workflows directory. Supports --goal flag and derives the
digraph name from the workflow name using PascalCase conversion. Also
defaults the `graph` field in workflow.toml to "workflow.fabro" so it
can be omitted from generated configs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:32:00 -04:00
Bryan Helmkamp
79ef0672d9 Extract RunDefaults::merge_overlay to replace inline field-by-field merge
The inline merge in run_command() duplicated the structure of
apply_defaults() with shallower (inconsistent) semantics. This extracts
a proper merge_overlay method that deep-merges compound fields (vars,
hooks, mcp_servers, sandbox sub-fields) consistently with apply_defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:23:23 -04:00
Bryan Helmkamp
465ec0b956 Fix logo SVG viewBox clipping the right edge of the O
The scale(1.25) transform pushed the O's rightmost extent to ~x=1488,
past the old viewBox width of 1455.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:11:36 -04:00
Bryan Helmkamp
c0b4a2dc83 Extend fabro.toml with project-level run defaults
Add project-level run defaults to fabro.toml so sandbox, LLM, hooks,
MCP servers, and other settings can be shared across workflows instead
of duplicated in each workflow.toml. Precedence: workflow.toml >
fabro.toml > cli.toml/server.toml.

- Rename `directory` → `work_dir` with backwards-compat serde alias
- Add `hooks` and `mcp_servers` to `RunDefaults` with merge logic
- Extend `ProjectConfig` with all run-defaults fields + `into_run_defaults()`
- Remove duplicate `McpServerEntry` from fabro-config (use run_config's)
- Move `hook_config` from ServerConfig into `run_defaults.hooks`
- Wire project config merge and hooks/mcp fallbacks in run_command()
- Update OpenAPI spec and regenerate TypeScript client

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:11:16 -04:00