Browse Source
G-015: complete the punk executable `script` subcommand - run a script file or piped stdin (scriptname optional) in an interp preloaded with the default punk shell modules/aliases, honest exit codes, no shellfilter transforms, no interactive-shell fallthrough. Replaces the fragile `...;exit | punkexe` repl-pipe pattern (currently a stub in punk_main.tcl that only emits stderr diagnostics). Lean bare-script execution deferred to a later `tclsh` subcommand expansion. G-016: extend `dev projects.work` to discover git-based projects alongside fossil (git has no central registry - enumeration-source candidates recorded in the detail file). G-017 (index-only): once G-015 lands, direct agents via repo guidance to locate sibling projects with a piped projects.work call instead of scanning the filesystem. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.commaster
3 changed files with 160 additions and 0 deletions
@ -0,0 +1,85 @@ |
|||||||
|
# G-015 Punk executable `script` subcommand: reliable non-interactive piped/script execution |
||||||
|
|
||||||
|
Status: proposed |
||||||
|
Scope: src/vfs/_config/punk_main.tcl, src/lib/app-punkshell/punkshell.tcl (script path or a leaner dedicated app package) |
||||||
|
Acceptance: as in root GOALS.md index (canonical). |
||||||
|
|
||||||
|
## Context |
||||||
|
|
||||||
|
Agents (and exec-style callers generally) currently have no reliable way to run a |
||||||
|
one-shot command against a punk executable. The working pattern is to pipe repl |
||||||
|
input into the default shell path, e.g. (powershell): |
||||||
|
|
||||||
|
'dev projects.work *tomlish*;exit' | punk902z |
||||||
|
|
||||||
|
This is fragile in two ways: |
||||||
|
|
||||||
|
1. The trailing `exit` is load-bearing: without it, stdin EOF triggers the |
||||||
|
app-punkshell eof handling, whose default heuristic policy attempts to reopen |
||||||
|
the console (`CONIN$` / `/dev/tty`) and drop into an interactive repl. When a |
||||||
|
console exists but no human is watching (typical agent/CI harness), that is a |
||||||
|
permanent hang. `PUNK_PIPE_EOF=exit` mitigates, but callers must know to set it. |
||||||
|
2. If any command before the `exit` errors, the repl keeps consuming input and |
||||||
|
the `exit` line may never run as intended - the session can still land |
||||||
|
interactive, and the process exit code does not reflect the failure. |
||||||
|
|
||||||
|
Meanwhile the `script` subcommand in the punk_main.tcl dispatch |
||||||
|
(src/vfs/_config/punk_main.tcl ~1163) is an incomplete stub: it prints stderr |
||||||
|
diagnostics ("main.tcl launching script with args: ...") and sets |
||||||
|
::tcl_interactive, but never actually executes anything. Note also the dispatch |
||||||
|
default (~1090): any first argument that is not a recognised mode/subcommand is |
||||||
|
reclassified as `script`, so completing this path also completes the bare |
||||||
|
`<punkexe> <somefile>` form. |
||||||
|
|
||||||
|
The `shell` subcommand path (app-punkshell) wraps the session in shellfilter |
||||||
|
channel stacks (`punkshellout`/`punkshellerr` with file/syslog logging targets, |
||||||
|
plus ansiwrap on stderr in places). Those transforms exist for interactive-shell |
||||||
|
ergonomics and logging; for a one-shot script call they add overhead, alter |
||||||
|
output, and risk stderr chatter - which matters because Tcl `exec` treats any |
||||||
|
stderr output as command failure by default (see the comment block in |
||||||
|
punk_main.tcl's `punk` subcommand case). |
||||||
|
|
||||||
|
## Approach |
||||||
|
|
||||||
|
- `<punkexe> script <file> [<args>...]` - execute the file with conventional |
||||||
|
::argv0/::argv semantics, exit with its status. |
||||||
|
- `... | <punkexe> script` - read the whole of stdin as the script when no |
||||||
|
scriptname is supplied and stdin is piped/redirected; terminate at EOF, never |
||||||
|
reopening a console. The scriptname form must still work with piped stdin |
||||||
|
present (the script may itself want to read stdin) - "with or without a |
||||||
|
trailing scriptname". |
||||||
|
- No shellfilter stacks/transforms on the script path; plain channels. |
||||||
|
- No launcher output of its own on stdout or stderr (remove the stub |
||||||
|
diagnostics); the script's own output is the only output. |
||||||
|
- Errors: report on stderr, exit nonzero. Success: exit 0 (or the script's own |
||||||
|
explicit exit code). No interactive fallback on this subcommand under any |
||||||
|
eof/error condition - PUNK_PIPE_EOF and console availability are irrelevant |
||||||
|
here by design. |
||||||
|
- The script interp loads the basic punk modules and aliases that are available |
||||||
|
by default in an interactive punk shell, so commands like `dev projects.work` |
||||||
|
work in a piped one-liner with no `package require` boilerplate. This is |
||||||
|
deliberately not the leanest environment for basic scripts - fast/bare script |
||||||
|
execution is the future province of an expanded `tclsh` subcommand, out of |
||||||
|
scope here. The exact recommended one-liner gets documented under G-017. |
||||||
|
|
||||||
|
## Alternatives considered |
||||||
|
|
||||||
|
- Hardening the shell/repl pipe path instead (PUNK_PIPE_EOF, error-aborts-input) |
||||||
|
- insufficient alone: the shell path's channel transforms and repl semantics |
||||||
|
are wrong for exec-style callers even when eof/error policy is fixed; a |
||||||
|
dedicated clean path is simpler than making the interactive path dual-purpose. |
||||||
|
- Requiring agents to set PUNK_PIPE_EOF=exit and keep the trailing `exit` - |
||||||
|
rejected as the durable answer: it leaves exit codes meaningless on mid-script |
||||||
|
errors and depends on every caller knowing the incantation. |
||||||
|
- A bare interp (no punk modules/aliases preloaded) for the script path - |
||||||
|
rejected for this subcommand: the point is that shell-default commands |
||||||
|
(`dev projects.work` etc.) work as piped one-liners; lean bare-script |
||||||
|
execution belongs to a later expansion of the `tclsh` subcommand. |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
- The eof policy machinery (PUNK_PIPE_EOF exit/interactive/heuristic) in |
||||||
|
app-punkshell (~punkshell.tcl 466-507) stays as-is for the `shell` subcommand; |
||||||
|
this goal does not change interactive-shell behaviour. |
||||||
|
- Related later goal: G-016 extends what the motivating `projects.work` call can |
||||||
|
find; G-017 documents the agent-facing invocation pattern once this lands. |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
# G-016 `projects.work` discovers git-based projects alongside fossil |
||||||
|
|
||||||
|
Status: proposed |
||||||
|
Scope: src/modules/punk/mix/commandset/project-999999.0a1.0.tm, src/modules/punk/repo-999999.0a1.0.tm |
||||||
|
Acceptance: as in root GOALS.md index (canonical). |
||||||
|
|
||||||
|
## Context |
||||||
|
|
||||||
|
`dev projects.work <glob>` (punk::mix::commandset::project::collection::work) |
||||||
|
answers "where are my projects checked out" by opening the central fossil |
||||||
|
config-db, globbing repository database filenames, and listing the known |
||||||
|
checkout directories per repo - with optional per-checkout file-state detail. |
||||||
|
This makes it the natural mechanism for agents to locate sibling projects |
||||||
|
(e.g. the tomlish project space referenced by G-014) instead of recursively |
||||||
|
scanning the filesystem. |
||||||
|
|
||||||
|
The gap: it is fossil-only. Git-based projects are invisible to it, and git has |
||||||
|
no equivalent of fossil's central config-db - there is no built-in registry of |
||||||
|
clones on a machine. So git discovery needs a defined enumeration source of its |
||||||
|
own before the listing can be extended. |
||||||
|
|
||||||
|
## Approach |
||||||
|
|
||||||
|
Enumeration-source candidates (implementation decision, to be recorded here when |
||||||
|
made): |
||||||
|
|
||||||
|
- **Configured search roots**: a punk::config setting (natural G-014 consumer) |
||||||
|
listing parent directories to scan one or two levels deep for `.git` dirs. |
||||||
|
Bounded scan, no registry maintenance, but discovery limited to declared roots. |
||||||
|
- **Punk-maintained registry**: record project paths when punk tooling |
||||||
|
creates/opens them (and offer a scan-once command to seed it). Fast lookups, |
||||||
|
works for arbitrary locations, but can go stale. |
||||||
|
- Hybrid: registry seeded/refreshed by an explicit scan of configured roots. |
||||||
|
|
||||||
|
Result-shape considerations: |
||||||
|
|
||||||
|
- Each row identifies its VCS. Fossil rows keep their current columns |
||||||
|
(repo db filename, project name/code, checkout dirs, dup-set annotations); |
||||||
|
what the git analogue of "project name" is (dir name, remote URL tail, |
||||||
|
configured name) is part of the design work. |
||||||
|
- A dual git+fossil workdir (like the shellspy repo itself) is one project row |
||||||
|
with both VCSs indicated, not two rows. |
||||||
|
- Fossil supports multiple checkouts per repo db; git worktrees are the |
||||||
|
analogous multi-workdir case and should at least not break the listing. |
||||||
|
|
||||||
|
## Alternatives considered |
||||||
|
|
||||||
|
- Filesystem-wide scanning at query time - rejected: unbounded cost and exactly |
||||||
|
the behaviour this mechanism is meant to replace for agents. |
||||||
|
- Relying on external tools' state (e.g. IDE/zoxide/gh caches) - rejected: |
||||||
|
non-portable, not present on all machines, opaque formats. |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
- Depends on nothing, but its value to agents is realised through G-015 |
||||||
|
(reliable piped invocation) and G-017 (agent guidance documenting the call). |
||||||
|
- The `-cd` / `-detail` options and case-insensitive glob behaviour of the |
||||||
|
existing command are contracts to preserve. |
||||||
Loading…
Reference in new issue