turbo-editors/turbo-jspublic Fork 0
v1.0.0
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-js.git
git clone ssh://git@rickub.com/turbo-editors/turbo-js.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

📦 Turbo JS 91999d1 · on v1.0.0 · k33g · 11h ago
summary.md · 102 lines · 17.0 KBmarkdown
Blame HistoryOpen raw

turbo-js — summary

A snapshot of the present. Edited in place; the history is in history.md.

What this is

A Turbo C-style terminal IDE for JavaScript on Node.js, written in Go, built on turbo-core — the library Turbo Go, Turbo Rust, Turbo Python, Turbo MoonBit and Turbo Golo already share. This repository holds the command, the profile that says the editor is for JavaScript, a JavaScript scanner that replaces the one the library ships, and a JSON scanner. Everything else — the event loop, the windows, the dialogs, the themes, the LSP client, the terminal emulator, the project tree, the snippets, tools and agent machinery — is the library's, and none of it is copied here.

JavaScript is the language; Node.js is the runtime. The docs say "JavaScript" for the language and its files, "Node" for the runtime and its toolchain (node, npm, npx), and never "interpreter".

Module rickub.com/turbo-editors/turbo-js, requireing turbo-core v0.8.0 from the module proxy with no active replace. The commented-out replace at the bottom of go.mod documents the escape hatch without being one; 01-release.tag.sh refuses to tag a release whose go.mod carries a live one. Remote: ssh://git@rickub.com/turbo-editors/turbo-js.git.

Layout

main.go flags (-theme, -list-themes, -no-lsp, -version), the terminal, jslang.Register(), the profile, the loop
internal/jslang/jslang.go Name, Slug, Language (= syntax.LanguageJavaScript), LanguageJSON, Profile(), Register(), ServerArgs(), ServerDirs() and the five directory helpers
internal/jslang/scan.go the JavaScript dispatcher, the two carries (block comment, template literal), the per-line state that decides whether / divides or opens a regular expression, hashbang, private names, decorators
internal/jslang/literals.go template literals across lines, numbers (1_000, 0xFF, 0o17, 0b1010, 1.5e-3, .5, 10n)
internal/jslang/words.go keywords, constants, the globals (standard library + Node), the property-after-dot rule, the contextual keywords, the naming conventions
internal/jslang/json.go the JSON scanner: keys as attributes, values as strings, comments tolerated
internal/jslang/*.toml.tmpl the four starter files, embedded by templates.go
internal/jslang/*_test.go profile, scanner, JSON, templates, agents file, the editor assembled on a SimulationScreen, nine tests against a real typescript-language-server, and reference_test.go holding docs/*/reference/languages.md to the scanners
diagram_test.go holds docs/diagrams/packages.drawio to go list
docs/{en,fr}/ 37 pages each (README included), Diátaxis; docs/README.md selects the language
demos/greeter/ a class with a private field and a regex, an ES module importing it, a node --test file; npm start and npm test both run
scripts/, Makefile, 0104-release*.sh, release.env adapted from turbo-rust; the installer checks the server by running it

How to build, test and measure

make check          # fmt, vet, then the whole suite — what a commit should pass
make build          # into bin/turbo-js, then scripts/check-version.sh on the binary
make install        # build, install onto PATH, report what it found (--with-server installs the language server with npm)
go test ./...       # the real-server tests skip themselves without typescript-language-server on PATH, and under -short
go test -short ./... # also skips the installer tests, which compile the whole editor

Quality gate, separate from the tests:

python3 ~/.claude/skills/quality/scripts/quality_report.py --workspace .

To build against a turbo-core you have changed but not released: go work init . ../turbo-core, then go list -f '{{.Dir}}' rickub.com/turbo-editors/turbo-core/app must not answer a path under pkg/mod. go.work and go.work.sum are gitignored.

