# bin/ ## Purpose Baked punk shell executables (kits with the punk boot layer), assorted tooling, and plain runtime kits under `runtime/`. Executables here are outputs of the bake and tool-build steps - agents do not hand-edit binaries. ### Generated polyglot .cmd scripts - never edit in place The `.cmd` scripts here (e.g `punk-runtime.cmd`) are punk MULTISHELL polyglots GENERATED by `punk::mix::commandset::scriptwrap::multishell` from scriptset sources under `src/scriptapps/bin/` (the home for bin-deployed scriptsets, e.g `punk-runtime.*` alongside `punk-getzig.*`) or `src/scriptapps/` itself (e.g `punk-tclargs.*`, `dtplite.*`): payload scripts (`.ps1`, `.bash`, `.tcl`, ...) plus a `_wrap.toml` config, spliced into the `punk.multishell.cmd` template. The polyglot structure is deliberately fragile (mutual shell-hiding tricks, LF-only endings, cmd.exe's 512-byte label-scanner constraints) - a hand-edit can silently break one of the participating shells. Polyglot scope policy (user, 2026-07-29): the multishell mechanism exists to serve BOOTSTRAPPING - reaching a working punkshell environment from a machine that has no tclsh or toolchain yet (`punk-runtime` fetches runtimes, `punk-getzig` fetches the zig toolchain). Because the mechanism is complex and potentially fragile over the long term, do not expand it unnecessarily: a capability that only runs when a tclsh is already present (anything make.tcl-adjacent - fetching build tools such as punkres, for example) belongs in-tree behind make.tcl (the G-006 direction), not in a new polyglot. Propose a new polyglot only when the no-tclsh bootstrap case genuinely applies. When asked to "fix bin/.cmd": 1. Edit the payload/config sources under `src/scriptapps/bin/` (never the output file). 2. Re-wrap from the scriptset's folder: cd `src/scriptapps/bin` then `punk::mix::commandset::scriptwrap::multishell -askme 0` (output defaults to `/bin`; requires Thread + the punk::mix modules, e.g a punk shell or a tclsh with project module paths). 3. The wrapper runs `checkfile` (the 512-byte label/boundary validator) automatically - heed ERROR output; "possibly bogus target" warnings are normal for polyglots. Payload growth can push a TEMPLATE label beyond the payloads (e.g `:exit_multishell`) across a 512-byte boundary: fix by resizing the byte-alignment spacer comment at the end of the affected payload (see the `(512B spacer)` comment in `punk-runtime.bash`) and re-wrapping until checkfile is ERROR-free. 4. Commit source AND regenerated output together: the scriptwrap test suite pins that a re-wrap of the punk-runtime scriptset reproduces `bin/punk-runtime.cmd` byte for byte (`src/tests/modules/punk/mix/testsuites/scriptwrap/runtimecmd_roundtrip.test` scriptwrap_runtime_cmd_roundtrip_no_drift), so drift in either direction fails tests. `bin/dtplite.cmd` is pinned the same way (`src/tests/shell/testsuites/binscripts/dtplite.test` dtplite_cmd_roundtrip_no_drift). ### dtplite (`dtplite.cmd`) `bin/dtplite.cmd` wraps tcllib's dtplite doctools processor (payload `src/scriptapps/dtplite.tcl`, tclsh nextshell on all platforms). It exists so the punk repl's unknown-handler can resolve the bare `dtplite` command via PATH - `dev doc.validate` (punk::mix::commandset::doc) depends on it. The payload falls back to the project-vendored tcllib (`src/vendorlib_tcl9//tcllib*`) when the invoking tclsh has no dtplite package installed. ## Local Contracts ### Utility naming policy: punk- prefix (G-096) Punkshell-own utilities deployed to `bin/` take a `punk-` name prefix (e.g `punk-getzig`) so a user who adds `/bin` to their PATH gets collision-resistant names. Exceptions: wrappers whose purpose is to present an external tool under its own name (`dtplite`, `sdx`, `kettle`), and `getpunk` (already punkshell-specific; intended future single-download cross-platform entry point). Policy decided by the user 2026-07-20 (G-096); the remaining pre-policy names were swept under G-097 (2026-07-21): `punk-bits`, `punk-runtime`, `punk-tclargs` and the punk- prefixed selfsign experiment scripts. `.ps1` twin (corrected 2026-07-22 - the twin is SELF-MATERIALIZED, not manually refreshed): the generated `.cmd`'s batch layer creates `.ps1` beside itself when missing AND `fc`-compares/re-copies it whenever content differs (powershell needs a `.ps1` extension for `-File`), so the twin regenerates on any `.cmd` launch and self-heals after a re-wrap - no manual copy step. The twin is a byte-copy of the polyglot and is VCS-ignored (a runtime artifact, unlike the committed `.cmd`). Caveats: a user who only ever launches the `.ps1` directly can ride a stale twin until the next `.cmd`-path launch heals it; and the two launch paths run DIFFERENT powershells - `./bin/` in a powershell session resolves the `.ps1` and runs it under the INVOKING shell (pwsh 7 or powershell 5), while the `.cmd` route is pinned by the wrap toml (`cmd.exe /c powershell` = Windows PowerShell) - payload code must be edition-portable and avoid implementation-defined behaviour (e.g. Hashtable enumeration order differs between the editions; sort explicitly). INVOCATION GUIDELINE for user-facing documentation (user policy 2026-07-22; no user-facing guidelines existed yet when recorded - apply to all future README/help/ doc examples involving the polyglot `bin/*.cmd` utilities): show invocations WITH the `.cmd` extension, e.g. `bin/punk-runtime.cmd list`, even though extensionless invocation happens to work on windows in some cases. Rationale: extensionless resolution in powershell prefers the `.ps1` twin, whose direct execution is ExecutionPolicy-gated (Restricted/AllSigned hosts fail) and runs under the invoking edition, whereas the `.cmd` always executes and follows the wrap-pinned, policy- bypassing tested path. The SAME `.cmd` file also runs on unix shells (the polyglot needs no extension stripping - `./bin/punk-runtime.cmd` from bash/zsh, or `bash bin/punk-runtime.cmd`), so one form serves all platforms and the only platform-specific difference left in examples is path separator style (`bin/punk-runtime.cmd` vs `bin\punk-runtime.cmd`). ### Launch package modes (built punk shells) The first argument to a built punk shell may be a dash-delimited package mode composed of tokens from `minted`, `os`, `src`, `internal` (e.g. `punksys src`, `punk902z minted-os`), optionally scoped with the `proj:` prefix (e.g. `punk91 proj:internal-src`). (`minted` was named `dev` before punkshell 0.43.0 - clean rename, no alias: old kits accept only `dev`, current kits only `minted`.) `internal` is the default and is always appended when absent. A first argument that is not a valid mode list is treated as a subcommand instead (a `proj:` prefix on a non-mode string is not consumed either). Implementation: `src/vfs/_config/punk_main.tcl` (search `all_package_modes`). The full packagemode contract (block meanings, ordering, proj: examples) is the punk::args documentation at id `(script)::punkexe` (module `punk::args::moduledoc::punkexe`), and the launch surface is self-documenting in every current kit (G-032; single-mechanism revision 2026-08-05): ` help ?subcommand? ?arg ...?` renders tabled usage (with the packagemode contract as the leader row; trailing words after the subject are accepted and ignored, so `help` can be prepended to an existing command line) - degrading to a plain subcommand list where the punk::args stack is unavailable. Help FLAGS are never intercepted: `-help` after a subcommand is that subcommand's own argument, and a leading `-help` falls under the refusal below. An unknown first argument that names no existing file (and no `lib:*` scriptlib reference) is refused with that usage rather than silently attempted as a script. A first argument of exactly `-e` instead runs the following argument as a Tcl one-liner (G-077; equivalently ` script -e