src/Agent.ps1

function Get-QuietAgentInstruction {
    [CmdletBinding()]param()
    @'
Use PowerShell Core 7.6+ and QuietCli 2. Define the question and business object first.
Read the executable's installed help or official documentation before writing its contract.
Use quiet -ContractPath <factory.ps1> [options] executable arguments, or an explicitly installed module.
Unknown commands return Unsupported without execution. QuietCli contains no CLI catalogue.
Keep local results in variables; send only a projection through Out-QuietResult.
Use -Record to retain explicit code, documentation and masked evidence. Replay records instead of rerunning commands.
Generate modules from explicit sources and independent assertions; test, then install explicitly.
Never output Capture or Internal. Existing authorization still applies to native commands.
'@

}
function Invoke-QuietHook {
    [CmdletBinding()]param([Parameter(Mandatory)][Alias('Event')]$HookInput,[ValidateSet('off','warn','deny','rewrite')][string]$Awareness='warn')
    $command=[string]$HookInput.tool_input.command
    if($Awareness -eq 'off' -or !$command){return @{}}
    # PowerShell AST inspects executable segments without evaluating the supplied command.
    $tokens=$null;$errors=$null;$ast=[Management.Automation.Language.Parser]::ParseInput($command,[ref]$tokens,[ref]$errors)
    $names=@($ast.FindAll({param($node) $node -is [Management.Automation.Language.CommandAst]},$true)|ForEach-Object {$_.GetCommandName()})
    $allowed=@('quiet','pump','FrenchExDev.QuietCli.PoSh\quiet','FrenchExDev.QuietCli.PoSh\pump','Invoke-QuietCli','Invoke-QuietTask','Out-QuietResult','Import-Module','Set-Location','cd')
    $uncovered=@($names|Where-Object {$_ -and $_ -notin $allowed})
    if(!$errors.Count -and !$uncovered.Count){return @{}}
    $message='Use QuietCli with a documented AI-authored contract or an installed module; never forward unbounded native output.'
    $specific=@{hookEventName='PreToolUse'}
    if($Awareness -eq 'deny' -and !$errors.Count){$specific.permissionDecision='deny';$specific.permissionDecisionReason=$message}
    elseif($Awareness -eq 'rewrite' -and !$errors.Count -and $names.Count -eq 1 -and $command -notmatch '[|;&<>$(){}*?]' -and $command.IndexOf([char]96) -lt 0 -and $command -notmatch '\\'){
        $first=($command.Trim() -split '\s+')[0]
        if(Resolve-QuietCommand $first){
            $manifest=(Join-Path $script:QuietRoot 'FrenchExDev.QuietCli.PoSh.psd1').Replace("'","''")
            $body="Import-Module '"+$manifest+"' -DisableNameChecking; quiet -- "+$command
            $encoded=[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($body))
            $specific.updatedInput=@{command='pwsh -NoProfile -EncodedCommand '+$encoded}
            $specific.permissionDecision='ask';$specific.permissionDecisionReason=$message
        }
        else{$specific.additionalContext=$message}
    }else{$specific.additionalContext=$message}
    @{hookSpecificOutput=$specific}
}
function Install-QuietHook {
    [CmdletBinding(SupportsShouldProcess)]param([Parameter(Mandatory)][string]$SettingsPath,[ValidateSet('off','warn','deny','rewrite')][string]$Awareness='warn')
    $path=$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SettingsPath)
    $settings=if(Test-Path $path){Get-Content $path -Raw|ConvertFrom-Json -AsHashtable}else{@{}}
    if(!$settings.ContainsKey('hooks')){$settings.hooks=@{}}
    $entries=if($settings.hooks.ContainsKey('PreToolUse')){@($settings.hooks.PreToolUse)}else{@()}
    $entries=@($entries|Where-Object {($_|ConvertTo-Json -Depth 20 -Compress) -notmatch 'Invoke-QuietHook\.ps1'})
    $launcher=Join-Path $script:QuietRoot 'scripts/Invoke-QuietHook.ps1'
    $pwsh=(Get-Process -Id $PID).Path
    $entry=@{matcher='Bash|PowerShell';hooks=@(@{type='command';command=('"'+$pwsh+'" -NoProfile -File "'+$launcher+'" -Awareness '+$Awareness)})}
    $settings.hooks.PreToolUse=$entries+@($entry)
    if($PSCmdlet.ShouldProcess($path,'Install QuietCli hook')){
        [void][IO.Directory]::CreateDirectory((Split-Path -Parent $path));[IO.File]::WriteAllText($path,($settings|ConvertTo-Json -Depth 30),[Text.UTF8Encoding]::new($false))
    }
}
function Uninstall-QuietHook {
    [CmdletBinding(SupportsShouldProcess)]param([Parameter(Mandatory)][string]$SettingsPath)
    if(!(Test-Path $SettingsPath)){return}
    $settings=Get-Content $SettingsPath -Raw|ConvertFrom-Json -AsHashtable
    if($settings.hooks -and $settings.hooks.PreToolUse){
        $settings.hooks.PreToolUse=@($settings.hooks.PreToolUse|Where-Object {($_|ConvertTo-Json -Depth 20 -Compress) -notmatch 'Invoke-QuietHook\.ps1'})
        if($PSCmdlet.ShouldProcess($SettingsPath,'Remove QuietCli hook')){[IO.File]::WriteAllText([IO.Path]::GetFullPath($SettingsPath),($settings|ConvertTo-Json -Depth 30),[Text.UTF8Encoding]::new($false))}
    }
}
function Get-QuietSession {
    [CmdletBinding()]param([Parameter(Mandatory)][string[]]$Path,[switch]$AsObject)
    $sessions=@(foreach($item in $Path){
        $files=if(Test-Path $item -PathType Container){@(Get-ChildItem $item -Filter '*.jsonl' -File -Recurse)}else{@(Get-Item $item)}
        foreach($file in $files){
            $calls=0;$pumped=0;$unknown=0
            foreach($line in [IO.File]::ReadLines($file.FullName)){
                try{$HookInput=$line|ConvertFrom-Json -AsHashtable
                    $blocks=if($HookInput.message){@($HookInput.message.content)}elseif($HookInput.payload){@($HookInput.payload)}else{@($HookInput)}
                    foreach($block in $blocks){
                        if($block -isnot [Collections.IDictionary]){continue}
                        $text=if($block.input -and $block.input.command){[string]$block.input.command}elseif($block.arguments){$a=$block.arguments|ConvertFrom-Json -AsHashtable;[string]$a.cmd}else{''}
                        if($text){$calls++;if($text -match '(?i)(?:^|[\s;])(?:(?:FrenchExDev\.QuietCli\.PoSh\\)?(?:pump|quiet)|Invoke-QuietCli|Invoke-QuietTask)(?:\s|$)'){$pumped++}else{$unknown++}}
                    }
                }catch{$unknown++}
            }
            @{Path=$file.FullName;Calls=$calls;Pumped=$pumped;UnpumpedOrUnknown=$unknown;Adoption=$(if($calls){$pumped/$calls}else{$null})}
        }
    })
    $result=New-QuietResult -Data $sessions -NoRegister
    if($AsObject){$result}else{Out-QuietResult $result}
}