diff --git a/plugins/token-diet/ctx-wire/README.md b/plugins/token-diet/ctx-wire/README.md index 7136d2f..6d85998 100644 --- a/plugins/token-diet/ctx-wire/README.md +++ b/plugins/token-diet/ctx-wire/README.md @@ -24,6 +24,10 @@ prose. That set is small: | **dotnet restore** | Yes (NuGet) | Yes (`All projects are up-to-date`, `Restored …`, `NU####`) | ✅ EN+FR | | **dotnet run** | Yes (build + app) | Strips build preamble; collapses all-passed MTP test runs | ✅ EN+FR | | **dotnet tool** | Yes (SDK) | Yes (`was successfully installed/updated`) | ✅ EN+FR | +| **dotnet publish** | Yes (build + NuGet) | Strips restore/build preamble, keeps the `-> …/publish/` artifact + errors/warnings (no `Build succeeded` line in .NET 10) | ✅ EN+FR | +| **dotnet pack** | Yes (NuGet) | Yes (`Successfully created package` / `Création réussie du package`) | ✅ EN+FR | +| **dotnet format** | Yes | **No** — silent on success; its diagnostics (`error WHITESPACE:`, analyzer IDs) must reach context. Nothing to collapse | n/a | +| **dotnet list package** | keys are English | **No** — `--outdated` / `--vulnerable` tables are the payload you *want* to keep; structural at most | n/a | | git log / diff / blame / list | Yes | **No** — purely structural (blank-strip, truncate, caps) | n/a | | grep / rg | Diagnostics only | **No** — `group_by` on `file:line:`, structural | n/a | | jira (CLI) | No (Go, English-only) | structural (strip blanks/`--`) | n/a | @@ -33,6 +37,14 @@ So localizing "all tools" would be almost entirely dead regex: the long tail emits English in any locale, and the structural filters (grep, git-log, ls, …) are already locale-agnostic by construction. +> **`dotnet build` vs `publish`/`pack` output in .NET 10** — verified against +> SDK 10.0.301: `dotnet build` still prints the classic `Build succeeded.` / +> `0 Error(s)` summary (piped, under a PTY, and with `--tl:off`), so the +> `dotnet-build` filter keys on it safely. `publish` and `pack` do **not** print +> that summary — their success markers are the `-> …/publish/` artifact line and +> `Successfully created package` respectively, which is why they get their own +> filters rather than reusing the build one. + ## Why no Romanian The two tools worth localizing **do not ship Romanian translations**: @@ -73,11 +85,15 @@ and secret-ish `key=value` / `--secret-flag value` pairs. So **GitHub tokens** and **Azure DevOps PATs / Atlassian tokens in the usual auth forms** (Basic header, `https://user:pat@…` URL, `token=…` assignment) are -already scrubbed. The one shape it doesn't know is a **bare `ATATT…` Atlassian -API token** (no matching prefix). Since the scrub rules aren't user-extensible, -`acli.toml` adds a filter `replace` that redacts `ATATT…` from acli output. To -scrub bare ATATT tokens everywhere (not just acli output), upstream an `ATATT` -entry to ctx-wire's `internal/scrub/scrub.go` token alternation + literal anchors. +already scrubbed. The shapes it doesn't know are the **bare `AT?…` Atlassian +tokens** — `ATATT…` (API), `ATCTT…` (Confluence/Bitbucket scoped) and `ATOAT…` +(OAuth) — none of which carry a prefix the built-in scrub matches. Since the +scrub rules aren't user-extensible, `acli.toml` adds a filter `replace` that +redacts all three (`AT(?:ATT|CTT|OAT)…`) from acli output to a single generic +`AT***REDACTED***` marker (the label deliberately doesn't echo which prefix — and +thus which token class — was present). To scrub these everywhere (not just acli +output), upstream an `AT(?:ATT|CTT|OAT)` entry to ctx-wire's +`internal/scrub/scrub.go` token alternation + literal anchors. ## Compressing MCP output diff --git a/plugins/token-diet/ctx-wire/filters.d/acli.toml b/plugins/token-diet/ctx-wire/filters.d/acli.toml index c44a81d..428548f 100644 --- a/plugins/token-diet/ctx-wire/filters.d/acli.toml +++ b/plugins/token-diet/ctx-wire/filters.d/acli.toml @@ -18,7 +18,12 @@ description = "Compact Atlassian CLI (acli) output; redact bare AT?TT tokens (AT match_command = "^acli\\b" strip_ansi = true replace = [ - { pattern = "AT[A-Z]TT[A-Za-z0-9_=.\\-]{20,}", replacement = "ATATT***REDACTED***" }, + # Match all three bare Atlassian token prefixes: ATATT (API), ATCTT + # (Confluence/Bitbucket scoped), ATOAT (OAuth). The old `AT[A-Z]TT` shape + # required a literal `TT` in positions 4-5, so it silently missed ATOAT + # (…OAT) and leaked OAuth tokens into context. Generic replacement label — + # don't leak which prefix (and thus token class) was present. + { pattern = "AT(?:ATT|CTT|OAT)[A-Za-z0-9_=.\\-]{20,}", replacement = "AT***REDACTED***" }, ] strip_lines_matching = [ "^\\s*$", @@ -41,12 +46,17 @@ expected = "TYPE\tKEY\tSUMMARY\tSTATUS\nStory\tPROJ-123\tAdd login\tIn Progress\ [[tests.acli]] name = "redacts bare ATATT Atlassian API token" input = "Authenticated. Your API token ATATT3xFfGF0abcDEF123ghiJKL456mnoPQR789stu=A1B2 is stored." -expected = "Authenticated. Your API token ATATT***REDACTED*** is stored." +expected = "Authenticated. Your API token AT***REDACTED*** is stored." [[tests.acli]] name = "redacts bare ATCTT scoped (Confluence/Bitbucket) token" input = "Authenticated. Your API token ATCTT3xFfGF0abcDEF123ghiJKL456mnoPQR789stu=A1B2 is stored." -expected = "Authenticated. Your API token ATATT***REDACTED*** is stored." +expected = "Authenticated. Your API token AT***REDACTED*** is stored." + +[[tests.acli]] +name = "redacts bare ATOAT Atlassian OAuth token" +input = "Authenticated. Your API token ATOAT3xFfGF0abcDEF123ghiJKL456mnoPQR789stu=A1B2 is stored." +expected = "Authenticated. Your API token AT***REDACTED*** is stored." [[tests.acli]] name = "single workitem view preserved" diff --git a/plugins/token-diet/ctx-wire/filters.d/dotnet-pack.toml b/plugins/token-diet/ctx-wire/filters.d/dotnet-pack.toml new file mode 100644 index 0000000..4040e1b --- /dev/null +++ b/plugins/token-diet/ctx-wire/filters.d/dotnet-pack.toml @@ -0,0 +1,79 @@ +# dotnet-pack (EN + FR) — token-diet multilingual filter for ctx-wire. +# `dotnet pack` runs a build then emits a NuGet packaging summary. On success +# it prints `Successfully created package '.nupkg'.` (often preceded by a +# benign "missing a readme" NOTE). Like `dotnet publish` it does NOT print a +# "Build succeeded." summary in .NET 10. Collapse a clean pack to a short ok; +# keep build/NuGet errors. Success + FR strings verified verbatim against .NET +# 10.0.301 (DOTNET_CLI_UI_LANGUAGE=fr): +# "Successfully created package '{0}'." -> "Création réussie du package '{0}'." +# No Romanian (.NET / NuGet ship none). +schema_version = 1 + +[filters.dotnet-pack] +description = "Compact dotnet pack output (EN+FR) - collapse a clean pack to ok, keep errors" +match_command = "^dotnet\\s+pack\\b" +strip_ansi = true +match_output = [ + { pattern = "(?mi)(Successfully created package|Création réussie du package)", message = "dotnet pack: ok", unless = "(?i)(:\\s*error|\\berror\\b|\\bNU[0-9]{3,4}\\b|Build FAILED|La génération a échoué|ÉCHEC de la build|\\bfailed\\b|\\béchec\\b)" }, +] +strip_lines_matching = [ + "^\\s*$", + "^Microsoft \\(R\\)", + "^Copyright \\(C\\)", + "^\\s*(Determining projects to restore|Identification des projets à restaurer)", + "^\\s*(Restoring packages for|Restauration des packages pour)", + "^\\s*(Restored |Restauration effectuée de )", + "^\\s*(All projects are up-to-date for restore|Tous les projets sont à jour pour la restauration)", + "^\\s*\\S+ -> .*\\.(dll|exe)$", + "^(Build succeeded|La génération a réussi)\\.?\\s*$", + "^(Time Elapsed|Temps écoulé) ", +] +max_lines = 60 + +# --- English --- +[[tests.dotnet-pack]] +name = "EN: success collapses (readme NOTE does not block)" +input = """ + Determining projects to restore... + All projects are up-to-date for restore. + MyApp -> /home/user/MyApp/bin/Release/net10.0/MyApp.dll + The package MyApp.1.0.0 is missing a readme. Go to https://aka.ms/nuget/authoring-best-practices/readme to learn why package readmes are important. + Successfully created package '/home/user/MyApp/bin/Release/MyApp.1.0.0.nupkg'. +""" +expected = "dotnet pack: ok" + +[[tests.dotnet-pack]] +name = "EN: build error preserved" +input = """ + Determining projects to restore... +/home/user/MyApp/Program.cs(1,24): error CS1002: ; expected [/home/user/MyApp/MyApp.csproj] +""" +expected = "/home/user/MyApp/Program.cs(1,24): error CS1002: ; expected [/home/user/MyApp/MyApp.csproj]" + +# --- French --- +[[tests.dotnet-pack]] +name = "FR: success collapses" +input = """ + Identification des projets à restaurer... + Tous les projets sont à jour pour la restauration. + MyApp -> /home/user/MyApp/bin/Release/net10.0/MyApp.dll + Il manque un fichier Lisez-moi au MyApp.1.0.0 du package. Accédez à https://aka.ms/nuget/authoring-best-practices/readme pour savoir pourquoi les lisez-moi de package sont importants. + Création réussie du package '/home/user/MyApp/bin/Release/MyApp.1.0.0.nupkg'. +""" +expected = "dotnet pack: ok" + +[[tests.dotnet-pack]] +name = "FR: build error preserved" +input = """ + Identification des projets à restaurer... +/home/user/MyApp/Program.cs(1,24): error CS1002: ; attendu [/home/user/MyApp/MyApp.csproj] +""" +expected = "/home/user/MyApp/Program.cs(1,24): error CS1002: ; attendu [/home/user/MyApp/MyApp.csproj]" + +[[tests.dotnet-pack]] +name = "NU packaging error not collapsed to ok" +input = """ + MyApp -> /home/user/MyApp/bin/Release/net10.0/MyApp.dll +error NU5017: Cannot create a package that has no dependencies nor content. +""" +expected = "error NU5017: Cannot create a package that has no dependencies nor content." diff --git a/plugins/token-diet/ctx-wire/filters.d/dotnet-publish.toml b/plugins/token-diet/ctx-wire/filters.d/dotnet-publish.toml new file mode 100644 index 0000000..8be1951 --- /dev/null +++ b/plugins/token-diet/ctx-wire/filters.d/dotnet-publish.toml @@ -0,0 +1,82 @@ +# dotnet-publish (EN + FR) — token-diet multilingual filter for ctx-wire. +# `dotnet publish` shares `dotnet build`'s restore/build preamble but — unlike +# build — does NOT print a "Build succeeded." / "0 Error(s)" summary in .NET 10 +# (verified against 10.0.301, piped and under a PTY). Its success signal is the +# publish artifact line ` -> /publish/`. So this is a STRIP filter, +# not a success-collapse: drop the restore banner + the intermediate `-> …dll` +# build arrow, and KEEP the `-> …/publish/` line plus any errors/warnings. +# +# Trim / AOT / self-contained publishes emit actionable `warning IL####` / +# `warning TrimmerXXXX` lines — those must survive, so we deliberately do NOT +# collapse to a bare "ok". FR strings verbatim from MSBuild/NuGet localization. +# No Romanian (.NET ships none). +schema_version = 1 + +[filters.dotnet-publish] +description = "Compact dotnet publish output (EN+FR) - strip restore/build preamble, keep the publish artifact line + errors/warnings" +match_command = "^dotnet\\s+publish\\b" +strip_ansi = true +strip_lines_matching = [ + "^\\s*$", + "^Microsoft \\(R\\)", + "^Copyright \\(C\\)", + "^\\s*(Determining projects to restore|Identification des projets à restaurer)", + "^\\s*(Restoring packages for|Restauration des packages pour)", + "^\\s*(Restored |Restauration effectuée de )", + "^\\s*(All projects are up-to-date for restore|Tous les projets sont à jour pour la restauration)", + # intermediate build arrow (`Proj -> …/MyApp.dll`); the publish arrow ends in + # `publish/` and is intentionally NOT matched here, so it survives. + "^\\s*\\S+ -> .*\\.(dll|exe)$", + "^(Build succeeded|La génération a réussi)\\.?\\s*$", + "^(Time Elapsed|Temps écoulé) ", +] +truncate_lines_at = 240 +max_lines = 200 + +# --- English --- +[[tests.dotnet-publish]] +name = "EN: preamble stripped, publish artifact kept" +input = """ + Determining projects to restore... + All projects are up-to-date for restore. + MyApp -> /home/user/MyApp/bin/Release/net10.0/MyApp.dll + MyApp -> /home/user/MyApp/bin/Release/net10.0/publish/ +""" +expected = " MyApp -> /home/user/MyApp/bin/Release/net10.0/publish/" + +[[tests.dotnet-publish]] +name = "EN: build error passes through" +input = """ + Determining projects to restore... + All projects are up-to-date for restore. +/home/user/MyApp/Program.cs(1,24): error CS1002: ; expected [/home/user/MyApp/MyApp.csproj] +""" +expected = "/home/user/MyApp/Program.cs(1,24): error CS1002: ; expected [/home/user/MyApp/MyApp.csproj]" + +[[tests.dotnet-publish]] +name = "EN: trim warnings preserved" +input = """ + MyApp -> /home/user/MyApp/bin/Release/net10.0/MyApp.dll +/home/user/MyApp/Program.cs(3,1): warning IL2026: Using member 'X' which has 'RequiresUnreferencedCodeAttribute' [/home/user/MyApp/MyApp.csproj] + MyApp -> /home/user/MyApp/bin/Release/net10.0/linux-x64/publish/ +""" +expected = "/home/user/MyApp/Program.cs(3,1): warning IL2026: Using member 'X' which has 'RequiresUnreferencedCodeAttribute' [/home/user/MyApp/MyApp.csproj]\n MyApp -> /home/user/MyApp/bin/Release/net10.0/linux-x64/publish/" + +# --- French --- +[[tests.dotnet-publish]] +name = "FR: preamble stripped, publish artifact kept" +input = """ + Identification des projets à restaurer... + Tous les projets sont à jour pour la restauration. + MyApp -> /home/user/MyApp/bin/Release/net10.0/MyApp.dll + MyApp -> /home/user/MyApp/bin/Release/net10.0/publish/ +""" +expected = " MyApp -> /home/user/MyApp/bin/Release/net10.0/publish/" + +[[tests.dotnet-publish]] +name = "FR: build error passes through" +input = """ + Identification des projets à restaurer... +/home/user/MyApp/Program.cs(1,24): error CS1002: ; attendu [/home/user/MyApp/MyApp.csproj] +""" +expected = "/home/user/MyApp/Program.cs(1,24): error CS1002: ; attendu [/home/user/MyApp/MyApp.csproj]" diff --git a/plugins/token-diet/ctx-wire/filters.d/dotnet-run.toml b/plugins/token-diet/ctx-wire/filters.d/dotnet-run.toml index 0295f58..7821e5d 100644 --- a/plugins/token-diet/ctx-wire/filters.d/dotnet-run.toml +++ b/plugins/token-diet/ctx-wire/filters.d/dotnet-run.toml @@ -64,7 +64,7 @@ input = """ Test run summary: Passed! total: 42 failed: 0 - passed: 42 + succeeded: 42 skipped: 0 duration: 1s """ @@ -77,11 +77,11 @@ failed MyNamespace.MyTests.Test1 Test run summary: Failed! total: 42 failed: 2 - passed: 40 + succeeded: 40 skipped: 0 duration: 1s """ -expected = "failed MyNamespace.MyTests.Test1\nTest run summary: Failed!\n total: 42\n failed: 2\n passed: 40\n skipped: 0\n duration: 1s" +expected = "failed MyNamespace.MyTests.Test1\nTest run summary: Failed!\n total: 42\n failed: 2\n succeeded: 40\n skipped: 0\n duration: 1s" # --- French (plain app) --- [[tests.dotnet-run]] @@ -104,14 +104,16 @@ src/Program.cs(10,5): error CS1002: ; attendu expected = "src/Program.cs(10,5): error CS1002: ; attendu\nÉCHEC de la build." # --- French (MTP test project run via dotnet run) --- +# FR MTP block label is `réussie :` (past participle), header `… : Réussite!` +# — verified against .NET 10.0.301 (DOTNET_CLI_UI_LANGUAGE=fr). [[tests.dotnet-run]] name = "FR MTP: all-passed test run collapses" input = """ MyTests -> /home/user/MyTests/bin/Debug/net8.0/MyTests.dll -Résumé de série de tests : Réussite ! +Résumé de série de tests : Réussite! total : 42 échec : 0 - réussite : 42 + réussie : 42 ignoré : 0 durée : 1s """ diff --git a/plugins/token-diet/ctx-wire/filters.d/dotnet-test.toml b/plugins/token-diet/ctx-wire/filters.d/dotnet-test.toml index e493742..ac5420c 100644 --- a/plugins/token-diet/ctx-wire/filters.d/dotnet-test.toml +++ b/plugins/token-diet/ctx-wire/filters.d/dotnet-test.toml @@ -9,7 +9,9 @@ # "{0} - échec : {1}, réussite : {2}, ignorée(s) : {3}, total : {4}, durée : {5}" # "Error Message:" -> "Message d'erreur :" # MTP: "Test run summary:" -> "Résumé de série de tests :" -# total/failed/passed/skipped/duration -> total/échec/réussite/ignoré/durée +# total/failed/succeeded/skipped/duration -> total/échec/réussie/ignoré/durée +# (EN field label is `succeeded:`, FR is `réussie :` — verified against +# .NET 10.0.301; NOT `passed:` / `réussite :`) # "Zero tests ran" -> "Zéro tests exécutés" # .NET ships no Romanian satellite resources, so EN+FR covers every locale. schema_version = 1 @@ -71,13 +73,19 @@ Message d'erreur : expected = "Échec ! - échec : 1, réussite : 41, ignorée(s) : 0, total : 42, durée : 1 s\nMessage d'erreur :\n Attendu true mais a obtenu false" # --- English (MTP, Microsoft.Testing.Platform) --- +# Field label is `succeeded:` (verified against .NET 10.0.301 `dotnet test` +# with the MTP runner, global.json `test.runner = Microsoft.Testing.Platform`), +# NOT `passed:`. The header is `Test run summary:` (with "run") in every path +# checked — single project, solution, and multi-project solution roll-up — so +# the trigger needs no `Test summary:` (no-"run") variant; that string does not +# occur in the .NET 10 SDK or Microsoft.Testing.Platform assemblies. [[tests.dotnet-test]] name = "EN MTP: summary block collapses" input = """ Test run summary: Passed! total: 42 failed: 0 - passed: 42 + succeeded: 42 skipped: 0 duration: 1s """ @@ -91,20 +99,55 @@ failed MyNamespace.MyTests.Test1 Test run summary: Failed! total: 42 failed: 2 - passed: 40 + succeeded: 40 skipped: 0 duration: 1s """ -expected = "failed MyNamespace.MyTests.Test1\n Assert.True failed\nTest run summary: Failed!\n total: 42\n failed: 2\n passed: 40\n skipped: 0\n duration: 1s" +expected = "failed MyNamespace.MyTests.Test1\n Assert.True failed\nTest run summary: Failed!\n total: 42\n failed: 2\n succeeded: 40\n skipped: 0\n duration: 1s" + +# --- English (MTP native `dotnet test` integration, .NET 10 SDK) --- +# The SDK-integrated runner prints a per-app status line followed by the same +# `Test run summary:` block. Verified verbatim against .NET 10.0.301. +[[tests.dotnet-test]] +name = "EN MTP native (dotnet test): per-app line + summary collapses" +input = """ +App.Tests.dll (net10.0|x64) passed (269ms) +Test run summary: Passed! + total: 42 + failed: 0 + succeeded: 42 + skipped: 0 + duration: 500ms +""" +expected = "dotnet test: ok" + +[[tests.dotnet-test]] +name = "EN MTP native (dotnet test): failures preserved" +input = """ +App.Tests.dll (net10.0|x64) failed with 1 error(s) (269ms) +Test run summary: Failed! + total: 5 + failed: 1 + succeeded: 3 + skipped: 1 + duration: 471ms +""" +expected = "App.Tests.dll (net10.0|x64) failed with 1 error(s) (269ms)\nTest run summary: Failed!\n total: 5\n failed: 1\n succeeded: 3\n skipped: 1\n duration: 471ms" # --- French (MTP) --- +# The MTP block's success label is `réussie :` (past participle), NOT the VSTest +# noun `réussite :`. Header renders as `Résumé de série de tests : Réussite!` / +# `… : Échec!` (space before the colon, none before the bang). Verified verbatim +# against .NET 10.0.301 with DOTNET_CLI_UI_LANGUAGE=fr. Collapse keys on the +# `Résumé de série de tests` header, so the label discrepancy never broke the +# match — but the fixtures now assert the real wording. [[tests.dotnet-test]] name = "FR MTP: summary block collapses" input = """ -Résumé de série de tests : Réussite ! +Résumé de série de tests : Réussite! total : 42 échec : 0 - réussite : 42 + réussie : 42 ignoré : 0 durée : 1s """ @@ -115,14 +158,14 @@ name = "FR MTP: failures preserved" input = """ échec MyNamespace.MyTests.Test1 Échec de Assert.True -Résumé de série de tests : Échec ! +Résumé de série de tests : Échec! total : 42 échec : 2 - réussite : 40 + réussie : 40 ignoré : 0 durée : 1s """ -expected = "échec MyNamespace.MyTests.Test1\n Échec de Assert.True\nRésumé de série de tests : Échec !\n total : 42\n échec : 2\n réussite : 40\n ignoré : 0\n durée : 1s" +expected = "échec MyNamespace.MyTests.Test1\n Échec de Assert.True\nRésumé de série de tests : Échec!\n total : 42\n échec : 2\n réussie : 40\n ignoré : 0\n durée : 1s" [[tests.dotnet-test]] name = "MTP: zero tests not collapsed to ok"