Decisions in force

  • The JavaScript scanner replaces the library's, under the library's own name. turbo-core colours JavaScript for every editor (README fences, web pages) and its scanner deliberately refuses regular expressions. An editor for JavaScript owes its user regexes, Node's globals, the hashbang, the name after function/class, #private names and @decorators, so Register() registers Language = syntax.LanguageJavaScript and the later registration wins — which syntax.Register documents as intended. Snippets saying languages = ["javascript"] and ```js fences reach this scanner with no library change. A test proves the override took (a .js file opened through app.New has its regex coloured), because forgetting Register() would otherwise leave an editor that still colours JavaScript, wrongly, with every test green.
  • A regular expression is coloured with the char class. The class set is closed and JavaScript has no character literal; a regex is the other kind of quoted literal. In turbo-classic syntax.char is the string green, so they share a colour there; other themes separate them. Documented in the reference.
  • The slash rule is bounded by the line. After a value (name, number, string, ), ]) / divides; after an operator, (, [, {, }, ,, ;, :, a keyword or at line start it opens a literal — but only if a closing / exists on the same line (a regex may not contain a newline), otherwise it divides. That bound is what makes the previous-token heuristic acceptable where the library declined it: a wrong guess costs one line, never the file.
  • A word after . or ?. is a property, never a keyword (map.get(k), options.default, promise.catch(), and a contextual keyword (get set static of from as) before ( is a function. async is excluded from that rule (async (x) => …).
  • A leading capital is a class, even before ( (new Greeter()); MAX_SIZE is coloured as one too — documented limitation, held by reference_test.go.
  • Only block comments and template literals cross a line. Strings stop at their line (the language forbids a newline in them); a backtick inside ${} ends the template early; ${…} is not coloured as code. All three documented as refusals.
  • JSON is registered here (json, .json, .jsonc): package.json is the manifest and the root marker, and turbo-core does not colour JSON. Keys → attribute, values → string, // and /* */ tolerated (tsconfig.json). If turbo-core learns JSON later, this registration still wins by ordering and can then be dropped.
  • The language server is typescript-language-server --stdio with TypeScript 6, not TypeScript 7's own. Both were measured against the same nine end-to-end tests. TypeScript 7 (typescript@7.0.2, the native port) ships no tsserver.js, so typescript-language-server 6.0.0 installed beside it fails at initialize ("Could not find a valid TypeScript installation"); the install hint therefore pins typescript@6 (6.0.3 tested). TypeScript 7's native server (tsc --lsp --stdio, "typescript-go 7.0.2") answers all eight requests, four times faster, and publishes no diagnostics — it offers pull diagnostics (textDocument/diagnostic), which turbo-core does not request — so the gutter stays blank on a broken file: exactly the failure this family designs against. Recorded in jslang.go's comment, the docs and turbo-core's memory; the day the library pulls, two profile fields switch it.
  • All nine of turbo-core's questions are answered for plain JavaScript, and each is driven against the real server by a test: completion of a buffer-only declaration, definition, references (declaration + two calls), implementations (the two subclasses of a class), type definition (new Circle() → the class), hover (the JSDoc above a declaration), document symbols, workspace symbols, and diagnostics for function main( {. Diagnostics on .js are syntax errors; type errors need // @ts-check or checkJs.
  • RootMarkers is ["package.json"] — the nearest one going up, so a monorepo's package is the server's root. Settings and the tree stay in the working directory (turbo-core's rule).
  • ServerDirs(): $NVM_BIN, then $NPM_CONFIG_PREFIX/bin or ~/.npm-global/bin, $PNPM_HOME, $VOLTA_HOME/bin or ~/.volta/bin, then /usr/local/bin and /opt/homebrew/bin. Empty variables contribute nothing; nvm's default alias is not resolved.
  • The toolchain menu is ~J~avaScript, Alt-J. J is free (fixed menus: F, E, S, R, C, O, W, N, A, H). Named after the language, not after npm or node, for the family's reason: the menu holds whatever the project's tools file says.
  • Seven starter tools: Install (npm install, first, because a fresh clone runs it first), Format (npx prettier --write .), Lint (npx eslint .), Test (node --test), Run (node {{script, e.g. main.js}}, terminal, the one placeholder — a Node project has no single entry point), Start (npm start, terminal), and Echo in a Tools menu. No Build: JavaScript has no compile step; node --check is left to the project. Every command was run against Node 24.19.0 / npm 11.17.0 / Prettier 3.9.7.
  • Snippets indent with two spaces (Prettier's default); a test holds every body to it. Seven JavaScript snippets, one JSON (scripts), plus General and Markdown. Bodies are TOML basic strings.
  • A node shebang is claimed (#!/usr/bin/env node, also env -S node …): a CLI tool is a .js-less file whose first line names node, and Node strips the line; the scanner colours it as a comment on line 1 only.
  • autosave = true in the starter settings file, false in settings.Default(). Two statements in two places on purpose.
  • The starter templates are embedded files, not Go constants, with the .tmpl suffix because settings.toml.tmpl holds theme = %q.
  • Agent windows are turbo-core's; what belongs here is the starter file, which names the javascript / js fence and the ```json one, and teaches the window's keyboard.
  • Not coloured, on purpose: TypeScript, JSX/TSX, CSS. The server serves .ts files anyway; the scanner is for JavaScript.

State as of 2026-09-16

  • Complete and green, uncommitted. go build, go vet, gofmt -l clean; the whole suite passes with typescript-language-server 6.0.0 + typescript 6.0.3 installed globally in the sandbox (/usr/local/share/npm-global/bin). The repository has no commits yet; everything is untracked on main.
  • Falsified by mutation: fourteen mutations over the scanner, the templates, the diagram and main.go; eleven went red at once, three stayed green and were fixed (0xE + 1 never reached the hexadecimal guard because the space ends the number — 0xE+1 does; the snippets language-list test searched the whole file where a languages key satisfied it — it now reads the header comment; nothing noticed main.go dropping Register()TestMainRegistersTheLanguageExplicitly does). All fourteen are red now.
  • Quality gate: PASS, run #3 (run #1 failed on one return-statements smell in classOfWord, split into three functions; run #2 passed at 108). 0 errors, 0 warnings, 0 smells; total complexity 109, worst file scan.go at 32 against a limit of 60. 154 top-level tests pass.
  • Documentation: 37 pages × EN + FR, README.md at the root, demos/README.md, docs/diagrams/packages.drawio checked against go list. Twenty-six pages per language were adapted from turbo-golo and read whole by six subagents against a fact sheet (/tmp/tj/FACTS.md); the eleven language-specific pages were written from scratch in English and translated by three more.
  • Verified in a real pty (/tmp/tj/tut.raw, replayed with /tmp/tj/screen.py): the bar File Edit Search Run Code Options Window Snippets Agent JavaScript Help; five times reaches Options and nine JavaScript; Alt-J shows Create/Open tools file, then the six starter commands with a Tools menu appearing between JavaScript and Help; Run asks script, e.g. main.js: and a terminal window titled node 'main.js' prints the three greetings; a function … greet(3; line gets × in the gutter and ⚠ ')' expected. on the status bar; F1 on greet opens a Symbol box beginning (method) Greeter.greet(times?: number); F12 jumps to line 12. Colours read back as SGR in turbo-classic: keyword 97;44;1, type 96;44, function 93;44;1, builtin 96;44;1, identifier 93;44, string and regex 92;44, number and constant 95;44, comment 38;2;143;143;143, operator and punctuation 37;44.
  • The editor repeats the previous line's indentation on Enter. Typing an indented listing verbatim doubles the indentation; the tutorial says to type only the change, and the pty driver does the same.
  • Registered in the family. turbo-core's README.md, both doc READMEs, both workspace how-tos, profile/profile.go's comment and .memory/summary.md count six editors; turbo-python's, turbo-moonbit's and turbo-golo's architecture pages (EN + FR) say "all six editors", turbo-golo's FR design-decisions too. turbo-core's .go strings were swept for hardcoded language names against v0.8.0: every hit innocent. No library change needed.

Known boundaries

  • lsp.Client.DidOpen sends languageId: "go" for every editor (a turbo-core quirk); tsserver decides by extension, so it does not matter here. Recorded in turbo-core's memory as a library cycle.
  • Diagnostics are pushed only. A pull-only server leaves the gutter blank — the reason TypeScript 7's server is not used.
  • Cross-file F12 is neither claimed nor denied in the docs: the end-to-end tests open a single main.js.
  • npx prettier / npx eslint download on first use when the project does not depend on them; ESLint 9 needs an eslint.config.js.

Not yet established

  • Agent windows have never been opened in this editor. The starter file is covered by tests that create it, load it back and check it names this editor's fences; nobody has run turbo-js, pressed Alt-A and talked to an agent from it.
  • Never run on macOS or Windows. Everything here was verified on Linux/arm64. /tmp/tj/turbo-js is an ELF built for the pty run and lives outside the tree on purpose; bin/ is empty.
  • No CI. No pipeline configuration in the repository.
  • Never released. No tag exists; release.env says TAG="v0.1.0". 0104 have only ever been read here, never run. turbo-js.token.env does not exist yet (gitignored by *.env).
  • No LICENSE file. The siblings are MIT (Copyright (c) 2026 turbo-editors); adding one is the owner's decision, and the root README says so.
  • The real-server tests have only run against typescript-language-server 6.0.0 + typescript 6.0.3. A newer server that starts with TypeScript 7 would make the @6 pin unnecessary; a test does not detect that.
  • Performance on a large file is unmeasured.
  • Nothing checks demos/greeter automatically. It was run by hand under Node 24.19.0.

State as of 2026-09-19 — moved to Rickub, released by a workflow

  • Module path rickub.com/turbo-editors/turbo-js, depending on rickub.com/turbo-editors/turbo-core v1.0.0 — the first turbo-core version published under that path (v0.9.0 on the proxy still declares the Codeberg path and cannot be required as rickub.com/…). Every import, the Makefile's VERSION_PKG, scripts/install.sh, the README and the docs say rickub.com. GOWORK=off make check green. The repository on this side is a fresh git init with origin at ssh://git@rickub.com/turbo-editors/turbo-js.git and no commit yet; 01-release.tag.sh makes the first one.
  • Releases are one script and one workflow, modelled on turbo-core's and identical to turbo-go's. 01-release.tag.sh runs make check under TURBO_JS_RELEASING=1, refuses a tag taken locally or on origin (bump, never move), refuses a replace in go.mod, commits, pushes the current branch, then tags and pushes the tag. That push starts .github/workflows/release.yml: go test with TURBO_JS_RELEASING=1, ./02-build-releases.sh "${GITHUB_REF_NAME}", release notes from the tag message, a run artifact, then softprops/action-gh-release@v2 attaching turbo-js-*, SHA256SUMS and README.md with the job's own GITHUB_TOKEN — the only credential Rickub's release API accepts. 02-release.publish.sh and 04-release.upload-binaries.sh are gone; the build script is now 02-build-releases.sh, takes the tag as $1 (CI has no release.env), validates it, refuses a replace, and starts from an empty release/${TAG}/. release.env holds only TAG and ABOUT; turbo-js.token.env is read by nothing.
  • release_test.go runs 01 for real against a throwaway bare remote (publishes, then refuses the same tag), runs 02 alone to see it refuse v0.o.0, and asserts the workflow's trigger, contents: write, ./02-build-releases.sh, fail_on_unmatched_files, docs linked at the tag, no secrets., and TURBO_JS_RELEASING. The copy of the module for the clone leaves out .git, bin, release, kits, demo, demos, *.env and go.work*; children run with GOWORK=off.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
# turbo-js — summary

A snapshot of the present. Edited in place; the history is in `history.md`.

## What this is

A Turbo C-style terminal IDE for **JavaScript on Node.js**, written in Go, built on **[turbo-core](https://rickub.com/turbo-editors/turbo-core)** — the library Turbo Go, Turbo Rust, Turbo Python, Turbo MoonBit and Turbo Golo already share. This repository holds the command, the profile that says the editor is for JavaScript, a JavaScript scanner that **replaces** the one the library ships, and a JSON scanner. Everything else — the event loop, the windows, the dialogs, the themes, the LSP client, the terminal emulator, the project tree, the snippets, tools and agent machinery — is the library's, and none of it is copied here.

**JavaScript is the language; Node.js is the runtime.** The docs say "JavaScript" for the language and its files, "Node" for the runtime and its toolchain (`node`, `npm`, `npx`), and never "interpreter".

Module `rickub.com/turbo-editors/turbo-js`, `require`ing turbo-core **v0.8.0** from the module proxy with **no active `replace`**. The commented-out `replace` at the bottom of `go.mod` documents the escape hatch without being one; `01-release.tag.sh` refuses to tag a release whose `go.mod` carries a live one. Remote: `ssh://git@rickub.com/turbo-editors/turbo-js.git`.

## Layout

| | |
| --- | --- |
| `main.go` | flags (`-theme`, `-list-themes`, `-no-lsp`, `-version`), the terminal, `jslang.Register()`, the profile, the loop |
| `internal/jslang/jslang.go` | `Name`, `Slug`, `Language` (= `syntax.LanguageJavaScript`), `LanguageJSON`, `Profile()`, `Register()`, `ServerArgs()`, `ServerDirs()` and the five directory helpers |
| `internal/jslang/scan.go` | the JavaScript dispatcher, the two carries (block comment, template literal), the per-line state that decides whether `/` divides or opens a regular expression, hashbang, private names, decorators |
| `internal/jslang/literals.go` | template literals across lines, numbers (`1_000`, `0xFF`, `0o17`, `0b1010`, `1.5e-3`, `.5`, `10n`) |
| `internal/jslang/words.go` | keywords, constants, the globals (standard library + Node), the property-after-dot rule, the contextual keywords, the naming conventions |
| `internal/jslang/json.go` | the JSON scanner: keys as attributes, values as strings, comments tolerated |
| `internal/jslang/*.toml.tmpl` | the four starter files, embedded by `templates.go` |
| `internal/jslang/*_test.go` | profile, scanner, JSON, templates, agents file, the editor assembled on a `SimulationScreen`, nine tests against a real `typescript-language-server`, and `reference_test.go` holding `docs/*/reference/languages.md` to the scanners |
| `diagram_test.go` | holds `docs/diagrams/packages.drawio` to `go list` |
| `docs/{en,fr}/` | 37 pages each (README included), Diátaxis; `docs/README.md` selects the language |
| `demos/greeter/` | a class with a private field and a regex, an ES module importing it, a `node --test` file; `npm start` and `npm test` both run |
| `scripts/`, `Makefile`, `01``04-release*.sh`, `release.env` | adapted from turbo-rust; the installer checks the server by **running** it |

## How to build, test and measure

```bash
make check          # fmt, vet, then the whole suite — what a commit should pass
make build          # into bin/turbo-js, then scripts/check-version.sh on the binary
make install        # build, install onto PATH, report what it found (--with-server installs the language server with npm)
go test ./...       # the real-server tests skip themselves without typescript-language-server on PATH, and under -short
go test -short ./... # also skips the installer tests, which compile the whole editor
```

Quality gate, separate from the tests:

```bash
python3 ~/.claude/skills/quality/scripts/quality_report.py --workspace .
```

To build against a turbo-core you have changed but not released: `go work init . ../turbo-core`, then `go list -f '{{.Dir}}' rickub.com/turbo-editors/turbo-core/app` must **not** answer a path under `pkg/mod`. `go.work` and `go.work.sum` are gitignored.

## Decisions in force

- **The JavaScript scanner replaces the library's, under the library's own name.** turbo-core colours JavaScript for every editor (README fences, web pages) and its scanner deliberately refuses regular expressions. An editor *for* JavaScript owes its user regexes, Node's globals, the hashbang, the name after `function`/`class`, `#private` names and `@decorators`, so `Register()` registers `Language = syntax.LanguageJavaScript` and the later registration wins — which `syntax.Register` documents as intended. Snippets saying `languages = ["javascript"]` and ```js fences reach this scanner with no library change. **A test proves the override took** (a `.js` file opened through `app.New` has its regex coloured), because forgetting `Register()` would otherwise leave an editor that still colours JavaScript, wrongly, with every test green.
- **A regular expression is coloured with the `char` class.** The class set is closed and JavaScript has no character literal; a regex is the other kind of quoted literal. In `turbo-classic` `syntax.char` is the string green, so they share a colour there; other themes separate them. Documented in the reference.
- **The slash rule is bounded by the line.** After a value (`name`, number, string, `)`, `]`) `/` divides; after an operator, `(`, `[`, `{`, `}`, `,`, `;`, `:`, a keyword or at line start it opens a literal — but only if a closing `/` exists on the same line (a regex may not contain a newline), otherwise it divides. That bound is what makes the previous-token heuristic acceptable where the library declined it: a wrong guess costs one line, never the file.
- **A word after `.` or `?.` is a property, never a keyword** (`map.get(k)`, `options.default`, `promise.catch(`), and a contextual keyword (`get set static of from as`) before `(` is a function. `async` is excluded from that rule (`async (x) => …`).
- **A leading capital is a class**, even before `(` (`new Greeter()`); `MAX_SIZE` is coloured as one too — documented limitation, held by `reference_test.go`.
- **Only block comments and template literals cross a line.** Strings stop at their line (the language forbids a newline in them); a backtick inside `${}` ends the template early; `${…}` is not coloured as code. All three documented as refusals.
- **JSON is registered here** (`json`, `.json`, `.jsonc`): `package.json` is the manifest and the root marker, and turbo-core does not colour JSON. Keys → attribute, values → string, `//` and `/* */` tolerated (tsconfig.json). If turbo-core learns JSON later, this registration still wins by ordering and can then be dropped.
- **The language server is `typescript-language-server --stdio` with TypeScript 6, not TypeScript 7's own.** Both were measured against the same nine end-to-end tests. TypeScript 7 (`typescript@7.0.2`, the native port) ships no `tsserver.js`, so `typescript-language-server` 6.0.0 installed beside it fails at `initialize` ("Could not find a valid TypeScript installation"); the install hint therefore pins `typescript@6` (6.0.3 tested). TypeScript 7's native server (`tsc --lsp --stdio`, "typescript-go 7.0.2") answers all eight requests, four times faster, and publishes **no** diagnostics — it offers pull diagnostics (`textDocument/diagnostic`), which turbo-core does not request — so the gutter stays blank on a broken file: exactly the failure this family designs against. Recorded in `jslang.go`'s comment, the docs and turbo-core's memory; the day the library pulls, two profile fields switch it.
- **All nine of turbo-core's questions are answered for plain JavaScript**, and each is driven against the real server by a test: completion of a buffer-only declaration, definition, references (declaration + two calls), implementations (the two subclasses of a class), type definition (`new Circle()` → the class), hover (the JSDoc above a declaration), document symbols, workspace symbols, and diagnostics for `function main( {`. Diagnostics on `.js` are **syntax errors**; type errors need `// @ts-check` or `checkJs`.
- **`RootMarkers` is `["package.json"]`** — the nearest one going up, so a monorepo's package is the server's root. Settings and the tree stay in the working directory (turbo-core's rule).
- **`ServerDirs()`**: `$NVM_BIN`, then `$NPM_CONFIG_PREFIX/bin` or `~/.npm-global/bin`, `$PNPM_HOME`, `$VOLTA_HOME/bin` or `~/.volta/bin`, then `/usr/local/bin` and `/opt/homebrew/bin`. Empty variables contribute nothing; nvm's default alias is not resolved.
- **The toolchain menu is `~J~avaScript`, `Alt-J`.** J is free (fixed menus: F, E, S, R, C, O, W, N, A, H). Named after the language, not after `npm` or `node`, for the family's reason: the menu holds whatever the project's tools file says.
- **Seven starter tools**: Install (`npm install`, first, because a fresh clone runs it first), Format (`npx prettier --write .`), Lint (`npx eslint .`), Test (`node --test`), Run (`node {{script, e.g. main.js}}`, terminal, the one placeholder — a Node project has no single entry point), Start (`npm start`, terminal), and Echo in a `Tools` menu. **No Build**: JavaScript has no compile step; `node --check` is left to the project. Every command was run against Node 24.19.0 / npm 11.17.0 / Prettier 3.9.7.
- **Snippets indent with two spaces** (Prettier's default); a test holds every body to it. Seven JavaScript snippets, one JSON (`scripts`), plus General and Markdown. Bodies are TOML basic strings.
- **A `node` shebang is claimed** (`#!/usr/bin/env node`, also `env -S node …`): a CLI tool is a `.js`-less file whose first line names node, and Node strips the line; the scanner colours it as a comment on line 1 only.
- **`autosave = true` in the starter settings file, `false` in `settings.Default()`.** Two statements in two places on purpose.
- **The starter templates are embedded files, not Go constants**, with the `.tmpl` suffix because `settings.toml.tmpl` holds `theme = %q`.
- **Agent windows are turbo-core's; what belongs here is the starter file**, which names the ```javascript / ```js fence and the ```json one, and teaches the window's keyboard.
- **Not coloured, on purpose**: TypeScript, JSX/TSX, CSS. The server serves `.ts` files anyway; the scanner is for JavaScript.

## State as of 2026-09-16

- **Complete and green, uncommitted.** `go build`, `go vet`, `gofmt -l` clean; the whole suite passes with `typescript-language-server` 6.0.0 + `typescript` 6.0.3 installed globally in the sandbox (`/usr/local/share/npm-global/bin`). The repository has **no commits yet**; everything is untracked on `main`.
- **Falsified by mutation**: fourteen mutations over the scanner, the templates, the diagram and `main.go`; eleven went red at once, three stayed green and were fixed (`0xE + 1` never reached the hexadecimal guard because the space ends the number — `0xE+1` does; the snippets language-list test searched the whole file where a `languages` key satisfied it — it now reads the header comment; nothing noticed `main.go` dropping `Register()``TestMainRegistersTheLanguageExplicitly` does). All fourteen are red now.
- **Quality gate: PASS**, run #3 (run #1 failed on one `return-statements` smell in `classOfWord`, split into three functions; run #2 passed at 108). 0 errors, 0 warnings, 0 smells; total complexity 109, worst file `scan.go` at 32 against a limit of 60. 154 top-level tests pass.
- **Documentation**: 37 pages × EN + FR, `README.md` at the root, `demos/README.md`, `docs/diagrams/packages.drawio` checked against `go list`. Twenty-six pages per language were adapted from turbo-golo and read whole by six subagents against a fact sheet (`/tmp/tj/FACTS.md`); the eleven language-specific pages were written from scratch in English and translated by three more.
- **Verified in a real pty** (`/tmp/tj/tut.raw`, replayed with `/tmp/tj/screen.py`): the bar ` File  Edit  Search  Run  Code  Options  Window  Snippets  Agent  JavaScript  Help`; `→` five times reaches Options and nine JavaScript; `Alt-J` shows Create/Open tools file, then the six starter commands with a `Tools` menu appearing between JavaScript and Help; `Run` asks `script, e.g. main.js:` and a terminal window titled `node 'main.js'` prints the three greetings; a `function … greet(3;` line gets `×` in the gutter and `⚠ ')' expected.` on the status bar; `F1` on `greet` opens a **Symbol** box beginning `(method) Greeter.greet(times?: number)`; `F12` jumps to line 12. Colours read back as SGR in `turbo-classic`: keyword `97;44;1`, type `96;44`, function `93;44;1`, builtin `96;44;1`, identifier `93;44`, string and regex `92;44`, number and constant `95;44`, comment `38;2;143;143;143`, operator and punctuation `37;44`.
- **The editor repeats the previous line's indentation on `Enter`.** Typing an indented listing verbatim doubles the indentation; the tutorial says to type only the change, and the pty driver does the same.
- **Registered in the family.** turbo-core's `README.md`, both doc READMEs, both workspace how-tos, `profile/profile.go`'s comment and `.memory/summary.md` count six editors; turbo-python's, turbo-moonbit's and turbo-golo's architecture pages (EN + FR) say "all six editors", turbo-golo's FR design-decisions too. turbo-core's `.go` strings were swept for hardcoded language names against v0.8.0: every hit innocent. **No library change needed.**

## Known boundaries

- **`lsp.Client.DidOpen` sends `languageId: "go"`** for every editor (a turbo-core quirk); tsserver decides by extension, so it does not matter here. Recorded in turbo-core's memory as a library cycle.
- **Diagnostics are pushed only.** A pull-only server leaves the gutter blank — the reason TypeScript 7's server is not used.
- **Cross-file `F12` is neither claimed nor denied** in the docs: the end-to-end tests open a single `main.js`.
- **`npx prettier` / `npx eslint` download on first use** when the project does not depend on them; ESLint 9 needs an `eslint.config.js`.

## Not yet established

- **Agent windows have never been opened in this editor.** The starter file is covered by tests that create it, load it back and check it names this editor's fences; nobody has run `turbo-js`, pressed `Alt-A` and talked to an agent from it.
- **Never run on macOS or Windows.** Everything here was verified on Linux/arm64. `/tmp/tj/turbo-js` is an ELF built for the pty run and lives outside the tree on purpose; `bin/` is empty.
- **No CI.** No pipeline configuration in the repository.
- **Never released.** No tag exists; `release.env` says `TAG="v0.1.0"`. `01``04` have only ever been read here, never run. `turbo-js.token.env` does not exist yet (gitignored by `*.env`).
- **No `LICENSE` file.** The siblings are MIT (`Copyright (c) 2026 turbo-editors`); adding one is the owner's decision, and the root README says so.
- **The real-server tests have only run against typescript-language-server 6.0.0 + typescript 6.0.3.** A newer server that starts with TypeScript 7 would make the `@6` pin unnecessary; a test does not detect that.
- **Performance on a large file is unmeasured.**
- **Nothing checks `demos/greeter` automatically.** It was run by hand under Node 24.19.0.

## State as of 2026-09-19 — moved to Rickub, released by a workflow

- **Module path `rickub.com/turbo-editors/turbo-js`**, depending on `rickub.com/turbo-editors/turbo-core v1.0.0` — the first turbo-core version published under that path (`v0.9.0` on the proxy still declares the Codeberg path and cannot be required as `rickub.com/…`). Every import, the Makefile's `VERSION_PKG`, `scripts/install.sh`, the README and the docs say `rickub.com`. `GOWORK=off make check` green. The repository on this side is a fresh `git init` with `origin` at `ssh://git@rickub.com/turbo-editors/turbo-js.git` and **no commit yet**; `01-release.tag.sh` makes the first one.
- **Releases are one script and one workflow**, modelled on turbo-core's and identical to turbo-go's. `01-release.tag.sh` runs `make check` under `TURBO_JS_RELEASING=1`, refuses a tag taken locally or on origin (bump, never move), refuses a `replace` in `go.mod`, commits, pushes the current branch, then tags and pushes the tag. That push starts `.github/workflows/release.yml`: `go test` with `TURBO_JS_RELEASING=1`, `./02-build-releases.sh "${GITHUB_REF_NAME}"`, release notes from the tag message, a run artifact, then `softprops/action-gh-release@v2` attaching `turbo-js-*`, `SHA256SUMS` and `README.md` with the job's own `GITHUB_TOKEN` — the only credential Rickub's release API accepts. **`02-release.publish.sh` and `04-release.upload-binaries.sh` are gone**; the build script is now `02-build-releases.sh`, takes the tag as `$1` (CI has no `release.env`), validates it, refuses a `replace`, and starts from an empty `release/${TAG}/`. `release.env` holds only `TAG` and `ABOUT`; `turbo-js.token.env` is read by nothing.
- **`release_test.go`** runs `01` for real against a throwaway bare remote (publishes, then refuses the same tag), runs `02` alone to see it refuse `v0.o.0`, and asserts the workflow's trigger, `contents: write`, `./02-build-releases.sh`, `fail_on_unmatched_files`, docs linked at the tag, no `secrets.`, and `TURBO_JS_RELEASING`. The copy of the module for the clone leaves out `.git`, `bin`, `release`, `kits`, `demo`, `demos`, `*.env` and `go.work*`; children run with `GOWORK=off`.