punk::path 0.3.0: extensionless tcl scripts via lib: (first-line identification)
scriptlib_resolve now lets an extensionless (or unknown-extension) lib:<name>
call match a file named exactly <name> when its first lines identify it as
tcl: a leading '# tcl' comment (prefix, case-insensitive), a shebang naming
tclsh/tclkit/wish, or the portable sh-trampoline ('#!/bin/sh' with a
commented 'exec tclsh' continuation - detected within the first 4 lines).
New internal punk::path::system::scriptfile_is_tcl does the bounded sniff
(512 bytes, UTF-8 BOM and CRLF tolerated, directories/unreadable files
rejected). When both <name> and <name>.tcl exist, the spelling matching the
call wins, checked per scriptlib location so kit-internal directory
precedence is never crossed by a spelling preference.
.kit joins the known extensions: lib:<name>.kit previously had .tcl appended
(searched <name>.kit.tcl), which made the shell subcommand's do_tclkit
dispatch arms unreachable - lib: kit dispatch is now restored. Result dict
reworked: scripttype (tcl/kit/py/pl/ps1/sh or empty) + notes keys added,
scriptname is now the matched relative name, and matches require file isfile.
app-punkshell and app_shellrun lib: handling refactored onto the shared
resolver (previously independent inline copies of the policy), fixing two
silent-failure bugs in that copy: no message at all when a scriptlib dir
existed but lacked the script, and the error listing showing
<kit>/scriptlib rather than the <kit>/app/scriptlib actually checked.
app-punkscript's post-resolve gate now uses scripttype and its not-found
report includes the identification skip notes. The unused has_globchars
vestige is dropped.
Fixtures scriptlib/_punktest/{extless,dual,dual.tcl,shebang_sh,notatcl} and
5 new scriptexec.test cases (extensionless resolve, both precedence
directions, trampoline shebang + argv, non-tcl skip note on not-found);
suite passes 17/17 against a freshly built punk902z kit. Sniff helper unit
checks pass under tclsh 9.0.3 and 8.6. punkshell project 0.13.0 -> 0.14.0.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
@ -5,6 +5,10 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to
Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy.
## [0.14.0] - 2026-07-18
- `lib:` scriptlib scripts may now be extensionless: an extensionless (or unknown-extension) `lib:<name>` call also matches a file named exactly `<name>` when its first lines identify it as tcl - a leading `# tcl` comment, a shebang naming tclsh/tclkit/wish, or the portable sh-trampoline (`#!/bin/sh` with a commented `exec tclsh` continuation). When both `<name>` and `<name>.tcl` exist, the spelling matching the call wins, checked per scriptlib location so kit-internal precedence is unaffected. Implemented in punk::path 0.3.0 (`scriptlib_resolve` + new internal `scriptfile_is_tcl`; result dict gains `scripttype`/`notes`, not-found errors list skip notes); `.kit` is now a known extension so `lib:<name>.kit` resolves as itself (previously it wrongly searched `<name>.kit.tcl`, making the shell subcommand's kit dispatch unreachable - now restored). The `shell` subcommand's `lib:` handling (app-punkshell, app_shellrun) is refactored onto the shared resolver, fixing two silent-failure bugs in its former inline copy (no message when a scriptlib dir existed but lacked the script; wrong kit-internal path in the error listing). Test fixtures + coverage added under `scriptlib/_punktest/` and `src/tests/shell/testsuites/punkexe/scriptexec.test`.
## [0.13.0] - 2026-07-18
- punk::ns 0.9.0: cmdhelp (`i`) gains `-return text` - plain-text argument documentation for machine/LLM consumption: no ANSI codes, no table layout, and the argument section of every command form rendered in a single output under a common header (`i -return text after` shows all six forms), with G-041 form selection still honoured when argument words are supplied. No advisory-parse error preamble (use `-return dict` for parse status).
@ -16,7 +16,7 @@ Source of truth for all editable `pkgIndex.tcl`-based library packages. These ar
- Libraries here include:
- `app-punk/` — Punk REPL app entry (`app-punk::repl`)
- `app-punkshell/` — Punkshell app entry (`app-punkshell`); on eof of its input channel the `env(PUNK_PIPE_EOF)` policy (`exit`|`interactive`|unset=console heuristic) decides between terminating and reopening the console for an interactive repl — automated callers should set `exit` or end piped input with an explicit `exit`
- `app-punkscript/` — lean one-shot script runner behind the punk executable `script` subcommand (G-015): runs a file, a `lib:<name>` scriptlib script (resolved via `punk::path::scriptlib_resolve`), or piped stdin in the default punk shell module/alias environment with honest exit codes; no shellfilter stacks or logging side effects, never falls into an interactive shell — the reliable path for automated/agent callers (prefer this over piping into `shell`); stdin form echoes the script's final result when non-empty; GUI parity with `tclsh` — a script leaving a registered Tk main loop is serviced until its window closes (so `script app.tcl` keeps a Tk app alive), while console scripts and errored scripts exit immediately
- `app-punkscript/` — lean one-shot script runner behind the punk executable `script` subcommand (G-015): runs a file, a `lib:<name>` scriptlib script (resolved via `punk::path::scriptlib_resolve`; an extensionless call also matches an extensionless file whose first lines identify it as tcl - `# tcl` comment or tcl shebang incl. the sh-trampoline - with the called spelling winning when both spellings exist), or piped stdin in the default punk shell module/alias environment with honest exit codes; no shellfilter stacks or logging side effects, never falls into an interactive shell — the reliable path for automated/agent callers (prefer this over piping into `shell`); stdin form echoes the script's final result when non-empty; GUI parity with `tclsh` — a script leaving a registered Tk main loop is serviced until its window closes (so `script app.tcl` keeps a Tk app alive), while console scripts and errored scripts exit immediately
#0.3.0 - scriptlib_resolve: extensionless tcl scripts resolvable via first-line identification (new system::scriptfile_is_tcl helper - '# tcl' comment, tclsh/tclkit/wish shebang, or sh-trampoline); per-directory spelling order (exact name as called wins over .tcl-appended within each location, kit-internal dir precedence preserved); .kit added to known extensions (previously lib:x.kit wrongly searched x.kit.tcl); return dict keys reworked: scripttype (tcl/kit/py/pl/ps1/sh or empty) + notes added, scriptname now the matched relative name; directories no longer match a spelling (file isfile); now also used by app-punkshell/app_shellrun lib: handling (previously app-punkscript only)
#0.2.2 - added scriptlib_resolve: shared resolution policy for the lib:<script> prefix (kit-internal app/scriptlib first and not externally overridable, then scriptlib dirs relative to the executable); PUNKARGS argdoc namespace + registration added to the module
@ -18,7 +18,7 @@ Tests for shell-level behavior, command-line execution, and stdin/stdout interac
- Tests that spawn the built executable must be hang-proof: use the event-loop `punk_run` pattern (pipe stdin, half-close for EOF, timeout then force-kill) rather than plain `exec`, because known failure modes leave the child waiting on a reopened console.
- `testsuites/punkexe/shellexit.test` guards piped-stdin termination behaviour of the built executable (no `invalid thread handle` on shutdown races, no console-reopen hang on eof/error paths, exitcode propagation); do not weaken these tests - a red result means a regression in punk::repl shutdown or app-punkshell eof handling.
- `testsuites/punkexe/shellnavns.test` proves the namespace-navigation session contract end-to-end over a piped 'src'-mode session (dev modules - deliberate, so current punk::repl/nav source is exercised without a rebuild): n/ sets ns_current, the current namespace continues into `subshell punk` and back out (currently via the documented first-subshell shared-code-interp asymmetry - the SHARED marker pins it; the repl::start seeding template for genuinely separate interps is proven in modules/punk/repl nscurrent.test), the punk::repl 0.5.2 stray-namespace fix holds live, and the session exits cleanly. KNOWN GAP pinned there: piped submissions evaluate at :: despite ns_current (interactive and module-level inscope behaviour differ) - investigate alongside G-038.
- `testsuites/punkexe/scriptexec.test` covers the `script` subcommand (app-punkscript, G-015): piped execution + honest exit codes, result echo, file-form argv, the `lib:<name>` scriptlib-resolution mechanism, and the tclsh-matching Tk main-loop behaviour. The `lib:` and Tk cases resolve committed fixtures under `scriptlib/_punktest/` (test-owned; a `lib:`-resolvable fixture cannot live in a tcltest temp dir since resolution only searches scriptlib locations relative to the exe). Tk cases are gated by the `punktk` constraint, auto-detected by probing whether the kit can load Tk.
- `testsuites/punkexe/scriptexec.test` covers the `script` subcommand (app-punkscript, G-015): piped execution + honest exit codes, result echo, file-form argv, the `lib:<name>` scriptlib-resolution mechanism (incl. punk::path 0.3.0 extensionless-tcl matching: first-line identification, called-spelling precedence over a `.tcl` sibling, sh-trampoline shebang acceptance, and the non-tcl skip note on not-found), and the tclsh-matching Tk main-loop behaviour. The `lib:` and Tk cases resolve committed fixtures under `scriptlib/_punktest/` (test-owned; a `lib:`-resolvable fixture cannot live in a tcltest temp dir since resolution only searches scriptlib locations relative to the exe). Tk cases are gated by the `punktk` constraint, auto-detected by probing whether the kit can load Tk.
- punkexe tests run against the built binary: after changing punk::repl or app-punkshell source, rebuild via `make.tcl packages`, `make.tcl vfscommonupdate -confirm 0` (unattended runs must pass the flag - the REPLACE confirmation aborts fast on non-interactive stdin instead of reading a piped `y`), then `make.tcl project` before trusting results; `make.tcl project` alone does not refresh `_vfscommon.vfs`.
- `testsuites/binscripts/runtimebash_wsl.test` (G-059) exercises the runtime.cmd unix payload (`src/scriptapps/bin/runtime.bash`) through WSL: active/use/run resolution, env override, stale-active guidance, single-candidate fallback, and `list -remote` local-vs-server comparison (offline via a crafted cached sha1sums.txt + `PUNKBIN_URL` pointed at an unreachable address). Gated by the `wsllinux` capability constraint from `src/tests/testsupport/wslprobe.tcl`; all execution happens in a WSL-native staging dir, and a final test asserts the Windows checkout's git status is unchanged by the run.
- `testsuites/punkexe/staticruntime.test` (G-058) targets a kit built on a runtime with statically-linked packages: resolved from `env(PUNK_STATICKIT_TEST_EXE)`, else `<projectroot>/bin/punk91.exe`, gated by the capability-probed `statickitavailable` constraint (a probe run must show a captured `::punkboot::static_packages` baseline including Thread - not mere file existence). It asserts repl boot without the codethread Thread failure, Thread resolution in the code interp / fabricated interps / snapshot-seeded threads, and twapi resolution per the static-vs-bundled policy. The verification runtime (tclsfe-x64.exe) is pinned in the punkbin artifact repo so the constraint is satisfiable off the original dev machine. Kit script-exec paths must be passed with forward slashes.