Browse Source

G-032 outputs: core 0.3.0 delivered (vfscommon), moduledoc 0.4.0/0.2.0 promoted, templates repack

make.tcl vfscommonupdate + modules ripple from the G-032 change-set
(driven with native Tcl903 per the recorded mkimg trap - kits assembled
archive-relative, no G-134 warnings): _vfscommon punkboot/core.tcl
regenerated from the 0.3.0 master; moduledoc::punkexe 0.3.0 -> 0.4.0 and
moduledoc::punkboot 0.1.0 -> 0.2.0 version renames; punk::mix templates
modpod repacked carrying the layout _config sweep (synced copies under
the #modpod-templates tree - punkcheck-managed, never hand-edited).

Claude-Session: https://claude.ai/code/session_01Y1diJnhjUxKgEG6EwYAzxj
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
155157e3db
  1. 24
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_config/project_main.tcl
  2. 10
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_config/punk_main.tcl
  3. 423
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_config/punkboot_core.tcl
  4. 423
      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. 54
      src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkboot-0.2.0.tm
  6. 141
      src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkexe-0.4.0.tm
  7. BIN
      src/vfs/_vfscommon.vfs/modules/punk/mix/templates-0.2.0.tm
  8. 431
      src/vfs/_vfscommon.vfs/punkboot/core.tcl

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

@ -15,7 +15,9 @@
# detection, package modes (internal/minted/src/os + proj: - see the
# 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).
# (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).
#
#WHAT TO EDIT
# Only the ==CUSTOMIZE== blocks below. Everything else is contract plumbing.
@ -44,8 +46,9 @@ apply { args {
#A dict mapping subcommand name -> Tcl handler script. The handler runs
#after boot with ::argv/::argc already holding the subcommand's arguments;
#the typical handler is a one-liner requiring your app package. Built-in
#subcommands (tclsh shellspy punk shell script) come from the boot core
#and cannot be shadowed - a colliding name is reported and ignored.
#subcommands (tclsh shellspy punk shell script buildinfo help) come from
#the boot core and cannot be shadowed - a colliding name is reported and
#ignored.
#
#Example - a kit for an app 'mytool' with a maintenance subcommand:
# namespace eval ::punkboot {variable project_subcommands [dict create \
@ -54,6 +57,21 @@ 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
#registration definition.
# namespace eval ::punkboot {variable project_subcommand_info [dict create \
# mytool {summary "Frob the widgets in a directory." \
# argsid (script)::mykit::mytool \
# package punk::args::moduledoc::mykit \
# parse 0} \
# ]}
namespace eval ::punkboot {variable project_subcommand_info [dict create]}
# ==CUSTOMIZE== 2. launch defaults --------------------------------------
#A dict with optional keys deciding what a launch without an explicit
#subcommand does:

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

@ -18,11 +18,17 @@ apply { args {
#dict: subcommand name -> Tcl handler script. The handler runs with
#::argv/::argc already holding the subcommand's arguments; the typical
#handler is just 'package require app-yourapp'. Built-in subcommands
#(tclsh shellspy punk shell script) come from the boot core and cannot be
#shadowed. punkshell itself declares none.
#(tclsh shellspy punk shell script buildinfo help) come from the boot
#core and cannot be shadowed. punkshell itself declares none.
#Example:
# namespace eval ::punkboot {variable project_subcommands [dict create \
# mytool {package require app-mytool}]}
#Optional help/parse metadata per declared name (G-032 - drives the
#launcher 'help' tables and argument gates; all keys optional, see
#punk::args::moduledoc::punkboot's registration definition):
# namespace eval ::punkboot {variable project_subcommand_info [dict create \
# mytool {summary "One-line summary." argsid (script)::mykit::mytool \
# package punk::args::moduledoc::mykit parse 0}]}
namespace eval ::punkboot {variable project_subcommands [dict create]}
# ==CUSTOMIZE== launch defaults ------------------------------------------

423
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,9 +11,13 @@
#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) extended by the thin main's declared
#project subcommands (G-031 registration model - see the dispatch section
#near the end of this file).
#(tclsh/shellspy/punk/shell/script/buildinfo/help) 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.
#
#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
@ -33,6 +37,11 @@
# to this file's own path)
# project_subcommands dict: subcommand name -> handler script (see the
# dispatch section; built-ins cannot be shadowed)
# project_subcommand_info dict: subcommand name -> info dict with
# optional keys summary/argsid/package/parse feeding
# the G-032 launcher help + declared-parse wiring
# (see the launcher help section; a name without an
# info entry keeps full passthrough semantics)
# launch_defaults dict: noargs / unknownfirst (see dispatch section)
# ------------------------------------------------------------------------------
@ -42,7 +51,7 @@
namespace eval ::punkboot {
#boot-core identity (G-031; stamped into kits + reported by the G-025
#buildinfo surfaces)
variable core_version 0.2.1
variable core_version 0.3.0
}
if {![info exists ::punkboot::launch_args]} {
namespace eval ::punkboot [list variable launch_args $::argv]
@ -53,6 +62,9 @@ if {![info exists ::punkboot::main_script]} {
if {![info exists ::punkboot::project_subcommands]} {
namespace eval ::punkboot {variable project_subcommands [dict create]}
}
if {![info exists ::punkboot::project_subcommand_info]} {
namespace eval ::punkboot {variable project_subcommand_info [dict create]}
}
if {![info exists ::punkboot::launch_defaults]} {
namespace eval ::punkboot {variable launch_defaults [dict create]}
}
@ -1509,20 +1521,291 @@ if {![info exists ::punkboot::launch_defaults]} {
}
namespace eval ::punkboot [list variable boot_state $boot_state_dict]
#---------------------------------------------------------------
#Launcher help + declared-parse machinery (G-032)
#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)
#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
#env hook as make.tcl), help degrades to a plain subcommand list, parse
#gates degrade to the historic switch semantics, and boot never fails.
#The tabled rendering additionally depends on the punk::ansi/textblock
#stack - rendering degrades (minimal errorstyle) independently of parsing.
#The per-kit top-level usage id (script)::punkexe.launcher is cloned from
#the static (script)::punkexe definition at first render, with the
#subcommand choices/choicelabels replaced by this kit's LIVE surface;
#(script)::punkexe.launcherhelp is the same clone of
#(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
variable launcher_builtin_summaries [dict create \
tclsh "Run as a (near) stock tclsh - no punk modules loaded." \
script "Run a script non-interactively in the punk script environment; honest exit codes." \
shell "Interactive punk shell (repl)." \
punk "punk shell launch; piped-stdin friendly." \
shellspy "Experimental shellspy command-line processor." \
buildinfo "Print the kit's build/identity stamp report and exit (G-025)." \
help "Show usage for the launch surface or one of its subcommands (G-032)." \
]
proc launcher_punkargs_ready {} {
#lazy guarded capability probe, one verdict per process. ok requires
#punk::args AND the moduledoc launch definitions to load and resolve
#with the snapshot actually available in this kit/context.
variable launcher_punkargs_state
if {$launcher_punkargs_state ne ""} {
return [expr {$launcher_punkargs_state eq "ok"}]
}
set launcher_punkargs_state plain
if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBOOT_PLAIN)]} {
return 0
}
if {[catch {
package require punk::args
package require punk::args::moduledoc::punkexe
if {[punk::args::get_spec (script)::punkexe] eq ""} {
error "definition (script)::punkexe did not resolve"
}
punk::args::get_spec (script)::punkexe::buildinfo
punk::args::get_spec (script)::punkexe::help
}]} {
return 0
}
set launcher_punkargs_state ok
return 1
}
proc launcher_errstyle {} {
#tabled (standard) parse errors when the rendering stack is
#loadable; minimal otherwise - rendering degrades independently of
#parsing (the G-030 split)
catch {package require punk::ansi}
catch {package require textblock}
if {[package provide punk::ansi] ne "" && [package provide textblock] ne ""} {
return standard
}
return minimal
}
proc launcher_subcommand_summary {name} {
#plain-text one-line summary for a known subcommand (built-in table
#or the project registration's declared summary)
variable launcher_builtin_summaries
variable project_subcommand_info
if {[dict exists $launcher_builtin_summaries $name]} {
return [dict get $launcher_builtin_summaries $name]
}
if {[dict exists $project_subcommand_info $name summary]} {
return [dict get $project_subcommand_info $name summary]
}
return "Project-declared subcommand (no summary registered)."
}
proc launcher_live_choices {} {
#this kit's subcommand set in display order: the static definition's
#choice order first (the documented built-in family), then 'help',
#then project registrations in declaration order
variable launch_subcommands
set ordered [list]
catch {
set spec [punk::args::get_spec (script)::punkexe]
foreach c [dict get $spec FORMS _default ARG_INFO subcommand -choices] {
if {$c in $launch_subcommands && $c ni $ordered} {
lappend ordered $c
}
}
}
foreach c $launch_subcommands {
if {$c ni $ordered} {
lappend ordered $c
}
}
return $ordered
}
proc launcher_overview_id {} {
#define (script)::punkexe.launcher: the per-kit top-level usage -
#the moduledoc (script)::punkexe definition with the subcommand
#choices/choicelabels replaced by this kit's live surface.
#Returns the id, or "" when the clone cannot be built.
variable launch_builtin_subcommands
set id (script)::punkexe.launcher
set existing ""
catch {set existing [punk::args::raw_def $id]}
if {$existing ne ""} {
return $id
}
if {[catch {
set spec [punk::args::get_spec (script)::punkexe]
set labels [dict get $spec FORMS _default ARG_INFO subcommand -choicelabels]
set choices [launcher_live_choices]
foreach c $choices {
if {$c eq "help" || $c ni $launch_builtin_subcommands || ![dict exists $labels $c]} {
dict set labels $c " [launcher_subcommand_summary $c]"
}
}
set ov [dict create \
@id [list -id $id] \
subcommand [list -choices $choices -choicelabels $labels] \
]
punk::args::define [punk::args::resolved_def -override $ov (script)::punkexe]
}]} {
return ""
}
return $id
}
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.
set id (script)::punkexe.launcherhelp
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 -choices [launcher_live_choices]] \
]
punk::args::define [punk::args::resolved_def -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 " 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:"
foreach sub [launcher_plain_choices] {
lappend lines [format " %-12s %s" $sub [launcher_subcommand_summary $sub]]
}
lappend lines " ('$exebase help ?subcommand?' shows detail; plain list because the punk::args"
lappend lines " tabled help stack is unavailable in this context, or PUNKBOOT_PLAIN is set)"
return [join $lines \n]
}
proc launcher_plain_choices {} {
#display order without punk::args: documented built-in order, then
#project registrations in declaration order
variable launch_subcommands
set ordered [list]
foreach c [list tclsh script shell punk shellspy buildinfo help] {
if {$c in $launch_subcommands} {
lappend ordered $c
}
}
foreach c $launch_subcommands {
if {$c ni $ordered} {
lappend ordered $c
}
}
return $ordered
}
proc launcher_show_help {{subject ""}} {
#render help to stdout: the top-level launch surface (empty
#subject) or one subcommand's usage. Tabled via punk::args when
#available; degrades to the plain list/summary. Never errors.
variable launch_builtin_subcommands
variable project_subcommand_info
if {[launcher_punkargs_ready]} {
set id ""
if {$subject eq ""} {
set id [launcher_overview_id]
} elseif {$subject eq "help"} {
set id [launcher_helpdef_id]
} elseif {$subject in $launch_builtin_subcommands} {
set id (script)::punkexe::$subject
} else {
#project-registered: guarded require of the declared
#package (if any), then the declared definition id (if any)
if {[dict exists $project_subcommand_info $subject package]} {
catch {package require [dict get $project_subcommand_info $subject package]}
}
if {[dict exists $project_subcommand_info $subject argsid]} {
set id [dict get $project_subcommand_info $subject argsid]
} else {
#help by registration alone: the summary line
puts stdout "$subject - [launcher_subcommand_summary $subject]"
puts stdout "(no punk::args definition registered for this subcommand - if it is an application entry point it may provide its own -help handling)"
return
}
}
if {$id ne "" && ![catch {punk::args::usage $id} out]} {
puts stdout $out
return
}
#fall through to the plain forms on any render failure
}
if {$subject eq ""} {
puts stdout [launcher_plain_help]
} else {
puts stdout "$subject - [launcher_subcommand_summary $subject]"
puts stdout "(plain help: the punk::args tabled help stack is unavailable in this context, or PUNKBOOT_PLAIN is set)"
}
return
}
proc launcher_unknown_first_error {word} {
#G-032 reclassification rule: an unknown first argument that names
#no existing file (and is no lib:* scriptlib reference) is an
#obvious mistake - refuse it with usage on stderr instead of
#silently attempting it as a script. Returns 1 when the refusal
#was emitted (caller exits 1); 0 when punk::args is unavailable
#(caller falls back to the historic script reclassification).
variable launch_subcommands
if {![launcher_punkargs_ready]} {
return 0
}
set exebase [file rootname [file tail [info nameofexecutable]]]
puts stderr "$exebase: unknown subcommand '$word' (no such subcommand, and no such script file exists)"
set id [launcher_overview_id]
if {$id ne "" && ![catch {punk::args::usage $id} out]} {
puts stderr $out
} else {
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"
return 1
}
}
#---------------------------------------------------------------
#Subcommand selection (G-031 registration model)
#Built-ins (tclsh/shellspy/punk/shell/script) ship with this core; a thin
#main may extend the set via ::punkboot::project_subcommands (dict:
#name -> handler script, evaluated in the dispatch below with ::argv/::argc
#holding the subcommand's arguments). Built-in names cannot be shadowed -
#a colliding declaration is reported and ignored.
#Built-ins (tclsh/shellspy/punk/shell/script/buildinfo/help) ship with
#this core; a thin main may extend the set via
#::punkboot::project_subcommands (dict: name -> handler script, evaluated
#in the dispatch below with ::argv/::argc holding the subcommand's
#arguments). Built-in names cannot be shadowed - a colliding declaration
#is reported and ignored. ::punkboot::project_subcommand_info optionally
#carries per-name help/parse metadata (summary/argsid/package/parse - see
#the launcher help section above, G-032).
#::punkboot::launch_defaults keys (both optional):
# noargs subcommand assumed for a bare launch (default: shell)
# unknownfirst 'script' treats a non-subcommand first argument as a
# script invocation (default), or the name of a known
# subcommand to receive the whole arglist as its arguments
#---------------------------------------------------------------
set builtin_subcommands [list tclsh shellspy punk shell script buildinfo]
set builtin_subcommands [list tclsh shellspy punk shell script buildinfo help]
set known_subcommands $builtin_subcommands
foreach k [dict keys $::punkboot::project_subcommands] {
if {$k in $builtin_subcommands} {
@ -1531,6 +1814,9 @@ if {![info exists ::punkboot::launch_defaults]} {
lappend known_subcommands $k
}
}
#publish for the launcher help machinery (and post-boot probes)
set ::punkboot::launch_builtin_subcommands $builtin_subcommands
set ::punkboot::launch_subcommands $known_subcommands
if {[dict exists $::punkboot::launch_defaults noargs]} {
set default_noargs [dict get $::punkboot::launch_defaults noargs]
} else {
@ -1544,10 +1830,56 @@ 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
}
}
} 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
}
}
}
} else {
set subcommand $default_noargs
}
@ -1726,8 +2058,20 @@ if {![info exists ::punkboot::launch_defaults]} {
#implementation is punk::buildinfo - this arm is the thin
#exe-subcommand wrapper. Stdout carries only the report; honest
#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.
if {[llength $subcommand_arglist]} {
puts stderr "buildinfo: takes no arguments"
if {[::punkboot::launcher_punkargs_ready]} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid (script)::punkexe::buildinfo} parse_msg]} {
puts stderr $parse_msg
}
}
exit 2
}
if {[catch {package require punk::buildinfo} errM]} {
@ -1736,6 +2080,41 @@ 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.
#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
#only and renders the plain forms. Help goes to stdout, exit 0
#(G-015 output-cleanliness/exit-code doctrine).
if {![llength $subcommand_arglist]} {
::punkboot::launcher_show_help
exit 0
}
if {[::punkboot::launcher_punkargs_ready]} {
set helpdef_id [::punkboot::launcher_helpdef_id]
if {$helpdef_id ne ""} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid $helpdef_id} argd]} {
puts stderr $argd
exit 1
}
set help_subject ""
catch {set help_subject [dict get $argd values subject]}
::punkboot::launcher_show_help $help_subject
exit 0
}
}
#degraded: exact-name subject only
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] {, }]"
exit 1
}
::punkboot::launcher_show_help $help_subject
exit 0
}
default {
#a project-declared subcommand (thin-main customization point): the
#handler script runs in this frame, ::argv/::argc already hold the
@ -1743,6 +2122,30 @@ if {![info exists ::punkboot::launch_defaults]} {
#misdeclared launch_defaults value - the selection above maps every
#launch form to a known subcommand.
if {[dict exists $::punkboot::project_subcommands $subcommand]} {
#G-032 declared-parse gate: a registration may opt its
#arguments into punk::args validation via
#project_subcommand_info keys parse=1 + argsid (optional
#package requirement). A parse failure is a tabled usage
#error on stderr, exit 1, and the handler is not invoked.
#Degrades to the historic no-validation passthrough when
#punk::args (or the declared id) is unavailable.
if {[dict exists $::punkboot::project_subcommand_info $subcommand parse]
&& [string is true -strict [dict get $::punkboot::project_subcommand_info $subcommand parse]]
&& [dict exists $::punkboot::project_subcommand_info $subcommand argsid]
&& [::punkboot::launcher_punkargs_ready]} {
if {[dict exists $::punkboot::project_subcommand_info $subcommand package]} {
catch {package require [dict get $::punkboot::project_subcommand_info $subcommand package]}
}
set project_argsid [dict get $::punkboot::project_subcommand_info $subcommand argsid]
set project_argsid_ok 0
catch {set project_argsid_ok [expr {[punk::args::raw_def $project_argsid] ne ""}]}
if {$project_argsid_ok} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid $project_argsid} parse_msg]} {
puts stderr $parse_msg
exit 1
}
}
}
eval [dict get $::punkboot::project_subcommands $subcommand]
} else {
puts stderr "main.tcl: no handler for subcommand '$subcommand' (check launch_defaults in the kit's main.tcl) - known subcommands: $known_subcommands"

423
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,9 +11,13 @@
#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) extended by the thin main's declared
#project subcommands (G-031 registration model - see the dispatch section
#near the end of this file).
#(tclsh/shellspy/punk/shell/script/buildinfo/help) 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.
#
#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
@ -33,6 +37,11 @@
# to this file's own path)
# project_subcommands dict: subcommand name -> handler script (see the
# dispatch section; built-ins cannot be shadowed)
# project_subcommand_info dict: subcommand name -> info dict with
# optional keys summary/argsid/package/parse feeding
# the G-032 launcher help + declared-parse wiring
# (see the launcher help section; a name without an
# info entry keeps full passthrough semantics)
# launch_defaults dict: noargs / unknownfirst (see dispatch section)
# ------------------------------------------------------------------------------
@ -42,7 +51,7 @@
namespace eval ::punkboot {
#boot-core identity (G-031; stamped into kits + reported by the G-025
#buildinfo surfaces)
variable core_version 0.2.1
variable core_version 0.3.0
}
if {![info exists ::punkboot::launch_args]} {
namespace eval ::punkboot [list variable launch_args $::argv]
@ -53,6 +62,9 @@ if {![info exists ::punkboot::main_script]} {
if {![info exists ::punkboot::project_subcommands]} {
namespace eval ::punkboot {variable project_subcommands [dict create]}
}
if {![info exists ::punkboot::project_subcommand_info]} {
namespace eval ::punkboot {variable project_subcommand_info [dict create]}
}
if {![info exists ::punkboot::launch_defaults]} {
namespace eval ::punkboot {variable launch_defaults [dict create]}
}
@ -1509,20 +1521,291 @@ if {![info exists ::punkboot::launch_defaults]} {
}
namespace eval ::punkboot [list variable boot_state $boot_state_dict]
#---------------------------------------------------------------
#Launcher help + declared-parse machinery (G-032)
#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)
#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
#env hook as make.tcl), help degrades to a plain subcommand list, parse
#gates degrade to the historic switch semantics, and boot never fails.
#The tabled rendering additionally depends on the punk::ansi/textblock
#stack - rendering degrades (minimal errorstyle) independently of parsing.
#The per-kit top-level usage id (script)::punkexe.launcher is cloned from
#the static (script)::punkexe definition at first render, with the
#subcommand choices/choicelabels replaced by this kit's LIVE surface;
#(script)::punkexe.launcherhelp is the same clone of
#(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
variable launcher_builtin_summaries [dict create \
tclsh "Run as a (near) stock tclsh - no punk modules loaded." \
script "Run a script non-interactively in the punk script environment; honest exit codes." \
shell "Interactive punk shell (repl)." \
punk "punk shell launch; piped-stdin friendly." \
shellspy "Experimental shellspy command-line processor." \
buildinfo "Print the kit's build/identity stamp report and exit (G-025)." \
help "Show usage for the launch surface or one of its subcommands (G-032)." \
]
proc launcher_punkargs_ready {} {
#lazy guarded capability probe, one verdict per process. ok requires
#punk::args AND the moduledoc launch definitions to load and resolve
#with the snapshot actually available in this kit/context.
variable launcher_punkargs_state
if {$launcher_punkargs_state ne ""} {
return [expr {$launcher_punkargs_state eq "ok"}]
}
set launcher_punkargs_state plain
if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBOOT_PLAIN)]} {
return 0
}
if {[catch {
package require punk::args
package require punk::args::moduledoc::punkexe
if {[punk::args::get_spec (script)::punkexe] eq ""} {
error "definition (script)::punkexe did not resolve"
}
punk::args::get_spec (script)::punkexe::buildinfo
punk::args::get_spec (script)::punkexe::help
}]} {
return 0
}
set launcher_punkargs_state ok
return 1
}
proc launcher_errstyle {} {
#tabled (standard) parse errors when the rendering stack is
#loadable; minimal otherwise - rendering degrades independently of
#parsing (the G-030 split)
catch {package require punk::ansi}
catch {package require textblock}
if {[package provide punk::ansi] ne "" && [package provide textblock] ne ""} {
return standard
}
return minimal
}
proc launcher_subcommand_summary {name} {
#plain-text one-line summary for a known subcommand (built-in table
#or the project registration's declared summary)
variable launcher_builtin_summaries
variable project_subcommand_info
if {[dict exists $launcher_builtin_summaries $name]} {
return [dict get $launcher_builtin_summaries $name]
}
if {[dict exists $project_subcommand_info $name summary]} {
return [dict get $project_subcommand_info $name summary]
}
return "Project-declared subcommand (no summary registered)."
}
proc launcher_live_choices {} {
#this kit's subcommand set in display order: the static definition's
#choice order first (the documented built-in family), then 'help',
#then project registrations in declaration order
variable launch_subcommands
set ordered [list]
catch {
set spec [punk::args::get_spec (script)::punkexe]
foreach c [dict get $spec FORMS _default ARG_INFO subcommand -choices] {
if {$c in $launch_subcommands && $c ni $ordered} {
lappend ordered $c
}
}
}
foreach c $launch_subcommands {
if {$c ni $ordered} {
lappend ordered $c
}
}
return $ordered
}
proc launcher_overview_id {} {
#define (script)::punkexe.launcher: the per-kit top-level usage -
#the moduledoc (script)::punkexe definition with the subcommand
#choices/choicelabels replaced by this kit's live surface.
#Returns the id, or "" when the clone cannot be built.
variable launch_builtin_subcommands
set id (script)::punkexe.launcher
set existing ""
catch {set existing [punk::args::raw_def $id]}
if {$existing ne ""} {
return $id
}
if {[catch {
set spec [punk::args::get_spec (script)::punkexe]
set labels [dict get $spec FORMS _default ARG_INFO subcommand -choicelabels]
set choices [launcher_live_choices]
foreach c $choices {
if {$c eq "help" || $c ni $launch_builtin_subcommands || ![dict exists $labels $c]} {
dict set labels $c " [launcher_subcommand_summary $c]"
}
}
set ov [dict create \
@id [list -id $id] \
subcommand [list -choices $choices -choicelabels $labels] \
]
punk::args::define [punk::args::resolved_def -override $ov (script)::punkexe]
}]} {
return ""
}
return $id
}
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.
set id (script)::punkexe.launcherhelp
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 -choices [launcher_live_choices]] \
]
punk::args::define [punk::args::resolved_def -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 " 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:"
foreach sub [launcher_plain_choices] {
lappend lines [format " %-12s %s" $sub [launcher_subcommand_summary $sub]]
}
lappend lines " ('$exebase help ?subcommand?' shows detail; plain list because the punk::args"
lappend lines " tabled help stack is unavailable in this context, or PUNKBOOT_PLAIN is set)"
return [join $lines \n]
}
proc launcher_plain_choices {} {
#display order without punk::args: documented built-in order, then
#project registrations in declaration order
variable launch_subcommands
set ordered [list]
foreach c [list tclsh script shell punk shellspy buildinfo help] {
if {$c in $launch_subcommands} {
lappend ordered $c
}
}
foreach c $launch_subcommands {
if {$c ni $ordered} {
lappend ordered $c
}
}
return $ordered
}
proc launcher_show_help {{subject ""}} {
#render help to stdout: the top-level launch surface (empty
#subject) or one subcommand's usage. Tabled via punk::args when
#available; degrades to the plain list/summary. Never errors.
variable launch_builtin_subcommands
variable project_subcommand_info
if {[launcher_punkargs_ready]} {
set id ""
if {$subject eq ""} {
set id [launcher_overview_id]
} elseif {$subject eq "help"} {
set id [launcher_helpdef_id]
} elseif {$subject in $launch_builtin_subcommands} {
set id (script)::punkexe::$subject
} else {
#project-registered: guarded require of the declared
#package (if any), then the declared definition id (if any)
if {[dict exists $project_subcommand_info $subject package]} {
catch {package require [dict get $project_subcommand_info $subject package]}
}
if {[dict exists $project_subcommand_info $subject argsid]} {
set id [dict get $project_subcommand_info $subject argsid]
} else {
#help by registration alone: the summary line
puts stdout "$subject - [launcher_subcommand_summary $subject]"
puts stdout "(no punk::args definition registered for this subcommand - if it is an application entry point it may provide its own -help handling)"
return
}
}
if {$id ne "" && ![catch {punk::args::usage $id} out]} {
puts stdout $out
return
}
#fall through to the plain forms on any render failure
}
if {$subject eq ""} {
puts stdout [launcher_plain_help]
} else {
puts stdout "$subject - [launcher_subcommand_summary $subject]"
puts stdout "(plain help: the punk::args tabled help stack is unavailable in this context, or PUNKBOOT_PLAIN is set)"
}
return
}
proc launcher_unknown_first_error {word} {
#G-032 reclassification rule: an unknown first argument that names
#no existing file (and is no lib:* scriptlib reference) is an
#obvious mistake - refuse it with usage on stderr instead of
#silently attempting it as a script. Returns 1 when the refusal
#was emitted (caller exits 1); 0 when punk::args is unavailable
#(caller falls back to the historic script reclassification).
variable launch_subcommands
if {![launcher_punkargs_ready]} {
return 0
}
set exebase [file rootname [file tail [info nameofexecutable]]]
puts stderr "$exebase: unknown subcommand '$word' (no such subcommand, and no such script file exists)"
set id [launcher_overview_id]
if {$id ne "" && ![catch {punk::args::usage $id} out]} {
puts stderr $out
} else {
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"
return 1
}
}
#---------------------------------------------------------------
#Subcommand selection (G-031 registration model)
#Built-ins (tclsh/shellspy/punk/shell/script) ship with this core; a thin
#main may extend the set via ::punkboot::project_subcommands (dict:
#name -> handler script, evaluated in the dispatch below with ::argv/::argc
#holding the subcommand's arguments). Built-in names cannot be shadowed -
#a colliding declaration is reported and ignored.
#Built-ins (tclsh/shellspy/punk/shell/script/buildinfo/help) ship with
#this core; a thin main may extend the set via
#::punkboot::project_subcommands (dict: name -> handler script, evaluated
#in the dispatch below with ::argv/::argc holding the subcommand's
#arguments). Built-in names cannot be shadowed - a colliding declaration
#is reported and ignored. ::punkboot::project_subcommand_info optionally
#carries per-name help/parse metadata (summary/argsid/package/parse - see
#the launcher help section above, G-032).
#::punkboot::launch_defaults keys (both optional):
# noargs subcommand assumed for a bare launch (default: shell)
# unknownfirst 'script' treats a non-subcommand first argument as a
# script invocation (default), or the name of a known
# subcommand to receive the whole arglist as its arguments
#---------------------------------------------------------------
set builtin_subcommands [list tclsh shellspy punk shell script buildinfo]
set builtin_subcommands [list tclsh shellspy punk shell script buildinfo help]
set known_subcommands $builtin_subcommands
foreach k [dict keys $::punkboot::project_subcommands] {
if {$k in $builtin_subcommands} {
@ -1531,6 +1814,9 @@ if {![info exists ::punkboot::launch_defaults]} {
lappend known_subcommands $k
}
}
#publish for the launcher help machinery (and post-boot probes)
set ::punkboot::launch_builtin_subcommands $builtin_subcommands
set ::punkboot::launch_subcommands $known_subcommands
if {[dict exists $::punkboot::launch_defaults noargs]} {
set default_noargs [dict get $::punkboot::launch_defaults noargs]
} else {
@ -1544,10 +1830,56 @@ 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
}
}
} 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
}
}
}
} else {
set subcommand $default_noargs
}
@ -1726,8 +2058,20 @@ if {![info exists ::punkboot::launch_defaults]} {
#implementation is punk::buildinfo - this arm is the thin
#exe-subcommand wrapper. Stdout carries only the report; honest
#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.
if {[llength $subcommand_arglist]} {
puts stderr "buildinfo: takes no arguments"
if {[::punkboot::launcher_punkargs_ready]} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid (script)::punkexe::buildinfo} parse_msg]} {
puts stderr $parse_msg
}
}
exit 2
}
if {[catch {package require punk::buildinfo} errM]} {
@ -1736,6 +2080,41 @@ 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.
#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
#only and renders the plain forms. Help goes to stdout, exit 0
#(G-015 output-cleanliness/exit-code doctrine).
if {![llength $subcommand_arglist]} {
::punkboot::launcher_show_help
exit 0
}
if {[::punkboot::launcher_punkargs_ready]} {
set helpdef_id [::punkboot::launcher_helpdef_id]
if {$helpdef_id ne ""} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid $helpdef_id} argd]} {
puts stderr $argd
exit 1
}
set help_subject ""
catch {set help_subject [dict get $argd values subject]}
::punkboot::launcher_show_help $help_subject
exit 0
}
}
#degraded: exact-name subject only
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] {, }]"
exit 1
}
::punkboot::launcher_show_help $help_subject
exit 0
}
default {
#a project-declared subcommand (thin-main customization point): the
#handler script runs in this frame, ::argv/::argc already hold the
@ -1743,6 +2122,30 @@ if {![info exists ::punkboot::launch_defaults]} {
#misdeclared launch_defaults value - the selection above maps every
#launch form to a known subcommand.
if {[dict exists $::punkboot::project_subcommands $subcommand]} {
#G-032 declared-parse gate: a registration may opt its
#arguments into punk::args validation via
#project_subcommand_info keys parse=1 + argsid (optional
#package requirement). A parse failure is a tabled usage
#error on stderr, exit 1, and the handler is not invoked.
#Degrades to the historic no-validation passthrough when
#punk::args (or the declared id) is unavailable.
if {[dict exists $::punkboot::project_subcommand_info $subcommand parse]
&& [string is true -strict [dict get $::punkboot::project_subcommand_info $subcommand parse]]
&& [dict exists $::punkboot::project_subcommand_info $subcommand argsid]
&& [::punkboot::launcher_punkargs_ready]} {
if {[dict exists $::punkboot::project_subcommand_info $subcommand package]} {
catch {package require [dict get $::punkboot::project_subcommand_info $subcommand package]}
}
set project_argsid [dict get $::punkboot::project_subcommand_info $subcommand argsid]
set project_argsid_ok 0
catch {set project_argsid_ok [expr {[punk::args::raw_def $project_argsid] ne ""}]}
if {$project_argsid_ok} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid $project_argsid} parse_msg]} {
puts stderr $parse_msg
exit 1
}
}
}
eval [dict get $::punkboot::project_subcommands $subcommand]
} else {
puts stderr "main.tcl: no handler for subcommand '$subcommand' (check launch_defaults in the kit's main.tcl) - known subcommands: $known_subcommands"

54
src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkboot-0.1.0.tm → src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkboot-0.2.0.tm

@ -6,7 +6,7 @@
# (C) 2026
#
# @@ Meta Begin
# Application punk::args::moduledoc::punkboot 0.1.0
# Application punk::args::moduledoc::punkboot 0.2.0
# Meta platform tcl
# Meta license BSD
# @@ Meta End
@ -64,6 +64,10 @@ tcl::namespace::eval punk::args::moduledoc::punkboot {
project_subcommands optional dict: subcommand name ->
handler script (see the registration
definition id (script)::punkboot::registration)
project_subcommand_info optional dict: subcommand name ->
help/parse metadata (summary/argsid/
package/parse - G-032; same
registration definition)
launch_defaults optional dict: noargs / unknownfirst
then source the core, first hit of:
<maindir>/punkboot/core.tcl (baked kit)
@ -99,22 +103,52 @@ tcl::namespace::eval punk::args::moduledoc::punkboot {
"::punkboot::project_subcommands - dict mapping subcommand
name -> Tcl handler script. Consulted by the boot core's
default dispatch AFTER the built-ins (tclsh shellspy punk
shell script buildinfo): a declared name extends the launch
surface; a name colliding with a built-in is reported on
stderr and ignored (built-ins cannot be shadowed). The
shell script buildinfo help): a declared name extends the
launch surface; a name colliding with a built-in is reported
on stderr and ignored (built-ins cannot be shadowed). The
handler runs in the boot frame with ::argv/::argc already
holding the subcommand's arguments - typically a one-line
'package require app-yourapp'.
::punkboot::project_subcommand_info - optional dict mapping a
declared subcommand name -> an info dict consumed by the
G-032 launcher help/parse wiring. All keys optional:
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.
package package to require (guarded) before resolving
argsid - typically a moduledoc-style package
carrying the definition
parse boolean (default 0): validate the subcommand's
arguments through argsid BEFORE the handler runs;
a punk::args usage error (stderr, exit 1) stops
dispatch. Degrades to no-validation when
punk::args or the id is unavailable.
A name without an info entry keeps full passthrough
semantics. Help for a registered subcommand appears in the
launcher's tables by registration alone - no dispatch edits.
::punkboot::launch_defaults - dict with optional keys:
noargs subcommand assumed for a bare launch
(default: shell)
unknownfirst 'script' treats a non-subcommand first
argument as a script invocation (default), or
the name of a known subcommand to receive the
whole arglist (tool-style kits)
Both dicts default to empty: an undeclared thin main yields
argument that names an existing file (or a
lib:* scriptlib reference) as a script
invocation and refuses anything else 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)
All dicts default to empty: an undeclared thin main yields
exactly the punkshell launch behaviour."
}]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
@ -216,6 +250,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.1.0
set version 0.2.0
}]
return

