You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
16 KiB
16 KiB
src/modules — Main Editable Module Source
Purpose
Source of truth for all editable Punk project modules. This is where agents should make generic module source edits. Each .tm file is a Tcl module belonging to a named package.
Ownership
- Agents should edit files here as the primary development area for generic modules.
- The top-level modules (non-punk namespace) include shellfilter, shellrun, overtype, textblock, patternpunk, and related packages.
- The
punk/subdirectory holds allpunk::*namespace modules. - Tcl-major-specific sibling trees under
src/modules_tcl8/andsrc/modules_tcl9/mirror these module conventions where applicable, but their local AGENTS.md files control source selection for version-specific behavior.
Local Contracts
- Module filenames use the literal suffix
-999999.0a1.0.tm. - Corresponding
<modulename>-buildversion.txtfiles hold the real version number. - The exception is
punk::libunknown, which uses its own version0.1. #modpod-*directories contain internal files packed into.tmarchives during build; do not flatten or edit them without understanding the modpod format._build/directory holds build intermediates and should not be manually edited.- Always declare dependencies explicitly using
package require <name>near file tops. - Prefer fully qualified namespaces when referencing external packages, such as
package require tcl::zliborpackage require TclOO. - Organize custom modules as namespaces mirroring directory structure, such as
namespace eval punk::<modulename>or deeper paths likepunk::lib::util::<somename>. - Use semantic versions that
package vcomparecan interpret; strip leading zeros according to tcl package version rules ie in each segment including either side of a or b. - For optional features, probe with
if {[catch {package require foo}]} { ... }and degrade gracefully. - Vendormodules, bootsupport snapshots, VFS modules, and built output modules do not use the magic version scheme unless their own local docs explicitly say otherwise.
Module Subsystems
- Terminal/display modules include
punk::ansifor ANSI codes, cp437, and Sixel support;punk::console;textblockfor text composition and framing; andovertypefor text overlay. - Shell modules include
punk::repl,punk::shellrunforrun/runout/runerr/runx, andpunk::nav::nsfor namespace browsing. - Functional and pattern modules include
patternandpatterncmd; treat them as experimental and use them only with user instruction or confirmation. - Argument handling uses
punk::argsfor processing, validation, and documentation generation; prefer it for public APIs and user-facing utilities unless the code path is performance critical. - Networking modules include
punk::imap4,punk::netbox,punk::basictelnet, andpunk::net::vxlan. - Plugin and capability provider/handler support uses
punk::cap.
Work Guidance
- Treat
src/modules/as the source of truth for generic editable Punk modules. - Expect the literal suffix
-999999.0a1.0.tmand do not shorten or normalize it. - Prefer Unix-style LF line endings for
.tmsource files in this tree. - If the same proc exists in both
src/modules/andsrc/modules_tcl<major>/, prefersrc/modules/unless version-specific behavior is relevant or only the version-specific file is active. - Use
deck module.new <name>orpunk::mix::commandset::module::newto scaffold new modules. - Run
tclsh src/make.tcl modulesto build modules, ortclsh src/make.tcl projectfor a full build.
Formatting And Layout
- Opening braces stay on the same line for procs.
- Multiline control structures may span lines for readability if Tcl syntax allows without line-continuation backslashes, such as braced
exprcontents orifexpressions. - Line-continuation backslashes should only be used in documentation blocks for
punk::args, such aspunk::args::definearguments orlappend PUNKARGSelements. - Proc bodies should not use line-continuation backslashes because they make debug line-number matching harder; use Tcl expand syntax where practical.
- For test-specific continuation rules, use
src/tests/AGENTS.md. - Keep pipelines readable by aligning
% var = ...andpipecasesegments when practical. - Keep inline comments concise and describe intent, not mechanics.
- Document non-trivial procedures and exports with the standard
argdoc/PUNKARGStemplate below.
Example expand-style dictionary literal:
dict create {*}{
a A
b B
c C
}
Example expand-style dictionary literal with substituted values:
dict create {*}{
} a A
b B
c C {*}{
} d $DVAL {*}{
}
Naming Conventions
- Procedures:
lowercase_with_underscoresfor internals;camelCaseis allowed for public APIs where existing patterns fit. - Variables:
lowercase_with_underscores; avoid single-letter names except for loop indices. - Namespaces: mirror directory structure; nested modules should reflect filesystem hierarchy.
- Private helpers: prefix with
_, such as_resolve_stream; do not export them. - Constants:
UPPER_CASE_WITH_UNDERSCORESdeclared throughnamespace eval { variable CONSTANT value }when practical. - Keep
namespace exportlists alphabetized for clarity.
Procedure Documentation Template Guide
#- comments such as this with a leading dash are descriptions for the agent of how-to/what-to to implement
#- whereas comments without the dash are either literal comments, or if angle-bracketed, descriptions of what to put in the comment.
namespace eval argdoc {
lappend PUNKARGS [list {
@id -id ::full::namespace::path::procedure_name
@cmd -name "full::namespace::path::procedure_name"\
-summary\
"Short single line procedure description"\
-help\
"Procedure summary without too much overlap with documented
arguments below.
Existing
- include description of return value"
@leaders
arg1 -type any -optional 0 -multiple 0 -help\
"description of mandatory leading argument arg1"
@opts
-force -type boolean
-flag1 -type none -help\
"description of non-argument accepting flag -flag1"
-- -type none -help\
"End of opts marker"
@values -min 1 -max -1
arg2 -type list -optional 0 -help\
{Description of mandatory arg arg2
possibly spanning multiple lines, but each line should
generally be wrapped manually to keep sensibly short
for terminal display.
Help blocks may be wrapped in curly braces or double quotes.
See the punk::args module for details of acceptable structure.
}
glob -type string -optional 1 -multiple 1 -help\
"Description of optional argument glob which can be repeated.
Indenting of argument -help contents should follow this example.
Note that we indent subsequent lines by 2 additional spaces,
whereas the @cmd -help does no such indenting. This provides
a more presentable display when calling `i procedure_name`."
}]
}
proc procedure_name {args} {
# <optional summary line to aid debug during viewing of proc body using tools like `cmdtrace` or `corp procedure_name`>
#- agent: these comments are not literal contents of the proc - they are to be read as a guide.
#- argument parsing implementation:
#- If not highly performance sensitive use punk::args to parse,
#- make use of `punk::args::parse $args withid ::full::namespace::path::procedurename`
#- namespace eval ::punk::args::register {
#- #use fully qualified so 8.6 doesn't find existing var in global namespace
#- lappend ::punk::args::register::NAMESPACES ::punk::ansi ::punk::ansi::ansistring ::punk::ansi::colour ::punk::ansi::argdoc
#- }
#- If performance sensitive or not part of a broad-concept of 'API' for the module and useful utilities,
#- use fast `switch` based code to parse the arguments.
#- procedure functionality implementation
}
Update the template with concrete details whenever functions are user-facing or complex.
Error Handling And Logging
- Prefer
try { ... } on error {result options} { ... }for structured Tcl 8.6+ error handling. - Fallback pattern:
if {[catch {some_command} result]} {
puts stderr "Error: $result"
return -code error $result
}
- For Punk pipelines, wrap risky commands inside
pipecaseblocks and emit descriptive messages viaputs stderror Punk logging helpers. - Never swallow errors silently; propagate with context so shell users see actionable details.
Pipeline And Functional Style Notes
- Punk Pipeline functionality is experimental and should usually be avoided in agent-generated code unless the user specifically requests it.
- Pipeline syntax includes forms such as
var.= command1 |> command2 |> command3. - Pattern-match assignment syntax includes forms such as
x@0,y@1,z@2.= list a b c, where@targets an index and/is range-safe. - Destructuring examples include
x@end/@@k1.= list {k1 aaa} {k1 bb}. .=means following args form a command;=accepts a single value.- Use
% var = ...bindings to capture intermediate values; keep names meaningful. pipecaseshould list specific patterns before catch-alls to avoid hidden matches.fun name pattern { ... }definitions should remain side-effect light; treat them as pure functions unless otherwise documented.- Keep pipelines short and composable; extract helper procs or
fundefinitions when they exceed about 10 logical steps.
Module Structure Expectations
# <Module description>
package require <dependencies>
#- comments such as this with a leading dash are descriptions for the agent of how-to/what-to to implement
#- whereas comments without the dash are either literal comments, or if angle-bracketed, descriptions of what to put in the comment.
namespace eval <module_namespace> {
variable version <semver>
namespace export public_proc1 public_proc2
namespace eval argdoc {
lappend PUNKARGS [list {
@id -id <module_namespace>::public_proc1
... elided ...
#- punk::args textblock similar to example given in Procedure Documentation Template
#- This should exist immediately prior to each proc (or at least very close),
#- even if it is only being used for documentation and not for parsing.
#- If the public_proc1 proc does not use the PUNKARGS definition for parsing, then the parsing within the proc
#- and the punk::args definitions of proc args and behaviour must be kept synchronized.
}]
}
proc public_proc1 {args} {
#- argument parsing code, using `punk::args::parse` if part of API and not performance critical
#- if argument parsing is done in code here, the behaviour must match the arguments as defined in the PUNKARGS list created above.
#- Implementation
}
namespace eval argdoc {
lappend PUNKARGS [list {
@id -id <module_namespace>::some_other_public_proc
... elided ...
}]
}
proc some_other_public_proc {args} {
... elided ...
}
namespace eval private {
# <basic description of namespace - optional but recommended>
#- use of same PUNKARGS mechanism within a further nested argdoc namespace is recommended for clarity
#- even within private namespaces.
#- Generally privately namespaced procs will use the PUNKARGS for documentation only,
#- unless there is no concern about the usually slight overhead of punk::args::parse.
proc private_helper {arg1 arg2} {
#- Private implementation
}
proc private_helper2 {args} {
#- argument parsing code, using `switch`, `regexp`, etc if needed.
#- Private implementation
}
}
namespace eval utils {
# <basic description of namespace - optional but recommended>
#- other appropriately named sub namespaces such as 'utils' or 'lib' can be used to structure the codebase.
#- If these form part of what can reasonably be considered the API of the module, they should
#- be documented using the punk::args mechanism described above.
#- more procs
}
}
namespace eval ::punk::args::register {
#use fully qualified so 8.6 doesn't find existing var in global namespace
lappend ::punk::args::register::NAMESPACES <module_namespace> <module_namespace>::utils
}
## Ready
package provide <module_namespace> [tcl::namespace::eval <module_namespace> {
variable version
#- this version number, exactly 999999.0a1.0, is a literal used in src module folders
#- we refer to this sometimes as the magic version number
set version 999999.0a1.0
}]
return
Type And Data Handling
- Tcl is dynamically typed; emulate structural typing through argument validation at proc boundaries.
- Validate user inputs with
punk::args::parse,switch -exact,regexp, or Punk pipeline predicates before mutation. - Use dictionaries for structured data, or parallel lists if it is likely to be a performance win.
- Tcl arrays can be used, but limit them to simple flat structures or cases where they are specifically warranted.
- When bridging to binary data, such as ANSI/xbin parsing, document expected encodings and conversions.
Versioning And Releases
- Stick to semantic versioning:
major.minor.patch. - Use the magic version
999999.0a1.0for source modules and add or update the actual version number in the associated<modulename>-buildversion.txtfile. - Update the
<modulename>-buildversion.txtfile when API changes are made. - There may be occasional exceptions such as
punk::libunknown-0.1.tm, which is deliberately manually versioned; new modules should use the magic version mechanism. - Modules with the magic version number must not appear in output paths such as
<projectdir>/modules. - When referencing ranges, use bounded specs such as
1.2.3-2.0.0. - Convert loose versions to bounded form in module metadata; helper utilities exist in boot modules for this purpose.
- Update
punk::libunknownregistries whenever adding or removing modules to keep discovery accurate.
Documentation And Comments
- Primary documentation for procs should use the
punk::argssystem, preferably within anargdocsub-namespace usinglappend PUNKARGS <list of textblocks>. - If a public proc parses arguments manually, keep the implementation behavior synchronized with its PUNKARGS definition.
- Update relevant docs or usage notes when behavior changes.
- Mention environment variables or flags required to run new features.
Verification
- VS Code Tcl lint diagnostics are clean for modified
.tmfiles when available. tclsh src/make.tcl modulescompletes without errors for module changes.- Module tests pass:
tclsh src/tests/modules/<path>/tests/all.tcl.
Child DOX Index
punk/— Core punk namespace modules (see punk/AGENTS.md)opunk/— Alternative punk namespace (opunk::str)punkcheck/— Build/check system (punkcheck + punkcheck::cli)test/— Installed-module test packages (see test/AGENTS.md)