Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,13 @@ jobs:
# processes — get genuine coverage on a runner where those facilities
# exist. The sandbox has no controlling terminal, so there is nothing for
# those tests to attach to.
name: integration tests (${{ matrix.target }})
runs-on: ${{ matrix.os }}
# One runner, no matrix: aarch64-darwin (macos-14) used to carry this leg
# too, but its runs were unreliable enough under CI load to be worse than
# no coverage. x86_64-linux is what CI gates; aarch64-darwin remains the
# local development target (see flake.nix), just without a CI leg here.
name: integration tests (x86_64-linux)
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-linux
- os: macos-14
target: aarch64-darwin
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,8 @@ guide and the [package standard](https://github.com/nerima-lisp/.github/blob/mai
## Support

See [SUPPORT](https://github.com/nerima-lisp/.github/blob/main/SUPPORT.md).
Report vulnerabilities privately via the org
[security policy](https://github.com/nerima-lisp/.github/blob/main/SECURITY.md)
rather than a public issue.
Report vulnerabilities privately per this repository's
[security policy](SECURITY.md) rather than a public issue.

## License

Expand Down
4 changes: 2 additions & 2 deletions docs/notes/macro-consolidation-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ per-site with a full test run, not applied as a blind find/replace.
position, and per-slot copier function all vary enough that capturing it
would need a small spec DSL for a ~4-5 line-per-site saving across 7
sites — the "three similar-but-different lines" trap this project's own
`docs/cps-audit.md` warns against. Left as raw `defstruct`s, consistent
`docs/notes/cps-audit.md` warns against. Left as raw `defstruct`s, consistent
with `docs/notes/value-struct-audit.md`'s stated reason (`define-value-struct`
assumes no inheritance).

Expand All @@ -83,4 +83,4 @@ prior refactoring rounds; this audit's one actionable finding (`ignore-errors`
consistency) is applied. What remains unconverted is either better solved
without a macro or is a narrow, higher-risk case for a modest win, both
consistent with this project's readability-bounded reading of "as much as
possible" (`docs/cps-audit.md`).
possible" (`docs/notes/cps-audit.md`).
2 changes: 1 addition & 1 deletion docs/notes/paredit-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ docstring).

| command | what it drove |
|---|---|
| `inspect unused-definitions src/**/*.lisp t/**/*.lisp` | the dead-code audit (`docs/dead-code-audit.md`): 179 candidates → 18 bulk-removable, each then verified a false positive. Scanning src+t together is what collapsed the 239 src-only candidates. |
| `inspect unused-definitions src/**/*.lisp t/**/*.lisp` | the dead-code audit (`docs/notes/dead-code-audit.md`): 179 candidates → 18 bulk-removable, each then verified a false positive. Scanning src+t together is what collapsed the 239 src-only candidates. |
| `inspect duplicates src/**/*.lisp` | found the four byte-identical prefix helpers later consolidated into `nshell.util:string-prefix-p`, and confirmed the top clone shapes were idiomatic `let` bindings that must *not* be macro-ified. |
| `inspect duplicates t/unit/*.lisp` | located the same-shape `it`-block clusters that became `it-each` tables (tokenizer word/redirect/command-sub/process-sub, parser diagnostics). |

Expand Down
2 changes: 1 addition & 1 deletion docs/notes/readability-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ A median 5-line, depth-4 function body is the core readability result: the code
is built from many small, single-purpose, shallow functions rather than a few
large ones. The DDD layering (domain / application / infrastructure /
presentation, one concern per file) reinforces this at the
file scale — see `docs/dead-code-audit.md` and the file-split history.
file scale — see `docs/notes/dead-code-audit.md` and the file-split history.

## Deep-nesting outliers, and how they were driven down

