|
| 1 | +#:property PublishAot=false |
| 2 | + |
| 3 | +using System.Text.Json; |
| 4 | + |
| 5 | +// Prompt hook for the canvas-apps plugin. |
| 6 | +// |
| 7 | +// The hook injects guidance instead of calling sync_canvas directly because it only |
| 8 | +// receives the session cwd, not the per-app working directory. A blanket sync could |
| 9 | +// overwrite the wrong directory or clobber local edits that have not been pushed. |
| 10 | +const string Reminder = |
| 11 | + "Canvas Apps coauthoring reminder: " + |
| 12 | + "Before acting on this request, if a Canvas Authoring session is " + |
| 13 | + "active (i.e. `connect` has already been called for a canvas app), first call " + |
| 14 | + "the `sync_canvas` MCP tool targeting the app's working directory to pull the " + |
| 15 | + "current session state into the local .pa.yaml files. This avoids editing stale " + |
| 16 | + "YAML. If no coauthoring session is active yet, or this request is unrelated to " + |
| 17 | + "a canvas app, skip the sync."; |
| 18 | + |
| 19 | +try |
| 20 | +{ |
| 21 | + var rawInput = await Console.In.ReadToEndAsync(); |
| 22 | + string? transformedPrompt = null; |
| 23 | + |
| 24 | + try |
| 25 | + { |
| 26 | + using var input = JsonDocument.Parse(rawInput); |
| 27 | + if (input.RootElement.ValueKind == JsonValueKind.Object && |
| 28 | + input.RootElement.TryGetProperty("transformedPrompt", out var prompt) && |
| 29 | + prompt.ValueKind == JsonValueKind.String) |
| 30 | + { |
| 31 | + transformedPrompt = prompt.GetString(); |
| 32 | + } |
| 33 | + } |
| 34 | + catch (JsonException) |
| 35 | + { |
| 36 | + // Hook failures must not block the user's prompt. If parsing fails, emit the |
| 37 | + // Claude Code additional-context shape (Copilot will ignore it). |
| 38 | + } |
| 39 | + |
| 40 | + using var writer = new Utf8JsonWriter(Console.OpenStandardOutput()); |
| 41 | + writer.WriteStartObject(); |
| 42 | + |
| 43 | + if (transformedPrompt is not null) |
| 44 | + { |
| 45 | + writer.WriteString( |
| 46 | + "modifiedTransformedPrompt", |
| 47 | + $"{transformedPrompt}\n\n{Reminder}"); |
| 48 | + } |
| 49 | + else |
| 50 | + { |
| 51 | + writer.WriteStartObject("hookSpecificOutput"); |
| 52 | + writer.WriteString("hookEventName", "UserPromptSubmit"); |
| 53 | + writer.WriteString("additionalContext", Reminder); |
| 54 | + writer.WriteEndObject(); |
| 55 | + } |
| 56 | + |
| 57 | + writer.WriteEndObject(); |
| 58 | + writer.Flush(); |
| 59 | +} |
| 60 | +catch |
| 61 | +{ |
| 62 | + // Hooks are advisory and must never prevent the host from processing a prompt. |
| 63 | +} |
0 commit comments