feat(shell): job-aware kill, disown, jobs/set flags, fallback REPL history

- Document interactive shell vs /bin/sh in bare-os-shell man, handbook §9,
  kill(1), and shell-completion guide
- Resolve kill %n and %% to synthetic PIDs via shellBackgroundJobs
- Add disown builtin; jobs -p (pgid-only) and -l (pid column); set -o/+o to
  print errexit/nounset/pipefail/noglob
- Expand completion-engine fallback flags for common utilities
- Persist lines to /.bare/repl_history_<USER> when BARE_OS_REPL_HISTORY=1
  and Fish REPL is off (repl-session + cli-readline)
- Tests: kill job specs, set -o output, jobs -p
This commit is contained in:
Raven Scott
2026-04-21 17:26:41 -04:00
parent c50bee46eb
commit 98dd91020e
46 changed files with 15812 additions and 13956 deletions
@@ -933,18 +933,21 @@ function shellPage() {
return {
name: 'bare-os-shell',
section: 1,
title: 'Bare OS interactive shell builtins',
synopsis: ['# builtins only — no full POSIX sh grammar'],
title: 'Bare OS interactive shell (Issue 7inspired subset)',
synopsis: [
'# Interactive session: builtins + /bin via ctx.execLine (see handbook §9)',
'# /bin/sh is a separate script runner — see man sh'
],
description:
'The line-at-a-time shell supports aliases, simple pipelines (simulated), redirection, and the builtins below. Compound commands (if, for, while) are not available.',
'The interactive shell runs in the booter: tokenization, pipelines (simulated capture), redirection, AND-OR lists (; && ||), bounded compound commands (if/fi, while/for/case), background jobs (&), optional command substitution ($(…)) when BARE_OS_SHELL_CMDSUBST=1, globbing, and subsets of errexit (-e), nounset (-u), and pipefail. There are no forked subshells and no full POSIX sh grammar. Canonical narrative: handbook ch.9; completion/REPL: docs/reference/shell-completion-and-repl-editor.md.',
options: [],
aliases: ['sh-builtins'],
keywords: [
'shell',
'builtin',
'cd',
'export',
'alias',
'pipeline',
'jobs',
'execLine',
'bare-os-shell',
'sh-builtins'
],
@@ -953,7 +956,7 @@ function shellPage() {
name: 'alias',
synopsis: ['alias', 'alias name=value ...', 'unalias name ...'],
description:
'Define or list command aliases. unalias removes definitions.'
'Define or list command aliases; unalias removes definitions.'
},
{
name: 'cd',
@@ -982,17 +985,63 @@ function shellPage() {
description:
'Show or set shell file creation mask (stored in env UMASK).'
},
{
name: 'set',
synopsis: [
'set -o',
'set +o',
'set -e | +e | -u | +u | -f | +f',
'set -o errexit|nounset|pipefail',
'set +o errexit|nounset|pipefail'
],
description:
'Toggle errexit (BARE_OS_SHELL_ERREXIT), nounset (BARE_OS_SHELL_NOUNSET), pipefail (BARE_OS_SHELL_PIPEFAIL), noglob (BARE_OS_SHELL_NOGLOB). Use set -o / set +o alone to print current shell options (subset).'
},
{
name: 'command',
synopsis: ['command -v|-V NAME', 'command ARGV...'],
description:
'Resolve or run a command without using shell functions (none) or aliases for -v/-V.'
description: 'Resolve or run a command; -v/-V skip aliases.'
},
{
name: 'type',
synopsis: ['type NAME'],
description: 'Report whether NAME is a builtin or a path under PATH.'
},
{
name: 'jobs',
synopsis: ['jobs [-l] [-p]'],
description:
'List logical background jobs. -p prints pgid only; -l includes pgid/sid in the listing.'
},
{
name: 'fg / bg / wait',
synopsis: [
'fg [%job]',
'bg [%job]',
'wait [n | %n]',
'wait -n (with BARE_OS_SHELL_POSIX_MODE=1)'
],
description:
'Cooperative job control: fg awaits a job; bg resumes stopped jobs; wait waits for jobs by id or all.'
},
{
name: 'suspend-job',
synopsis: ['suspend-job [%job]'],
description:
'Mark a running background job stopped (logical); resume with fg or bg.'
},
{
name: 'disown',
synopsis: ['disown [%job]'],
description:
'Remove a job from the jobs table without cancelling its async work (still runs to completion).'
},
{
name: 'trap',
synopsis: ['trap -l', 'trap -p', 'trap CMD SIGNAL'],
description:
'List signals, print handlers, or register synthetic trap handlers (ctx.shellTrapHandlers).'
},
{
name: 'login / logout',
synopsis: ['login [--new] passphrase...', 'logout [--save]'],
@@ -1008,20 +1057,30 @@ function shellPage() {
name: 'exit',
synopsis: ['exit [n]'],
description: 'Request booter exit with status n (builtin path).'
},
{
name: 'read',
synopsis: ['read [-r] [NAME ...]'],
description:
'Optional when BARE_OS_SHELL_READ_BUILTIN=1; bounded line from shell stdin or readLine.'
}
],
seeAlso: [
{ name: 'sh', section: 1 },
{ name: 'help', section: 1 },
{ name: 'man', section: 1 }
],
bareOsNotes: 'Pipelines do not use OS pipes; see handbook ch.4 and ch.9.',
bareOsNotes:
'Pipelines use simulated capture (not OS pipes). kill and wait accept %n job specs when shellBackgroundJobs is populated. Full UX (history file, tab menu, Ctrl+R) uses BARE_OS_FISH≠0 on a TTY; see shell-completion-and-repl-editor.md.',
examples: [
{ caption: 'pipeline (simulated)', code: 'ls -1 /bin | grep man' },
{
caption: 'errexit + compound',
code: 'set -e\nif true; then echo ok; fi'
},
{ caption: 'background job', code: 'sleep 1 &\njobs' },
{ caption: 'redirect out', code: 'echo hi > ~/hello.txt' },
{ caption: 'append', code: 'date >> ~/log.txt' },
{ caption: 'alias + use', code: "alias ll='ls -la'\nll ~" },
{ caption: 'export for children', code: 'export EDITOR=ed\nman ls' },
{ caption: 'temp var for one command', code: 'PATH=/bin man which' }
{ caption: 'alias + use', code: "alias ll='ls -la'\nll ~" }
]
}
}