Expand Down
2 changes: 1 addition & 1 deletion docs/notes/timeout-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Verified empirically, not just by inspection: `interactive-stream-p` returns
`T` for SBCL's real `*standard-output*` under `script(1)` (a genuine PTY) and
`NIL` under a plain pipe — confirmed by direct `sbcl --eval` runs before
writing the fix. A new end-to-end test,
`e2e-main-interactive-pty-foreground-command-ignores-external-command-timeout`
`e2e-main-interactive-pty-foreground-pipeline-ignores-external-command-timeout`
(`t/e2e/test-smoke.lisp`), spawns real `nshell` under a real PTY with
`*external-command-timeout*` overridden to `0.5`, runs `sleep 2; echo
pty-outlived-timeout`, and asserts the echo occurs and no timeout message
Expand Down
11 changes: 6 additions & 5 deletions docs/notes/value-struct-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ for **every** raw `defstruct` that remains, with the concrete reason it is not a
| `%history-token-window` | `domain/history/last-argument.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%input-edit-snapshot` | `presentation/input-state-undo.lisp` | mutable — writable slot(s): buffer, cursor-pos |
| `%input-session-clear` | `presentation/input-state-helpers.lisp` | mutable — writable slot(s): kind, overrides |
| `%kb-command-entry` | `domain/completion/knowledge-base.lisp` | mutable — writable slot(s): description, subcommands, flags, option-values, exclusive-options |
| `%mixed-sequence-build-state` | `domain/parsing/parser-assembly.lisp` | mutable — writable slot(s): sequence-commands, sequence-separators, pipe-group |
| `%parse-result-facts` | `domain/parsing/parse-result.lisp` | mutable — writable slot(s): ast |
| `%parsed-command-line-case-clause` | `domain/parsing/parse-result.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%path-command-directory-cache-entry` | `domain/completion/filesystem-path-command.lisp` | mutable — writable slot(s): stamp |
| `%path-command-query` | `domain/completion/filesystem-path-command.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%redirect-entry` | `domain/parsing/parser-data.lisp` | mutable — writable slot(s): kind, target |
| `%redirect-facts` | `domain/parsing/parser-data.lisp` | mutable — writable slot(s): text, kind, fd-dup-p |
Expand Down Expand Up @@ -98,9 +98,8 @@ for **every** raw `defstruct` that remains, with the concrete reason it is not a
| `%tokenizer-left-angle-route` | `domain/parsing/tokenizer-handlers.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%tokenizer-left-paren-route` | `domain/parsing/tokenizer-handlers.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%tokenizer-pipe-route` | `domain/parsing/tokenizer-handlers.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%tokenizer-right-redirect-route` | `domain/parsing/tokenizer-handlers.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%tokenizer-right-angle-route` | `domain/parsing/tokenizer-handlers.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%tokenizer-special-dispatch-route` | `domain/parsing/tokenizer-handlers.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%tokenizer-word-scan-action` | `domain/parsing/tokenizer-readers.lisp` | capsule — read-only, but its slots have no public readers (behavior-only API); the macro would leak them |
| `%transient-session-clear` | `presentation/input-state-session.lisp` | mutable — writable slot(s): kind, overrides |
| `%type-options` | `application/builtin-type-helpers.lisp` | mutable — writable slot(s): all-p, short-p, no-functions-p, color-p, query-p, path-p, force-path-p, type-p, help-p |
| `%undo-recording-step` | `presentation/input-state-undo.lisp` | mutable — writable slot(s): undo-stack, redo-stack |
Expand All @@ -126,14 +125,16 @@ for **every** raw `defstruct` that remains, with the concrete reason it is not a
| `here-doc-target-replacer` | `domain/parsing/parser-here-doc.lisp` | mutable — writable slot(s): bodies, target-pending-p |
| `if-node` | `domain/parsing/ast.lisp` | include-hierarchy — `:include ast-node`; the macro cannot generate an inheriting struct |
| `incomplete-node` | `domain/parsing/ast.lisp` | include-hierarchy — `:include ast-node`; the macro cannot generate an inheriting struct |
| `input-state` | `presentation/input-state-core.lisp` | mutable — writable slot(s): cursor-pos, completion-index, completion-base-buffer, completion-base-cursor, last-candidates, suggestion, mode, vi-count, vi-visual-anchor, abbreviation-expander, kill-ring, last-yank-start, last-yank-end, last-yank-index, last-argument-start, last-argument-end, last-argument-index, search-query, search-original-buffer, search-original-cursor, search-index, undo-stack, redo-stack, buffer |
| `input-state` | `presentation/input-state-data.lisp` | mutable — writable slot(s): cursor-pos, completion-index, completion-base-buffer, completion-base-cursor, last-candidates, suggestion, mode, vi-count, vi-visual-anchor, abbreviation-expander, kill-ring, last-yank-start, last-yank-end, last-yank-index, last-argument-start, last-argument-end, last-argument-index, search-query, search-original-buffer, search-original-cursor, search-index, undo-stack, redo-stack, buffer, mouse-selection-anchor, mouse-selection-end |
| `job` | `domain/execution/job.lisp` | mutable — writable slot(s): state-kw, pgid-int, exit-code-int, pids-list, command-line-str, background-visible-p |
| `job-monitor` | `domain/job-control/monitor.lisp` | mutable — writable slot(s): jobs-table, next-id-int |
| `job-wait-event` | `application/manage-job.lisp` | mutable — writable slot(s): pid, state, status-code |
| `knowledge-base` | `domain/completion/knowledge-base.lisp` | mutable — writable slot(s): commands |
| `operator-node` | `domain/parsing/ast.lisp` | include-hierarchy — `:include ast-node`; the macro cannot generate an inheriting struct |
| `pipeline-node` | `domain/parsing/ast.lisp` | include-hierarchy — `:include ast-node`; the macro cannot generate an inheriting struct |
| `process-substitution-resource` | `infrastructure/acl/syscall-process-substitution.lisp` | mutable — writable slot(s): path, fd, processes |
| `pty-process` | `infrastructure/acl/pty.lisp` | mutable — writable slot(s): pid, pgid, master-fd, stream |
| `redirect-fd-dup-target` | `domain/parsing/parser-data.lisp` | mutable — writable slot(s): source, target, operator |
| `redirect-output-destinations` | `domain/parsing/parser-data.lisp` | mutable — writable slot(s): stdout-target, stdout-mode, stderr-target, stderr-mode |
| `rule-knowledge-base` | `domain/completion/rule-data.lisp` | mutable — writable slot(s): facts, rules |
| `sequence-node` | `domain/parsing/ast.lisp` | include-hierarchy — `:include ast-node`; the macro cannot generate an inheriting struct |
Expand All @@ -145,7 +146,7 @@ for **every** raw `defstruct` that remains, with the concrete reason it is not a
| `while-node` | `domain/parsing/ast.lisp` | include-hierarchy — `:include ast-node`; the macro cannot generate an inheriting struct |
| `whitespace-field-scanner` | `domain/expansion/fields.lisp` | mutable — writable slot(s): boundaries, start |