141
src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkexe-0.3.0.tm → src/vfs/_vfscommon.vfs/modules/punk/args/moduledoc/punkexe-0.4.0.tm

@ -8,7 +8,7 @@
# (C) 2026
#
# @@ Meta Begin
# Application punk::args::moduledoc::punkexe 0.3.0
# Application punk::args::moduledoc::punkexe 0.4.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.3.0]
#[manpage_begin punkshell_module_punk::args::moduledoc::punkexe 0 0.4.0]
#[copyright "2026"]
#[titledesc {punk executable launch documentation}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
@ -26,17 +26,20 @@
#[keywords module launch subcommand tclsh]
#[description]
#[para] punk::args definitions documenting the punk executable launch dispatch
#[para] (the subcommand family implemented in the kit boot script src/vfs/_config/punk_main.tcl)
#[para] (the subcommand family implemented in the shared boot core sourced by the
#[para] kit boot script - masters src/vfs/_config/punkboot_core.tcl + punk_main.tcl)
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] These definitions are the documentation home for the launch subcommand
#[para] family of the punk executables: tclsh, script, shell, punk, shellspy
#[para] (G-118 item 10). They are authored to be consumable by the future
#[para] launcher help/parse wiring (goal G-032) - rendering, option parsing and
#[para] degradation wiring stay with that goal. Id convention follows the
#[para] family of the punk executables: tclsh, script, shell, punk, shellspy,
#[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] 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.
@ -75,9 +78,9 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
variable PUNKARGS
namespace eval argdoc {
#Top-level launch dispatch (kit boot script punk_main.tcl, mirrored in
#project_main.tcl for generated projects). Display-first definitions:
#the dispatch itself does not parse via punk::args yet (goal G-032).
#Top-level launch dispatch (thin mains punk_main.tcl/project_main.tcl
#sourcing the shared boot core - G-031). The core's G-032 help/parse
#wiring consumes these definitions at dispatch time (see the Overview).
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
lappend PUNKARGS [list {
@id -id (script)::punkexe
@ -102,9 +105,28 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
The next argument selects a subcommand from the choices below.
When the first non-mode argument is not a known subcommand:
with no arguments at all the interactive punk shell is launched
('shell'); any other first argument is treated as a script
invocation (handled as for the 'script' subcommand)."
with no arguments at all the interactive punk shell is
launched ('shell'); a first argument naming an existing file
(or a lib:* scriptlib reference) is treated as a script
invocation (handled as for the 'script' subcommand); anything
else is refused with this usage on stderr, exit 1 (G-032 -
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."
@form -synopsis "<punkexe> ?packagemode? ?subcommand? ?arg ...?"
@leaders -min 0 -max 2
packagemode -type string -optional 1 -default internal -help\
@ -133,7 +155,7 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
ties (faithful to the project's snapshot vintage)
The launch reports the detected project root and effective
precedence."
subcommand -type string -optional 1 -choicerestricted 0 -choices {tclsh script shell punk shellspy buildinfo} -choicelabels {
subcommand -type string -optional 1 -choicerestricted 0 -choices {tclsh script shell punk shellspy buildinfo help} -choicelabels {
tclsh
" Run as a (near) stock tclsh - no punk modules loaded."
script
@ -146,6 +168,8 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
" Experimental shellspy command-line processor."
buildinfo
" Print the kit's build/identity stamp report and exit (G-025)."
help
" Show usage for the launch surface or one of its subcommands (G-032)."
} -help\
"Subcommand to run. Each subcommand's own contract is documented
under its definition id (script)::punkexe::<subcommand>."
@ -183,6 +207,10 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
script text into ::argv and reads stdin, exactly like stock
tclsh. That parity is deliberate; one-liner affordances belong
to the punk-level launch surfaces, not 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.
A scriptfile of the form 'lib:name' is refused with a pointer to
the 'script' subcommand: scriptlib resolution is a punk
@ -264,9 +292,12 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
}]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
#Sibling subcommands: summary-level contracts. Deep punk::args parse
#definitions for these surfaces are G-032's lane ('script' is its
#natural first parsed subcommand).
#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).
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
lappend PUNKARGS [list {
@id -id (script)::punkexe::script
@ -285,7 +316,11 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
is a usage error - no interactive fallback), or via scriptlib
resolution when the argument has the form 'lib:name' (resolved
from the scriptlib locations associated with the executable,
including extensionless and shebang'd scripts)."
including extensionless and shebang'd scripts).
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."
@form -synopsis "<punkexe> script ?scriptfile|lib:name? ?arg ...?"
@values -min 0 -max -1
scriptfile -type string -optional 1 -help\
@ -304,7 +339,9 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
"Launch the interactive punk shell (app-punkshell). With
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."
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)."
@form -synopsis "<punkexe> shell ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
@ -322,7 +359,9 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
interactive shell is launched directly. Commands piped into
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."
callers do not misreport failure.
A help flag (-help etc) as the FIRST argument renders this
usage and exits 0 (G-032)."
@form -synopsis "<punkexe> punk ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
@ -338,7 +377,9 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
"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)."
punkshell).
A help flag (-help etc) as the FIRST argument renders this
usage and exits 0 (G-032)."
@form -synopsis "<punkexe> shellspy ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
@ -376,12 +417,68 @@ 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.
In-process equivalent (no exec): the 'buildinfo' shell command
/ punk::buildinfo::report."
@form -synopsis "<punkexe> buildinfo"
@values -min 0 -max 0
}]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
#help subcommand: the G-032 launcher help surface. The boot core
#renders per-kit clones of these definitions: (script)::punkexe.launcher
#(the top-level table with the kit's LIVE subcommand set - built-ins +
#project registrations) and (script)::punkexe.launcherhelp (this
#definition with the subject choices replaced by that live set).
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
lappend PUNKARGS [list {
@id -id (script)::punkexe::help
@cmd -name "punkexe help"\
-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).
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).
Degradation (G-030 doctrine): when punk::args, these
definitions, or the rendering stack are unavailable in the
kit/context - or PUNKBOOT_PLAIN=1 is set - help degrades to
a plain subcommand list and boot/dispatch never fail on the
help path.
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
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."
}]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
}
#*** !doctools
@ -404,7 +501,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.3.0
set version 0.4.0
}]
return

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

