{"id":31897979,"url":"https://github.com/cognominal/lush","last_synced_at":"2026-04-15T22:32:46.347Z","repository":{"id":316801595,"uuid":"1064774757","full_name":"cognominal/lush","owner":"cognominal","description":"WIP. A nodejs based interactive shell. Now focusing on tokenized multiline editing","archived":false,"fork":false,"pushed_at":"2025-10-26T19:11:00.000Z","size":486,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-26T21:08:31.898Z","etag":null,"topics":["acorn","ast","interactive","nodejs","nushell","shell"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cognominal.png","metadata":{"files":{"readme":"README.md","changelog":"history.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-09-26T14:29:55.000Z","updated_at":"2025-10-26T19:11:04.000Z","dependencies_parsed_at":"2025-10-17T17:13:52.335Z","dependency_job_id":"f9b85f80-8170-4d44-b7d8-1802a9963311","html_url":"https://github.com/cognominal/lush","commit_stats":null,"previous_names":["cognominal/lush"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cognominal/lush","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cognominal%2Flush","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cognominal%2Flush/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cognominal%2Flush/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cognominal%2Flush/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cognominal","download_url":"https://codeload.github.com/cognominal/lush/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cognominal%2Flush/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31863492,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acorn","ast","interactive","nodejs","nushell","shell"],"created_at":"2025-10-13T11:49:54.621Z","updated_at":"2026-04-15T22:32:46.340Z","avatar_url":"https://github.com/cognominal.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lush, a shell in node\n\n## Run me\n\nNo release yet.\n\n```bash\nbun run start\n```\n\n\n## Screen layout\n\nSee [completion-expansion.md](./completion-expansion.md) for the full completion UX.\n\nThe prompt stays where you launch lush.\nCompletion arrays render directly under the prompt. The status bar remains\npinned to the bottom of the terminal.\n\n\n### Via Docker\n\n```bash\ndocker build -t lush .\ndocker run -it --rm lush\n```\n\n## Lush is special\n\nA shell that runs on node. Highlighting is used as primary representation. That\ncomplexifies slightly the input by the user but that makes code syntax simpler\nand more readable. Most obvious consequences : rejuvenate the concept of naked\nstring. Hilite as syntax avoids the syntactic complexity of variables\n(may be expression) interpolation\nin string (no sigil or\n[mustache](https://en.wikipedia.org/wiki/Mustache_(template_system)) needed).\nThe input is encoded as a sequence of tokens. In fact more like an ast tree.\n\nFor ts and svelte, see [augmentations](./Acorn-augmentations.md)\n\n# Work in progress\n\nSee [now](#now). See [AGENTS.md](./AGENTS.md) for general\ndevelopment instructions. They are for API but are relevant for humans too.\nAt early this stage I don't even care to have commits that break everything.\nAlso lush is just a way to bootstrap a more ambitious project.\nSee [plan](./plan.md)\n\n# Implementing\n\nAt this point, this  README is about the implementation spec. As the guide for\ncodex to generate code.\n\nTBD html, to demonstrate the shell in action. I have a builtin for that.\n\nSee [naked strings](#naked-strings-cool-again-no-poisoned-apple) (TBD). We focus\nnow on features more than configurability.\n\nToken shapes live in [src/tokenLine.ts](src/tokenLine.ts). Eventually the\nreference readable representation of code is an augmented AST (astre) with\nnodes using unique id, this will revolutionize diff handling. The augmented AST\nis now\n[acorn](https://github.com/acornjs/acorn). The astre will be unparsed in\n[unparse.ts](./src/unparse.ts)`. More info [here](./unparsing.md). But that's\nfor [later](#more-long-term). See [.secureHash.ts](src/secureHash.ts) for uuids.\n\n## Data driven\n\nLushed (currently a multi line editor for the lish shell)\nis data driven by `lang.yml`\n\n## Token editing\n\nThe multiline editor mutates existing `InputToken`s instead of rebuilding a\nline from plain text. Every insert, delete, or split updates the token (and any\n`subTokens`) that owns the edited character, then re-computes the `tokenIdx`\nand `x` fields for the affected containers. Parent tokens with `subTokens`\nkeep their `text` undefined; their surface text is derived from the\nconcatenation of child token texts. This behaviour lives in\n[src/tokenEdit.ts](src/tokenEdit.ts) and lets highlighting and token metadata\nsurvive incremental edits without re-tokenizing the entire line.\n\nSubmission mirrors shell enter semantics. `Enter` inserts newlines by default,\nbut when the cursor sits on the final empty line of a command that already has\ncontent on earlier lines it submits the buffer. Pressing `Enter` with a fully\nempty buffer keeps editing in insert mode and rings the bell instead of\nrunning.\n\n## Mode and Token Types\n\nThe editor is modal depending on what we are currently editing.\nIn a given mode, just a certain set of token types is allowed.\nWhen the mode is changed with `setMode` when entering certain places\nwe update the map `tokenMap`\n\n## Planned/Done\n\nThis is the detailed plan. For the general plane, see [plan](./plan.md)\n\nCurrently, we do lush as a command line editor. We want to know how far we can\ngo without structural editing. For structural editing, we should thing of an API\nsimilar in Lua (for nvim) and ts (for the terminal). Some builtins have for sole\npurpose to help development. `ts`, `lush` and `lush` will be passed path to\nfiles in [sample-js](./sample-js) that are simplistic files. The `ts` builtin\naccepts `.js`, `.ts`, and `.svelte` sources and prints the parsed AST (Acorn for\nJS/TS, Svelte compiler for Svelte). We focus on features that help top bootstrap\nthe rest. But, hey, short term usefulness helps too.\n\nSome items are moved to avoid cluttering the Readme.\n\n[builtins](./builtins.md)\n\n- Programming. Not necessarily linked to a user facing feature but needed to run/grow the\n  system\n  - [x] `Token` should be a registry not an enum.\n  - []  edition of insecable token is forbidden. Such token is erased when\n        backspacing from the\n        token after it.\n  - [x]  Status field below the multi line editor would help\n  - [ ] specify display and edition of multilevel tokens. Maybe driven\n        by new fields. See[modes and snippets](/mode-and-snippets.md)\n  - [x]  the `tokenstypes` field in `lang.yml` should be a map keyed by type names\n\n- Multi line editor.\n  - [x] correct handling of tokens when launching builtins and commands, meaning\n        space token separates arguments\n  - [ ] On submit on empty command, possibly multi line, emit a bell, don't add\n        to history\n  - [ ] Space handling\n    Single space inside NakedString or Space token must insert a literal space.\n    Single space elsewhere (only on a secable token)\n    must split the token and insert a distinct Space token.\n    Rapid double-space  on a non Space token move the cursor to the adjacent space\n    token or create one to do so,\n    Double space on a space token just rotate the previous token type if any.\n      [ ]\n  - [ ] Backslash for metachars specially highlighted as one char\n  - [ ] Same for globbing\n  - [ ] Command/builtin completion\n  - [x] the mline status:  below the mline editor, shows the current mode, the\n       current token idx, a list of valid types with the current type highlighted.\n  - [ ] bring it to the next level. See [modes and snippets](/mode-and-snippets.md)\n- lush : Features specific to lush\n- [ ] Hooking to acorn to do more than launching commands and executing\n      builtins.\n- [ ] Typed pipes a la nushell\n- [ ] A builtin `ts` that output the unparsing of ts/js/svelte file\n- syntax and semantic (depends on Acorn hooking)\n- [ ] Expressions with less spacing to identify subexpressions `a + b *c` means\n      `(a+b)*c`\n- [ ] Variables, sigil or sigiless\n- [ ] Optional autovivifying (explicit in programs, default in command line).\n\n- classic shell features\n  - [ ] Implicit cd\n  - [x] (shortened) path in prompt\n  - [ ] Aliases ??\n  - [ ] Globbing\n  - [ ] Simple redirections\n  - [ ] Job control (code not tested)\n  - History\n    - [x] ^P, ^N move in history\n    - [ ] But should display only entries with same initial tokens\n    - [x] saving history, per cwd\n    - [x] History saved as yaml (astre later)\n    - [ ] History saved as yaml (also as AST)\n- Astre (Ast REference Representation) is what it says, and what we interact\n  with is an Astre unparsing\n  - [ ] Build on Acorn a la svelte\n  - [ ] Node UUID\n  - [ ] A map that binds symbol UUID to external names (general, localized,\n        personal)\n  - [ ] Grit, a diff system based on Astre, not on lines\n- Leste, a better svelte representation, adapted from code of\n  [svelte.dev](https://github.com/sveltejs/svelte.dev)\n  - [ ] Using xtermjs in svelte to make shell as notebooks\n- Various\n  - [x] Docker image\n  - [ ] Stackblitz. Run a shell server side ?\n- [ ] Nvim. We now run in a terminal. We want to program lush in nvim\n- [ ] Doc. I have tons of thoughts in various .mds. This tick list makes little\n      sense without it. Make some of them readable for an newcomer. But a demo\n      is even better\n\n\n## Naked strings cool again, no poisoned Apple\n\nApple file names contains spaces, they are a nightmare to deal with shells.\n\nVanilla shells have metachars that cannot be used for file names. Spaces are\nused to separate command arguments so they must be escaped when used in naked\nstring.\n\nIn lush, if one want to use metachars, say for globbing, they must be escaped\nand will be displayed in bold.\n\n```\necho  a\\ b\\ c\necho  'a b c'\n```\n\n## space keybinding\n\nDouble spacing (two space in rapid succession) moves the cursor after a symbol,\nthen rotate between its token potential types. So the interface guesses the\ntoken type wrong you can rectify it. Example : keyword is preferred over naked\nstring.\n\n## Interpolation in naked string\n\nCode interpolation will be underlined (slightly better then mustache). No\nrecursive code interpolation.\n\nVariable interpolation will use highlighting for variables so no underlining\nexcept within non atomic code interpolation.\n\nEscape ends the code interpolation.\n\n## mixed editing, structural and normal\n\nStatements will be edited using structural editing. Expressions within such\nStatements will be edited using normal editing. We don't support structural\nediting yet. We want to have fun as soon as possible. So we will have a `ts`\nbuiltin that will unparse code and add it to the history. So we can edit it. We\nneed to think what tokens and subtokens are, and their names too. Anyway tokens\nother than expression, will be readonly. So the `forwardToken` and\n`backwardToken` will skip readonly token.\n\n## variable names\n\nThey are always sigiled. But the underlying name is not sigiled.\n\n## Consequences\n\nThe line editor must be rewritten in term of lines of sequence of token\n\n## Now\n\nEmbryo of a shell in node with a multi line edit and history. No pipe, no\nredirection. When the first word is a command in $PATH (shown in red), execute\nthe line command. \n\nSee [prompt](./prompt.md) for forthcoming IA based updates. See\n[keybindings](./keybindings.md)\n\nThe shell is special because it uses hightlighting as primary representation.\nFor now, the command and its argument are naked strings highlighted with a light\ngray background. You don't need to escape metachars, obviously you need to use\nescape convention for non printable chars.\n\nTo separate arguments, type 2 space in rapid sequence.\n\n## Next\n\nTBD retrofit into tick list\n\nNot necessarily in the given order.\n\n- implicit `cd`. A naked string as unique token that can be interpreted as\n  folder path. Other tokens are ignored and not registered in history. See also\n  [cd](./builtins.md#cd) and [z](./builtins#z).\n- builtins, user functions (aliases can be made user functions?)\n- An history for each cwd stored using freedesktop file hierarchy conventions.\n  Moving up and down selecting commands which start with the same current\n  string.\n- highlighting as primary notation. Start with naked string with metachars as\n  regular code. Naked string highlighted as a special background.\n- borrow from [slash](https://github.com/cronvel/slash) for regular pipes and\n  redirections\n- typed pipes\n\n## Thinking mid term\n\nTBD retrofit into tick list\n\nEventually I need to do the shell program edition in nvim. Later in\ncodemirror/monaco. But I want to defer that. Can I do menu driven structural\nediting using terminal-kit. Should I add my multi line input field to it? Can I\nuse/create an adapter and use nvim, with what plugin, as a backend. Add Raku\nsyntax in the mix.\n\n- to enable it. - to disable it. `@a+\u003ctoto\u003e` Raku syntax. Will be simplified\n  with highlighting as primary notation. `@*PATH`\n\n### LLM\n\nLush has not been thought with LLMs in mind. Let's try.\nThe source of the most popular modules (in whatever lush supported language)\nlush aware or not can be pulled and a huffmanization of the most used symbols\ncan be done so as to be used as unique tokens.\n\nMaybe a serialization of astres can be done using the huffmnized symbols\nMaybe a lush aware model can be build using that scheme.\n\n### History file location\n\nCommand history persists between sessions. When `$LUSH_HISTORY` is set, Lush\nuses that absolute or tilde-expanded path. Otherwise it follows the [freedesktop](https://specifications.freedesktop.org/basedir-spec/latest/)\nBase Directory spec: if `$XDG_STATE_HOME` is defined the history is written to\n`$XDG_STATE_HOME/lush/history.yaml`; when it is unset, the fallback path is\n`~/.local/state/lush/history.yaml`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcognominal%2Flush","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcognominal%2Flush","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcognominal%2Flush/lists"}