9 Commits

Author SHA1 Message Date
Alexander ab49450467 Compose fixes to a fixpoint and learn only from success
New thefuck/compose.py resolve() iterates the auto-run sources
(learned-db exact, error-suggestion, history, path guess, help) per
round on the previous round's script until stable, a revisit aborts
or no source has a safe candidate; dangerous candidates are declined
mid-round and consultation continues. Output-aware sources get a
synthesized Command wrapping the round's script with the original
failure's output. Zero accepted steps return None so the original
failing script is never auto-executed; no round cap (termination
contract: visited-set + fixed-range/word-count-preserving sources,
stated in the module docstring; a compose-local memo acts only as a
determinism tripwire).

fix_command runs the inline chain through the composer; under
settings.repeat or a non-nushell shell it records immediately before
the run unless the only step replayed a learned-db exact hit; under
nushell it closes the learned db first and records on returncode 0,
forgets on failure (poison self-heal; CorrectedCommand.run now
stashes the nu returncode on the instance).

learned.forget(original, corrected) is the exact inverse of record
(cmd: + matching-count word:/part: deletes, assembly failures are a
documented no-op); learned.close_db is idempotent and atexit now
registers the stable close_db method exactly once across
close/reopen cycles.

Test migration (authorized): test_fix_command_learned.py rebased to
composer semantics (per-round consultation incl. error-suggestion;
input-aware fakes so round 2 declines, preserving the recordings==[]
pin in test_exact_learned_wins_over_guess).
2026-09-14 21:02:14 +02:00
Alexander 7d89277717 Scale typo gates with length and refuse destructive auto-runs
Similarity requirements now scale with word length (typo.floor_ok:
single edit or ratio >= max(0.6, 1 - 3/max(len,3)), first char kept;
zsh spdist / nushell did_you_mean derivation in the source). The
fixed-cutoff gates are swapped: history_resolver._similar ->
floor_ok with the divergence cap deleted (every diverged token is
gated per-token, not counted), learned.guess_from_path and the help
resolver candidate gates -> floor_ok, so _TOKEN_CUTOFF,
_MAX_DIVERGED, GUESS_CUTOFF, _CUTOFF and their difflib plumbing are
gone; unique-survivor, no-op and which/'/'/'.'/flag guards kept.

New thefuck/danger.py is_dangerous(script) parses via bashlex
directly and fail-safes to True when bashlex is unavailable or the
script refuses to parse (the flat fallback is head-only); with a
tree it matches rm/rmdir recursive flags, dd/mkfs*/shred/wipefs/
mkswap heads, git push --force/-f (not --force-with-lease),
chmod/chown -R with a 777-style mode, kill -9, fork-bomb shapes,
pipe-to-shell tails and file redirects outside /tmp and /dev/null.
fix_command checks it before ANY auto-run, learned-db exact hits
included: dangerous candidates fall through to rules+ask.

Test-migration inventory (authorized semantic inversions):
- tests/resolvers/test_history_resolver.py: declines-3-diverged ->
  corrects (cap deleted); 0.8-cutoff boundary arithmetic re-based to
  floor boundaries (len-3 0.6 / len-10 0.7 / len-30 0.9);
  _TOKEN_CUTOFF import removed with the constant; declines-just-
  below-cutoff re-based to the len-20 floor 0.85; added a 17-char
  below-floor decline.
- tests/test_learned.py: returns-none-below-cutoff re-based to a
  same-first-char below-floor pair (0.6 < len-10 floor 0.7); added a
  ratio-0.7 acceptance pin; single-edit-under-cutoff renamed.
- tests/resolvers/test_help_resolver.py: transposition comments
  re-based to the floor; added a below-floor subcommand decline.
- tests/entrypoints/test_fix_command_learned.py: exact-learned-wins
  fixture's 'git push --force' correction (now correctly refused)
  replaced by benign scripts; mock_learned stubs danger benign for
  platform-neutral auto-apply tests; added TestDangerOverride
  (real module, all four sources: reaches select_command, nothing
  auto-runs).
2026-09-14 20:42:26 +02:00
Alexander 572d301bfe Run resolvers before rules with recording
Before consulting rules, fix_command now walks the resolver chain in
the fixed order learned-db -> history -> /nix/store/zc6fy62c341aibk36p4ywff7rf9wn4wx-ripgrep-15.2.0/bin:/run/wrappers/bin:/home/fujin/.nix-profile/bin:/nix/profile/bin:/home/fujin/.local/state/nix/profile/bin:/etc/profiles/per-user/fujin/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/home/fujin/.local/share/JetBrains/Toolbox/scripts:/nix/store/rm982p6i9ry7rwq5jn638kdpv0a2331f-binutils-wrapper-2.46/bin:/nix/store/xabfaf6i4ry9r7jdg8rn7z56j34fjfgm-hyprland-qtutils-0.1.5/bin:/nix/store/mjgd7lbvp8rli365likrg9m4a5mmi5w4-pciutils-3.15.0/bin:/nix/store/paknc34adnxagnadmx2pg4c90hss8amv-pkgconf-wrapper-2.5.1/bin guess -> binary help:
history similarity runs before the disk-wide program-name guess per
the user requirement, and the binary's own --help is the last
automatic option before asking. Whichever resolver yields a script
is logged with its source, recorded via record(), and run through
the existing priority-0 CorrectedCommand block, so every automatic
fix is remembered; declines and aborts still record nothing and the
ask path is unchanged.
2026-09-13 20:59:16 +02:00
Alexander 1d3e4b7e8a Guess mistyped commands from $PATH before asking the user
When no learned correction matches, fuzzy-match the mistyped token
against executables from $PATH and shell aliases. A guess is auto-run
and recorded into the learned-corrections db only when it is
unambiguous: the token is not an existing executable, contains no
path separators or extensions, shares its first character with the
candidate, has similarity of at least 0.8, and exactly one candidate
survives. Asking the user stays the last resort.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-13 20:01:22 +02:00
Alexander 8d2f7d13c7 Add dynamic learning from accepted corrections
Record user-accepted corrections in a shelve-backed store and replay
them on future matching mistakes, bypassing rule evaluation entirely.

Two-level matching: exact full-command lookup, then word-level
reconstruction that generalises to different arguments (e.g. learning
'git psuh origin main' also fixes 'git psuh origin dev').
2026-04-15 12:06:41 +02:00
Joris Hartog 0f4a523dc4 Encapsulate force_command in _get_raw_command
Using the `force_command` argument will run into issues as the
`_get_raw_command` method simply returns the value of `force_command`
(which is a string) while it should actually return a list.

Fix #1240
2021-12-20 00:05:47 +01:00
Pablo Santiago Blum de Aguiar 58ddd4338a #1215: Initiate settings before printing the alias 2021-07-21 13:09:19 +02:00
Vladimir Iakovlev 4875d75a64 #682: Ensure that script exists 2017-09-03 10:10:50 +02:00
Vladimir Iakovlev f9a4b69362 #N/A: Reorganize entrypoints 2017-09-02 09:30:03 +02:00