**Total: 137 raw defstructs.** mutable: 93; capsule: 28; include-hierarchy: 13; accessor/type mismatch: 1; encapsulation: 1; helper-collision: 1
**Total: 138 raw defstructs.** mutable: 95; capsule: 27; include-hierarchy: 13; accessor/type mismatch: 1; encapsulation: 1; helper-collision: 1

`command-history` and `history-entry` (both formerly `domain/history/`) are gone from this
count: generic history storage is now `history-kit:history`/`history-kit:history-entry`,
Expand Down
15 changes: 12 additions & 3 deletions docs/src/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ and reads batch input from stdin otherwise.
With -c/--command, nshell executes COMMAND once in batch mode; trailing ARGS
are available as $argv.
With SCRIPT, nshell runs the script file; trailing ARGS are available as $argv.

Options:
-i, --interactive Force the interactive line editor.
--no-config Do not load the interactive startup file.
--config PATH Load PATH instead of ~/.nshellrc.
--no-history Do not read or write interactive history.
-h, --help Show usage and exit.
-V, --version Show version and exit.
```

## Build from source
Expand All @@ -84,9 +92,10 @@ nix develop # dev shell with SBCL + cl-weave
```

`flake.nix` declares `x86_64-linux` and `aarch64-darwin`. The full hermetic
flake and release-binary gate runs on `x86_64-linux`; `aarch64-darwin` is the
development and non-sandboxed integration target, and some build checks can be
unavailable there when a pinned upstream package has no Darwin build.
flake gate, the release-binary gate, and the non-sandboxed integration suite
all run in CI on `x86_64-linux` only; `aarch64-darwin` is a local development
target (`nix build`, `nix develop`), and some build checks can be unavailable
there when a pinned upstream package has no Darwin build.

Inside `nix develop`, load the system into a REPL:

Expand Down
4 changes: 2 additions & 2 deletions docs/src/project/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ behaviour you want nshell to own.

## Reporting a vulnerability