Binary file not shown.

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

@ -11,9 +11,13 @@
#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) extended by the thin main's declared
#project subcommands (G-031 registration model - see the dispatch section
#near the end of this file).
#(tclsh/shellspy/punk/shell/script/buildinfo/help) 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.
#
#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
@ -33,6 +37,11 @@
# to this file's own path)
# project_subcommands dict: subcommand name -> handler script (see the
# dispatch section; built-ins cannot be shadowed)
# project_subcommand_info dict: subcommand name -> info dict with
# optional keys summary/argsid/package/parse feeding
# the G-032 launcher help + declared-parse wiring
# (see the launcher help section; a name without an
# info entry keeps full passthrough semantics)
# launch_defaults dict: noargs / unknownfirst (see dispatch section)
# ------------------------------------------------------------------------------
@ -42,7 +51,7 @@
namespace eval ::punkboot {
#boot-core identity (G-031; stamped into kits + reported by the G-025
#buildinfo surfaces)
variable core_version 0.2.1
variable core_version 0.3.0
}
if {![info exists ::punkboot::launch_args]} {
namespace eval ::punkboot [list variable launch_args $::argv]
@ -53,6 +62,9 @@ if {![info exists ::punkboot::main_script]} {
if {![info exists ::punkboot::project_subcommands]} {
namespace eval ::punkboot {variable project_subcommands [dict create]}
}
if {![info exists ::punkboot::project_subcommand_info]} {
namespace eval ::punkboot {variable project_subcommand_info [dict create]}
}
if {![info exists ::punkboot::launch_defaults]} {
namespace eval ::punkboot {variable launch_defaults [dict create]}
}
@ -1509,20 +1521,299 @@ if {![info exists ::punkboot::launch_defaults]} {
}
namespace eval ::punkboot [list variable boot_state $boot_state_dict]
#---------------------------------------------------------------
#Launcher help + declared-parse machinery (G-032)
#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)
#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
#env hook as make.tcl), help degrades to a plain subcommand list, parse
#gates degrade to the historic switch semantics, and boot never fails.
#The tabled rendering additionally depends on the punk::ansi/textblock
#stack - rendering degrades (minimal errorstyle) independently of parsing.
#The per-kit top-level usage id (script)::punkexe.launcher is cloned from
#the static (script)::punkexe definition at first render, with the
#subcommand choices/choicelabels replaced by this kit's LIVE surface;
#(script)::punkexe.launcherhelp is the same clone of
#(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
variable launcher_builtin_summaries [dict create \
tclsh "Run as a (near) stock tclsh - no punk modules loaded." \
script "Run a script non-interactively in the punk script environment; honest exit codes." \
shell "Interactive punk shell (repl)." \
punk "punk shell launch; piped-stdin friendly." \
shellspy "Experimental shellspy command-line processor." \
buildinfo "Print the kit's build/identity stamp report and exit (G-025)." \
help "Show usage for the launch surface or one of its subcommands (G-032)." \
]
proc launcher_punkargs_ready {} {
#lazy guarded capability probe, one verdict per process. ok requires
#punk::args AND the moduledoc launch definitions to load and resolve
#with the snapshot actually available in this kit/context.
variable launcher_punkargs_state
if {$launcher_punkargs_state ne ""} {
return [expr {$launcher_punkargs_state eq "ok"}]
}
set launcher_punkargs_state plain
if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBOOT_PLAIN)]} {
return 0
}
if {[catch {
package require punk::args
#punk::args (and the punk::ansi/textblock render stack) call
#punk::lib without requiring it (known module-web gap): on
#Tcl 8.6 the choices-grid render path needs punk::lib's
#forward-compat builtins (lpop etc). Guarded - require order
#matters (punk::lib itself hard-requires punk::args, so this
#call-time direction breaks no cycle); a kit without punk::lib
#still probes ok and rendering degrades where it must.
catch {package require punk::lib}
package require punk::args::moduledoc::punkexe
if {[punk::args::get_spec (script)::punkexe] eq ""} {
error "definition (script)::punkexe did not resolve"
}
punk::args::get_spec (script)::punkexe::buildinfo
punk::args::get_spec (script)::punkexe::help
}]} {
return 0
}
set launcher_punkargs_state ok
return 1
}
proc launcher_errstyle {} {
#tabled (standard) parse errors when the rendering stack is
#loadable; minimal otherwise - rendering degrades independently of
#parsing (the G-030 split)
catch {package require punk::ansi}
catch {package require textblock}
if {[package provide punk::ansi] ne "" && [package provide textblock] ne ""} {
return standard
}
return minimal
}
proc launcher_subcommand_summary {name} {
#plain-text one-line summary for a known subcommand (built-in table
#or the project registration's declared summary)
variable launcher_builtin_summaries
variable project_subcommand_info
if {[dict exists $launcher_builtin_summaries $name]} {
return [dict get $launcher_builtin_summaries $name]
}
if {[dict exists $project_subcommand_info $name summary]} {
return [dict get $project_subcommand_info $name summary]
}
return "Project-declared subcommand (no summary registered)."
}
proc launcher_live_choices {} {
#this kit's subcommand set in display order: the static definition's
#choice order first (the documented built-in family), then 'help',
#then project registrations in declaration order
variable launch_subcommands
set ordered [list]
catch {
set spec [punk::args::get_spec (script)::punkexe]
foreach c [dict get $spec FORMS _default ARG_INFO subcommand -choices] {
if {$c in $launch_subcommands && $c ni $ordered} {
lappend ordered $c
}
}
}
foreach c $launch_subcommands {
if {$c ni $ordered} {
lappend ordered $c
}
}
return $ordered
}
proc launcher_overview_id {} {
#define (script)::punkexe.launcher: the per-kit top-level usage -
#the moduledoc (script)::punkexe definition with the subcommand
#choices/choicelabels replaced by this kit's live surface.
#Returns the id, or "" when the clone cannot be built.
variable launch_builtin_subcommands
set id (script)::punkexe.launcher
set existing ""
catch {set existing [punk::args::raw_def $id]}
if {$existing ne ""} {
return $id
}
if {[catch {
set spec [punk::args::get_spec (script)::punkexe]
set labels [dict get $spec FORMS _default ARG_INFO subcommand -choicelabels]
set choices [launcher_live_choices]
foreach c $choices {
if {$c eq "help" || $c ni $launch_builtin_subcommands || ![dict exists $labels $c]} {
dict set labels $c " [launcher_subcommand_summary $c]"
}
}
set ov [dict create \
@id [list -id $id] \
subcommand [list -choices $choices -choicelabels $labels] \
]
punk::args::define [punk::args::resolved_def -override $ov (script)::punkexe]
}]} {
return ""
}
return $id
}
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.
set id (script)::punkexe.launcherhelp
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 -choices [launcher_live_choices]] \
]
punk::args::define [punk::args::resolved_def -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 " 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:"
foreach sub [launcher_plain_choices] {
lappend lines [format " %-12s %s" $sub [launcher_subcommand_summary $sub]]
}
lappend lines " ('$exebase help ?subcommand?' shows detail; plain list because the punk::args"
lappend lines " tabled help stack is unavailable in this context, or PUNKBOOT_PLAIN is set)"
return [join $lines \n]
}
proc launcher_plain_choices {} {
#display order without punk::args: documented built-in order, then
#project registrations in declaration order
variable launch_subcommands
set ordered [list]
foreach c [list tclsh script shell punk shellspy buildinfo help] {
if {$c in $launch_subcommands} {
lappend ordered $c
}
}
foreach c $launch_subcommands {
if {$c ni $ordered} {
lappend ordered $c
}
}
return $ordered
}
proc launcher_show_help {{subject ""}} {
#render help to stdout: the top-level launch surface (empty
#subject) or one subcommand's usage. Tabled via punk::args when
#available; degrades to the plain list/summary. Never errors.
variable launch_builtin_subcommands
variable project_subcommand_info
if {[launcher_punkargs_ready]} {
set id ""
if {$subject eq ""} {
set id [launcher_overview_id]
} elseif {$subject eq "help"} {
set id [launcher_helpdef_id]
} elseif {$subject in $launch_builtin_subcommands} {
set id (script)::punkexe::$subject
} else {
#project-registered: guarded require of the declared
#package (if any), then the declared definition id (if any)
if {[dict exists $project_subcommand_info $subject package]} {
catch {package require [dict get $project_subcommand_info $subject package]}
}
if {[dict exists $project_subcommand_info $subject argsid]} {
set id [dict get $project_subcommand_info $subject argsid]
} else {
#help by registration alone: the summary line
puts stdout "$subject - [launcher_subcommand_summary $subject]"
puts stdout "(no punk::args definition registered for this subcommand - if it is an application entry point it may provide its own -help handling)"
return
}
}
if {$id ne "" && ![catch {punk::args::usage $id} out]} {
puts stdout $out
return
}
#fall through to the plain forms on any render failure
}
if {$subject eq ""} {
puts stdout [launcher_plain_help]
} else {
puts stdout "$subject - [launcher_subcommand_summary $subject]"
puts stdout "(plain help: the punk::args tabled help stack is unavailable in this context, or PUNKBOOT_PLAIN is set)"
}
return
}
proc launcher_unknown_first_error {word} {
#G-032 reclassification rule: an unknown first argument that names
#no existing file (and is no lib:* scriptlib reference) is an
#obvious mistake - refuse it with usage on stderr instead of
#silently attempting it as a script. Returns 1 when the refusal
#was emitted (caller exits 1); 0 when punk::args is unavailable
#(caller falls back to the historic script reclassification).
variable launch_subcommands
if {![launcher_punkargs_ready]} {
return 0
}
set exebase [file rootname [file tail [info nameofexecutable]]]
puts stderr "$exebase: unknown subcommand '$word' (no such subcommand, and no such script file exists)"
set id [launcher_overview_id]
if {$id ne "" && ![catch {punk::args::usage $id} out]} {
puts stderr $out
} else {
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"
return 1
}
}
#---------------------------------------------------------------
#Subcommand selection (G-031 registration model)
#Built-ins (tclsh/shellspy/punk/shell/script) ship with this core; a thin
#main may extend the set via ::punkboot::project_subcommands (dict:
#name -> handler script, evaluated in the dispatch below with ::argv/::argc
#holding the subcommand's arguments). Built-in names cannot be shadowed -
#a colliding declaration is reported and ignored.
#Built-ins (tclsh/shellspy/punk/shell/script/buildinfo/help) ship with
#this core; a thin main may extend the set via
#::punkboot::project_subcommands (dict: name -> handler script, evaluated
#in the dispatch below with ::argv/::argc holding the subcommand's
#arguments). Built-in names cannot be shadowed - a colliding declaration
#is reported and ignored. ::punkboot::project_subcommand_info optionally
#carries per-name help/parse metadata (summary/argsid/package/parse - see
#the launcher help section above, G-032).
#::punkboot::launch_defaults keys (both optional):
# noargs subcommand assumed for a bare launch (default: shell)
# unknownfirst 'script' treats a non-subcommand first argument as a
# script invocation (default), or the name of a known
# subcommand to receive the whole arglist as its arguments
#---------------------------------------------------------------
set builtin_subcommands [list tclsh shellspy punk shell script buildinfo]
set builtin_subcommands [list tclsh shellspy punk shell script buildinfo help]
set known_subcommands $builtin_subcommands
foreach k [dict keys $::punkboot::project_subcommands] {
if {$k in $builtin_subcommands} {
@ -1531,6 +1822,9 @@ if {![info exists ::punkboot::launch_defaults]} {
lappend known_subcommands $k
}
}
#publish for the launcher help machinery (and post-boot probes)
set ::punkboot::launch_builtin_subcommands $builtin_subcommands
set ::punkboot::launch_subcommands $known_subcommands
if {[dict exists $::punkboot::launch_defaults noargs]} {
set default_noargs [dict get $::punkboot::launch_defaults noargs]
} else {
@ -1544,10 +1838,56 @@ 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
}
}
} 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
}
}
}
} else {
set subcommand $default_noargs
}
@ -1726,8 +2066,20 @@ if {![info exists ::punkboot::launch_defaults]} {
#implementation is punk::buildinfo - this arm is the thin
#exe-subcommand wrapper. Stdout carries only the report; honest
#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.
if {[llength $subcommand_arglist]} {
puts stderr "buildinfo: takes no arguments"
if {[::punkboot::launcher_punkargs_ready]} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid (script)::punkexe::buildinfo} parse_msg]} {
puts stderr $parse_msg
}
}
exit 2
}
if {[catch {package require punk::buildinfo} errM]} {
@ -1736,6 +2088,41 @@ 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.
#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
#only and renders the plain forms. Help goes to stdout, exit 0
#(G-015 output-cleanliness/exit-code doctrine).
if {![llength $subcommand_arglist]} {
::punkboot::launcher_show_help
exit 0
}
if {[::punkboot::launcher_punkargs_ready]} {
set helpdef_id [::punkboot::launcher_helpdef_id]
if {$helpdef_id ne ""} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid $helpdef_id} argd]} {
puts stderr $argd
exit 1
}
set help_subject ""
catch {set help_subject [dict get $argd values subject]}
::punkboot::launcher_show_help $help_subject
exit 0
}
}
#degraded: exact-name subject only
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] {, }]"
exit 1
}
::punkboot::launcher_show_help $help_subject
exit 0
}
default {
#a project-declared subcommand (thin-main customization point): the
#handler script runs in this frame, ::argv/::argc already hold the
@ -1743,6 +2130,30 @@ if {![info exists ::punkboot::launch_defaults]} {
#misdeclared launch_defaults value - the selection above maps every
#launch form to a known subcommand.
if {[dict exists $::punkboot::project_subcommands $subcommand]} {
#G-032 declared-parse gate: a registration may opt its
#arguments into punk::args validation via
#project_subcommand_info keys parse=1 + argsid (optional
#package requirement). A parse failure is a tabled usage
#error on stderr, exit 1, and the handler is not invoked.
#Degrades to the historic no-validation passthrough when
#punk::args (or the declared id) is unavailable.
if {[dict exists $::punkboot::project_subcommand_info $subcommand parse]
&& [string is true -strict [dict get $::punkboot::project_subcommand_info $subcommand parse]]
&& [dict exists $::punkboot::project_subcommand_info $subcommand argsid]
&& [::punkboot::launcher_punkargs_ready]} {
if {[dict exists $::punkboot::project_subcommand_info $subcommand package]} {
catch {package require [dict get $::punkboot::project_subcommand_info $subcommand package]}
}
set project_argsid [dict get $::punkboot::project_subcommand_info $subcommand argsid]
set project_argsid_ok 0
catch {set project_argsid_ok [expr {[punk::args::raw_def $project_argsid] ne ""}]}
if {$project_argsid_ok} {
if {[catch {punk::args::parse $subcommand_arglist -errorstyle [::punkboot::launcher_errstyle] withid $project_argsid} parse_msg]} {
puts stderr $parse_msg
exit 1
}
}
}
eval [dict get $::punkboot::project_subcommands $subcommand]
} else {
puts stderr "main.tcl: no handler for subcommand '$subcommand' (check launch_defaults in the kit's main.tcl) - known subcommands: $known_subcommands"

Loading…
Cancel
Save