Browse Source

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
master
Julian Noble 4 days ago
parent
commit
a58d0117f4
  1. 122
      .agents/skills/punkpath/SKILL.md
  2. 122
      .claude/skills/punkpath/SKILL.md

122
.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 <name>}' 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 <pat>?... ?-context <ns>? ?-conflicts 0|1? ?<binglob>...?'
- `-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.
- `<binglob>...` - 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 <pat>...` - 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 <namespace>` - 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).

122
.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 <name>}' 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 <pat>?... ?-context <ns>? ?-conflicts 0|1? ?<binglob>...?'
- `-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.
- `<binglob>...` - 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 <pat>...` - 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 <namespace>` - 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).
Loading…
Cancel
Save