Report privately through the org
[security policy](https://github.com/nerima-lisp/.github/blob/main/SECURITY.md),
Report privately through this repository's
[security policy](https://github.com/nerima-lisp/nshell/blob/main/SECURITY.md),
never as a public issue. Redact environment values and command history unless
they are required to reproduce.

Expand Down
9 changes: 4 additions & 5 deletions docs/src/project/public-readiness.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ and scripting subset.
Before a public release can claim world-level interactive-shell quality:

1. `nix flake check --print-build-logs` passes on `x86_64-linux` CI, the
hermetic check target. `aarch64-darwin` remains declared for development and
the non-sandboxed integration matrix, but is not a flake-check or
release-binary CI target.
hermetic check target. `aarch64-darwin` remains declared for development,
but is not a flake-check, integration, or release-binary CI target.
2. The non-sandboxed integration suite passes for PTY, subprocess, terminal,
signal, and job-control coverage on every target in the CI matrix:
`x86_64-linux` and `aarch64-darwin`.
signal, and job-control coverage on the sole CI matrix target,
`x86_64-linux`.
3. A release binary is built on `x86_64-linux`, starts successfully, and ships
with `README.md`, `LICENSE`, and the `nshell(1)` man page.
4. User-visible behavior changes are represented in README, man page, the
Expand Down
9 changes: 5 additions & 4 deletions docs/src/reference/builtins.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
Run `help` inside nshell for per-command detail, and `type NAME` to check
whether a name resolves to a builtin, a function, or an external program.

`alias`, `abbr`, `bg`, `cd`, `complete`, `contains`, `count`, `disown`, `echo`,
`exec`, `exit`, `export`, `false`, `fg`, `function`, `help`, `history`, `jobs`,
`ls`, `not`, `pipeline-graph`, `printf`, `pwd`, `read`, `seq`, `set`, `source`,
`string`, `test`, `true`, `type`, `unset`, `which`.
`alias`, `abbr`, `bg`, `break`, `cd`, `command`, `complete`, `contains`,
`continue`, `count`, `disown`, `echo`, `eval`, `exec`, `exit`, `export`,
`false`, `fg`, `function`, `help`, `history`, `jobs`, `kill`, `ls`, `not`,
`pipeline-graph`, `printf`, `pwd`, `read`, `seq`, `set`, `source`, `string`,
`test`, `true`, `type`, `unset`, `wait`, `which`.

## Notes on a few of them

Expand Down
30 changes: 30 additions & 0 deletions man/nshell.1
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ nshell \- a modern, fish-inspired interactive shell written in Common Lisp
.B nshell
.RB [ \-\-help ]
.RB [ \-\-version ]
.RB [ \-i | \-\-interactive ]
.RB [ \-\-no\-config ]
.RB [ \-\-config
.IR PATH ]
.RB [ \-\-no\-history ]
.RB [ \-c
.IR COMMAND
.RI [ ARGS .\|.\|.]]
Expand Down Expand Up @@ -48,6 +53,22 @@ once in batch mode and exit with its status. Trailing
.I ARGS
are exposed as
.BR $argv .
.TP
.BR \-i ", " \-\-interactive
Force the interactive line editor, even when standard input is not a
terminal.
.TP
.B \-\-no\-config
Do not load the interactive startup file.
.TP
.BI \-\-config " PATH"
Load
.I PATH
instead of
.BR ~/.nshellrc .
.TP
.B \-\-no\-history
Do not read or write interactive history.
.SH KEY BINDINGS
By default the line editor uses Emacs-style key bindings, including cursor
motion (\fBC\-a\fR, \fBC\-e\fR, \fBM\-f\fR, \fBM\-b\fR), editing (\fBC\-k\fR,
Expand Down Expand Up @@ -103,12 +124,16 @@ provides the following builtins:
.BR alias ,
.BR abbr ,
.BR bg ,
.BR break ,
.BR cd ,
.BR command ,
.BR complete ,
.BR contains ,
.BR continue ,
.BR count ,
.BR disown ,
.BR echo ,
.BR eval ,
.BR exec ,
.BR exit ,
.BR export ,
Expand All @@ -118,8 +143,11 @@ provides the following builtins:
.BR help ,
.BR history ,
.BR jobs ,
.BR kill ,
.BR ls ,
.BR not ,
.BR pipeline\-graph ,
.BR printf ,
.BR pwd ,
.BR read ,
.BR seq ,
Expand All @@ -129,6 +157,8 @@ provides the following builtins:
.BR test ,
.BR true ,
.BR type ,
.BR unset ,
.BR wait ,
.BR which .
Run
.B help
Expand Down
Loading