Source of truth for active work. Translates SPEC.md §16 (bash — shipped)
and SPEC.POWERSHELL.md §16 (PowerShell — v0.2.0) into NOW / NEXT / LATER
buckets. Park items aggressively — autonomous loops will otherwise bulldoze
priorities.
Hard rule: every PR ends with this file updated (item moved / completed / parked) so the plan reflects reality.
Spec:
SPEC.POWERSHELL.md(v0.2.0). The PowerShell parser is implemented — phases 1–14 ofSPEC.POWERSHELL.md§16 are complete (see below). What remains is the downstream Netclaw integration, which needs actions outside this repository.
- 1. Public-API surface —
ShellParserOptionsbase;BashParserOptionsreparented;PwshParserOptions+PwshParser; additiveVerbChain.CanonicalVerb/IsDynamic;Clause.IsBashCWrapped→IsCommandStringWrapped.PublicApiSnapshotTests, corpus DTOs, and the bash corpus JSON key all updated. - 2. Verb & binding tables —
PwshApprovedVerbs,PwshAliases(complete default set),PwshVerbs,PwshBindingTables,PwshPerVerbRules. - 3.
PwshLexer— quoting, backtick escape,$var/$env:/${name}, parameters, stream redirects, statement separators, comments, opaque regions,--%;OpaqueRegionScannerbacktick mode. - 4–10.
PwshCommandParser— pipeline / statement splitting, verb-chain extraction, the §6.5 binding model,PwshResolver(§8), per-verb path rules (§7),Set-Locationpropagation (§9),pwsh -Command/-EncodedCommandrecursion (§10), anomaly safe-fail and the 64 KiB cap (§11). - 11. Multi-shell corpus runner + PII audit — directory-routed by
Corpus/<shell>/. - 12. PowerShell corpus — 273 entries under
Corpus/powershell/, every §13 category minimum exceeded. - 13.
pwshvalidation gate +tools/PwshCorpusTool—PwshOracleTestsenforces the §13 oracle matrix + thePwshAliasescompleteness[Fact]; the tool is registered inTOOLING.md. - 14.
SPEC.mdedits + CI + version bump —SPEC.md§1 / §2 / §3 / §6.4 / §15 updated;VersionPrefix→0.2.0,VersionSuffix→alpha; CI verifiespwshand runs both corpora on Linux + Windows;RELEASE_NOTES.mdv0.2.0 section; CLI + Web samples gain a shell selector;README.mdupdated.
- Issue #63 — static Invoke-Expression command-string recursion.
Recurse into provably static
Invoke-Expression/iexpayloads, safe-fail computed and pipeline-fed code, share the existing recursion limits, and preserve current-scope PowerShell location attribution. - Issue #62 — source-ordered clause elements. Added the additive
Clause.Elementsprovenance view for Bash and PowerShell with exact raw spelling, decoded values, source spans when available, verb-relative argument placement, path facts, redirects, and conservative wrapper-span handling. Authored order is authoritative; element roles andPrecedingVerbElementCountexplicitly mirror the greedy parser projection rather than executable semantics. Paired Bash/PowerShell corpus cases cover Git-c/-C, multiple occurrences, and a valueless option that stops the greedy walk. Existing projection shapes and synthetic cwd attribution remain compatible; native options that differ only by case receive corrected metadata. The post-implementation option audit explicitly covers Wget-o/-O, curl-d/-D/-o/-O, Git-c/-C, and tar-c/-C/-f/-F; paired corpus cases pin Wget log/document output, curl data/header-output and@filesemantics, and tar helper-command safe-fail behavior in both shells. Adversarial review added deterministic coverage for quoted inline native fragment runs (including unquoted prefixes and mixed-quote safe-fail), PowerShell backtick-decoded colon bindings, native file-verb boundaries, and outer redirects on PowerShell command wrappers, including empty payloads. The corpus runner now verifies direct authored-token coverage even for legacy entries without explicit element expectations. Docker-vremains explicitly context-sensitive: the generic table supportsdocker run, while consumers use authored elements for global-option interpretation. Command-string provenance is integrated with the laterInvoke-Expressionrecursion work: static expansion clears unmappable outer spans, while dynamic payloads retain conservative source-aligned elements. Nested and dynamicbash -ccases pin the equivalent Bash boundary. Consumer guidance separates strict authored-stream matching from general executable-aware normalization; Netclaw can use the latter for reusable approvals without treating parser verb roles as semantic command boundaries. - Issue #64 — path-shaped operands after native verb chains. Stop the Bash and PowerShell native greedy passes before a token that matches the shared path-shape rules. Preserve that token as a resolved argument without a command dictionary or a public API change.
- Consumer guide. Added
docs/CONSUMER_GUIDE.mdwith the shell-neutral security-consumer algorithm, Bash and PowerShell guidance, worked public use cases, and immutable permalinks to Netclaw's production integration. Linked it from the README and aligned stale PowerShell prerelease/status wording in the public project docs. - Issue #52 — hyphenated PowerShell parameters/native options.
Preserve internal hyphens, apply bash-compatible native
--flag=valuesplitting and path classification, keep colon binding cmdlet-only, and pin the behavior in unit tests plus the PowerShell corpus. Review follow-ups shipped with it: the equals-form split moved to a sharedNativeFlagSyntaxso the two parsers can't drift, a colon value under an=-bearing parameter name safe-fails toDynamicSkip,-?lexes as one parameter token, and native option tables now use ordinal spelling while PowerShell cmdlet parameters remain case-insensitive.
- Tag
0.2.0-alpha;publish_nuget.ymlproducedShellSyntaxTree.0.2.0-alpha.nupkgand it is live on nuget.org (released 2026-05-20). -
0.2.0-beta.1so Netclaw validates the parser + the breaking rename - Publish the next
0.2.0prerelease with the additive issue #62Clause.Elementsprovenance surface and migration guidance - Promote to stable
0.2.0after Netclaw validation
- Netclaw consumes the v0.2.0 package; absorbs the
Clauserename (separate repository — cannot be done here) - ≥1 Netclaw integration test exercises a real PowerShell corpus entry through the live matcher and gets the expected gate decision
- Create the release-level OpenSpec proposal, design, capability deltas,
and ordered task list under
openspec/changes/v0-3-structured-shell-analysis/. - Create issue #72 as the v0.3 roadmap and cross-link issue #71 control flow and issue #69 shared native argument-fragment classification without merging their scopes.
- Complete OpenSpec task group 1: lock the additive public type names, compatibility projection, fixed analysis bounds, and supported-construct matrix before production implementation.
- Implement issue #69 as the first behavior-preserving preparation after contract lock.
- Add the structural and command-occurrence projections for the existing grammar before enabling any control-flow construct.
- Deliver Bash
for ... inand PowerShellforeachas the first two language-specific vertical slices, then extract only the shared analysis proven by both implementations.
- Seed corpus entries from sanitized real-world dogfood logs (SPEC §14 workflow) — both shells.
- Expand verb / cmdlet / alias tables as the corpus surfaces real commands.
- Performance sanity check (~1 ms typical) with a tiny BenchmarkDotNet harness — only if anything in the daemon hot path complains.
- The remaining v0.2.x candidate from
SPEC.POWERSHELL.md§18 is per-element comma-array path extraction (-Path a,b,c). Lossless cross-shell redirect identity is now part of issue #72's explicit v0.3 redirect model. - PowerShell definitions,
param()/begin/process/endblocks, and.ps1file parsing remain outside issue #72 (SPEC.POWERSHELL.md§18). - Any broader shared parser/analysis extraction beyond issue #69 follows the two language-specific tracer bullets in issue #72; lexers and structural parsers remain shell-specific unless proven duplication justifies a narrower composed helper.
- Windows
cmdparser. - Source-mapping (line/column on AST nodes) — only if an IDE consumer asks.
- Heredoc body preservation and process substitution are separately gated issue #72 tasks backed by production need; Bash function definitions remain deferred until a consumer need surfaces.
(empty; move items here when scope changes rather than deleting them)