Browse Source

Single-help-mechanism outputs: core 0.5.0 delivered (vfscommon), moduledoc 0.6.0/0.3.0, pending module promotions swept

make.tcl modules + vfscommonupdate + bake ripple (native Tcl903 driver)
from the single-mechanism change-set ac19df2a: _vfscommon
punkboot/core.tcl regenerated from the 0.5.0 master; moduledoc::punkexe
0.5.0 -> 0.6.0 and moduledoc::punkboot 0.2.0 -> 0.3.0 version renames;
punk::mix templates modpod repacked carrying the layout _config sweep
(modpod-template copies + layout make.tcl copies via the established
sync channels). The same vfscommonupdate swept in promotions pending
from committed src work of the last sessions: commandstack 0.7.1,
punk::args 0.15.1, punk::auto_exec 0.1.1, punk::ns 0.9.2,
punk::tcltestrun 0.4.2, punk::zip 0.4.0.

Claude-Session: https://claude.ai/code/session_01Y1diJnhjUxKgEG6EwYAzxj
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 6 days ago
parent
commit
62c596cec7
  1. 100
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl
  2. 16
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_config/project_main.tcl
  3. 200
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_config/punkboot_core.tcl
  4. 200
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_vfscommon.vfs/punkboot/core.tcl
  5. 100
      src/project_layouts/vendor/punk/basic/src/make.tcl
  6. 100
      src/project_layouts/vendor/punk/project-0.1/src/make.tcl
  7. 43
      src/vfs/_vfscommon.vfs/modules/commandstack-0.7.1.tm
  8. 124
      src/vfs/_vfscommon.vfs/modules/punk/args-0.15.1.tm
  9. 25
      src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkboot-0.3.0.tm
  10. 125
      src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkexe-0.6.0.tm
  11. 28
      src/vfs/_vfscommon.vfs/modules/punk/auto_exec-0.1.1.tm
  12. BIN
      src/vfs/_vfscommon.vfs/modules/punk/mix/templates-0.2.0.tm
  13. 12
      src/vfs/_vfscommon.vfs/modules/punk/ns-0.9.2.tm
  14. 14
      src/vfs/_vfscommon.vfs/modules/punk/tcltestrun-0.4.2.tm
  15. 158
      src/vfs/_vfscommon.vfs/modules/punk/zip-0.4.0.tm
  16. 166
      src/vfs/_vfscommon.vfs/punkboot/core.tcl

100
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl vendored

