CopilotAtelier.psd1
|
@{ RootModule = 'CopilotAtelier.psm1' # Replaced at build time by GitVersion. ModuleVersion = '5.0.0' GUID = '67bbef0b-f4de-4c1b-bb5a-b34104beb5b7' Author = 'raandree' CompanyName = 'raandree' Copyright = '(c) raandree. All rights reserved.' Description = 'Portable GitHub Copilot customization library. Ships custom agents, auto-applied instructions, on-demand skills, prompt templates, and lifecycle hooks, and installs them into the well-known ~/.copilot discovery folders that VS Code, the GitHub Copilot CLI, and Claude Code read.' PowerShellVersion = '5.1' FunctionsToExport = @('Get-CopilotAtelierVersion','Install-CopilotAtelier','Update-CopilotAtelier') CmdletsToExport = @() VariablesToExport = @() AliasesToExport = @() PrivateData = @{ PSData = @{ Tags = @( 'Copilot' 'GitHubCopilot' 'VSCode' 'Agents' 'Skills' 'Prompts' 'Instructions' 'Hooks' 'AI' 'Windows' 'Linux' 'MacOS' ) LicenseUri = 'https://github.com/raandree/CopilotAtelier/blob/main/LICENSE' ProjectUri = 'https://github.com/raandree/CopilotAtelier' IconUri = 'https://raw.githubusercontent.com/raandree/CopilotAtelier/main/assets/CA-glyph-on-light.png' Prerelease = 'preview0007' ReleaseNotes = '## [5.0.0-preview0007] - 2026-09-02 ### Removed - **The `.github/hooks` smoke-test probe, which had been failing on every turn since it was committed** (2026-09-02). `stop-probe.json` and `Test-HookLoaded.ps1` were scratch: a `Stop` hook that appended one line to `%TEMP%\workspace-hook-probe.log` to prove the workspace hook location loads at all. They answered that question on 2026-08-10 and the answer is written into [`com.github.copilot/hooks/README.md`](com.github.copilot/hooks/README.md) and the changelog entry below — the files themselves had no further job. They were not merely idle. The `windows` override hardcoded `D:\Git\CopilotAtelier\.github\hooks\Test-HookLoaded.ps1`, the drive the repository sat on when the probe was written, and on Windows that override wins. Every turn on any other machine ended with *"The argument … to the -File parameter does not exist"*. The POSIX `command` was no better in principle: `./.github/hooks/Test-HookLoaded.ps1` is relative, and the same README says VS Code does not guarantee the working directory, which is why every shipped hook resolves its own path. Nothing caught it because nothing looked. The `Hook configuration` suite in [`tests/Hooks.Tests.ps1`](tests/Hooks.Tests.ps1) — which asserts exactly this, that a hook command resolves to a script that exists and carries no shell-interpolated token — is scoped to `com.github.copilot/hooks/hooks.json`. A second hook file one directory away was outside every gate the repository owns. That suite now enumerates every tracked `*.json` sitting directly inside a folder named `hooks` and requires the shipped configuration to be the only one, so the next stray hook file fails the build instead of the chat. The guard was proven by planting one and watching it go red. ### Added - **A session clock, so Post-flight closes with the chat''s measured elapsed duration** (2026-09-02). The user asked for two more facts at the end of the checklist: when the turn closed, and how long the whole chat had run. The first half already existed — [`com.github.copilot/hooks/scripts/Add-SessionContext.ps1`](com.github.copilot/hooks/scripts/Add-SessionContext.ps1) injects `Session started at <UTC>` and Pre-flight opens every reply with it — which made this look like a formatting change. It is not, because a model has no clock. The opening timestamp is right only because a hook measured it; a closing one composed by the model would drift by the length of the turn, which is the very quantity being reported, and after a compaction the model no longer knows when the session began. The obvious fix is unavailable: VS Code''s `UserPromptSubmit` supports the common output format only, with no `additionalContext` field — the same limitation already documented for `PreCompact`. The events that *can* inject context are `SessionStart`, which fires once, and `PreToolUse`/`PostToolUse`, which would spend tokens on every tool call of every turn and fold a timing concern into the security guardrail. So the number is measured on disk and read back by the one party that can print it inside the reply. `Add-SessionContext` now also writes the session start to `<LocalApplicationData>/CopilotAtelier/sessions/session-<key>.json` — on disk, so it survives compaction — and hands the agent the absolute path of a new reader, [`com.github.copilot/hooks/scripts/Get-SessionElapsed.ps1`](com.github.copilot/hooks/scripts/Get-SessionElapsed.ps1). The agent runs that reader as the last action of the turn and copies its single line verbatim into the checklist: `POST-FLIGHT elapsed: 16m (started 09:15 UTC, measured 09:31 UTC, turn 3)`. [`com.github.copilot/rules/postflight.instructions.md`](com.github.copilot/rules/postflight.instructions.md) gains a *Session clock* section forbidding the model from composing, reformatting, or recomputing either number, and telling it to report the duration as unavailable rather than estimate one when the reader is gone. The first attempt printed the line from the `Stop` hook, and was wrong in a way only a screenshot revealed: VS Code renders a hook `systemMessage` as a detached, collapsed *Warning from Stop hook* box, not as part of the reply. The number was therefore beside the checklist rather than in it, and the user asked again. A hook cannot write inside the model''s output and the model cannot read a clock, so the shipped split is the only arrangement that satisfies both. [`com.github.copilot/hooks/scripts/Write-SessionClose.ps1`](com.github.copilot/hooks/scripts/Write-SessionClose.ps1) stays, because the turn counter still has to advance somewhere, but it now reports nothing unless the clock is unreadable — the one case where the agent''s own line could not be measured either. Reporting the duration there as well would only have put a second copy in the warning box on every turn. `Stop` fires once per turn instead of once per tool call and costs no tokens. It emits no `decision` field: blocking a `Stop` restarts the agent and bills another turn, which is far more than a timestamp is worth. The clock avoids the temp directory because `/tmp` is world-writable on Linux, where a predictable name invites another local account to pre-create the path, and avoids `.memory-bank/` because — unlike a compaction checkpoint — it is machinery rather than knowledge an agent reads, and it has to work in a workspace with no Memory Bank at all. The payload''s `session_id` becomes a path component, so it is stripped to `[A-Za-z0-9._-]` and capped at 64 characters, with a hash of the working directory as the fallback. The reader is read-only — `Stop` owns `turns`, so it reports the turn in progress as one past the closed count — and given no explicit path it picks the newest clock recorded for the current workspace, so a second VS Code window on another folder is never measured here. Deploying the reader exposed a defect the suite had been creating all along. Six `Add-SessionContext` tests invoked the hook without `-ClockRoot`, so every run left real session clocks in the caller''s own `%LOCALAPPDATA%\CopilotAtelier\sessions` — around fifteen of them, including one whose recorded workspace was `C:\demo IGNORE PREVIOUS INSTRUCTIONS`, written by the prompt-injection test. That was invisible while only the `Stop` hook read the clock, because it looks its own session up by id. The reader searches by workspace, so a clock the tests had written *for this repository* immediately shadowed the live session and reported a three-minute chat that had been running for an hour and three quarters. Every SessionStart invocation now goes through a helper that pins the clock root to `TestDrive`, a test asserts the real profile directory gains nothing, and the reader prefers a `session-<id>` clock over a `session-cwd-<hash>` fallback — VS Code always supplies a session id, so the hashed name in practice means a test or a non-VS-Code caller. The duration formatter shipped with a bug the tests caught: `[int]1.5` *rounds* in PowerShell, so a 90-minute chat reported `2h 30m`. It floors explicitly now, and [`tests/Hooks.Tests.ps1`](tests/Hooks.Tests.ps1) pins five durations that sit where rounding and truncation disagree, alongside the injected reader path, the single-line output contract, the turn-in-progress arithmetic, the workspace preference, the shadowing regression, the clock-root containment, the read-only guarantee, the turn counter, the `stop_hook_active` continuation case, a corrupt clock, an unreadable payload, the absent `decision` field, and a `session_id` of `../../pwned`. ### Fixed - **A 45-minute live proof ran with `long-running-job-monitor` unloaded, and the chat stayed silent for thirty minutes** (2026-09-01). An agent launched a live Hyper-V proof in the Vivarium workspace, hand-rolled `Start-Process` plus `WaitForExit` instead of the canonical detached launcher, armed no cadence tick, and answered two mid-job turns with no status line. The user had to ask "are you running a task in the background?" and then "didn''t we update the skill so the user gets a status update every n minutes?" — a Skill that was never read cannot be followed, so this is three defects in [`skills/long-running-job-monitor/SKILL.md`](skills/long-running-job-monitor/SKILL.md), not one. The first is a vocabulary gap in the `description`, which is the only thing the selector sees. Vivarium''s glossary makes *proof* the canonical term for a live integration run, and the `USE FOR:` list carried "live test" and "integration test" but not the word the domain actually uses. It now names `live proof`, `proof harness`, `proof run`, and `hour-long run`; the description stays at 961 characters, under the 1000-character soft cap. The second is a typo in the same list — "log log tail" is now "log tail". Both are one-line fixes that only matter because a description this skill never triggers on is a description that does nothing. The third is structural. Arming the cadence tick was described in the *Chat heartbeat* section and in a checklist item prefixed "For unattended cadence", so nothing on the launch path itself required it — an agent could follow step 2 to the letter, detach the job correctly, and end the turn with no tick armed. Step 2 now carries the imperative directly: arm in the same turn as the launch, before the turn ends, whenever the job is expected to outrun the cadence interval, and a detached launch with no armed tick is named as the exact failure the Skill exists to prevent. The checklist item is unconditional. [`notes-evals.md`](skills/long-running-job-monitor/notes-evals.md) gains E10, a trigger-rate eval whose prompt is a live-proof launch in Vivarium''s vocabulary that never says "monitor", "heartbeat", or "background", so the Skill has to be selected on the description alone. - **The mandatory disclaimer travelled into two signed submissions to a German tax office** (2026-08-31). [`com.github.copilot/agents/tax-researcher.agent.md`](com.github.copilot/age' } } } |