From a58d0117f4ef95c6f34f8747d7c2e17b96849bdb Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Fri, 7 Aug 2026 04:15:37 +1000 Subject: [PATCH] Add punkpath agent skill: PATH-executable resolution + Tcl-command clash diagnosis A new .agents/skills/punkpath/SKILL.md (canonical) with a byte-identical .claude/skills/punkpath/SKILL.md copy for Claude Code discovery (verified sync with a plain diff). Models the tcl-whatis/tcl-nslist shape: a single ~0.7s side-effect-free punkshell invocation answering two questions agents guess wrong: 1. Which PATH executable a bare command name actually runs, and what shadows it (overshadowed/overshadowed_by entry-idx fields). This is the diagnostic companion to the tcl-runtests warning that bare tclsh from a Bash tool often resolves to the MSYS tclsh - the skill gets the machine-readable verdict on which copy wins instead of guessing. 2. Whether a PATH executable clashes with a Tcl command (tcl_conflicts command:match=exact|nocase) - the 'invalid command name X' where X is X.exe on PATH (dir/sort/find/clip). Documents the -return json/text/dict forms, the -pathglob/-conflicts filters, and the -context lever (G-173: pass it for a stable conflict verdict through a wrapper/one-liner; the default-caller namespace is for interactive use). Scope kept tight to PATH-resolution and Tcl-command clash; explicitly disclaims being a general 'what is installed' tool. No root AGENTS.md index change (the .agents/skills/ entry is generic and does not list individual skills, matching the tcl-whatis/tcl-nslist/ tcl-runtests handling). Assisted-by: harness=pi; primary-model=huggingface/zai-org/GLM-5.2; api-location=huggingface.co --- .agents/skills/punkpath/SKILL.md | 122 +++++++++++++++++++++++++++++++ .claude/skills/punkpath/SKILL.md | 122 +++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 .agents/skills/punkpath/SKILL.md create mode 100644 .claude/skills/punkpath/SKILL.md diff --git a/.agents/skills/punkpath/SKILL.md b/.agents/skills/punkpath/SKILL.md new file mode 100644 index 00000000..c7f65382 --- /dev/null +++ b/.agents/skills/punkpath/SKILL.md @@ -0,0 +1,122 @@ +--- +name: punkpath +description: "Use to resolve which PATH executable a bare command name actually runs (and what shadows it), and whether a PATH executable clashes with a Tcl command - the two questions agents guess wrong when debugging test failures or 'invalid command name X' errors. Cost: one ~0.7s side-effect-free punkshell invocation. Returns JSON via 'punk91 src script -e {punk::path -return json }' listing PATH entries whose executables match the name, each with overshadowed/overshadowed_by (entry idx) and tcl_conflicts (command:match=exact|nocase) fields. Not a general 'what is installed' tool - PATH shadowing and Tcl-command clash only." +--- + +# punkpath + +PATH-executable resolution and Tcl-command clash diagnosis for the punkshell +repo - the "which tclsh will I actually get, and does X.exe shadow a Tcl +command?" answer that agents guess wrong. Asks the live interpreter via +`punk::path -return json`, so the verdict is the OS PATH-resolution truth +(first non-shadowed entry wins), not a guess from `which`/`where` or a +single-env-var read. + +## When to use + +- A test run fails in a way that smells like the wrong interpreter + (spurious Tcl-version failures, msys path semantics). The tcl-runtests + skill warns that bare `tclsh` from a Bash tool often resolves to the + MSYS/Git-for-Windows tclsh; run this to get the machine-readable verdict + on which `tclsh.exe` wins and what it shadows, instead of guessing. +- An "invalid command name X" error where X might be an `X.exe` on PATH + clashing with a Tcl command (`dir`, `sort`, `find`, `clip` are classic). + The `tcl_conflicts` field reports the resolved Tcl command and the + exact|nocase match. +- Before asserting an external tool is available, or which copy of a + duplicate (e.g. several `tclsh.exe`) the shell will execute. + +## Command + +Run from the repo root (`punk91` lives in `bin/`, so `bin/punk91` from the +Bash tool). `punk91` embeds Tcl 9.1; `punk905` (Tcl 9.0) or `punksys` +(Tcl 8.6) take the same one-liner. Cost: ~0.7s filtered, ~0.7s full PATH, +no side effects (read-only). + + bin/punk91 src script -e 'punk::path -return json ?-pathglob ?... ?-context ? ?-conflicts 0|1? ?...?' + +- `-return json` - machine-readable; the only form worth parsing. `table` + (default) is the human ANSI table; `text` is a plain-ASCII fixed-key + layout; `dict` returns a Tcl dict. All three non-table forms are + ANSI-free regardless of other options. +- `...` - executable-name glob filter (default `*` = all). Pass + a bare name (`tclsh`) or glob (`tclsh*`, `dir`) to narrow to the + executables you care about. ALWAYS filter by name unless you genuinely + need the whole PATH (the unfiltered scan returns every executable on + PATH - thousands). +- `-pathglob ...` - filter PATH entries by directory (case-insensitive + glob on the directory path). Default `*` = all PATH entries. Use to + narrow "which scoop shims provide X" etc. +- `-context ` - the namespace whose commands are checked for + conflicts. Default empty = the caller's namespace. PASS THIS when + invoked through a wrapper/one-liner (`src script -e`) to make the + conflict verdict stable and meaningful; the default-caller behaviour + is for interactive use. `::` checks the global namespace. +- `-conflicts 0|1` - default 1 (compute Tcl conflicts). Set 0 to skip for + a small perf win when you only care about overshadowing; the conflict + fields are then empty. + +Load noise ("src mode: registered ...") goes to stderr; read stdout only +(append `2>/dev/null` in bash). Prefer the Bash tool over the PowerShell +tool (attached consoles flip punkshell's terminal detection). + +## Output structure (JSON) + + { + "summary": { + "context": "::", "is_windows": 1, "separator": ";", + "path_entries": 130, # total PATH entries scanned + "shown_entries": 6, # entries with matching executables + "executable_total": 6, # matching executables across shown entries + "overshadowed_total": 5, # how many are shadowed by an earlier entry + "conflict_total": 0 # how many clash with a Tcl command + }, + "entries": [{ + "idx": 0, # PATH order index (0 = first on PATH) + "path": "C:\\Program Files\\Git\\mingw64\\bin", + "normalised": "...", "is_directory": 1, + "is_duplicate": 0, "duplicate_of": -1, # same dir appearing again on PATH + "exe_count": 55, "overshadowed_count": 0, + "executables": [{ + "name": "tclsh.exe", + "overshadowed": 0, # 1 = an earlier PATH entry also has this exe + "overshadowed_by": -1, # entry IDX (into entries[]) that shadows it + "tcl_conflicts": [] # list of {command, match} + }] + }, ...] + } + +## Reading the verdict + +- **Which copy wins:** the first entry (lowest `idx`) whose executable has + `overshadowed: 0` - that is what a bare command name resolves to. All + later copies of the same name have `overshadowed: 1` and + `overshadowed_by` = the winning entry's `idx`. +- **Tcl-command clash:** `tcl_conflicts` lists `{command, match}` pairs + where `command` is the resolved Tcl command name (namespace origin) and + `match` is `exact` (case-identical) or `nocase` (case-differing). An empty + list means the PATH executable does not clash with any resolvable Tcl + command in `-context`. +- **`shown_entries` < `path_entries`** means the binglob/pathglob filtered + some entries out; that is expected when you pass a name. +- **`overshadowed_by` is an entry idx, not an exe name** - cross-reference + it against `entries[].idx` to name the shadowing copy. + +## Caveats + +- This is PATH-resolution and Tcl-command-clash only. It is NOT a general + "what is installed" / `which` replacement and does not report + non-PATH tools, shell builtins, or aliases. +- The conflict verdict depends on `-context` (which namespace's commands + are resolvable). The default (caller's namespace) is wrong for a + wrapper/one-liner invocation - pass `-context ::` or the target + namespace explicitly. +- `is_duplicate`/`duplicate_of` refers to a PATH DIRECTORY appearing + more than once (the later copies have no own executable list); it is + not the same as `overshadowed` (an executable name repeated across + different directories). +- Full (unfiltered) output is large - thousands of executables across + the whole PATH. Filter by name unless you need the whole picture. +- From Tcl code, `punk::path -return dict` returns the same structure as + a dict; `punk::path -return json` round-trips to the same data (decode + via tcllib json and recompare - the G-173 acceptance criterion). \ No newline at end of file diff --git a/.claude/skills/punkpath/SKILL.md b/.claude/skills/punkpath/SKILL.md new file mode 100644 index 00000000..c7f65382 --- /dev/null +++ b/.claude/skills/punkpath/SKILL.md @@ -0,0 +1,122 @@ +--- +name: punkpath +description: "Use to resolve which PATH executable a bare command name actually runs (and what shadows it), and whether a PATH executable clashes with a Tcl command - the two questions agents guess wrong when debugging test failures or 'invalid command name X' errors. Cost: one ~0.7s side-effect-free punkshell invocation. Returns JSON via 'punk91 src script -e {punk::path -return json }' listing PATH entries whose executables match the name, each with overshadowed/overshadowed_by (entry idx) and tcl_conflicts (command:match=exact|nocase) fields. Not a general 'what is installed' tool - PATH shadowing and Tcl-command clash only." +--- + +# punkpath + +PATH-executable resolution and Tcl-command clash diagnosis for the punkshell +repo - the "which tclsh will I actually get, and does X.exe shadow a Tcl +command?" answer that agents guess wrong. Asks the live interpreter via +`punk::path -return json`, so the verdict is the OS PATH-resolution truth +(first non-shadowed entry wins), not a guess from `which`/`where` or a +single-env-var read. + +## When to use + +- A test run fails in a way that smells like the wrong interpreter + (spurious Tcl-version failures, msys path semantics). The tcl-runtests + skill warns that bare `tclsh` from a Bash tool often resolves to the + MSYS/Git-for-Windows tclsh; run this to get the machine-readable verdict + on which `tclsh.exe` wins and what it shadows, instead of guessing. +- An "invalid command name X" error where X might be an `X.exe` on PATH + clashing with a Tcl command (`dir`, `sort`, `find`, `clip` are classic). + The `tcl_conflicts` field reports the resolved Tcl command and the + exact|nocase match. +- Before asserting an external tool is available, or which copy of a + duplicate (e.g. several `tclsh.exe`) the shell will execute. + +## Command + +Run from the repo root (`punk91` lives in `bin/`, so `bin/punk91` from the +Bash tool). `punk91` embeds Tcl 9.1; `punk905` (Tcl 9.0) or `punksys` +(Tcl 8.6) take the same one-liner. Cost: ~0.7s filtered, ~0.7s full PATH, +no side effects (read-only). + + bin/punk91 src script -e 'punk::path -return json ?-pathglob ?... ?-context ? ?-conflicts 0|1? ?...?' + +- `-return json` - machine-readable; the only form worth parsing. `table` + (default) is the human ANSI table; `text` is a plain-ASCII fixed-key + layout; `dict` returns a Tcl dict. All three non-table forms are + ANSI-free regardless of other options. +- `...` - executable-name glob filter (default `*` = all). Pass + a bare name (`tclsh`) or glob (`tclsh*`, `dir`) to narrow to the + executables you care about. ALWAYS filter by name unless you genuinely + need the whole PATH (the unfiltered scan returns every executable on + PATH - thousands). +- `-pathglob ...` - filter PATH entries by directory (case-insensitive + glob on the directory path). Default `*` = all PATH entries. Use to + narrow "which scoop shims provide X" etc. +- `-context ` - the namespace whose commands are checked for + conflicts. Default empty = the caller's namespace. PASS THIS when + invoked through a wrapper/one-liner (`src script -e`) to make the + conflict verdict stable and meaningful; the default-caller behaviour + is for interactive use. `::` checks the global namespace. +- `-conflicts 0|1` - default 1 (compute Tcl conflicts). Set 0 to skip for + a small perf win when you only care about overshadowing; the conflict + fields are then empty. + +Load noise ("src mode: registered ...") goes to stderr; read stdout only +(append `2>/dev/null` in bash). Prefer the Bash tool over the PowerShell +tool (attached consoles flip punkshell's terminal detection). + +## Output structure (JSON) + + { + "summary": { + "context": "::", "is_windows": 1, "separator": ";", + "path_entries": 130, # total PATH entries scanned + "shown_entries": 6, # entries with matching executables + "executable_total": 6, # matching executables across shown entries + "overshadowed_total": 5, # how many are shadowed by an earlier entry + "conflict_total": 0 # how many clash with a Tcl command + }, + "entries": [{ + "idx": 0, # PATH order index (0 = first on PATH) + "path": "C:\\Program Files\\Git\\mingw64\\bin", + "normalised": "...", "is_directory": 1, + "is_duplicate": 0, "duplicate_of": -1, # same dir appearing again on PATH + "exe_count": 55, "overshadowed_count": 0, + "executables": [{ + "name": "tclsh.exe", + "overshadowed": 0, # 1 = an earlier PATH entry also has this exe + "overshadowed_by": -1, # entry IDX (into entries[]) that shadows it + "tcl_conflicts": [] # list of {command, match} + }] + }, ...] + } + +## Reading the verdict + +- **Which copy wins:** the first entry (lowest `idx`) whose executable has + `overshadowed: 0` - that is what a bare command name resolves to. All + later copies of the same name have `overshadowed: 1` and + `overshadowed_by` = the winning entry's `idx`. +- **Tcl-command clash:** `tcl_conflicts` lists `{command, match}` pairs + where `command` is the resolved Tcl command name (namespace origin) and + `match` is `exact` (case-identical) or `nocase` (case-differing). An empty + list means the PATH executable does not clash with any resolvable Tcl + command in `-context`. +- **`shown_entries` < `path_entries`** means the binglob/pathglob filtered + some entries out; that is expected when you pass a name. +- **`overshadowed_by` is an entry idx, not an exe name** - cross-reference + it against `entries[].idx` to name the shadowing copy. + +## Caveats + +- This is PATH-resolution and Tcl-command-clash only. It is NOT a general + "what is installed" / `which` replacement and does not report + non-PATH tools, shell builtins, or aliases. +- The conflict verdict depends on `-context` (which namespace's commands + are resolvable). The default (caller's namespace) is wrong for a + wrapper/one-liner invocation - pass `-context ::` or the target + namespace explicitly. +- `is_duplicate`/`duplicate_of` refers to a PATH DIRECTORY appearing + more than once (the later copies have no own executable list); it is + not the same as `overshadowed` (an executable name repeated across + different directories). +- Full (unfiltered) output is large - thousands of executables across + the whole PATH. Filter by name unless you need the whole picture. +- From Tcl code, `punk::path -return dict` returns the same structure as + a dict; `punk::path -return json` round-trips to the same data (decode + via tcllib json and recompare - the G-173 acceptance criterion). \ No newline at end of file