The current state of the system is always reflected in the /proc file system:

$ head /proc/meminfo
MemTotal:       16303688 kB
MemFree:         1455276 kB
MemAvailable:   11735720 kB
Buffers:         1204956 kB
Cached:          9583644 kB
SwapCached:            0 kB
Active:          7984812 kB
Inactive:        6185032 kB
Active(anon):    3322712 kB
Inactive(anon):   584080 kB

The system's best guess on available memory is MemAvailable, so we will calculate the percentage available.

Usually when you run a command on a regular interval, you get each new line as a separate event. But not in this case - you want the full text output (ignore-line-breaks):

name: memfree
input:
    exec:
        command: head /proc/meminfo
        ignore-line-breaks: true
        interval: 1s

Can now extract the total (I simply copied the first line and replaced the actual number with a group that captures all digits).

And then continue with a separate extract to find the available kB. Note that we did not say remove: true since we want to keep _raw alive for two separate extractions.

If we did not have ignore-line-breaks, then the first extract would match the first line, but the second extract would not pass it through - we needed all these lines in _raw at the same time.

actions:
- extract:
    input-field: _raw
    pattern: 'MemTotal:       (\d+) kB'
    output-fields: [mem_total]
- extract:
    input-field: _raw
    pattern: 'MemAvailable:   (\d+) kB'
    output-fields: [mem_available]
- convert:
  - mem_total: num
  - mem_available: num
- script:
    let:
    - percent_available: round(100*mem_available/mem_total)
- remove: [_raw,mem_total,mem_available]
output:
    write: console

Then it's a matter of converting to numbers, calculating the percentage, and removing unneeded fields.