ワンクリックで
fsharp-shell-process
Wrap git and residual external processes from F#; in-process-first eliminates most run-audit.sh shelling.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Wrap git and residual external processes from F#; in-process-first eliminates most run-audit.sh shelling.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Consumer-facing guide to hosting an interactive FS.Skia.UI app — the keyboard/pointer input surface, the preview-vs-tree render distinction, and the windowed-fullscreen blur caveat.
Build Skia-rendered FS.Skia.UI Controls, rich text, chart controls, graph controls, DataGrid, custom wrappers, and generated product examples.
Generated product guidance for Skia-rendered FS.Skia.UI Controls, rich text, chart controls, graph controls, DataGrid, and custom wrappers.
Wire a generated FS.Skia.UI product to the desktop viewer host.
Understand and work with the internal keyed VDOM diff over the lowered Control<'msg> IR (feature 067) — its key-first-then-positional matching, the NodePatch/ChildOp operation set, the totality/determinism/identity-at-rest/round-trip invariants, and the module's disposition (internal, property-tested, wired onto the live render path via RetainedRender in feature 091 and current through feature 103 — layout/bounds cache, injected-delta animation clock, visual-state cross-fade). Use when reading the diff invariants, extending the property tests, or working on the wired retained render path.
Maintainer-facing guide to the FS.Skia.UI.Controls.Elmish interactive-host seam — how runInteractiveApp drives the retained render structure each frame (RetainedRender.step over the keyed diff), advances per-identity animation clocks from an injected Tick delta, stamps runtime visual state pre-reconcile, routes keys focus-first through routeFocusedKey, and resolves pointer hits to a stable identity via retainedHitTest. Use when reading or extending the live controls host loop, the per-frame retained-state/clock/visual-state wiring, or the key/pointer routing seam.
| name | fsharp-shell-process |
| description | Wrap git and residual external processes from F#; in-process-first eliminates most run-audit.sh shelling. |
| compatibility | F# governance library (build/Governance) under net10.0; build-tooling scope only. |
| metadata | {"author":"fs-skia-ui","source":"docs/reports/2026-05-31-1714-foundations-fsharp-capabilities-and-libraries.md"} |
Cookbook for replacing the Bash orchestration (run-audit.sh, parts of common.sh) with F#. Owns
C15 (git wrapping: base-ref resolve, merge-base, diff --unified=0) and C17 (residual
process orchestration + the exit-code contract). Verdicts come from the capability report
(metadata.source) §7.
git from F# — base-ref resolve, merge-base, diff --unified=0 (C15).dotnet, smoke runners) with explicit exit-code capture (C17).The strategic win is not to re-wrap Bash in F# — it is to make most shelling disappear. Once the
graph/audit/diff-scan live in the governance library, the build calls F# directly instead of build.fsx → run-audit.sh → python → JSON → re-parse; run-audit.sh shrinks to a shim or is deleted (plan
4.5–4.6). Only genuine external work (git, dotnet) remains.
Fake.Tools.Git 6.1.4 (FAKE family), or Fake.Core.Process for raw git.
Adopt Fake.Tools.Git.dotnet, smoke runners) (C17) → Fake.Core.Process — already transitively
present via Fake.Core.Target; keeps the dependency family consistent. Adopt.Fake.Core.Process suffices.fake.sh/fake.cmd launchers and container entrypoints stay shell; no
payoff to F#-ifying a three-line launcher.Fake.Tools.Git)directRunGitCommand repoDir args runs git and returns a success bool (to probe a ref);
runSimpleGitCommand repoDir args runs git and returns stdout as a string (to capture
merge-base / diff). Resolve the base ref the same way run-audit.sh does — main → master →
HEAD~1 — for diff-scan parity.
open Fake.Tools
/// C15 — probe candidate base refs in run-audit.sh order; first that verifies wins.
let resolveBaseRef (repoDir: string) =
let exists (ref: string) =
Fake.Tools.Git.CommandHelper.directRunGitCommand repoDir (sprintf "rev-parse --verify %s" ref)
[ "origin/main"; "origin/master"; "HEAD~1" ] |> List.tryFind exists
/// merge-base and a zero-context unified diff, captured as strings.
let mergeBase (repoDir: string) (left: string) (right: string) =
Fake.Tools.Git.CommandHelper.runSimpleGitCommand repoDir (sprintf "merge-base %s %s" left right)
let unifiedDiff (repoDir: string) (baseRef: string) =
Fake.Tools.Git.CommandHelper.runSimpleGitCommand repoDir (sprintf "diff --unified=0 %s" baseRef)
Fake.Core.Process)CreateProcess.fromRawCommandLine + Proc.run gives an explicit ExitCode and captured output.
Preserve the Bash/Python exit-code contract when rewiring a gate (e.g. 0 PASS, 2 needs-evidence,
3 graph-compute-failed) so callers and CI keep working.
open Fake.Core
/// C17 — run dotnet (or any residual tool), capturing exit code + stdout.
let runDotnet (workingDir: string) (arguments: string) =
let result =
CreateProcess.fromRawCommandLine "dotnet" arguments
|> CreateProcess.withWorkingDirectory workingDir
|> CreateProcess.redirectOutput
|> Proc.run
result.ExitCode, result.Result.Output
Process and git wrapping now ship as the real ShellProcess module in the FS.Skia.UI.SkillSupport
package — its .fsi surface lives at src/SkillSupport/ShellProcess.fsi. FS.Skia.UI.Build consumes
the same code via ProjectReference, so the governance build shells through exactly these functions:
ShellProcess.run: string -> string list -> string -> ProcResult — run exe args workingDir,
capturing the full result; ProcResult = { ExitCode: int; StdOut: string; StdErr: string }.ShellProcess.git — convenience wrapper that runs git with the given args in a repo dir.open FS.Skia.UI.SkillSupport
// Residual process with explicit exit-code capture (C17).
let result = ShellProcess.run "dotnet" [ "--version" ] "."
if result.ExitCode <> 0 then failwithf "dotnet failed: %s" result.StdErr
printfn "sdk %s" (result.StdOut.Trim())
// Git wrapping (C15): zero-context unified diff against a base ref.
let diff = ShellProcess.git "." [ "diff"; "--unified=0"; "origin/main" ]
printfn "%s" diff.StdOut
.fake state and are not safe to run
concurrently — run them sequentially in the deterministic order from CLAUDE.md/AGENTS.md.run-audit.sh does (main → master → HEAD~1); pair with the
C16 diff scanner in [[fsharp-parsing]].build/Governance; ships nowhere; no FCS.Stage 4.5–4.6 (shrink/delete run-audit.sh; in-process git + diff), Stage 5 (compiled front-end
running residual dotnet/smoke processes). See the plan in metadata.source.
When a problem outlasts reasonable in-repo attempts, extensive external research is
mandatory — consult official online docs first (the F#/.NET docs and the driven
library's own documentation/API reference), then community sources (forums, Reddit, Q&A
sites, issue trackers and changelogs). Record the findings and resolving links in the
feature's specs/<feature>/feedback/ folder and, for durable lessons, in this skill's
Sources line. Offline, the mandate degrades to recording "research blocked — "
rather than hard-failing the phase.
docs/reports/2026-05-31-1714-foundations-fsharp-capabilities-and-libraries.md §7.[[fsharp-io-globbing]] (diff file matching), [[fsharp-graph-algorithms]] (the in-process gate that replaces the shell orchestration), [[fsharp-parsing]] (the C16 diff scanner), [[fsharp-build-orchestration]] (the compiled front-end that calls these).