@ -198,7 +198,6 @@ namespace eval ::punkboot {
variable scriptfolder [file normalize [file dirname [info script]]]
variable foldername [file tail $scriptfolder]
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable help_flags [list -help --help /? -h]
variable known_commands [list bakehouse packages modules libs bake bakelist vfslibs bin info check shell vendorupdate libfetch bootsupport vfscommonupdate projectversion workflow buildsuite tool]
}
@ -2588,17 +2587,18 @@ proc ::punkboot::punkboot_gethelp {args} {
set scriptname [file tail [info script]]
append h "Usage:" \n
append h "" \n
append h " $scriptname -help or $scriptname --help or $scriptname /? or just $scriptname" \n
append h " $scriptname help or just $scriptname" \n
append h " - This help." \n \n
append h " $scriptname bakehouse ?-k? ?-dirty-abort 1|0?" \n
append h " - consumer run from a clean checkout: mints the packages (modules + libs) then bakes kit/zipkit executables to <projectdir>/bin" \n
append h " - refuses uncommitted src by default (-dirty-abort defaults ON: the bakehouse bakes from the committed recipe; pass -dirty-abort 0 to override)" \n
append h " - the optional -k flag will terminate running processes matching the executable being baked (if applicable)" \n
append h " - does NOT run the promotion gates (bootsupport, vfscommonupdate) - on a clean checkout they are already satisfied by the committed tree" \n \n
append h " $scriptname bake ?-k? ?kitname ...?" \n
append h " $scriptname bake ?-k? ?-force? ?kitname ...?" \n
append h " - assemble kit/zipkit executables from the promoted payload (src/vfs) and src/runtime runtimes into <projectdir>/bin" \n
append h " - includes the vfslibs phase; does not re-mint modules/libs and does not run the promotion gates" \n
append h " - with kitname arguments, bakes and deploys only the named configured kits (unknown names error before any bake)" \n \n
append h " - with kitname arguments, bakes and deploys only the named configured kits (unknown names error before any bake)" \n
append h " - -force re-assembles kits whose inputs are unchanged (bypasses the 'no change detected' skip)" \n \n
append h " $scriptname bakelist ?kitname ...?" \n
append h " - list the kit outputs configured in src/runtime/mapvfs.toml: name, kit type, runtime (with presence)," \n
append h " vfs folder and deployed state (bin copy absent/current/stale/nobake vs the src/_bake product)" \n
@ -2655,7 +2655,7 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - zig is OPTIONAL: packages/bake never require this step; without a suitable toolchain the state is" \n
append h " reported and build/test exit nonzero with fetch guidance (bin/punk-getzig.cmd)" \n \n
append h " $scriptname help ?subcommand? ?arg ...?" \n
append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname <subcommand> -help);" \n
append h " - show usage for $scriptname or one of its subcommands (the single help mechanism);" \n
append h " extra words are the subcommand's own command line, dry-run through its declaration - an accepted" \n
append h " line shows the matched action's usage (tool, buildsuite), a rejected line that subcommand's own" \n
append h " usage error (tabled help only)" \n \n
@ -3078,7 +3078,9 @@ set scriptargs $::argv
# G-030: make.tcl dogfoods punk::args.
# Subcommands and their options are declared as punk::args definitions ((script)::punkboot and
# (script)::punkboot::<subcommand>) and parsed with punk::args::parse - 'make.tcl',
# 'make.tcl help ?subcommand?' and 'make.tcl <subcommand> -help' render tabled usage, and invalid
# 'make.tcl help ?subcommand? ?arg ...?' renders tabled usage (the single help mechanism -
# help FLAGS like -help/--help are ordinary arguments since 2026-08-05: they fail whatever
# gate they meet, the usage error itself being documentation), and invalid
# arguments produce punk::args usage errors.
# Degrade rule (hard requirement - see goals/archive/G-030-maketcl-punkargs.md): punk::args is
# guarded. When it (or a definition feature it needs) is unavailable from bootsupport, dispatch
@ -3308,14 +3310,18 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
update contract for keeping it in step with make.tcl behaviour."
help
"
Show tabled usage for make.tcl as a whole, or for a single subcommand.
After the subject, accepts that subcommand's own command line and
dry-runs it through the subcommand's declaration: a line the
subcommand would accept shows the matched action's usage - 'make.tcl
help tool build -test 0 punkzip' shows the tool build form, as does
appending -help to the command line itself ('make.tcl tool build
-test 0 punkzip -help') - while a line it would reject shows the
same usage error the subcommand itself would produce (the error
Show tabled usage for make.tcl as a whole, or for a single
subcommand. This leading 'help' word is the SINGLE help
mechanism: there is no -help flag handling anywhere (a
-help/--help in a command line is an ordinary argument that
fails whatever gate it meets, and that usage error is itself
the documentation).
After the subject, accepts that subcommand's own command line
and dry-runs it through the subcommand's declaration: a line
the subcommand would accept shows the matched action's usage -
'make.tcl help tool build -test 0 punkzip' shows the tool
build form - while a line it would reject shows the same
usage error the subcommand itself would produce (the error
table carries the forms' synopsis, argument rows and per-form
reasons; exit 1). An accepted line is confirmed with a one-line
report of where each word landed ('kitname = punk91 -confirm 0'
@ -3424,6 +3430,14 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
"Terminate running processes matching the executable being built
(if applicable) so the bake can install over it."
}
variable OPT_FORCE {
-force -type none -default 0 -help\
"Re-assemble kits even when no input change is detected.
By default a kit whose vfs/runtime inputs are unchanged since its
last bake is skipped ('no change detected'); -force bypasses that
skip for the selected kits - e.g to compare assembly behaviour
across driving interpreters without touching inputs."
}
#bakehouse defaults the dirty gate ON:
#the bakehouse bakes from the committed recipe (G-112 two-persona model).
variable OPT_DIRTYABORT_ON {
@ -3503,6 +3517,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
{${[dict get $HELPTEXTS bakehouse]}}
@opts
${$OPT_FORCEKILL}
${$OPT_FORCE}
${$OPT_DIRTYABORT_ON}
${$OPT_CONFIRM}
@values -min 0 -max 0
@ -3548,6 +3563,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
{${[dict get $HELPTEXTS bake]}}
@opts
${$OPT_FORCEKILL}
${$OPT_FORCE}
${$OPT_DIRTYABORT}
${$OPT_CONFIRM}
@values -min 0 -max -1
@ -3792,11 +3808,12 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
'bin' and 'modules' folders at the same level as 'src'.
General usage: make.tcl <subcommand> ?flags?
'make.tcl help <subcommand> ?arg ...?' or 'make.tcl <subcommand>
?arg ...? -help' shows a subcommand's own usage - the words are
dry-run through the subcommand's declaration, so an accepted
command line shows the matched action's usage (tool, buildsuite)
and a rejected one shows that subcommand's own usage error.
'make.tcl help <subcommand> ?arg ...?' shows a subcommand's own
usage (the single help mechanism - there is no -help flag) -
the words after the subject are dry-run through the
subcommand's declaration, so an accepted command line shows
the matched action's usage (tool, buildsuite) and a rejected
one shows that subcommand's own usage error.
Interactive y/n confirmations can be driven non-interactively
with the -confirm flag declared on the relevant subcommands.}
punk::args::define {
@ -3901,21 +3918,22 @@ set ::punkboot::opt_kitnames [list] ;#G-121: requested kit names for bake/bak
if {$::punkboot::punkargs_ok} {
set first [lindex $scriptargs 0]
if {![llength $scriptargs] || $first in $::punkboot::help_flags} {
if {![llength $scriptargs]} {
set do_help 1
} else {
#validate/resolve the subcommand word via the top-level definition
#(allows unambiguous prefixes; unknown subcommands get a punk::args usage error)
#(allows unambiguous prefixes; unknown subcommands get a punk::args
#usage error - INCLUDING help flags like -help/--help since the
#2026-08-05 single-mechanism revision: the leading 'help' word and
#the bare invocation are the help surface, and a '-help' anywhere
#else is an ordinary argument that fails whatever gate it meets,
#with the usage error itself carrying the documentation)
if {[catch {punk::args::parse [list $first] -errorstyle $::punkboot::errstyle withid (script)::punkboot} argd]} {
puts stderr $argd
exit 1
}
set subcommand [dict get $argd leaders subcommand]
set subargs [lrange $scriptargs 1 end]
set wants_help 0
foreach h $::punkboot::help_flags {
if {$h in $subargs} {set wants_help 1 ; break}
}
if {$subcommand eq "help"} {
set do_help 1
if {[llength $subargs]} {
@ -3932,17 +3950,6 @@ if {$::punkboot::punkargs_ok} {
set help_subject [dict get $argd values subject]
set help_words [lrange $subargs 1 end]
}
} elseif {$wants_help} {
set do_help 1
set help_subject $subcommand
#'<subcommand> ?arg ...? -help' - the same contract as
#'make.tcl help <subcommand> ?arg ...?': the remaining words are the
#subcommand's command line, with the help flag itself removed
#(wherever it appeared).
set help_words [list]
foreach w $subargs {
if {$w ni $::punkboot::help_flags} {lappend help_words $w}
}
} elseif {$subcommand eq "shell"} {
#declared passthrough: everything after 'shell' goes to the repl unparsed
set ::punkboot::command shell
@ -4035,13 +4042,12 @@ if {$::punkboot::punkargs_ok} {
set ::punkboot::opt_confirm [expr {bool($v)}]
}
default {
if {$a in $::punkboot::help_flags} {
set do_help 1
} else {
puts stderr "Unknown flag: $a\n"
set do_help 1
set help_exitcode 1
}
#help flags are not special (2026-08-05 single-mechanism
#revision): they land here as unknown flags, and the
#resulting help display carries exit 1
puts stderr "Unknown flag: $a\n"
set do_help 1
set help_exitcode 1
}
}
} else {
@ -8645,7 +8651,8 @@ foreach vfstail $vfs_tails {
set changed_unchanged [$vfs_event targetset_source_changes]
set vfs_or_runtime_changed [expr {[llength [dict get $changed_unchanged changed]] || [llength [$vfs_event get_targets_exist]] < [llength [$vfs_event get_targets]]}]
#bake -force bypasses the no-change skip (always re-assemble the selected kits)
set vfs_or_runtime_changed [expr {$::punkboot::opt_force || [llength [dict get $changed_unchanged changed]] || [llength [$vfs_event get_targets_exist]] < [llength [$vfs_event get_targets]]}]
if {$vfs_or_runtime_changed} {
#source .vfs folder has changes
@ -9111,7 +9118,12 @@ foreach vfstail $vfs_tails {
#stamps (and kits) differ run to run. Same principle as the G-117
#embedded records, which keep sha1/size/built in the SIDECAR only -
#time facts belong to punkcheck records and sidecars, not payloads.
#LF translation pinned for the same reason (G-165): the default
#text translation follows the driving interpreter (crlf on windows
#drivers, lf on unix-family ones), which made the stamp's BYTES
#driver-dependent - caught by the G-165 four-driver parity run.
set fd [open [file join $targetvfs punkkit-stamp.toml] w]
chan configure $fd -translation lf
puts -nonewline $fd "[join $lines \n]\n"
close $fd
puts stdout " kit stamp (G-025): punkkit-stamp.toml (project_version=$stamp_project_version vfs=$vfstail runtime=$runtime_fullname boot_core=$core_ver)"

16
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_config/project_main.tcl vendored

@ -16,8 +16,9 @@
# punk::args::moduledoc::punkexe package for the documented launch surface),
# module and library path setup, punk::libunknown, and the default dispatch
# (tclsh/shellspy/punk/shell/script/buildinfo/help) with its G-032 help
# surface ('<kit> help', '-help', '<subcommand> -help' - tabled usage
# listing your registered subcommands, degrading to a plain list).
# surface ('<kit> help ?subcommand? ?arg ...?' - the single help mechanism:
# tabled usage listing your registered subcommands, degrading to a plain
# list; help FLAGS are never intercepted).
#
#WHAT TO EDIT
# Only the ==CUSTOMIZE== blocks below. Everything else is contract plumbing.
@ -58,11 +59,12 @@ apply { args {
namespace eval ::punkboot {variable project_subcommands [dict create]}
#Optional per-name help/parse metadata (G-032): with this declared, your
#subcommands appear in '<kit> help' with summaries, 'help <name>' /
#'<name> -help' render your punk::args definition, and parse 1 validates
#arguments through it before your handler runs (usage errors tabled,
#exit 1). All keys optional; a name without an entry keeps full
#passthrough semantics. Contract detail: punk::args::moduledoc::punkboot
#subcommands appear in '<kit> help' with summaries, 'help <name>' renders
#your punk::args definition, and parse 1 validates arguments through it
#before your handler runs (usage errors tabled, exit 1). All keys
#optional; a name without an entry keeps full passthrough semantics (the
#launcher never touches a subcommand's own arguments - help flags
#included). Contract detail: punk::args::moduledoc::punkboot
#registration definition.
# namespace eval ::punkboot {variable project_subcommand_info [dict create \
# mytool {summary "Frob the widgets in a directory." \

200
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_config/punkboot_core.tcl vendored

@ -11,13 +11,17 @@
#package_mode parsing (minted|os|internal|src with the proj: scope prefix,
#G-033), module-path and auto_path assembly, punk::libunknown, src-mode
##modpod registration, then the default subcommand dispatch
#(tclsh/shellspy/punk/shell/script/buildinfo/help) extended by the thin
#(tclsh/shellspy/punk/shell/script/buildinfo/help plus the G-077 top-level
#'-e' one-liner reclassification to 'script -e') extended by the thin
#main's declared project subcommands (G-031 registration model - see the
#dispatch section near the end of this file). The launch surface documents
#itself via punk::args (G-032): '<punkexe> help ?subcommand?', '-help' and
#'<subcommand> -help' render tabled usage from the moduledoc::punkexe
#definitions plus the live registrations, degrading to a plain subcommand
#list when that stack is unavailable.
#itself via punk::args (G-032; -help flag forms withdrawn 2026-08-05):
#'<punkexe> help ?subcommand? ?arg ...?' - the single help mechanism -
#renders tabled usage from the moduledoc::punkexe definitions plus the live
#registrations, degrading to a plain subcommand list when that stack is
#unavailable. Help flags (-help etc) are never intercepted: a leading one
#falls under the unknown-first-argument refusal (which shows usage on
#stderr), and after a subcommand they are the subcommand's own arguments.
#
#The package_mode surface (documented in full by punk::args::moduledoc::punkexe):
#an optional FIRST launch argument of the form minted|os|internal|src or any
@ -51,7 +55,7 @@
namespace eval ::punkboot {
#boot-core identity (G-031; stamped into kits + reported by the G-025
#buildinfo surfaces)
variable core_version 0.3.0
variable core_version 0.5.0
}
if {![info exists ::punkboot::launch_args]} {
namespace eval ::punkboot [list variable launch_args $::argv]
@ -1522,23 +1526,25 @@ if {![info exists ::punkboot::launch_defaults]} {
namespace eval ::punkboot [list variable boot_state $boot_state_dict]
#---------------------------------------------------------------
#Launcher help + declared-parse machinery (G-032)
#Launcher help + declared-parse machinery (G-032; -help flag forms
#withdrawn 2026-08-05 by user direction - single mechanism)
#The launch surface documents itself through punk::args (definition ids
#from the punk::args::moduledoc::punkexe family):
# <punkexe> help ?subcommand? tabled usage; the top-level table lists
# built-ins AND project-registered
# subcommands with summaries
# <punkexe> -help same as bare 'help' - only on kits whose
# unknownfirst default is 'script' (a
# tool-style kit routes flags to its own
# processor; its surface stays reachable
# via the 'help' word)
# <punkexe> <subcommand> -help the subcommand's tabled usage when -help
# is its FIRST argument (tclsh: only when
# -help is the SOLE argument - stock
# parity keeps every other dash form in
# ::argv; project subcommands: only when
# the registration declares an argsid)
#from the punk::args::moduledoc::punkexe family) via ONE mechanism, the
#leading 'help' word (the punkshell 'i <cmd> ...' idiom):
# <punkexe> help tabled top-level usage; the table
# lists built-ins AND project-registered
# subcommands with summaries
# <punkexe> help <subcommand> ?arg ...?
# the subcommand's tabled usage; words
# after the subject are accepted and
# ignored, so 'help' can be prepended
# to any existing command line
#There is deliberately NO -help/--help flag handling anywhere in the
#dispatch: a leading help-flag word falls under the unknown-first-argument
#rule (whose refusal shows this usage anyway, stderr exit 1), and
#'<subcommand> -help' belongs to the subcommand's own arguments (full
#passthrough - e.g app-shellspy handles -help/--help/help itself; a
#parse-declared subcommand rejects it like any undeclared flag).
#Everything is guarded and lazy (the G-030 degradation doctrine): nothing
#here loads punk::args on a normal boot/dispatch path. When punk::args or
#the moduledoc definitions are unavailable (or PUNKBOOT_PLAIN=1 - same
@ -1553,7 +1559,6 @@ if {![info exists ::punkboot::launch_defaults]} {
#(script)::punkexe::help for subject validation.
#---------------------------------------------------------------
namespace eval ::punkboot {
variable help_flags [list -help --help -h /?]
variable launch_builtin_subcommands [list]
variable launch_subcommands [list]
variable launcher_punkargs_state "" ;#"" unprobed | ok | plain
@ -1679,8 +1684,8 @@ if {![info exists ::punkboot::launch_defaults]} {
proc launcher_helpdef_id {} {
#define (script)::punkexe.launcherhelp: the moduledoc
#(script)::punkexe::help definition with the subject choices
#replaced by the live subcommand set, so 'help <name>' validates
#(and prefix-resolves) against what this kit actually offers.
#replaced by the live subcommand set - the DISPLAY clone rendered
#by 'help help'.
set id (script)::punkexe.launcherhelp
set existing ""
catch {set existing [punk::args::raw_def $id]}
@ -1698,11 +1703,37 @@ if {![info exists ::punkboot::launch_defaults]} {
}
return $id
}
proc launcher_helpsubjectdef_id {} {
#define (script)::punkexe.launcherhelpsubject: the VALIDATION
#clone the help arm parses the subject word against - subject
#REQUIRED with the live choices, and the display definition's
#ignored trailing-arg row suppressed (-antiglobs). Without this
#the display shape lets punk::args SKIP a mistyped optional
#subject into the unconstrained arg row, silently rendering the
#top-level table where a tabled choice error is wanted.
set id (script)::punkexe.launcherhelpsubject
set existing ""
catch {set existing [punk::args::raw_def $id]}
if {$existing ne ""} {
return $id
}
if {[catch {
set ov [dict create \
@id [list -id $id] \
subject [list -optional 0 -choices [launcher_live_choices]] \
]
punk::args::define [punk::args::resolved_def -antiglobs {arg} -override $ov (script)::punkexe::help]
}]} {
return ""
}
return $id
}
proc launcher_plain_help {} {
#degraded help: plain subcommand list built without punk::args
set exebase [file rootname [file tail [info nameofexecutable]]]
set lines [list]
lappend lines "Usage: $exebase ?packagemode? ?subcommand? ?arg ...?"
lappend lines " $exebase ?packagemode? -e <script> ?arg ...? (one-liner - same as 'script -e', G-077)"
lappend lines " packagemode: ordered dash-separated list of internal|minted|os|src (default internal),"
lappend lines " optionally scoped with the 'proj:' prefix (e.g proj:internal-src)"
lappend lines " subcommands:"
@ -1793,6 +1824,7 @@ if {![info exists ::punkboot::launch_defaults]} {
puts stderr "known subcommands: [join [launcher_plain_choices] {, }]"
}
puts stderr "use '$exebase help' for the launch surface, or '$exebase script $word ?arg ...?' to force script interpretation"
puts stderr "for a one-liner use '$exebase -e <script> ?arg ...?' (equivalently '$exebase script -e <script> ?arg ...?')"
return 1
}
}
@ -1838,53 +1870,44 @@ if {![info exists ::punkboot::launch_defaults]} {
set subcommand [lindex $arglist 0]
if {$subcommand in $known_subcommands} {
set subcommand_arglist [lrange $arglist 1 end]
#G-032: a help flag as the subcommand's FIRST argument renders that
#subcommand's usage. tclsh: only when it is the SOLE argument (stock
#parity keeps every other leading-dash form - including '-help' with
#further arguments - in ::argv); project subcommands: only when the
#registration declares an argsid (a handler-only registration keeps
#full passthrough - the application may do its own -help handling).
if {[lindex $subcommand_arglist 0] in $::punkboot::help_flags} {
set do_subhelp 0
if {$subcommand eq "tclsh"} {
if {[llength $subcommand_arglist] == 1} {
set do_subhelp 1
}
} elseif {$subcommand in $builtin_subcommands} {
set do_subhelp 1
} elseif {[dict exists $::punkboot::project_subcommand_info $subcommand argsid]} {
set do_subhelp 1
}
if {$do_subhelp} {
::punkboot::launcher_show_help $subcommand
exit 0
}
}
#G-032 (revised 2026-08-05): a subcommand's arguments are entirely its
#own - there is no first-argument help-flag interception. Use
#'<punkexe> help <subcommand>' for a subcommand's usage; '-help' after
#a subcommand reaches the app/script unchanged (tclsh keeps full stock
#dash parity; app-shellspy handles -help/--help/help itself; a
#parse-declared subcommand rejects it like any undeclared flag).
} else {
set subcommand_arglist $arglist
if {[llength $subcommand_arglist]} {
#G-032: a help flag in first position is a launch-surface help
#request on kits whose unknown-first default is 'script'.
#Tool-style kits (unknownfirst names a project subcommand) keep
#routing flags to their processor - their surface stays
#discoverable via the 'help' subcommand word.
if {$subcommand in $::punkboot::help_flags && $default_unknownfirst eq "script"} {
::punkboot::launcher_show_help
exit 0
}
set subcommand $default_unknownfirst
if {$subcommand eq "script"} {
#G-032 unknown-first-arg reclassification rule (recorded in
#goals/G-032-launcher-punkargs.md): reclassify to 'script'
#only when the argument plausibly names a script - an
#existing file path, or a lib:* scriptlib reference. Anything
#else is refused with usage on stderr (exit 1); when
#punk::args is unavailable the refusal degrades to the
#historic always-reclassify behaviour.
set unknown_word [lindex $subcommand_arglist 0]
if {!([file exists $unknown_word] || [string match -nocase lib:* $unknown_word])} {
if {[::punkboot::launcher_unknown_first_error $unknown_word]} {
exit 1
#G-077: a first argument of exactly '-e' is the one-liner form -
#reclassify to 'script' with the WHOLE arglist ('-e' included) as
#its arguments, so the script subcommand's own '-e' handling runs
#it. This sits ahead of the unknown-first-arg refusal below, which
#would otherwise reject '-e' as naming no existing file. Only
#script-default kits intercept it - a tool-style kit keeps '-e'
#routed to its own processor, and '<punkexe> tclsh -e ...' keeps
#stock parity (the tclsh subcommand never reaches this path).
#A leading help FLAG (-help etc) is deliberately NOT special
#(2026-08-05 single-mechanism revision): on script-default kits it
#falls to the refusal below, whose stderr usage answers it anyway;
#tool-style kits route it to their processor.
if {$subcommand eq "-e" && $default_unknownfirst eq "script"} {
set subcommand script
} else {
set subcommand $default_unknownfirst
if {$subcommand eq "script"} {
#G-032 unknown-first-arg reclassification rule (recorded in
#goals/G-032-launcher-punkargs.md): reclassify to 'script'
#only when the argument plausibly names a script - an
#existing file path, or a lib:* scriptlib reference. Anything
#else is refused with usage on stderr (exit 1); when
#punk::args is unavailable the refusal degrades to the
#historic always-reclassify behaviour.
set unknown_word [lindex $subcommand_arglist 0]
if {!([file exists $unknown_word] || [string match -nocase lib:* $unknown_word])} {
if {[::punkboot::launcher_unknown_first_error $unknown_word]} {
exit 1
}
}
}
}
@ -2068,11 +2091,11 @@ if {![info exists ::punkboot::launch_defaults]} {
#exit code; no repl fallthrough (G-015-compatible).
#G-032: the argument contract ((script)::punkexe::buildinfo,
#@values -min 0 -max 0) is enforced through punk::args when
#available: any argument earns the historic one-line refusal PLUS
#the tabled usage error on stderr, exit 2 (buildinfo.test pins the
#one-liner and the exit code). The empty-argument fast path never
#touches punk::args; a '-help' first argument was already
#intercepted at selection.
#available: any argument (including '-help' - no flag
#interception, 2026-08-05 revision) earns the historic one-line
#refusal PLUS the tabled usage error on stderr, exit 2
#(buildinfo.test pins the one-liner and the exit code). The
#empty-argument fast path never touches punk::args.
if {[llength $subcommand_arglist]} {
puts stderr "buildinfo: takes no arguments"
if {[::punkboot::launcher_punkargs_ready]} {
@ -2089,9 +2112,14 @@ if {![info exists ::punkboot::launch_defaults]} {
exit [punk::buildinfo::main]
}
help {
#G-032: launcher help surface. Bare 'help' renders the top-level
#tabled usage (built-ins + project-registered subcommands with
#summaries); 'help <subcommand>' renders that subcommand's usage.
#G-032: launcher help surface - THE single help mechanism
#(2026-08-05 revision; the punkshell 'i <cmd> ...' idiom). Bare
#'help' renders the top-level tabled usage (built-ins +
#project-registered subcommands with summaries);
#'help <subcommand> ?arg ...?' renders that subcommand's usage -
#words after the subject are accepted and ignored, so 'help' can
#be prepended to any existing command line
#('punk9_beta help tclsh somefile somearg' shows the tclsh usage).
#Subject validation parses through the launcherhelp clone when
#punk::args is available (tabled choice errors on stderr exit 1,
#unambiguous prefixes resolve); degraded mode accepts exact names
@ -2102,9 +2130,16 @@ if {![info exists ::punkboot::launch_defaults]} {
exit 0
}
if {[::punkboot::launcher_punkargs_ready]} {
set helpdef_id [::punkboot::launcher_helpdef_id]
set helpdef_id [::punkboot::launcher_helpsubjectdef_id]
if {$helpdef_id ne ""} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid $helpdef_id} argd]} {
#validate/resolve ONLY the subject word (the make.tcl help
#precedent), against the required-subject validation clone:
#a mistyped subject earns the tabled choice error (exit 1)
#instead of being skipped past to render the top-level
#table; everything after the subject is deliberately
#unparsed (accepted and ignored - it is the subject's own
#command line, any mix of its words).
if {[catch {punk::args::parse [lrange $subcommand_arglist 0 0] -errorstyle [::punkboot::launcher_errstyle] withid $helpdef_id} argd]} {
puts stderr $argd
exit 1
}
@ -2114,10 +2149,11 @@ if {![info exists ::punkboot::launch_defaults]} {
exit 0
}
}
#degraded: exact-name subject only
#degraded: exact-name subject only; trailing words ignored as in
#the parsed path
set help_subject [lindex $subcommand_arglist 0]
if {[llength $subcommand_arglist] > 1 || $help_subject ni $::punkboot::launch_subcommands} {
puts stderr "help: expected 'help ?subcommand?' with subcommand one of: [join [::punkboot::launcher_plain_choices] {, }]"
if {$help_subject ni $::punkboot::launch_subcommands} {
puts stderr "help: expected 'help ?subcommand? ?arg ...?' with subcommand one of: [join [::punkboot::launcher_plain_choices] {, }]"
exit 1
}
::punkboot::launcher_show_help $help_subject

200
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_vfscommon.vfs/punkboot/core.tcl vendored

@ -11,13 +11,17 @@
#package_mode parsing (minted|os|internal|src with the proj: scope prefix,
#G-033), module-path and auto_path assembly, punk::libunknown, src-mode
##modpod registration, then the default subcommand dispatch
#(tclsh/shellspy/punk/shell/script/buildinfo/help) extended by the thin
#(tclsh/shellspy/punk/shell/script/buildinfo/help plus the G-077 top-level
#'-e' one-liner reclassification to 'script -e') extended by the thin
#main's declared project subcommands (G-031 registration model - see the
#dispatch section near the end of this file). The launch surface documents
#itself via punk::args (G-032): '<punkexe> help ?subcommand?', '-help' and
#'<subcommand> -help' render tabled usage from the moduledoc::punkexe
#definitions plus the live registrations, degrading to a plain subcommand
#list when that stack is unavailable.
#itself via punk::args (G-032; -help flag forms withdrawn 2026-08-05):
#'<punkexe> help ?subcommand? ?arg ...?' - the single help mechanism -
#renders tabled usage from the moduledoc::punkexe definitions plus the live
#registrations, degrading to a plain subcommand list when that stack is
#unavailable. Help flags (-help etc) are never intercepted: a leading one
#falls under the unknown-first-argument refusal (which shows usage on
#stderr), and after a subcommand they are the subcommand's own arguments.
#
#The package_mode surface (documented in full by punk::args::moduledoc::punkexe):
#an optional FIRST launch argument of the form minted|os|internal|src or any
@ -51,7 +55,7 @@
namespace eval ::punkboot {
#boot-core identity (G-031; stamped into kits + reported by the G-025
#buildinfo surfaces)
variable core_version 0.3.0
variable core_version 0.5.0
}
if {![info exists ::punkboot::launch_args]} {
namespace eval ::punkboot [list variable launch_args $::argv]
@ -1522,23 +1526,25 @@ if {![info exists ::punkboot::launch_defaults]} {
namespace eval ::punkboot [list variable boot_state $boot_state_dict]
#---------------------------------------------------------------
#Launcher help + declared-parse machinery (G-032)
#Launcher help + declared-parse machinery (G-032; -help flag forms
#withdrawn 2026-08-05 by user direction - single mechanism)
#The launch surface documents itself through punk::args (definition ids
#from the punk::args::moduledoc::punkexe family):
# <punkexe> help ?subcommand? tabled usage; the top-level table lists
# built-ins AND project-registered
# subcommands with summaries
# <punkexe> -help same as bare 'help' - only on kits whose
# unknownfirst default is 'script' (a
# tool-style kit routes flags to its own
# processor; its surface stays reachable
# via the 'help' word)
# <punkexe> <subcommand> -help the subcommand's tabled usage when -help
# is its FIRST argument (tclsh: only when
# -help is the SOLE argument - stock
# parity keeps every other dash form in
# ::argv; project subcommands: only when
# the registration declares an argsid)
#from the punk::args::moduledoc::punkexe family) via ONE mechanism, the
#leading 'help' word (the punkshell 'i <cmd> ...' idiom):
# <punkexe> help tabled top-level usage; the table
# lists built-ins AND project-registered
# subcommands with summaries
# <punkexe> help <subcommand> ?arg ...?
# the subcommand's tabled usage; words
# after the subject are accepted and
# ignored, so 'help' can be prepended
# to any existing command line
#There is deliberately NO -help/--help flag handling anywhere in the
#dispatch: a leading help-flag word falls under the unknown-first-argument
#rule (whose refusal shows this usage anyway, stderr exit 1), and
#'<subcommand> -help' belongs to the subcommand's own arguments (full
#passthrough - e.g app-shellspy handles -help/--help/help itself; a
#parse-declared subcommand rejects it like any undeclared flag).
#Everything is guarded and lazy (the G-030 degradation doctrine): nothing
#here loads punk::args on a normal boot/dispatch path. When punk::args or
#the moduledoc definitions are unavailable (or PUNKBOOT_PLAIN=1 - same
@ -1553,7 +1559,6 @@ if {![info exists ::punkboot::launch_defaults]} {
#(script)::punkexe::help for subject validation.
#---------------------------------------------------------------
namespace eval ::punkboot {
variable help_flags [list -help --help -h /?]
variable launch_builtin_subcommands [list]
variable launch_subcommands [list]
variable launcher_punkargs_state "" ;#"" unprobed | ok | plain
@ -1679,8 +1684,8 @@ if {![info exists ::punkboot::launch_defaults]} {
proc launcher_helpdef_id {} {
#define (script)::punkexe.launcherhelp: the moduledoc
#(script)::punkexe::help definition with the subject choices
#replaced by the live subcommand set, so 'help <name>' validates
#(and prefix-resolves) against what this kit actually offers.
#replaced by the live subcommand set - the DISPLAY clone rendered
#by 'help help'.
set id (script)::punkexe.launcherhelp
set existing ""
catch {set existing [punk::args::raw_def $id]}
@ -1698,11 +1703,37 @@ if {![info exists ::punkboot::launch_defaults]} {
}
return $id
}
proc launcher_helpsubjectdef_id {} {
#define (script)::punkexe.launcherhelpsubject: the VALIDATION
#clone the help arm parses the subject word against - subject
#REQUIRED with the live choices, and the display definition's
#ignored trailing-arg row suppressed (-antiglobs). Without this
#the display shape lets punk::args SKIP a mistyped optional
#subject into the unconstrained arg row, silently rendering the
#top-level table where a tabled choice error is wanted.
set id (script)::punkexe.launcherhelpsubject
set existing ""
catch {set existing [punk::args::raw_def $id]}
if {$existing ne ""} {
return $id
}
if {[catch {
set ov [dict create \
@id [list -id $id] \
subject [list -optional 0 -choices [launcher_live_choices]] \
]
punk::args::define [punk::args::resolved_def -antiglobs {arg} -override $ov (script)::punkexe::help]
}]} {
return ""
}
return $id
}
proc launcher_plain_help {} {
#degraded help: plain subcommand list built without punk::args
set exebase [file rootname [file tail [info nameofexecutable]]]
set lines [list]
lappend lines "Usage: $exebase ?packagemode? ?subcommand? ?arg ...?"
lappend lines " $exebase ?packagemode? -e <script> ?arg ...? (one-liner - same as 'script -e', G-077)"
lappend lines " packagemode: ordered dash-separated list of internal|minted|os|src (default internal),"
lappend lines " optionally scoped with the 'proj:' prefix (e.g proj:internal-src)"
lappend lines " subcommands:"
@ -1793,6 +1824,7 @@ if {![info exists ::punkboot::launch_defaults]} {
puts stderr "known subcommands: [join [launcher_plain_choices] {, }]"
}
puts stderr "use '$exebase help' for the launch surface, or '$exebase script $word ?arg ...?' to force script interpretation"
puts stderr "for a one-liner use '$exebase -e <script> ?arg ...?' (equivalently '$exebase script -e <script> ?arg ...?')"
return 1
}
}
@ -1838,53 +1870,44 @@ if {![info exists ::punkboot::launch_defaults]} {
set subcommand [lindex $arglist 0]
if {$subcommand in $known_subcommands} {
set subcommand_arglist [lrange $arglist 1 end]
#G-032: a help flag as the subcommand's FIRST argument renders that
#subcommand's usage. tclsh: only when it is the SOLE argument (stock
#parity keeps every other leading-dash form - including '-help' with
#further arguments - in ::argv); project subcommands: only when the
#registration declares an argsid (a handler-only registration keeps
#full passthrough - the application may do its own -help handling).
if {[lindex $subcommand_arglist 0] in $::punkboot::help_flags} {
set do_subhelp 0
if {$subcommand eq "tclsh"} {
if {[llength $subcommand_arglist] == 1} {
set do_subhelp 1
}
} elseif {$subcommand in $builtin_subcommands} {
set do_subhelp 1
} elseif {[dict exists $::punkboot::project_subcommand_info $subcommand argsid]} {
set do_subhelp 1
}
if {$do_subhelp} {
::punkboot::launcher_show_help $subcommand
exit 0
}
}
#G-032 (revised 2026-08-05): a subcommand's arguments are entirely its
#own - there is no first-argument help-flag interception. Use
#'<punkexe> help <subcommand>' for a subcommand's usage; '-help' after
#a subcommand reaches the app/script unchanged (tclsh keeps full stock
#dash parity; app-shellspy handles -help/--help/help itself; a
#parse-declared subcommand rejects it like any undeclared flag).
} else {
set subcommand_arglist $arglist
if {[llength $subcommand_arglist]} {
#G-032: a help flag in first position is a launch-surface help
#request on kits whose unknown-first default is 'script'.
#Tool-style kits (unknownfirst names a project subcommand) keep
#routing flags to their processor - their surface stays
#discoverable via the 'help' subcommand word.
if {$subcommand in $::punkboot::help_flags && $default_unknownfirst eq "script"} {
::punkboot::launcher_show_help
exit 0
}
set subcommand $default_unknownfirst
if {$subcommand eq "script"} {
#G-032 unknown-first-arg reclassification rule (recorded in
#goals/G-032-launcher-punkargs.md): reclassify to 'script'
#only when the argument plausibly names a script - an
#existing file path, or a lib:* scriptlib reference. Anything
#else is refused with usage on stderr (exit 1); when
#punk::args is unavailable the refusal degrades to the
#historic always-reclassify behaviour.
set unknown_word [lindex $subcommand_arglist 0]
if {!([file exists $unknown_word] || [string match -nocase lib:* $unknown_word])} {
if {[::punkboot::launcher_unknown_first_error $unknown_word]} {
exit 1
#G-077: a first argument of exactly '-e' is the one-liner form -
#reclassify to 'script' with the WHOLE arglist ('-e' included) as
#its arguments, so the script subcommand's own '-e' handling runs
#it. This sits ahead of the unknown-first-arg refusal below, which
#would otherwise reject '-e' as naming no existing file. Only
#script-default kits intercept it - a tool-style kit keeps '-e'
#routed to its own processor, and '<punkexe> tclsh -e ...' keeps
#stock parity (the tclsh subcommand never reaches this path).
#A leading help FLAG (-help etc) is deliberately NOT special
#(2026-08-05 single-mechanism revision): on script-default kits it
#falls to the refusal below, whose stderr usage answers it anyway;
#tool-style kits route it to their processor.
if {$subcommand eq "-e" && $default_unknownfirst eq "script"} {
set subcommand script
} else {
set subcommand $default_unknownfirst
if {$subcommand eq "script"} {
#G-032 unknown-first-arg reclassification rule (recorded in
#goals/G-032-launcher-punkargs.md): reclassify to 'script'
#only when the argument plausibly names a script - an
#existing file path, or a lib:* scriptlib reference. Anything
#else is refused with usage on stderr (exit 1); when
#punk::args is unavailable the refusal degrades to the
#historic always-reclassify behaviour.
set unknown_word [lindex $subcommand_arglist 0]
if {!([file exists $unknown_word] || [string match -nocase lib:* $unknown_word])} {
if {[::punkboot::launcher_unknown_first_error $unknown_word]} {
exit 1
}
}
}
}
@ -2068,11 +2091,11 @@ if {![info exists ::punkboot::launch_defaults]} {
#exit code; no repl fallthrough (G-015-compatible).
#G-032: the argument contract ((script)::punkexe::buildinfo,
#@values -min 0 -max 0) is enforced through punk::args when
#available: any argument earns the historic one-line refusal PLUS
#the tabled usage error on stderr, exit 2 (buildinfo.test pins the
#one-liner and the exit code). The empty-argument fast path never
#touches punk::args; a '-help' first argument was already
#intercepted at selection.
#available: any argument (including '-help' - no flag
#interception, 2026-08-05 revision) earns the historic one-line
#refusal PLUS the tabled usage error on stderr, exit 2
#(buildinfo.test pins the one-liner and the exit code). The
#empty-argument fast path never touches punk::args.
if {[llength $subcommand_arglist]} {
puts stderr "buildinfo: takes no arguments"
if {[::punkboot::launcher_punkargs_ready]} {
@ -2089,9 +2112,14 @@ if {![info exists ::punkboot::launch_defaults]} {
exit [punk::buildinfo::main]
}
help {
#G-032: launcher help surface. Bare 'help' renders the top-level
#tabled usage (built-ins + project-registered subcommands with
#summaries); 'help <subcommand>' renders that subcommand's usage.
#G-032: launcher help surface - THE single help mechanism
#(2026-08-05 revision; the punkshell 'i <cmd> ...' idiom). Bare
#'help' renders the top-level tabled usage (built-ins +
#project-registered subcommands with summaries);
#'help <subcommand> ?arg ...?' renders that subcommand's usage -
#words after the subject are accepted and ignored, so 'help' can
#be prepended to any existing command line
#('punk9_beta help tclsh somefile somearg' shows the tclsh usage).
#Subject validation parses through the launcherhelp clone when
#punk::args is available (tabled choice errors on stderr exit 1,
#unambiguous prefixes resolve); degraded mode accepts exact names
@ -2102,9 +2130,16 @@ if {![info exists ::punkboot::launch_defaults]} {
exit 0
}
if {[::punkboot::launcher_punkargs_ready]} {
set helpdef_id [::punkboot::launcher_helpdef_id]
set helpdef_id [::punkboot::launcher_helpsubjectdef_id]
if {$helpdef_id ne ""} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid $helpdef_id} argd]} {
#validate/resolve ONLY the subject word (the make.tcl help
#precedent), against the required-subject validation clone:
#a mistyped subject earns the tabled choice error (exit 1)
#instead of being skipped past to render the top-level
#table; everything after the subject is deliberately
#unparsed (accepted and ignored - it is the subject's own
#command line, any mix of its words).
if {[catch {punk::args::parse [lrange $subcommand_arglist 0 0] -errorstyle [::punkboot::launcher_errstyle] withid $helpdef_id} argd]} {
puts stderr $argd
exit 1
}
@ -2114,10 +2149,11 @@ if {![info exists ::punkboot::launch_defaults]} {
exit 0
}
}
#degraded: exact-name subject only
#degraded: exact-name subject only; trailing words ignored as in
#the parsed path
set help_subject [lindex $subcommand_arglist 0]
if {[llength $subcommand_arglist] > 1 || $help_subject ni $::punkboot::launch_subcommands} {
puts stderr "help: expected 'help ?subcommand?' with subcommand one of: [join [::punkboot::launcher_plain_choices] {, }]"
if {$help_subject ni $::punkboot::launch_subcommands} {
puts stderr "help: expected 'help ?subcommand? ?arg ...?' with subcommand one of: [join [::punkboot::launcher_plain_choices] {, }]"
exit 1
}
::punkboot::launcher_show_help $help_subject

100
src/project_layouts/vendor/punk/basic/src/make.tcl vendored

@ -198,7 +198,6 @@ namespace eval ::punkboot {
variable scriptfolder [file normalize [file dirname [info script]]]
variable foldername [file tail $scriptfolder]
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable help_flags [list -help --help /? -h]
variable known_commands [list bakehouse packages modules libs bake bakelist vfslibs bin info check shell vendorupdate libfetch bootsupport vfscommonupdate projectversion workflow buildsuite tool]
}
@ -2588,17 +2587,18 @@ proc ::punkboot::punkboot_gethelp {args} {
set scriptname [file tail [info script]]
append h "Usage:" \n
append h "" \n
append h " $scriptname -help or $scriptname --help or $scriptname /? or just $scriptname" \n
append h " $scriptname help or just $scriptname" \n
append h " - This help." \n \n
append h " $scriptname bakehouse ?-k? ?-dirty-abort 1|0?" \n
append h " - consumer run from a clean checkout: mints the packages (modules + libs) then bakes kit/zipkit executables to <projectdir>/bin" \n
append h " - refuses uncommitted src by default (-dirty-abort defaults ON: the bakehouse bakes from the committed recipe; pass -dirty-abort 0 to override)" \n
append h " - the optional -k flag will terminate running processes matching the executable being baked (if applicable)" \n
append h " - does NOT run the promotion gates (bootsupport, vfscommonupdate) - on a clean checkout they are already satisfied by the committed tree" \n \n
append h " $scriptname bake ?-k? ?kitname ...?" \n
append h " $scriptname bake ?-k? ?-force? ?kitname ...?" \n
append h " - assemble kit/zipkit executables from the promoted payload (src/vfs) and src/runtime runtimes into <projectdir>/bin" \n
append h " - includes the vfslibs phase; does not re-mint modules/libs and does not run the promotion gates" \n
append h " - with kitname arguments, bakes and deploys only the named configured kits (unknown names error before any bake)" \n \n
append h " - with kitname arguments, bakes and deploys only the named configured kits (unknown names error before any bake)" \n
append h " - -force re-assembles kits whose inputs are unchanged (bypasses the 'no change detected' skip)" \n \n
append h " $scriptname bakelist ?kitname ...?" \n
append h " - list the kit outputs configured in src/runtime/mapvfs.toml: name, kit type, runtime (with presence)," \n
append h " vfs folder and deployed state (bin copy absent/current/stale/nobake vs the src/_bake product)" \n
@ -2655,7 +2655,7 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - zig is OPTIONAL: packages/bake never require this step; without a suitable toolchain the state is" \n
append h " reported and build/test exit nonzero with fetch guidance (bin/punk-getzig.cmd)" \n \n
append h " $scriptname help ?subcommand? ?arg ...?" \n
append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname <subcommand> -help);" \n
append h " - show usage for $scriptname or one of its subcommands (the single help mechanism);" \n
append h " extra words are the subcommand's own command line, dry-run through its declaration - an accepted" \n
append h " line shows the matched action's usage (tool, buildsuite), a rejected line that subcommand's own" \n
append h " usage error (tabled help only)" \n \n
@ -3078,7 +3078,9 @@ set scriptargs $::argv
# G-030: make.tcl dogfoods punk::args.
# Subcommands and their options are declared as punk::args definitions ((script)::punkboot and
# (script)::punkboot::<subcommand>) and parsed with punk::args::parse - 'make.tcl',
# 'make.tcl help ?subcommand?' and 'make.tcl <subcommand> -help' render tabled usage, and invalid
# 'make.tcl help ?subcommand? ?arg ...?' renders tabled usage (the single help mechanism -
# help FLAGS like -help/--help are ordinary arguments since 2026-08-05: they fail whatever
# gate they meet, the usage error itself being documentation), and invalid
# arguments produce punk::args usage errors.
# Degrade rule (hard requirement - see goals/archive/G-030-maketcl-punkargs.md): punk::args is
# guarded. When it (or a definition feature it needs) is unavailable from bootsupport, dispatch
@ -3308,14 +3310,18 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
update contract for keeping it in step with make.tcl behaviour."
help
"
Show tabled usage for make.tcl as a whole, or for a single subcommand.
After the subject, accepts that subcommand's own command line and
dry-runs it through the subcommand's declaration: a line the
subcommand would accept shows the matched action's usage - 'make.tcl
help tool build -test 0 punkzip' shows the tool build form, as does
appending -help to the command line itself ('make.tcl tool build
-test 0 punkzip -help') - while a line it would reject shows the
same usage error the subcommand itself would produce (the error
Show tabled usage for make.tcl as a whole, or for a single
subcommand. This leading 'help' word is the SINGLE help
mechanism: there is no -help flag handling anywhere (a
-help/--help in a command line is an ordinary argument that
fails whatever gate it meets, and that usage error is itself
the documentation).
After the subject, accepts that subcommand's own command line
and dry-runs it through the subcommand's declaration: a line
the subcommand would accept shows the matched action's usage -
'make.tcl help tool build -test 0 punkzip' shows the tool
build form - while a line it would reject shows the same
usage error the subcommand itself would produce (the error
table carries the forms' synopsis, argument rows and per-form
reasons; exit 1). An accepted line is confirmed with a one-line
report of where each word landed ('kitname = punk91 -confirm 0'
@ -3424,6 +3430,14 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
"Terminate running processes matching the executable being built
(if applicable) so the bake can install over it."
}
variable OPT_FORCE {
-force -type none -default 0 -help\
"Re-assemble kits even when no input change is detected.
By default a kit whose vfs/runtime inputs are unchanged since its
last bake is skipped ('no change detected'); -force bypasses that
skip for the selected kits - e.g to compare assembly behaviour
across driving interpreters without touching inputs."
}
#bakehouse defaults the dirty gate ON:
#the bakehouse bakes from the committed recipe (G-112 two-persona model).
variable OPT_DIRTYABORT_ON {
@ -3503,6 +3517,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
{${[dict get $HELPTEXTS bakehouse]}}
@opts
${$OPT_FORCEKILL}
${$OPT_FORCE}
${$OPT_DIRTYABORT_ON}
${$OPT_CONFIRM}
@values -min 0 -max 0
@ -3548,6 +3563,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
{${[dict get $HELPTEXTS bake]}}
@opts
${$OPT_FORCEKILL}
${$OPT_FORCE}
${$OPT_DIRTYABORT}
${$OPT_CONFIRM}
@values -min 0 -max -1
@ -3792,11 +3808,12 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
'bin' and 'modules' folders at the same level as 'src'.
General usage: make.tcl <subcommand> ?flags?
'make.tcl help <subcommand> ?arg ...?' or 'make.tcl <subcommand>
?arg ...? -help' shows a subcommand's own usage - the words are
dry-run through the subcommand's declaration, so an accepted
command line shows the matched action's usage (tool, buildsuite)
and a rejected one shows that subcommand's own usage error.
'make.tcl help <subcommand> ?arg ...?' shows a subcommand's own
usage (the single help mechanism - there is no -help flag) -
the words after the subject are dry-run through the
subcommand's declaration, so an accepted command line shows
the matched action's usage (tool, buildsuite) and a rejected
one shows that subcommand's own usage error.
Interactive y/n confirmations can be driven non-interactively
with the -confirm flag declared on the relevant subcommands.}
punk::args::define {
@ -3901,21 +3918,22 @@ set ::punkboot::opt_kitnames [list] ;#G-121: requested kit names for bake/bak
if {$::punkboot::punkargs_ok} {
set first [lindex $scriptargs 0]
if {![llength $scriptargs] || $first in $::punkboot::help_flags} {
if {![llength $scriptargs]} {
set do_help 1
} else {
#validate/resolve the subcommand word via the top-level definition
#(allows unambiguous prefixes; unknown subcommands get a punk::args usage error)
#(allows unambiguous prefixes; unknown subcommands get a punk::args
#usage error - INCLUDING help flags like -help/--help since the
#2026-08-05 single-mechanism revision: the leading 'help' word and
#the bare invocation are the help surface, and a '-help' anywhere
#else is an ordinary argument that fails whatever gate it meets,
#with the usage error itself carrying the documentation)
if {[catch {punk::args::parse [list $first] -errorstyle $::punkboot::errstyle withid (script)::punkboot} argd]} {
puts stderr $argd
exit 1
}
set subcommand [dict get $argd leaders subcommand]
set subargs [lrange $scriptargs 1 end]
set wants_help 0
foreach h $::punkboot::help_flags {
if {$h in $subargs} {set wants_help 1 ; break}
}
if {$subcommand eq "help"} {
set do_help 1
if {[llength $subargs]} {
@ -3932,17 +3950,6 @@ if {$::punkboot::punkargs_ok} {
set help_subject [dict get $argd values subject]
set help_words [lrange $subargs 1 end]
}
} elseif {$wants_help} {
set do_help 1
set help_subject $subcommand
#'<subcommand> ?arg ...? -help' - the same contract as
#'make.tcl help <subcommand> ?arg ...?': the remaining words are the
#subcommand's command line, with the help flag itself removed
#(wherever it appeared).
set help_words [list]
foreach w $subargs {
if {$w ni $::punkboot::help_flags} {lappend help_words $w}
}
} elseif {$subcommand eq "shell"} {
#declared passthrough: everything after 'shell' goes to the repl unparsed
set ::punkboot::command shell
@ -4035,13 +4042,12 @@ if {$::punkboot::punkargs_ok} {
set ::punkboot::opt_confirm [expr {bool($v)}]
}
default {
if {$a in $::punkboot::help_flags} {
set do_help 1
} else {
puts stderr "Unknown flag: $a\n"
set do_help 1
set help_exitcode 1
}
#help flags are not special (2026-08-05 single-mechanism
#revision): they land here as unknown flags, and the
#resulting help display carries exit 1
puts stderr "Unknown flag: $a\n"
set do_help 1
set help_exitcode 1
}
}
} else {
@ -8645,7 +8651,8 @@ foreach vfstail $vfs_tails {
set changed_unchanged [$vfs_event targetset_source_changes]
set vfs_or_runtime_changed [expr {[llength [dict get $changed_unchanged changed]] || [llength [$vfs_event get_targets_exist]] < [llength [$vfs_event get_targets]]}]
#bake -force bypasses the no-change skip (always re-assemble the selected kits)
set vfs_or_runtime_changed [expr {$::punkboot::opt_force || [llength [dict get $changed_unchanged changed]] || [llength [$vfs_event get_targets_exist]] < [llength [$vfs_event get_targets]]}]
if {$vfs_or_runtime_changed} {
#source .vfs folder has changes
@ -9111,7 +9118,12 @@ foreach vfstail $vfs_tails {
#stamps (and kits) differ run to run. Same principle as the G-117
#embedded records, which keep sha1/size/built in the SIDECAR only -
#time facts belong to punkcheck records and sidecars, not payloads.
#LF translation pinned for the same reason (G-165): the default
#text translation follows the driving interpreter (crlf on windows
#drivers, lf on unix-family ones), which made the stamp's BYTES
#driver-dependent - caught by the G-165 four-driver parity run.
set fd [open [file join $targetvfs punkkit-stamp.toml] w]
chan configure $fd -translation lf
puts -nonewline $fd "[join $lines \n]\n"
close $fd
puts stdout " kit stamp (G-025): punkkit-stamp.toml (project_version=$stamp_project_version vfs=$vfstail runtime=$runtime_fullname boot_core=$core_ver)"

100
src/project_layouts/vendor/punk/project-0.1/src/make.tcl vendored

@ -198,7 +198,6 @@ namespace eval ::punkboot {
variable scriptfolder [file normalize [file dirname [info script]]]
variable foldername [file tail $scriptfolder]
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable help_flags [list -help --help /? -h]
variable known_commands [list bakehouse packages modules libs bake bakelist vfslibs bin info check shell vendorupdate libfetch bootsupport vfscommonupdate projectversion workflow buildsuite tool]
}
@ -2588,17 +2587,18 @@ proc ::punkboot::punkboot_gethelp {args} {
set scriptname [file tail [info script]]
append h "Usage:" \n
append h "" \n
append h " $scriptname -help or $scriptname --help or $scriptname /? or just $scriptname" \n
append h " $scriptname help or just $scriptname" \n
append h " - This help." \n \n
append h " $scriptname bakehouse ?-k? ?-dirty-abort 1|0?" \n
append h " - consumer run from a clean checkout: mints the packages (modules + libs) then bakes kit/zipkit executables to <projectdir>/bin" \n
append h " - refuses uncommitted src by default (-dirty-abort defaults ON: the bakehouse bakes from the committed recipe; pass -dirty-abort 0 to override)" \n
append h " - the optional -k flag will terminate running processes matching the executable being baked (if applicable)" \n
append h " - does NOT run the promotion gates (bootsupport, vfscommonupdate) - on a clean checkout they are already satisfied by the committed tree" \n \n
append h " $scriptname bake ?-k? ?kitname ...?" \n
append h " $scriptname bake ?-k? ?-force? ?kitname ...?" \n
append h " - assemble kit/zipkit executables from the promoted payload (src/vfs) and src/runtime runtimes into <projectdir>/bin" \n
append h " - includes the vfslibs phase; does not re-mint modules/libs and does not run the promotion gates" \n
append h " - with kitname arguments, bakes and deploys only the named configured kits (unknown names error before any bake)" \n \n
append h " - with kitname arguments, bakes and deploys only the named configured kits (unknown names error before any bake)" \n
append h " - -force re-assembles kits whose inputs are unchanged (bypasses the 'no change detected' skip)" \n \n
append h " $scriptname bakelist ?kitname ...?" \n
append h " - list the kit outputs configured in src/runtime/mapvfs.toml: name, kit type, runtime (with presence)," \n
append h " vfs folder and deployed state (bin copy absent/current/stale/nobake vs the src/_bake product)" \n
@ -2655,7 +2655,7 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - zig is OPTIONAL: packages/bake never require this step; without a suitable toolchain the state is" \n
append h " reported and build/test exit nonzero with fetch guidance (bin/punk-getzig.cmd)" \n \n
append h " $scriptname help ?subcommand? ?arg ...?" \n
append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname <subcommand> -help);" \n
append h " - show usage for $scriptname or one of its subcommands (the single help mechanism);" \n
append h " extra words are the subcommand's own command line, dry-run through its declaration - an accepted" \n
append h " line shows the matched action's usage (tool, buildsuite), a rejected line that subcommand's own" \n
append h " usage error (tabled help only)" \n \n
@ -3078,7 +3078,9 @@ set scriptargs $::argv
# G-030: make.tcl dogfoods punk::args.
# Subcommands and their options are declared as punk::args definitions ((script)::punkboot and
# (script)::punkboot::<subcommand>) and parsed with punk::args::parse - 'make.tcl',
# 'make.tcl help ?subcommand?' and 'make.tcl <subcommand> -help' render tabled usage, and invalid
# 'make.tcl help ?subcommand? ?arg ...?' renders tabled usage (the single help mechanism -
# help FLAGS like -help/--help are ordinary arguments since 2026-08-05: they fail whatever
# gate they meet, the usage error itself being documentation), and invalid
# arguments produce punk::args usage errors.
# Degrade rule (hard requirement - see goals/archive/G-030-maketcl-punkargs.md): punk::args is
# guarded. When it (or a definition feature it needs) is unavailable from bootsupport, dispatch
@ -3308,14 +3310,18 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
update contract for keeping it in step with make.tcl behaviour."
help
"
Show tabled usage for make.tcl as a whole, or for a single subcommand.
After the subject, accepts that subcommand's own command line and
dry-runs it through the subcommand's declaration: a line the
subcommand would accept shows the matched action's usage - 'make.tcl
help tool build -test 0 punkzip' shows the tool build form, as does
appending -help to the command line itself ('make.tcl tool build
-test 0 punkzip -help') - while a line it would reject shows the
same usage error the subcommand itself would produce (the error
Show tabled usage for make.tcl as a whole, or for a single
subcommand. This leading 'help' word is the SINGLE help
mechanism: there is no -help flag handling anywhere (a
-help/--help in a command line is an ordinary argument that
fails whatever gate it meets, and that usage error is itself
the documentation).
After the subject, accepts that subcommand's own command line
and dry-runs it through the subcommand's declaration: a line
the subcommand would accept shows the matched action's usage -
'make.tcl help tool build -test 0 punkzip' shows the tool
build form - while a line it would reject shows the same
usage error the subcommand itself would produce (the error
table carries the forms' synopsis, argument rows and per-form
reasons; exit 1). An accepted line is confirmed with a one-line
report of where each word landed ('kitname = punk91 -confirm 0'
@ -3424,6 +3430,14 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
"Terminate running processes matching the executable being built
(if applicable) so the bake can install over it."
}
variable OPT_FORCE {
-force -type none -default 0 -help\
"Re-assemble kits even when no input change is detected.
By default a kit whose vfs/runtime inputs are unchanged since its
last bake is skipped ('no change detected'); -force bypasses that
skip for the selected kits - e.g to compare assembly behaviour
across driving interpreters without touching inputs."
}
#bakehouse defaults the dirty gate ON:
#the bakehouse bakes from the committed recipe (G-112 two-persona model).
variable OPT_DIRTYABORT_ON {
@ -3503,6 +3517,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
{${[dict get $HELPTEXTS bakehouse]}}
@opts
${$OPT_FORCEKILL}
${$OPT_FORCE}
${$OPT_DIRTYABORT_ON}
${$OPT_CONFIRM}
@values -min 0 -max 0
@ -3548,6 +3563,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
{${[dict get $HELPTEXTS bake]}}
@opts
${$OPT_FORCEKILL}
${$OPT_FORCE}
${$OPT_DIRTYABORT}
${$OPT_CONFIRM}
@values -min 0 -max -1
@ -3792,11 +3808,12 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
'bin' and 'modules' folders at the same level as 'src'.
General usage: make.tcl <subcommand> ?flags?
'make.tcl help <subcommand> ?arg ...?' or 'make.tcl <subcommand>
?arg ...? -help' shows a subcommand's own usage - the words are
dry-run through the subcommand's declaration, so an accepted
command line shows the matched action's usage (tool, buildsuite)
and a rejected one shows that subcommand's own usage error.
'make.tcl help <subcommand> ?arg ...?' shows a subcommand's own
usage (the single help mechanism - there is no -help flag) -
the words after the subject are dry-run through the
subcommand's declaration, so an accepted command line shows
the matched action's usage (tool, buildsuite) and a rejected
one shows that subcommand's own usage error.
Interactive y/n confirmations can be driven non-interactively
with the -confirm flag declared on the relevant subcommands.}
punk::args::define {
@ -3901,21 +3918,22 @@ set ::punkboot::opt_kitnames [list] ;#G-121: requested kit names for bake/bak
if {$::punkboot::punkargs_ok} {
set first [lindex $scriptargs 0]
if {![llength $scriptargs] || $first in $::punkboot::help_flags} {
if {![llength $scriptargs]} {
set do_help 1
} else {
#validate/resolve the subcommand word via the top-level definition
#(allows unambiguous prefixes; unknown subcommands get a punk::args usage error)
#(allows unambiguous prefixes; unknown subcommands get a punk::args
#usage error - INCLUDING help flags like -help/--help since the
#2026-08-05 single-mechanism revision: the leading 'help' word and
#the bare invocation are the help surface, and a '-help' anywhere
#else is an ordinary argument that fails whatever gate it meets,
#with the usage error itself carrying the documentation)
if {[catch {punk::args::parse [list $first] -errorstyle $::punkboot::errstyle withid (script)::punkboot} argd]} {
puts stderr $argd
exit 1
}
set subcommand [dict get $argd leaders subcommand]
set subargs [lrange $scriptargs 1 end]
set wants_help 0
foreach h $::punkboot::help_flags {
if {$h in $subargs} {set wants_help 1 ; break}
}
if {$subcommand eq "help"} {
set do_help 1
if {[llength $subargs]} {
@ -3932,17 +3950,6 @@ if {$::punkboot::punkargs_ok} {
set help_subject [dict get $argd values subject]
set help_words [lrange $subargs 1 end]
}
} elseif {$wants_help} {
set do_help 1
set help_subject $subcommand
#'<subcommand> ?arg ...? -help' - the same contract as
#'make.tcl help <subcommand> ?arg ...?': the remaining words are the
#subcommand's command line, with the help flag itself removed
#(wherever it appeared).
set help_words [list]
foreach w $subargs {
if {$w ni $::punkboot::help_flags} {lappend help_words $w}
}
} elseif {$subcommand eq "shell"} {
#declared passthrough: everything after 'shell' goes to the repl unparsed
set ::punkboot::command shell
@ -4035,13 +4042,12 @@ if {$::punkboot::punkargs_ok} {
set ::punkboot::opt_confirm [expr {bool($v)}]
}
default {
if {$a in $::punkboot::help_flags} {
set do_help 1
} else {
puts stderr "Unknown flag: $a\n"
set do_help 1
set help_exitcode 1
}
#help flags are not special (2026-08-05 single-mechanism
#revision): they land here as unknown flags, and the
#resulting help display carries exit 1
puts stderr "Unknown flag: $a\n"
set do_help 1
set help_exitcode 1
}
}
} else {
@ -8645,7 +8651,8 @@ foreach vfstail $vfs_tails {
set changed_unchanged [$vfs_event targetset_source_changes]
set vfs_or_runtime_changed [expr {[llength [dict get $changed_unchanged changed]] || [llength [$vfs_event get_targets_exist]] < [llength [$vfs_event get_targets]]}]
#bake -force bypasses the no-change skip (always re-assemble the selected kits)
set vfs_or_runtime_changed [expr {$::punkboot::opt_force || [llength [dict get $changed_unchanged changed]] || [llength [$vfs_event get_targets_exist]] < [llength [$vfs_event get_targets]]}]
if {$vfs_or_runtime_changed} {
#source .vfs folder has changes
@ -9111,7 +9118,12 @@ foreach vfstail $vfs_tails {
#stamps (and kits) differ run to run. Same principle as the G-117
#embedded records, which keep sha1/size/built in the SIDECAR only -
#time facts belong to punkcheck records and sidecars, not payloads.
#LF translation pinned for the same reason (G-165): the default
#text translation follows the driving interpreter (crlf on windows
#drivers, lf on unix-family ones), which made the stamp's BYTES
#driver-dependent - caught by the G-165 four-driver parity run.
set fd [open [file join $targetvfs punkkit-stamp.toml] w]
chan configure $fd -translation lf
puts -nonewline $fd "[join $lines \n]\n"
close $fd
puts stdout " kit stamp (G-025): punkkit-stamp.toml (project_version=$stamp_project_version vfs=$vfstail runtime=$runtime_fullname boot_core=$core_ver)"

43
src/vfs/_vfscommon.vfs/modules/commandstack-0.7.0.tm → src/vfs/_vfscommon.vfs/modules/commandstack-0.7.1.tm

@ -11,6 +11,12 @@
# - oo dispatch features may be a better implementation - especially for allowing undoing command renames in the middle of a stack.
# - document that replacement command should use 'commandstack::get_next_command <cmd> <renamer>' for delegating to command as it was prior to rename
#changes:
#2026-08-04 (G-160 follow-on 2)
# - reload contract: known_renamers and debug are now info-exists guarded
# like the rest of the module state (all_stacks, renamer_command_tokens,
# token_implementations) - previously a module re-source reset both while
# the stacks/tokens survived, stranding live stacks whose renamers were
# no longer known (the removal forms gate on known_renamers membership)
#2026-08-04 (G-160 follow-on)
# - remove_rename convenience forms (the long-parked code todos):
# pop_rename (pop a renamer's topmost entry - per command, or searched
@ -63,12 +69,23 @@
namespace eval commandstack {
#Reload contract: ALL module state survives a re-source (only proc
#definitions refresh) - every state variable here is info-exists guarded.
#known_renamers in particular must survive with all_stacks: stack records
#reference renamer strings and the removal forms gate on known_renamers
#membership, so a reset would strand live stacks. ('variable name <val>'
#is no guard - with a value it re-assigns on every re-source.)
variable all_stacks
variable debug
set debug 0
#the strings the vendored cooperating packages actually pass as -renamer
#(reconciled 2026-08-03 - the historical defaults ::packagetrace ::packageSuppress matched no actual registration)
variable known_renamers [list packagetrace packagesuppress]
if {![info exists debug]} {
set debug 0
}
variable known_renamers
if {![info exists known_renamers]} {
#the strings the vendored cooperating packages actually pass as -renamer
#(reconciled 2026-08-03 - the historical defaults ::packagetrace ::packageSuppress matched no actual registration)
set known_renamers [list packagetrace packagesuppress]
}
if {![info exists all_stacks]} {
#don't wipe it
set all_stacks [dict create]
@ -239,6 +256,11 @@ namespace eval commandstack {
when enabled; errors always raise)
Notes
- Reload contract: a module re-source (re-source / package forget+require
during development) refreshes proc definitions only - ALL module state
survives: all_stacks, renamer_command_tokens, token_implementations,
known_renamers and debug are each info-exists guarded at load, so live
stacks never lose the state that references them.
- The renamer string defaults to the calling namespace.
- Cooperating packages are identified by a magic comment in installed proc
bodies: IMPLEMENTOR_<renamer>! (added automatically by rename_command).
@ -264,7 +286,8 @@ namespace eval commandstack {
argument. When enabled, rename_command and remove_rename
report progress and informational warnings on stderr - with
debug off (the default) they emit nothing (errors are still
raised as errors)."
raised as errors). The setting survives a module re-source
(the reload contract - see commandstack::help)."
@values -min 0 -max 1
on_off -type boolean -optional 1 -help -&
"New debug state. Omit to query the current state."
@ -936,8 +959,8 @@ namespace eval commandstack {
gated by known_renamers (unlike remove_rename) - renamers
evidenced by the stack records are registered into
known_renamers first, mirroring rename_command, so a
restore still works after state loss such as a module
re-source that reset known_renamers while stacks survived.
restore still works after state loss such as known_renamers
being cleared while stacks survived.
Returns the number of records removed: 0 when the command
has no live stack (never renamed, or only the empty residue
entry - prune that with Delete_stack).}
@ -959,8 +982,8 @@ namespace eval commandstack {
if {![llength $stack]} {
return 0
}
#a repair operation must not be gated by known_renamers (which a module
#re-source may have reset while stacks survived) - register the renamers
#a repair operation must not be gated by known_renamers (which state
#surgery may have lost while stacks survived) - register the renamers
#the stack evidences, mirroring rename_command
foreach record $stack {
set record_renamer [dict get $record renamer]
@ -1235,7 +1258,7 @@ namespace eval ::punk::args::register {
}
package provide commandstack [namespace eval commandstack {
set version 0.7.0
set version 0.7.1
}]

124
src/vfs/_vfscommon.vfs/modules/punk/args-0.14.0.tm → src/vfs/_vfscommon.vfs/modules/punk/args-0.15.1.tm

@ -8,7 +8,7 @@
# (C) 2024
#
# @@ Meta Begin
# Application punk::args 0.14.0
# Application punk::args 0.15.1
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -18,7 +18,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::args 0 0.14.0]
#[manpage_begin punkshell_module_punk::args 0 0.15.1]
#[copyright "2024"]
#[titledesc {args parsing}] [comment {-- Name section and table of contents description --}]
#[moddesc {args to nested dict of opts and values}] [comment {-- Description at end of page heading --}]
@ -69,6 +69,7 @@
#[para]The lines beginning with @ are usually optional in most cases and can be used to set defaults and some extra controls
#[para] - the above example would work just fine with only the -<optionname> lines, but would allow zero filenames to be supplied as no -min value is set for @values
#[para]valid @ directive lines begin with @id @cmd @form @leaders @opts @values @formdisplay @doc @examples (plus the bare directives @dynamic and @normalize)
#[para]A @values section may declare -flagvalues 1: unmatched dash-led words in option position are then consumed as trailing values instead of raising invalidoption (declared options still match first; the '--' end-of-options marker is unaffected). This makes forms such as a '--help' form robust to ignored trailing words of any shape.
#[para]lines beginning with a dash define options - a name can optionally be given to each trailing positional argument.
#[para]If no names are defined for positional arguments, they will end up in the values key of the dict with numerical keys starting at zero.
#[para]e.g the result from the punk::args::parse call above may be something like:
@ -1330,6 +1331,7 @@ tcl::namespace::eval punk::args {
} VAL_MIN "" {*}{
} VAL_MAX "" {*}{
} VAL_UNNAMED false {*}{
} VAL_FLAGVALUES false {*}{
} VALSPEC_DEFAULTS $valdirective_defaults {*}{
} VAL_CHECKS_DEFAULTS {} {*}{
} FORMDISPLAY [tcl::dict::create] {*}{
@ -2948,6 +2950,14 @@ tcl::namespace::eval punk::args {
}
dict set F $fid VAL_UNNAMED $v
}
-flagvalues {
#G-150 follow-on: unmatched dash-led words in option position are
#consumed as trailing values instead of raising invalidoption.
if {![string is boolean -strict $v]} {
error "punk::args::resolve - invalid type of value '$v' for key '$k' in @values line. Must be boolean @id:$DEF_definition_id"
}
dict set F $fid VAL_FLAGVALUES $v
}
default {
set known { -type -range -typeranges
-min -form -minvalues -max -maxvalues
@ -2960,7 +2970,7 @@ tcl::namespace::eval punk::args {
-optional -allow_ansi -validate_ansistripped -strip_ansi -multiple
-multipleunique -choicemultipleunique -choicemultipleuniqueset
-regexprepass -regexprefail -regexprefailmsg -validationtransform
-unnamed
-unnamed -flagvalues
}
error "punk::args::resolve - unrecognised key '$k' in @values line. Known keys: $known @id:$DEF_definition_id"
}
@ -6548,6 +6558,16 @@ tcl::namespace::eval punk::args {
The verdict is conservative for arguments whose -type is a
multi-member clause: input ending inside such a clause reports
invalid even when the partial clause words match.
Reservation-stolen flag words (G-164): when the valmin reservation
seats a word that literally names a declared required option into
a required value slot at end-of-input, the viability probe
re-parses with the stolen word(s) consumed as the options they
name and grants 'incomplete' on pure exhaustion after that
re-seating (e.g a form requiring flag -t plus a value classes
viable for {-t}, since {-t foo} parses). This re-probe does not
extend to a stolen word naming a value-TAKING required option,
which stays conservative (invalid). Runtime seating is unchanged
in both cases - the trailing-flag-as-value rule is load-bearing.
Arguments matching several forms cleanly raise errorcode
PUNKARGS VALIDATION {multipleformmatches forms <fidlist>}
Consumers (form narrowing, interactive completion) should select
@ -7753,6 +7773,16 @@ tcl::namespace::eval punk::args {
{literal(elseif) expr script}): input ending
inside such a clause reports invalid even when
the partial clause words match.
Reservation-stolen flag words (G-164): when the
valmin reservation seats a word that literally
names a declared required option into a required
value slot at end-of-input, the viability probe
re-parses with the stolen word(s) consumed as the
options they name and grants 'incomplete' on pure
exhaustion after that re-seating (a stolen word
naming a value-TAKING required option stays
conservative). Runtime seating is unchanged - the
trailing-flag-as-value rule is load-bearing.
Single-form parses report their one form; when
multi-form candidacy ran, every attempted form is
reported - partial-arglist consumers (e.g command
@ -9664,11 +9694,32 @@ tcl::namespace::eval punk::args {
#consumed, which is the soundness backstop: a probe raise means something already
#supplied contradicts the form. Callers only test raised-vs-returned; the
#returned dict of a probe run is partial and must be discarded.
#G-164 refinement (alternative-allocation re-probe): when the valmin
#reservation seats a word that literally names a declared required option
#into a required value slot at end-of-input, and the resulting optionmissing
#stands in the probe (a value word was consumed, defeating the position
#guard), the probe re-parses with the stolen word(s) consumed as the options
#they name (a {1 reseat_words} viabilitycheck argument); pure end-of-input
#exhaustion after that re-seating means the words are a valid prefix (a
#completing suffix exists - e.g {-t} on a form requiring flag -t plus a
#value: {-t foo} parses). Runtime seating is unchanged - the re-probe runs
#only inside the probe. A stolen word naming a value-TAKING required option
#stays conservative: re-seated, it seats as a value again at end-of-input
#(the trailing-flag-as-value rule) and the optionmissing stands.
#Known conservatism (documented in the parse/parse_status contracts): input
#ending INSIDE a multi-member type clause raises (clause allocation cannot
#affirm the partial words) - such prefixes report non-viable even when the
#partial clause words match.
proc private::get_dict_form {argspecs fid rawargs {viabilitycheck 0}} {
#G-164: the probe's alternative-allocation re-probe invokes this proc with a
#2-element viabilitycheck {1 reseat_words} - words the valmin reservation
#stole into required value slots in the primary probe that literally name
#declared required options; on this pass they are consumed as the options
#they name (see the optionmissing site far below).
set reseat_words [list]
if {[llength $viabilitycheck] > 1} {
lassign $viabilitycheck viabilitycheck reseat_words
}
#puts "-arg_info->$arg_info"
set flagsreceived [list] ;#for checking if required flags satisfied
set solosreceived [list]
@ -9727,6 +9778,7 @@ tcl::namespace::eval punk::args {
set VAL_MIN [dict get $formdict VAL_MIN]
set VAL_MAX [dict get $formdict VAL_MAX]
set VAL_UNNAMED [dict get $formdict VAL_UNNAMED]
set VAL_FLAGVALUES [dict get $formdict VAL_FLAGVALUES]
set VALSPEC_DEFAULTS [dict get $formdict VALSPEC_DEFAULTS]
set VAL_CHECKS_DEFAULTS [dict get $formdict VAL_CHECKS_DEFAULTS]
@ -9739,6 +9791,10 @@ tcl::namespace::eval punk::args {
#a word consumed by a later slot leaves its entry unused.
set alloc_rejections [dict create]
#G-164: words the valmin reservation seats as values without ever comparing
#them to the declared options (recorded for the probe's re-probe trigger)
set reservation_seated [list]
if {$VAL_MIN eq ""} {
set valmin 0
#set VAL_MIN 0
@ -10136,10 +10192,19 @@ tcl::namespace::eval punk::args {
set remaining_args_including_this [expr {[llength $remaining_rawargs] - $i}]
#lowest valmin is 0
if {$remaining_args_including_this <= $valmin} {
# if current arg is -- it will pass through as a value here
set arglist [lrange $remaining_rawargs 0 $i-1]
set post_values [lrange $remaining_rawargs $i end]
break
if {[llength $reseat_words] && [lindex $remaining_rawargs $i] in $reseat_words} {
#G-164 re-probe: this word literally names a declared required
#option the reservation stole into a value slot on the primary
#probe pass - fall through to consume it as the option it names
} else {
# if current arg is -- it will pass through as a value here
set arglist [lrange $remaining_rawargs 0 $i-1]
set post_values [lrange $remaining_rawargs $i end]
#G-164: seated as values by the valmin reservation - never
#compared to the declared options
set reservation_seated $post_values
break
}
}
set a [lindex $remaining_rawargs $i]
set a1 [string index $a 0]
@ -10623,12 +10688,14 @@ tcl::namespace::eval punk::args {
} else {
#starts with - but unmatched option flag
#comparison to valmin already done above
if {$valmax ne -1 && $remaining_args_including_this <= $valmax} {
if {$VAL_FLAGVALUES || ($valmax ne -1 && $remaining_args_including_this <= $valmax)} {
#todo - look at optspec_default and see if solo/vs opt-val pair
#we may need to lookahead by 2 regarding valmax valmin
#even if optany - assume an unknown within the space of possible values is a value
#unmatched option in right position to be considered a value - treat like eopts
#(unconditional when the form declares @values -flagvalues 1 - dash-led
#words are values by declaration, so no invalidoption is possible here)
#review - document that an unspecified arg within range of possible values will act like eopts --
set arglist [lrange $remaining_rawargs 0 $i-1]
set post_values [lrange $remaining_rawargs $i end]
@ -11391,6 +11458,45 @@ tcl::namespace::eval punk::args {
set api_opt_required [lmap v $OPT_REQUIRED {lindex [split $v |] end}]
if {[llength [set missing [punk::args::system::punklib_ldiff $api_opt_required $flagsreceived]]]} {
#G-164 viability probe, alternative-allocation re-probe: this
#optionmissing may be an artifact of the valmin reservation having
#stolen flag words into required value slots at end-of-input (a
#value word was consumed, so the probe's position guard above lets
#the absence stand - e.g {-t} on a form requiring flag -t plus a
#value, where {-t foo} parses). When a missing required option is
#literally named by a reservation-seated word, re-probe with the
#stolen word(s) consumed as the options they name: if the
#re-seated parse then fails only on pure end-of-input exhaustion
#(or not at all), the supplied words ARE a valid prefix of the
#form and the probe passes. Options still missing after the
#re-seating are ordinary exhaustion in the re-probe (no value
#words precede them) - they do not block the pass. Runtime
#seating is unchanged - this runs only inside the probe, and only
#via reservation-seated words (the trailing-flag-as-value rule is
#load-bearing).
if {$viabilitycheck && [llength $reservation_seated]} {
set reseat [list]
foreach m $missing {
set spellings [list]
if {[dict exists $lookup_optset $m]} {
foreach optdef [split [dict get $lookup_optset $m] |] {
lappend spellings [string trimright $optdef =]
}
} else {
lappend spellings $m
}
foreach w $reservation_seated {
if {$w in $spellings} {
lappend reseat $w
break
}
}
}
if {[llength $reseat] && ![catch {private::get_dict_form $argspecs $fid $rawargs [list 1 $reseat]}]} {
#re-seated parse is exhausted-only or clean: valid prefix
return
}
}
set full_missing [list]
foreach m $missing {
if {[dict exists $lookup_optset $m]} {
@ -14259,7 +14365,7 @@ package provide punk::args [tcl::namespace::eval punk::args {
tcl::namespace::path {::punk::args::lib ::punk::args::system}
variable pkg punk::args
variable version
set version 0.14.0
set version 0.15.1
}]
return

25
src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkboot-0.2.0.tm → src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkboot-0.3.0.tm

@ -6,7 +6,7 @@
# (C) 2026
#
# @@ Meta Begin
# Application punk::args::moduledoc::punkboot 0.2.0
# Application punk::args::moduledoc::punkboot 0.3.0
# Meta platform tcl
# Meta license BSD
# @@ Meta End
@ -116,11 +116,12 @@ tcl::namespace::eval punk::args::moduledoc::punkboot {
summary one-line summary shown beside the name in the
top-level 'help' table
argsid punk::args definition id documenting the
subcommand: 'help <name>' and '<name> -help'
render it. Without an argsid, '<name> -help'
stays FULL passthrough to the handler (the app
may do its own -help) and 'help <name>' shows
the summary line.
subcommand: 'help <name>' renders it. Without an
argsid, 'help <name>' shows the summary line.
(The launcher never intercepts a subcommand's own
arguments - help flags included, 2026-08-05
single-mechanism revision - so an app may do its
own -help handling regardless.)
package package to require (guarded) before resolving
argsid - typically a moduledoc-style package
carrying the definition
@ -139,14 +140,16 @@ tcl::namespace::eval punk::args::moduledoc::punkboot {
unknownfirst 'script' treats a non-subcommand first
argument that names an existing file (or a
lib:* scriptlib reference) as a script
invocation and refuses anything else with
invocation, runs a first argument of exactly
'-e' as the G-077 one-liner, and refuses
anything else - help flags included - with
usage (default; the refusal is G-032 and
degrades to always-treat-as-script without
punk::args), or the name of a known
subcommand to receive the whole arglist
(tool-style kits - these also keep help
FLAGS routed to that subcommand; the 'help'
word remains the launch-surface entry)
(tool-style kits; in every kit the 'help'
word is the launch-surface help entry -
help FLAGS are never intercepted)
All dicts default to empty: an undeclared thin main yields
exactly the punkshell launch behaviour."
@ -250,6 +253,6 @@ namespace eval ::punk::args::register {
## Ready
package provide punk::args::moduledoc::punkboot [tcl::namespace::eval punk::args::moduledoc::punkboot {
variable version
set version 0.2.0
set version 0.3.0
}]
return

125
src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkexe-0.5.0.tm → src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkexe-0.6.0.tm

@ -8,7 +8,7 @@
# (C) 2026
#
# @@ Meta Begin
# Application punk::args::moduledoc::punkexe 0.5.0
# Application punk::args::moduledoc::punkexe 0.6.0
# Meta platform tcl
# Meta license BSD
# @@ Meta End
@ -18,7 +18,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::args::moduledoc::punkexe 0 0.5.0]
#[manpage_begin punkshell_module_punk::args::moduledoc::punkexe 0 0.6.0]
#[copyright "2026"]
#[titledesc {punk executable launch documentation}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
@ -38,8 +38,9 @@
#[para] buildinfo, help (G-118 item 10). The launcher help/parse wiring (G-032)
#[para] renders and parses from them at dispatch time: the boot core's help
#[para] surface clones (script)::punkexe / (script)::punkexe::help per kit with
#[para] the live subcommand set, and per-subcommand ids back '<subcommand>
#[para] -help' and the declared argument gates. Id convention follows the
#[para] the live subcommand set, and per-subcommand ids back the 'help
#[para] <subcommand>' renders and the declared argument gates (the single help
#[para] mechanism - -help flag forms withdrawn 2026-08-05). Id convention follows the
#[para] make.tcl precedent for script-level surfaces: (script)::punkexe and
#[para] (script)::punkexe::<subcommand>, with explicit @form -synopsis overrides
#[para] since a constructed id is not an invocable command name.
@ -115,20 +116,24 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
the refusal degrades to the historic treat-as-script
behaviour when punk::args is unavailable in the kit/context).
Help (G-032): 'help' or '-help' as the first non-mode
argument renders this usage; 'help <subcommand>' or
'<subcommand> -help' renders a subcommand's own usage
(see the 'help' choice below for the carve-outs). A kit
whose thin main routes unknown first arguments to a project
subcommand (a tool-style kit) keeps routing help FLAGS to
that subcommand - its launch surface stays reachable via the
'help' subcommand word. Project-registered subcommands
appear in the rendered table by registration alone, with
the summaries and definition ids their registrations
declare (see punk::args::moduledoc::punkboot's registration
definition). These built-in launch behaviours are subject
to the thin main's declared launch defaults - the rendered
per-kit table is the authority for a given executable."
Help (G-032; single mechanism since 2026-08-05): 'help' as
the first non-mode argument renders this usage;
'help <subcommand> ?arg ...?' renders a subcommand's own
usage - words after the subject are accepted and ignored,
so 'help' can simply be prepended to an existing command
line. There is deliberately NO -help flag handling: a
leading '-help' falls under the unknown-first-argument rule
above (its refusal shows this usage on stderr), and
'-help' after a subcommand is that subcommand's own
argument (tclsh keeps full stock dash parity; shellspy's
processor handles -help/--help/help itself).
Project-registered subcommands appear in the rendered table
by registration alone, with the summaries and definition
ids their registrations declare (see
punk::args::moduledoc::punkboot's registration definition).
These built-in launch behaviours are subject to the thin
main's declared launch defaults - the rendered per-kit
table is the authority for a given executable."
@form -synopsis "<punkexe> ?packagemode? ?subcommand? ?arg ...?"
@leaders -min 0 -max 2
packagemode -type string -optional 1 -default internal -help -&
@ -212,10 +217,10 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
lives on the punk-level launch surfaces instead (G-077): use
'<punkexe> -e <script> ?arg ...?' or '<punkexe> script -e
<script> ?arg ...?', never this subcommand.
G-032 help carve-out (the single deliberate exception): a help
flag (-help/--help/-h//?) as the SOLE argument renders this
usage and exits 0; the same flag accompanied by ANY further
argument keeps the stock all-args-to-::argv semantics above.
The parity has no help-flag exception (the 2026-08-05
single-mechanism revision withdrew the earlier sole-argument
'-help' carve-out): use '<punkexe> help tclsh' for this
usage.
A scriptfile of the form 'lib:name' is refused with a pointer to
the 'script' subcommand: scriptlib resolution is a punk
@ -299,10 +304,10 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
#Sibling subcommands: summary-level contracts. These are declared
#PASSTHROUGH surfaces (G-032 decision): their arguments belong to the
#script/app they launch, so dispatch never parses them - only the
#first-argument help flag is intercepted (see each -help body).
#buildinfo below is the dispatch-parsed subcommand (exact @values
#contract enforced through punk::args).
#script/app they launch, so dispatch never parses NOR intercepts them
#(-help flag interception withdrawn 2026-08-05 - 'help <subcommand>'
#is the help route). buildinfo below is the dispatch-parsed
#subcommand (exact @values contract enforced through punk::args).
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
lappend PUNKARGS [list {
@id -id (script)::punkexe::script
@ -336,9 +341,9 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
same one-liner is available at the top level as
'<punkexe> -e <script> ?arg ...?'.
A help flag (-help/--help/-h//?) as the FIRST argument renders
this usage and exits 0 (G-032); in any later position it is an
ordinary script argument."
Help flags are ordinary script arguments in every position
(no interception - 2026-08-05 single-mechanism revision):
use '<punkexe> help script' for this usage."
@form -form {file} -synopsis "<punkexe> script ?scriptfile|lib:name? ?arg ...?"
@values -min 0 -max -1
scriptfile -type string -optional 1 -help -&
@ -366,8 +371,8 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
arguments, a script can be run first with the interactive
shell maintained afterwards. This is also the default when the
executable is launched with no arguments at all.
A help flag (-help etc) as the FIRST argument renders this
usage and exits 0 (G-032)."
All arguments pass through untouched; use '<punkexe> help
shell' for this usage."
@form -synopsis "<punkexe> shell ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
@ -386,8 +391,8 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
stdin are supported (e.g: echo 'puts hello' | <punkexe> punk)
and stderr is kept clean of launch chatter so exec-style
callers do not misreport failure.
A help flag (-help etc) as the FIRST argument renders this
usage and exits 0 (G-032)."
All arguments pass through untouched; use '<punkexe> help
punk' for this usage."
@form -synopsis "<punkexe> punk ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
@ -401,11 +406,10 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
"Experimental shellspy command-line processor." -&
-help -&
"Pass all arguments to the experimental shellspy command-line
processor (app-shellspy). Note: 'shellspy' is only this launch
subcommand - it is not the project name (the project is
punkshell).
A help flag (-help etc) as the FIRST argument renders this
usage and exits 0 (G-032)."
processor (app-shellspy) - including help flags: the
processor handles -help/--help/help itself. Note: 'shellspy'
is only this launch subcommand - it is not the project name
(the project is punkshell)."
@form -synopsis "<punkexe> shellspy ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
@ -443,11 +447,10 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
stamp_present=0 marks an unstamped kit; fields of absent
notions are omitted rather than invented.
Arguments are refused: exit 2 with a one-line refusal plus
this usage as a punk::args error table on stderr (G-032; the
table degrades away when punk::args is unavailable). A help
flag (-help etc) as the first argument renders this usage and
exits 0.
Arguments are refused - help flags included: exit 2 with a
one-line refusal plus this usage as a punk::args error table
on stderr (G-032; the table degrades away when punk::args is
unavailable). Use '<punkexe> help buildinfo' for this usage.
In-process equivalent (no exec): the 'buildinfo' shell command
/ punk::buildinfo::report."
@ -468,25 +471,26 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
-summary -&
"Show usage for the launch surface or one of its subcommands (G-032)." -&
-help -&
"Render launch usage on stdout and exit 0 (G-032).
"Render launch usage on stdout and exit 0 (G-032). This is
the launcher's SINGLE help mechanism (the punkshell
'i <cmd> ...' idiom; -help flag forms were withdrawn
2026-08-05 - help flags are never intercepted anywhere in
the dispatch).
With no argument: the top-level table - the package modes and
every subcommand this kit's launch surface carries (the
built-in family plus project-registered subcommands, each
with its one-line summary).
With a subcommand argument: that subcommand's own usage. For
a project-registered subcommand this is the punk::args
definition id its registration declares; a registration
without one gets its registered summary line (such a
subcommand may implement its own -help handling - the
launcher does not intercept its arguments).
'<punkexe> -help' renders the same top-level table on kits
whose unknown-first-argument default is 'script';
'<punkexe> <subcommand> -help' renders the subcommand usage
(see the top-level definition for the tool-style-kit and
tclsh carve-outs).
With a subcommand argument: that subcommand's own usage.
Words after the subject are accepted and ignored, so 'help'
can simply be prepended to an existing command line
('<punkexe> help tclsh somefile somearg' shows the tclsh
usage). For a project-registered subcommand the rendered
usage is the punk::args definition id its registration
declares; a registration without one gets its registered
summary line (such a subcommand may implement its own -help
handling - the launcher does not touch its arguments).
Degradation (G-030 doctrine): when punk::args, these
definitions, or the rendering stack are unavailable in the
@ -497,12 +501,15 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
The subject choices below are the built-in family; in a live
kit the valid (and prefix-resolvable) choices are that kit's
actual subcommand set."
@form -synopsis "<punkexe> help ?subcommand?"
@values -min 0 -max 1
@form -synopsis "<punkexe> help ?subcommand? ?arg ...?"
@values -min 0 -max -1
subject -type string -optional 1 -choices {tclsh script shell punk shellspy buildinfo help} -help -&
"Subcommand to show usage for. In a kit the valid choices are
the live launch surface: the built-ins plus any
project-registered subcommands."
arg -type any -optional 1 -multiple 1 -help -&
"Accepted and ignored (reserved): the subject's own command
line may be left in place when prepending 'help'."
}]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
}
@ -527,7 +534,7 @@ namespace eval ::punk::args::register {
package provide punk::args::moduledoc::punkexe [tcl::namespace::eval punk::args::moduledoc::punkexe {
variable pkg punk::args::moduledoc::punkexe
variable version
set version 0.5.0
set version 0.6.0
}]
return

28
src/vfs/_vfscommon.vfs/modules/punk/auto_exec-0.1.0.tm → src/vfs/_vfscommon.vfs/modules/punk/auto_exec-0.1.1.tm

@ -8,7 +8,7 @@
# (C) 2026
#
# @@ Meta Begin
# Application punk::auto_exec 0.1.0
# Application punk::auto_exec 0.1.1
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -119,9 +119,15 @@ tcl::namespace::eval punk::auto_exec {
@form -form {show_or_set}
@opts -min 0 -max 0
@values -min 0 -max -1
name -type string -multiple 1 -optional 1 -default {} -help\
name -type string -multiple 1 -optional 1 -default {} -regexprefail {^-} -help\
"One or more autoexec command names to set.
If no names are provided, then all autoexec commands in the hash table will be shown."
If no names are provided, then all autoexec commands in the hash table will be shown.
Names may not begin with a dash: a flag-led line selects a flag form
instead (-r/-t/-d/--help), mirroring the handler's first-word dispatch.
(G-150: the constraint makes a bare punk::args::parse of a flag-led line
select its flag form uniquely instead of reporting an ambiguous match
with this form, so declaration-driven consumers such as 'i hash -r'
form narrowing work without handler involvement.)"
#---------------------
@form -form {rehash}
@opts -min 1 -max 1
@ -156,13 +162,17 @@ tcl::namespace::eval punk::auto_exec {
#-l (build a list of hash -p <path> <name> entries for all autoexec commands that can be used in a script to pre-populate the hash table without needing to call auto_execok for each command at runtime)
#---------------------
@form -form {help}
@opts -min 1 -max 1 -anyopts 1
@opts -min 1 -max 1
--help -type none -optional 0 -help\
"Display usage information for this command."
"Display usage information for this command.
Must be the first word after 'hash'; trailing words are
consumed as ignored values."
@values -min 0 -max -1
@values -min 0 -max -1 -flagvalues 1
ignored -type any -multiple 1 -optional 1 -help\
"Additional arguments that are ignored when --help is used"
"Additional arguments that are ignored when --help is used.
Dash-led words are consumed as values here (not treated
as options - @values -flagvalues 1)."
}]
}
@ -221,7 +231,7 @@ tcl::namespace::eval punk::auto_exec {
append result "$nm [set auto_execs($nm)]\n"
} else {
#review
puts stderr "$hash: nm: not found"
puts stderr "hash: $nm: not found"
}
}
if {$result ne ""} {
@ -1002,7 +1012,7 @@ namespace eval ::punk::args::register {
package provide punk::auto_exec [tcl::namespace::eval punk::auto_exec {
variable pkg punk::auto_exec
variable version
set version 0.1.0
set version 0.1.1
}]
return

BIN
src/vfs/_vfscommon.vfs/modules/punk/mix/templates-0.2.0.tm

Binary file not shown.

12
src/vfs/_vfscommon.vfs/modules/punk/ns-0.9.1.tm → src/vfs/_vfscommon.vfs/modules/punk/ns-0.9.2.tm

@ -7,7 +7,7 @@
# (C) 2023
#
# @@ Meta Begin
# Application punk::ns 0.9.1
# Application punk::ns 0.9.2
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -5522,7 +5522,13 @@ y" {return quirkykeyscript}
definition and reflected in the usage display: a complete
valid set renders with the 'info' scheme and the received
arguments marked, an incomplete or invalid set renders with
the 'error' scheme."
the 'error' scheme.
On a flat multi-form command the advisory parse also selects
the displayed form: argument words resolving to exactly one
form narrow the usage display to that form's argument table
(marked in the synopsis), while lines selecting no single
form (no words, an ambiguous match, or words no form
accepts) keep the whole-command render."
}
proc cmdhelp {args} {
set nscaller [uplevel 1 [list ::tcl::namespace::current]]
@ -6728,6 +6734,6 @@ namespace eval ::punk::args::register {
## Ready
package provide punk::ns [tcl::namespace::eval punk::ns {
variable version
set version 0.9.1
set version 0.9.2
}]
return

14
src/vfs/_vfscommon.vfs/modules/punk/tcltestrun-0.4.1.tm → src/vfs/_vfscommon.vfs/modules/punk/tcltestrun-0.4.2.tm

@ -8,7 +8,7 @@
# (C) 2026
#
# @@ Meta Begin
# Application punk::tcltestrun 0.4.1
# Application punk::tcltestrun 0.4.2
# Meta platform tcl
# Meta license BSD
# @@ Meta End
@ -366,7 +366,15 @@ tcl::namespace::eval punk::tcltestrun {
}
if {[string match "==== * FAILED" $ln_trimright]} {
set line_inner [string range $ln 5 end-7] ;#trim leading ====<space> and trailing <space>FAILED
if {[string match "$line_inner*" [dict get $test_case_fail test_openingtext]]} {
#G-161 follow-on (0.4.2): the closing-banner prefix check is an EXACT
#byte compare, never a glob - the raw test name was previously the
#string match pattern, so a name containing glob metacharacters
#(* ? [ ] backslash) failed to match its own opening text: the closing
#banner never closed the failure section and every later event
#including the summary line was swallowed (the G-161 wedge class).
#An empty line_inner keeps the historic match-everything semantics
#(zero-length prefix, as glob "*" did).
if {[string equal -length [string length $line_inner] $line_inner [dict get $test_case_fail test_openingtext]]} {
set test_description [string trim [string range [dict get $test_case_fail test_openingtext] [string length $line_inner] end]]
set test_name [string trim $line_inner]
# add to testcase_fails list
@ -641,7 +649,7 @@ namespace eval ::punk::args::register {
package provide punk::tcltestrun [tcl::namespace::eval punk::tcltestrun {
variable pkg punk::tcltestrun
variable version
set version 0.4.1
set version 0.4.2
}]
return

158
src/vfs/_vfscommon.vfs/modules/punk/zip-0.3.0.tm → src/vfs/_vfscommon.vfs/modules/punk/zip-0.4.0.tm

@ -9,7 +9,7 @@
# (C) 2009 Path Thoyts <patthyts@users.sourceforge.net>
#
# @@ Meta Begin
# Application punk::zip 0.3.0
# Application punk::zip 0.4.0
# Meta platform tcl
# Meta license MIT
# @@ Meta End
@ -19,7 +19,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::zip 0 0.3.0]
#[manpage_begin punkshell_module_punk::zip 0 0.4.0]
#[copyright "2024"]
#[titledesc {Module API}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
@ -1537,7 +1537,16 @@ tcl::namespace::eval punk::zip {
-summary\
"Create a zip archive in 'filename'."\
-help\
"Create a zip archive in 'filename'"
"Create a zip archive in 'filename'.
When the punkzip accelerator is available (see punk::zip::accelerator)
and the call is one it serves identically - directory-scan mode,
archive-relative offsets, no -runtime/-zipkit prefix, whole-tree
globs, ascii comment - the archive is written by punkzip's build
(rooted at the same base, same exclusion set) instead of the
pure-Tcl writer, and verified against this module's own walk
result; any accelerator failure falls back to pure Tcl silently.
punk::zip::last_write_engine records which engine ran."
@opts
-offsettype -default "archive" -choices {archive file}\
-help\
@ -1729,50 +1738,111 @@ tcl::namespace::eval punk::zip {
return ""
}
set zf [open $filename wb]
if {$opts(-runtime) ne ""} {
#todo - strip any existing vfs - option to merge contents.. only if zip attached?
set rt [open $opts(-runtime) rb]
fcopy $rt $zf
close $rt
} elseif {$opts(-zipkit)} {
#TODO - update to zipfs ?
#see modpod
set zkd "#!/usr/bin/env tclkit\n\# This is a zip-based Tcl Module\n"
append zkd "package require vfs::zip\n"
append zkd "vfs::zip::Mount \[info script\] \[info script\]\n"
append zkd "if {\[file exists \[file join \[info script\] main.tcl\]\]} {\n"
append zkd " source \[file join \[info script\] main.tcl\]\n"
append zkd "}\n"
append zkd \x1A
puts -nonewline $zf $zkd
}
#todo - subtract this from the endrec offset
if {$opts(-offsettype) eq "archive"} {
set dataStartOffset [tell $zf] ;#the overall file offset of the start of archive-data //JMN 2024
#G-165 write-path acceleration (mirrors the G-126 read-path seam in
#unzip): hand whole-tree archive WRITING to the vendored punkzip binary
#when this call is one it serves identically - directory-scan mode,
#archive-relative offsets, no -runtime/-zipkit prefix (prefix
#composition is the caller's concern, e.g assemble_zipcat_image),
#default whole-tree globs and an ascii comment. punk::zip's own walk
#above stays authoritative for the member set and the returned names
#(identical to the floor's, order included); punkzip's build re-walks
#with -b rooting at the same base and the same -x exclusion patterns
#(Tcl string match by entry name, both engines). The produced archive
#is read back and its member names compared against the walk result -
#any accelerator failure, unreadable output or mismatch deletes the
#file and falls back to the pure-Tcl writer silently.
#punk::zip::last_write_engine records which engine ran (tcl|accelerated)
#and punk::zip::last_write_note why the accelerator was skipped.
set accelerated 0
variable last_write_engine
variable last_write_note
set last_write_engine tcl
set last_write_note ""
if {$opts(-directory) ne "" && $opts(-offsettype) eq "archive"
&& $opts(-runtime) eq "" && !$opts(-zipkit)
&& [llength [dict get $argd values globs]] == 1
&& [lindex [dict get $argd values globs] 0] eq "*"
&& [string is ascii $opts(-comment)]} {
set acc [accelerator]
if {$acc eq ""} {
set last_write_note "no accelerator binary resolved (see punk::zip::accelerator)"
} else {
set acccmd [list $acc build]
if {$opts(-comment) ne ""} {
lappend acccmd -c $opts(-comment)
}
lappend acccmd -b $base
foreach pat $opts(-exclude) {
lappend acccmd -x $pat
}
lappend acccmd $filename $opts(-directory)
if {[catch {exec {*}$acccmd 2>@1} accout]} {
file delete -force $filename
set last_write_note "accelerator '$acc' failed - fell back to pure tcl: [string range $accout 0 300]"
} elseif {[catch {set accnames [lsort [lmap m [members $filename] {dict get $m name}]]} verr]} {
file delete -force $filename
set last_write_note "accelerator output unreadable - fell back to pure tcl: $verr"
} elseif {$accnames ne [lsort $paths]} {
file delete -force $filename
set last_write_note "accelerator member set mismatch - fell back to pure tcl"
} else {
set accelerated 1
}
}
} else {
set dataStartOffset 0 ;#offsets relative to file - the (old) zipfs mkzip way :/
set last_write_note "call not write-accelerator eligible (non-directory scan, -offsettype file, -runtime/-zipkit prefix, selective globs, or non-ascii comment) - pure tcl"
}
set count 0
set cd ""
if {$accelerated} {
set last_write_engine accelerated
puts stderr "punk::zip::mkzip: accelerated write via $acc (punkzip build -b $base - [llength $paths] members)"
set members $paths
} else {
set zf [open $filename wb]
if {$opts(-runtime) ne ""} {
#todo - strip any existing vfs - option to merge contents.. only if zip attached?
set rt [open $opts(-runtime) rb]
fcopy $rt $zf
close $rt
} elseif {$opts(-zipkit)} {
#TODO - update to zipfs ?
#see modpod
set zkd "#!/usr/bin/env tclkit\n\# This is a zip-based Tcl Module\n"
append zkd "package require vfs::zip\n"
append zkd "vfs::zip::Mount \[info script\] \[info script\]\n"
append zkd "if {\[file exists \[file join \[info script\] main.tcl\]\]} {\n"
append zkd " source \[file join \[info script\] main.tcl\]\n"
append zkd "}\n"
append zkd \x1A
puts -nonewline $zf $zkd
}
set members [list]
foreach path $paths {
#puts $path
lappend members $path
append cd [Addentry $zf $base $path $dataStartOffset] ;#path already includes relpath
incr count
}
set cdoffset [tell $zf]
set endrec [binary format a4ssssiis PK\05\06 0 0 \
$count $count [string length $cd] [expr {$cdoffset - $dataStartOffset}]\
[string length $opts(-comment)]]
append endrec $opts(-comment)
puts -nonewline $zf $cd
puts -nonewline $zf $endrec
close $zf
#todo - subtract this from the endrec offset
if {$opts(-offsettype) eq "archive"} {
set dataStartOffset [tell $zf] ;#the overall file offset of the start of archive-data //JMN 2024
} else {
set dataStartOffset 0 ;#offsets relative to file - the (old) zipfs mkzip way :/
}
set count 0
set cd ""
set members [list]
foreach path $paths {
#puts $path
lappend members $path
append cd [Addentry $zf $base $path $dataStartOffset] ;#path already includes relpath
incr count
}
set cdoffset [tell $zf]
set endrec [binary format a4ssssiis PK\05\06 0 0 \
$count $count [string length $cd] [expr {$cdoffset - $dataStartOffset}]\
[string length $opts(-comment)]]
append endrec $opts(-comment)
puts -nonewline $zf $cd
puts -nonewline $zf $endrec
close $zf
}
set result ""
switch -exact -- $opts(-return) {
@ -1832,7 +1902,7 @@ tcl::namespace::eval punk::zip::lib {
package provide punk::zip [tcl::namespace::eval punk::zip {
variable pkg punk::zip
variable version
set version 0.3.0
set version 0.4.0
}]
return

166
src/vfs/_vfscommon.vfs/punkboot/core.tcl

@ -15,10 +15,13 @@
#'-e' one-liner reclassification to 'script -e') extended by the thin
#main's declared project subcommands (G-031 registration model - see the
#dispatch section near the end of this file). The launch surface documents
#itself via punk::args (G-032): '<punkexe> help ?subcommand?', '-help' and
#'<subcommand> -help' render tabled usage from the moduledoc::punkexe
#definitions plus the live registrations, degrading to a plain subcommand
#list when that stack is unavailable.
#itself via punk::args (G-032; -help flag forms withdrawn 2026-08-05):
#'<punkexe> help ?subcommand? ?arg ...?' - the single help mechanism -
#renders tabled usage from the moduledoc::punkexe definitions plus the live
#registrations, degrading to a plain subcommand list when that stack is
#unavailable. Help flags (-help etc) are never intercepted: a leading one
#falls under the unknown-first-argument refusal (which shows usage on
#stderr), and after a subcommand they are the subcommand's own arguments.
#
#The package_mode surface (documented in full by punk::args::moduledoc::punkexe):
#an optional FIRST launch argument of the form minted|os|internal|src or any
@ -52,7 +55,7 @@
namespace eval ::punkboot {
#boot-core identity (G-031; stamped into kits + reported by the G-025
#buildinfo surfaces)
variable core_version 0.4.0
variable core_version 0.5.0
}
if {![info exists ::punkboot::launch_args]} {
namespace eval ::punkboot [list variable launch_args $::argv]
@ -1523,23 +1526,25 @@ if {![info exists ::punkboot::launch_defaults]} {
namespace eval ::punkboot [list variable boot_state $boot_state_dict]
#---------------------------------------------------------------
#Launcher help + declared-parse machinery (G-032)
#Launcher help + declared-parse machinery (G-032; -help flag forms
#withdrawn 2026-08-05 by user direction - single mechanism)
#The launch surface documents itself through punk::args (definition ids
#from the punk::args::moduledoc::punkexe family):
# <punkexe> help ?subcommand? tabled usage; the top-level table lists
# built-ins AND project-registered
# subcommands with summaries
# <punkexe> -help same as bare 'help' - only on kits whose
# unknownfirst default is 'script' (a
# tool-style kit routes flags to its own
# processor; its surface stays reachable
# via the 'help' word)
# <punkexe> <subcommand> -help the subcommand's tabled usage when -help
# is its FIRST argument (tclsh: only when
# -help is the SOLE argument - stock
# parity keeps every other dash form in
# ::argv; project subcommands: only when
# the registration declares an argsid)
#from the punk::args::moduledoc::punkexe family) via ONE mechanism, the
#leading 'help' word (the punkshell 'i <cmd> ...' idiom):
# <punkexe> help tabled top-level usage; the table
# lists built-ins AND project-registered
# subcommands with summaries
# <punkexe> help <subcommand> ?arg ...?
# the subcommand's tabled usage; words
# after the subject are accepted and
# ignored, so 'help' can be prepended
# to any existing command line
#There is deliberately NO -help/--help flag handling anywhere in the
#dispatch: a leading help-flag word falls under the unknown-first-argument
#rule (whose refusal shows this usage anyway, stderr exit 1), and
#'<subcommand> -help' belongs to the subcommand's own arguments (full
#passthrough - e.g app-shellspy handles -help/--help/help itself; a
#parse-declared subcommand rejects it like any undeclared flag).
#Everything is guarded and lazy (the G-030 degradation doctrine): nothing
#here loads punk::args on a normal boot/dispatch path. When punk::args or
#the moduledoc definitions are unavailable (or PUNKBOOT_PLAIN=1 - same
@ -1554,7 +1559,6 @@ if {![info exists ::punkboot::launch_defaults]} {
#(script)::punkexe::help for subject validation.
#---------------------------------------------------------------
namespace eval ::punkboot {
variable help_flags [list -help --help -h /?]
variable launch_builtin_subcommands [list]
variable launch_subcommands [list]
variable launcher_punkargs_state "" ;#"" unprobed | ok | plain
@ -1680,8 +1684,8 @@ if {![info exists ::punkboot::launch_defaults]} {
proc launcher_helpdef_id {} {
#define (script)::punkexe.launcherhelp: the moduledoc
#(script)::punkexe::help definition with the subject choices
#replaced by the live subcommand set, so 'help <name>' validates
#(and prefix-resolves) against what this kit actually offers.
#replaced by the live subcommand set - the DISPLAY clone rendered
#by 'help help'.
set id (script)::punkexe.launcherhelp
set existing ""
catch {set existing [punk::args::raw_def $id]}
@ -1699,6 +1703,31 @@ if {![info exists ::punkboot::launch_defaults]} {
}
return $id
}
proc launcher_helpsubjectdef_id {} {
#define (script)::punkexe.launcherhelpsubject: the VALIDATION
#clone the help arm parses the subject word against - subject
#REQUIRED with the live choices, and the display definition's
#ignored trailing-arg row suppressed (-antiglobs). Without this
#the display shape lets punk::args SKIP a mistyped optional
#subject into the unconstrained arg row, silently rendering the
#top-level table where a tabled choice error is wanted.
set id (script)::punkexe.launcherhelpsubject
set existing ""
catch {set existing [punk::args::raw_def $id]}
if {$existing ne ""} {
return $id
}
if {[catch {
set ov [dict create \
@id [list -id $id] \
subject [list -optional 0 -choices [launcher_live_choices]] \
]
punk::args::define [punk::args::resolved_def -antiglobs {arg} -override $ov (script)::punkexe::help]
}]} {
return ""
}
return $id
}
proc launcher_plain_help {} {
#degraded help: plain subcommand list built without punk::args
set exebase [file rootname [file tail [info nameofexecutable]]]
@ -1841,49 +1870,27 @@ if {![info exists ::punkboot::launch_defaults]} {
set subcommand [lindex $arglist 0]
if {$subcommand in $known_subcommands} {
set subcommand_arglist [lrange $arglist 1 end]
#G-032: a help flag as the subcommand's FIRST argument renders that
#subcommand's usage. tclsh: only when it is the SOLE argument (stock
#parity keeps every other leading-dash form - including '-help' with
#further arguments - in ::argv); project subcommands: only when the
#registration declares an argsid (a handler-only registration keeps
#full passthrough - the application may do its own -help handling).
if {[lindex $subcommand_arglist 0] in $::punkboot::help_flags} {
set do_subhelp 0
if {$subcommand eq "tclsh"} {
if {[llength $subcommand_arglist] == 1} {
set do_subhelp 1
}
} elseif {$subcommand in $builtin_subcommands} {
set do_subhelp 1
} elseif {[dict exists $::punkboot::project_subcommand_info $subcommand argsid]} {
set do_subhelp 1
}
if {$do_subhelp} {
::punkboot::launcher_show_help $subcommand
exit 0
}
}
#G-032 (revised 2026-08-05): a subcommand's arguments are entirely its
#own - there is no first-argument help-flag interception. Use
#'<punkexe> help <subcommand>' for a subcommand's usage; '-help' after
#a subcommand reaches the app/script unchanged (tclsh keeps full stock
#dash parity; app-shellspy handles -help/--help/help itself; a
#parse-declared subcommand rejects it like any undeclared flag).
} else {
set subcommand_arglist $arglist
if {[llength $subcommand_arglist]} {
#G-032: a help flag in first position is a launch-surface help
#request on kits whose unknown-first default is 'script'.
#Tool-style kits (unknownfirst names a project subcommand) keep
#routing flags to their processor - their surface stays
#discoverable via the 'help' subcommand word.
if {$subcommand in $::punkboot::help_flags && $default_unknownfirst eq "script"} {
::punkboot::launcher_show_help
exit 0
}
#G-077: a first argument of exactly '-e' is the one-liner form -
#reclassify to 'script' with the WHOLE arglist ('-e' included) as
#its arguments, so the script subcommand's own '-e' handling runs
#it. This sits ahead of the unknown-first-arg refusal below, which
#would otherwise reject '-e' as naming no existing file. Same
#carve-out shape as the help flags: only script-default kits
#intercept it - a tool-style kit keeps '-e' routed to its own
#processor, and '<punkexe> tclsh -e ...' keeps stock parity
#(the tclsh subcommand never reaches this path).
#would otherwise reject '-e' as naming no existing file. Only
#script-default kits intercept it - a tool-style kit keeps '-e'
#routed to its own processor, and '<punkexe> tclsh -e ...' keeps
#stock parity (the tclsh subcommand never reaches this path).
#A leading help FLAG (-help etc) is deliberately NOT special
#(2026-08-05 single-mechanism revision): on script-default kits it
#falls to the refusal below, whose stderr usage answers it anyway;
#tool-style kits route it to their processor.
if {$subcommand eq "-e" && $default_unknownfirst eq "script"} {
set subcommand script
} else {
@ -2084,11 +2091,11 @@ if {![info exists ::punkboot::launch_defaults]} {
#exit code; no repl fallthrough (G-015-compatible).
#G-032: the argument contract ((script)::punkexe::buildinfo,
#@values -min 0 -max 0) is enforced through punk::args when
#available: any argument earns the historic one-line refusal PLUS
#the tabled usage error on stderr, exit 2 (buildinfo.test pins the
#one-liner and the exit code). The empty-argument fast path never
#touches punk::args; a '-help' first argument was already
#intercepted at selection.
#available: any argument (including '-help' - no flag
#interception, 2026-08-05 revision) earns the historic one-line
#refusal PLUS the tabled usage error on stderr, exit 2
#(buildinfo.test pins the one-liner and the exit code). The
#empty-argument fast path never touches punk::args.
if {[llength $subcommand_arglist]} {
puts stderr "buildinfo: takes no arguments"
if {[::punkboot::launcher_punkargs_ready]} {
@ -2105,9 +2112,14 @@ if {![info exists ::punkboot::launch_defaults]} {
exit [punk::buildinfo::main]
}
help {
#G-032: launcher help surface. Bare 'help' renders the top-level
#tabled usage (built-ins + project-registered subcommands with
#summaries); 'help <subcommand>' renders that subcommand's usage.
#G-032: launcher help surface - THE single help mechanism
#(2026-08-05 revision; the punkshell 'i <cmd> ...' idiom). Bare
#'help' renders the top-level tabled usage (built-ins +
#project-registered subcommands with summaries);
#'help <subcommand> ?arg ...?' renders that subcommand's usage -
#words after the subject are accepted and ignored, so 'help' can
#be prepended to any existing command line
#('punk9_beta help tclsh somefile somearg' shows the tclsh usage).
#Subject validation parses through the launcherhelp clone when
#punk::args is available (tabled choice errors on stderr exit 1,
#unambiguous prefixes resolve); degraded mode accepts exact names
@ -2118,9 +2130,16 @@ if {![info exists ::punkboot::launch_defaults]} {
exit 0
}
if {[::punkboot::launcher_punkargs_ready]} {
set helpdef_id [::punkboot::launcher_helpdef_id]
set helpdef_id [::punkboot::launcher_helpsubjectdef_id]
if {$helpdef_id ne ""} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid $helpdef_id} argd]} {
#validate/resolve ONLY the subject word (the make.tcl help
#precedent), against the required-subject validation clone:
#a mistyped subject earns the tabled choice error (exit 1)
#instead of being skipped past to render the top-level
#table; everything after the subject is deliberately
#unparsed (accepted and ignored - it is the subject's own
#command line, any mix of its words).
if {[catch {punk::args::parse [lrange $subcommand_arglist 0 0] -errorstyle [::punkboot::launcher_errstyle] withid $helpdef_id} argd]} {
puts stderr $argd
exit 1
}
@ -2130,10 +2149,11 @@ if {![info exists ::punkboot::launch_defaults]} {
exit 0
}
}
#degraded: exact-name subject only
#degraded: exact-name subject only; trailing words ignored as in
#the parsed path
set help_subject [lindex $subcommand_arglist 0]
if {[llength $subcommand_arglist] > 1 || $help_subject ni $::punkboot::launch_subcommands} {
puts stderr "help: expected 'help ?subcommand?' with subcommand one of: [join [::punkboot::launcher_plain_choices] {, }]"
if {$help_subject ni $::punkboot::launch_subcommands} {
puts stderr "help: expected 'help ?subcommand? ?arg ...?' with subcommand one of: [join [::punkboot::launcher_plain_choices] {, }]"
exit 1
}
::punkboot::launcher_show_help $help_subject

Loading…
Cancel
Save