Compare commits

...

835 Commits

Author SHA1 Message Date
Stef Heyenrath
facd2b66ca Squad Test 2026-04-16 16:29:28 +02:00
dependabot[bot]
6e2a4d7e04 Bump log4net from 2.0.15 to 3.3.0 (#1440)
---
updated-dependencies:
- dependency-name: log4net
  dependency-version: 3.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-16 07:49:17 +02:00
Jayaraman Venkatesan
479bb0b8ec bug/wiremock-1268 moving Scenario state change before global response delay (#1436)
Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
2026-04-03 10:51:24 +02:00
Stef Heyenrath
a453e00fdb Fix WireMock.Net.WebApplication.IIS example (#1435) 2026-03-31 22:52:27 +02:00
Stef Heyenrath
3214c2ebc7 2.2.0 2026-03-30 19:51:09 +02:00
Stef Heyenrath
6c6a42979e Add comments for ScenarioStateStore related code (#1433) 2026-03-30 19:49:28 +02:00
Stef Heyenrath
b4f5b9256c Upgrade Scriban.Signed (#1434) 2026-03-30 19:49:16 +02:00
Stef Heyenrath
070e4b6ab9 2.1.0 2026-03-29 13:16:23 +02:00
m4tchl0ck
f919929cb7 Add injectable IScenarioStateStore for distributed scenario state (#1430)
* Move ScenarioState to Abstractions and add IScenarioStateStore interface

ScenarioState is moved to the Abstractions project so it can be referenced
by the new IScenarioStateStore interface. The interface defines the contract
for storing and retrieving scenario states, enabling distributed implementations.

* Add InMemoryScenarioStateStore default implementation

Wraps ConcurrentDictionary with OrdinalIgnoreCase comparer, preserving
exact current behavior. The Update method encapsulates read-modify-write
so distributed implementations can make it atomic.

* Wire IScenarioStateStore into middleware options, settings, and consumers

Replace direct ConcurrentDictionary<string, ScenarioState> usage with
IScenarioStateStore across all consumer files. The store is injectable
via WireMockServerSettings.ScenarioStateStore, defaulting to the
InMemoryScenarioStateStore for backward compatibility.

* Add FileBasedScenarioStateStore for persistent scenario state

In-memory ConcurrentDictionary backed by JSON file persistence in
__admin/scenarios/. Reads from cache, mutations write through to disk.
Constructor loads existing state from disk on startup.

* Make ScenarioStateStore non-nullable with default InMemoryScenarioStateStore

Move InMemoryScenarioStateStore from WireMock.Net.Minimal to
WireMock.Net.Shared so it lives alongside WireMockServerSettings.
This allows WireMockServerSettings.ScenarioStateStore to be
non-nullable with a default value, following the same pattern as
DefaultJsonSerializer. The null-coalescing fallback in
WireMockMiddlewareOptionsHelper is no longer needed.
2026-03-25 13:04:44 +01:00
Logan Dam
cdd33695e5 Add helpers for query params fluent MappingModelBuilder (#1425)
* Add helpers for query params

* add example with query params

* add fluent helpers for WithHeaders and WithCookies

---------

Co-authored-by: Logan Dam <Logan.Dam@rabobank.com>
Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
2026-03-14 10:39:30 +01:00
Stef Heyenrath
c4caa25eb6 Fix WithWebSocketProxy_Should_Proxy_Binary_Messages 2026-03-14 10:39:04 +01:00
Stef Heyenrath
ca788cb9b0 Add WireMockAspNetCoreLogger to log Kestrel warnings/errors (#1432)
* Add WireMockAspNetCoreLogger

* fix tests

* x

* .
2026-03-14 10:30:56 +01:00
Stef Heyenrath
d08ce944b6 Fix WireMockLogger implementation in dotnet-WireMock (#1431) 2026-03-13 18:34:47 +01:00
Stef Heyenrath
0a9f37e857 readme 2026-03-11 18:14:09 +01:00
Stef Heyenrath
291b32a058 readme 2026-03-11 17:12:37 +01:00
Stef Heyenrath
d6ac24490d 2.0.0 2026-03-11 17:11:09 +01:00
Stef Heyenrath
a292f28dda Version 2.x (#1359)
* Version 2.x

* Setup .NET 9

* 12

* cleanup some #if for NETSTANDARD1_3

* cleanup + fix tests for net8

* openapi

* NO ConfigureAwait(false) + cleanup

* .

* #endif

* HashSet

* WireMock.Net.NUnit

* HttpContext

* Add WebSockets (#1423)

* Add WebSockets

* Add tests

* fix

* more tests

* Add tests

* ...

* remove IOwin

* -

* tests

* fluent

* ok

* match

* .

* byte[]

* x

* func

* func

* byte

* trans

* ...

* frameworks.........

* jmes

* xxx

* sc

* using var httpClient = new HttpClient();

* usings

* maxRetries

* up

* xunit v3

* ct

* ---

* ct

* ct2

* T Unit

* WireMock.Net.TUnitTests / 10

* t unit first

* --project

* no tunit

* t2

* --project

* --project

* ci -  --project

* publish ./test/wiremock-coverage.xml

* windows

* .

* log

* ...

* log

* goed

* BodyType

* .

* .

* --scenario

* ...

* pact

* ct

* .

* WireMock.Net.RestClient.AwesomeAssertions (#1427)

* WireMock.Net.RestClient.AwesomeAssertions

* ok

* atpath

* fix test

* sonar fixes

* ports

* proxy test

* FIX?

* ---

* await Task.Delay(100, _ct);

* ?

* --project

* Aspire: use IDistributedApplicationEventingSubscriber (#1428)

* broadcast

* ok

* more tsts

* .

* Collection

* up

* .

* 2

* remove nfluent

* <VersionPrefix>2.0.0-preview-02</VersionPrefix>

* ...

* .

* nuget icon

* .

* <PackageReference Include="JmesPath.Net" Version="1.1.0" />

* x

* 500

* .

* fix some warnings

* ws
2026-03-11 17:02:47 +01:00
Stef Heyenrath
d6e19532bc Fix building example projects 2026-03-02 19:24:10 +01:00
Stef Heyenrath
df85649b67 MappingSerializer (Newtonsoft or SystemText)-Json (#1394)
* MappingSerializer

* json

* .

* 0.8.0

* test

* mm
2026-02-14 08:42:18 +01:00
Stef Heyenrath
f73bd5fc4f 1.25.0 2026-01-25 10:08:32 +01:00
Stef Heyenrath
702e156ddc Fix MimePartMatcher and add more tests (#1389)
* mp

* .

* --return

* Fixed

* --

* ...

* fix

* ...

* .
2026-01-24 09:15:43 +01:00
Stef Heyenrath
317fcb1b30 1.24.0 2026-01-18 19:54:34 +01:00
Stef Heyenrath
4b602dd777 Small updates to WireMock.Net.OpenTelemetry 2026-01-18 17:56:07 +01:00
Petr Houška
4525c61847 Add OTEL tracing support for Wiremock + automatic OTEL for Aspire integration (#1418)
* Update aspire to 13.1 (examples + code)

Allows usage of aspire CLI which is very useful for dev in codespaces (for my next PR).

* Add OTEL support

* Initial PR feedback

* PR feedback

* PR feedback

* PR feedback

* Cleanup.

* Cleanup

* Fix

* Fix

* Rename stuff around to be more accurate

* PR feedback

* Update WireMock.Net.OpenTelemetry.csproj

Update <Authors>

* PR feedback parser

* PR feedback package versions

* Status code feedback.

* Update preprocessor directives to to Activity Tracing instead of OpenTelemetry. Is more descriptive.

* Add tests

* Improve tests

---------

Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
2026-01-18 17:22:36 +01:00
Stef Heyenrath
abe996671e Add Copilot Setup Steps action (#1419) 2026-01-09 18:20:12 +01:00
Petr Houška
9f819de696 Update aspire to 13.1 (examples + code) (#1417)
Allows usage of aspire CLI which is very useful for dev in codespaces (for my next PR).
2026-01-09 18:01:45 +01:00
Stef Heyenrath
f5d53453e5 1.23.0 2026-01-05 21:34:11 +01:00
samlatham
0e60e3f3f9 Fix: Pass AllowedHandlebarsHelpers configuration to Handlebars.Net.Helpers (#1416)
Pass AllowedHandlebarsHelpers configuration to Handlebars.Net.Helpers so that optional handlebars helpers can be enabled.

Co-authored-by: Sam Latham <sam.latham@citrix.com>
2026-01-05 21:24:48 +01:00
Luca Ma
9cee6dde00 Pass the parameter matchOperator in Request.WithPath to its inner calls (#1414)
Co-authored-by: Luca Ma <lucama@microsoft.com>
2026-01-04 08:03:19 +01:00
Stef Heyenrath
c88e7378a7 1.22.0 2026-01-02 21:30:59 +01:00
Vadim Hatsura
b090296559 chore(testcontainers): bump up Testcontainers to version 4.10.0 (#1412) 2026-01-02 21:25:28 +01:00
Stef Heyenrath
e5afd69f7c 1.21.0 2025-12-25 15:00:54 +01:00
Stef Heyenrath
f38133d7a4 Fix readyness-check for Testcontainers (#1408)
* Add XUnit Logging to TestcontainersTests

* .
2025-12-25 13:56:29 +01:00
Stef Heyenrath
597c95000e vmImage: 'windows-2025' (#1407) 2025-12-24 16:59:02 +01:00
Stef Heyenrath
4617b99c30 [Collection("Grpc")] 2025-12-24 12:32:56 +01:00
Stef Heyenrath
ffd4d89946 Re-enable TestcontainersTestsGrpc (#1406)
* Re-enable TestcontainersTestsGrpc

* //[Collection("Grpc")]
2025-12-24 12:16:56 +01:00
Stef Heyenrath
2d46c86f47 1.20.0 2025-12-24 10:11:53 +01:00
Stef Heyenrath
75f4fbe9d0 Fix Testcontainers AddProtoDefinition (#1405)
* Fix Testcontainers AddProtoDefinition

* .

* UntilHttpRequestIsSucceeded

* WireMockContainer.ContainerPort

* System.Net/System.Net.Http

* ...

* WithWaitStrategy

* MaxHealthCheckRetries

* for

* _adminApi

* static

* ...

* testOutputHelper.WriteLine("Dumping WireMock logs:");

* Console.WriteLine(

* testOutputHelper.WriteLine("Dumping WireMock.Net mappings:");

* fix WithWaitStrategy

* [Fact]

* <PackageReference Include="ProtoBufJsonConverter" Version="0.11.0" />

* [Collection("Grpc")] / [Fact(Skip = "TODO")]

* ...
2025-12-24 10:09:30 +01:00
Stef Heyenrath
16e3872402 Run the Grpc TestcontainersTests sequential (#1402) 2025-12-21 09:40:16 +01:00
Stef Heyenrath
4c797c328f Add WireMock.Net.NUnit project (#1400)
* Add WireMock.Net.NUnit project

* <Version>0.0.1-preview-01</Version>

* --v
2025-12-20 13:43:54 +01:00
Stef Heyenrath
a5e75a7278 Fix Grpc tests (#1401)
* Fix some Grpc tests

* await Task.Delay(1000);

* ports
2025-12-20 12:08:41 +01:00
Stef Heyenrath
56f65c19e2 Upgrade RamlToOpenApiConverter and YamlDotNet (#1399)
* Upgrade RamlToOpenApiConverter and YamlDotNet

* fix
2025-12-19 18:33:58 +01:00
Stef Heyenrath
6aef4816a5 WireMockServer_WithRequiredClientCertificates_Should_Work_Correct --> IgnoreOnContinuousIntegrationFact 2025-12-19 17:52:20 +01:00
Stef Heyenrath
197a211a52 TestcontainersTests 2025-12-13 11:48:48 +01:00
Stef Heyenrath
3cfeec6035 1.19.0 2025-12-12 11:16:38 +01:00
Stef Heyenrath
b57d5e7548 WireMockContainerBuilder: allow only docker images named wiremock (#1392) 2025-12-11 22:21:39 +01:00
Stef Heyenrath
36b89afce5 fix CI link in Readme 2025-12-11 11:25:28 +01:00
Stef Heyenrath
e2acac55a4 Update WireMockContainerBuilder (WithImage and WithCustomImage) (#1391)
* Update WireMockContainerBuilder (WithImage and WithCustomImage)

* .
2025-12-11 10:55:31 +01:00
Stef Heyenrath
ceabd27ce0 1.18.0 2025-12-09 18:28:28 +01:00
Stef Heyenrath
f8e2c7ee90 Add WithBodyAsType to RequestMatcher (#1388)
* Add WithBody<T>

* .

* t

* t2
2025-12-08 19:15:14 +01:00
Stef Heyenrath
c25d8f33d2 1.17.0 2025-12-07 10:55:07 +01:00
Stef Heyenrath
6da190e596 Aspire: Add WithProtoDefinition to support proto definition at server level (#1383)
* Add property UseHttp2 to WireMockServerArguments

* .

* additionalUrls

* ok?

* WireMockServerArguments

* fx

* AddProtoDefinition

* ...

* FIX

* Always add the lifecycle hook to support dynamic mappings and proto definitions
2025-12-07 10:50:11 +01:00
Stef Heyenrath
44388ce80d Fix random delay in mapping json file (#1386) 2025-11-25 20:54:06 +01:00
Stef Heyenrath
5e25ca767d Fix BuildId (#1384) 2025-11-23 11:19:39 +01:00
Stef Heyenrath
0cc583a4a3 WireMock.Net.xUnit.v3 (netstandard2.0) 2025-11-18 18:52:07 +01:00
Stef Heyenrath
f9633adac1 1.16.0 2025-11-18 18:45:12 +01:00
Stef Heyenrath
37bad618a3 Add WireMock.Net.xUnit.v3 project (#1380)
* Add WireMock.Net.xUnit.v3 project

* .
2025-11-18 18:42:28 +01:00
Johannes Häggqvist
8e69f36f04 Add WireMockHealthCheck in WireMock.Net.Aspire (#1375)
* Add WireMockHealthCheck

For use with Aspire, to make WaitFor(wiremock) more useful.
Calls /__admin/health and checks the result, as well as checks if mappings using AdminApiMappingBuilder has been submitted to the server.

This created a catch-22 problem where the mappings were not submitted until the health check was healthy, but the health check was not healthy until the mappings were submitted.

To avoid this, the WireMockServerLifecycleHook class has been slightly re-arranged, and is now using the AfterEndpointsAllocatedAsync callback rather than the AfterResourcesCreatedAsync callback. Within which a separate Task is created that waits until the server is ready and submits the mappings.

* Move WireMockMappingState to its own file

* Dispose the cancellation tokens in WireMockServerLifecycleHook
2025-11-17 20:14:42 +01:00
Stef Heyenrath
21601889e0 Check if the path is valid when using WithPath(...) (#1377) 2025-11-08 09:02:00 +01:00
Stef Heyenrath
dfeabf228e WireMock.Net.OpenApiParser : support Examples (#1366) 2025-11-08 07:45:38 +01:00
Stef Heyenrath
1feb0ade70 Fix wiki links (#1373)
* Change all links from wiki to documention website

* .

* doc

* ws
2025-10-26 10:13:58 +01:00
Stef Heyenrath
8b1bd1b21b 1.15.0 2025-10-22 10:58:44 +02:00
Michi
c1b23b615e Support Testcontainers 4.8.0 (#1370) 2025-10-22 10:18:16 +02:00
Stef Heyenrath
5885324dfb Fix WithProbability logic (#1367)
* Fix WithProbability logic

* .

* FIX

* Update src/WireMock.Net.Minimal/Owin/MappingMatcher.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-22 10:16:59 +02:00
Tom Akehurst
e3f3e0a8f2 Tweaked docs message in README 2025-10-13 20:29:29 +01:00
Tom Akehurst
685d28db0b Added link to new doc site from README.md 2025-10-13 20:23:18 +01:00
Stef Heyenrath
f6c5225fe0 1.14.0 2025-10-06 17:00:19 +02:00
Stef Heyenrath
b9019a2f61 Update ProxyUrlReplaceSettingsModel with TransformTemplate property (#1362)
* Update ProxyUrlReplaceSettingsModel with TransformTemplate property + parse settings correctly

* oldValue nullable

* <Version>1.14.0-preview-01</Version>
2025-10-06 09:16:25 +02:00
Stef Heyenrath
b82dad2563 Add Tls13 (#1363)
* Add Tls13

* fix
2025-10-05 15:51:47 +02:00
Stef Heyenrath
45d4e7077d 1.13.0 2025-09-28 12:44:14 +02:00
Stef Heyenrath
19e95325fa ProxyUrlTransformer (#1361)
* ProxyUrlTransformer

* tests

* Update src/WireMock.Net.Shared/Settings/ProxyUrlReplaceSettings.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-28 12:40:33 +02:00
Stef Heyenrath
371bfdc160 TypeLoader: implement Try methods (#1358)
* TypeLoader: implement Try methods

* fix
2025-08-31 08:48:29 +02:00
Stef Heyenrath
5c5e104f2c 1.12.0 2025-08-30 11:49:20 +02:00
Stef Heyenrath
068fdf33e3 Upgrade Testcontainers to 4.7.0 (#1357)
* Upgrade Testcontainers to 4.7.0

* .
2025-08-30 11:46:52 +02:00
Stef Heyenrath
358590918e 1.11.2 2025-08-27 10:25:31 +02:00
Stef Heyenrath
32afea5d30 Revert JetBrains.Annotations and add System.Text.RegularExpressions to solve CVE (#1355) 2025-08-27 10:04:36 +02:00
Stef Heyenrath
50b3d58a01 1.11.1 2025-08-27 08:47:58 +02:00
Stef Heyenrath
35bf5e94a5 Add System.Net.Http again to solve CVE warning (#1354) 2025-08-27 08:45:24 +02:00
Stef Heyenrath
9fcc9ade10 Add additional try-catch to TypeLoader logic (#1352) 2025-08-27 08:33:01 +02:00
Stef Heyenrath
865bbf2432 WireMock.Net.ProtoBuf: fix version 2025-08-26 15:42:23 +02:00
Stef Heyenrath
560540099e 1.11.0 2025-08-26 07:58:04 +02:00
Stef Heyenrath
e5c4605020 Create WireMock.Net.ProtoBuf project (#1350)
* Create WireMock.Net.ProtoBuf project

* ok

* Update Directory.Build.props

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-26 07:55:02 +02:00
Stef Heyenrath
124d29c86a 1.10.1 2025-08-22 19:58:00 +02:00
Stef Heyenrath
08117e870b Add AtPath and AtAbsolutePath to Assertions projects (#1349)
* Add AtPath and AtAbsolutePath to Assertions projects

* tst
2025-08-22 19:40:59 +02:00
Stef Heyenrath
ddb181cc52 WireMock.Net.Extensions.Routing 2025-08-18 20:47:10 +02:00
Stef Heyenrath
10fdd24fca 1.10.0 2025-08-18 20:22:49 +02:00
Gennadii Saltyshchak
be2ea67b89 Add new package WireMock.Net.Extensions.Routing which provides minimal-API-style routing for WireMock.Net (#1344)
* Add new package WireMock.Net.Extensions.Routing

* Update documentation for WireMock.Net.Extensions.Routing

* Cleanup imports

* Add header to all source files inside WireMock.Net.Extensions.Routing

* Add header to all source files inside WireMock.Net.Extensions.Routing.Tests

* Revert unintended changes

* Remove redundant build configurations

* Remove incorrect links from documentation

* Update nuget package references

* Revert unintended changes

* Migrate to AwesomeAssertions

* Remove redundant project reference

* Adjust formatting

* Migrate to primary constructor

* Refactoring: rename delegate parameter

* Abstract over JSON converter

* Replace WireMock with WireMock.Net in comments

* Move local functions to the bottom of the methods
2025-08-18 19:52:42 +02:00
Stef Heyenrath
60eb519ae2 1.9.1 2025-08-17 10:11:48 +02:00
Stef Heyenrath
22ed94918a Fix generating source code for Scenario and State (#1347)
* Fix generating source code for Scenario and State

* Update src/WireMock.Net.Minimal/Serialization/MappingConverter.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.Minimal/Serialization/MappingConverter.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-17 10:06:39 +02:00
Stef Heyenrath
faffc56484 Add TimesInSameState to MappingModel (#1345)
* Add TimesInSameState to MappingModel

* fix tests
2025-08-11 08:46:18 +02:00
Stef Heyenrath
a5558777e2 1.9.0 2025-08-10 19:07:24 +02:00
Stef Heyenrath
6722ca40ba Update feature_request.md 2025-08-10 19:02:24 +02:00
Stef Heyenrath
0597a73e0e Create GraphQL project (#1334)
* Create new project for GraphQL

* ...

* .

* ok?

* Update src/WireMock.Net.Shared/Extensions/AnyOfExtensions.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* --

* ...

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-10 19:00:22 +02:00
Stef Heyenrath
0d510cdde8 1.8.18 2025-08-04 18:03:44 +02:00
Stef Heyenrath
52a396beef 1.8.18 2025-08-04 18:03:23 +02:00
Sam Fields
6ccfe68686 Fixes an issue with matching JSON bodies as bytes (#1339)
* Fixes an issue with matching JSON bodies as bytes

* Adding tests for exact object matching

* Simplify the check for byte data
2025-08-02 20:11:13 +02:00
Stef Heyenrath
e400e92452 1.8.17 2025-07-23 09:40:55 +02:00
Stef Heyenrath
7a187dfb78 Add more examples for WithGraphQLSchema (#1335) 2025-07-23 09:34:31 +02:00
Stef Heyenrath
e6ff8776fb Make CSharpCodeMatcher public (#1337) 2025-07-23 09:29:53 +02:00
Stef Heyenrath
c32e904f4d 1.8.16 2025-07-19 08:16:34 +02:00
Stef Heyenrath
e80d436dd6 Use corerct Handlebars.Net.Helpers.Xslt (2.5.2) (#1332) 2025-07-18 10:57:58 +02:00
Stef Heyenrath
fcc95ff06f 1.8.15 2025-07-18 08:45:42 +02:00
Stef Heyenrath
020cc15420 Correctly map the Pact Interaction Description property (#1331)
* Correctly map the Pact Interaction Description property

* Update src/WireMock.Net.Minimal/Serialization/PactMapper.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* post

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-18 08:41:04 +02:00
Stef Heyenrath
aeb15725e4 1.8.14 2025-07-13 08:53:53 +02:00
Stef Heyenrath
a06ee6b158 Fix HandlebarsContext.ParseAndEvaluate (#1329) 2025-07-12 11:05:02 +02:00
Stef Heyenrath
b0076b4e81 Implement IMimeMessageData (#1326)
* Implement IMimeMessageData

* 1

* Update src/WireMock.Net.MimePart/Util/MimeKitUtils.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* v1

* v2

* e

* ?

* fix

* if (Array.TrueForAll(_funcs, func => func(value).IsPerfect()))

* Update src/WireMock.Net.Shared/Util/IMimeKitUtils.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.Minimal/Matchers/Request/RequestMessageMultiPartMatcher.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.MimePart/Models/MimeEntityDataWrapper.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Models.Mime.IMimeMessageData? BodyAsMimeMessage { get; }

* Update src/WireMock.Net.MimePart/Util/MimeKitUtils.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.MimePart/Models/MimePartDataWrapper.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.MimePart/Models/MimeMessageDataWrapper.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.Shared/Util/IMimeKitUtils.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-12 09:54:18 +02:00
Stef Heyenrath
6c61f87ef3 Add method CreateHttpClientFactory (#1325)
* Add method CreateHttpClientFactory

* rev
2025-07-08 10:50:30 +02:00
Stef Heyenrath
35cd06b47b Update README.md 2025-06-24 09:31:22 +02:00
Stef Heyenrath
b925c537c7 1.8.13 2025-06-23 08:07:30 +02:00
Stef Heyenrath
f80925c1fb Add Scenario set State method (#1322)
* Add SetScenarioState

* add tests

* summary

* .

* 1.8.13-preview-01

* fix

* fix name
2025-06-23 08:03:11 +02:00
Stef Heyenrath
43cff52b69 1.8.12 2025-06-15 11:50:31 +02:00
Stef Heyenrath
7b93b2668d Fix TypeLoader (#1320)
* no ilmerge

* .

* .

* nullable

* .UsingNuGet

* fix

* .

* directoriesToSearch

* .
2025-06-15 11:44:09 +02:00
Dom Light
70a9180af4 Set description when converting MappingModel to IRespondWithAProvider (#1317)
Adding a mapping with a description to `WireMockServer.WithMapping`
did not include the description to the resulting
`IRespondWithAProvider`, which means that calling
`WireMockServer.SavePact` does not populate the description in the
contract file.

This PR includes the description when mapping from `MappingModel` to
`IRespondWithAProvider`.
2025-06-14 11:13:04 +02:00
Stef Heyenrath
acd6592562 remove some old console projects 2025-06-13 13:07:48 +02:00
Sébastien Crocquesel
2a010dcd42 Use default resource cleaning behavior in test (#1316) 2025-06-12 22:06:58 +02:00
Stef Heyenrath
8151119cca 1.8.11 2025-06-11 12:04:21 +02:00
Stef Heyenrath
77000372c6 Fix for WithTransformer and JsonBody as list (#1315)
* Fix for WithTransformer and JsonBody as list

* Fix WithTransformer when the response BodyAsJson is a List
2025-06-11 11:51:29 +02:00
Stef Heyenrath
ec248a9a78 Fix TestcontainersTests to ignore exception when stopping (#1314) 2025-06-11 11:12:58 +02:00
Stef Heyenrath
2f7e3a3178 Update TestcontainersTests to ignore exception when stopping 2025-06-11 10:21:42 +02:00
Stef Heyenrath
ac9c51e34e Update RandomDataGenerator.Net to 1.0.19 (#1313)
* Update RandomDataGenerator.Net to 1.0.19

* Handlebars.Net.Helpers
2025-06-11 09:00:23 +02:00
Sébastien Crocquesel
8ba243ddcd Bump Testcontainers version to 4.5.0 (#1311)
* Bump Testcontainers version to 4.5.0

The Testcontainers dependency Docker.DotNet was bumped to 3.128.1 and is not binary compatible with previous version.
When a user has a direct dependency on Testcontainers 4.5.0, WireMock.Net.Testcontainers fails with :

System.MissingMethodException : Method not found: 'Docker.DotNet.DockerClient Docker.DotNet.DockerClientConfiguration.CreateClient(System.Version)'

* Bump System.Net.Http.Json version to 8.0.1

Minimum required version for Testcontainers 4.5.0

* Do not dispose null container
2025-06-10 22:23:56 +02:00
Stef Heyenrath
d4b95e73ea Remove unit test which uses postman-echo 2025-06-10 13:16:24 +02:00
Stef Heyenrath
f9ae045847 Images/Icons 2025-06-10 12:23:07 +02:00
Stef Heyenrath
05b5876b5c 1.8.10 2025-06-10 08:15:22 +02:00
Stef Heyenrath
c1bd2d315f Update AwesomeAssertions to version 9 (#1309) 2025-06-06 21:06:38 +02:00
Stef Heyenrath
8917a6eaaa For some projects, change dependency to WireMock.Net.Minimal (#1308) 2025-06-06 11:22:34 +02:00
Stef Heyenrath
3cc9040f51 Update RequestModelBuilder (add WithHeader) (#1306)
* Update RequestModelBuilder (add WithHeader)

* rejectOnMatch

* fix
2025-06-05 12:24:40 +02:00
Stef Heyenrath
6136bc177c 1.8.9 2025-05-28 07:18:51 +02:00
Stef Heyenrath
86d4717216 Add option to provide X509Certificate (#1303)
* Add option to provide X509CertificateRawData

* X509Certificate

* remove X509CertificateRawData
2025-05-28 07:15:46 +02:00
Stef Heyenrath
3438539138 1.8.8 2025-05-24 12:34:30 +02:00
Stef Heyenrath
96eca4262a Create WireMock.Net.MimePart project (#1300)
* Create WireMock.Net.MimePart project

* .

* REFACTOR

* ILRepack

* --

* ...

* x

* x

* .

* fix

* public class MimePartMatcher

* shared

* min

* .

* <!--<DelaySign>true</DelaySign>-->

* Update README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-24 12:17:42 +02:00
Stef Heyenrath
c15206ecd8 Update README.md [codecov] 2025-05-22 15:30:01 +02:00
Stef Heyenrath
ec15c544c4 Update README.md (fix codefactor link) 2025-05-22 08:46:11 +02:00
Stef Heyenrath
339d3ab3a8 Fix CSharpFormatterTests 2025-05-22 08:42:23 +02:00
Stef Heyenrath
001ba03ee9 1.8.7 2025-05-22 08:22:48 +02:00
Stef Heyenrath
17545da2c3 Add extra unit test test to CSharpFormatterTests 2025-05-22 08:14:45 +02:00
Asaf Agami
b4279be3cb Fix exception when converting json array to C# code (#1301) 2025-05-22 07:54:58 +02:00
Stef Heyenrath
d628ce2270 Fix BodyParser to correctly check for json (#1297)
* Fix BodyParser to correctly check for json

* JsonUtils
2025-05-21 17:28:29 +02:00
Stef Heyenrath
1e23c58bf2 Use TinyMapper.Signed (#1299)
* Use TinyMapper.Signed

* <PackageReference Include="TinyMapper.Signed" Version="4.0.0" />
2025-05-17 18:29:03 +02:00
Stef Heyenrath
9b5801f828 1.8.6 2025-05-15 20:16:17 +02:00
Stef Heyenrath
61b6eb8752 Content-Type multipart/form-data header should also be proxied (#1296) 2025-05-15 18:21:21 +02:00
Stef Heyenrath
baa33552e9 1.8.5 2025-05-14 07:34:04 +02:00
Stef Heyenrath
492f01ade1 Add more tests for WireMockOpenApiParser (#1294) 2025-05-13 22:11:25 +02:00
Stef Heyenrath
7596967fcc Grpc: Fix parsing null value for google.protobuf.Timestamp (#1293)
* Add another example for Grpc client + mapping

* <PackageReference Include="ProtoBufJsonConverter" Version="0.9.0" />
2025-05-10 12:53:18 +02:00
Stef Heyenrath
56c058fe24 Cleanup old WireMock.Net.OpenApiParser 2025-05-10 08:10:20 +02:00
Stef
b43be28b5f 1.8.4 2025-05-08 20:15:23 +02:00
Stef Heyenrath
5ed09d84a3 Use ILRepack to include Microsoft.OpenApi as internal (#1290)
* .

* Use ILRepack to include Microsoft.OpenApi as internal

* ...

* OpenApiSpecificationVersion

* .

* 080

* 4
2025-05-08 20:11:41 +02:00
Stef
cfcc55d2dd 1.8.3 2025-05-06 21:47:29 +02:00
Stef Heyenrath
249b3562ab Update AzureADAuthenticationMatcher to support V2 Azure AAD tokens (#1288)
* Update AzureADAuthenticationMatcher to support V2 Azure AAD tokens

* fix ;-)

* add tests

* Update test/WireMock.Net.Tests/Authentication/MockJwtSecurityTokenHandler.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .

* WIREMOCK_AAD_TENANT

* update logging

* throw new SecurityTokenInvalidIssuerException($"tenant {extractedTenant} does not match {_tenant}.");

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-06 18:49:21 +02:00
Stef
cfc13b2449 1.8.2 2025-05-05 10:23:14 +02:00
Stef Heyenrath
4303706530 Update ProtoBufJsonConverter to fix conflict for 'MessageOptions' (#1291)
* Update ProtoBufJsonConverter to fix conflict for 'MessageOptions'

* <PackageReference Include="ProtoBufJsonConverter" Version="0.8.0" />
2025-05-05 10:21:15 +02:00
Stef Heyenrath
5c8105b50d move 'WireMock.Net.OpenApiParser.Preview' to ''src-preview' folder 2025-05-02 09:15:59 +02:00
Stef Heyenrath
630ffab56e Do not generate NuGet for WireMock.Net.OpenApiParser.Preview 2025-05-02 08:50:32 +02:00
Stef Heyenrath
c181d0286a Do not generate NuGet for WireMock.Net.OpenApiParser.Preview 2025-05-02 08:41:51 +02:00
Stef Heyenrath
16dab99175 1.8.1 2025-05-02 07:29:11 +02:00
Stef Heyenrath
cf0dcf5855 Revert changes to WireMock.Net.OpenApiParser (#1289)
* Revert changes to WireMock.Net.OpenApiParser

* revert
2025-05-02 07:18:35 +02:00
Stef
e7310fbc7b 1.8.0 2025-04-28 19:35:48 +02:00
Jonathan Mezach
8a07286b89 Add an launch inspector command to Aspire Dashboard (#1283)
* Upgrade to Aspire 9.2.0

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Remove workload installs from CI pipeline

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Missed package upgrade

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Fix usings

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Add Open Inspector command

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Fix broken test

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* PR comments

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* More PR comments

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

---------

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>
2025-04-25 20:23:19 +02:00
Emil Tang Kristensen
9392069f8a Enable support for WireMock Middleware in Hosted Services (#1285) 2025-04-25 16:35:55 +02:00
Stef Heyenrath
0fd190b5a3 Fix Changelog 2025-04-24 20:17:15 +02:00
Stef Heyenrath
4368e3cde6 1.7.x (#1268)
* Fix construction of path in OpenApiParser (#1265)

* Server-Sent Events (#1269)

* Server Side Events

* fixes

* await HandleSseStringAsync(responseMessage, response, bodyData);

* 1.7.5-preview-01

* IBlockingQueue

* 1.7.5-preview-02 (03 April 2025)

* IBlockingQueue

* ...

* Support OpenApi V31 (#1279)

* Support OpenApi V31

* Update src/WireMock.Net.OpenApiParser/Extensions/OpenApiSchemaExtensions.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fx

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add ProtoDefinitionHelper.FromDirectory (#1263)

* Add ProtoDefinitionHelper.FromDirectory

* .

* unix-windows

* move test

* imports in the proto files indeed should use a forward slash

* updates

* .

* private Func<IdOrTexts> ProtoDefinitionFunc()

* OpenTelemetry

* .

* fix path utils

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-23 11:51:44 +02:00
Stef Heyenrath
fc0f82db33 Add HandlebarsSettings (#1271)
* Add HandlebarsSettings class

* DefaultAllowedHandlebarsHelpers

* HB - 2.5.0-preview-01

* readme

* fix

* readme

* Handlebars.Net.Helpers Version="2.5.0"
2025-04-23 07:47:37 +02:00
Stef Heyenrath
beabba4064 Add extra unit tests for PR #1278 2025-04-23 07:47:07 +02:00
jollyjoyce1995
d9a7e80360 changed null check in JSONPathMatcher and JmesPathMatcher to ensure that the body is not an empty string (the json parse would throw an exception at this point) (#1278) 2025-04-23 07:30:52 +02:00
Stef Heyenrath
66a048a487 update readme for WireMock.Net.AwesomeAssertions 2025-04-23 07:30:21 +02:00
Francesco Venturoli
04d53f3a9e feat(awesome-assertions): Added new project WireMock.Net.AwesomeAssertions (#1273)
* feat(awesome-assertions): Added new project WireMock.Net.AwesomeAssertions

* feat(awesome-assertions): Applied dotnet naming convention for private readonly fields

---------

Co-authored-by: Francesco Venturoli <f.venturoli@crif.com>
2025-04-22 22:51:40 +02:00
Stef Heyenrath
a8562fda32 Use vmImage ubuntu-22.04 and install aspire workload in Azure DevOps CI-CD pipeline (#1282)
* Install aspire workload in Azure DevOps CI-CD pipeline

* vmImage: 'ubuntu-22.04'
2025-04-22 22:47:43 +02:00
Stef Heyenrath
5abb424d3c Add extra JsonPartialWildcardMatcher Tests (#1267)
* Add extra JsonPartialWildcardMatcher Tests

* responseText.Should().Contain
2025-04-02 08:57:44 +02:00
Stef Heyenrath
db158bcc7e Update readme 2025-03-04 18:17:09 +01:00
Stef Heyenrath
0effda3cfa 1.8.0-prview-01 (Update the usage from the custom Handlebars.Net File helper) 2025-03-04 18:11:13 +01:00
Stef Heyenrath
ff36c1ee6f Merge commit from fork 2025-03-04 17:58:38 +01:00
Stef Heyenrath
be55022a2a 1.7.4 2025-02-27 07:15:02 +01:00
Stef Heyenrath
7c68fc1d94 Add ToArray() to ConcurrentObservableCollection (#1256)
* Add ToArray() to ConcurrentObservableCollection

* ---
2025-02-27 07:12:05 +01:00
Stef Heyenrath
e7d442e5ac 1.7.3 2025-02-24 23:14:50 +01:00
Stef Heyenrath
f977b3eb86 Update QueryStringParser to support param with equal but no value (#1253)
* RequestMessageParamMatcher

* ?

* .

* .
2025-02-13 18:09:18 +01:00
Stef
84e5ba6dce 1.7.2 2025-02-12 06:15:52 +01:00
Stef
e0d693c515 1.7.2 2025-02-12 06:14:39 +01:00
Stef Heyenrath
e8de5aa73c Add ProtoDefinition to WireMockContainer (#1250)
* AddProtoDefinitionAsync

* ...

* Body

* "

* .

* .

* .

* [Fact(Skip = "new docker is needed")]

* x
2025-02-12 06:08:55 +01:00
Stef Heyenrath
a02ff47db6 Update WireMockProtoFileResolver and add tests for ProtoBufUtils (#1252)
* Update WireMockProtoFileResolver and add tests for ProtoBufUtils

* .
2025-02-01 22:27:32 +01:00
JvE-iO
29bf9b42f8 Add exception message to logging when mapping fails due to an exception. (#1248)
* Add exception message to logging when mapping fails due to an exception.

* Revert "Add exception message to logging when mapping fails due to an exception."

This reverts commit eb7cf46c95.

* Fix loggers with improved exception logging.
2025-01-30 10:59:22 +01:00
Stef Heyenrath
52b00d74a9 Add "AddUrl" to WireMockContainerBuilder to support grpc (#1246)
* Add "AddUrl" to WireMockContainerBuilder to support grpc

* fix

* fix for windows

* wip

* fix !

* change some example code
2025-01-29 22:09:17 +01:00
Stef
f5fe51e227 1.7.1 2025-01-26 08:56:47 +01:00
Stef Heyenrath
fa8f45a7ac Use Handlebars.Net.Helpers version 2.4.10 (#1245) 2025-01-26 08:52:37 +01:00
Stef Heyenrath
ed07da7d18 Fix ProtoBuf mapping.json (#1236)
* Fix ProtoBuf Mappings

* [Fact(Skip = "#1233")]

* fix?

* PortUtils
2025-01-26 08:37:17 +01:00
Stef Heyenrath
442d8a715c Update README.md [Breaking changes] 2025-01-25 11:53:56 +01:00
Stef Heyenrath
09f4953936 1.7.0 2025-01-22 10:36:22 +01:00
Stef Heyenrath
12d2219752 Disable DynamicLinq to fix CVE (#1242)
* Disable DynamicLinq

* Disable DynamicLinq functionality
2025-01-22 10:30:52 +01:00
Stef
888d913729 1.6.12 2025-01-21 22:12:35 +01:00
Stef Heyenrath
6688a64d49 Upgrade to Handlebars.Net.Helpers 2.4.9 (#1241)
* Upgrade to Handlebars.Net.Helpers 2.4.9

* uses: actions/setup-dotnet@v4
2025-01-21 22:10:19 +01:00
Stef Heyenrath
6aa1594d31 Fix ArgumentException in FindLogEntries (#1235) 2025-01-09 21:14:11 +01:00
Stef Heyenrath
44c1c7aaa8 Fix google protobuf WellKnownTypes: Empty, Duration and Timestamp (#1231)
* Fix google protobuf WellKnownTypes: Timestamp and Duration

* Fix protobuf Empty

* .

* small refactor

* 006

* fix

* policy

* ---

* <PackageReference Include="ProtoBufJsonConverter" Version="0.7.0" />
2025-01-09 18:50:16 +01:00
Stef
9c94324cff 1.6.11 2025-01-02 10:42:39 +01:00
Stef
1198feae5e Add unit test "WireMockServer_WithBodyAsProtoBuf_JsonPartialWildcardMatcher" 2025-01-02 09:00:39 +01:00
Stef Heyenrath
ab7ce37e7e Fix WireMockContainerBuilder (duplicate entries) (#1222) 2024-12-31 18:24:29 +01:00
Stef Heyenrath
485f7ad952 FindLogEntries (#1224) 2024-12-23 20:00:03 +01:00
Stef Heyenrath
c4ae4eaf8e Add functionality to call a PostTransform method after the Webhook request has been transformed (#1223)
* Add functionality to call a PostTransform method after the Webhook request has been transformed

* UseTransformer == true
2024-12-22 18:38:12 +01:00
Stef Heyenrath
6db5427e6e Add overloads to AtUrl and AtAbsoluteUrl which can use a IStringMatcher (#1221) 2024-12-22 17:11:21 +01:00
Stef Heyenrath
deda7fb686 1.6.10 2024-12-15 11:39:03 +01:00
Stef Heyenrath
2a19b4491f WireMock.Net.Testcontainers: implement watching the static mapping folder for changes (#1189)
* WireMock.Net.Testcontainers: implement watching the static mapping files + folder for changes

* ReloadStaticMappings

* fix

* .

* .

* .

* .

* .

* .

* .

* CopyAsync

* <VersionPrefix>1.6.7-preview-02</VersionPrefix>

* <VersionPrefix>1.6.7-preview-03</VersionPrefix>
2024-12-15 11:31:25 +01:00
Stef Heyenrath
c548600dea 1.6.9 2024-12-06 19:22:25 +01:00
Stef Heyenrath
7f640dfa0d Fix JsonPartialMatcher when using property names with dot (#1216)
Fix JsonPartialMatcher when using property names with dot (#1216)
2024-12-06 09:23:31 +01:00
Stef Heyenrath
4b3e9feca0 1.6.8 2024-11-24 16:22:32 +01:00
Stef Heyenrath
4aaed2a6ca Fix HandlebarsContext ParseAndEvaluate method (#1213)
* Fix HandlebarsContext ParseAndEvaluate method

* test

* xxx
2024-11-22 07:58:23 +01:00
Stef Heyenrath
6f73dfe360 Use GraphQL 8.2.1 (#1211) 2024-11-18 10:23:00 +01:00
Stef Heyenrath
f4103b47aa Fix security issues (#1206)
* .

* .

* x
2024-11-17 17:25:21 +01:00
Stef Heyenrath
edab3ad7e5 WireMockLogger 2024-11-01 10:31:23 +01:00
Stef Heyenrath
38c2131472 IWireMockLogger.Error(string, Exception) 2024-10-29 19:55:02 +01:00
Stef Heyenrath
3693d6a676 Log exception when (static) mapping file cannot be read (#1202) 2024-10-29 19:52:31 +01:00
Stef Heyenrath
214fb539ec 1.6.7 2024-10-17 18:49:24 +02:00
Stef Heyenrath
a468b89788 Fix Google.Protobuf.WellKnownTypes.Value (#1198)
* <PackageReference Include="ProtoBufJsonConverter" Version="0.5.0-preview-01" />

* Fix Google.Protobuf.WellKnownTypes.Value
2024-10-17 18:47:09 +02:00
Stef Heyenrath
1682c61a0c Use latest ProtoBufJsonConverter to support WellKnownTypes (#1161)
* Use latest ProtoBufJsonConverter to support WellKnownTypes

* Fix

* 02

* WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes

* .

* extra test

* 0.4.0-preview-06

* 7

* <PackageReference Include="ProtoBufJsonConverter" Version="0.4.0-preview-08" />

* Update README.md

* <PackageReference Include="ProtoBufJsonConverter" Version="0.4.0-preview-09" />

* <PackageReference Include="ProtoBufJsonConverter" Version="0.4.0" />

* Update README.md
2024-10-16 10:57:47 +02:00
dependabot[bot]
ac693e0f96 Bump System.Text.Json from 8.0.4 to 8.0.5 in /src/dotnet-WireMock.Net (#1197)
Bumps [System.Text.Json](https://github.com/dotnet/runtime) from 8.0.4 to 8.0.5.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v8.0.4...v8.0.5)

---
updated-dependencies:
- dependency-name: System.Text.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-16 10:41:07 +02:00
dependabot[bot]
45755adae4 Bump System.Text.Json in /examples/WireMock.Net.Console.Net472.Classic (#1190)
Bumps [System.Text.Json](https://github.com/dotnet/runtime) from 8.0.4 to 8.0.5.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v8.0.4...v8.0.5)

---
updated-dependencies:
- dependency-name: System.Text.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-15 21:46:08 +02:00
Stef Heyenrath
2ffdae1863 Upgrade System.Text.RegularExpressions to 4.3.1 to solve CVE-2019-0820 (#1194)
* Upgrade Handlebars.Net.Helpers to 2.4.6 to solve CVE-2019-0820

* .
2024-10-10 08:29:57 +02:00
Stef Heyenrath
5adbff0fa3 Update README.md (add blog) 2024-10-06 18:31:46 +02:00
Stef Heyenrath
5e04ff1a42 Add an architecture diagram for Aspire project (#1174)
* Add an architecture diagram for Aspire project

* .

* "WireMock.Net : " +

* .
2024-10-04 19:30:47 +02:00
Stef Heyenrath
eb7e6c397f 1.6.6 2024-10-01 17:43:18 +02:00
Stef Heyenrath
f56ecf943d Fix StaticMappingsPath in WireMockContainerBuilder (#1187)
* Fix StaticMappingsPath in WireMockContainerBuilder

* .
2024-10-01 17:39:59 +02:00
Stef Heyenrath
76ae1466cc Throw exception in case WithTransformer is used after WithBodyFromFile (#1185)
* Fix .WithBodyFromFile + .WithTransformer combination

* Ex
2024-09-29 21:29:34 +02:00
Stef Heyenrath
edbc7aeb5c 1.6.5 2024-09-28 10:26:38 +02:00
Stef Heyenrath
42306d1864 Add WireMock.Net.AspNetCore.Middleware (#1175)
* Add WireMock.Net.AspNetCore.Middleware

* .

* WireMock.Net.Middleware.Tests

* .

* X-WireMock-Response-Delay
2024-09-27 20:39:57 +02:00
Stef Heyenrath
c57590b2ba Set <SonarQubeExclude>true</SonarQubeExclude> in test projects (#1183) 2024-09-27 20:06:22 +02:00
Stef Heyenrath
dca3fd0260 WireMock.Net.Testcontainers: Use 'sheyenrath/wiremock.net-alpine' image as default for Linux (#1181)
* WireMock.Net.Testcontainers: Use 'sheyenrath/wiremock.net-alpine' image as default for Linux

* ...

* .

* WithBindMount

* fix

* r

* .
2024-09-27 19:10:51 +02:00
Jason Landbridge
95994421ae pass in the request when no matching is found to the warn logger (#1182) 2024-09-27 19:10:39 +02:00
Stef Heyenrath
697411555a Update README.md (add WireMock.Net.TUnit) 2024-09-26 18:47:36 +02:00
Stef Heyenrath
f89f0fa466 1.6.4 2024-09-25 08:56:44 +02:00
Stef Heyenrath
b892e85d2a Add WireMock.Net.TUnit project (#1179)
* Add WireMock.Net.TUnit project

* fix

* .

* fix

* bd

* 0.1.812

* dotnet test

* 0.1.817

* cat

* type

* type2

* find

* -- --diagnostic

* --no-build

* fix?
2024-09-25 08:52:40 +02:00
Stef Heyenrath
836976ca19 Upgrade CS-Script to version 4.8.17 (#1178) 2024-09-20 15:20:38 +02:00
Stef Heyenrath
c845f73dd0 Unpin Testcontainers version and upgrade to version 3.10.0 (#1177) 2024-09-20 15:20:25 +02:00
Stef Heyenrath
2cbbef01ae Refactor some code (IBodyDataExtensions) 2024-09-20 13:52:45 +02:00
Ruxo
dd80fd7822 Attempt to fix JSON parsing of text/plain content type (#1172)
* UseContentType

* Fix unit tests

* Add a unit test and an integration test for the fix.

* Simplify body type checking with GetBodyType extension.

* Split IBodyDataExtension, and use imperative style instead of functional style

* Remove excessive null forgiving operators

* Adjust braces

---------

Co-authored-by: Ruxo Zheng <rz@just.sent.as>
2024-09-20 13:19:32 +02:00
Stef Heyenrath
527278e60c Update the .NET Aspire tests (#1170)
* Skip unit tests when Docker is not running in Linux container mode using DockerIsRunningInLinuxContainerModeFact

* IgnoreOnContinuousIntegrationFact
2024-09-10 16:14:05 +02:00
Stef Heyenrath
7d7f1f8fbb Allow mapping without Path or Url (#1169) 2024-09-09 20:48:09 +02:00
Stef Heyenrath
af124d556d 1.6.3 2024-09-07 09:36:33 +02:00
cocoon
60931f6273 Fix listen on AnyIP for url 0.0.0.0 (#1165)
* fix listen to AnyIP if url is 0.0.0.0

* Add Test for listenin on AnyIP for url 0.0.0.0

* add missing using, use var, indent, remove empty line

* remove assert for ipv4/v6 address list

* test only if NET6_0_OR_GREATER

* use same code style

* add missing +

* Asser. to Assert

* split single test into one for IPv4 and one for IPv6

* Create IgnoreOnContinuousIntegrationFact.cs

* Ignore tests if CI/CD

* change to file - scoped namespace and add GITHUB_ACTIONS

* use PortUtils.FindFreeTcpPort()

* add and use GetIPAddressesByFamily

* add using System.Net.Sockets

* use #if for both unit tests and include new helper method inside
2024-09-07 09:31:19 +02:00
Stef Heyenrath
b99a80e782 Update README.md (Fix NuGet and MyGet badges) 2024-09-06 15:11:30 +02:00
Stef Heyenrath
07c9aebf44 1.6.2 2024-09-04 18:50:59 +02:00
Stef Heyenrath
8348a7b9a3 Also update IWireMockMiddlewareOptions when settings are updated via admin interface (#1166)
* Also update IWireMockMiddlewareOptions when settings are updated via admin interface

* ci
2024-09-04 18:47:25 +02:00
Stef Heyenrath
9a49e6a1cd Upgrade Aspire to version 8.2.0 (#1163) 2024-08-31 17:06:30 +02:00
Stef Heyenrath
f82891c996 Fix version from "WireMock.Net.Aspire" 2024-08-29 18:51:25 +02:00
Stef Heyenrath
da100298c1 Update MappingConverter to correctly write the Matcher as C# code (#1152)
* Update MappingConverter to correctly write the Matcher as C# code

* .

* CSharpCodeMatcher

* tests

* .
2024-08-27 19:38:07 +02:00
Stef Heyenrath
95573eeb96 1.6.1 2024-08-22 20:33:39 +02:00
Stef Heyenrath
487d6d2db1 Use default timeout for Regex (#1160) 2024-08-22 20:31:03 +02:00
Stef Heyenrath
d2b53893db 1.6.0 2024-08-16 13:19:07 +02:00
Stef Heyenrath
088444024f Allow setting Content-Length header on the response (#1158)
* Allow setting Content-Length header on the response

* fix?
2024-08-16 12:14:42 +02:00
Stef Heyenrath
7e162a00ab Fix FormUrlEncodedMatcher (MatchOperator.And) (#1157) 2024-08-15 11:01:37 +02:00
Stef Heyenrath
8dcf35d8ac Use Guid.TryParseExact with format "D" (#1148) 2024-07-29 07:56:10 +02:00
Stef Heyenrath
4b12f3419f Add Aspire Extension (#1109)
* WireMock.Net.Aspire

* .

* xxx

* nuget

* [CodeFactor] Apply fixes

* ut

* t

* **WireMock.Net.Aspire**

* .

* t

* .

* .

* .

* TESTS

* docker utils

* Install .NET Aspire workload

* 4

* 4!

* projects: '**/test/**/*.csproj'

* script: 'dotnet workload install aspire'

* projects: '**/test/**/*.csproj'

* coverage

* WithWatchStaticMappings

* Admin

* typo

* port

* fix

* .

* x

* ...

* wait

* readme

* x

* 2

* async

* <Version>0.0.1-preview-03</Version>

* ...

* fix aspire

* admin/pwd

* Install .NET Aspire workload

* 0.0.1-preview-04

* WaitForHealthAsync

* ...

* IsHealthyAsync

* .

* add eps

* name: 'Execute Aspire Tests'

* name: Install .NET Aspire workload

* .

* dotnet test

* remove duplicate

* .

* cc

* dotnet tool install --global coverlet.console

* -*

* merge

* /d:sonar.pullrequest.provider=github

* <Version>0.0.1-preview-05</Version>

* // Copyright © WireMock.Net

* .

---------

Co-authored-by: codefactor-io <support@codefactor.io>
2024-07-27 18:53:59 +02:00
Stef Heyenrath
69c829fae0 Update + add fluent builder methods (#1042)
* Update some fluent builder methods

* fix

* sc

* ThenRespondWith

* // Copyright © WireMock.Net
2024-07-27 16:16:11 +02:00
Stef Heyenrath
275816c414 Fix Directory.Build.props 2024-07-27 14:59:10 +02:00
Stef Heyenrath
3888b9b00e 1.5.62 2024-07-27 14:46:08 +02:00
Stef Heyenrath
3353be65b5 Add FormUrlEncodedMatcher (#1147)
* FormUrlEncodedMatcher

* .

* Fix

* new

* support wildcard
2024-07-27 14:40:23 +02:00
Stef Heyenrath
926eaaece4 1.5.61 2024-07-22 21:59:47 +02:00
Stef Heyenrath
d79f6f128d Fix OpenApiPathsMapper (#1122)
* Fix OpenApiPathsMapper

* utc

* s
2024-07-22 21:36:45 +02:00
Stef Heyenrath
422e7c9b5e Make properyt FromConfiguredStub nullable (#1142) 2024-07-22 21:35:19 +02:00
Stef Heyenrath
6055b0df1a Fix some SonarCloud warnings (#1138)
* r

* hdr

* sc

* StyleCop.Analyzers
2024-07-22 21:24:30 +02:00
Stef Heyenrath
6ab1a6fd13 Update WireMockContainerBuilder.WithMappings for "includeSubDirectories" (#1141) 2024-07-22 21:24:08 +02:00
Stef Heyenrath
54edf0bebc Add link to TIOBE Index on main page + fix issues (#1137)
* Add TIOBE + include SonarAnalyzer.CSharp

* .

* cp

* Copyright © WireMock.Net

* more fixes

* fix

* xpath

* if (Matchers == null || !Matchers.Any())

* if (Matchers != null)

* ?

* .

* .
2024-07-18 18:06:04 +02:00
dependabot[bot]
baac83c9b9 Bump System.Text.Json from 8.0.0 to 8.0.4 in /src/dotnet-WireMock.Net (#1136)
Bumps System.Text.Json from 8.0.0 to 8.0.4.

---
updated-dependencies:
- dependency-name: System.Text.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-10 20:28:27 +02:00
dependabot[bot]
be2698d246 Bump System.Text.Json in /examples/WireMock.Net.Console.Net472.Classic (#1135)
Bumps System.Text.Json from 4.7.2 to 8.0.4.

---
updated-dependencies:
- dependency-name: System.Text.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-10 08:17:00 +02:00
Stef Heyenrath
b367299643 1.5.60 2024-07-09 18:45:37 +02:00
rmeshksar
2179df7b44 Multipart Matcher Fix (#1132)
* Add unit tests for AdminApiMappingBuilder (#1131)

* Multipart Matcher Fix

---------

Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
2024-07-09 18:42:07 +02:00
Stef Heyenrath
8788d9ba4a Add AdminPath to WireMockServerSettings (#1130)
* Make admin endpoint configurable

* Add AdminPath to WireMockServerSettings

* sealed

* foo

* WireMockServer_CreateClient_And_CallAdminSettingsEndpoint
2024-07-09 07:06:38 +02:00
Stef Heyenrath
d96ae9b063 Remove some files and folders (#1134) 2024-07-08 22:00:03 +02:00
Stef Heyenrath
ecb8e620ed Add unit tests for AdminApiMappingBuilder (#1133) 2024-07-08 21:41:05 +02:00
Stef Heyenrath
780c233ef3 Add Handlebars.Net.Helpers.Xslt (#1128) 2024-07-03 17:32:13 +02:00
Stef Heyenrath
4d83d82b91 1.5.59 2024-06-26 19:30:08 +02:00
Eric Dugas
bf28ba79b5 Made changes to accommodate breaking change in testcontainers-dotnet v3.9. (#1127)
Ref: https://github.com/testcontainers/testcontainers-dotnet/pull/1100
2024-06-26 19:26:27 +02:00
Stef Heyenrath
90e017b79a Use dotnet sonar scanner (part 2) (#1125)
* ddd

* ---ddd

* f

* revert

* /d:sonar.branch.name=$(Build.SourceBranchName)

* FIX

* coverlet

* coverlet - 1 line

* dotnet-coverage

* --configuration Debug --no-build --framework net8.0

* script

* /d:

* collect?

* "wiremock-coverage.xml"

* see

* tests
2024-06-20 15:35:37 +02:00
Stef Heyenrath
2602db566b Revert Sonar to old way (#1124) 2024-06-19 18:21:37 +02:00
Stef Heyenrath
61937db0b3 Fix SonarCloud integration (#1123)
* Use sonarscanner global dotnet tool

* io

* jdk?

* JAVA_HOME_17_X64

* /d:sonar.pullrequest.base=master

* /d:sonar.pullrequest.branch=$(Build.SourceBranch)

* /d:sonar.pullrequest.key=$(Build.BuildNumber)

* /d:sonar.pullrequest.branch=$(Build.SourceBranchName)

* ...

* $(System.PullRequest.PullRequestId)

* ---

* PR?

* cleanup
2024-06-19 17:07:52 +02:00
Stef Heyenrath
8a60950620 Fix link to ci.yml 2024-06-18 21:54:42 +02:00
Stef Heyenrath
90747462eb 1.5.58 2024-06-08 09:49:52 +02:00
Stef Heyenrath
8b03307a94 Add some methods to the BodyModelBuilder (#1116)
* Add some methods to the BodyModelBuilder

* .
2024-06-08 09:26:21 +02:00
Stef Heyenrath
86f8877039 Fix JsonMatcher if IgnoreCase and Regex are used 2024-06-08 09:11:22 +02:00
Stef Heyenrath
4538f6cd27 1.5.57 2024-06-04 14:31:01 +02:00
Stef Heyenrath
43746631e1 Add some Extension methods to IWireMockAdminApi (#1113) 2024-06-04 14:28:07 +02:00
Stef Heyenrath
8eda46ffc7 1.5.56 2024-06-03 11:01:46 +02:00
Stef Heyenrath
17f5ab5145 Add "/__admin/health" endpoint (#1112) 2024-06-03 10:59:44 +02:00
Stef Heyenrath
f76ea1d8ec Update Unit Test Response_ProvideResponse_Transformer_WithBodyAsFile_JsonPath 2024-05-25 09:31:39 +02:00
Stef Heyenrath
ea4ea95866 Fix Request.Create().WithBodyAsJson(...) (#1111)
* Fix Request.Create().WithBodyAsJson(...)

* [CodeFactor] Apply fixes

---------

Co-authored-by: codefactor-io <support@codefactor.io>
2024-05-25 09:09:03 +02:00
Stef Heyenrath
13f87a1364 1.5.55 2024-05-22 16:38:30 +02:00
Stef Heyenrath
dd35cea44e When only Port is provided, bind to * (Fixes #1100) (#1107)
* Fix for #1100

* tst
2024-05-22 16:33:35 +02:00
Stef Heyenrath
11b39cf57c 1.5.54 2024-05-18 09:06:24 +02:00
Stef Heyenrath
2c001f661d Add support to bind to ip-address instead of only localhost (#1100)
* Add support to bind to ip-address instead of only localhost

* .

* .
2024-05-17 21:46:07 +02:00
Aaron Sherber
0b278dbbbb Use try..catch to set encoding in WireMockConsoleLogger (#1104) 2024-05-17 20:58:08 +02:00
Stef Heyenrath
add3f662ba Fix build for .NET Framework example projects #1103 2024-05-17 10:05:54 +02:00
Stef Heyenrath
c0c07ea127 1.5.53 2024-05-08 17:43:21 +02:00
Stef Heyenrath
e20a90b615 Fix MappingConverter to support Body with JsonMatcher (#1101)
* Fix MappingBuilder for Body

* .

* .

* Fix MappingConverter
2024-05-08 17:40:53 +02:00
Stef Heyenrath
9210957e55 Update Handlebars.Net (#1093)
* Use latest version (2.4.2) from Handlebars.Net.Helpers

* .1
2024-04-25 10:40:31 +02:00
Stef Heyenrath
c135854cbe 1.5.52 2024-04-06 18:14:40 +02:00
Stef Heyenrath
54fe0823dc Add RegEx support to JsonMatcher (#1091)
* json matcher regex

* better test

* regression
2024-04-06 18:08:45 +02:00
Stef Heyenrath
ef9baf3472 Add example for IRequestMessage.BodyAsMimeMessage 2024-04-06 13:59:51 +02:00
Stef Heyenrath
22f9647e88 1.5.51 2024-03-20 08:26:38 +01:00
Stef Heyenrath
d5fa385a46 Fix FluentAssertions (actual body is not displayed in error message) (#1085)
* Fix FluentAssertions (actual body is not displayed in error message)

* .

* .

* raw
2024-03-20 08:24:43 +01:00
Stef Heyenrath
27a673953d 1.5.50 2024-03-12 20:31:50 +01:00
Stef Heyenrath
511540a7f1 Make WireMockAssertions extendable (#1082) 2024-03-12 20:27:12 +01:00
Stef Heyenrath
5b609915e1 Fix FluentAssertions on Header(s) (#1080)
* Fix FluentAssertions on Header(s)

* ...
2024-03-09 08:39:52 +01:00
Stef Heyenrath
a7b0d502c6 1.5.49 2024-03-06 13:46:51 +01:00
Stef Heyenrath
fcd67466b5 Upgrade ProtoBufJsonConverter to fix issue with dot(s) in package name (#1078) 2024-03-06 13:44:10 +01:00
Stef Heyenrath
ce833c1f29 Extend TypeLoader (#1069)
* Extend PluginLoader

* rename
2024-02-23 08:51:38 +01:00
Stef Heyenrath
2364866f97 Update README.md - blog 2024-02-22 10:07:00 +01:00
Stef Heyenrath
d4d588c593 1.5.48 2024-02-17 13:37:05 +01:00
Stef Heyenrath
97a749d54a Fix some SonarCloud issues (#1058)
* Fix some SonarCloud issues

* added some xml comments
2024-02-17 13:32:28 +01:00
Stef Heyenrath
6ac95cf57d Add Grpc ProtoBuf support (request-response) (#1047)
* ProtoBuf

* .

* x

* ---

* x

* fx

* ...

* sc

* ...

* .

* groen

* x

* fix tests

* ok!?

* fix tests

* fix tests

* !

* x

* 6

* .

* x

* ivaluematcher

* transformer

* .

* sc

* .

* mapping

* x

* tra

* com

* ...

* .

* .

* .

* AddProtoDefinition

* .

* set

* grpahj

* .

* .

* IdOrText

* ...

* async

* async2

* .

* t

* nuget

* <PackageReference Include="ProtoBufJsonConverter" Version="0.2.0-preview-04" />

* http version

* tests

* .WithHttpVersion("2")

* <PackageReference Include="ProtoBufJsonConverter" Version="0.2.0" />

* HttpVersionParser
2024-02-16 17:16:51 +01:00
Stef Heyenrath
801546fae7 1.5.47 2024-01-25 10:13:54 +01:00
Stef Heyenrath
57c42492bc Pin the version from Testcontainers to 3.7.0 in WireMock.Net.Testcontainers (#1057)
* TestcontainersTests

* WireMockContainer_Build_and_StartAsync_and_StopAsync

* --

* [3.7.0]
2024-01-25 09:54:25 +01:00
dependabot[bot]
057cd6c963 Bump System.IdentityModel.Tokens.Jwt (#1055)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.25.0 to 6.34.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.25.0...6.34.0)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-23 21:48:41 +01:00
Stef Heyenrath
b2b7172043 fix: src/WireMock.Net/WireMock.Net.csproj to reduce vulnerabilities (#1053)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DOTNET-MICROSOFTIDENTITYMODELJSONWEBTOKENS-6148656
- https://snyk.io/vuln/SNYK-DOTNET-SYSTEMIDENTITYMODELTOKENSJWT-6148655

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2024-01-23 21:32:55 +01:00
Stef Heyenrath
13a3dfa9fe Update README.md 2024-01-08 08:12:38 +01:00
Stef Heyenrath
4020a178a6 Add WithoutHeader to WireMock.FluentAssertions (#1049)
* Add WithoutHeader to WireMock.FluentAssertions

* .

* .
2023-12-27 08:18:56 +01:00
Stef Heyenrath
30ee768430 1.5.46 2023-12-23 09:44:59 +01:00
Thomas Levesque
5299ced000 Change FindRequestByMappingGuidAsync to return a collection of entries (#1046) 2023-12-23 09:40:35 +01:00
Stef Heyenrath
e4df6cc93d WireMockServerSettingsParser (#1044)
* WireMockServerSettingsParser

* nameof
2023-12-22 14:36:42 +01:00
Stef Heyenrath
abf3969bee 1.5.45 2023-12-21 22:18:26 +01:00
Stef Heyenrath
a8339a0867 FindRequestByMappingGuidAsync (#1043)
* FindRequestByMappingGuidAsync

* fix

* sc
2023-12-21 20:03:22 +01:00
Stef Heyenrath
4721b73a16 Update Handlebars Transformer logic (ReplaceNodeOptions) (#1036)
* Update Handlebars Transformer logic (ReplaceNodeOptions)

* okeee

* EvaluateAndKeep = Evaluate

* fix?

* linux

* _

* tt

* xxx

* fx

* x

* fix test
2023-12-21 20:02:58 +01:00
Stef Heyenrath
c96d7d31b1 1.5.44 2023-12-14 21:08:05 +01:00
Mindaugas Laganeckas
7b8e7bb684 Implement prefix for saved mapping file (#1040)
* Implement PrefixForSavedMappingFile

* Add missing new line

* Add missing new line

* Fix warning

* Fix typo

* Change from readonly to const

* Assign default value

* Use nameof()

* Change from readonly to const

* Update tests

* Update failing test

* Rename settingsMock to settings

* Create public const

* Use const from ProxyAndRecordSettings

---------

Co-authored-by: Mindaugas Laganeckas <mindaugas.laganeckas@nexigroup.com>
2023-12-14 08:28:21 +01:00
Stef Heyenrath
68ad015dcf 1.5.43 2023-12-11 17:34:39 +01:00
sameena syed
97d26338e6 Proxy all requests - even a repeated one (#1038)
* Adding proxyall

* Adding proxyAll flag

* Reverting extra spaces added

* Make proxyall to true

* Resolving review comments for proxyall

* Resolving codefactor spaces in the code

* Adding proxyall parser in wmserversettingsparser

---------

Co-authored-by: Sameena Syed <sameena.syed@nexigroup.com>
2023-12-11 17:16:30 +01:00
Stef Heyenrath
0c6129e86b Add ProxyUrlReplaceSettings to Response (#1026) 2023-12-10 09:57:37 +01:00
Stef Heyenrath
61c8ce76eb 1.5.42 2023-12-09 09:24:27 +01:00
Stef Heyenrath
8e1b6f87f0 Workaround for: Random.Generate Type="Long" (#1034)
* Workaround for: Random.Generate Type="Long"

* x

* 11

* .

* 2
2023-12-09 09:18:16 +01:00
Stef Heyenrath
c6c7ba13b4 Calling Reset also resets the scenarios (#1031)
* Calling Reset also resets the scenarios

* .
2023-12-07 16:03:37 +01:00
Stef Heyenrath
1174acce6e ResponseWithHandlebarsDateTimeTests (#1029) 2023-12-06 20:20:45 +01:00
Stef Heyenrath
932dde07e4 FixMapping[] for WireMock.Org REST API (#1023) 2023-12-06 09:24:58 +01:00
Stef Heyenrath
c193bcf61f 1.5.41 - releasenotes 2023-12-04 18:11:55 +01:00
Stef Heyenrath
9757245c61 1.5.41 2023-12-04 18:09:03 +01:00
Stef Heyenrath
8b91cec457 Add .NET 8 (#1018)
* Add .NET 8

* 'Use .NET 8'

* tests 8

* 8

* tc8

* ci

* CI
2023-12-04 18:05:18 +01:00
Stef Heyenrath
fb3ae53e1c GraphQL - custom scalar support (#1012)
* x

* CustomScalars

* more tests

* .

* add or set

* ...

* x
2023-12-04 18:02:03 +01:00
Stef Heyenrath
124ecc2097 Add Github Action (#1020)
* CI github action

* push
2023-11-21 08:12:54 +01:00
Stef Heyenrath
29e149a7fa 1.5.40 2023-11-07 18:03:34 +01:00
Stef Heyenrath
7160dbdd19 FluentAssertions - WithBody and WithBodyAsJson and WithBodyAsBytes (#1014)
* WithBody

* .

* fix

* .

* .
2023-11-04 16:17:23 +01:00
Stef Heyenrath
2f29d80336 Add more tests for JmesPathMatchers and StringUtils.ParseMatchOperator (#1009)
* Add more tests for JmesPathMatchers and StringUtils.ParseMatchOperator

* .

* prio
2023-10-16 17:13:52 +02:00
Stef Heyenrath
f7cd4b100e GraphQL - add support for standard scalar types in the schema (#1011)
* GraphQL: register BuiltInTypes

* GraphQLMatcher_For_ValidSchema_And_CorrectGraphQL_Mutation_IsMatch
2023-10-16 16:13:53 +02:00
Stef Heyenrath
62fa4666b5 Add unit tests for HttpClient with WebProxy (#1010)
* Add test for SSL / Https

* Add unit tests for HttpClient with WebProxy

* cert.pem

* x

* skip

* example google

* revert

* host tests

* sonar
2023-10-14 17:55:29 +02:00
Stef Heyenrath
30372a9348 1.5.39 2023-10-09 18:30:19 +02:00
Stef Heyenrath
bc75db8c8c Fix RequestMessageParamMatcher : RejectOnMatch (#1006) 2023-10-09 18:28:22 +02:00
Stef Heyenrath
6254ee3950 1.5.38 2023-10-02 19:33:32 +02:00
Carsten Alder
60bf12e2a9 Support for xml namespaces in XPathMatcher (#1005)
* Support for xml namespaces in XPathMatcher

* Review findings of Stef implemented.

* Fix of build error

* New review findings by Stef

---------

Co-authored-by: Carsten Alder <carsten.alder@schleupen.de>
2023-10-02 19:29:31 +02:00
Stef Heyenrath
a25a8cabf8 1.5.37 2023-09-27 21:43:50 +02:00
Stef Heyenrath
9f9fc85a64 JmesPathMatcherTests (#998) 2023-09-27 21:42:34 +02:00
Stef Heyenrath
b63076a9ac Fix MappingModel to map IgnoreCase and RejectOnMatch for Headers, Cookies and Parameters (#1004) 2023-09-26 21:01:33 +02:00
Stef Heyenrath
05e2aa548b 1.5.36 2023-09-21 11:40:34 +02:00
Stef Heyenrath
b9a8ee4145 Fixed logic for SaveUnmatchedRequests (#1002)
* Fixed logic for SaveUnmatchedRequests

* fix
2023-09-21 11:38:01 +02:00
Stef Heyenrath
59aab9e1c3 Remove dependency on Microsoft.AspNet.WebApi.Client (#996)
* Using an alias for System.Net.Http.Formatting

* .

* fix

* space
2023-08-29 17:53:17 +02:00
Stef Heyenrath
93c87845c2 Write logging in case a Matcher throws an exception (#986)
* ThrowException

* ...

* .

* ...

* b

* fix test

* ...

* .

* sonar

* ft

* .

* fix tst
2023-08-21 20:07:46 +02:00
Stef Heyenrath
09a302baf2 1.5.35 2023-08-19 10:00:18 +02:00
Stef Heyenrath
f0139eb837 Update comment in JsonPathMatcher.cs 2023-08-17 17:22:05 +02:00
DayLightDancer
8adf34fb56 Update JSONPathMatcher.cs to cover the string path selection to a child (#993)
* Update JSONPathMatcher.cs to cover the string path selection to a child 

The .SelectToken method accept string path selection and JSONPath queries. The current code works only for the queries because the result is JObject. When the string path is selected the result is JValue and event with a valid result the code the code doesn't return valued result.
https://www.newtonsoft.com/json/help/html/SelectToken.htm

* Added unit tests

* Addressed the comments

* Addressed the comments

* Update JSONPathMatcher.cs
2023-08-17 17:18:32 +02:00
Stef Heyenrath
fd816f0952 Add extra unit test for WithParam multiple values comma (#992) 2023-08-12 21:05:13 +02:00
Stef Heyenrath
205c1d598b 1.5.34 2023-08-04 10:51:51 +02:00
Stef Heyenrath
b986633eca Fix MimeKitLite NuGet include (#989)
* Fix 500

* MimeKiteLite : fix NuGet include

* .
2023-08-04 10:44:53 +02:00
Stef Heyenrath
6aa7aac151 1.5.33 2023-08-03 16:08:50 +02:00
Stef Heyenrath
4688f556b5 Add MultiPart/MimePart Request Matcher (#981)
* wip

* .

* mm

* x

* .

* .

* .

* tests

* .

* more tests

* trans

* x

* win

* fix

* .

* tests
2023-08-03 15:55:46 +02:00
Stef Heyenrath
a58ead7b4e Upgrade to Handlebars.Net.Helpers 2.4.0 to update XPath.SelectTokens and XPath.EvaluateToString (#976)
* Upgrade to Handlebars.Net.Helpers 2.4.0 to update XPath.SelectTokens and XPath.EvaluateToString

* fix
2023-07-20 21:49:00 +02:00
Tymur Nesterenko
58bfb3ea33 JsonPartialMatcher - match guid and string (#972)
* JsonPartialMatcher - match guid and string (#971)

* JsonPartialMatcher - match guid and string. Add Regex with Guid test (#971)
2023-07-19 22:34:02 +02:00
Stef Heyenrath
9c51548d2b 1.5.32 2023-07-15 09:41:05 +02:00
Stef Heyenrath
98b8ede826 Fixed JsonPathMatcher to match nested objects (#966)
* Fixed JsonPathMatcher to match nested objects

* fix

* .

* 100%
2023-07-15 09:29:13 +02:00
Stef Heyenrath
a6f3f976af 1.5.31 2023-07-08 14:57:12 +02:00
Stef Heyenrath
b495eb83b1 Add GraphQL Schema matching (#964)
* Add GrapQLMatcher

* tests

* x

* .

* .

* RequestMessageGraphQLMatcher

* .

* more tests

* tests

* ...

* ms

* .

* more tests

* GraphQL.NET !!!

* .

* executionResult

* nw

* sonarcloud
2023-07-07 21:43:46 +02:00
Stef Heyenrath
9443e4f071 1.5.30 2023-06-28 08:12:10 +02:00
dependabot[bot]
7a914481e5 Bump System.Linq.Dynamic.Core (#963)
Bumps [System.Linq.Dynamic.Core](https://github.com/zzzprojects/System.Linq.Dynamic.Core) from 1.2.23 to 1.3.0.
- [Release notes](https://github.com/zzzprojects/System.Linq.Dynamic.Core/releases)
- [Changelog](https://github.com/zzzprojects/System.Linq.Dynamic.Core/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zzzprojects/System.Linq.Dynamic.Core/compare/v1.2.23...v1.3.0)

---
updated-dependencies:
- dependency-name: System.Linq.Dynamic.Core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-28 07:58:03 +02:00
dependabot[bot]
1ad836659d Bump System.Linq.Dynamic.Core (#962)
Bumps [System.Linq.Dynamic.Core](https://github.com/zzzprojects/System.Linq.Dynamic.Core) from 1.2.23 to 1.3.0.
- [Release notes](https://github.com/zzzprojects/System.Linq.Dynamic.Core/releases)
- [Changelog](https://github.com/zzzprojects/System.Linq.Dynamic.Core/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zzzprojects/System.Linq.Dynamic.Core/compare/v1.2.23...v1.3.0)

---
updated-dependencies:
- dependency-name: System.Linq.Dynamic.Core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-28 07:57:50 +02:00
Stef Heyenrath
ed7f9c1143 Add unit-test for Param - MatcherModel - LinqMatcher (#961) 2023-06-27 22:31:14 +02:00
Stef Heyenrath
c92183558b Fixed logic for FluentAssertions WithHeader (#959) 2023-06-23 15:20:57 +02:00
Stef Heyenrath
8ce24249d0 1.5.29 2023-06-22 19:02:10 +02:00
Stef Heyenrath
7ca70309cb Support setting WireMockServerSettings via Environment (#954)
* Support parsing environment variables (WireMockServerSettings__)

* case ignore

* fix

* SimpleSettingsParserTests

* .

* int

* more test
2023-06-22 10:35:21 +02:00
Stef Heyenrath
5d0bf6f4e1 Fix some SonarCloud issues (#955)
* Fixed some SonarCloud issues

* if (value.Contains('\n'))
2023-06-13 19:31:04 +02:00
Stef Heyenrath
f6e35cbe2d 1.5.28 2023-06-11 14:22:54 +02:00
Stef Heyenrath
dc4c8d1dba WireMock.Net.Testcontainers (#948)
* WireMock.Net.Testcontainers

* .

* logger?

* .

* .

* WatchStaticMappings

* linux

* .

* --

* ContainerInfo

* .

* 02

* .

* fix

* .
2023-06-11 13:55:57 +02:00
Stef Heyenrath
adf1914877 Allow setting the Content-Length header for a HTTP method HEAD (#951)
* Allow setting the Content-Length header for a HTTP method HEAD

* .
2023-06-06 22:44:14 +02:00
Stef Heyenrath
1f1bc05f00 1.5.27 2023-06-03 17:00:18 +02:00
Stef Heyenrath
c107e38e3b Fix WireMock.Net.FluentAssertions for net47 2023-06-02 12:03:12 +02:00
Stef Heyenrath
a77c4fe1ac Add ".NET Framework 4.7" to WireMock.Net.FluentAssertions (#949) 2023-06-01 18:06:28 +02:00
Stef Heyenrath
c1e71707c5 Add warning logging when sending a request to a Webhook does not return status 200 (#946) 2023-05-28 10:53:54 +02:00
Oleg Nenashev
69499afe43 Update Slack link (#944)
Better to use https://slack.wiremock.org/ to have proper Slack signup
2023-05-26 10:01:16 +02:00
Stef Heyenrath
aadac78577 add Slack 2023-05-25 23:49:12 +02:00
Stef Heyenrath
71393204cc 1.5.26 2023-05-25 21:36:01 +02:00
Cezary Piątek
e5cc6f570c Code generator improvements (#940)
* Fix quotation marks escaping in multiline string

* Add support for JsonPartialMatcher and JsonPartialWildcardMatcher in mapping code generator
2023-05-25 20:59:13 +02:00
Stef Heyenrath
7c3a0c815d Add GetParameter method to IRequestMessage (#942) 2023-05-25 15:14:02 +02:00
Stef Heyenrath
e61f08fe48 WireMockMiddleware should use HandleRequestsSynchronously correctly (#939) 2023-05-19 21:43:14 +02:00
Stef Heyenrath
11f4c47851 Add more unitests for CSharpFormatter utils (#938)
* Add unit-tests for CSharpFormatter

* .

* t
2023-05-19 16:14:26 +02:00
Stef Heyenrath
3956cd703b 1.5.25 (ReleaseNotes) 2023-05-13 11:17:03 +02:00
Stef Heyenrath
27682d0ce4 1.5.25 + Fixes in CSharpFormatter 2023-05-13 11:15:40 +02:00
Cezary Piątek
8444c8c506 Code generator improvements (#934)
* Handle new line escaping in C# mapping code generator

* Prevent date conversion when value persisted as string

* Handle object properties named as csharp keywords

* Refactor: Extract logic responsible for generating anonymous object definition to a separate class
2023-05-13 09:33:25 +02:00
Stef Heyenrath
6ef116a295 1.5.24 2023-05-07 14:48:20 +02:00
Stef Heyenrath
59195eaed8 Refactor some code (MappingConverter) 2023-05-07 14:42:00 +02:00
Cezary Piątek
7d9e450814 C# code generator improvements (#933)
* Escape quotes in generated C# strings

* Handle response with JSON body in C# code generator
2023-05-07 14:37:48 +02:00
Stef Heyenrath
7019a5a78c Add Package Readme (#932)
* Add Package Readme

* C# .NET API

* <PackageReadmeFile>PackageReadme.md</PackageReadmeFile>
2023-05-07 09:24:48 +02:00
Stef Heyenrath
d29f3e81f3 Add property 'IsStartedWithAdminInterface' to 'IWireMockServer' (#931)
* Add property 'IsStartedWithAdminInterface' to 'IWireMockServer'

* update tests

* .
2023-05-06 13:12:00 +02:00
Stef Heyenrath
ccd8026884 Update C# mapping code generator for WithStatusCode (#930) 2023-05-06 10:24:53 +02:00
Cezary Piątek
1214ba5108 Enrich generated code with status code (#927) 2023-05-06 09:40:41 +02:00
Cezary Piątek
427715a38a Fix csharp mapping code generator (#926) 2023-05-02 16:48:32 +02:00
Stef Heyenrath
d949dfb64c 1.5.23 2023-04-23 12:06:38 +02:00
Stef Heyenrath
0a2763c06e Add IgnoreCase option to ProxyUrlReplaceSettings (#925)
* Add IgnoreCase option to ProxyUrlReplaceSettings

* fix
2023-04-23 11:56:08 +02:00
nudejustin
9ef8bd0b7b Allow removal of prefix when proxying to another server (#630) (#924)
* #630 Allow removal of prefix when proxying to another server

* #630 Rename replace to replace settings and ensure properties used in place of fields

* #630 Update replace settings type name to ProxyUrlReplaceSettings

* #630 Add admin model and update settings parser to parse new values

* Fix formatting issues

* #630 Ensure json mapping between admin model and internal model takes place

* #630 Refactor parsing and structure of extracting new proxy url

* Reduce function complexity

* #630 Fix line length issues and remove try prefix from parser methods
2023-04-23 09:31:38 +02:00
Stef Heyenrath
090e0eb437 Add WithProbability (#922)
* WithProbability

* fix

* x

* ,

* .

* .

* .
2023-04-12 20:48:53 +02:00
Stef Heyenrath
f3d52adbb2 1.5.22 2023-04-08 21:37:18 +02:00
Stef Heyenrath
a8775c3b77 Include WireMockOpenApiParser project (#916)
* Fix some nullability warnings for WireMockOpenApiParser

* .

* .

* .

* opt

* FromText

* ab

* .

* private const string AdminOpenApi = "/__admin/openapi";

* fix test

* rnd

* .

* urldetails

* 0

* ,

* .

* tests

* .

* CompressionUtilsTests

* ut

* .
2023-04-08 21:25:17 +02:00
Stef Heyenrath
3e24e3452b Add Blogs 2023-04-01 11:21:16 +02:00
Walid Haidari
95bf8e31aa #912 add excluded params to proxy mapping (#914) 2023-03-24 16:35:09 +01:00
Stef Heyenrath
090989ea7f Update comments for models (#913) 2023-03-24 09:20:23 +01:00
Stef Heyenrath
651486f718 Make some classes internal + chnage some files to file-scoped namespaces 2023-03-22 21:57:23 +01:00
Stef Heyenrath
9dea577da1 1.5.21 2023-03-22 16:18:19 +01:00
Stef Heyenrath
7ca4294de6 Fixed QueryStringParser for UrlEncoded values (#911) 2023-03-22 16:15:50 +01:00
Stef Heyenrath
66245409f9 RequestBuilder : add WithBodyAsJson and WithBody (with IJsonConverter) (#908)
* RequestBuilder : add WithBodyAsJson and WithBody (with IJsonConverter)

* tests
2023-03-22 15:47:58 +01:00
Stef Heyenrath
da6cb9fe0a 1.5.20 2023-03-19 10:23:24 +01:00
Stef Heyenrath
b30e4faab6 Fix issue with application/x-www-form-urlencoded and ExactMatcher (#907) 2023-03-19 10:21:47 +01:00
Stef Heyenrath
1221d52c69 Add DeserializeFormUrl Encoded to the settings (#905)
* Add DeserializeFormUrlEncoded to Settings

* EmptyArray<>.Value

* .
2023-03-19 09:19:53 +01:00
Stef Heyenrath
52d2109c7e packagereleasenotes 2023-03-17 17:16:35 +01:00
Stef Heyenrath
30064b922b 1.5.19 2023-03-17 17:15:19 +01:00
Stef Heyenrath
78b94d2ebc Add WithBody with IDictionary (form-urlencoded values) (#903)
* .

* x

* fx

* fix

* f

* tests

* fix tests

* add tst
2023-03-17 17:08:45 +01:00
Stef Heyenrath
19701f5260 Update Handlebars.Net.Helpers to 2.3.15 (#904) 2023-03-15 18:30:34 +01:00
Stef Heyenrath
1269fb178f 1.5.18 2023-03-09 19:49:29 +01:00
Stef Heyenrath
7426bf76ee Add 'Data' to response which can be used during transforming the response (#893)
* Add 'Data' to response which can be used during transforming the response

* md

* hb

* fix

* Linq

* fix test

* v4

* 14

* .

* x

* remove

* s
2023-03-09 17:20:34 +01:00
dependabot[bot]
36c9d95abb Bump Microsoft.Owin in /examples/WireMock.Net.Service (#896)
Bumps [Microsoft.Owin](https://github.com/aspnet/AspNetKatana) from 2.0.2 to 4.2.2.
- [Release notes](https://github.com/aspnet/AspNetKatana/releases)
- [Commits](https://github.com/aspnet/AspNetKatana/compare/v2.0.2...v4.2.2)

---
updated-dependencies:
- dependency-name: Microsoft.Owin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 17:20:15 +01:00
Stef Heyenrath
674fa89c3e ProxySettings : Add logic to not save some requests depending on HttpMethods (#900)
* Add ExcludedHttpMethods to ProxySettings

* tst

* fix

* SaveMappingSettings

* .
2023-03-09 15:28:52 +01:00
Stef Heyenrath
61cdc13fae Add extra test for WithCallback 2023-03-01 12:10:03 +01:00
Stef Heyenrath
c344b73f45 Cleanup some code from JsonUtils.cs 2023-02-27 21:23:43 +01:00
Stef Heyenrath
2ac9ca207a 1.5.17 2023-02-25 12:53:41 +01:00
Stef Heyenrath
f099f3a288 AdminApiMappingBuilder (#890)
* AdminApiMappingBuilder

* .

* IWireMockAdminApi

* add methods

* .
2023-02-25 12:47:06 +01:00
Stef Heyenrath
02b607cc95 Slow test (#882) 2023-02-18 18:09:21 +01:00
Stef Heyenrath
7ac89e85b7 Add WithBodyAsJson builder method with accepts a Func (#881)
* Add WithBodyAsJson builder method with accepts a Func

* ut
2023-02-06 20:50:11 +01:00
Stef Heyenrath
cc4cf27101 1.5.16 2023-02-01 20:47:32 +01:00
Stef Heyenrath
6839b11d35 Add WithProxy(string proxyUrl, X509Certificate2 certificate) (#880) 2023-02-01 10:42:35 +01:00
Stef Heyenrath
1000f4409f 1.5.15 2023-01-29 10:27:53 +01:00
Stef Heyenrath
7fe2c8af78 Update REST Admin interface to support "Get Mapping(s) as C# Code" (#878)
* Add /__admin/mappings/code endpoint

* api

* fix

* .

* fix

* .

* .

* .
2023-01-29 10:24:58 +01:00
Stef Heyenrath
0fc664b404 1.5.14 2023-01-24 16:50:58 +01:00
Stef Heyenrath
770a670e53 Generate C# code from Mapping (#842)
* 1

* .

* v

* .

* .

* -

* b

* res b

* Fix UT

* .

* Verify

* v

* ...

* .

* .

* dir

* m
2023-01-24 16:45:47 +01:00
eseneckiy
b4c8779d68 Fix Self referencing loop detected for property 'Parent' with type 'System.Globalization.CultureInfo' (#875)
Co-authored-by: evgeniy.s <evgeniy.s@uklon.com.ua>
2023-01-23 20:07:06 +01:00
Stef Heyenrath
c85eaf1072 Add unit test example for Transformer Handlebars String.Append String.Join (#877)
* Response_ProvideResponse_Transformer_WithBodyAsJson_Handlebars_StringAppend

* fix
2023-01-20 08:21:59 +01:00
Stef Heyenrath
b2a8178161 Fix unsubscribe from LogEntriesChanged event handler (#872)
* Fix unsubscribe from LogEntriesChanged event handler

* .

* f
2023-01-19 14:23:38 +01:00
Stef Heyenrath
20eb37b0c8 Add MappingBuilder to build mappings in code and export to Models or JSON (#869)
* MappingBuilder

* .

* ...

* sc

* t

* .
2023-01-06 19:11:56 +01:00
Gerhard Gradnig
742f1d1f0a Add UseWebhooksFireAndForget to Server ConvertMapping (#871)
Co-authored-by: Gerhard.Gradnig <gerhard.gradnig@admiral.at>
2023-01-05 18:33:08 +01:00
Stef Heyenrath
d8927b88c8 Fix example projects 2022-12-24 17:00:41 +01:00
Stef Heyenrath
7ab136557a Include="Nullable" Version="1.3.1" 2022-12-14 17:11:45 +01:00
Stef Heyenrath
3d17913f35 1.5.13 2022-12-11 21:13:36 +01:00
billybraga
9ed6a75384 Add client certificate support (#862)
* Add client certificate support

* Add missing test certificate file

* Review fixes

* Review fixes

* Review fixes

* Review fixes
2022-12-11 20:30:47 +01:00
Stef Heyenrath
9606fee8cb Update Transformer functionality to return value instead of string (#858) 2022-12-11 11:07:56 +01:00
Stef Heyenrath
6b03dfaa8c Update WireMockServer.CreateClient/CreateClients to include handlers (#863) 2022-12-10 12:25:49 +01:00
Stef Heyenrath
e2f3ffd33a Add UpdatedAt property to Mapping (#859)
* Add UpdatedAt property to Mapping

* .
2022-12-09 14:18:50 +01:00
Stef Heyenrath
080efe4fcb 1.5.12 2022-12-03 14:12:15 +01:00
Stef Heyenrath
53adb17e07 Update some dependencies (#857)
* fb

* .

* linq

* ,,,

* anno

* log

* rd
2022-12-03 11:45:44 +01:00
Stef Heyenrath
795dcf42e2 Add .Net 7 (#853)
* Add .NET 7

* UseDotNet

* 7

* Ubuntu 6

* 'Use .NET 7'

* 67

* .

* use 7

* use 7

* test = 7

* coverage.7.0.opencover.xml
2022-12-03 11:03:57 +01:00
Stef Heyenrath
be1cbc5a12 Fix logic for QueryParameterMultipleValueSupport (#854)
* Add more QueryParameterMultipleValueSupport NoComma tests

* fix tests

* fx

* cf

* Fix

* cf

* select id, name, value from table where id in (1, 2, 3, 4, 5)
2022-12-03 11:03:45 +01:00
Stef Heyenrath
35d42a5c0d Fix Linux CI build + Fix opencover (#851)
* Fix Linux CI Test (opencover.xml)

* 2

* Build & Execute Unit tests

* ,cmd

* cout

* 12

* b

* server

* b =b

* /p:CoverletOutput=./test/WireMock.Net.Tests/WireMock.Net.Tests

* co?

* 2p

* 2?

* failOnStderr: false

* e0

* cc

* pub

* sc

* coverlet

* props

* pt

* coverage.net6.0.opencover.xml
2022-11-25 18:13:02 +01:00
Stef Heyenrath
429d6830ae 1.5.11 2022-11-24 21:47:56 +01:00
Stef Heyenrath
38634ac65a Use try-catch when adding or removing logEntry (#848)
* Use try-catch when removing logEntry

* .

* try catch add

* Add extra check

* ...
2022-11-21 07:30:27 +01:00
Stef Heyenrath
ef5f988786 Add Settings.QueryParameterMultipleValueSupport (#836)
* QueryParameterMultipleValueSupport

* .

* ,

* ,
2022-11-08 19:27:44 +01:00
Stef Heyenrath
1e44f52ad6 1.5.10 2022-11-06 13:29:12 +01:00
Stef Heyenrath
7fd1d30d0e Add WireMockNullLogger as valid commandline logger option (#845)
* Add WireMockNullLogger as valid commandline logger option

* .
2022-11-06 13:25:26 +01:00
Gerhard Gradnig
49b29d74dc Webhook: Use the transformed URL to create the HttpRequestMessage (#843)
Co-authored-by: Gerhard Gradnig <gerhard.gradnig@admiral.at>
2022-11-05 10:54:39 +01:00
Stef Heyenrath
80931e9fb5 Update Azure.Storage NuGets 2022-10-29 14:06:43 +02:00
Stef Heyenrath
cce344ff83 1.5.9 2022-10-29 14:02:30 +02:00
Stef Heyenrath
0972d2cb8f Add option to ProxySettings to append guid to mapping file (#838)
* Add option to ProxySettings to append guid to mapping file

* .

* .

* .
2022-10-29 13:58:29 +02:00
dependabot[bot]
a39b7fc633 Bump Microsoft.AspNetCore.Server.Kestrel.Core (#837)
Bumps [Microsoft.AspNetCore.Server.Kestrel.Core](https://github.com/aspnet/AspNetCore) from 2.1.7 to 2.1.25.
- [Release notes](https://github.com/aspnet/AspNetCore/releases)
- [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md)
- [Commits](https://github.com/aspnet/AspNetCore/compare/v2.1.7...v2.1.25)

---
updated-dependencies:
- dependency-name: Microsoft.AspNetCore.Server.Kestrel.Core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-27 07:46:01 +02:00
Stef Heyenrath
31298d281d Support deleting / resetting a single scenario (#834)
* Support deleting / resetting a single scenario

* move files
2022-10-26 08:43:51 +02:00
Stef Heyenrath
b4c32dd66b WireMock.Net.WebApplication.NET6 2022-10-25 17:19:13 +02:00
Stef Heyenrath
57115f1a3d Add setting to skip saving the string-response in the logging when using WithBody(Func...) (#828)
* Add extra unit-test for Response WithBody (dynamic code)

* DoNotSaveDynamicResponseInLogEntry

* update SettingsModel

* fix

* .
2022-10-21 14:47:26 +02:00
Stef Heyenrath
306c69f478 Fixes for WireMock.Net.FluentAssertions (callcount behaviour) (#832)
* UsingAnyMethod

* fix

* .
2022-10-17 21:50:24 +02:00
Stef Heyenrath
fb8fec0376 Update funding.yml 2022-10-16 09:17:41 +02:00
Stef Heyenrath
dd1a6fa508 1.5.8 2022-10-16 09:13:18 +02:00
Stef Heyenrath
36037627bc WebHook - Transform Url (#824)
* WebHook - Url

* .
2022-10-15 08:55:05 +02:00
Stef Heyenrath
55afc8041f ExactMatcher : IgnoreCase (#817)
* ...

* mm

* fix some null warnings

* fx
2022-10-15 08:23:58 +02:00
Stef Heyenrath
b523ab9125 Some fixes to WireMock.Net.Assertions (#816)
* Add extra unit test for UsingPost

* .X

* ok

* ok2

* header
2022-10-15 08:21:48 +02:00
Stef Heyenrath
14dd619763 1.5.7 2022-10-11 09:57:11 +02:00
Stef Heyenrath
430c01a461 Add implicit operators to WireMockList (#823)
* Add implicit operators to WireMockList

* .
2022-10-01 10:50:18 +02:00
Stef Heyenrath
f7b04f3234 Add UseDefinedRequestMatchers to ProxyAndRecordSettings (#821)
* .

* UseDefinedRequestMatchers

* ok

* .

* ClientIP

* t

* fix ut

* .

* cf

* cf2
2022-09-30 11:25:11 +02:00
Stef Heyenrath
c0b18631a3 Add option to run the server on http & https (#818)
* HostingProtocol.HttpAndHttps

* .

* .

* .

* HostingScheme
2022-09-26 14:24:45 +02:00
Stef Heyenrath
fd996ab4ed 1.5.6 2022-09-12 20:45:26 +02:00
Stef Heyenrath
a57626c63a 1.5.6 2022-09-12 20:38:45 +02:00
Stef Heyenrath
98a0f2fa28 WebHook : UseFireAndForget + Delay (#803)
* UseFireAndForget

* ...

* delay

* async

* updated code accorsing to proposal

* Change nuget to package reference for WireMock.Net.Console.Net472.Classic, move the new FireAndForget into the main mapping, out of individual webhook mappings making it all or nothing, update tests, change Middleware to await or not the firing of all webhooks. Update models as needed. (#804)

Co-authored-by: Matt Philmon <Matt_Philmon@carmax.com>

* small update

* Tweak middleware and fix bug in example (#806)

Co-authored-by: Matt Philmon <Matt_Philmon@carmax.com>

* .ConfigureAwait(false)

Co-authored-by: mattisking <mattisking@gmail.com>
Co-authored-by: Matt Philmon <Matt_Philmon@carmax.com>
2022-09-12 20:30:40 +02:00
Stef Heyenrath
13a06b9b38 1.5.5 2022-09-03 08:57:30 +02:00
Stef Heyenrath
74480c8ba9 Add support to use 'mapping' object in in reponse templating (#798)
* mapping

* .

* .
2022-09-03 08:52:05 +02:00
Rafael Moreira Fonseca
862c04e722 Add assertions for request methods (#802)
* Add assertions for request methods

* Fix assertions tests when using connect method

* Remove unnecessary clear and fix assert condition
2022-09-03 08:49:39 +02:00
dependabot[bot]
cd93422554 Bump Microsoft.Owin from 4.1.1 to 4.2.2 in /src/WireMock.Net (#800)
Bumps [Microsoft.Owin](https://github.com/aspnet/AspNetKatana) from 4.1.1 to 4.2.2.
- [Release notes](https://github.com/aspnet/AspNetKatana/releases)
- [Commits](https://github.com/aspnet/AspNetKatana/compare/v4.1.1...v4.2.2)

---
updated-dependencies:
- dependency-name: Microsoft.Owin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 21:19:37 +02:00
Stef Heyenrath
775c4fb2e3 1.5.4 2022-08-24 08:34:20 +02:00
Stef Heyenrath
2d4f513753 Update some NuGet packages (#781) 2022-08-24 08:26:35 +02:00
Stef Heyenrath
3d29d7fb2f Add Response.WithBody with IJsonConverter (#790)
* Response_ProvideResponse_WithBody_IJsonConverter_SystemTextJson

* Guard.NotNull(converter);

* .

* 0.1.0

* j
2022-08-23 15:49:54 +02:00
Florian
f704de65d8 Fixes header match handling using RejectOnMatch behavior (#797)
Co-authored-by: flts <>
2022-08-23 07:52:45 +02:00
Stef Heyenrath
f0d6ed26bc Add check for duplicate Guids when posting multiple mappings in one request (#795)
* Add check for DuplicateGuids

* Add check for duplicate Guids when posting mapping(s)

* mappingModels

* fix ut
2022-08-22 20:05:52 +02:00
Stef Heyenrath
330559b9fd Add support for PEM certificates (#787)
* Support PEM

* net5

* 31

* txt

* FILE

* new

* Fixed

* .

* .

* RSA
2022-08-16 13:26:00 +02:00
Stef Heyenrath
e2bd56531d Add support for Matcher.Pattern in Pact Body mapping (#789)
* Add support for Matcher.Pattern in Pact Body mapping

* SavePact_Get_Request_And_Response_WithNullBody
2022-08-15 09:28:16 +02:00
Stef Heyenrath
d2a1d0f069 Fix WithBody when using Pact and added more nullable annotations (#783)
* More nullable annotations

* .

* .

* FIX

* pact

* .

* p

* xxx

* ...

* auth

* array

* ...
2022-08-11 10:57:33 +02:00
Stef Heyenrath
b1af37f044 Fix Proxying when StartAdminInterface=true (#778)
* ProxyHelper fixes

* .

* more reformat

* .
2022-08-09 19:41:45 +02:00
Stef Heyenrath
be4b0addca 1.5.3 2022-07-29 13:21:22 +02:00
Stef Heyenrath
ae91ed2a79 Update Scriban.Signed to version 5.5.0 (#777) 2022-07-29 13:18:23 +02:00
Stef Heyenrath
968aa598e2 1.5.2 2022-07-24 16:03:30 +02:00
Stef Heyenrath
bdd421e128 JsonPartialMatcher - support Regex (#771)
* JsonPartialMatcher - support Regex

* .

* .

* more tests

* .

* .
2022-07-24 15:54:53 +02:00
Stef Heyenrath
150b448d07 Added some more tests for JsonMatcher + refactored some code to use nullable (#770) 2022-07-09 15:34:17 +02:00
dependabot[bot]
717179fd35 Bump Microsoft.AspNetCore.Server.Kestrel.Core (#769)
Bumps [Microsoft.AspNetCore.Server.Kestrel.Core](https://github.com/aspnet/AspNetCore) from 2.1.3 to 2.1.7.
- [Release notes](https://github.com/aspnet/AspNetCore/releases)
- [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md)
- [Commits](https://github.com/aspnet/AspNetCore/compare/2.1.3...v2.1.7)

---
updated-dependencies:
- dependency-name: Microsoft.AspNetCore.Server.Kestrel.Core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-08 21:52:07 +02:00
Stef Heyenrath
9b7e5908cb 1.5.1 2022-07-08 17:41:43 +02:00
Stef Heyenrath
b1032c9dcd Update WireMock.Org.Abstractions and WireMock.Org.RestClient (#765)
* Update WireMock.Org.Abstractions and WireMock.Org.RestClient

* .

* rename
2022-07-08 11:02:18 +02:00
Stef Heyenrath
4d0f96eabe Rename (WireMock.Pact.Models.V2)-Request to PactRequest and -Response to PactResponse (#767) 2022-07-08 10:32:12 +02:00
dependabot[bot]
ef12cb70cc Bump Microsoft.AspNetCore.Http (#766)
Bumps [Microsoft.AspNetCore.Http](https://github.com/aspnet/AspNetCore) from 2.1.1 to 2.1.22.
- [Release notes](https://github.com/aspnet/AspNetCore/releases)
- [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md)
- [Commits](https://github.com/aspnet/AspNetCore/compare/2.1.1...v2.1.22)

---
updated-dependencies:
- dependency-name: Microsoft.AspNetCore.Http
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-08 08:34:19 +02:00
dependabot[bot]
c212d07c53 Bump Newtonsoft.Json in /examples/WireMock.Net.Client.Net472 (#763)
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 6.0.1 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/6.0.1...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-23 11:58:49 +02:00
dependabot[bot]
b9afb126cf Bump Newtonsoft.Json in /examples/WireMock.Net.WebApplication.NETCore2 (#762)
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 11.0.2 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/11.0.2...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-23 10:53:43 +02:00
Stef Heyenrath
8dc9c4b46c 1.5.0 2022-06-09 21:35:44 +02:00
Stef Heyenrath
0441c1d85e Add MatchOperator "Or", "And" and "Average" for patterns (#755)
* wip

* ...

* .

* ...

* ...

* path

* url

* b

* t

* client

* .

* RequestMessageMethodMatcherTests

* .

* h

* .

* fix tests

* .
2022-06-09 21:31:54 +02:00
Stef Heyenrath
1f23022460 1.4.43 2022-05-21 16:15:11 +02:00
Stef Heyenrath
8d57f37261 Add WireMock.Net.xUnit project (#759)
* WireMock.Net.xunit

* Xunit

* c

* .

* x
2022-05-21 09:00:24 +02:00
Kenneth Siewers Møller
ca6c39c63a Log correct exception message when handling aggregate exceptions in console logger (#757)
Co-authored-by: Kenneth Siewers Møller <ksm@templafy.com>
2022-05-19 23:20:47 +02:00
Stef Heyenrath
d7173d34a3 1.4.42 2022-05-13 22:06:33 +02:00
Stef Heyenrath
5e301fd74b Swagger support (#749)
* r

* fix

* sw

* x

* s

* .

* .

* .

* CreateTypeFromJObject

* .

* .

* f

* c

* .

* .

* .

* .

* .

* .

* ok

* ,

* .

* .

* .

* .

* n

* pact

* fix

* schema

* null

* fluent

* r

* -p

* .

* .

* refs

* .
2022-05-13 22:01:46 +02:00
Stef Heyenrath
0d8b3b1438 Update NuGets packages (#751)
* .

* re

* .

* fb

* .
2022-04-30 16:20:53 +02:00
Stef Heyenrath
fe265faf33 Newtonsoft.Json 13.0.1 2022-04-25 10:01:04 +02:00
Snyk bot
7da264636e [Snyk] Security upgrade Newtonsoft.Json from 11.0.2 to 13.0.1 (#750)
* fix: src/WireMock.Net/WireMock.Net.csproj to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DOTNET-NEWTONSOFTJSON-2774678

* Update WireMock.Net.csproj

Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
2022-04-25 09:12:42 +02:00
Stef Heyenrath
14a5f37bc6 Newtonsoft.Json 13.0.1 2022-04-24 19:26:23 +02:00
Stef Heyenrath
a6ee2dacc7 Initial support for converting the mappings to a Pact(flow) json file (#748)
* WithDescription

* WithConsumer / WithProvider

* x

* .

* .

* .

* .

* fix

* pact

* nullable

* ficx

* .

* fix
2022-04-22 16:17:50 +02:00
Stef Heyenrath
b06b3c8e8b 1.4.41 2022-04-21 18:28:49 +02:00
Stef Heyenrath
2e5bfc41d5 Update the logic for ProxyAndRecord (#747)
* .

* set

* .

* .

* .

* .

* prio

* appsettings

* TinyMapperUtils

* set

* p

* nullable

* .

* ,

* fs

* .

* .

* --PreferProxyMapping
2022-04-21 17:58:44 +02:00
Stef Heyenrath
9d54994747 Allow Timeout.InfiniteTimeSpan for WithDelay (#746)
* Update WithDelay

* Allow Timeout.InfiniteTimeSpan for WithDelay
2022-04-18 20:55:23 +02:00
Stef Heyenrath
5da6e103ba 1.4.40 2022-03-26 11:22:45 +01:00
Stef Heyenrath
cbf82836f5 Add Port and Url property to WireMockServer (#740) 2022-03-26 11:03:37 +01:00
Stef Heyenrath
0789b97883 1.4.39 2022-03-25 21:39:32 +01:00
Stef Heyenrath
ce4d39ae55 1.4.39 2022-03-25 21:39:14 +01:00
Stef Heyenrath
11c0782673 Upgrade NuGet for RandomDataGenerator.Net to 1.0.14 (#739)
* RandomDataGenerator.Net 1.0.14

* .
2022-03-25 20:34:39 +01:00
Stef Heyenrath
6b15b80605 launchsettings 2022-03-11 14:48:08 +01:00
Stef Heyenrath
55fbc52ce9 net6 (#737) 2022-03-11 14:46:25 +01:00
Stef Heyenrath
2a1d14b52c 1.4.38 2022-03-11 10:12:02 +01:00
Stef Heyenrath
bb3147eb8d Remove interface for all Settings (#736) 2022-03-10 20:58:18 +01:00
Stef Heyenrath
3087ce55b1 Icon for preview 2022-03-07 08:53:13 +01:00
Stef Heyenrath
a5af2ef920 1.4.37 2022-03-02 08:38:53 +01:00
Stef Heyenrath
a9e8e01877 Make X509CertificatePassword optional (#732) 2022-03-01 19:46:33 +01:00
Stef Heyenrath
6c68033739 Fix FileSystemWatcher (#733)
* Define EnhancedFileSystemWatcher in class

* d
2022-03-01 19:45:39 +01:00
Stef Heyenrath
344f5c8111 Fix RootNamespace for 'WireMock.Org.*' projects 2022-02-26 10:34:39 +01:00
Stef Heyenrath
02d28a34fd Fixed bug "dotnet nuget push -n" (#730)
* Use .NET 6.0

* 2

* remove depends on

* -n

* Fixed bug "dotnet nuget push -n"
2022-02-25 21:33:55 +01:00
Stef Heyenrath
8a5f1837ab 1.4.36 2022-02-25 17:03:01 +01:00
Stef Heyenrath
bb33b3f01a Update NuGet packages (#728)
* Update NuGet pacakges

* .

* .

* 3

* FluentBuilder

* 10000

* typo

* f

* .

* net6

* fix build

* t

* t2

* net6

* .

* <PackageReference Include="System.Text.Encodings.Web" Version="4.7.2" />

* .

* ...

* .

* fix
2022-02-25 16:59:31 +01:00
Stef Heyenrath
be070af7ad BodyAsFile should use BodyAsFileIsCached value (#729)
* BodyAsFile should use BodyAsFileIsCached value

* .
2022-02-21 20:41:51 +01:00
Stef Heyenrath
d3ac811a40 1.4.35 2022-02-09 19:17:28 +01:00
Stef Heyenrath
16e0f1aa35 1.4.35 2022-02-09 19:16:56 +01:00
Stef Heyenrath
f919a03d7f Fixed for 'Response BodyAsJson with JArray does not work' (#722) 2022-02-09 19:13:19 +01:00
Stef Heyenrath
bb906587ff 1.4.34 2022-01-27 12:36:35 +01:00
Stef Heyenrath
f2fab98abb MatcherMapper : Always use Pattern (#716) 2022-01-27 12:33:48 +01:00
Stef Heyenrath
288a50ccaf 1.4.33 2022-01-24 12:54:36 +01:00
Stef Heyenrath
ccd433b202 Add support for Cors (#714) 2022-01-24 12:26:19 +01:00
Stef Heyenrath
d6c36bc23b 1.4.32 2022-01-17 15:56:28 +00:00
Levan Nozadze
6b393ebc1d Added support of custom matchers in static mappings (#713)
* Added support of custom matchers in static mappings

* Fixed code style issues

* Fixed naming and code style

* added empty line

* Ignore serialization of CustomMatcherMappings property in WireMockServerSettings

* Added integration tests for CustomMatcherMappings
2022-01-17 16:04:37 +01:00
Stef Heyenrath
60bdc06d29 Small refactor on Template logic 2022-01-06 21:25:15 +01:00
Stef Heyenrath
0f1a4f32ef 1.4.31 2022-01-06 15:52:08 +01:00
Stef Heyenrath
e3a693ede7 Add example for Cors (#712)
* Cors

* Update Program.cs
2022-01-06 15:41:47 +01:00
Stef Heyenrath
b153024de3 Add ReplaceNodeOption flag (#710) 2022-01-05 17:03:29 +01:00
Stef Heyenrath
e8e28c21a1 Use NuGet "Stef.Validation" (#707)
* Use NuGet "Stef.Validation"

* nuget

* .
2021-12-30 10:44:50 +01:00
Bruno Targhetta
fd1f4968b4 Provide open api schema to dynamic examples generator so you can generate accurate data (#706)
* Provide open api schema to dynamic examples generator so you can generate accurate data using settings like max-length in case of a string

* Rename Schema Property and add a dynamic examples generator with properties from settings like max-length

* Remove blank lines

* Add virtual to all public method in WireMockOpenApiParserExampleValues and ireMockOpenApiParserDynamicExampleValues to extend and overrides examples values
2021-12-28 17:38:42 +01:00
Stef Heyenrath
eec9c486a5 1.4.30 2021-12-25 11:06:42 +01:00
Stef Heyenrath
b5ae087b95 Add .ConfigureAwait(false); to the await Task calls (#704)
Add .ConfigureAwait(false); to the await Task calls
2021-12-24 14:08:43 +01:00
Stef Heyenrath
1d1ff4a418 SaveUnmatchedRequests (#703) 2021-12-24 09:34:26 +01:00
Stef Heyenrath
3e1c3598f7 RegexExtended : fix SonarCloud issue 2021-12-14 07:57:42 +00:00
Stef Heyenrath
12b868401d fix test 2021-12-12 15:55:07 +01:00
Stef Heyenrath
a7d283e12b 1.4.29 (release notes) 2021-12-12 15:53:08 +01:00
Stef Heyenrath
5280970765 1.4.29 2021-12-12 15:51:56 +01:00
Stef Heyenrath
6943b90da6 RegexExtended in settings (#700)
* Add extra unittest for RegexExtended

* settings
2021-12-12 15:40:38 +01:00
Michael Brogdon
4a434b5dba GUID Pattern support in RegexMatcher (#699)
* Add the RegexGuid class

* Use of RegexGuid in the RegexMatcher

* Fix up the CodeFactor violations

* Rename RegexGuid --> RegexExtended
2021-12-11 10:57:15 +01:00
Stef Heyenrath
3dafd2e725 update WireMockServer_Should_delay_responses_for_a_given_route 2021-12-08 21:56:26 +01:00
Stef Heyenrath
c6e4608039 1.4.28 2021-12-01 18:51:39 +01:00
Stef Heyenrath
57f89a06e1 Update WireMockOpenApiParserSettings 2021-11-29 08:40:39 +01:00
Daniel L. Romero
4d80eb53fe Allow configure IgnoreCase in settings (#695)
* Add IgnoreCase = true in Request body, query parameters, headers, example value

* Ignorecase is configurable in settings!

* Remove unnecesary comments!
2021-11-29 08:25:15 +01:00
Daniel L. Romero
13c002fede Filter required properti in headers, query params, request body (#696) 2021-11-27 09:44:24 +01:00
mcheguini
9db6e800ad RamlToOpenAPI updated to 0.5.0 (#694)
Co-authored-by: Mazeyar <mcheguini@valley.com>
2021-11-26 20:53:16 +01:00
Stef Heyenrath
897ee9ffe3 Update the OpenApiPathsMapper to handle Value/Wildcard (#691) 2021-11-23 08:09:38 +01:00
Stef Heyenrath
8865543bf1 some code refactorings for WireMock.Net.OpenApiParser 2021-11-21 10:07:27 +01:00
Stef Heyenrath
48cfd2d20e Upgrade some NuGet packages (Codecov, coverlet and NFluent) (#689) 2021-11-19 11:42:48 +01:00
dependabot[bot]
71d2660aff Bump System.Text.Encodings.Web (#688)
Bumps [System.Text.Encodings.Web](https://github.com/dotnet/corefx) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/dotnet/corefx/releases)
- [Commits](https://github.com/dotnet/corefx/commits)

---
updated-dependencies:
- dependency-name: System.Text.Encodings.Web
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-19 10:49:24 +01:00
Snyk bot
7f2a42de96 fix: src/WireMock.Net/WireMock.Net.csproj to reduce vulnerabilities (#686)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DOTNET-MICROSOFTOWIN-1019387
2021-11-19 10:37:09 +01:00
Stef Heyenrath
c333b8b263 1.4.27 2021-11-17 07:45:08 +00:00
Daniel L. Romero
32ddd48321 Support RequestBody (#678)
* Support RequestBody

* SerializeObject in the request matcher and apply JsonMatcher

* Refactor names

* Call to method TryGetContent

* Applied comments

* Changes applied!

* Comments applied V2!
2021-11-17 08:16:31 +01:00
Daniel L. Romero
260abf5275 Support enums in properties (#681)
* Support enums when a example is generated, priority is the enum

* Add null validation to Enum

* Refactor MapSchemaEnum

* Redactor merhod name

* Resolve merge conflict

* Check schema null

* Refactor GetRandom method
2021-11-16 20:38:34 +01:00
Daniel L. Romero
d3b2422ec1 Support examples in properties (#680)
* When the schema properti has an example then it uses the example

* Comments applied
2021-11-08 08:45:01 +01:00
Stef Heyenrath
823917a4ab Simplify test 'Response_ProvideResponseAsync_Handlebars_Humanizer' 2021-11-05 08:03:57 +00:00
Stef Heyenrath
1e968106d1 1.4.26 2021-11-04 07:10:56 +00:00
Daniel L. Romero
49e10ed20f Support basepath from servers (#675)
* Support basepath from servers

* Refactor BasePath

* Comments applied
2021-11-02 15:28:02 +01:00
Daniel L. Romero
c3ec3a66b3 Fix random generate data in url no spaces (#676)
* Fix random generate data in url no spaces

* Format class
2021-11-01 18:53:44 +01:00
Stef Heyenrath
5644491942 1.4.26 2021-10-30 09:26:33 +02:00
Stef Heyenrath
6e5e629525 "RandomDataGenerator.Net" Version="1.0.13" 2021-10-30 09:15:43 +02:00
Daniel L. Romero
77ee123340 Support examples random data generation (#673)
* Add Support Random Generation in Examples, this commit add the bool property DynamicExamples.

* Comments applied

* Remove  '#pragma warning disable 1591'
2021-10-30 09:13:24 +02:00
Daniel L. Romero
ae2a05e86b Improve method MapSchemaToObject to support array and object (#670) 2021-10-28 08:21:30 +02:00
Stef Heyenrath
0e06cf6346 1.4.25 2021-10-27 19:22:46 +02:00
Stef Heyenrath
e9db520cc3 Add TimeSettings (Start, End and TTL) (#661)
* time

* .

* UT

* using JetBrains.Annotations;
2021-10-27 18:57:13 +02:00
Stef Heyenrath
a0e661fae9 Support Schema Example and Support AllOf in definitions (#669)
* Support an Example within a Schema

* Support AllOf in definitions

* Refactor MapSchemaAllOfToObject method and add test files

* Include schema examples

* Refactor duplicate code, create method MapPropertyAsJObject

* Remove commented code

* Merge from master + update WireMock.Net.OpenApiParser.ConsoleApp

Co-authored-by: Daniel <raiga1234@gmail.com>
2021-10-27 09:40:12 +02:00
Stef Heyenrath
25666152bb Add JsonPartialWildcardMatcher (#667)
* JsonPartialWildcardMatcher

* .

* more tests
2021-10-27 08:16:18 +02:00
Stef Heyenrath
6f5eeb5359 Use IWireMockLogger in example 2021-10-26 18:40:34 +02:00
Stef Heyenrath
7ca2095576 Update logging (add version) 2021-10-24 10:44:39 +02:00
Daniel L. Romero
74edad517b Support Array in OpenApi Examples (#664) 2021-10-21 22:12:59 +02:00
Stef Heyenrath
6c65dfcff6 1.4.24 2021-10-20 20:40:02 +02:00
Stef Heyenrath
affe388e30 Add support for AzureAD authentication for REST admin interface (#637) 2021-10-20 16:39:51 +02:00
Stef Heyenrath
48b3e7a305 Add more tests for WithBody (Json and String) and WildcardMatcher 2021-10-19 09:28:52 +00:00
Stef Heyenrath
6194f4e460 Update NotNullOrEmptyMatcher to also implement IStringMatcher (#654)
* Update NotNullOrEmptyMatcher to also implement IStringMatcher

* NotNullOrEmptyMatcher_GetPatterns_Should_Return_EmptyArray

* 24
2021-10-15 12:59:03 +02:00
Stef Heyenrath
57cc616aa3 Implement PatternAsFile for StringMatcher (#651) 2021-10-15 08:54:12 +02:00
Daniel L. Romero
a2a581c84b Refactor method name MapHeaders and httpStatusCode (#649)
* Support edge case: first object, next an array.

* Add mapping to header parameters.

* Refactor the method MapHeadersParameters to MapHeaders

* Refactor method name 'cause is duplicate, only pass httpStatusCode when fail
2021-10-10 19:22:28 +02:00
Stef Heyenrath
e64ed45fcf Fix build from .NET452 and .NET461 example projects 2021-10-09 18:25:18 +02:00
Daniel L. Romero
267f0b7b6d Mapping headers in OpenAPI (#644)
* Support edge case: first object, next an array.

* Add mapping to header parameters.

* Refactor the method MapHeadersParameters to MapHeaders
2021-10-07 19:15:26 +02:00
Daniel L. Romero
34083d826e Support edge case: first object, next an array. (#643) 2021-10-06 17:18:46 +02:00
Stef Heyenrath
72eec7140b WireMock.FluentAssertions 2021-09-29 15:33:06 +00:00
Stef Heyenrath
6fa0f893e7 1.4.23 2021-09-27 21:06:05 +02:00
Stef Heyenrath
2ab075ee09 WireMock.Net.FluentAssertions : upgrade to latest FluentAssertions (#635)
* .

* #634

* fix UT
2021-09-27 21:03:54 +02:00
Stef Heyenrath
772398bea1 1.4.22 2021-09-22 11:39:36 +00:00
Stef Heyenrath
ba0b9d9fd8 Implement Random Delay (#633)
* implement random delays

* fixing CodeFactor issue

* fix code comments

* ...

* UT

Co-authored-by: Michael Yarichuk <michael.yarichuk@gmail.com>
2021-09-22 13:37:41 +02:00
Stef Heyenrath
c67bf75a4b Update FUNDING.yml 2021-09-16 22:25:23 +02:00
Stef Heyenrath
ed74871a26 1.4.21 2021-09-16 13:06:56 +00:00
Stef Heyenrath
cb66c04199 Add WireMock.org RestClient (#631)
* wip...

* x

* .

* .

* .

* r

* 1.4.21-preview-02

* 1.4.21-preview-03

* .

* usings

* wip

* .

* ut

* .

* .

* .

* tests

* .

* comments

* readme
2021-09-16 14:35:08 +02:00
Stef Heyenrath
fd28ebdffa update 'Solution Items' folder 2021-08-14 13:47:18 +02:00
Stef Heyenrath
ce36daa326 Create CreateRelease.yml 2021-08-14 13:45:54 +02:00
Stef Heyenrath
b5a5f5e464 1.4.20 2021-08-06 10:20:28 +00:00
Stef Heyenrath
f5d624eeed Fix issue with FluentBuilder (#628) 2021-08-06 12:17:05 +02:00
Stef Heyenrath
799ea2d219 1.4.19 2021-08-04 14:38:41 +00:00
Stef Heyenrath
0f99e06acc Add NotNullOrEmptyMatcher (#625) 2021-08-04 16:22:22 +02:00
Stef Heyenrath
9d0682bff6 Add FluentBuilder for client models (#622) 2021-08-04 15:40:35 +02:00
Stef Heyenrath
cb1d2a5294 1.4.18 2021-07-10 11:48:54 +02:00
Stef Heyenrath
4b435faf0b Update Handlebars.Net.Helpers.XPath to fix issue with 'xml version' (#619)
Update Handlebars.Net.Helpers.XPath to fix issue with 'xml version'
2021-07-10 11:43:59 +02:00
Stef Heyenrath
5e7cb44525 Update FUNDING.yml 2021-07-07 20:07:40 +02:00
Stef Heyenrath
444298c28c 1.4.17 2021-07-07 18:31:07 +02:00
Stef Heyenrath
6f42aa99bc Handlebars.Net.Helpers.Humanizer (#617) 2021-07-07 17:02:15 +02:00
Stef Heyenrath
8a9ea1b843 Update FUNDING.yml 2021-07-06 12:13:13 +02:00
Stef Heyenrath
dade24de37 Update FUNDING.yml 2021-07-06 12:12:48 +02:00
Stef Heyenrath
6beaa631f4 Update FUNDING.yml 2021-07-03 09:44:33 +02:00
Stef Heyenrath
282281aa7f 1.4.16 2021-06-05 11:00:51 +02:00
Stef Heyenrath
4052a0ef3b Upgrade Handlebars.Net.Helpers to 2.19 (#616)
* Upgrade Handlebars.Net.Helpers to 2.19

* fix ut
2021-06-05 10:59:09 +02:00
Stef Heyenrath
2ca394b7f6 1.4.15 2021-05-19 12:01:19 +00:00
Stef Heyenrath
b04000bfdd Add support for multiple webhooks (#615) 2021-05-19 13:58:48 +02:00
Stef Heyenrath
93ab4e1853 1.4.14 2021-05-11 05:45:07 +00:00
starkpl
80b5eaac6e Add AdditionalServiceRegistration action for custom ASP.NET Core DI setup (#611) 2021-05-11 07:37:20 +02:00
Stef Heyenrath
a1dc2ba646 condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests (#612) 2021-05-10 22:06:42 +02:00
Stef Heyenrath
934c444902 Fix some SonarCloud issues in UnitTests (#610) 2021-05-07 18:23:23 +02:00
Stef Heyenrath
83d178bdb5 1.4.13 2021-04-26 14:53:15 +00:00
Stef Heyenrath
d91b5d5831 Add possibility to use settings to generate MappingModel models with wildcard path parameters. (#609)
* Add optional settings for WithMappingFromOpenApi

* .

* .

* cleanup
2021-04-26 14:44:45 +02:00
Stef Heyenrath
43b96ce340 Add OpenApiParser NuGet to readme 2021-04-23 17:51:45 +02:00
dependabot[bot]
4d8cf43357 Bump System.Text.Encodings.Web (#607)
Bumps [System.Text.Encodings.Web](https://github.com/dotnet/corefx) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/dotnet/corefx/releases)
- [Commits](https://github.com/dotnet/corefx/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-22 18:11:30 +02:00
Stef Heyenrath
328e9090b1 1.4.12 2021-04-22 06:46:24 +00:00
dependabot[bot]
a22b3bfbc5 Bump System.Net.Http in /examples/WireMock.Net.Service (#606)
Bumps System.Net.Http from 4.3.3 to 4.3.4.

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-22 07:49:32 +02:00
dependabot[bot]
29974c7ad4 Bump System.Net.Http from 4.3.3 to 4.3.4 in /src/WireMock.Net (#605)
Bumps System.Net.Http from 4.3.3 to 4.3.4.

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-22 07:48:39 +02:00
Stef Heyenrath
660a09e656 1.4.11 2021-04-18 21:32:42 +02:00
Stef Heyenrath
fbecd3b119 Fix match logic (#604) 2021-04-18 21:30:03 +02:00
Stef Heyenrath
b17840cea9 1.4.10 2021-04-15 19:46:41 +02:00
Stef Heyenrath
969b0da8e2 Fix callback with Headers (#603) 2021-04-15 19:41:45 +02:00
Stef Heyenrath
dc078b57ea 1.4.9 2021-03-31 18:23:49 +02:00
Stef Heyenrath
8140b37095 WithProxy() should save the new mapping (#600)
* WithProxy should save the new mapping

* fix ut

* .
2021-03-31 18:17:27 +02:00
Stef Heyenrath
2ad060bbd4 1.4.8 2021-03-24 17:20:31 +00:00
Stef Heyenrath
d758301e4f Webhook (#591)
Webhook
2021-03-24 18:15:31 +01:00
Stef Heyenrath
cee73023c7 1.4.7 2021-03-21 09:22:18 +01:00
Ben Arnold
6f7d2c83f5 Remove an approximate two second delay in response to the first request from a new socket connection, only occuring on some Windows 10 machines. (#597)
A side-effect of this fix is that is also allows connections to IPv6 addresses.
2021-03-21 09:12:19 +01:00
Stef Heyenrath
2fb0f92a2d Use Handlebars.Net.Helpers Version="2.1.2" (#595) 2021-03-19 15:17:39 +01:00
Stef Heyenrath
ddf2b49240 wip (#594) 2021-03-18 14:29:13 +01:00
Stef Heyenrath
3617e95db6 1.4.6 2021-02-26 12:37:54 +00:00
Stef Heyenrath
aa8510fab3 Fix WithCallback logic when using other fluent builder statements (#587) 2021-02-26 13:10:25 +01:00
Stef Heyenrath
e23249c144 1.4.5 2021-02-11 11:03:09 +00:00
Roman Vovk
7a8f4c3630 Fix response date header (#585) 2021-02-11 11:49:24 +01:00
Stef Heyenrath
ddcf2b29a3 coverlet 2021-02-09 20:43:24 +01:00
Stef Heyenrath
23709fa587 Use new Handlebars.Net.Helpers (#581) 2021-02-09 20:35:44 +01:00
Stef Heyenrath
3b0dc46771 Add Xamarin UI tests (#582)
* xam

* .

* .
2021-02-06 11:54:39 +01:00
Stef Heyenrath
f6ebb1c256 1.4.3 2021-02-05 18:59:25 +01:00
Stef Heyenrath
e4071b9c6a Net5 issue (#579)
* 472

* .

* .

* p

* buffers

* 3

* Fix date

* fix
2021-02-02 22:21:30 +01:00
Stef Heyenrath
48c50e8207 update WireMock.Net.Console.NET5 2021-01-31 15:55:03 +01:00
dependabot[bot]
acee016dec Bump log4net in /examples/WireMock.Net.Console.NETCoreApp2 (#571)
Bumps [log4net](https://github.com/apache/logging-log4net) from 2.0.8 to 2.0.10.
- [Release notes](https://github.com/apache/logging-log4net/releases)
- [Changelog](https://github.com/apache/logging-log4net/blob/master/ReleaseInstructions.txt)
- [Commits](https://github.com/apache/logging-log4net/compare/rel/2.0.8...rel/2.0.10)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-30 09:16:25 +01:00
dependabot[bot]
951f8e5508 Bump log4net in /examples/WireMock.Net.Console.NETCoreApp (#572)
Bumps [log4net](https://github.com/apache/logging-log4net) from 2.0.8 to 2.0.10.
- [Release notes](https://github.com/apache/logging-log4net/releases)
- [Changelog](https://github.com/apache/logging-log4net/blob/master/ReleaseInstructions.txt)
- [Commits](https://github.com/apache/logging-log4net/compare/rel/2.0.8...rel/2.0.10)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-30 09:16:12 +01:00
dependabot[bot]
1a021a7cff Bump log4net in /examples/WireMock.Net.Console.Net461.Classic (#573)
Bumps [log4net](https://github.com/apache/logging-log4net) from 2.0.8 to 2.0.10.
- [Release notes](https://github.com/apache/logging-log4net/releases)
- [Changelog](https://github.com/apache/logging-log4net/blob/master/ReleaseInstructions.txt)
- [Commits](https://github.com/apache/logging-log4net/compare/rel/2.0.8...rel/2.0.10)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-30 09:15:58 +01:00
dependabot[bot]
ced9fc58f5 Bump log4net in /examples/WireMock.Net.Console.Net452.Classic (#574)
Bumps [log4net](https://github.com/apache/logging-log4net) from 2.0.8 to 2.0.10.
- [Release notes](https://github.com/apache/logging-log4net/releases)
- [Changelog](https://github.com/apache/logging-log4net/blob/master/ReleaseInstructions.txt)
- [Commits](https://github.com/apache/logging-log4net/compare/rel/2.0.8...rel/2.0.10)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-30 09:15:45 +01:00
dependabot[bot]
24ae9a7dad Bump log4net in /examples/WireMock.Net.StandAlone.Net452 (#575)
Bumps [log4net](https://github.com/apache/logging-log4net) from 2.0.8 to 2.0.10.
- [Release notes](https://github.com/apache/logging-log4net/releases)
- [Changelog](https://github.com/apache/logging-log4net/blob/master/ReleaseInstructions.txt)
- [Commits](https://github.com/apache/logging-log4net/compare/rel/2.0.8...rel/2.0.10)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-30 09:15:32 +01:00
dependabot[bot]
919b28fa36 Bump log4net in /examples/WireMock.Net.StandAlone.NETCoreApp (#570)
Bumps [log4net](https://github.com/apache/logging-log4net) from 2.0.8 to 2.0.10.
- [Release notes](https://github.com/apache/logging-log4net/releases)
- [Changelog](https://github.com/apache/logging-log4net/blob/master/ReleaseInstructions.txt)
- [Commits](https://github.com/apache/logging-log4net/compare/rel/2.0.8...rel/2.0.10)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-30 09:15:17 +01:00
dependabot[bot]
5de2ea44e3 Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Service (#576)
Bumps [log4net](https://github.com/apache/logging-log4net) from 2.0.8 to 2.0.10.
- [Release notes](https://github.com/apache/logging-log4net/releases)
- [Changelog](https://github.com/apache/logging-log4net/blob/master/ReleaseInstructions.txt)
- [Commits](https://github.com/apache/logging-log4net/compare/rel/2.0.8...rel/2.0.10)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-30 09:15:02 +01:00
Stef Heyenrath
ebb9f128c5 Update Directory.Build.props
1.4.2
2021-01-24 19:27:02 +01:00
Stef Heyenrath
0141cc0d04 1.4.2 2021-01-24 12:36:10 +01:00
Stef Heyenrath
c41989c0f3 Do not save Mappings when SaveMappingForStatusCodePattern does not match (#566) 2021-01-24 12:30:59 +01:00
Stef Heyenrath
e8181e9d53 azure 2021-01-22 20:10:37 +01:00
Stef Heyenrath
e1c9b7be9b 141 2021-01-19 21:25:40 +01:00
Stef Heyenrath
c35315e610 Refactor Transformer (add Scriban) (#562) 2021-01-19 21:11:33 +01:00
Stef Heyenrath
73e73cebb7 1.4.0 2021-01-12 15:58:37 +00:00
Stef Heyenrath
92923a12ae Move CSharpCodeMatcher to a new project (#548)
* matcher

* wip

* fix

* <VersionPrefix>1.4.0</VersionPrefix>

* .

* x

* ?

* netstandard2.1

* {}

* test

* Fix: Assembly with same name is already loaded

* _format file

* AssemblyFile = $"WireMock.CodeHelper.Class{Guid.NewGuid()}"

* AssemblyFile = $"WireMock.CodeHelper.Class{Guid.NewGuid().ToString().Replace("-", "")}"

* GC

* x

* remove load ex

* ret

* readme

* no GC

* GetImplementationTypeByInterface

* ``

* PluginLoader

* type
2021-01-12 16:56:03 +01:00
Stef Heyenrath
d4da8dc15d 1.3.10 2020-12-23 07:50:01 +00:00
Stef Heyenrath
f104b24f66 Update comment for SaveMappingToFile (#553) 2020-12-23 08:46:24 +01:00
Stef Heyenrath
a228cdcb7c Do not save "admin" mappings when running in Proxy - mode (#561)
* wip

* .

* .
2020-12-23 08:46:07 +01:00
Stef Heyenrath
fae27f9dc7 Add more tests for Proxy with Authorization (#555)
* WireMockServer_Proxy_Should_preserve_Authorization_header_in_proxied_request

* !admin

* x

* .
2020-12-16 22:14:53 +01:00
Stef Heyenrath
fa08d0e617 1.3.9 2020-12-08 08:38:53 +00:00
Stef Heyenrath
65e7dbfbd3 [Obsolete("This class will be moved to a separate NuGet package 'WireMock.Net.Matchers.CSharpCode'")] (#551) 2020-12-08 09:32:33 +01:00
Stef Heyenrath
35565f6aa8 WithProxy(...) also use all proxy settings (#550) 2020-12-08 08:21:00 +01:00
Stef Heyenrath
04d55b00a7 NuGet build use: windows-2019 2020-12-03 09:07:44 +00:00
Stef Heyenrath
63f2715db3 1.3.8 2020-12-03 08:40:13 +00:00
Stef Heyenrath
3dfee689b5 Fix Proxying with SSL and NetCoreApp3.1 (#547)
* Fix Proxying with SSL and NetCoreApp3.1

* add test

* ServicePointManager.ServerCertificateValidationCallback = (message, cert, chain, errors) => true;

* dotnet dev-certs https

* x

* .
2020-12-03 09:36:42 +01:00
Stef Heyenrath
933bd7d046 remove branch name from "SonarCloudPrepare" 2020-12-02 22:39:04 +01:00
Stef Heyenrath
fd62c52669 Update README.md 2020-11-30 16:28:41 +01:00
Stef Heyenrath
db2caadf70 Fix SonarCloud OpenCover (coverlet-coverage) (#545) 2020-11-30 15:48:17 +01:00
Stef Heyenrath
3e0c6cce5f RUN_SONAR_CMD_OPENCOVER 2020-11-28 19:14:56 +01:00
Stef Heyenrath
8861b8a3f0 s 2020-11-28 19:12:06 +01:00
Stef Heyenrath
2363cc1311 Build - arguments: '--configuration Debug --framework netcoreapp3.1' 2020-11-28 18:59:00 +01:00
Stef Heyenrath
1f99834ae3 - task: DotNetCoreCLI@2 2020-11-28 18:51:50 +01:00
Stef Heyenrath
8659b352a3 VSTest@2 2020-11-28 18:48:54 +01:00
Stef Heyenrath
8f3aa12086 Codecov 2020-11-28 18:30:17 +01:00
Stef Heyenrath
9b64dbcae3 RUN_SONAR_CMD 2020-11-28 18:20:34 +01:00
Stef Heyenrath
33fd383612 projects: './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' 2020-11-28 18:02:15 +01:00
Stef Heyenrath
01d8dc6b86 SonarCloud 2020-11-28 17:57:08 +01:00
Stef Heyenrath
6af127e9f2 RUN_TEST_NET452 2020-11-28 15:27:32 +01:00
Stef Heyenrath
a3629a4147 using var 2020-11-28 15:21:18 +01:00
Stef Heyenrath
e222a0a9c3 netcoreapp3.1 (with coverage) 2020-11-28 15:15:01 +01:00
Stef Heyenrath
00a6fec7b4 wiremock-net 2020-11-28 15:04:00 +01:00
Stef Heyenrath
47b1d1ab43 SonarCloud - WireMock-Net_WireMock.Net 2020-11-28 14:54:05 +01:00
Stef Heyenrath
e7b6e12855 SonarCloud - WireMock-Net_WireMock.Net 2020-11-28 14:51:53 +01:00
Stef Heyenrath
94e5e99207 Use JDK11 by default 2020-11-28 13:50:52 +01:00
Stef Heyenrath
37a89cbaa4 **\ 2020-11-28 13:33:44 +01:00
Stef Heyenrath
323d0f9dae coverage.netcoreapp3.1.opencover.xml 2020-11-28 13:21:17 +01:00
Stef Heyenrath
16e939746a <TargetFrameworks>net452;netcoreapp3.1</TargetFrameworks> 2020-11-28 13:11:20 +01:00
Stef Heyenrath
85dabc7638 BUILD_SOLUTION_AS_DEBUG 2020-11-28 13:10:33 +01:00
Stef Heyenrath
f0bddf0604 SonarCloud 2020-11-28 13:00:04 +01:00
Stef Heyenrath
3829a5a7f9 Use Java 11 in Azure Pipelines (needed for SonarCloud) (#544)
* Install Java 11

* jdkSourceOption: 'PreInstalled'

* nuget

* 1.12.3
2020-11-28 12:13:56 +01:00
Stef Heyenrath
37d81aabad .NET 5 (#543) 2020-11-28 11:03:55 +01:00
Stef Heyenrath
d1afba5058 Update README.md 2020-11-25 23:32:11 +01:00
Stef Heyenrath
bc19a1c6b9 changelog 2020-11-25 14:38:52 +00:00
Stef Heyenrath
45713eb0d9 Create dotnet-wiremock tool (#542)
* dotnet tool

* .

* c

* x

* ### As a dotnet tool

* help

* v
2020-11-25 15:36:01 +01:00
Stef Heyenrath
4fb455a1b1 Add more info to IWireMockServer 2020-11-24 11:15:52 +00:00
Stef Heyenrath
bde3126f81 Add wiki link 2020-11-21 09:16:34 +01:00
Stef Heyenrath
0d7de47848 1.3.7 2020-11-17 17:50:45 +00:00
Gleb Osokin
548fc2c2c8 Support for partial JSON matching (#539)
* support Json partial match with JsonPartialMatcher

* fix erroneous filenames

* add newline

* newlines fix

* add JsonPartialMatcher to mapper

* curly braces for ifs

* fix JToken type comparison

* more test cases

* rename AreEqual -> IsMatch + more test cases

* separate tests for JPath matcher values

Co-authored-by: Gleb Osokin <gleb.osokin@avira.com>
2020-11-17 17:18:58 +01:00
Stef Heyenrath
2d95167866 1.3.6 2020-11-10 15:58:08 +00:00
Stef Heyenrath
b14bc01bf2 ProxyThisHttps (#538) 2020-11-10 16:42:17 +01:00
Mahmoud Ali
c104b8beba Add assertions for ClientIP, Url and ProxyUrl (#529)
* Add assertions for ClientIP Url and ProxyUrl

* Requested changes
2020-11-10 16:41:44 +01:00
Stef Heyenrath
09533f1e3a Add Custom Certificate settings (#537) 2020-11-10 16:40:15 +01:00
Stef Heyenrath
a0fdc002c8 WithCallback should use also use enum HttpStatusCode (#535)
* Fix #533

* simplyfy code
2020-11-10 10:20:57 +01:00
Stef Heyenrath
e107b5cfca <VersionPrefix>1.3.5</VersionPrefix> 2020-11-04 17:44:51 +01:00
Stef Heyenrath
5d3cbdbfc6 1.3.5 2020-11-04 16:43:03 +00:00
Stef Heyenrath
b8cbeb55b9 WithCallback-Async (#531) 2020-11-04 17:32:32 +01:00
Stef Heyenrath
5b083d753e Fix dotnet-sonarscanner (#530) 2020-11-02 19:37:46 +01:00
Stef Heyenrath
99dbb3f9b6 1.3.4 2020-10-17 08:50:41 +02:00
Stef Heyenrath
6b3bbd8540 Handlebars.Net.Helpers Version="1.1.0" (#525) 2020-10-17 08:47:11 +02:00
Stef Heyenrath
c465ecdd40 ContinuousIntegrationBuild (#522) 2020-10-16 12:40:55 +02:00
Stef Heyenrath
1750d4e1ad 1.3.3 2020-10-15 14:18:59 +00:00
Eduardo Cáceres
94e176dd85 Make kestrel limits configurable (#520)
* Allow kestrel options to be overriden with values of config files and environment variables (see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.1#kestrel-options)

* Make appsettings.json optional

* Move changes to AspNetCoreSelfHost.Framework files.

* Implement ConfigureKestrelServerOptions for .NET Standard 1.3.
2020-10-15 16:05:07 +02:00
Stef Heyenrath
65e01937a6 _logger.Info("WireMock.Net server using .NET Core 3.1"); 2020-10-14 21:38:17 +02:00
Stef Heyenrath
c880dcfc31 1.3.2 2020-10-14 20:40:52 +02:00
Stef Heyenrath
c02dbeb5ee Fix reading JsonMatcher-mapping with object as pattern (#505)
* Fix reading mapping with object pattern for JsonMatcher

* .

* .
2020-10-14 20:21:51 +02:00
Crossbow78
477f3b5cd3 Apply ILogger, IOptions, IHostedService patterns to .NET Core 3.1 example (#514) 2020-10-06 12:24:45 +02:00
Stef Heyenrath
b303f7cf89 add example static .json 2020-10-05 14:29:21 +00:00
Stef Heyenrath
0964eb2c2d WireMock.Net.WebApplication.NETCore3 2020-10-01 12:32:51 +00:00
Stef Heyenrath
5f8bdff936 1.3.1 2020-09-30 18:18:33 +02:00
Anthony Iacono
942fc3a385 Adding netcoreapp3.1 as a target framework (#509)
* Adding netcoreapp3.0 and netcoreapp3.1 as a target framework

* Expanding NETSTANDARD check to include ASP.NET Core targets

* End of life netcoreapp3.0 changes and using NETCOREAPP3_1 instead of USE_ASPNETCORE where possible
2020-09-30 18:11:32 +02:00
Stef Heyenrath
2f5298b0a2 1.3.0 2020-09-29 12:21:14 +00:00
Stef Heyenrath
55cf0f0416 Fix vulnerability in NuGet dependencies (#508)
* .

* .

* test

* Microsoft.AspNetCore.Server.IIS
2020-09-29 14:16:01 +02:00
Stef Heyenrath
76f0ac6465 WireMock (java version) 2020-09-28 17:11:15 +02:00
Stef Heyenrath
7abf56eafa Fix Sonarqube issues 2020-08-13 14:14:04 +02:00
Stef Heyenrath
9665729e58 1.2.18 2020-08-13 09:02:51 +02:00
Stef Heyenrath
06be3aff95 Add ThrowExceptionWhenMatcherFails option to all Matchers (#500)
* .

* ut

* IMatcher.ThrowException

* settings

* more tests

* linq matcher throw

* .

* .
2020-08-13 08:58:18 +02:00
Stef Heyenrath
0d102f3af4 Fix MyGet badges 2020-08-09 16:28:20 +02:00
Stef Heyenrath
b55435ddac HandleRequestsSynchronously (#496) 2020-08-07 08:08:54 +02:00
Stef Heyenrath
c4ee91c614 1.2.17 2020-08-01 18:42:50 +02:00
Stef Heyenrath
4d0373d4ca Scenario : stay on current state for a number of times (#495)
* state

* xml comment
2020-08-01 18:40:35 +02:00
Stef Heyenrath
6c27820659 1.2.16 2020-07-27 19:26:03 +02:00
Stef Heyenrath
9a532108b8 Mark BlacklistedHeaders and BlacklistedCookies as obsolete (#492)
* #489

* .

* FluentMockServerSettings
2020-07-27 17:24:24 +02:00
Stef Heyenrath
9491280fd2 1.2.15 2020-07-19 10:11:12 +02:00
Mahmoud Ali
da62a43875 Add fluent assertions for headers (#485)
* Add headers assertions

* Update FluentAssertions tests with suggested changes
2020-07-19 10:09:07 +02:00
Stef Heyenrath
197f96e303 . 2020-07-13 20:18:26 +02:00
Stef Heyenrath
26997af2d1 1.2.14 2020-07-09 21:53:50 +02:00
Stef Heyenrath
d9c5faa966 NuGet " --skip-duplicate" 2020-07-09 21:50:13 +02:00
Stef Heyenrath
1c88f5d97d fix (#487) 2020-07-09 21:37:39 +02:00
Mahmoud Ali
28865bd053 Bring WireMock.Net.FluentAssertions tests (#483) 2020-07-05 10:52:17 +02:00
Stef Heyenrath
aff936e3b6 Refactor: extract interfaces (#484)
* .

* MatchDetail

* rm

* resp

* log

* f
2020-07-05 10:51:49 +02:00
Stef Heyenrath
9ae02823df Create FUNDING.yml 2020-07-05 10:49:58 +02:00
Stef Heyenrath
bec7de4284 WireMock.Net.FluentAssertions 2020-07-04 09:52:48 +00:00
Stef Heyenrath
c484b48c35 Add PartialMatch to logging / logentries (#482)
* .

* FluentAssertions

* .

* .
2020-07-04 11:39:50 +02:00
Stef Heyenrath
d8c708e97c WireMock.Net.OpenApiParser : version 2020-07-01 07:59:36 +00:00
Stef Heyenrath
e2fbfda3f0 An OpenApi (swagger) parser to generate MappingModel or mapping.json file (#479)
* wip

* .

* .

* nuget

* .

* .

* WithMappingModel

* tests

* json

* codefactor

* sign

* .

* interface

* sln

* comments
2020-07-01 09:57:52 +02:00
Stef Heyenrath
769ddc1fd3 Update issue templates 2020-06-04 14:06:25 +02:00
Stef Heyenrath
4c68a98bf8 {{Math.Add request.query.start.[0] 42}} 2020-06-02 23:38:09 +02:00
Stef Heyenrath
de9fe75d9d update example - {{Math.Add 1 2}} 2020-06-02 23:29:27 +02:00
Stef Heyenrath
da9d624bf1 1.2.13 2020-05-24 22:03:37 +02:00
Stef Heyenrath
432fb54aba Limits.KeepAliveTimeout & Limits.RequestHeadersTimeout (#475) 2020-05-24 22:01:51 +02:00
Stef Heyenrath
c94113308d 1.2.12 2020-05-23 17:33:32 +02:00
Stef Heyenrath
1b1ddeab83 Fixed Proxy when using MultipartForm with byte[] (#473)
* wip

* ByteArrayContentHelper

* ByteArrayContentHelperTests
2020-05-23 16:48:25 +02:00
Stef Heyenrath
d9e3f38fee Create new .sln (#472)
* new sln

* .
2020-05-22 13:35:40 +02:00
Stef Heyenrath
c42c2d5d7a 1.2.11.0 2020-05-18 18:30:47 +02:00
Stef Heyenrath
d67a160144 Fix unhandled exception when target is unavailable (#469)
* wip

* fix

* 31

* known
2020-05-18 15:07:30 +02:00
Stef Heyenrath
7033d85e3a 1.2.10 2020-05-17 20:01:30 +02:00
Stef Heyenrath
7d873611ee Limits (#457) 2020-05-17 19:55:36 +02:00
Stef Heyenrath
f26bf62a13 Include Handlebars.Net.Helpers project (#456)
* wip

* h 100

* rename

* csproj
2020-05-17 19:55:06 +02:00
Stef Heyenrath
6938b6f73c 1.2.9.0 2020-05-14 06:25:36 +00:00
Stef Heyenrath
5b2cd061a6 Fix method ResetMappingsAsync in the RestEase-AdminApi (#465) 2020-05-14 08:00:44 +02:00
Stef Heyenrath
80993c7740 1.2.8.0 2020-05-04 17:36:28 +02:00
Sean Fausett
d4d0f8becd Fix SourceLink support (#463) 2020-05-04 17:33:41 +02:00
Stef Heyenrath
cd613d1c76 1.2.7.0 2020-04-30 17:52:48 +02:00
Stef Heyenrath
5f4c688e49 fix (#461) 2020-04-29 18:01:54 +02:00
Stef Heyenrath
e7949a47d9 1.2.6.0 2020-04-29 07:05:21 +00:00
Stef Heyenrath
a6cf7a48dc When using ResponseMessageTransformer : keep BodyEncoding (#460) 2020-04-29 08:40:49 +02:00
Stef Heyenrath
bf5afef7a1 1.2.5.0 2020-04-17 10:22:40 +00:00
Stef Heyenrath
a8934ec7f9 fix net452 (#454) 2020-04-17 12:18:45 +02:00
Stef Heyenrath
d2ac56e49a 1.2.4.0 2020-04-10 22:54:19 +02:00
Stef Heyenrath
e8a4d52797 Add ValidatedNotNullAttribute (for SonarQube) (#452)
* ValidatedNotNull

* usings
2020-04-10 22:51:34 +02:00
Stef Heyenrath
dac73b6fe0 codecov\1.10.0 (#451) 2020-04-10 20:28:07 +02:00
Stef Heyenrath
e91be0a4d1 Add support for GZip and Deflate (#439)
* gzip - wip

* wip

* tests

* fix gzip and deflate

* CheckIfShouldKillVBCSCompiler

* DisableRequestBodyDecompressing
2020-04-10 19:05:09 +02:00
Stef Heyenrath
a9974a4874 Add readme.md + license from mock4net (#444)
* add readme.md + license from mock4net

* common

* add license info
2020-04-05 18:42:22 +02:00
Stef Heyenrath
1ddd8ff58f Merge branch 'master' of https://github.com/WireMock-Net/WireMock.Net 2020-04-01 22:45:46 +02:00
Stef Heyenrath
5174f9823c 1.2.3.0 2020-04-01 22:45:36 +02:00
Stef Heyenrath
79da7d042b Netstandard21 (#449)
* Add NetStandard2.1

* 21

* CS-Script.Core

* csscript

* ex
2020-04-01 17:10:45 +02:00
Stef Heyenrath
e041e78bc7 1.2.2.0 2020-03-25 17:05:01 +00:00
Stef Heyenrath
5809fae602 fix port (#446) 2020-03-25 17:00:15 +01:00
Stef Heyenrath
8a295e806c post_with_query ; 4 params 2020-03-23 07:56:32 +00:00
Stef Heyenrath
d5d9d4bd1b WireMock.Net.Console.NETCoreApp3 2020-03-22 14:02:51 +01:00
Stef Heyenrath
5f274f1179 example: post_with_query 2020-03-22 12:04:26 +01:00
Stef Heyenrath
8c1cd41df9 1.2.1.0 2020-03-17 22:15:39 +01:00
Stef Heyenrath
fe4ad50119 Fix Null body in handlebar transformation (#442) 2020-03-17 13:52:43 +01:00
Stef Heyenrath
bde981b522 .net 472 example (#440)
ok
2020-03-15 11:17:36 +01:00
Stef Heyenrath
a1d2deb6a9 1.2.0.0 2020-03-14 11:11:32 +01:00
Stef Heyenrath
10dbff2c02 AllowOnlyDefinedHttpStatusCodeInResponse (#422) 2020-03-14 09:13:14 +01:00
Stef Heyenrath
68ffcda53b Let the .NET core/standard WebHostBuilder use a random port (#417)
* wip

* code reformat
2020-03-14 08:51:26 +01:00
Stef Heyenrath
aeb95b02d2 1.1.10 2020-03-05 18:23:16 +01:00
Stef Heyenrath
2dbb984a1e wip (#435) 2020-03-05 18:20:49 +01:00
Stef Heyenrath
88dd1b9aa4 Option to disable JSON deserialization (#434)
* Option to disable JSON deserialization

* Fix build errors, add test case

* make new parameter optional

* set default for contentType as well
2020-03-05 17:59:24 +01:00
Stef Heyenrath
87c4344d65 UsingOptions, UsingConnect and UsingTrace (#427) 2020-03-05 17:56:24 +01:00
Stef Heyenrath
2851c820e0 1.1.9 2020-02-25 22:22:01 +01:00
Stef Heyenrath
13ab37dd3e LinqMatcher (#431) 2020-02-25 22:17:15 +01:00
Stef Heyenrath
f49046374a 1.1.8.0 2020-02-22 11:14:11 +01:00
Stef Heyenrath
69488ced5f fixes for Cookie and Header Reject on Match (#423) 2020-02-22 11:07:45 +01:00
Stef Heyenrath
e6bcd625f7 Don't return empty dictionary object for response headers in JSON mapping (#424) 2020-02-22 11:07:04 +01:00
Stef Heyenrath
d1b42bf436 wip (#421) 2020-02-15 08:30:08 +01:00
Stef Heyenrath
f4861d9bab /needs-a-key 2020-02-14 16:34:52 +01:00
Noah Lerner
4c01ef4838 Support multi line wild card matching (#419) 2020-02-13 20:05:56 +01:00
Stef Heyenrath
83866f5719 Example: RejectOnMatch 2020-02-11 08:55:06 +01:00
Stef Heyenrath
f72c3c33ef 1.1.7 2020-02-06 18:44:52 +01:00
Noah Lerner
32248b6585 Fix new Delete with body missing from IWireMockAdminApi interface (#413)
* Fix new Delete with body missing from IWireMockAdminApi interface

* Admin Delete with mappings in body (#409)

* Add unit test for delete with body

* change order of checks for readability. looks nicer.

* Allow body in DELETE requests

* Fix unit tests according to DELETE supporting body

* Re-run CI

* Fix DELETE with body unit test

* Fix ElementAt index in mappings list

* Fix DELETE with body unit test

* Fix theory tag must be accompanied by some InlineData or Member

* Fix didn't use correct checking syntax

* Fix wrap entire unit test in if region
2020-02-05 19:11:44 +01:00
Stef Heyenrath
1df4502631 fix (#414) 2020-02-05 19:09:57 +01:00
Noah Lerner
5e76a82a21 Improved relative path checking based on file existence (#411)
* Improved relative path checking based on file existence

If the file exists at the relative path, then use it. If not, then use the path as is.

* Apply File.Exists logic to ReadResponseBodyAsString as well

* Make path handling more robust since path is user defined

* Unit tests for relative path feature

* Replace all back and forward slashes with system dependent DirectorySeparatorChar

* Attempt fix broken directory separator chars for Unix platforms

* Revert wrapping GetMappingFolder with CleanPath

* Move CleanPath logic to its own class

* Remove whitespace

* Remove more whitespace

* Improve CleanPath method

* Move PathUtils tests to separate class

Add another test to ResponseWithBodyFromFileTests

* Fix Response_ProvideResponse_WithBodyFromFile_InAdminMappingFolder

* Debug Linux CI build

* Debug Linux CI

* print all files from admin mappings folder

* Debug CleanPath

* Fix removed leading directory separator char in Linux breaks file path

Remove debugging statements

* Move combine to PathUtils

* PathUtils + PathUtilsTests

* Remove replicated (3x) tests throughout ResponseWithBodyFromFileTests

Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
2020-02-02 13:49:34 +01:00
Stef Heyenrath
06ae9d72c3 1.1.6.0 2020-01-27 18:51:31 +01:00
Stef Heyenrath
307a89d324 AllowAnyHttpStatusCodeInResponse (#407)
* .

* ,

* PUBLISH_TESTRESULTS

* fix logging

* fix compile error

* codefactor fix

* Debug - Sonar + other things in csproj
2020-01-27 18:47:58 +01:00
Stef Heyenrath
6ae7fc1d75 <VersionPrefix>1.1.5</VersionPrefix> 2020-01-25 18:06:51 +01:00
Stef Heyenrath
29e86abe12 1.1.5.0 2020-01-25 17:58:44 +01:00
Stef Heyenrath
710fc8dcf6 Fixed StatusCode = null or < 0 (#406)
* .

* fix tests

* responseModel.StatusCode is int statusCodeAsInt && statusCodeAsInt > 0

* < 0
2020-01-25 17:51:38 +01:00
Stef Heyenrath
dfbfa5fd35 1.1.4.0 2020-01-25 15:28:43 +01:00
Stef Heyenrath
69bbd76ca4 Log Exception (#405) 2020-01-25 15:25:47 +01:00
Stef Heyenrath
bd0c5a83c9 WireMock.Net.Service 2020-01-25 13:54:09 +01:00
Stef Heyenrath
a08efe2f78 steps: 2020-01-25 12:39:36 +01:00
Stef Heyenrath
6e151098e5 azure-pipelines 2020-01-25 12:29:13 +01:00
Stef Heyenrath
43d481435c 1.1.3 2020-01-22 16:40:10 +01:00
Kashif Jamal Soofi
ea1be6641a Fix for invalid cast exception (#403) 2020-01-22 14:27:44 +01:00
Stef Heyenrath
93613885c1 1.1.2.0 2020-01-09 20:18:07 +01:00
Vitaliy Davydiak
caee5895eb ResponseModel.StatusCode is deserialized as either string or long. (#399) 2020-01-09 20:15:15 +01:00
Stef Heyenrath
101d755a00 1.1.1.0 2020-01-09 18:04:41 +01:00
Kashif Jamal Soofi
368fdd4c7d Feature/xpath transformer (#398)
* XPath transformer, added handlerbars helper to select nodes using xpath and setting the outerxml value in response

* Added test to select attribute value and node text

* Removed extra empty lines
2020-01-09 17:51:44 +01:00
1390 changed files with 212514 additions and 45898 deletions

View File

@@ -1,15 +0,0 @@
{
"TestRunner": "",
"TestPlatform": "x86",
"TestApartmentState": "STA",
"TestSettings": "",
"ExcludeAttributes": "System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute",
"ExcludeFiles": "",
"ExcludeDirectories": "",
"Filters": "+[*]*",
"IsIncludingSolutionAssemblies": true,
"IsExcludingTestAssemblies": false,
"IsCoveringByTest": true,
"IsMergingByHash": true,
"IsSkippingAutoProps": true
}

14
.copilot/mcp-config.json Normal file
View File

@@ -0,0 +1,14 @@
{
"mcpServers": {
"EXAMPLE-github": {
"command": "npx",
"args": [
"-y",
"@anthropic/github-mcp-server"
],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}

View File

@@ -0,0 +1,42 @@
---
name: "agent-collaboration"
description: "Standard collaboration patterns for all squad agents — worktree awareness, decisions, cross-agent communication"
domain: "team-workflow"
confidence: "high"
source: "extracted from charter boilerplate — identical content in 18+ agent charters"
---
## Context
Every agent on the team follows identical collaboration patterns for worktree awareness, decision recording, and cross-agent communication. These were previously duplicated in every charter's Collaboration section (~300 bytes × 18 agents = ~5.4KB of redundant context). Now centralized here.
The coordinator's spawn prompt already instructs agents to read decisions.md and their history.md. This skill adds the patterns for WRITING decisions and requesting help.
## Patterns
### Worktree Awareness
Use the `TEAM ROOT` path provided in your spawn prompt. All `.squad/` paths are relative to this root. If TEAM ROOT is not provided (rare), run `git rev-parse --show-toplevel` as fallback. Never assume CWD is the repo root.
### Decision Recording
After making a decision that affects other team members, write it to:
`.squad/decisions/inbox/{your-name}-{brief-slug}.md`
Format:
```
### {date}: {decision title}
**By:** {Your Name}
**What:** {the decision}
**Why:** {rationale}
```
### Cross-Agent Communication
If you need another team member's input, say so in your response. The coordinator will bring them in. Don't try to do work outside your domain.
### Reviewer Protocol
If you have reviewer authority and reject work: the original author is locked out from revising that artifact. A different agent must own the revision. State who should revise in your rejection response.
## Anti-Patterns
- Don't read all agent charters — you only need your own context + decisions.md
- Don't write directly to `.squad/decisions.md` — always use the inbox drop-box
- Don't modify other agents' history.md files — that's Scribe's job
- Don't assume CWD is the repo root — always use TEAM ROOT

View File

@@ -0,0 +1,24 @@
---
name: "agent-conduct"
description: "Shared hard rules enforced across all squad agents"
domain: "team-governance"
confidence: "high"
source: "reskill extraction — Product Isolation Rule and Peer Quality Check appeared in all 20 agent charters"
---
## Context
Every squad agent must follow these two hard rules. They were previously duplicated in every charter. Now they live here as a shared skill, loaded once.
## Patterns
### Product Isolation Rule (hard rule)
Tests, CI workflows, and product code must NEVER depend on specific agent names from any particular squad. "Our squad" must not impact "the squad." No hardcoded references to agent names (Flight, EECOM, FIDO, etc.) in test assertions, CI configs, or product logic. Use generic/parameterized values. If a test needs agent names, use obviously-fake test fixtures (e.g., "test-agent-1", "TestBot").
### Peer Quality Check (hard rule)
Before finishing work, verify your changes don't break existing tests. Run the test suite for files you touched. If CI has been failing, check your changes aren't contributing to the problem. When you learn from mistakes, update your history.md.
## Anti-Patterns
- Don't hardcode dev team agent names in product code or tests
- Don't skip test verification before declaring work done
- Don't ignore pre-existing CI failures that your changes may worsen

View File

@@ -0,0 +1,151 @@
---
name: "architectural-proposals"
description: "How to write comprehensive architectural proposals that drive alignment before code is written"
domain: "architecture, product-direction"
confidence: "high"
source: "earned (2026-02-21 interactive shell proposal)"
tools:
- name: "view"
description: "Read existing codebase, prior decisions, and team context before proposing changes"
when: "Always read .squad/decisions.md, relevant PRDs, and current architecture docs before writing proposal"
- name: "create"
description: "Create proposal in docs/proposals/ with structured format"
when: "After gathering context, before any implementation work begins"
---
## Context
Proposals create alignment before code is written. Cheaper to change a doc than refactor code. Use this pattern when:
- Architecture shifts invalidate existing assumptions
- Product direction changes require new foundation
- Multiple waves/milestones will be affected by a decision
- External dependencies (Copilot CLI, SDK APIs) change
## Patterns
### Proposal Structure (docs/proposals/)
**Required sections:**
1. **Problem Statement** — Why current state is broken (specific, measurable evidence)
2. **Proposed Architecture** — Solution with technical specifics (not hand-waving)
3. **What Changes** — Impact on existing work (waves, milestones, modules)
4. **What Stays the Same** — Preserve existing functionality (no regression)
5. **Key Decisions Needed** — Explicit choices with recommendations
6. **Risks and Mitigations** — Likelihood + impact + mitigation strategy
7. **Scope** — What's in v1, what's deferred (timeline clarity)
**Optional sections:**
- Implementation Plan (high-level milestones)
- Success Criteria (measurable outcomes)
- Open Questions (unresolved items)
- Appendix (prior art, alternatives considered)
### Tone Ceiling Enforcement
**Always:**
- Cite specific evidence (user reports, performance data, failure modes)
- Justify recommendations with technical rationale
- Acknowledge trade-offs (no perfect solutions)
- Be specific about APIs, libraries, file paths
**Never:**
- Hype ("revolutionary", "game-changing")
- Hand-waving ("we'll figure it out later")
- Unsubstantiated claims ("users will love this")
- Vague timelines ("soon", "eventually")
### Wave Restructuring Pattern
When a proposal invalidates existing wave structure:
1. **Acknowledge the shift:** "This becomes Wave 0 (Foundation)"
2. **Cascade impacts:** Adjust downstream waves (Wave 1, Wave 2, Wave 3)
3. **Preserve non-blocking work:** Identify what can proceed in parallel
4. **Update dependencies:** Document new blocking relationships
**Example (Interactive Shell):**
- Wave 0 (NEW): Interactive Shell — blocks all other waves
- Wave 1 (ADJUSTED): npm Distribution — shell bundled in cli.js
- Wave 2 (DEFERRED): SquadUI — waits for shell foundation
- Wave 3 (ADJUSTED): Public Docs — now documents shell as primary interface
### Decision Framing
**Format:** "Recommendation: X (recommended) or alternatives?"
**Components:**
- Recommendation (pick one, justify)
- Alternatives (what else was considered)
- Decision rationale (why recommended option wins)
- Needs sign-off from (which agents/roles must approve)
**Example:**
```
### 1. Terminal UI Library: `ink` (recommended) or alternatives?
**Recommendation:** `ink`
**Alternatives:** `blessed`, raw readline
**Decision rationale:** Component model enables testable UI. Battle-tested ecosystem.
**Needs sign-off from:** Brady (product direction), Fortier (runtime performance)
```
### Risk Documentation
**Format per risk:**
- **Risk:** Specific failure mode
- **Likelihood:** Low / Medium / High (not percentages)
- **Impact:** Low / Medium / High
- **Mitigation:** Concrete actions (measurable)
**Example:**
```
### Risk 2: SDK Streaming Reliability
**Risk:** SDK streaming events might drop messages or arrive out of order.
**Likelihood:** Low (SDK is production-grade).
**Impact:** High — broken streaming makes shell unusable.
**Mitigation:**
- Add integration test: Send 1000-message stream, verify all deltas arrive in order
- Implement fallback: If streaming fails, fall back to polling session state
- Log all SDK events to `.squad/orchestration-log/sdk-events.jsonl` for debugging
```
## Examples
**File references from interactive shell proposal:**
- Full proposal: `docs/proposals/squad-interactive-shell.md`
- User directive: `.squad/decisions/inbox/copilot-directive-2026-02-21T202535Z.md`
- Team decisions: `.squad/decisions.md`
- Current architecture: `docs/architecture/module-map.md`, `docs/prd-23-release-readiness.md`
**Key patterns demonstrated:**
1. Read user directive first (understand the "why")
2. Survey current architecture (module map, existing waves)
3. Research SDK APIs (exploration task to validate feasibility)
4. Document problem with specific evidence (unreliable handoffs, zero visibility, UX mismatch)
5. Propose solution with technical specifics (ink components, SDK session management, spawn.ts module)
6. Restructure waves when foundation shifts (Wave 0 becomes blocker)
7. Preserve backward compatibility (squad.agent.md still works, VS Code mode unchanged)
8. Frame decisions explicitly (5 key decisions with recommendations)
9. Document risks with mitigations (5 risks, each with concrete actions)
10. Define scope (what's in v1 vs. deferred)
## Anti-Patterns
**Avoid:**
- ❌ Proposals without problem statements (solution-first thinking)
- ❌ Vague architecture ("we'll use a shell") — be specific (ink components, session registry, spawn.ts)
- ❌ Ignoring existing work — always document impact on waves/milestones
- ❌ No risk analysis — every architecture has risks, document them
- ❌ Unbounded scope — draw the v1 line explicitly
- ❌ Missing decision ownership — always say "needs sign-off from X"
- ❌ No backward compatibility plan — users don't care about your replatform
- ❌ Hand-waving timelines ("a few weeks") — be specific (2-3 weeks, 1 engineer full-time)
**Red flags in proposal reviews:**
- "Users will love this" (citation needed)
- "We'll figure out X later" (scope creep incoming)
- "This is revolutionary" (tone ceiling violation)
- No section on "What Stays the Same" (regression risk)
- No risks documented (wishful thinking)

View File

@@ -0,0 +1,84 @@
---
name: "ci-validation-gates"
description: "Defensive CI/CD patterns: semver validation, token checks, retry logic, draft detection — earned from v0.8.22"
domain: "ci-cd"
confidence: "high"
source: "extracted from Drucker and Trejo charters — earned knowledge from v0.8.22 release incident"
---
## Context
CI workflows must be defensive. These patterns were learned from the v0.8.22 release disaster where invalid semver, wrong token types, missing retry logic, and draft releases caused a multi-hour outage. Both Drucker (CI/CD) and Trejo (Release Manager) carried this knowledge in their charters — now centralized here.
## Patterns
### Semver Validation Gate
Every publish workflow MUST validate version format before `npm publish`. 4-part versions (e.g., 0.8.21.4) are NOT valid semver — npm mangles them.
```yaml
- name: Validate semver
run: |
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}"
if ! npx semver "$VERSION" > /dev/null 2>&1; then
echo "❌ Invalid semver: $VERSION"
echo "Only 3-part versions (X.Y.Z) or prerelease (X.Y.Z-tag.N) are valid."
exit 1
fi
echo "✅ Valid semver: $VERSION"
```
### NPM Token Type Verification
NPM_TOKEN MUST be an Automation token, not a User token with 2FA:
- User tokens require OTP — CI can't provide it → EOTP error
- Create Automation tokens at npmjs.com → Settings → Access Tokens → Automation
- Verify before first publish in any workflow
### Retry Logic for npm Registry Propagation
npm registry uses eventual consistency. After `npm publish` succeeds, the package may not be immediately queryable.
- Propagation: typically 5-30s, up to 2min in rare cases
- All verify steps: 5 attempts, 15-second intervals
- Log each attempt: "Attempt 1/5: Checking package..."
- Exit loop on success, fail after max attempts
```yaml
- name: Verify package (with retry)
run: |
MAX_ATTEMPTS=5
WAIT_SECONDS=15
for attempt in $(seq 1 $MAX_ATTEMPTS); do
echo "Attempt $attempt/$MAX_ATTEMPTS: Checking $PACKAGE@$VERSION..."
if npm view "$PACKAGE@$VERSION" version > /dev/null 2>&1; then
echo "✅ Package verified"
exit 0
fi
[ $attempt -lt $MAX_ATTEMPTS ] && sleep $WAIT_SECONDS
done
echo "❌ Failed to verify after $MAX_ATTEMPTS attempts"
exit 1
```
### Draft Release Detection
Draft releases don't emit `release: published` event. Workflows MUST:
- Trigger on `release: published` (NOT `created`)
- If using workflow_dispatch: verify release is published via GitHub API before proceeding
### Build Script Protection
Set `SKIP_BUILD_BUMP=1` (or `$env:SKIP_BUILD_BUMP = "1"` on Windows) before ANY release build. bump-build.mjs is for dev builds ONLY — it silently mutates versions.
## Known Failure Modes (v0.8.22 Incident)
| # | What Happened | Root Cause | Prevention |
|---|---------------|-----------|------------|
| 1 | 4-part version published, npm mangled it | No semver validation gate | `npx semver` check before every publish |
| 2 | CI failed 5+ times with EOTP | User token with 2FA | Automation token only |
| 3 | Verify returned false 404 | No retry logic for propagation | 5 attempts, 15s intervals |
| 4 | Workflow never triggered | Draft release doesn't emit event | Never create draft releases |
| 5 | Version mutated during release | bump-build.mjs ran in release | SKIP_BUILD_BUMP=1 |
## Anti-Patterns
- ❌ Publishing without semver validation gate
- ❌ Single-shot verification without retry
- ❌ Hard-coded secrets in workflows
- ❌ Silent CI failures — every error needs actionable output with remediation
- ❌ Assuming npm publish is instantly queryable

View File

@@ -0,0 +1,47 @@
# Skill: CLI Command Wiring
**Bug class:** Commands implemented in `packages/squad-cli/src/cli/commands/` but never routed in `cli-entry.ts`.
## Checklist — Adding a New CLI Command
1. **Create command file** in `packages/squad-cli/src/cli/commands/<name>.ts`
- Export a `run<Name>(cwd, options)` async function (or class with static methods for utility modules)
2. **Add routing block** in `packages/squad-cli/src/cli-entry.ts` inside `main()`:
```ts
if (cmd === '<name>') {
const { run<Name> } = await import('./cli/commands/<name>.js');
// parse args, call function
await run<Name>(process.cwd(), options);
return;
}
```
3. **Add help text** in the help section of `cli-entry.ts` (search for `Commands:`):
```ts
console.log(` ${BOLD}<name>${RESET} <description>`);
console.log(` Usage: <name> [flags]`);
```
4. **Verify both exist** — the recurring bug is doing step 1 but missing steps 2-3.
## Wiring Patterns by Command Type
| Type | Example | How to wire |
|------|---------|-------------|
| Standard command | `export.ts`, `build.ts` | `run*()` function, parse flags from `args` |
| Placeholder command | `loop`, `hire` | Inline in cli-entry.ts, prints pending message |
| Utility/check module | `rc-tunnel.ts`, `copilot-bridge.ts` | Wire as diagnostic check (e.g., `isDevtunnelAvailable()`) |
| Subcommand of another | `init-remote.ts` | Already used inside parent + standalone alias |
## Common Import Pattern
```ts
import { BOLD, RESET, DIM, RED, GREEN, YELLOW } from './cli/core/output.js';
```
Use dynamic `await import()` for command modules to keep startup fast (lazy loading).
## History
- **#237 / PR #244:** 4 commands wired (rc, copilot-bridge, init-remote, rc-tunnel). aspire, link, loop, hire were already present.

View File

@@ -0,0 +1,89 @@
---
name: "client-compatibility"
description: "Platform detection and adaptive spawning for CLI vs VS Code vs other surfaces"
domain: "orchestration"
confidence: "high"
source: "extracted"
---
## Context
Squad runs on multiple Copilot surfaces (CLI, VS Code, JetBrains, GitHub.com). The coordinator must detect its platform and adapt spawning behavior accordingly. Different tools are available on different platforms, requiring conditional logic for agent spawning, SQL usage, and response timing.
## Patterns
### Platform Detection
Before spawning agents, determine the platform by checking available tools:
1. **CLI mode**`task` tool is available → full spawning control. Use `task` with `agent_type`, `mode`, `model`, `description`, `prompt` parameters. Collect results via `read_agent`.
2. **VS Code mode**`runSubagent` or `agent` tool is available → conditional behavior. Use `runSubagent` with the task prompt. Drop `agent_type`, `mode`, and `model` parameters. Multiple subagents in one turn run concurrently (equivalent to background mode). Results return automatically — no `read_agent` needed.
3. **Fallback mode** — neither `task` nor `runSubagent`/`agent` available → work inline. Do not apologize or explain the limitation. Execute the task directly.
If both `task` and `runSubagent` are available, prefer `task` (richer parameter surface).
### VS Code Spawn Adaptations
When in VS Code mode, the coordinator changes behavior in these ways:
- **Spawning tool:** Use `runSubagent` instead of `task`. The prompt is the only required parameter — pass the full agent prompt (charter, identity, task, hygiene, response order) exactly as you would on CLI.
- **Parallelism:** Spawn ALL concurrent agents in a SINGLE turn. They run in parallel automatically. This replaces `mode: "background"` + `read_agent` polling.
- **Model selection:** Accept the session model. Do NOT attempt per-spawn model selection or fallback chains — they only work on CLI. In Phase 1, all subagents use whatever model the user selected in VS Code's model picker.
- **Scribe:** Cannot fire-and-forget. Batch Scribe as the LAST subagent in any parallel group. Scribe is light work (file ops only), so the blocking is tolerable.
- **Launch table:** Skip it. Results arrive with the response, not separately. By the time the coordinator speaks, the work is already done.
- **`read_agent`:** Skip entirely. Results return automatically when subagents complete.
- **`agent_type`:** Drop it. All VS Code subagents have full tool access by default. Subagents inherit the parent's tools.
- **`description`:** Drop it. The agent name is already in the prompt.
- **Prompt content:** Keep ALL prompt structure — charter, identity, task, hygiene, response order blocks are surface-independent.
### Feature Degradation Table
| Feature | CLI | VS Code | Degradation |
|---------|-----|---------|-------------|
| Parallel fan-out | `mode: "background"` + `read_agent` | Multiple subagents in one turn | None — equivalent concurrency |
| Model selection | Per-spawn `model` param (4-layer hierarchy) | Session model only (Phase 1) | Accept session model, log intent |
| Scribe fire-and-forget | Background, never read | Sync, must wait | Batch with last parallel group |
| Launch table UX | Show table → results later | Skip table → results with response | UX only — results are correct |
| SQL tool | Available | Not available | Avoid SQL in cross-platform code paths |
| Response order bug | Critical workaround | Possibly necessary (unverified) | Keep the block — harmless if unnecessary |
### SQL Tool Caveat
The `sql` tool is **CLI-only**. It does not exist on VS Code, JetBrains, or GitHub.com. Any coordinator logic or agent workflow that depends on SQL (todo tracking, batch processing, session state) will silently fail on non-CLI surfaces. Cross-platform code paths must not depend on SQL. Use filesystem-based state (`.squad/` files) for anything that must work everywhere.
## Examples
**Example 1: CLI parallel spawn**
```typescript
// Coordinator detects task tool available → CLI mode
task({ agent_type: "general-purpose", mode: "background", model: "claude-sonnet-4.5", ... })
task({ agent_type: "general-purpose", mode: "background", model: "claude-haiku-4.5", ... })
// Later: read_agent for both
```
**Example 2: VS Code parallel spawn**
```typescript
// Coordinator detects runSubagent available → VS Code mode
runSubagent({ prompt: "...Fenster charter + task..." })
runSubagent({ prompt: "...Hockney charter + task..." })
runSubagent({ prompt: "...Scribe charter + task..." }) // Last in group
// Results return automatically, no read_agent
```
**Example 3: Fallback mode**
```typescript
// Neither task nor runSubagent available → work inline
// Coordinator executes the task directly without spawning
```
## Anti-Patterns
- ❌ Using SQL tool in cross-platform workflows (breaks on VS Code/JetBrains/GitHub.com)
- ❌ Attempting per-spawn model selection on VS Code (Phase 1 — only session model works)
- ❌ Fire-and-forget Scribe on VS Code (must batch as last subagent)
- ❌ Showing launch table on VS Code (results already inline)
- ❌ Apologizing or explaining platform limitations to the user
- ❌ Using `task` when only `runSubagent` is available
- ❌ Dropping prompt structure (charter/identity/task) on non-CLI platforms

View File

@@ -0,0 +1,114 @@
---
name: "cross-squad"
description: "Coordinating work across multiple Squad instances"
domain: "orchestration"
confidence: "medium"
source: "manual"
tools:
- name: "squad-discover"
description: "List known squads and their capabilities"
when: "When you need to find which squad can handle a task"
- name: "squad-delegate"
description: "Create work in another squad's repository"
when: "When a task belongs to another squad's domain"
---
## Context
When an organization runs multiple Squad instances (e.g., platform-squad, frontend-squad, data-squad), those squads need to discover each other, share context, and hand off work across repository boundaries. This skill teaches agents how to coordinate across squads without creating tight coupling.
Cross-squad orchestration applies when:
- A task requires capabilities owned by another squad
- An architectural decision affects multiple squads
- A feature spans multiple repositories with different squads
- A squad needs to request infrastructure, tooling, or support from another squad
## Patterns
### Discovery via Manifest
Each squad publishes a `.squad/manifest.json` declaring its name, capabilities, and contact information. Squads discover each other through:
1. **Well-known paths**: Check `.squad/manifest.json` in known org repos
2. **Upstream config**: Squads already listed in `.squad/upstream.json` are checked for manifests
3. **Explicit registry**: A central `squad-registry.json` can list all squads in an org
```json
{
"name": "platform-squad",
"version": "1.0.0",
"description": "Platform infrastructure team",
"capabilities": ["kubernetes", "helm", "monitoring", "ci-cd"],
"contact": {
"repo": "org/platform",
"labels": ["squad:platform"]
},
"accepts": ["issues", "prs"],
"skills": ["helm-developer", "operator-developer", "pipeline-engineer"]
}
```
### Context Sharing
When delegating work, share only what the target squad needs:
- **Capability list**: What this squad can do (from manifest)
- **Relevant decisions**: Only decisions that affect the target squad
- **Handoff context**: A concise description of why this work is being delegated
Do NOT share:
- Internal team state (casting history, session logs)
- Full decision archives (send only relevant excerpts)
- Authentication credentials or secrets
### Work Handoff Protocol
1. **Check manifest**: Verify the target squad accepts the work type (issues, PRs)
2. **Create issue**: Use `gh issue create` in the target repo with:
- Title: `[cross-squad] <description>`
- Label: `squad:cross-squad` (or the squad's configured label)
- Body: Context, acceptance criteria, and link back to originating issue
3. **Track**: Record the cross-squad issue URL in the originating squad's orchestration log
4. **Poll**: Periodically check if the delegated issue is closed/completed
### Feedback Loop
Track delegated work completion:
- Poll target issue status via `gh issue view`
- Update originating issue with status changes
- Close the feedback loop when delegated work merges
## Examples
### Discovering squads
```bash
# List all squads discoverable from upstreams and known repos
squad discover
# Output:
# platform-squad → org/platform (kubernetes, helm, monitoring)
# frontend-squad → org/frontend (react, nextjs, storybook)
# data-squad → org/data (spark, airflow, dbt)
```
### Delegating work
```bash
# Delegate a task to the platform squad
squad delegate platform-squad "Add Prometheus metrics endpoint for the auth service"
# Creates issue in org/platform with cross-squad label and context
```
### Manifest in squad.config.ts
```typescript
export default defineSquad({
manifest: {
name: 'platform-squad',
capabilities: ['kubernetes', 'helm'],
contact: { repo: 'org/platform', labels: ['squad:platform'] },
accepts: ['issues', 'prs'],
skills: ['helm-developer', 'operator-developer'],
},
});
```
## Anti-Patterns
- **Direct file writes across repos** — Never modify another squad's `.squad/` directory. Use issues and PRs as the communication protocol.
- **Tight coupling** — Don't depend on another squad's internal structure. Use the manifest as the public API contract.
- **Unbounded delegation** — Always include acceptance criteria and a timeout. Don't create open-ended requests.
- **Skipping discovery** — Don't hardcode squad locations. Use manifests and the discovery protocol.
- **Sharing secrets** — Never include credentials, tokens, or internal URLs in cross-squad issues.
- **Circular delegation** — Track delegation chains. If squad A delegates to B which delegates back to A, something is wrong.

View File

@@ -0,0 +1,287 @@
---
name: "distributed-mesh"
description: "How to coordinate with squads on different machines using git as transport"
domain: "distributed-coordination"
confidence: "high"
source: "multi-model-consensus (Opus 4.6, Sonnet 4.5, GPT-5.4)"
---
## SCOPE
**✅ THIS SKILL PRODUCES (exactly these, nothing more):**
1. **`mesh.json`** — Generated from user answers about zones and squads (which squads participate, what zone each is in, paths/URLs for each), using `mesh.json.example` in this skill's directory as the schema template
2. **`sync-mesh.sh` and `sync-mesh.ps1`** — Copied from this skill's directory into the project root (these are bundled resources, NOT generated code)
3. **Zone 2 state repo initialization** (if applicable) — If the user specified a Zone 2 shared state repo, run `sync-mesh.sh --init` to scaffold the state repo structure
4. **A decision entry** in `.squad/decisions/inbox/` documenting the mesh configuration for team awareness
**❌ THIS SKILL DOES NOT PRODUCE:**
- **No application code** — No validators, libraries, or modules of any kind
- **No test files** — No test suites, test cases, or test scaffolding
- **No GENERATING sync scripts** — They are bundled with this skill as pre-built resources. COPY them, don't generate them.
- **No daemons or services** — No background processes, servers, or persistent runtimes
- **No modifications to existing squad files** beyond the decision entry (no changes to team.md, routing.md, agent charters, etc.)
**Your role:** Configure the mesh topology and install the bundled sync scripts. Nothing more.
## Context
When squads are on different machines (developer laptops, CI runners, cloud VMs, partner orgs), the local file-reading convention still works — but remote files need to arrive on your disk first. This skill teaches the pattern for distributed squad communication.
**When this applies:**
- Squads span multiple machines, VMs, or CI runners
- Squads span organizations or companies
- An agent needs context from a squad whose files aren't on the local filesystem
**When this does NOT apply:**
- All squads are on the same machine (just read the files directly)
## Patterns
### The Core Principle
> "The filesystem is the mesh, and git is how the mesh crosses machine boundaries."
The agent interface never changes. Agents always read local files. The distributed layer's only job is to make remote files appear locally before the agent reads them.
### Three Zones of Communication
**Zone 1 — Local:** Same filesystem. Read files directly. Zero transport.
**Zone 2 — Remote-Trusted:** Different host, same org, shared git auth. Transport: `git pull` from a shared repo. This collapses Zone 2 into Zone 1 — files materialize on disk, agent reads them normally.
**Zone 3 — Remote-Opaque:** Different org, no shared auth. Transport: `curl` to fetch published contracts (SUMMARY.md). One-way visibility — you see only what they publish.
### Agent Lifecycle (Distributed)
```
1. SYNC: git pull (Zone 2) + curl (Zone 3) — materialize remote state
2. READ: cat .mesh/**/state.md — all files are local now
3. WORK: do their assigned work (the agent's normal task, NOT mesh-building)
4. WRITE: update own billboard, log, drops
5. PUBLISH: git add + commit + push — share state with remote peers
```
Steps 24 are identical to local-only. Steps 1 and 5 are the entire distributed extension. **Note:** "WORK" means the agent performs its normal squad duties — it does NOT mean "build mesh infrastructure."
### The mesh.json Config
```json
{
"squads": {
"auth-squad": { "zone": "local", "path": "../auth-squad/.mesh" },
"ci-squad": {
"zone": "remote-trusted",
"source": "git@github.com:our-org/ci-squad.git",
"ref": "main",
"sync_to": ".mesh/remotes/ci-squad"
},
"partner-fraud": {
"zone": "remote-opaque",
"source": "https://partner.dev/squad-contracts/fraud/SUMMARY.md",
"sync_to": ".mesh/remotes/partner-fraud",
"auth": "bearer"
}
}
}
```
Three zone types, one file. Local squads need only a path. Remote-trusted need a git URL. Remote-opaque need an HTTP URL.
### Write Partitioning
Each squad writes only to its own directory (`boards/{self}.md`, `squads/{self}/*`, `drops/{date}-{self}-*.md`). No two squads write to the same file. Git push/pull never conflicts. If push fails ("branch is behind"), the fix is always `git pull --rebase && git push`.
### Trust Boundaries
Trust maps to git permissions:
- **Same repo access** = full mesh visibility
- **Read-only access** = can observe, can't write
- **No access** = invisible (correct behavior)
For selective visibility, use separate repos per audience (internal, partner, public). Git permissions ARE the trust negotiation.
### Phased Rollout
- **Phase 0:** Convention only — document zones, agree on mesh.json fields, manually run `git pull`/`git push`. Zero new code.
- **Phase 1:** Sync script (~30 lines bash or PowerShell) when manual sync gets tedious.
- **Phase 2:** Published contracts + curl fetch when a Zone 3 partner appears.
- **Phase 3:** Never. No MCP federation, A2A, service discovery, message queues.
**Important:** Phases are NOT auto-advanced. These are project-level decisions — you start at Phase 0 (manual sync) and only move forward when the team decides complexity is justified.
### Mesh State Repo
The shared mesh state repo is a plain git repository — NOT a Squad project. It holds:
- One directory per participating squad
- Each directory contains at minimum a SUMMARY.md with the squad's current state
- A root README explaining what the repo is and who participates
No `.squad/` folder, no agents, no automation. Write partitioning means each squad only pushes to its own directory. The repo is a rendezvous point, not an intelligent system.
If you want a squad that *observes* mesh health, that's a separate Squad project that lists the state repo as a Zone 2 remote in its `mesh.json` — it does NOT live inside the state repo.
## Examples
### Developer Laptop + CI Squad (Zone 2)
Auth-squad agent wakes up. `git pull` brings ci-squad's latest results. Agent reads: "3 test failures in auth module." Adjusts work. Pushes results when done. **Overhead: one `git pull`, one `git push`.**
### Two Orgs Collaborating (Zone 3)
Payment-squad fetches partner's published SUMMARY.md via curl. Reads: "Risk scoring v3 API deprecated April 15. New field `device_fingerprint` required." The consuming agent (in payment-squad's team) reads this information and uses it to inform its work — for example, updating payment integration code to include the new field. Partner can't see payment-squad's internals.
### Same Org, Shared Mesh Repo (Zone 2)
Three squads on different machines. One shared git repo holds the mesh. Each squad: `git pull` before work, `git push` after. Write partitioning ensures zero merge conflicts.
## AGENT WORKFLOW (Deterministic Setup)
When a user invokes this skill to set up a distributed mesh, follow these steps **exactly, in order:**
### Step 1: ASK the user for mesh topology
Ask these questions (adapt phrasing naturally, but get these answers):
1. **Which squads are participating?** (List of squad names)
2. **For each squad, which zone is it in?**
- `local` — same filesystem (just need a path)
- `remote-trusted` — different machine, same org, shared git access (need git URL + ref)
- `remote-opaque` — different org, no shared auth (need HTTPS URL to published contract)
3. **For each squad, what's the connection info?**
- Local: relative or absolute path to their `.mesh/` directory
- Remote-trusted: git URL (SSH or HTTPS), ref (branch/tag), and where to sync it to locally
- Remote-opaque: HTTPS URL to their SUMMARY.md, where to sync it, and auth type (none/bearer)
4. **Where should the shared state live?** (For Zone 2 squads: git repo URL for the mesh state, or confirm each squad syncs independently)
### Step 2: GENERATE `mesh.json`
Using the answers from Step 1, create a `mesh.json` file at the project root. Use `mesh.json.example` from THIS skill's directory (`.squad/skills/distributed-mesh/mesh.json.example`) as the schema template.
Structure:
```json
{
"squads": {
"<squad-name>": { "zone": "local", "path": "<relative-or-absolute-path>" },
"<squad-name>": {
"zone": "remote-trusted",
"source": "<git-url>",
"ref": "<branch-or-tag>",
"sync_to": ".mesh/remotes/<squad-name>"
},
"<squad-name>": {
"zone": "remote-opaque",
"source": "<https-url-to-summary>",
"sync_to": ".mesh/remotes/<squad-name>",
"auth": "<none|bearer>"
}
}
}
```
Write this file to the project root. Do NOT write any other code.
### Step 3: COPY sync scripts
Copy the bundled sync scripts from THIS skill's directory into the project root:
- **Source:** `.squad/skills/distributed-mesh/sync-mesh.sh`
- **Destination:** `sync-mesh.sh` (project root)
- **Source:** `.squad/skills/distributed-mesh/sync-mesh.ps1`
- **Destination:** `sync-mesh.ps1` (project root)
These are bundled resources. Do NOT generate them — COPY them directly.
### Step 4: RUN `--init` (if Zone 2 state repo exists)
If the user specified a Zone 2 shared state repo in Step 1, run the initialization:
**On Unix/Linux/macOS:**
```bash
bash sync-mesh.sh --init
```
**On Windows:**
```powershell
.\sync-mesh.ps1 -Init
```
This scaffolds the state repo structure (squad directories, placeholder SUMMARY.md files, root README).
**Skip this step if:**
- No Zone 2 squads are configured (local/opaque only)
- The state repo already exists and is initialized
### Step 5: WRITE a decision entry
Create a decision file at `.squad/decisions/inbox/<your-agent-name>-mesh-setup.md` with this content:
```markdown
### <YYYY-MM-DD>: Mesh configuration
**By:** <your-agent-name> (via distributed-mesh skill)
**What:** Configured distributed mesh with <N> squads across zones <list-zones-used>
**Squads:**
- `<squad-name>` — Zone <X> — <brief-connection-info>
- `<squad-name>` — Zone <X> — <brief-connection-info>
- ...
**State repo:** <git-url-if-zone-2-used, or "N/A (local/opaque only)">
**Why:** <user's stated reason for setting up the mesh, or "Enable cross-machine squad coordination">
```
Write this file. The Scribe will merge it into the main decisions file later.
### Step 6: STOP
**You are done.** Do not:
- Generate sync scripts (they're bundled with this skill — COPY them)
- Write validator code
- Write test files
- Create any other modules, libraries, or application code
- Modify existing squad files (team.md, routing.md, charters)
- Auto-advance to Phase 2 or Phase 3
Output a simple completion message:
```
✅ Mesh configured. Created:
- mesh.json (<N> squads)
- sync-mesh.sh and sync-mesh.ps1 (copied from skill bundle)
- Decision entry: .squad/decisions/inbox/<filename>
Run `bash sync-mesh.sh` (or `.\sync-mesh.ps1` on Windows) before agents start to materialize remote state.
```
---
## Anti-Patterns
**❌ Code generation anti-patterns:**
- Writing `mesh-config-validator.js` or any validator module
- Writing test files for mesh configuration
- Generating sync scripts instead of copying the bundled ones from this skill's directory
- Creating library modules or utilities
- Building any code that "runs the mesh" — the mesh is read by agents, not executed
**❌ Architectural anti-patterns:**
- Building a federation protocol — Git push/pull IS federation
- Running a sync daemon or server — Agents are not persistent. Sync at startup, publish at shutdown
- Real-time notifications — Agents don't need real-time. They need "recent enough." `git pull` is recent enough
- Schema validation for markdown — The LLM reads markdown. If the format changes, it adapts
- Service discovery protocol — mesh.json is a file with 10 entries. Not a "discovery problem"
- Auth framework — Git SSH keys and HTTPS tokens. Not a framework. Already configured
- Message queues / event buses — Agents wake, read, work, write, sleep. Nobody's home to receive events
- Any component requiring a running process — That's the line. Don't cross it
**❌ Scope creep anti-patterns:**
- Auto-advancing phases without user decision
- Modifying agent charters or routing rules
- Setting up CI/CD pipelines for mesh sync
- Creating dashboards or monitoring tools

View File

@@ -0,0 +1,30 @@
{
"squads": {
"auth-squad": {
"zone": "local",
"path": "../auth-squad/.mesh"
},
"api-squad": {
"zone": "local",
"path": "../api-squad/.mesh"
},
"ci-squad": {
"zone": "remote-trusted",
"source": "git@github.com:our-org/ci-squad.git",
"ref": "main",
"sync_to": ".mesh/remotes/ci-squad"
},
"data-squad": {
"zone": "remote-trusted",
"source": "git@github.com:our-org/data-pipeline.git",
"ref": "main",
"sync_to": ".mesh/remotes/data-squad"
},
"partner-fraud": {
"zone": "remote-opaque",
"source": "https://partner.example.com/squad-contracts/fraud/SUMMARY.md",
"sync_to": ".mesh/remotes/partner-fraud",
"auth": "bearer"
}
}
}

View File

@@ -0,0 +1,111 @@
# sync-mesh.ps1 — Materialize remote squad state locally
#
# Reads mesh.json, fetches remote squads into local directories.
# Run before agent reads. No daemon. No service. ~40 lines.
#
# Usage: .\sync-mesh.ps1 [path-to-mesh.json]
# .\sync-mesh.ps1 -Init [path-to-mesh.json]
# Requires: git
param(
[switch]$Init,
[string]$MeshJson = "mesh.json"
)
$ErrorActionPreference = "Stop"
# Handle -Init mode
if ($Init) {
if (-not (Test-Path $MeshJson)) {
Write-Host "$MeshJson not found"
exit 1
}
Write-Host "🚀 Initializing mesh state repository..."
$config = Get-Content $MeshJson -Raw | ConvertFrom-Json
$squads = $config.squads.PSObject.Properties.Name
# Create squad directories with placeholder SUMMARY.md
foreach ($squad in $squads) {
if (-not (Test-Path $squad)) {
New-Item -ItemType Directory -Path $squad | Out-Null
Write-Host " ✓ Created $squad/"
} else {
Write-Host "$squad/ exists (skipped)"
}
$summaryPath = "$squad/SUMMARY.md"
if (-not (Test-Path $summaryPath)) {
"# $squad`n`n_No state published yet._" | Set-Content $summaryPath
Write-Host " ✓ Created $summaryPath"
} else {
Write-Host "$summaryPath exists (skipped)"
}
}
# Generate root README.md
if (-not (Test-Path "README.md")) {
$readme = @"
# Squad Mesh State Repository
This repository tracks published state from participating squads.
## Participating Squads
"@
foreach ($squad in $squads) {
$zone = $config.squads.$squad.zone
$readme += "- **$squad** (Zone: $zone)`n"
}
$readme += @"
Each squad directory contains a ``SUMMARY.md`` with their latest published state.
State is synchronized using ``sync-mesh.sh`` or ``sync-mesh.ps1``.
"@
$readme | Set-Content "README.md"
Write-Host " ✓ Created README.md"
} else {
Write-Host " • README.md exists (skipped)"
}
Write-Host ""
Write-Host "✅ Mesh state repository initialized"
exit 0
}
$config = Get-Content $MeshJson -Raw | ConvertFrom-Json
# Zone 2: Remote-trusted — git clone/pull
foreach ($entry in $config.squads.PSObject.Properties | Where-Object { $_.Value.zone -eq "remote-trusted" }) {
$squad = $entry.Name
$source = $entry.Value.source
$ref = if ($entry.Value.ref) { $entry.Value.ref } else { "main" }
$target = $entry.Value.sync_to
if (Test-Path "$target/.git") {
git -C $target pull --rebase --quiet 2>$null
if ($LASTEXITCODE -ne 0) { Write-Host "${squad}: pull failed (using stale)" }
} else {
New-Item -ItemType Directory -Force -Path (Split-Path $target -Parent) | Out-Null
git clone --quiet --depth 1 --branch $ref $source $target 2>$null
if ($LASTEXITCODE -ne 0) { Write-Host "${squad}: clone failed (unavailable)" }
}
}
# Zone 3: Remote-opaque — fetch published contracts
foreach ($entry in $config.squads.PSObject.Properties | Where-Object { $_.Value.zone -eq "remote-opaque" }) {
$squad = $entry.Name
$source = $entry.Value.source
$target = $entry.Value.sync_to
$auth = $entry.Value.auth
New-Item -ItemType Directory -Force -Path $target | Out-Null
$params = @{ Uri = $source; OutFile = "$target/SUMMARY.md"; UseBasicParsing = $true }
if ($auth -eq "bearer") {
$tokenVar = ($squad.ToUpper() -replace '-', '_') + "_TOKEN"
$token = [Environment]::GetEnvironmentVariable($tokenVar)
if ($token) { $params.Headers = @{ Authorization = "Bearer $token" } }
}
try { Invoke-WebRequest @params -ErrorAction Stop }
catch { "# ${squad} — unavailable ($(Get-Date))" | Set-Content "$target/SUMMARY.md" }
}
Write-Host "✓ Mesh sync complete"

View File

@@ -0,0 +1,104 @@
#!/bin/bash
# sync-mesh.sh — Materialize remote squad state locally
#
# Reads mesh.json, fetches remote squads into local directories.
# Run before agent reads. No daemon. No service. ~40 lines.
#
# Usage: ./sync-mesh.sh [path-to-mesh.json]
# ./sync-mesh.sh --init [path-to-mesh.json]
# Requires: jq (https://github.com/jqlang/jq), git, curl
set -euo pipefail
# Handle --init mode
if [ "${1:-}" = "--init" ]; then
MESH_JSON="${2:-mesh.json}"
if [ ! -f "$MESH_JSON" ]; then
echo "$MESH_JSON not found"
exit 1
fi
echo "🚀 Initializing mesh state repository..."
squads=$(jq -r '.squads | keys[]' "$MESH_JSON")
# Create squad directories with placeholder SUMMARY.md
for squad in $squads; do
if [ ! -d "$squad" ]; then
mkdir -p "$squad"
echo " ✓ Created $squad/"
else
echo "$squad/ exists (skipped)"
fi
if [ ! -f "$squad/SUMMARY.md" ]; then
echo -e "# $squad\n\n_No state published yet._" > "$squad/SUMMARY.md"
echo " ✓ Created $squad/SUMMARY.md"
else
echo "$squad/SUMMARY.md exists (skipped)"
fi
done
# Generate root README.md
if [ ! -f "README.md" ]; then
{
echo "# Squad Mesh State Repository"
echo ""
echo "This repository tracks published state from participating squads."
echo ""
echo "## Participating Squads"
echo ""
for squad in $squads; do
zone=$(jq -r ".squads.\"$squad\".zone" "$MESH_JSON")
echo "- **$squad** (Zone: $zone)"
done
echo ""
echo "Each squad directory contains a \`SUMMARY.md\` with their latest published state."
echo "State is synchronized using \`sync-mesh.sh\` or \`sync-mesh.ps1\`."
} > README.md
echo " ✓ Created README.md"
else
echo " • README.md exists (skipped)"
fi
echo ""
echo "✅ Mesh state repository initialized"
exit 0
fi
MESH_JSON="${1:-mesh.json}"
# Zone 2: Remote-trusted — git clone/pull
for squad in $(jq -r '.squads | to_entries[] | select(.value.zone == "remote-trusted") | .key' "$MESH_JSON"); do
source=$(jq -r ".squads.\"$squad\".source" "$MESH_JSON")
ref=$(jq -r ".squads.\"$squad\".ref // \"main\"" "$MESH_JSON")
target=$(jq -r ".squads.\"$squad\".sync_to" "$MESH_JSON")
if [ -d "$target/.git" ]; then
git -C "$target" pull --rebase --quiet 2>/dev/null \
|| echo "$squad: pull failed (using stale)"
else
mkdir -p "$(dirname "$target")"
git clone --quiet --depth 1 --branch "$ref" "$source" "$target" 2>/dev/null \
|| echo "$squad: clone failed (unavailable)"
fi
done
# Zone 3: Remote-opaque — fetch published contracts
for squad in $(jq -r '.squads | to_entries[] | select(.value.zone == "remote-opaque") | .key' "$MESH_JSON"); do
source=$(jq -r ".squads.\"$squad\".source" "$MESH_JSON")
target=$(jq -r ".squads.\"$squad\".sync_to" "$MESH_JSON")
auth=$(jq -r ".squads.\"$squad\".auth // \"\"" "$MESH_JSON")
mkdir -p "$target"
auth_flag=""
if [ "$auth" = "bearer" ]; then
token_var="$(echo "${squad}" | tr '[:lower:]-' '[:upper:]_')_TOKEN"
[ -n "${!token_var:-}" ] && auth_flag="--header \"Authorization: Bearer ${!token_var}\""
fi
eval curl --silent --fail $auth_flag "$source" -o "$target/SUMMARY.md" 2>/dev/null \
|| echo "# ${squad} — unavailable ($(date))" > "$target/SUMMARY.md"
done
echo "✓ Mesh sync complete"

View File

@@ -0,0 +1,71 @@
---
name: "docs-standards"
description: "Microsoft Style Guide + Squad-specific documentation patterns"
domain: "documentation"
confidence: "high"
source: "earned (PAO charter, multiple doc PR reviews)"
---
## Context
Squad documentation follows the Microsoft Style Guide with Squad-specific conventions. Consistency across docs builds trust and improves discoverability.
## Patterns
### Microsoft Style Guide Rules
- **Sentence-case headings:** "Getting started" not "Getting Started"
- **Active voice:** "Run the command" not "The command should be run"
- **Second person:** "You can configure..." not "Users can configure..."
- **Present tense:** "The system routes..." not "The system will route..."
- **No ampersands in prose:** "and" not "&" (except in code, brand names, or UI elements)
### Squad Formatting Patterns
- **Scannability first:** Paragraphs for narrative (3-4 sentences max), bullets for scannable lists, tables for structured data
- **"Try this" prompts at top:** Start feature/scenario pages with practical prompts users can copy
- **Experimental warnings:** Features in preview get callout at top
- **Cross-references at bottom:** Related pages linked after main content
### Structure
- **Title (H1)** → **Warning/callout****Try this code****Overview****HR****Content (H2 sections)**
### Test Sync Rule
- **Always update test assertions:** When adding docs pages to `features/`, `scenarios/`, `guides/`, update corresponding `EXPECTED_*` arrays in `test/docs-build.test.ts` in the same commit
## Examples
**Correct:**
```markdown
# Getting started with Squad
> ⚠️ **Experimental:** This feature is in preview.
Try this:
\`\`\`bash
squad init
\`\`\`
Squad helps you build AI teams...
---
## Install Squad
Run the following command...
```
**Incorrect:**
```markdown
# Getting Started With Squad // Title case
Squad is a tool which will help users... // Third person, future tense
You can install Squad with npm & configure it... // Ampersand in prose
```
## Anti-Patterns
- Title-casing headings because "it looks nicer"
- Writing in passive voice or third person
- Long paragraphs of dense text (breaks scannability)
- Adding doc pages without updating test assertions
- Using ampersands outside code blocks

View File

@@ -0,0 +1,114 @@
---
name: "economy-mode"
description: "Shifts Layer 3 model selection to cost-optimized alternatives when economy mode is active."
domain: "model-selection"
confidence: "low"
source: "manual"
---
## SCOPE
✅ THIS SKILL PRODUCES:
- A modified Layer 3 model selection table applied when economy mode is active
- `economyMode: true` written to `.squad/config.json` when activated persistently
- Spawn acknowledgments with `💰` indicator when economy mode is active
❌ THIS SKILL DOES NOT PRODUCE:
- Code, tests, or documentation
- Cost reports or billing artifacts
- Changes to Layer 0, Layer 1, or Layer 2 resolution (user intent always wins)
## Context
Economy mode shifts Layer 3 (Task-Aware Auto-Selection) to lower-cost alternatives. It does NOT override persistent config (`defaultModel`, `agentModelOverrides`) or per-agent charter preferences — those represent explicit user intent and always take priority.
Use this skill when the user wants to reduce costs across an entire session or permanently, without manually specifying models for each agent.
## Activation Methods
| Method | How |
|--------|-----|
| Session phrase | "use economy mode", "save costs", "go cheap", "reduce costs" |
| Persistent config | `"economyMode": true` in `.squad/config.json` |
| CLI flag | `squad --economy` |
**Deactivation:** "turn off economy mode", "disable economy mode", or remove `economyMode` from `config.json`.
## Economy Model Selection Table
When economy mode is **active**, Layer 3 auto-selection uses this table instead of the normal defaults:
| Task Output | Normal Mode | Economy Mode |
|-------------|-------------|--------------|
| Writing code (implementation, refactoring, bug fixes) | `claude-sonnet-4.5` | `gpt-4.1` or `gpt-5-mini` |
| Writing prompts or agent designs | `claude-sonnet-4.5` | `gpt-4.1` or `gpt-5-mini` |
| Docs, planning, triage, changelogs, mechanical ops | `claude-haiku-4.5` | `gpt-4.1` or `gpt-5-mini` |
| Architecture, code review, security audits | `claude-opus-4.5` | `claude-sonnet-4.5` |
| Scribe / logger / mechanical file ops | `claude-haiku-4.5` | `gpt-4.1` |
**Prefer `gpt-4.1` over `gpt-5-mini`** when the task involves structured output or agentic tool use. Prefer `gpt-5-mini` for pure text generation tasks where latency matters.
## AGENT WORKFLOW
### On Session Start
1. READ `.squad/config.json`
2. CHECK for `economyMode: true` — if present, activate economy mode for the session
3. STORE economy mode state in session context
### On User Phrase Trigger
**Session-only (no config change):** "use economy mode", "save costs", "go cheap"
1. SET economy mode active for this session
2. ACKNOWLEDGE: `✅ Economy mode active — using cost-optimized models this session. (Layer 0 and Layer 2 preferences still apply)`
**Persistent:** "always use economy mode", "save economy mode"
1. WRITE `economyMode: true` to `.squad/config.json` (merge, don't overwrite other fields)
2. ACKNOWLEDGE: `✅ Economy mode saved — cost-optimized models will be used until disabled.`
### On Every Agent Spawn (Economy Mode Active)
1. CHECK Layer 0a/0b first (agentModelOverrides, defaultModel) — if set, use that. Economy mode does NOT override Layer 0.
2. CHECK Layer 1 (session directive for a specific model) — if set, use that. Economy mode does NOT override explicit session directives.
3. CHECK Layer 2 (charter preference) — if set, use that. Economy mode does NOT override charter preferences.
4. APPLY economy table at Layer 3 instead of normal table.
5. INCLUDE `💰` in spawn acknowledgment: `🔧 {Name} ({model} · 💰 economy) — {task}`
### On Deactivation
**Trigger phrases:** "turn off economy mode", "disable economy mode", "use normal models"
1. REMOVE `economyMode` from `.squad/config.json` (if it was persisted)
2. CLEAR session economy mode state
3. ACKNOWLEDGE: `✅ Economy mode disabled — returning to standard model selection.`
### STOP
After updating economy mode state and including the `💰` indicator in spawn acknowledgments, this skill is done. Do NOT:
- Change Layer 0, Layer 1, or Layer 2 model choices
- Override charter-specified models
- Generate cost reports or comparisons
- Fall back to premium models via economy mode (economy mode never bumps UP)
## Config Schema
`.squad/config.json` economy-related fields:
```json
{
"version": 1,
"economyMode": true
}
```
- `economyMode` — when `true`, Layer 3 uses the economy table. Optional; absent = economy mode off.
- Combines with `defaultModel` and `agentModelOverrides` — Layer 0 always wins.
## Anti-Patterns
- **Don't override Layer 0 in economy mode.** If the user set `defaultModel: "claude-opus-4.6"`, they want quality. Economy mode only affects Layer 3 auto-selection.
- **Don't silently apply economy mode.** Always acknowledge when activated or deactivated.
- **Don't treat economy mode as permanent by default.** Session phrases activate session-only; only "always" or `config.json` persist it.
- **Don't bump premium tasks down too far.** Architecture and security reviews shift from opus to sonnet in economy mode — they do NOT go to fast/cheap models.

View File

@@ -0,0 +1,329 @@
---
name: "external-comms"
description: "PAO workflow for scanning, drafting, and presenting community responses with human review gate"
domain: "community, communication, workflow"
confidence: "low"
source: "manual (RFC #426 — PAO External Communications)"
tools:
- name: "github-mcp-server-list_issues"
description: "List open issues for scan candidates and lightweight triage"
when: "Use for recent open issue scans before thread-level review"
- name: "github-mcp-server-issue_read"
description: "Read the full issue, comments, and labels before drafting"
when: "Use after selecting a candidate so PAO has complete thread context"
- name: "github-mcp-server-search_issues"
description: "Search for candidate issues or prior squad responses"
when: "Use when filtering by keywords, labels, or duplicate response checks"
- name: "gh CLI"
description: "Fallback for GitHub issue comments and discussions workflows"
when: "Use gh issue list/comment and gh api or gh api graphql when MCP coverage is incomplete"
---
## Context
Phase 1 is **draft-only mode**.
- PAO scans issues and discussions, drafts responses with the humanizer skill, and presents a review table for human approval.
- **Human review gate is mandatory** — PAO never posts autonomously.
- Every action is logged to `.squad/comms/audit/`.
- This workflow is triggered manually only ("PAO, check community") — no automated or Ralph-triggered activation in Phase 1.
## Patterns
### 1. Scan
Find unanswered community items with GitHub MCP tools first, or `gh issue list` / `gh api` as fallback for issues and discussions.
- Include **open** issues and discussions only.
- Filter for items with **no squad team response**.
- Limit to items created in the last 7 days.
- Exclude items labeled `squad:internal` or `wontfix`.
- Include discussions **and** issues in the same sweep.
- Phase 1 scope is **issues and discussions only** — do not draft PR replies.
### Discussion Handling (Phase 1)
Discussions use the GitHub Discussions API, which differs from issues:
- **Scan:** `gh api /repos/{owner}/{repo}/discussions --jq '.[] | select(.answer_chosen_at == null)'` to find unanswered discussions
- **Categories:** Filter by Q&A and General categories only (skip Announcements, Show and Tell)
- **Answers vs comments:** In Q&A discussions, PAO drafts an "answer" (not a comment). The human marks it as accepted answer after posting.
- **Phase 1 scope:** Issues and Discussions ONLY. No PR comments.
### 2. Classify
Determine the response type before drafting.
- Welcome (new contributor)
- Troubleshooting (bug/help)
- Feature guidance (feature request/how-to)
- Redirect (wrong repo/scope)
- Acknowledgment (confirmed, no fix)
- Closing (resolved)
- Technical uncertainty (unknown cause)
- Empathetic disagreement (pushback on a decision or design)
- Information request (need more reproduction details or context)
### Template Selection Guide
| Signal in Issue/Discussion | → Response Type | Template |
|---------------------------|-----------------|----------|
| New contributor (0 prior issues) | Welcome | T1 |
| Error message, stack trace, "doesn't work" | Troubleshooting | T2 |
| "How do I...?", "Can Squad...?", "Is there a way to...?" | Feature Guidance | T3 |
| Wrong repo, out of scope for Squad | Redirect | T4 |
| Confirmed bug, no fix available yet | Acknowledgment | T5 |
| Fix shipped, PR merged that resolves issue | Closing | T6 |
| Unclear cause, needs investigation | Technical Uncertainty | T7 |
| Author disagrees with a decision or design | Empathetic Disagreement | T8 |
| Need more reproduction info or context | Information Request | T9 |
Use exactly one template as the base draft. Replace placeholders with issue-specific details, then apply the humanizer patterns. If the thread spans multiple signals, choose the highest-risk template and capture the nuance in the thread summary.
### Confidence Classification
| Confidence | Criteria | Example |
|-----------|----------|---------|
| 🟢 High | Answer exists in Squad docs or FAQ, similar question answered before, no technical ambiguity | "How do I install Squad?" |
| 🟡 Medium | Technical answer is sound but involves judgment calls, OR docs exist but don't perfectly match the question, OR tone is tricky | "Can Squad work with Azure DevOps?" (yes, but setup is nuanced) |
| 🔴 Needs Review | Technical uncertainty, policy/roadmap question, potential reputational risk, author is frustrated/angry, question about unreleased features | "When will Squad support Claude?" |
**Auto-escalation rules:**
- Any mention of competitors → 🔴
- Any mention of pricing/licensing → 🔴
- Author has >3 follow-up comments without resolution → 🔴
- Question references a closed-wontfix issue → 🔴
### 3. Draft
Use the humanizer skill for every draft.
- Complete **Thread-Read Verification** before writing.
- Read the **full thread**, including all comments, before writing.
- Select the matching template from the **Template Selection Guide** and record the template ID in the review notes.
- Treat templates as reusable drafting assets: keep the structure, replace placeholders, and only improvise when the thread truly requires it.
- Validate the draft against the humanizer anti-patterns.
- Flag long threads (`>10` comments) with `⚠️`.
### Thread-Read Verification
Before drafting, PAO MUST verify complete thread coverage:
1. **Count verification:** Compare API comment count with actually-read comments. If mismatch, abort draft.
2. **Deleted comment check:** Use `gh api` timeline to detect deleted comments. If found, flag as ⚠️ in review table.
3. **Thread summary:** Include in every draft: "Thread: {N} comments, last activity {date}, {summary of key points}"
4. **Long thread flag:** If >10 comments, add ⚠️ to review table and include condensed thread summary
5. **Evidence line in review table:** Each draft row includes "Read: {N}/{total} comments" column
### 4. Present
Show drafts for review in this exact format:
```text
📝 PAO — Community Response Drafts
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
| # | Item | Author | Type | Confidence | Read | Preview |
|---|------|--------|------|------------|------|---------|
| 1 | Issue #N | @user | Type | 🟢/🟡/🔴 | N/N | "First words..." |
Confidence: 🟢 High | 🟡 Medium | 🔴 Needs review
Full drafts below ▼
```
Each full draft must begin with the thread summary line:
`Thread: {N} comments, last activity {date}, {summary of key points}`
### 5. Human Action
Wait for explicit human direction before anything is posted.
- `pao approve 1 3` — approve drafts 1 and 3
- `pao edit 2` — edit draft 2
- `pao skip` — skip all
- `banana` — freeze all pending (safe word)
### Rollback — Bad Post Recovery
If a posted response turns out to be wrong, inappropriate, or needs correction:
1. **Delete the comment:**
- Issues: `gh api -X DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}`
- Discussions: `gh api graphql -f query='mutation { deleteDiscussionComment(input: {id: "{node_id}"}) { comment { id } } }'`
2. **Log the deletion:** Write audit entry with action `delete`, include reason and original content
3. **Draft replacement** (if needed): PAO drafts a corrected response, goes through normal review cycle
4. **Postmortem:** If the error reveals a pattern gap, update humanizer anti-patterns or add a new test case
**Safe word — `banana`:**
- Immediately freezes all pending drafts in the review queue
- No new scans or drafts until `pao resume` is issued
- Audit entry logged with halter identity and reason
### 6. Post
After approval:
- Human posts via `gh issue comment` for issues or `gh api` for discussion answers/comments.
- PAO helps by preparing the CLI command.
- Write the audit entry after the posting action.
### 7. Audit
Log every action.
- Location: `.squad/comms/audit/{timestamp}.md`
- Required fields vary by action — see `.squad/comms/templates/audit-entry.md` Conditional Fields table
- Universal required fields: `timestamp`, `action`
- All other fields are conditional on the action type
## Examples
These are reusable templates. Keep the structure, replace placeholders, and adjust only where the thread requires it.
### Example scan command
```bash
gh issue list --state open --json number,title,author,labels,comments --limit 20
```
### Example review table
```text
📝 PAO — Community Response Drafts
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
| # | Item | Author | Type | Confidence | Read | Preview |
|---|------|--------|------|------------|------|---------|
| 1 | Issue #426 | @newdev | Welcome | 🟢 | 1/1 | "Hey @newdev! Welcome to Squad..." |
| 2 | Discussion #18 | @builder | Feature guidance | 🟡 | 4/4 | "Great question! Today the CLI..." |
| 3 | Issue #431 ⚠️ | @debugger | Technical uncertainty | 🔴 | 12/12 | "Interesting find, @debugger..." |
Confidence: 🟢 High | 🟡 Medium | 🔴 Needs review
Full drafts below ▼
```
### Example audit entry (post action)
```markdown
---
timestamp: "2026-03-16T21:30:00Z"
action: "post"
item_number: 426
draft_id: 1
reviewer: "@bradygaster"
---
## Context (draft, approve, edit, skip, post, delete actions)
- Thread depth: 3
- Response type: welcome
- Confidence: 🟢
- Long thread flag: false
## Draft Content (draft, edit, post actions)
Thread: 3 comments, last activity 2026-03-16, reporter hit a preview-build regression after install.
Hey @newdev! Welcome to Squad 👋 Thanks for opening this.
We reproduced the issue in preview builds and we're checking the regression point now.
Let us know if you can share the command you ran right before the failure.
## Post Result (post, delete actions)
https://github.com/bradygaster/squad/issues/426#issuecomment-123456
```
### T1 — Welcome
```text
Hey {author}! Welcome to Squad 👋 Thanks for opening this.
{specific acknowledgment or first answer}
Let us know if you have questions — happy to help!
```
### T2 — Troubleshooting
```text
Thanks for the detailed report, {author}!
Here's what we think is happening: {explanation}
{steps or workaround}
Let us know if that helps, or if you're seeing something different.
```
### T3 — Feature Guidance
```text
Great question! {context on current state}
{guidance or workaround}
We've noted this as a potential improvement — {tracking info if applicable}.
```
### T4 — Redirect
```text
Thanks for reaching out! This one is actually better suited for {correct location}.
{brief explanation of why}
Feel free to open it there — they'll be able to help!
```
### T5 — Acknowledgment
```text
Good catch, {author}. We've confirmed this is a real issue.
{what we know so far}
We'll update this thread when we have a fix. Thanks for flagging it!
```
### T6 — Closing
```text
This should be resolved in {version/PR}! 🎉
{brief summary of what changed}
Thanks for reporting this, {author} — it made Squad better.
```
### T7 — Technical Uncertainty
```text
Interesting find, {author}. We're not 100% sure what's causing this yet.
Here's what we've ruled out: {list}
We'd love more context if you have it — {specific ask}.
We'll dig deeper and update this thread.
```
### T8 — Empathetic Disagreement
```text
We hear you, {author}. That's a fair concern.
The current design choice was driven by {reason}. We know it's not ideal for every use case.
{what alternatives exist or what trade-off was made}
If you have ideas for how to make this work better for your scenario, we'd love to hear them — open a discussion or drop your thoughts here!
```
### T9 — Information Request
```text
Thanks for reporting this, {author}!
To help us dig into this, could you share:
- {specific ask 1}
- {specific ask 2}
- {specific ask 3, if applicable}
That context will help us narrow down what's happening. Appreciate it!
```
## Anti-Patterns
- ❌ Posting without human review (NEVER — this is the cardinal rule)
- ❌ Drafting without reading full thread (context is everything)
- ❌ Ignoring confidence flags (🔴 items need Flight/human review)
- ❌ Scanning closed issues (only open items)
- ❌ Responding to issues labeled `squad:internal` or `wontfix`
- ❌ Skipping audit logging (every action must be recorded)
- ❌ Drafting for issues where a squad member already responded (avoid duplicates)
- ❌ Drafting pull request responses in Phase 1 (issues/discussions only)
- ❌ Treating templates like loose examples instead of reusable drafting assets
- ❌ Asking for more info without specific requests

View File

@@ -0,0 +1,183 @@
---
name: "gh-auth-isolation"
description: "Safely manage multiple GitHub identities (EMU + personal) in agent workflows"
domain: "security, github-integration, authentication, multi-account"
confidence: "high"
source: "earned (production usage across 50+ sessions with EMU corp + personal GitHub accounts)"
tools:
- name: "gh"
description: "GitHub CLI for authenticated operations"
when: "When accessing GitHub resources requiring authentication"
---
## Context
Many developers use GitHub through an Enterprise Managed User (EMU) account at work while maintaining a personal GitHub account for open-source contributions. AI agents spawned by Squad inherit the shell's default `gh` authentication — which is usually the EMU account. This causes failures when agents try to push to personal repos, create PRs on forks, or interact with resources outside the enterprise org.
This skill teaches agents how to detect the active identity, switch contexts safely, and avoid mixing credentials across operations.
## Patterns
### Detect Current Identity
Before any GitHub operation, check which account is active:
```bash
gh auth status
```
Look for:
- `Logged in to github.com as USERNAME` — the active account
- `Token scopes: ...` — what permissions are available
- Multiple accounts will show separate entries
### Extract a Specific Account's Token
When you need to operate as a specific user (not the default):
```bash
# Get the personal account token (by username)
gh auth token --user personaluser
# Get the EMU account token
gh auth token --user corpalias_enterprise
```
**Use case:** Push to a personal fork while the default `gh` auth is the EMU account.
### Push to Personal Repos from EMU Shell
The most common scenario: your shell defaults to the EMU account, but you need to push to a personal GitHub repo.
```bash
# 1. Extract the personal token
$token = gh auth token --user personaluser
# 2. Push using token-authenticated HTTPS
git push https://personaluser:$token@github.com/personaluser/repo.git branch-name
```
**Why this works:** `gh auth token --user` reads from `gh`'s credential store without switching the active account. The token is used inline for a single operation and never persisted.
### Create PRs on Personal Forks
When the default `gh` context is EMU but you need to create a PR from a personal fork:
```bash
# Option 1: Use --repo flag (works if token has access)
gh pr create --repo upstream/repo --head personaluser:branch --title "..." --body "..."
# Option 2: Temporarily set GH_TOKEN for one command
$env:GH_TOKEN = $(gh auth token --user personaluser)
gh pr create --repo upstream/repo --head personaluser:branch --title "..."
Remove-Item Env:\GH_TOKEN
```
### Config Directory Isolation (Advanced)
For complete isolation between accounts, use separate `gh` config directories:
```bash
# Personal account operations
$env:GH_CONFIG_DIR = "$HOME/.config/gh-public"
gh auth login # Login with personal account (one-time setup)
gh repo clone personaluser/repo
# EMU account operations (default)
Remove-Item Env:\GH_CONFIG_DIR
gh auth status # Back to EMU account
```
**Setup (one-time):**
```bash
# Create isolated config for personal account
mkdir ~/.config/gh-public
$env:GH_CONFIG_DIR = "$HOME/.config/gh-public"
gh auth login --web --git-protocol https
```
### Shell Aliases for Quick Switching
Add to your shell profile for convenience:
```powershell
# PowerShell profile
function ghp { $env:GH_CONFIG_DIR = "$HOME/.config/gh-public"; gh @args; Remove-Item Env:\GH_CONFIG_DIR }
function ghe { gh @args } # Default EMU
# Usage:
# ghp repo clone personaluser/repo # Uses personal account
# ghe issue list # Uses EMU account
```
```bash
# Bash/Zsh profile
alias ghp='GH_CONFIG_DIR=~/.config/gh-public gh'
alias ghe='gh'
# Usage:
# ghp repo clone personaluser/repo
# ghe issue list
```
## Examples
### ✓ Correct: Agent pushes blog post to personal GitHub Pages
```powershell
# Agent needs to push to personaluser.github.io (personal repo)
# Default gh auth is corpalias_enterprise (EMU)
$token = gh auth token --user personaluser
git remote set-url origin https://personaluser:$token@github.com/personaluser/personaluser.github.io.git
git push origin main
# Clean up — don't leave token in remote URL
git remote set-url origin https://github.com/personaluser/personaluser.github.io.git
```
### ✓ Correct: Agent creates a PR from personal fork to upstream
```powershell
# Fork: personaluser/squad, Upstream: bradygaster/squad
# Agent is on branch contrib/fix-docs in the fork clone
git push origin contrib/fix-docs # Pushes to fork (may need token auth)
# Create PR targeting upstream
gh pr create --repo bradygaster/squad --head personaluser:contrib/fix-docs `
--title "docs: fix installation guide" `
--body "Fixes #123"
```
### ✗ Incorrect: Blindly pushing with wrong account
```bash
# BAD: Agent assumes default gh auth works for personal repos
git push origin main
# ERROR: Permission denied — EMU account has no access to personal repo
# BAD: Hardcoding tokens in scripts
git push https://personaluser:ghp_xxxxxxxxxxxx@github.com/personaluser/repo.git main
# SECURITY RISK: Token exposed in command history and process list
```
### ✓ Correct: Check before you push
```bash
# Always verify which account has access before operations
gh auth status
# If wrong account, use token extraction:
$token = gh auth token --user personaluser
git push https://personaluser:$token@github.com/personaluser/repo.git main
```
## Anti-Patterns
-**Hardcoding tokens** in scripts, environment variables, or committed files. Use `gh auth token --user` to extract at runtime.
-**Assuming the default `gh` auth works** for all repos. EMU accounts can't access personal repos and vice versa.
-**Switching `gh auth login`** globally mid-session. This changes the default for ALL processes and can break parallel agents.
-**Storing personal tokens in `.env`** or `.squad/` files. These get committed by Scribe. Use `gh`'s credential store.
-**Ignoring token cleanup** after inline HTTPS pushes. Always reset the remote URL to avoid persisting tokens.
-**Using `gh auth switch`** in multi-agent sessions. One agent switching affects all others sharing the shell.
-**Mixing EMU and personal operations** in the same git clone. Use separate clones or explicit remote URLs per operation.

View File

@@ -0,0 +1,204 @@
---
name: "git-workflow"
description: "Squad branching model: dev-first workflow with insiders preview channel"
domain: "version-control"
confidence: "high"
source: "team-decision"
---
## Context
Squad uses a three-branch model. **All feature work starts from `dev`, not `main`.**
| Branch | Purpose | Publishes |
|--------|---------|-----------|
| `main` | Released, tagged, in-npm code only | `npm publish` on tag |
| `dev` | Integration branch — all feature work lands here | `npm publish --tag preview` on merge |
| `insiders` | Early-access channel — synced from dev | `npm publish --tag insiders` on sync |
## Branch Naming Convention
Issue branches MUST use: `squad/{issue-number}-{kebab-case-slug}`
Examples:
- `squad/195-fix-version-stamp-bug`
- `squad/42-add-profile-api`
## Workflow for Issue Work
1. **Branch from dev:**
```bash
git checkout dev
git pull origin dev
git checkout -b squad/{issue-number}-{slug}
```
2. **Mark issue in-progress:**
```bash
gh issue edit {number} --add-label "status:in-progress"
```
3. **Create draft PR targeting dev:**
```bash
gh pr create --base dev --title "{description}" --body "Closes #{issue-number}" --draft
```
4. **Do the work.** Make changes, write tests, commit with issue reference.
5. **Push and mark ready:**
```bash
git push -u origin squad/{issue-number}-{slug}
gh pr ready
```
6. **After merge to dev:**
```bash
git checkout dev
git pull origin dev
git branch -d squad/{issue-number}-{slug}
git push origin --delete squad/{issue-number}-{slug}
```
## Parallel Multi-Issue Work (Worktrees)
When the coordinator routes multiple issues simultaneously (e.g., "fix bugs X, Y, and Z"), use `git worktree` to give each agent an isolated working directory. No filesystem collisions, no branch-switching overhead.
### When to Use Worktrees vs Sequential
| Scenario | Strategy |
|----------|----------|
| Single issue | Standard workflow above — no worktree needed |
| 2+ simultaneous issues in same repo | Worktrees — one per issue |
| Work spanning multiple repos | Separate clones as siblings (see Multi-Repo below) |
### Setup
From the main clone (must be on dev or any branch):
```bash
# Ensure dev is current
git fetch origin dev
# Create a worktree per issue — siblings to the main clone
git worktree add ../squad-195 -b squad/195-fix-stamp-bug origin/dev
git worktree add ../squad-193 -b squad/193-refactor-loader origin/dev
```
**Naming convention:** `../{repo-name}-{issue-number}` (e.g., `../squad-195`, `../squad-pr-42`).
Each worktree:
- Has its own working directory and index
- Is on its own `squad/{issue-number}-{slug}` branch from dev
- Shares the same `.git` object store (disk-efficient)
### Per-Worktree Agent Workflow
Each agent operates inside its worktree exactly like the single-issue workflow:
```bash
cd ../squad-195
# Work normally — commits, tests, pushes
git add -A && git commit -m "fix: stamp bug (#195)"
git push -u origin squad/195-fix-stamp-bug
# Create PR targeting dev
gh pr create --base dev --title "fix: stamp bug" --body "Closes #195" --draft
```
All PRs target `dev` independently. Agents never interfere with each other's filesystem.
### .squad/ State in Worktrees
The `.squad/` directory exists in each worktree as a copy. This is safe because:
- `.gitattributes` declares `merge=union` on append-only files (history.md, decisions.md, logs)
- Each agent appends to its own section; union merge reconciles on PR merge to dev
- **Rule:** Never rewrite or reorder `.squad/` files in a worktree — append only
### Cleanup After Merge
After a worktree's PR is merged to dev:
```bash
# From the main clone
git worktree remove ../squad-195
git worktree prune # clean stale metadata
git branch -d squad/195-fix-stamp-bug
git push origin --delete squad/195-fix-stamp-bug
```
If a worktree was deleted manually (rm -rf), `git worktree prune` recovers the state.
---
## Multi-Repo Downstream Scenarios
When work spans multiple repositories (e.g., squad-cli changes need squad-sdk changes, or a user's app depends on squad):
### Setup
Clone downstream repos as siblings to the main repo:
```
~/work/
squad-pr/ # main repo
squad-sdk/ # downstream dependency
user-app/ # consumer project
```
Each repo gets its own issue branch following its own naming convention. If the downstream repo also uses Squad conventions, use `squad/{issue-number}-{slug}`.
### Coordinated PRs
- Create PRs in each repo independently
- Link them in PR descriptions:
```
Closes #42
**Depends on:** squad-sdk PR #17 (squad-sdk changes required for this feature)
```
- Merge order: dependencies first (e.g., squad-sdk), then dependents (e.g., squad-cli)
### Local Linking for Testing
Before pushing, verify cross-repo changes work together:
```bash
# Node.js / npm
cd ../squad-sdk && npm link
cd ../squad-pr && npm link squad-sdk
# Go
# Use replace directive in go.mod:
# replace github.com/org/squad-sdk => ../squad-sdk
# Python
cd ../squad-sdk && pip install -e .
```
**Important:** Remove local links before committing. `npm link` and `go replace` are dev-only — CI must use published packages or PR-specific refs.
### Worktrees + Multi-Repo
These compose naturally. You can have:
- Multiple worktrees in the main repo (parallel issues)
- Separate clones for downstream repos
- Each combination operates independently
---
## Anti-Patterns
- ❌ Branching from main (branch from dev)
- ❌ PR targeting main directly (target dev)
- ❌ Non-conforming branch names (must be squad/{number}-{slug})
- ❌ Committing directly to main or dev (use PRs)
- ❌ Switching branches in the main clone while worktrees are active (use worktrees instead)
- ❌ Using worktrees for cross-repo work (use separate clones)
- ❌ Leaving stale worktrees after PR merge (clean up immediately)
## Promotion Pipeline
- dev → insiders: Automated sync on green build
- dev → main: Manual merge when ready for stable release, then tag
- Hotfixes: Branch from main as `hotfix/{slug}`, PR to dev, cherry-pick to main if urgent

View File

@@ -0,0 +1,95 @@
---
name: github-multi-account
description: Detect and set up account-locked gh aliases for multi-account GitHub. The AI reads this skill, detects accounts, asks the user which is personal/work, and runs the setup automatically.
confidence: high
source: https://github.com/tamirdresher/squad-skills/tree/main/plugins/github-multi-account
author: tamirdresher
---
# GitHub Multi-Account — AI-Driven Setup
## When to Activate
When the user has multiple GitHub accounts (check with `gh auth status`). If you see 2+ accounts listed, this skill applies.
## What to Do (as the AI agent)
### Step 1: Detect accounts
Run: `gh auth status`
Look for multiple accounts. Note which usernames are listed.
### Step 2: Ask the user
Ask: "I see you have multiple GitHub accounts: {list them}. Which one is your personal account and which is your work/EMU account?"
### Step 3: Run the setup automatically
Once the user confirms, do ALL of this for them:
```powershell
# 1. Define the functions
$personal = "THEIR_PERSONAL_USERNAME"
$work = "THEIR_WORK_USERNAME"
# 2. Add to PowerShell profile
$profilePath = $PROFILE.CurrentUserAllHosts
if (!(Test-Path $profilePath)) { New-Item -Path $profilePath -Force | Out-Null }
$existing = Get-Content $profilePath -Raw -ErrorAction SilentlyContinue
if ($existing -notmatch "gh-personal") {
$block = @"
# === GitHub Multi-Account Aliases ===
function gh-personal { gh auth switch --user $personal 2>`$null | Out-Null; gh @args }
function gh-work { gh auth switch --user $work 2>`$null | Out-Null; gh @args }
Set-Alias ghp gh-personal
Set-Alias ghw gh-work
"@
Add-Content -Path $profilePath -Value $block
}
# 3. Create CMD wrappers
$binDir = Join-Path $env:USERPROFILE ".squad\bin"
if (!(Test-Path $binDir)) { New-Item -ItemType Directory -Path $binDir -Force | Out-Null }
"@echo off`ngh auth switch --user $personal >nul 2>&1`ngh %*" | Out-File "$binDir\ghp.cmd" -Encoding ascii
"@echo off`ngh auth switch --user $work >nul 2>&1`ngh %*" | Out-File "$binDir\ghw.cmd" -Encoding ascii
# 4. Add to PATH
$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
if ($userPath -notmatch [regex]::Escape($binDir)) {
[Environment]::SetEnvironmentVariable("PATH", "$binDir;$userPath", "User")
$env:PATH = "$binDir;$env:PATH"
}
# 5. Load in current session
function gh-personal { gh auth switch --user $personal 2>$null | Out-Null; gh @args }
function gh-work { gh auth switch --user $work 2>$null | Out-Null; gh @args }
Set-Alias ghp gh-personal
Set-Alias ghw gh-work
```
### Step 4: Verify for the user
Run these and show the results:
```powershell
ghp api user --jq '.login' # should show personal username
ghw api user --jq '.login' # should show work username
```
### Step 5: Tell the user
"All set! From now on use `ghp` for personal repos and `ghw` for work repos. I'll use them too."
## After Setup — Usage Rules
1. **NEVER** use bare `gh` for repo operations — always `ghp` or `ghw`
2. **NEVER** manually `gh auth switch` — the aliases handle it
3. Determine alias by repo owner:
- Personal account repos → `ghp` / `gh-personal`
- Work/EMU account repos → `ghw` / `gh-work`
## Repo-Specific Account Binding
This repo (`bradygaster/squad`) is bound to the **bradygaster** (personal) account.
All `gh` operations in this repo MUST use `ghp` / `gh-personal`.
## For Squad Agents
At the TOP of any script touching GitHub, define:
```powershell
function gh-personal { gh auth switch --user bradygaster 2>$null | Out-Null; gh @args }
function gh-work { gh auth switch --user bradyg_microsoft 2>$null | Out-Null; gh @args }
```

View File

@@ -0,0 +1,36 @@
---
name: history-hygiene
description: Record final outcomes to history.md, not intermediate requests or reversed decisions
domain: documentation, team-collaboration
confidence: high
source: earned (Kobayashi v0.6.0 incident, team intervention)
---
## Context
History files (.md files tracking decisions, spawns, outcomes) are read cold by future agents. Stale or incorrect entries poison decision-making downstream. The Kobayashi incident proved this: history said "Brady decided v0.6.0" when Brady had reversed that to v0.8.17. Future spawns read the wrong truth and repeated the mistake.
## Patterns
- **Record the final outcome**, not the initial request.
- **Wait for confirmation** before writing to history — don't log intermediate states.
- **If a decision reverses**, update the entry immediately — don't leave stale data.
- **One read = one truth.** A future agent should never need to cross-reference other files to understand what actually happened.
## Examples
**Correct:**
- "Migration target: v0.8.17 (initially discussed as v0.6.0, corrected by Brady)"
- "Reverted to Node 18 per Brady's explicit request on 2024-01-15"
**Incorrect:**
- "Brady directed v0.6.0" (when later reversed)
- Recording what was *requested* instead of what *actually happened*
- Logging entries before outcome is confirmed
## Anti-Patterns
- Writing intermediate or "for now" states to disk
- Attributing decisions without confirming final direction
- Treating history like a draft — history is the source of truth
- Assuming readers will cross-reference or verify; they won't

View File

@@ -0,0 +1,105 @@
---
name: "humanizer"
description: "Tone enforcement patterns for external-facing community responses"
domain: "communication, tone, community"
confidence: "low"
source: "manual (RFC #426 — PAO External Communications)"
---
## Context
Use this skill whenever PAO drafts external-facing responses for issues or discussions.
- Tone must be warm, helpful, and human-sounding — never robotic or corporate.
- Brady's constraint applies everywhere: **Humanized tone is mandatory**.
- This applies to **all external-facing content** drafted by PAO in Phase 1 issues/discussions workflows.
## Patterns
1. **Warm opening** — Start with acknowledgment ("Thanks for reporting this", "Great question!")
2. **Active voice** — "We're looking into this" not "This is being investigated"
3. **Second person** — Address the person directly ("you" not "the user")
4. **Conversational connectors** — "That said...", "Here's what we found...", "Quick note:"
5. **Specific, not vague** — "This affects the casting module in v0.8.x" not "We are aware of issues"
6. **Empathy markers** — "I can see how that would be frustrating", "Good catch!"
7. **Action-oriented closes** — "Let us know if that helps!" not "Please advise if further assistance is required"
8. **Uncertainty is OK** — "We're not 100% sure yet, but here's what we think is happening..." is better than false confidence
9. **Profanity filter** — Never include profanity, slurs, or aggressive language, even when quoting
10. **Baseline comparison** — Responses should align with tone of 5-10 "gold standard" responses (>80% similarity threshold)
11. **Empathetic disagreement** — "We hear you. That's a fair concern." before explaining the reasoning
12. **Information request** — Ask for specific details, not open-ended "can you provide more info?"
13. **No link-dumping** — Don't just paste URLs. Provide context: "Check out the [getting started guide](url) — specifically the section on routing" not just a bare link
## Examples
### 1. Welcome
```text
Hey {author}! Welcome to Squad 👋 Thanks for opening this.
{substantive response}
Let us know if you have questions — happy to help!
```
### 2. Troubleshooting
```text
Thanks for the detailed report, {author}!
Here's what we think is happening: {explanation}
{steps or workaround}
Let us know if that helps, or if you're seeing something different.
```
### 3. Feature guidance
```text
Great question! {context on current state}
{guidance or workaround}
We've noted this as a potential improvement — {tracking info if applicable}.
```
### 4. Redirect
```text
Thanks for reaching out! This one is actually better suited for {correct location}.
{brief explanation of why}
Feel free to open it there — they'll be able to help!
```
### 5. Acknowledgment
```text
Good catch, {author}. We've confirmed this is a real issue.
{what we know so far}
We'll update this thread when we have a fix. Thanks for flagging it!
```
### 6. Closing
```text
This should be resolved in {version/PR}! 🎉
{brief summary of what changed}
Thanks for reporting this, {author} — it made Squad better.
```
### 7. Technical uncertainty
```text
Interesting find, {author}. We're not 100% sure what's causing this yet.
Here's what we've ruled out: {list}
We'd love more context if you have it — {specific ask}.
We'll dig deeper and update this thread.
```
## Anti-Patterns
- ❌ Corporate speak: "We appreciate your patience as we investigate this matter"
- ❌ Marketing hype: "Squad is the BEST way to..." or "This amazing feature..."
- ❌ Passive voice: "It has been determined that..." or "The issue is being tracked"
- ❌ Dismissive: "This works as designed" without empathy
- ❌ Over-promising: "We'll ship this next week" without commitment from the team
- ❌ Empty acknowledgment: "Thanks for your feedback" with no substance
- ❌ Robot signatures: "Best regards, PAO" or "Sincerely, The Squad Team"
- ❌ Excessive emoji: More than 1-2 emoji per response
- ❌ Quoting profanity: Even when the original issue contains it, paraphrase instead
- ❌ Link-dumping: Pasting URLs without context ("See: https://...")
- ❌ Open-ended info requests: "Can you provide more information?" without specifying what information

View File

@@ -0,0 +1,102 @@
---
name: "init-mode"
description: "Team initialization flow (Phase 1 proposal + Phase 2 creation)"
domain: "orchestration"
confidence: "high"
source: "extracted"
tools:
- name: "ask_user"
description: "Confirm team roster with selectable menu"
when: "Phase 1 proposal — requires explicit user confirmation"
---
## Context
Init Mode activates when `.squad/team.md` does not exist, or exists but has zero roster entries under `## Members`. The coordinator proposes a team (Phase 1), waits for user confirmation, then creates the team structure (Phase 2).
## Patterns
### Phase 1: Propose the Team
No team exists yet. Propose one — but **DO NOT create any files until the user confirms.**
1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey Brady, what are you building?"*). Store their name (NOT email) in `team.md` under Project Context. **Never read or store `git config user.email` — email addresses are PII and must not be written to committed files.**
2. Ask: *"What are you building? (language, stack, what it does)"*
3. **Cast the team.** Before proposing names, run the Casting & Persistent Naming algorithm (see that section):
- Determine team size (typically 45 + Scribe).
- Determine assignment shape from the user's project description.
- Derive resonance signals from the session and repo context.
- Select a universe. If the universe is custom, allocate character names from that universe based on the related list found in the `.squad/templates/casting/` directory. Prefer custom universes when available.
- Scribe is always "Scribe" — exempt from casting.
- Ralph is always "Ralph" — exempt from casting.
4. Propose the team with their cast names. Example (names will vary per cast):
```
🏗️ {CastName1} — Lead Scope, decisions, code review
⚛️ {CastName2} — Frontend Dev React, UI, components
🔧 {CastName3} — Backend Dev APIs, database, services
🧪 {CastName4} — Tester Tests, quality, edge cases
📋 Scribe — (silent) Memory, decisions, session logs
🔄 Ralph — (monitor) Work queue, backlog, keep-alive
```
5. Use the `ask_user` tool to confirm the roster. Provide choices so the user sees a selectable menu:
- **question:** *"Look right?"*
- **choices:** `["Yes, hire this team", "Add someone", "Change a role"]`
**⚠️ STOP. Your response ENDS here. Do NOT proceed to Phase 2. Do NOT create any files or directories. Wait for the user's reply.**
### Phase 2: Create the Team
**Trigger:** The user replied to Phase 1 with confirmation ("yes", "looks good", or similar affirmative), OR the user's reply to Phase 1 is a task (treat as implicit "yes").
> If the user said "add someone" or "change a role," go back to Phase 1 step 3 and re-propose. Do NOT enter Phase 2 until the user confirms.
6. Create the `.squad/` directory structure (see `.squad/templates/` for format guides or use the standard structure: team.md, routing.md, ceremonies.md, decisions.md, decisions/inbox/, casting/, agents/, orchestration-log/, skills/, log/).
**Casting state initialization:** Copy `.squad/templates/casting-policy.json` to `.squad/casting/policy.json` (or create from defaults). Create `registry.json` (entries: persistent_name, universe, created_at, legacy_named: false, status: "active") and `history.json` (first assignment snapshot with unique assignment_id).
**Seeding:** Each agent's `history.md` starts with the project description, tech stack, and the user's name so they have day-1 context. Agent folder names are the cast name in lowercase (e.g., `.squad/agents/ripley/`). The Scribe's charter includes maintaining `decisions.md` and cross-agent context sharing.
**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `sync-squad-labels.yml`) for label automation. If the header is missing or titled differently, label routing breaks.
**Merge driver for append-only files:** Create or update `.gitattributes` at the repo root to enable conflict-free merging of `.squad/` state across branches:
```
.squad/decisions.md merge=union
.squad/agents/*/history.md merge=union
.squad/log/** merge=union
.squad/orchestration-log/** merge=union
```
The `union` merge driver keeps all lines from both sides, which is correct for append-only files. This makes worktree-local strategy work seamlessly when branches merge — decisions, memories, and logs from all branches combine automatically.
7. Say: *"✅ Team hired. Try: '{FirstCastName}, set up the project structure'"*
8. **Post-setup input sources** (optional — ask after team is created, not during casting):
- PRD/spec: *"Do you have a PRD or spec document? (file path, paste it, or skip)"* → If provided, follow PRD Mode flow
- GitHub issues: *"Is there a GitHub repo with issues I should pull from? (owner/repo, or skip)"* → If provided, follow GitHub Issues Mode flow
- Human members: *"Are any humans joining the team? (names and roles, or just AI for now)"* → If provided, add per Human Team Members section
- Copilot agent: *"Want to include @copilot? It can pick up issues autonomously. (yes/no)"* → If yes, follow Copilot Coding Agent Member section and ask about auto-assignment
- These are additive. Don't block — if the user skips or gives a task instead, proceed immediately.
## Examples
**Example flow:**
1. Coordinator detects no team.md → Init Mode
2. Runs `git config user.name` → "Brady"
3. Asks: *"Hey Brady, what are you building?"*
4. User: *"TypeScript CLI tool with GitHub API integration"*
5. Coordinator runs casting algorithm → selects "The Usual Suspects" universe
6. Proposes: Keaton (Lead), Verbal (Prompt), Fenster (Backend), Hockney (Tester), Scribe, Ralph
7. Uses `ask_user` with choices → user selects "Yes, hire this team"
8. Coordinator creates `.squad/` structure, initializes casting state, seeds agents
9. Says: *"✅ Team hired. Try: 'Keaton, set up the project structure'"*
## Anti-Patterns
- ❌ Creating files before user confirms Phase 1
- ❌ Mixing agents from different universes in the same cast
- ❌ Skipping the `ask_user` tool and assuming confirmation
- ❌ Proceeding to Phase 2 when user said "add someone" or "change a role"
- ❌ Using `## Team Roster` instead of `## Members` as the header (breaks GitHub workflows)
- ❌ Forgetting to initialize `.squad/casting/` state files
- ❌ Reading or storing `git config user.email` (PII violation)

View File

@@ -0,0 +1,117 @@
# Model Selection
> Determines which LLM model to use for each agent spawn.
## SCOPE
✅ THIS SKILL PRODUCES:
- A resolved `model` parameter for every `task` tool call
- Persistent model preferences in `.squad/config.json`
- Spawn acknowledgments that include the resolved model
❌ THIS SKILL DOES NOT PRODUCE:
- Code, tests, or documentation
- Model performance benchmarks
- Cost reports or billing artifacts
## Context
Squad supports 18+ models across three tiers (premium, standard, fast). The coordinator must select the right model for each agent spawn. Users can set persistent preferences that survive across sessions.
## 5-Layer Model Resolution Hierarchy
Resolution is **first-match-wins** — the highest layer with a value wins.
| Layer | Name | Source | Persistence |
|-------|------|--------|-------------|
| **0a** | Per-Agent Config | `.squad/config.json``agentModelOverrides.{name}` | Persistent (survives sessions) |
| **0b** | Global Config | `.squad/config.json``defaultModel` | Persistent (survives sessions) |
| **1** | Session Directive | User said "use X" in current session | Session-only |
| **2** | Charter Preference | Agent's `charter.md``## Model` section | Persistent (in charter) |
| **3** | Task-Aware Auto | Code → sonnet, docs → haiku, visual → opus | Computed per-spawn |
| **4** | Default | `claude-haiku-4.5` | Hardcoded fallback |
**Key principle:** Layer 0 (persistent config) beats everything. If the user said "always use opus" and it was saved to config.json, every agent gets opus regardless of role or task type. This is intentional — the user explicitly chose quality over cost.
## AGENT WORKFLOW
### On Session Start
1. READ `.squad/config.json`
2. CHECK for `defaultModel` field — if present, this is the Layer 0 override for all spawns
3. CHECK for `agentModelOverrides` field — if present, these are per-agent Layer 0a overrides
4. STORE both values in session context for the duration
### On Every Agent Spawn
1. CHECK Layer 0a: Is there an `agentModelOverrides.{agentName}` in config.json? → Use it.
2. CHECK Layer 0b: Is there a `defaultModel` in config.json? → Use it.
3. CHECK Layer 1: Did the user give a session directive? → Use it.
4. CHECK Layer 2: Does the agent's charter have a `## Model` section? → Use it.
5. CHECK Layer 3: Determine task type:
- Code (implementation, tests, refactoring, bug fixes) → `claude-sonnet-4.6`
- Prompts, agent designs → `claude-sonnet-4.6`
- Visual/design with image analysis → `claude-opus-4.6`
- Non-code (docs, planning, triage, changelogs) → `claude-haiku-4.5`
6. FALLBACK Layer 4: `claude-haiku-4.5`
7. INCLUDE model in spawn acknowledgment: `🔧 {Name} ({resolved_model}) — {task}`
### When User Sets a Preference
**Trigger phrases:** "always use X", "use X for everything", "switch to X", "default to X"
1. VALIDATE the model ID against the catalog (18+ models)
2. WRITE `defaultModel` to `.squad/config.json` (merge, don't overwrite)
3. ACKNOWLEDGE: `✅ Model preference saved: {model} — all future sessions will use this until changed.`
**Per-agent trigger:** "use X for {agent}"
1. VALIDATE model ID
2. WRITE to `agentModelOverrides.{agent}` in `.squad/config.json`
3. ACKNOWLEDGE: `✅ {Agent} will always use {model} — saved to config.`
### When User Clears a Preference
**Trigger phrases:** "switch back to automatic", "clear model preference", "use default models"
1. REMOVE `defaultModel` from `.squad/config.json`
2. ACKNOWLEDGE: `✅ Model preference cleared — returning to automatic selection.`
### STOP
After resolving the model and including it in the spawn template, this skill is done. Do NOT:
- Generate model comparison reports
- Run benchmarks or speed tests
- Create new config files (only modify existing `.squad/config.json`)
- Change the model after spawn (fallback chains handle runtime failures)
## Config Schema
`.squad/config.json` model-related fields:
```json
{
"version": 1,
"defaultModel": "claude-opus-4.6",
"agentModelOverrides": {
"fenster": "claude-sonnet-4.6",
"mcmanus": "claude-haiku-4.5"
}
}
```
- `defaultModel` — applies to ALL agents unless overridden by `agentModelOverrides`
- `agentModelOverrides` — per-agent overrides that take priority over `defaultModel`
- Both fields are optional. When absent, Layers 1-4 apply normally.
## Fallback Chains
If a model is unavailable (rate limit, plan restriction), retry within the same tier:
```
Premium: claude-opus-4.6 → claude-opus-4.6-fast → claude-opus-4.5 → claude-sonnet-4.6
Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → claude-sonnet-4
Fast: claude-haiku-4.5 → gpt-5.1-codex-mini → gpt-4.1 → gpt-5-mini
```
**Never fall UP in tier.** A fast task won't land on a premium model via fallback.

View File

@@ -0,0 +1,24 @@
# Skill: nap
> Context hygiene — compress, prune, archive .squad/ state
## What It Does
Reclaims context window budget by compressing agent histories, pruning old logs,
archiving stale decisions, and cleaning orphaned inbox files.
## When To Use
- Before heavy fan-out work (many agents will spawn)
- When history.md files exceed 15KB
- When .squad/ total size exceeds 1MB
- After long-running sessions or sprints
## Invocation
- CLI: `squad nap` / `squad nap --deep` / `squad nap --dry-run`
- REPL: `/nap` / `/nap --dry-run` / `/nap --deep`
## Confidence
medium — Confirmed by team vote (4-1) and initial implementation

View File

@@ -0,0 +1,57 @@
# Personal Squad — Skill Document
## What is a Personal Squad?
A personal squad is a user-level collection of AI agents that travel with you across projects. Unlike project agents (defined in a project's `.squad/` directory), personal agents live in your global config directory and are automatically discovered when you start a squad session.
## Directory Structure
```
~/.config/squad/personal-squad/ # Linux/macOS
%APPDATA%/squad/personal-squad/ # Windows
├── agents/
│ ├── {agent-name}/
│ │ ├── charter.md
│ │ └── history.md
│ └── ...
└── config.json # Optional: personal squad config
```
## How It Works
1. **Ambient Discovery:** When Squad starts a session, it checks for a personal squad directory
2. **Merge:** Personal agents are merged into the session cast alongside project agents
3. **Ghost Protocol:** Personal agents can read project state but not write to it
4. **Kill Switch:** Set `SQUAD_NO_PERSONAL=1` to disable ambient discovery
## Commands
- `squad personal init` — Bootstrap a personal squad directory
- `squad personal list` — List your personal agents
- `squad personal add {name} --role {role}` — Add a personal agent
- `squad personal remove {name}` — Remove a personal agent
- `squad cast` — Show the current session cast (project + personal)
## Ghost Protocol
See `templates/ghost-protocol.md` for the full rules. Key points:
- Personal agents advise; project agents execute
- No writes to project `.squad/` state
- Transparent origin tagging in logs
- Project agents take precedence on conflicts
## Configuration
Optional `config.json` in the personal squad directory:
```json
{
"defaultModel": "auto",
"ghostProtocol": true,
"agents": {}
}
```
## Environment Variables
- `SQUAD_NO_PERSONAL` — Set to any value to disable personal squad discovery
- `SQUAD_PERSONAL_DIR` — Override the default personal squad directory path

View File

@@ -0,0 +1,56 @@
---
name: "project-conventions"
description: "Core conventions and patterns for this codebase"
domain: "project-conventions"
confidence: "medium"
source: "template"
---
## Context
> **This is a starter template.** Replace the placeholder patterns below with your actual project conventions. Skills train agents on codebase-specific practices — accurate documentation here improves agent output quality.
## Patterns
### [Pattern Name]
Describe a key convention or practice used in this codebase. Be specific about what to do and why.
### Error Handling
<!-- Example: How does your project handle errors? -->
<!-- - Use try/catch with specific error types? -->
<!-- - Log to a specific service? -->
<!-- - Return error objects vs throwing? -->
### Testing
<!-- Example: What test framework? Where do tests live? How to run them? -->
<!-- - Test framework: Jest/Vitest/node:test/etc. -->
<!-- - Test location: test/, __tests__/, *.test.ts, etc. -->
<!-- - Run command: npm test, etc. -->
### Code Style
<!-- Example: Linting, formatting, naming conventions -->
<!-- - Linter: ESLint config? -->
<!-- - Formatter: Prettier? -->
<!-- - Naming: camelCase, snake_case, etc.? -->
### File Structure
<!-- Example: How is the project organized? -->
<!-- - src/ — Source code -->
<!-- - test/ — Tests -->
<!-- - docs/ — Documentation -->
## Examples
```
// Add code examples that demonstrate your conventions
```
## Anti-Patterns
<!-- List things to avoid in this codebase -->
- **[Anti-pattern]** — Explanation of what not to do and why.

View File

@@ -0,0 +1,423 @@
---
name: "release-process"
description: "Step-by-step release checklist for Squad — prevents v0.8.22-style disasters"
domain: "release-management"
confidence: "high"
source: "team-decision"
---
## Context
This is the **definitive release runbook** for Squad. Born from the v0.8.22 release disaster (4-part semver mangled by npm, draft release never triggered publish, wrong NPM_TOKEN type, 6+ hours of broken `latest` dist-tag).
**Rule:** No agent releases Squad without following this checklist. No exceptions. No improvisation.
---
## Pre-Release Validation
Before starting ANY release work, validate the following:
### 1. Version Number Validation
**Rule:** Only 3-part semver (major.minor.patch) or prerelease (major.minor.patch-tag.N) are valid. 4-part versions (0.8.21.4) are NOT valid semver and npm will mangle them.
```bash
# Check version is valid semver
node -p "require('semver').valid('0.8.22')"
# Output: '0.8.22' = valid
# Output: null = INVALID, STOP
# For prerelease versions
node -p "require('semver').valid('0.8.23-preview.1')"
# Output: '0.8.23-preview.1' = valid
```
**If `semver.valid()` returns `null`:** STOP. Fix the version. Do NOT proceed.
### 2. NPM_TOKEN Verification
**Rule:** NPM_TOKEN must be an **Automation token** (no 2FA required). User tokens with 2FA will fail in CI with EOTP errors.
```bash
# Check token type (requires npm CLI authenticated)
npm token list
```
Look for:
-`read-write` tokens with NO 2FA requirement = Automation token (correct)
- ❌ Tokens requiring OTP = User token (WRONG, will fail in CI)
**How to create an Automation token:**
1. Go to npmjs.com → Settings → Access Tokens
2. Click "Generate New Token"
3. Select **"Automation"** (NOT "Publish")
4. Copy token and save as GitHub secret: `NPM_TOKEN`
**If using a User token:** STOP. Create an Automation token first.
### 3. Branch and Tag State
**Rule:** Release from `main` branch. Ensure clean state, no uncommitted changes, latest from origin.
```bash
# Ensure on main and clean
git checkout main
git pull origin main
git status # Should show: "nothing to commit, working tree clean"
# Check tag doesn't already exist
git tag -l "v0.8.22"
# Output should be EMPTY. If tag exists, release already done or collision.
```
**If tag exists:** STOP. Either release was already done, or there's a collision. Investigate before proceeding.
### 4. Disable bump-build.mjs
**Rule:** `bump-build.mjs` is for dev builds ONLY. It must NOT run during release builds (it increments build numbers, creating 4-part versions).
```bash
# Set env var to skip bump-build.mjs
export SKIP_BUILD_BUMP=1
# Verify it's set
echo $SKIP_BUILD_BUMP
# Output: 1
```
**For Windows PowerShell:**
```powershell
$env:SKIP_BUILD_BUMP = "1"
```
**If not set:** `bump-build.mjs` will run and mutate versions. This causes disasters (see v0.8.22).
---
## Release Workflow
### Step 1: Version Bump
Update version in all 3 package.json files (root + both workspaces) in lockstep.
```bash
# Set target version (no 'v' prefix)
VERSION="0.8.22"
# Validate it's valid semver BEFORE proceeding
node -p "require('semver').valid('$VERSION')"
# Must output the version string, NOT null
# Update all 3 package.json files
npm version $VERSION --workspaces --include-workspace-root --no-git-tag-version
# Verify all 3 match
grep '"version"' package.json packages/squad-sdk/package.json packages/squad-cli/package.json
# All 3 should show: "version": "0.8.22"
```
**Checkpoint:** All 3 package.json files have identical versions. Run `semver.valid()` one more time to be sure.
### Step 2: Commit and Tag
```bash
# Commit version bump
git add package.json packages/squad-sdk/package.json packages/squad-cli/package.json
git commit -m "chore: bump version to $VERSION
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
# Create tag (with 'v' prefix)
git tag -a "v$VERSION" -m "Release v$VERSION"
# Push commit and tag
git push origin main
git push origin "v$VERSION"
```
**Checkpoint:** Tag created and pushed. Verify with `git tag -l "v$VERSION"`.
### Step 3: Create GitHub Release
**CRITICAL:** Release must be **published**, NOT draft. Draft releases don't trigger `publish.yml` workflow.
```bash
# Create GitHub Release (NOT draft)
gh release create "v$VERSION" \
--title "v$VERSION" \
--notes "Release notes go here" \
--latest
# Verify release is PUBLISHED (not draft)
gh release view "v$VERSION"
# Output should NOT contain "(draft)"
```
**If output contains `(draft)`:** STOP. Delete the release and recreate without `--draft` flag.
```bash
# If you accidentally created a draft, fix it:
gh release edit "v$VERSION" --draft=false
```
**Checkpoint:** Release is published (NOT draft). The `release: published` event fired and triggered `publish.yml`.
### Step 4: Monitor Workflow
The `publish.yml` workflow should start automatically within 10 seconds of release creation.
```bash
# Watch workflow runs
gh run list --workflow=publish.yml --limit 1
# Get detailed status
gh run view --log
```
**Expected flow:**
1. `publish-sdk` job runs → publishes `@bradygaster/squad-sdk`
2. Verify step runs with retry loop (up to 5 attempts, 15s interval) to confirm SDK on npm registry
3. `publish-cli` job runs → publishes `@bradygaster/squad-cli`
4. Verify step runs with retry loop to confirm CLI on npm registry
**If workflow fails:** Check the logs. Common issues:
- EOTP error = wrong NPM_TOKEN type (use Automation token)
- Verify step timeout = npm propagation delay (retry loop should handle this, but propagation can take up to 2 minutes in rare cases)
- Version mismatch = package.json version doesn't match tag
**Checkpoint:** Both jobs succeeded. Workflow shows green checkmarks.
### Step 5: Verify npm Publication
Manually verify both packages are on npm with correct `latest` dist-tag.
```bash
# Check SDK
npm view @bradygaster/squad-sdk version
# Output: 0.8.22
npm dist-tag ls @bradygaster/squad-sdk
# Output should show: latest: 0.8.22
# Check CLI
npm view @bradygaster/squad-cli version
# Output: 0.8.22
npm dist-tag ls @bradygaster/squad-cli
# Output should show: latest: 0.8.22
```
**If versions don't match:** Something went wrong. Check workflow logs. DO NOT proceed with GitHub Release announcement until npm is correct.
**Checkpoint:** Both packages show correct version. `latest` dist-tags point to the new version.
### Step 6: Test Installation
Verify packages can be installed from npm (real-world smoke test).
```bash
# Create temp directory
mkdir /tmp/squad-release-test && cd /tmp/squad-release-test
# Test SDK installation
npm init -y
npm install @bradygaster/squad-sdk
node -p "require('@bradygaster/squad-sdk/package.json').version"
# Output: 0.8.22
# Test CLI installation
npm install -g @bradygaster/squad-cli
squad --version
# Output: 0.8.22
# Cleanup
cd -
rm -rf /tmp/squad-release-test
```
**If installation fails:** npm registry issue or package metadata corruption. DO NOT announce release until this works.
**Checkpoint:** Both packages install cleanly. Versions match.
### Step 7: Sync dev to Next Preview
After main release, sync dev to the next preview version.
```bash
# Checkout dev
git checkout dev
git pull origin dev
# Bump to next preview version (e.g., 0.8.23-preview.1)
NEXT_VERSION="0.8.23-preview.1"
# Validate semver
node -p "require('semver').valid('$NEXT_VERSION')"
# Must output the version string, NOT null
# Update all 3 package.json files
npm version $NEXT_VERSION --workspaces --include-workspace-root --no-git-tag-version
# Commit
git add package.json packages/squad-sdk/package.json packages/squad-cli/package.json
git commit -m "chore: bump dev to $NEXT_VERSION
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
# Push
git push origin dev
```
**Checkpoint:** dev branch now shows next preview version. Future dev builds will publish to `@preview` dist-tag.
---
## Manual Publish (Fallback)
If `publish.yml` workflow fails or needs to be bypassed, use `workflow_dispatch` to manually trigger publish.
```bash
# Trigger manual publish
gh workflow run publish.yml -f version="0.8.22"
# Monitor the run
gh run watch
```
**Rule:** Only use this if automated publish failed. Always investigate why automation failed and fix it for next release.
---
## Rollback Procedure
If a release is broken and needs to be rolled back:
### 1. Unpublish from npm (Nuclear Option)
**WARNING:** npm unpublish is time-limited (24 hours) and leaves the version slot burned. Only use if version is critically broken.
```bash
# Unpublish (requires npm owner privileges)
npm unpublish @bradygaster/squad-sdk@0.8.22
npm unpublish @bradygaster/squad-cli@0.8.22
```
### 2. Deprecate on npm (Preferred)
**Preferred approach:** Mark version as deprecated, publish a hotfix.
```bash
# Deprecate broken version
npm deprecate @bradygaster/squad-sdk@0.8.22 "Broken release, use 0.8.22.1 instead"
npm deprecate @bradygaster/squad-cli@0.8.22 "Broken release, use 0.8.22.1 instead"
# Publish hotfix version
# (Follow this runbook with version 0.8.22.1)
```
### 3. Delete GitHub Release and Tag
```bash
# Delete GitHub Release
gh release delete "v0.8.22" --yes
# Delete tag locally and remotely
git tag -d "v0.8.22"
git push origin --delete "v0.8.22"
```
### 4. Revert Commit on main
```bash
# Revert version bump commit
git checkout main
git revert HEAD
git push origin main
```
**Checkpoint:** Tag and release deleted. main branch reverted. npm packages deprecated or unpublished.
---
## Common Failure Modes
### EOTP Error (npm OTP Required)
**Symptom:** Workflow fails with `EOTP` error.
**Root cause:** NPM_TOKEN is a User token with 2FA enabled. CI can't provide OTP.
**Fix:** Replace NPM_TOKEN with an Automation token (no 2FA). See "NPM_TOKEN Verification" above.
### Verify Step 404 (npm Propagation Delay)
**Symptom:** Verify step fails with 404 even though publish succeeded.
**Root cause:** npm registry propagation delay (5-30 seconds).
**Fix:** Verify step now has retry loop (5 attempts, 15s interval). Should auto-resolve. If not, wait 2 minutes and re-run workflow.
### Version Mismatch (package.json ≠ tag)
**Symptom:** Verify step fails with "Package version (X) does not match target version (Y)".
**Root cause:** package.json version doesn't match the tag version.
**Fix:** Ensure all 3 package.json files were updated in Step 1. Re-run `npm version` if needed.
### 4-Part Version Mangled by npm
**Symptom:** Published version on npm doesn't match package.json (e.g., 0.8.21.4 became 0.8.2-1.4).
**Root cause:** 4-part versions are NOT valid semver. npm's parser misinterprets them.
**Fix:** NEVER use 4-part versions. Only 3-part (0.8.22) or prerelease (0.8.23-preview.1). Run `semver.valid()` before ANY commit.
### Draft Release Didn't Trigger Workflow
**Symptom:** Release created but `publish.yml` never ran.
**Root cause:** Release was created as a draft. Draft releases don't emit `release: published` event.
**Fix:** Edit release and change to published: `gh release edit "v$VERSION" --draft=false`. Workflow should trigger immediately.
---
## Validation Checklist
Before starting ANY release, confirm:
- [ ] Version is valid semver: `node -p "require('semver').valid('VERSION')"` returns the version string (NOT null)
- [ ] NPM_TOKEN is an Automation token (no 2FA): `npm token list` shows `read-write` without OTP requirement
- [ ] Branch is clean: `git status` shows "nothing to commit, working tree clean"
- [ ] Tag doesn't exist: `git tag -l "vVERSION"` returns empty
- [ ] `SKIP_BUILD_BUMP=1` is set: `echo $SKIP_BUILD_BUMP` returns `1`
Before creating GitHub Release:
- [ ] All 3 package.json files have matching versions: `grep '"version"' package.json packages/*/package.json`
- [ ] Commit is pushed: `git log origin/main..main` returns empty
- [ ] Tag is pushed: `git ls-remote --tags origin vVERSION` returns the tag SHA
After GitHub Release:
- [ ] Release is published (NOT draft): `gh release view "vVERSION"` output doesn't contain "(draft)"
- [ ] Workflow is running: `gh run list --workflow=publish.yml --limit 1` shows "in_progress"
After workflow completes:
- [ ] Both jobs succeeded: Workflow shows green checkmarks
- [ ] SDK on npm: `npm view @bradygaster/squad-sdk version` returns correct version
- [ ] CLI on npm: `npm view @bradygaster/squad-cli version` returns correct version
- [ ] `latest` tags correct: `npm dist-tag ls @bradygaster/squad-sdk` shows `latest: VERSION`
- [ ] Packages install: `npm install @bradygaster/squad-cli` succeeds
After dev sync:
- [ ] dev branch has next preview version: `git show dev:package.json | grep version` shows next preview
---
## Post-Mortem Reference
This skill was created after the v0.8.22 release disaster. Full retrospective: `.squad/decisions/inbox/keaton-v0822-retrospective.md`
**Key learnings:**
1. No release without a runbook = improvisation = disaster
2. Semver validation is mandatory — 4-part versions break npm
3. NPM_TOKEN type matters — User tokens with 2FA fail in CI
4. Draft releases are a footgun — they don't trigger automation
5. Retry logic is essential — npm propagation takes time
**Never again.**

View File

@@ -0,0 +1,92 @@
---
name: "reskill"
description: "Team-wide charter and history optimization through skill extraction"
domain: "team-optimization"
confidence: "high"
source: "manual — Brady directive to reduce per-agent context overhead"
---
## Context
When the coordinator hears "team, reskill" (or similar: "optimize context", "slim down charters"), trigger a team-wide optimization pass. The goal: reduce per-agent context consumption by extracting shared patterns from charters and histories into reusable skills.
This is a periodic maintenance activity. Run whenever charter/history bloat is suspected.
## Process
### Step 1: Audit
Read all agent charters and histories. Measure byte sizes. Identify:
- **Boilerplate** — sections repeated across ≥3 charters with <10% variation (collaboration, model, boundaries template)
- **Shared knowledge** — domain knowledge duplicated in 2+ charters (incident postmortems, technical patterns)
- **Mature learnings** — history entries appearing 3+ times across agents that should be promoted to skills
### Step 2: Extract
For each identified pattern:
1. Create or update a skill at `.squad/skills/{skill-name}/SKILL.md`
2. Follow the skill template format (frontmatter + Context + Patterns + Examples + Anti-Patterns)
3. Set confidence: low (first observation), medium (2+ agents), high (team-wide)
### Step 3: Trim
**Charters** — target ≤1.5KB per agent:
- Remove Collaboration section entirely (spawn prompt + agent-collaboration skill covers it)
- Remove Voice section (tagline blockquote at top of charter already captures it)
- Trim Model section to single line: `Preferred: {model}`
- Remove "When I'm unsure" boilerplate from Boundaries
- Remove domain knowledge now covered by a skill — add skill reference comment if helpful
- Keep: Identity, What I Own, unique How I Work patterns, Boundaries (domain list only)
**Histories** — target ≤8KB per agent:
- Apply history-hygiene skill to any history >12KB
- Promote recurring patterns (3+ occurrences across agents) to skills
- Summarize old entries into `## Core Context` section
- Remove session-specific metadata (dates, branch names, requester names)
### Step 4: Report
Output a savings table:
| Agent | Charter Before | Charter After | History Before | History After | Saved |
|-------|---------------|---------------|----------------|---------------|-------|
Include totals and percentage reduction.
## Patterns
### Minimal Charter Template (target format after reskill)
```
# {Name} — {Role}
> {Tagline — one sentence capturing voice and philosophy}
## Identity
- **Name:** {Name}
- **Role:** {Role}
- **Expertise:** {comma-separated list}
## What I Own
- {bullet list of owned artifacts/domains}
## How I Work
- {unique patterns and principles — NOT boilerplate}
## Boundaries
**I handle:** {domain list}
**I don't handle:** {explicit exclusions}
## Model
Preferred: {model}
```
### Skill Extraction Threshold
- **1 charter** → leave in charter (unique to that agent)
- **2 charters** → consider extracting if >500 bytes of overlap
- **3+ charters** → always extract to a shared skill
## Anti-Patterns
- Don't delete unique per-agent identity or domain-specific knowledge
- Don't create skills for content only one agent uses
- Don't merge unrelated patterns into a single mega-skill
- Don't remove Model preference line (coordinator needs it for model selection)
- Don't touch `.squad/decisions.md` during reskill
- Don't remove the tagline blockquote — it's the charter's soul in one line

View File

@@ -0,0 +1,79 @@
---
name: "reviewer-protocol"
description: "Reviewer rejection workflow and strict lockout semantics"
domain: "orchestration"
confidence: "high"
source: "extracted"
---
## Context
When a team member has a **Reviewer** role (e.g., Tester, Code Reviewer, Lead), they may approve or reject work from other agents. On rejection, the coordinator enforces strict lockout rules to ensure the original author does NOT self-revise. This prevents defensive feedback loops and ensures independent review.
## Patterns
### Reviewer Rejection Protocol
When a team member has a **Reviewer** role:
- Reviewers may **approve** or **reject** work from other agents.
- On **rejection**, the Reviewer may choose ONE of:
1. **Reassign:** Require a *different* agent to do the revision (not the original author).
2. **Escalate:** Require a *new* agent be spawned with specific expertise.
- The Coordinator MUST enforce this. If the Reviewer says "someone else should fix this," the original agent does NOT get to self-revise.
- If the Reviewer approves, work proceeds normally.
### Strict Lockout Semantics
When an artifact is **rejected** by a Reviewer:
1. **The original author is locked out.** They may NOT produce the next version of that artifact. No exceptions.
2. **A different agent MUST own the revision.** The Coordinator selects the revision author based on the Reviewer's recommendation (reassign or escalate).
3. **The Coordinator enforces this mechanically.** Before spawning a revision agent, the Coordinator MUST verify that the selected agent is NOT the original author. If the Reviewer names the original author as the fix agent, the Coordinator MUST refuse and ask the Reviewer to name a different agent.
4. **The locked-out author may NOT contribute to the revision** in any form — not as a co-author, advisor, or pair. The revision must be independently produced.
5. **Lockout scope:** The lockout applies to the specific artifact that was rejected. The original author may still work on other unrelated artifacts.
6. **Lockout duration:** The lockout persists for that revision cycle. If the revision is also rejected, the same rule applies again — the revision author is now also locked out, and a third agent must revise.
7. **Deadlock handling:** If all eligible agents have been locked out of an artifact, the Coordinator MUST escalate to the user rather than re-admitting a locked-out author.
## Examples
**Example 1: Reassign after rejection**
1. Fenster writes authentication module
2. Hockney (Tester) reviews → rejects: "Error handling is missing. Verbal should fix this."
3. Coordinator: Fenster is now locked out of this artifact
4. Coordinator spawns Verbal to revise the authentication module
5. Verbal produces v2
6. Hockney reviews v2 → approves
7. Lockout clears for next artifact
**Example 2: Escalate for expertise**
1. Edie writes TypeScript config
2. Keaton (Lead) reviews → rejects: "Need someone with deeper TS knowledge. Escalate."
3. Coordinator: Edie is now locked out
4. Coordinator spawns new agent (or existing TS expert) to revise
5. New agent produces v2
6. Keaton reviews v2
**Example 3: Deadlock handling**
1. Fenster writes module → rejected
2. Verbal revises → rejected
3. Hockney revises → rejected
4. All 3 eligible agents are now locked out
5. Coordinator: "All eligible agents have been locked out. Escalating to user: [artifact details]"
**Example 4: Reviewer accidentally names original author**
1. Fenster writes module → rejected
2. Hockney says: "Fenster should fix the error handling"
3. Coordinator: "Fenster is locked out as the original author. Please name a different agent."
4. Hockney: "Verbal, then"
5. Coordinator spawns Verbal
## Anti-Patterns
- ❌ Allowing the original author to self-revise after rejection
- ❌ Treating the locked-out author as an "advisor" or "co-author" on the revision
- ❌ Re-admitting a locked-out author when deadlock occurs (must escalate to user)
- ❌ Applying lockout across unrelated artifacts (scope is per-artifact)
- ❌ Accepting the Reviewer's assignment when they name the original author (must refuse and ask for a different agent)
- ❌ Clearing lockout before the revision is approved (lockout persists through revision cycle)
- ❌ Skipping verification that the revision agent is not the original author

View File

@@ -0,0 +1,200 @@
---
name: secret-handling
description: Never read .env files or write secrets to .squad/ committed files
domain: security, file-operations, team-collaboration
confidence: high
source: earned (issue #267 — credential leak incident)
---
## Context
Spawned agents have read access to the entire repository, including `.env` files containing live credentials. If an agent reads secrets and writes them to `.squad/` files (decisions, logs, history), Scribe auto-commits them to git, exposing them in remote history. This skill codifies absolute prohibitions and safe alternatives.
## Patterns
### Prohibited File Reads
**NEVER read these files:**
- `.env` (production secrets)
- `.env.local` (local dev secrets)
- `.env.production` (production environment)
- `.env.development` (development environment)
- `.env.staging` (staging environment)
- `.env.test` (test environment with real credentials)
- Any file matching `.env.*` UNLESS explicitly allowed (see below)
**Allowed alternatives:**
- `.env.example` (safe — contains placeholder values, no real secrets)
- `.env.sample` (safe — documentation template)
- `.env.template` (safe — schema/structure reference)
**If you need config info:**
1. **Ask the user directly** — "What's the database connection string?"
2. **Read `.env.example`** — shows structure without exposing secrets
3. **Read documentation** — check `README.md`, `docs/`, config guides
**NEVER assume you can "just peek at .env to understand the schema."** Use `.env.example` or ask.
### Prohibited Output Patterns
**NEVER write these to `.squad/` files:**
| Pattern Type | Examples | Regex Pattern (for scanning) |
|--------------|----------|-------------------------------|
| API Keys | `OPENAI_API_KEY=sk-proj-...`, `GITHUB_TOKEN=ghp_...` | `[A-Z_]+(?:KEY|TOKEN|SECRET)=[^\s]+` |
| Passwords | `DB_PASSWORD=super_secret_123`, `password: "..."` | `(?:PASSWORD|PASS|PWD)[:=]\s*["']?[^\s"']+` |
| Connection Strings | `postgres://user:pass@host:5432/db`, `Server=...;Password=...` | `(?:postgres|mysql|mongodb)://[^@]+@|(?:Server|Host)=.*(?:Password|Pwd)=` |
| JWT Tokens | `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...` | `eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+` |
| Private Keys | `-----BEGIN PRIVATE KEY-----`, `-----BEGIN RSA PRIVATE KEY-----` | `-----BEGIN [A-Z ]+PRIVATE KEY-----` |
| AWS Credentials | `AKIA...`, `aws_secret_access_key=...` | `AKIA[0-9A-Z]{16}|aws_secret_access_key=[^\s]+` |
| Email Addresses | `user@example.com` (PII violation per team decision) | `[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}` |
**What to write instead:**
- Placeholder values: `DATABASE_URL=<set in .env>`
- Redacted references: `API key configured (see .env.example)`
- Architecture notes: "App uses JWT auth — token stored in session"
- Schema documentation: "Requires OPENAI_API_KEY, GITHUB_TOKEN (see .env.example for format)"
### Scribe Pre-Commit Validation
**Before committing `.squad/` changes, Scribe MUST:**
1. **Scan all staged files** for secret patterns (use regex table above)
2. **Check for prohibited file names** (don't commit `.env` even if manually staged)
3. **If secrets detected:**
- STOP the commit (do NOT proceed)
- Remove the file from staging: `git reset HEAD <file>`
- Report to user:
```
🚨 SECRET DETECTED — commit blocked
File: .squad/decisions/inbox/river-db-config.md
Pattern: DATABASE_URL=postgres://user:password@localhost:5432/prod
This file contains credentials and MUST NOT be committed.
Please remove the secret, replace with placeholder, and try again.
```
- Exit with error (never silently skip)
4. **If no secrets detected:**
- Proceed with commit as normal
**Implementation note for Scribe:**
- Run validation AFTER staging files, BEFORE calling `git commit`
- Use PowerShell `Select-String` or `git diff --cached` to scan staged content
- Fail loud — secret leaks are unacceptable, blocking the commit is correct behavior
### Remediation — If a Secret Was Already Committed
**If you discover a secret in git history:**
1. **STOP immediately** — do not make more commits
2. **Alert the user:**
```
🚨 CREDENTIAL LEAK DETECTED
A secret was found in git history:
Commit: abc1234
File: .squad/decisions/inbox/agent-config.md
Pattern: API_KEY=sk-proj-...
This requires immediate remediation:
1. Revoke the exposed credential (regenerate API key, rotate password)
2. Remove from git history (git filter-repo or BFG)
3. Force-push the cleaned history
Do NOT proceed with new work until this is resolved.
```
3. **Do NOT attempt to fix it yourself** — secret removal requires specialized tools
4. **Wait for user confirmation** before resuming work
## Examples
### ✓ Correct: Reading Config Schema
**Agent needs to know what environment variables are required:**
```
Agent: "What environment variables does this app need?"
→ Reads `.env.example`:
OPENAI_API_KEY=sk-...
DATABASE_URL=postgres://user:pass@localhost:5432/db
REDIS_URL=redis://localhost:6379
→ Writes to .squad/decisions/inbox/river-env-setup.md:
"App requires three environment variables:
- OPENAI_API_KEY (OpenAI API key, format: sk-...)
- DATABASE_URL (Postgres connection string)
- REDIS_URL (Redis connection string)
See .env.example for full schema."
```
### ✗ Incorrect: Reading Live Credentials
**Agent needs to know database schema:**
```
Agent: (reads .env)
DATABASE_URL=postgres://admin:super_secret_pw@prod.example.com:5432/appdb
→ Writes to .squad/decisions/inbox/river-db-schema.md:
"Database connection: postgres://admin:super_secret_pw@prod.example.com:5432/appdb"
🚨 VIOLATION: Live credential written to committed file
```
**Correct approach:**
```
Agent: (reads .env.example OR asks user)
User: "It's a Postgres database, schema is in migrations/"
→ Writes to .squad/decisions/inbox/river-db-schema.md:
"Database: Postgres (connection configured in .env). Schema defined in db/migrations/."
```
### ✓ Correct: Scribe Pre-Commit Validation
**Scribe is about to commit:**
```powershell
# Stage files
git add .squad/
# Scan staged content for secrets
$stagedContent = git diff --cached
$secretPatterns = @(
'[A-Z_]+(?:KEY|TOKEN|SECRET)=[^\s]+',
'(?:PASSWORD|PASS|PWD)[:=]\s*["'']?[^\s"'']+',
'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+'
)
$detected = $false
foreach ($pattern in $secretPatterns) {
if ($stagedContent -match $pattern) {
$detected = $true
Write-Host "🚨 SECRET DETECTED: $($matches[0])"
break
}
}
if ($detected) {
# Remove from staging, report, exit
git reset HEAD .squad/
Write-Error "Commit blocked — secret detected in staged files"
exit 1
}
# Safe to commit
git commit -F $msgFile
```
## Anti-Patterns
- ❌ Reading `.env` "just to check the schema" — use `.env.example` instead
- ❌ Writing "sanitized" connection strings that still contain credentials
- ❌ Assuming "it's just a dev environment" makes secrets safe to commit
- ❌ Committing first, scanning later — validation MUST happen before commit
- ❌ Silently skipping secret detection — fail loud, never silent
- ❌ Trusting agents to "know better" — enforce at multiple layers (prompt, hook, architecture)
- ❌ Writing secrets to "temporary" files in `.squad/` — Scribe commits ALL `.squad/` changes
- ❌ Extracting "just the host" from a connection string — still leaks infrastructure topology

View File

@@ -0,0 +1,155 @@
---
name: "session-recovery"
description: "Find and resume interrupted Copilot CLI sessions using session_store queries"
domain: "workflow-recovery"
confidence: "high"
source: "earned"
tools:
- name: "sql"
description: "Query session_store database for past session history"
when: "Always — session_store is the source of truth for session history"
---
## Context
Squad agents run in Copilot CLI sessions that can be interrupted — terminal crashes, network drops, machine restarts, or accidental window closes. When this happens, in-progress work may be left in a partially-completed state: branches with uncommitted changes, issues marked in-progress with no active agent, or checkpoints that were never finalized.
Copilot CLI stores session history in a SQLite database called `session_store` (read-only, accessed via the `sql` tool with `database: "session_store"`). This skill teaches agents how to query that store to detect interrupted sessions and resume work.
## Patterns
### 1. Find Recent Sessions
Query the `sessions` table filtered by time window. Include the last checkpoint to understand where the session stopped:
```sql
SELECT
s.id,
s.summary,
s.cwd,
s.branch,
s.updated_at,
(SELECT title FROM checkpoints
WHERE session_id = s.id
ORDER BY checkpoint_number DESC LIMIT 1) AS last_checkpoint
FROM sessions s
WHERE s.updated_at >= datetime('now', '-24 hours')
ORDER BY s.updated_at DESC;
```
### 2. Filter Out Automated Sessions
Automated agents (monitors, keep-alive, heartbeat) create high-volume sessions that obscure human-initiated work. Exclude them:
```sql
SELECT s.id, s.summary, s.cwd, s.updated_at,
(SELECT title FROM checkpoints
WHERE session_id = s.id
ORDER BY checkpoint_number DESC LIMIT 1) AS last_checkpoint
FROM sessions s
WHERE s.updated_at >= datetime('now', '-24 hours')
AND s.id NOT IN (
SELECT DISTINCT t.session_id FROM turns t
WHERE t.turn_index = 0
AND (LOWER(t.user_message) LIKE '%keep-alive%'
OR LOWER(t.user_message) LIKE '%heartbeat%')
)
ORDER BY s.updated_at DESC;
```
### 3. Search by Topic (FTS5)
Use the `search_index` FTS5 table for keyword search. Expand queries with synonyms since this is keyword-based, not semantic:
```sql
SELECT DISTINCT s.id, s.summary, s.cwd, s.updated_at
FROM search_index si
JOIN sessions s ON si.session_id = s.id
WHERE search_index MATCH 'auth OR login OR token OR JWT'
AND s.updated_at >= datetime('now', '-48 hours')
ORDER BY s.updated_at DESC
LIMIT 10;
```
### 4. Search by Working Directory
```sql
SELECT s.id, s.summary, s.updated_at,
(SELECT title FROM checkpoints
WHERE session_id = s.id
ORDER BY checkpoint_number DESC LIMIT 1) AS last_checkpoint
FROM sessions s
WHERE s.cwd LIKE '%my-project%'
AND s.updated_at >= datetime('now', '-48 hours')
ORDER BY s.updated_at DESC;
```
### 5. Get Full Session Context Before Resuming
Before resuming, inspect what the session was doing:
```sql
-- Conversation turns
SELECT turn_index, substr(user_message, 1, 200) AS ask, timestamp
FROM turns WHERE session_id = 'SESSION_ID' ORDER BY turn_index;
-- Checkpoint progress
SELECT checkpoint_number, title, overview
FROM checkpoints WHERE session_id = 'SESSION_ID' ORDER BY checkpoint_number;
-- Files touched
SELECT file_path, tool_name
FROM session_files WHERE session_id = 'SESSION_ID';
-- Linked PRs/issues/commits
SELECT ref_type, ref_value
FROM session_refs WHERE session_id = 'SESSION_ID';
```
### 6. Detect Orphaned Issue Work
Find sessions that were working on issues but may not have completed:
```sql
SELECT DISTINCT s.id, s.branch, s.summary, s.updated_at,
sr.ref_type, sr.ref_value
FROM sessions s
JOIN session_refs sr ON s.id = sr.session_id
WHERE sr.ref_type = 'issue'
AND s.updated_at >= datetime('now', '-48 hours')
ORDER BY s.updated_at DESC;
```
Cross-reference with `gh issue list --label "status:in-progress"` to find issues that are marked in-progress but have no active session.
### 7. Resume a Session
Once you have the session ID:
```bash
# Resume directly
copilot --resume SESSION_ID
```
## Examples
**Recovering from a crash during PR creation:**
1. Query recent sessions filtered by branch name
2. Find the session that was working on the PR
3. Check its last checkpoint — was the code committed? Was the PR created?
4. Resume or manually complete the remaining steps
**Finding yesterday's work on a feature:**
1. Use FTS5 search with feature keywords
2. Filter to the relevant working directory
3. Review checkpoint progress to see how far the session got
4. Resume if work remains, or start fresh with the context
## Anti-Patterns
- ❌ Searching by partial session IDs — always use full UUIDs
- ❌ Resuming sessions that completed successfully — they have no pending work
- ❌ Using `MATCH` with special characters without escaping — wrap paths in double quotes
- ❌ Skipping the automated-session filter — high-volume automated sessions will flood results
- ❌ Assuming FTS5 is semantic search — it's keyword-based; always expand queries with synonyms
- ❌ Ignoring checkpoint data — checkpoints show exactly where the session stopped

View File

@@ -0,0 +1,69 @@
---
name: "squad-conventions"
description: "Core conventions and patterns used in the Squad codebase"
domain: "project-conventions"
confidence: "high"
source: "manual"
---
## Context
These conventions apply to all work on the Squad CLI tool (`create-squad`). Squad is a zero-dependency Node.js package that adds AI agent teams to any project. Understanding these patterns is essential before modifying any Squad source code.
## Patterns
### Zero Dependencies
Squad has zero runtime dependencies. Everything uses Node.js built-ins (`fs`, `path`, `os`, `child_process`). Do not add packages to `dependencies` in `package.json`. This is a hard constraint, not a preference.
### Node.js Built-in Test Runner
Tests use `node:test` and `node:assert/strict` — no test frameworks. Run with `npm test`. Test files live in `test/`. The test command is `node --test test/`.
### Error Handling — `fatal()` Pattern
All user-facing errors use the `fatal(msg)` function which prints a red `✗` prefix and exits with code 1. Never throw unhandled exceptions or print raw stack traces. The global `uncaughtException` handler calls `fatal()` as a safety net.
### ANSI Color Constants
Colors are defined as constants at the top of `index.js`: `GREEN`, `RED`, `DIM`, `BOLD`, `RESET`. Use these constants — do not inline ANSI escape codes.
### File Structure
- `.squad/` — Team state (user-owned, never overwritten by upgrades)
- `.squad/templates/` — Template files copied from `templates/` (Squad-owned, overwritten on upgrade)
- `.github/agents/squad.agent.md` — Coordinator prompt (Squad-owned, overwritten on upgrade)
- `templates/` — Source templates shipped with the npm package
- `.squad/skills/` — Team skills in SKILL.md format (user-owned)
- `.squad/decisions/inbox/` — Drop-box for parallel decision writes
### Windows Compatibility
Always use `path.join()` for file paths — never hardcode `/` or `\` separators. Squad must work on Windows, macOS, and Linux. All tests must pass on all platforms.
### Init Idempotency
The init flow uses a skip-if-exists pattern: if a file or directory already exists, skip it and report "already exists." Never overwrite user state during init. The upgrade flow overwrites only Squad-owned files.
### Copy Pattern
`copyRecursive(src, target)` handles both files and directories. It creates parent directories with `{ recursive: true }` and uses `fs.copyFileSync` for files.
## Examples
```javascript
// Error handling
function fatal(msg) {
console.error(`${RED}${RESET} ${msg}`);
process.exit(1);
}
// File path construction (Windows-safe)
const agentDest = path.join(dest, '.github', 'agents', 'squad.agent.md');
// Skip-if-exists pattern
if (!fs.existsSync(ceremoniesDest)) {
fs.copyFileSync(ceremoniesSrc, ceremoniesDest);
console.log(`${GREEN}${RESET} .squad/ceremonies.md`);
} else {
console.log(`${DIM}ceremonies.md already exists — skipping${RESET}`);
}
```
## Anti-Patterns
- **Adding npm dependencies** — Squad is zero-dep. Use Node.js built-ins only.
- **Hardcoded path separators** — Never use `/` or `\` directly. Always `path.join()`.
- **Overwriting user state on init** — Init skips existing files. Only upgrade overwrites Squad-owned files.
- **Raw stack traces** — All errors go through `fatal()`. Users see clean messages, not stack traces.
- **Inline ANSI codes** — Use the color constants (`GREEN`, `RED`, `DIM`, `BOLD`, `RESET`).

View File

@@ -0,0 +1,37 @@
---
name: "test-discipline"
description: "Update tests when changing APIs — no exceptions"
domain: "quality"
confidence: "high"
source: "earned (Fenster/Hockney incident, test assertion sync violations)"
---
## Context
When APIs or public interfaces change, tests must be updated in the same commit. When test assertions reference file counts or expected arrays, they must be kept in sync with disk reality. Stale tests block CI for other contributors.
## Patterns
- **API changes → test updates (same commit):** If you change a function signature, public interface, or exported API, update the corresponding tests before committing
- **Test assertions → disk reality:** When test files contain expected counts (e.g., `EXPECTED_FEATURES`, `EXPECTED_SCENARIOS`), they must match the actual files on disk
- **Add files → update assertions:** When adding docs pages, features, or any counted resource, update the test assertion array in the same commit
- **CI failures → check assertions first:** Before debugging complex failures, verify test assertion arrays match filesystem state
## Examples
**Correct:**
- Changed auth API signature → updated auth.test.ts in same commit
- Added `distributed-mesh.md` to features/ → added `'distributed-mesh'` to EXPECTED_FEATURES array
- Deleted two scenario files → removed entries from EXPECTED_SCENARIOS
**Incorrect:**
- Changed spawn parameters → committed without updating casting.test.ts (CI breaks for next person)
- Added `built-in-roles.md` → left EXPECTED_FEATURES at old count (PR blocked)
- Test says "expected 7 files" but disk has 25 (assertion staleness)
## Anti-Patterns
- Committing API changes without test updates ("I'll fix tests later")
- Treating test assertion arrays as static (they evolve with content)
- Assuming CI passing means coverage is correct (stale assertions can pass while being wrong)
- Leaving gaps for other agents to discover

View File

@@ -0,0 +1,74 @@
---
name: "windows-compatibility"
description: "Cross-platform path handling and command patterns"
domain: "platform"
confidence: "high"
source: "earned (multiple Windows-specific bugs: colons in filenames, git -C failures, path separators)"
---
## Context
Squad runs on Windows, macOS, and Linux. Several bugs have been traced to platform-specific assumptions: ISO timestamps with colons (illegal on Windows), `git -C` with Windows paths (unreliable), forward-slash paths in Node.js on Windows.
## Patterns
### Filenames & Timestamps
- **Never use colons in filenames:** ISO 8601 format `2026-03-15T05:30:00Z` is illegal on Windows
- **Use `safeTimestamp()` utility:** Replaces colons with hyphens → `2026-03-15T05-30-00Z`
- **Centralize formatting:** Don't inline `.toISOString().replace(/:/g, '-')` — use the utility
### Git Commands
- **Never use `git -C {path}`:** Unreliable with Windows paths (backslashes, spaces, drive letters)
- **Always `cd` first:** Change directory, then run git commands
- **Check for changes before commit:** `git diff --cached --quiet` (exit 0 = no changes)
### Commit Messages
- **Never embed newlines in `-m` flag:** Backtick-n (`\n`) fails silently in PowerShell
- **Use temp file + `-F` flag:** Write message to file, commit with `git commit -F $msgFile`
### Paths
- **Never assume CWD is repo root:** Always use `TEAM ROOT` from spawn prompt or run `git rev-parse --show-toplevel`
- **Use path.join() or path.resolve():** Don't manually concatenate with `/` or `\`
## Examples
**Correct:**
```javascript
// Timestamp utility
const safeTimestamp = () => new Date().toISOString().replace(/:/g, '-').split('.')[0] + 'Z';
// Git workflow (PowerShell)
cd $teamRoot
git add .squad/
if ($LASTEXITCODE -eq 0) {
$msg = @"
docs(ai-team): session log
Changes:
- Added decisions
"@
$msgFile = [System.IO.Path]::GetTempFileName()
Set-Content -Path $msgFile -Value $msg -Encoding utf8
git commit -F $msgFile
Remove-Item $msgFile
}
```
**Incorrect:**
```javascript
// Colon in filename
const logPath = `.squad/log/${new Date().toISOString()}.md`; // ILLEGAL on Windows
// git -C with Windows path
exec('git -C C:\\src\\squad add .squad/'); // UNRELIABLE
// Inline newlines in commit message
exec('git commit -m "First line\nSecond line"'); // FAILS silently in PowerShell
```
## Anti-Patterns
- Testing only on one platform (bugs ship to other platforms)
- Assuming Unix-style paths work everywhere
- Using `git -C` because it "looks cleaner" (it doesn't work)
- Skipping `git diff --cached --quiet` check (creates empty commits)

10
.editorconfig Normal file
View File

@@ -0,0 +1,10 @@
[*]
indent_style = space
indent_size = 4
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = true
# C# files
[*.cs]
file_header_template=Copyright © WireMock.Net

5
.gitattributes vendored Normal file
View File

@@ -0,0 +1,5 @@
# Squad: union merge for append-only team state files
.squad/decisions.md merge=union
.squad/agents/*/history.md merge=union
.squad/log/** merge=union
.squad/orchestration-log/** merge=union

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [StefH]
patreon: # Replace with a single Patreon username
open_collective: # wiremocknet
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://www.paypal.me/stefheyenrath

21
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,21 @@
---
name: Bug report
about: Create a report
title: ''
labels: bug
assignees: ''
---
### Describe the bug
A clear and concise description of what the bug is.
### Expected behavior:
A clear and concise description of what you expected to happen.
### Test to reproduce
- 1
- 2
### Other related info
Provide additional information if any.

View File

@@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest an idea or new feature
title: ''
labels: feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Is your feature request supported by [WireMock (java version)](https://www.wiremock.org)? Please provide details.**
Provide relevant information if requested feature is supported but is missing in this implementation.
**Additional context**
Add any other context or screenshots about the feature request here.

14
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,14 @@
---
name: Question
about: Ask a question
title: ''
labels: question
assignees: ''
---
Ask question related to the repo, e.g:
- How to achieve ... ?
- How does ... work?
Please be specific so we can provide the best answer possible.

1287
.github/agents/squad.agent.md vendored Normal file

File diff suppressed because it is too large Load Diff

4
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,4 @@
# Copilot Instructions
## Project Guidelines
- All new byte[xx] calls should use using var data = ArrayPool<byte>.Shared.Lease(xx); instead of directly allocating byte arrays

21
.github/workflows/CreateRelease.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: CreateRelease
permissions:
contents: write
on:
push:
tags:
- "*.*.*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true

67
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Run Tests
on:
pull_request:
branches:
- '**'
push:
branches:
- 'main'
jobs:
windows-build-and-run:
name: Run Tests on Windows
runs-on: windows-2022
env:
IsRunningOnGitHubActions: 'true'
steps:
- uses: actions/checkout@v4
- name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: 'WireMock.Net.TUnitTests'
run: dotnet test --project './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release
- name: 'WireMock.Net.Tests'
run: dotnet test --project './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0
- name: 'WireMock.Net.Tests.UsingNuGet'
run: dotnet test --project './test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj' -c Release
- name: 'WireMock.Net.Middleware.Tests'
run: dotnet test --project './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj' -c Release
linux-build-and-run:
name: Run Tests on Linux
runs-on: ubuntu-latest
env:
IsRunningOnGitHubActions: 'true'
steps:
- uses: actions/checkout@v4
- name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: 'WireMock.Net.TUnitTests'
run: dotnet test --project './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release
- name: 'WireMock.Net.Tests'
run: dotnet test --project './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0
- name: 'WireMock.Net.Tests.UsingNuGet'
run: dotnet test --project './test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj' -c Release
- name: 'WireMock.Net.Middleware.Tests'
run: dotnet test --project './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj' -c Release
- name: 'WireMock.Net.Aspire.Tests'
run: dotnet test --project './test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj' -c Release

View File

@@ -0,0 +1,36 @@
name: "Copilot Setup Steps"
# Automatically run the setup steps when they are changed to allow for easy validation, and
# allow manual testing through the repository's "Actions" tab
on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml
jobs:
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
copilot-setup-steps:
runs-on: ubuntu-latest
# Set the permissions to the lowest permissions possible needed for your steps.
# Copilot will be given its own token for its operations.
permissions:
# If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.
contents: read
# You can define any steps you want, and they will run before the agent starts.
# If you do not check out your code, Copilot will do this for you.
steps:
- name: Install .NET 10.x
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
10.x
dotnet-quality: preview
- name: dotnet --info
run: dotnet --info

171
.github/workflows/squad-heartbeat.yml vendored Normal file
View File

@@ -0,0 +1,171 @@
name: Squad Heartbeat (Ralph)
# ⚠️ SYNC: This workflow is maintained in 4 locations. Changes must be applied to all:
# - templates/workflows/squad-heartbeat.yml (source template)
# - packages/squad-cli/templates/workflows/squad-heartbeat.yml (CLI package)
# - .squad/templates/workflows/squad-heartbeat.yml (installed template)
# - .github/workflows/squad-heartbeat.yml (active workflow)
# Run 'squad upgrade' to sync installed copies from source templates.
on:
schedule:
# Every 30 minutes — adjust via cron expression as needed
- cron: '*/30 * * * *'
# React to completed work or new squad work
issues:
types: [closed, labeled]
pull_request:
types: [closed]
# Manual trigger
workflow_dispatch:
permissions:
issues: write
contents: read
pull-requests: read
jobs:
heartbeat:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check triage script
id: check-script
run: |
if [ -f ".squad/templates/ralph-triage.js" ]; then
echo "has_script=true" >> $GITHUB_OUTPUT
else
echo "has_script=false" >> $GITHUB_OUTPUT
echo "⚠️ ralph-triage.js not found — run 'squad upgrade' to install"
fi
- name: Ralph — Smart triage
if: steps.check-script.outputs.has_script == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
node .squad/templates/ralph-triage.js \
--squad-dir .squad \
--output triage-results.json
- name: Ralph — Apply triage decisions
if: steps.check-script.outputs.has_script == 'true' && hashFiles('triage-results.json') != ''
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = 'triage-results.json';
if (!fs.existsSync(path)) {
core.info('No triage results — board is clear');
return;
}
const results = JSON.parse(fs.readFileSync(path, 'utf8'));
if (results.length === 0) {
core.info('📋 Board is clear — Ralph found no untriaged issues');
return;
}
for (const decision of results) {
try {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: decision.issueNumber,
labels: [decision.label]
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: decision.issueNumber,
body: [
'### 🔄 Ralph — Auto-Triage',
'',
`**Assigned to:** ${decision.assignTo}`,
`**Reason:** ${decision.reason}`,
`**Source:** ${decision.source}`,
'',
'> Ralph auto-triaged this issue using routing rules.',
'> To reassign, swap the `squad:*` label.'
].join('\n')
});
core.info(`Triaged #${decision.issueNumber} → ${decision.assignTo} (${decision.source})`);
} catch (e) {
core.warning(`Failed to triage #${decision.issueNumber}: ${e.message}`);
}
}
core.info(`🔄 Ralph triaged ${results.length} issue(s)`);
# Copilot auto-assign step (uses PAT if available)
- name: Ralph — Assign @copilot issues
if: success()
uses: actions/github-script@v7
with:
github-token: ${{ secrets.COPILOT_ASSIGN_TOKEN || secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
let teamFile = '.squad/team.md';
if (!fs.existsSync(teamFile)) {
teamFile = '.ai-team/team.md';
}
if (!fs.existsSync(teamFile)) return;
const content = fs.readFileSync(teamFile, 'utf8');
// Check if @copilot is on the team with auto-assign
const hasCopilot = content.includes('🤖 Coding Agent') || content.includes('@copilot');
const autoAssign = content.includes('<!-- copilot-auto-assign: true -->');
if (!hasCopilot || !autoAssign) return;
// Find issues labeled squad:copilot with no assignee
try {
const { data: copilotIssues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
labels: 'squad:copilot',
state: 'open',
per_page: 5
});
const unassigned = copilotIssues.filter(i =>
!i.assignees || i.assignees.length === 0
);
if (unassigned.length === 0) {
core.info('No unassigned squad:copilot issues');
return;
}
// Get repo default branch
const { data: repoData } = await github.rest.repos.get({
owner: context.repo.owner,
repo: context.repo.repo
});
for (const issue of unassigned) {
try {
await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/assignees', {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
assignees: ['copilot-swe-agent[bot]'],
agent_assignment: {
target_repo: `${context.repo.owner}/${context.repo.repo}`,
base_branch: repoData.default_branch,
custom_instructions: `Read .squad/team.md (or .ai-team/team.md) for team context and .squad/routing.md (or .ai-team/routing.md) for routing rules.`
}
});
core.info(`Assigned copilot-swe-agent[bot] to #${issue.number}`);
} catch (e) {
core.warning(`Failed to assign @copilot to #${issue.number}: ${e.message}`);
}
}
} catch (e) {
core.info(`No squad:copilot label found or error: ${e.message}`);
}

161
.github/workflows/squad-issue-assign.yml vendored Normal file
View File

@@ -0,0 +1,161 @@
name: Squad Issue Assign
on:
issues:
types: [labeled]
permissions:
issues: write
contents: read
jobs:
assign-work:
# Only trigger on squad:{member} labels (not the base "squad" label)
if: startsWith(github.event.label.name, 'squad:')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Identify assigned member and trigger work
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const issue = context.payload.issue;
const label = context.payload.label.name;
// Extract member name from label (e.g., "squad:ripley" → "ripley")
const memberName = label.replace('squad:', '').toLowerCase();
// Read team roster — check .squad/ first, fall back to .ai-team/
let teamFile = '.squad/team.md';
if (!fs.existsSync(teamFile)) {
teamFile = '.ai-team/team.md';
}
if (!fs.existsSync(teamFile)) {
core.warning('No .squad/team.md or .ai-team/team.md found — cannot assign work');
return;
}
const content = fs.readFileSync(teamFile, 'utf8');
const lines = content.split('\n');
// Check if this is a coding agent assignment
const isCopilotAssignment = memberName === 'copilot';
let assignedMember = null;
if (isCopilotAssignment) {
assignedMember = { name: '@copilot', role: 'Coding Agent' };
} else {
let inMembersTable = false;
for (const line of lines) {
if (line.match(/^##\s+(Members|Team Roster)/i)) {
inMembersTable = true;
continue;
}
if (inMembersTable && line.startsWith('## ')) {
break;
}
if (inMembersTable && line.startsWith('|') && !line.includes('---') && !line.includes('Name')) {
const cells = line.split('|').map(c => c.trim()).filter(Boolean);
if (cells.length >= 2 && cells[0].toLowerCase() === memberName) {
assignedMember = { name: cells[0], role: cells[1] };
break;
}
}
}
}
if (!assignedMember) {
core.warning(`No member found matching label "${label}"`);
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `⚠️ No squad member found matching label \`${label}\`. Check \`.squad/team.md\` (or \`.ai-team/team.md\`) for valid member names.`
});
return;
}
// Post assignment acknowledgment
let comment;
if (isCopilotAssignment) {
comment = [
`### 🤖 Routed to @copilot (Coding Agent)`,
'',
`**Issue:** #${issue.number} — ${issue.title}`,
'',
`@copilot has been assigned and will pick this up automatically.`,
'',
`> The coding agent will create a \`copilot/*\` branch and open a draft PR.`,
`> Review the PR as you would any team member's work.`,
].join('\n');
} else {
comment = [
`### 📋 Assigned to ${assignedMember.name} (${assignedMember.role})`,
'',
`**Issue:** #${issue.number} — ${issue.title}`,
'',
`${assignedMember.name} will pick this up in the next Copilot session.`,
'',
`> **For Copilot coding agent:** If enabled, this issue will be worked automatically.`,
`> Otherwise, start a Copilot session and say:`,
`> \`${assignedMember.name}, work on issue #${issue.number}\``,
].join('\n');
}
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: comment
});
core.info(`Issue #${issue.number} assigned to ${assignedMember.name} (${assignedMember.role})`);
# Separate step: assign @copilot using PAT (required for coding agent)
- name: Assign @copilot coding agent
if: github.event.label.name == 'squad:copilot'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.COPILOT_ASSIGN_TOKEN }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const issue_number = context.payload.issue.number;
// Get the default branch name (main, master, etc.)
const { data: repoData } = await github.rest.repos.get({ owner, repo });
const baseBranch = repoData.default_branch;
try {
await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/assignees', {
owner,
repo,
issue_number,
assignees: ['copilot-swe-agent[bot]'],
agent_assignment: {
target_repo: `${owner}/${repo}`,
base_branch: baseBranch,
custom_instructions: '',
custom_agent: '',
model: ''
},
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
core.info(`Assigned copilot-swe-agent to issue #${issue_number} (base: ${baseBranch})`);
} catch (err) {
core.warning(`Assignment with agent_assignment failed: ${err.message}`);
// Fallback: try without agent_assignment
try {
await github.rest.issues.addAssignees({
owner, repo, issue_number,
assignees: ['copilot-swe-agent']
});
core.info(`Fallback assigned copilot-swe-agent to issue #${issue_number}`);
} catch (err2) {
core.warning(`Fallback also failed: ${err2.message}`);
}
}

260
.github/workflows/squad-triage.yml vendored Normal file
View File

@@ -0,0 +1,260 @@
name: Squad Triage
on:
issues:
types: [labeled]
permissions:
issues: write
contents: read
jobs:
triage:
if: github.event.label.name == 'squad'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Triage issue via Lead agent
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const issue = context.payload.issue;
// Read team roster — check .squad/ first, fall back to .ai-team/
let teamFile = '.squad/team.md';
if (!fs.existsSync(teamFile)) {
teamFile = '.ai-team/team.md';
}
if (!fs.existsSync(teamFile)) {
core.warning('No .squad/team.md or .ai-team/team.md found — cannot triage');
return;
}
const content = fs.readFileSync(teamFile, 'utf8');
const lines = content.split('\n');
// Check if @copilot is on the team
const hasCopilot = content.includes('🤖 Coding Agent');
const copilotAutoAssign = content.includes('<!-- copilot-auto-assign: true -->');
// Parse @copilot capability profile
let goodFitKeywords = [];
let needsReviewKeywords = [];
let notSuitableKeywords = [];
if (hasCopilot) {
// Extract capability tiers from team.md
const goodFitMatch = content.match(/🟢\s*Good fit[^:]*:\s*(.+)/i);
const needsReviewMatch = content.match(/🟡\s*Needs review[^:]*:\s*(.+)/i);
const notSuitableMatch = content.match(/🔴\s*Not suitable[^:]*:\s*(.+)/i);
if (goodFitMatch) {
goodFitKeywords = goodFitMatch[1].toLowerCase().split(',').map(s => s.trim());
} else {
goodFitKeywords = ['bug fix', 'test coverage', 'lint', 'format', 'dependency update', 'small feature', 'scaffolding', 'doc fix', 'documentation'];
}
if (needsReviewMatch) {
needsReviewKeywords = needsReviewMatch[1].toLowerCase().split(',').map(s => s.trim());
} else {
needsReviewKeywords = ['medium feature', 'refactoring', 'api endpoint', 'migration'];
}
if (notSuitableMatch) {
notSuitableKeywords = notSuitableMatch[1].toLowerCase().split(',').map(s => s.trim());
} else {
notSuitableKeywords = ['architecture', 'system design', 'security', 'auth', 'encryption', 'performance'];
}
}
const members = [];
let inMembersTable = false;
for (const line of lines) {
if (line.match(/^##\s+(Members|Team Roster)/i)) {
inMembersTable = true;
continue;
}
if (inMembersTable && line.startsWith('## ')) {
break;
}
if (inMembersTable && line.startsWith('|') && !line.includes('---') && !line.includes('Name')) {
const cells = line.split('|').map(c => c.trim()).filter(Boolean);
if (cells.length >= 2 && cells[0] !== 'Scribe') {
members.push({
name: cells[0],
role: cells[1]
});
}
}
}
// Read routing rules — check .squad/ first, fall back to .ai-team/
let routingFile = '.squad/routing.md';
if (!fs.existsSync(routingFile)) {
routingFile = '.ai-team/routing.md';
}
let routingContent = '';
if (fs.existsSync(routingFile)) {
routingContent = fs.readFileSync(routingFile, 'utf8');
}
// Find the Lead
const lead = members.find(m =>
m.role.toLowerCase().includes('lead') ||
m.role.toLowerCase().includes('architect') ||
m.role.toLowerCase().includes('coordinator')
);
if (!lead) {
core.warning('No Lead role found in team roster — cannot triage');
return;
}
// Build triage context
const memberList = members.map(m =>
`- **${m.name}** (${m.role}) → label: \`squad:${m.name.toLowerCase()}\``
).join('\n');
// Determine best assignee based on issue content and routing
const issueText = `${issue.title}\n${issue.body || ''}`.toLowerCase();
let assignedMember = null;
let triageReason = '';
let copilotTier = null;
// First, evaluate @copilot fit if enabled
if (hasCopilot) {
const isNotSuitable = notSuitableKeywords.some(kw => issueText.includes(kw));
const isGoodFit = !isNotSuitable && goodFitKeywords.some(kw => issueText.includes(kw));
const isNeedsReview = !isNotSuitable && !isGoodFit && needsReviewKeywords.some(kw => issueText.includes(kw));
if (isGoodFit) {
copilotTier = 'good-fit';
assignedMember = { name: '@copilot', role: 'Coding Agent' };
triageReason = '🟢 Good fit for @copilot — matches capability profile';
} else if (isNeedsReview) {
copilotTier = 'needs-review';
assignedMember = { name: '@copilot', role: 'Coding Agent' };
triageReason = '🟡 Routing to @copilot (needs review) — a squad member should review the PR';
} else if (isNotSuitable) {
copilotTier = 'not-suitable';
// Fall through to normal routing
}
}
// If not routed to @copilot, use keyword-based routing
if (!assignedMember) {
for (const member of members) {
const role = member.role.toLowerCase();
if ((role.includes('frontend') || role.includes('ui')) &&
(issueText.includes('ui') || issueText.includes('frontend') ||
issueText.includes('css') || issueText.includes('component') ||
issueText.includes('button') || issueText.includes('page') ||
issueText.includes('layout') || issueText.includes('design'))) {
assignedMember = member;
triageReason = 'Issue relates to frontend/UI work';
break;
}
if ((role.includes('backend') || role.includes('api') || role.includes('server')) &&
(issueText.includes('api') || issueText.includes('backend') ||
issueText.includes('database') || issueText.includes('endpoint') ||
issueText.includes('server') || issueText.includes('auth'))) {
assignedMember = member;
triageReason = 'Issue relates to backend/API work';
break;
}
if ((role.includes('test') || role.includes('qa') || role.includes('quality')) &&
(issueText.includes('test') || issueText.includes('bug') ||
issueText.includes('fix') || issueText.includes('regression') ||
issueText.includes('coverage'))) {
assignedMember = member;
triageReason = 'Issue relates to testing/quality work';
break;
}
if ((role.includes('devops') || role.includes('infra') || role.includes('ops')) &&
(issueText.includes('deploy') || issueText.includes('ci') ||
issueText.includes('pipeline') || issueText.includes('docker') ||
issueText.includes('infrastructure'))) {
assignedMember = member;
triageReason = 'Issue relates to DevOps/infrastructure work';
break;
}
}
}
// Default to Lead if no routing match
if (!assignedMember) {
assignedMember = lead;
triageReason = 'No specific domain match — assigned to Lead for further analysis';
}
const isCopilot = assignedMember.name === '@copilot';
const assignLabel = isCopilot ? 'squad:copilot' : `squad:${assignedMember.name.toLowerCase()}`;
// Add the member-specific label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: [assignLabel]
});
// Apply default triage verdict
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['go:needs-research']
});
// Auto-assign @copilot if enabled
if (isCopilot && copilotAutoAssign) {
try {
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
assignees: ['copilot']
});
} catch (err) {
core.warning(`Could not auto-assign @copilot: ${err.message}`);
}
}
// Build copilot evaluation note
let copilotNote = '';
if (hasCopilot && !isCopilot) {
if (copilotTier === 'not-suitable') {
copilotNote = `\n\n**@copilot evaluation:** 🔴 Not suitable — issue involves work outside the coding agent's capability profile.`;
} else {
copilotNote = `\n\n**@copilot evaluation:** No strong capability match — routed to squad member.`;
}
}
// Post triage comment
const comment = [
`### 🏗️ Squad Triage — ${lead.name} (${lead.role})`,
'',
`**Issue:** #${issue.number} — ${issue.title}`,
`**Assigned to:** ${assignedMember.name} (${assignedMember.role})`,
`**Reason:** ${triageReason}`,
copilotTier === 'needs-review' ? `\n⚠ **PR review recommended** — a squad member should review @copilot's work on this one.` : '',
copilotNote,
'',
`---`,
'',
`**Team roster:**`,
memberList,
hasCopilot ? `- **@copilot** (Coding Agent) → label: \`squad:copilot\`` : '',
'',
`> To reassign, remove the current \`squad:*\` label and add the correct one.`,
].filter(Boolean).join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: comment
});
core.info(`Triaged issue #${issue.number} → ${assignedMember.name} (${assignLabel})`);

169
.github/workflows/sync-squad-labels.yml vendored Normal file
View File

@@ -0,0 +1,169 @@
name: Sync Squad Labels
on:
push:
paths:
- '.squad/team.md'
- '.ai-team/team.md'
workflow_dispatch:
permissions:
issues: write
contents: read
jobs:
sync-labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Parse roster and sync labels
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
let teamFile = '.squad/team.md';
if (!fs.existsSync(teamFile)) {
teamFile = '.ai-team/team.md';
}
if (!fs.existsSync(teamFile)) {
core.info('No .squad/team.md or .ai-team/team.md found — skipping label sync');
return;
}
const content = fs.readFileSync(teamFile, 'utf8');
const lines = content.split('\n');
// Parse the Members table for agent names
const members = [];
let inMembersTable = false;
for (const line of lines) {
if (line.match(/^##\s+(Members|Team Roster)/i)) {
inMembersTable = true;
continue;
}
if (inMembersTable && line.startsWith('## ')) {
break;
}
if (inMembersTable && line.startsWith('|') && !line.includes('---') && !line.includes('Name')) {
const cells = line.split('|').map(c => c.trim()).filter(Boolean);
if (cells.length >= 2 && cells[0] !== 'Scribe') {
members.push({
name: cells[0],
role: cells[1]
});
}
}
}
core.info(`Found ${members.length} squad members: ${members.map(m => m.name).join(', ')}`);
// Check if @copilot is on the team
const hasCopilot = content.includes('🤖 Coding Agent');
// Define label color palette for squad labels
const SQUAD_COLOR = '9B8FCC';
const MEMBER_COLOR = '9B8FCC';
const COPILOT_COLOR = '10b981';
// Define go: and release: labels (static)
const GO_LABELS = [
{ name: 'go:yes', color: '0E8A16', description: 'Ready to implement' },
{ name: 'go:no', color: 'B60205', description: 'Not pursuing' },
{ name: 'go:needs-research', color: 'FBCA04', description: 'Needs investigation' }
];
const RELEASE_LABELS = [
{ name: 'release:v0.4.0', color: '6B8EB5', description: 'Targeted for v0.4.0' },
{ name: 'release:v0.5.0', color: '6B8EB5', description: 'Targeted for v0.5.0' },
{ name: 'release:v0.6.0', color: '8B7DB5', description: 'Targeted for v0.6.0' },
{ name: 'release:v1.0.0', color: '8B7DB5', description: 'Targeted for v1.0.0' },
{ name: 'release:backlog', color: 'D4E5F7', description: 'Not yet targeted' }
];
const TYPE_LABELS = [
{ name: 'type:feature', color: 'DDD1F2', description: 'New capability' },
{ name: 'type:bug', color: 'FF0422', description: 'Something broken' },
{ name: 'type:spike', color: 'F2DDD4', description: 'Research/investigation — produces a plan, not code' },
{ name: 'type:docs', color: 'D4E5F7', description: 'Documentation work' },
{ name: 'type:chore', color: 'D4E5F7', description: 'Maintenance, refactoring, cleanup' },
{ name: 'type:epic', color: 'CC4455', description: 'Parent issue that decomposes into sub-issues' }
];
// High-signal labels — these MUST visually dominate all others
const SIGNAL_LABELS = [
{ name: 'bug', color: 'FF0422', description: 'Something isn\'t working' },
{ name: 'feedback', color: '00E5FF', description: 'User feedback — high signal, needs attention' }
];
const PRIORITY_LABELS = [
{ name: 'priority:p0', color: 'B60205', description: 'Blocking release' },
{ name: 'priority:p1', color: 'D93F0B', description: 'This sprint' },
{ name: 'priority:p2', color: 'FBCA04', description: 'Next sprint' }
];
// Ensure the base "squad" triage label exists
const labels = [
{ name: 'squad', color: SQUAD_COLOR, description: 'Squad triage inbox — Lead will assign to a member' }
];
for (const member of members) {
labels.push({
name: `squad:${member.name.toLowerCase()}`,
color: MEMBER_COLOR,
description: `Assigned to ${member.name} (${member.role})`
});
}
// Add @copilot label if coding agent is on the team
if (hasCopilot) {
labels.push({
name: 'squad:copilot',
color: COPILOT_COLOR,
description: 'Assigned to @copilot (Coding Agent) for autonomous work'
});
}
// Add go:, release:, type:, priority:, and high-signal labels
labels.push(...GO_LABELS);
labels.push(...RELEASE_LABELS);
labels.push(...TYPE_LABELS);
labels.push(...PRIORITY_LABELS);
labels.push(...SIGNAL_LABELS);
// Sync labels (create or update)
for (const label of labels) {
try {
await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name
});
// Label exists — update it
await github.rest.issues.updateLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
color: label.color,
description: label.description
});
core.info(`Updated label: ${label.name}`);
} catch (err) {
if (err.status === 404) {
// Label doesn't exist — create it
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
color: label.color,
description: label.description
});
core.info(`Created label: ${label.name}`);
} else {
throw err;
}
}
}
core.info(`Label sync complete: ${labels.length} labels synced`);

12
.gitignore vendored
View File

@@ -252,3 +252,15 @@ paket-files/
*.sln.iml
./report/coverlet/
/test/WireMock.Net.Tests/coverage.opencover.xml
/test/WireMock.Net.Tests/coverage.netcoreapp3.1.opencover.xml
/test/WireMock.Net.Tests/coverage.net8.0.opencover.xml
*.received.*
# Squad: ignore runtime state (logs, inbox, sessions)
.squad/orchestration-log/
.squad/log/
.squad/decisions/inbox/
.squad/sessions/
# Squad: SubSquad activation file (local to this machine)
.squad-workstream

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Code Coverage"
uri="datacollector://Microsoft/CodeCoverage/2.0"
assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Configuration>
<CodeCoverage>
<ModulePaths>
<Include>
<ModulePath>.*\.dll$</ModulePath>
</Include>
<Exclude>
<ModulePath>.*Validation.*</ModulePath>
<ModulePath>.*\.tests.dll$</ModulePath>
<ModulePath>.*simmetrics.*</ModulePath>
</Exclude>
</ModulePaths>
<UseVerifiableInstrumentation>True</UseVerifiableInstrumentation>
<AllowLowIntegrityProcesses>True</AllowLowIntegrityProcesses>
<CollectFromChildProcesses>True</CollectFromChildProcesses>
<CollectAspDotNet>False</CollectAspDotNet>
</CodeCoverage>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>

View File

@@ -1,14 +0,0 @@
{
"ServerUri": "https://sonarcloud.io",
"Organization": {
"Key": "stefh-github",
"Name": "Stef Heyenrath (Organization)"
},
"ProjectKey": "wiremock",
"Profiles": {
"CSharp": {
"ProfileKey": "AWRupN8iowGp6EI3lYmF",
"ProfileTimestamp": "2018-09-06T11:06:47+02:00"
}
}
}

View File

@@ -1,362 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="SonarQube - wiremock Sonar way" Description="This rule set was automatically generated from SonarQube.&#xD;&#xA;https://sonarcloud.io/profiles/show?key=AWRupN8iowGp6EI3lYmF" ToolsVersion="15.0">
<Rules AnalyzerId="SonarAnalyzer.CSharp" RuleNamespace="SonarAnalyzer.CSharp">
<Rule Id="S100" Action="None" />
<Rule Id="S1006" Action="Warning" />
<Rule Id="S101" Action="Warning" />
<Rule Id="S103" Action="None" />
<Rule Id="S104" Action="None" />
<Rule Id="S1048" Action="Warning" />
<Rule Id="S105" Action="None" />
<Rule Id="S106" Action="None" />
<Rule Id="S1066" Action="Warning" />
<Rule Id="S1067" Action="None" />
<Rule Id="S107" Action="Warning" />
<Rule Id="S1075" Action="Warning" />
<Rule Id="S108" Action="Warning" />
<Rule Id="S109" Action="None" />
<Rule Id="S110" Action="Warning" />
<Rule Id="S1104" Action="Warning" />
<Rule Id="S1109" Action="None" />
<Rule Id="S1110" Action="Warning" />
<Rule Id="S1116" Action="Warning" />
<Rule Id="S1117" Action="Warning" />
<Rule Id="S1118" Action="Warning" />
<Rule Id="S112" Action="Warning" />
<Rule Id="S1121" Action="Warning" />
<Rule Id="S1123" Action="Warning" />
<Rule Id="S1125" Action="Warning" />
<Rule Id="S113" Action="None" />
<Rule Id="S1134" Action="Warning" />
<Rule Id="S1135" Action="Warning" />
<Rule Id="S1144" Action="Warning" />
<Rule Id="S1145" Action="None" />
<Rule Id="S1147" Action="None" />
<Rule Id="S1151" Action="None" />
<Rule Id="S1155" Action="Warning" />
<Rule Id="S1163" Action="Warning" />
<Rule Id="S1168" Action="Warning" />
<Rule Id="S1172" Action="Warning" />
<Rule Id="S1185" Action="Warning" />
<Rule Id="S1186" Action="Warning" />
<Rule Id="S1192" Action="None" />
<Rule Id="S1200" Action="None" />
<Rule Id="S1206" Action="Warning" />
<Rule Id="S121" Action="None" />
<Rule Id="S1210" Action="Warning" />
<Rule Id="S1215" Action="Warning" />
<Rule Id="S122" Action="None" />
<Rule Id="S1226" Action="None" />
<Rule Id="S1227" Action="None" />
<Rule Id="S1244" Action="None" />
<Rule Id="S125" Action="Warning" />
<Rule Id="S126" Action="None" />
<Rule Id="S1264" Action="Warning" />
<Rule Id="S127" Action="None" />
<Rule Id="S1301" Action="None" />
<Rule Id="S1309" Action="None" />
<Rule Id="S131" Action="None" />
<Rule Id="S1313" Action="None" />
<Rule Id="S134" Action="None" />
<Rule Id="S138" Action="None" />
<Rule Id="S1449" Action="None" />
<Rule Id="S1450" Action="Warning" />
<Rule Id="S1451" Action="None" />
<Rule Id="S1479" Action="Warning" />
<Rule Id="S1481" Action="Warning" />
<Rule Id="S1541" Action="None" />
<Rule Id="S1607" Action="Warning" />
<Rule Id="S1643" Action="Warning" />
<Rule Id="S1656" Action="Warning" />
<Rule Id="S1659" Action="None" />
<Rule Id="S1694" Action="None" />
<Rule Id="S1696" Action="None" />
<Rule Id="S1697" Action="None" />
<Rule Id="S1698" Action="None" />
<Rule Id="S1699" Action="Warning" />
<Rule Id="S1751" Action="Warning" />
<Rule Id="S1764" Action="Warning" />
<Rule Id="S1821" Action="None" />
<Rule Id="S1848" Action="Warning" />
<Rule Id="S1854" Action="Warning" />
<Rule Id="S1858" Action="None" />
<Rule Id="S1862" Action="Warning" />
<Rule Id="S1871" Action="Warning" />
<Rule Id="S1905" Action="Warning" />
<Rule Id="S1939" Action="Warning" />
<Rule Id="S1940" Action="Warning" />
<Rule Id="S1944" Action="Warning" />
<Rule Id="S1994" Action="None" />
<Rule Id="S2068" Action="Warning" />
<Rule Id="S2070" Action="None" />
<Rule Id="S2092" Action="None" />
<Rule Id="S2114" Action="Warning" />
<Rule Id="S2123" Action="Warning" />
<Rule Id="S2148" Action="None" />
<Rule Id="S2156" Action="None" />
<Rule Id="S2178" Action="Warning" />
<Rule Id="S2183" Action="Warning" />
<Rule Id="S2184" Action="Warning" />
<Rule Id="S2187" Action="Warning" />
<Rule Id="S2190" Action="Warning" />
<Rule Id="S2197" Action="None" />
<Rule Id="S2201" Action="Warning" />
<Rule Id="S2219" Action="Warning" />
<Rule Id="S2221" Action="None" />
<Rule Id="S2223" Action="Warning" />
<Rule Id="S2225" Action="Warning" />
<Rule Id="S2228" Action="None" />
<Rule Id="S2234" Action="Warning" />
<Rule Id="S2245" Action="Warning" />
<Rule Id="S2255" Action="Warning" />
<Rule Id="S2259" Action="Warning" />
<Rule Id="S2275" Action="Warning" />
<Rule Id="S2278" Action="Warning" />
<Rule Id="S2290" Action="Warning" />
<Rule Id="S2291" Action="Warning" />
<Rule Id="S2292" Action="Warning" />
<Rule Id="S2302" Action="None" />
<Rule Id="S2306" Action="Warning" />
<Rule Id="S2325" Action="None" />
<Rule Id="S2326" Action="Warning" />
<Rule Id="S2327" Action="Warning" />
<Rule Id="S2328" Action="Warning" />
<Rule Id="S2330" Action="None" />
<Rule Id="S2333" Action="None" />
<Rule Id="S2339" Action="None" />
<Rule Id="S2342" Action="Warning" />
<Rule Id="S2344" Action="Warning" />
<Rule Id="S2345" Action="Warning" />
<Rule Id="S2346" Action="Warning" />
<Rule Id="S2357" Action="None" />
<Rule Id="S2360" Action="None" />
<Rule Id="S2365" Action="Warning" />
<Rule Id="S2368" Action="Warning" />
<Rule Id="S2372" Action="Warning" />
<Rule Id="S2376" Action="Warning" />
<Rule Id="S2386" Action="Warning" />
<Rule Id="S2387" Action="None" />
<Rule Id="S2436" Action="Warning" />
<Rule Id="S2437" Action="Warning" />
<Rule Id="S2486" Action="Warning" />
<Rule Id="S2551" Action="Warning" />
<Rule Id="S2583" Action="Warning" />
<Rule Id="S2589" Action="Warning" />
<Rule Id="S2674" Action="None" />
<Rule Id="S2681" Action="Warning" />
<Rule Id="S2688" Action="Warning" />
<Rule Id="S2692" Action="Warning" />
<Rule Id="S2696" Action="Warning" />
<Rule Id="S2699" Action="None" />
<Rule Id="S2701" Action="None" />
<Rule Id="S2737" Action="Warning" />
<Rule Id="S2743" Action="Warning" />
<Rule Id="S2757" Action="Warning" />
<Rule Id="S2758" Action="Warning" />
<Rule Id="S2760" Action="None" />
<Rule Id="S2761" Action="Warning" />
<Rule Id="S2930" Action="Warning" />
<Rule Id="S2931" Action="None" />
<Rule Id="S2933" Action="Warning" />
<Rule Id="S2934" Action="Warning" />
<Rule Id="S2952" Action="None" />
<Rule Id="S2953" Action="Warning" />
<Rule Id="S2955" Action="None" />
<Rule Id="S2971" Action="Warning" />
<Rule Id="S2995" Action="Warning" />
<Rule Id="S2996" Action="Warning" />
<Rule Id="S2997" Action="Warning" />
<Rule Id="S3005" Action="Warning" />
<Rule Id="S3010" Action="Warning" />
<Rule Id="S3052" Action="None" />
<Rule Id="S3060" Action="Warning" />
<Rule Id="S3168" Action="Warning" />
<Rule Id="S3169" Action="Warning" />
<Rule Id="S3172" Action="Warning" />
<Rule Id="S3215" Action="None" />
<Rule Id="S3216" Action="None" />
<Rule Id="S3217" Action="Warning" />
<Rule Id="S3218" Action="Warning" />
<Rule Id="S3220" Action="Warning" />
<Rule Id="S3234" Action="None" />
<Rule Id="S3235" Action="None" />
<Rule Id="S3236" Action="Warning" />
<Rule Id="S3237" Action="Warning" />
<Rule Id="S3240" Action="None" />
<Rule Id="S3241" Action="Warning" />
<Rule Id="S3242" Action="None" />
<Rule Id="S3244" Action="Warning" />
<Rule Id="S3246" Action="Warning" />
<Rule Id="S3247" Action="Warning" />
<Rule Id="S3249" Action="Warning" />
<Rule Id="S3251" Action="Warning" />
<Rule Id="S3253" Action="None" />
<Rule Id="S3254" Action="None" />
<Rule Id="S3256" Action="Warning" />
<Rule Id="S3257" Action="None" />
<Rule Id="S3261" Action="Warning" />
<Rule Id="S3262" Action="Warning" />
<Rule Id="S3263" Action="Warning" />
<Rule Id="S3264" Action="Warning" />
<Rule Id="S3265" Action="Warning" />
<Rule Id="S3330" Action="None" />
<Rule Id="S3343" Action="Warning" />
<Rule Id="S3346" Action="Warning" />
<Rule Id="S3353" Action="None" />
<Rule Id="S3358" Action="Warning" />
<Rule Id="S3366" Action="None" />
<Rule Id="S3376" Action="Warning" />
<Rule Id="S3397" Action="Warning" />
<Rule Id="S3400" Action="Warning" />
<Rule Id="S3415" Action="Warning" />
<Rule Id="S3427" Action="Warning" />
<Rule Id="S3431" Action="None" />
<Rule Id="S3433" Action="Warning" />
<Rule Id="S3440" Action="Warning" />
<Rule Id="S3441" Action="None" />
<Rule Id="S3442" Action="Warning" />
<Rule Id="S3443" Action="Warning" />
<Rule Id="S3444" Action="Warning" />
<Rule Id="S3445" Action="Warning" />
<Rule Id="S3447" Action="Warning" />
<Rule Id="S3449" Action="Warning" />
<Rule Id="S3450" Action="Warning" />
<Rule Id="S3451" Action="Warning" />
<Rule Id="S3453" Action="Warning" />
<Rule Id="S3456" Action="Warning" />
<Rule Id="S3457" Action="Warning" />
<Rule Id="S3458" Action="Warning" />
<Rule Id="S3459" Action="Warning" />
<Rule Id="S3464" Action="Warning" />
<Rule Id="S3466" Action="Warning" />
<Rule Id="S3532" Action="None" />
<Rule Id="S3597" Action="Warning" />
<Rule Id="S3598" Action="Warning" />
<Rule Id="S3600" Action="Warning" />
<Rule Id="S3603" Action="Warning" />
<Rule Id="S3604" Action="Warning" />
<Rule Id="S3610" Action="Warning" />
<Rule Id="S3626" Action="Warning" />
<Rule Id="S3655" Action="Warning" />
<Rule Id="S3693" Action="Warning" />
<Rule Id="S3717" Action="None" />
<Rule Id="S3776" Action="Warning" />
<Rule Id="S3869" Action="Warning" />
<Rule Id="S3871" Action="Warning" />
<Rule Id="S3872" Action="None" />
<Rule Id="S3874" Action="None" />
<Rule Id="S3875" Action="Warning" />
<Rule Id="S3876" Action="None" />
<Rule Id="S3877" Action="Warning" />
<Rule Id="S3880" Action="None" />
<Rule Id="S3881" Action="Warning" />
<Rule Id="S3884" Action="Warning" />
<Rule Id="S3885" Action="Warning" />
<Rule Id="S3887" Action="Warning" />
<Rule Id="S3889" Action="Warning" />
<Rule Id="S3897" Action="Warning" />
<Rule Id="S3898" Action="None" />
<Rule Id="S3900" Action="None" />
<Rule Id="S3902" Action="None" />
<Rule Id="S3903" Action="Warning" />
<Rule Id="S3904" Action="Warning" />
<Rule Id="S3906" Action="None" />
<Rule Id="S3908" Action="None" />
<Rule Id="S3909" Action="None" />
<Rule Id="S3923" Action="Warning" />
<Rule Id="S3925" Action="Warning" />
<Rule Id="S3926" Action="Warning" />
<Rule Id="S3927" Action="Warning" />
<Rule Id="S3928" Action="Warning" />
<Rule Id="S3937" Action="None" />
<Rule Id="S3956" Action="None" />
<Rule Id="S3962" Action="None" />
<Rule Id="S3963" Action="Warning" />
<Rule Id="S3966" Action="Warning" />
<Rule Id="S3967" Action="None" />
<Rule Id="S3971" Action="Warning" />
<Rule Id="S3972" Action="Warning" />
<Rule Id="S3973" Action="Warning" />
<Rule Id="S3981" Action="Warning" />
<Rule Id="S3984" Action="Warning" />
<Rule Id="S3990" Action="None" />
<Rule Id="S3992" Action="None" />
<Rule Id="S3993" Action="None" />
<Rule Id="S3994" Action="None" />
<Rule Id="S3995" Action="None" />
<Rule Id="S3996" Action="None" />
<Rule Id="S3997" Action="None" />
<Rule Id="S3998" Action="Warning" />
<Rule Id="S4000" Action="None" />
<Rule Id="S4002" Action="None" />
<Rule Id="S4004" Action="None" />
<Rule Id="S4005" Action="None" />
<Rule Id="S4015" Action="Warning" />
<Rule Id="S4016" Action="Warning" />
<Rule Id="S4017" Action="None" />
<Rule Id="S4018" Action="None" />
<Rule Id="S4019" Action="Warning" />
<Rule Id="S4022" Action="None" />
<Rule Id="S4023" Action="None" />
<Rule Id="S4025" Action="None" />
<Rule Id="S4026" Action="None" />
<Rule Id="S4027" Action="None" />
<Rule Id="S4035" Action="Warning" />
<Rule Id="S4039" Action="None" />
<Rule Id="S4040" Action="None" />
<Rule Id="S4041" Action="None" />
<Rule Id="S4047" Action="None" />
<Rule Id="S4049" Action="None" />
<Rule Id="S4050" Action="None" />
<Rule Id="S4052" Action="None" />
<Rule Id="S4055" Action="None" />
<Rule Id="S4056" Action="None" />
<Rule Id="S4057" Action="None" />
<Rule Id="S4058" Action="None" />
<Rule Id="S4059" Action="None" />
<Rule Id="S4060" Action="None" />
<Rule Id="S4061" Action="Warning" />
<Rule Id="S4069" Action="None" />
<Rule Id="S4070" Action="None" />
<Rule Id="S4142" Action="None" />
<Rule Id="S4143" Action="Warning" />
<Rule Id="S4144" Action="Warning" />
<Rule Id="S4158" Action="Warning" />
<Rule Id="S4159" Action="Warning" />
<Rule Id="S4200" Action="Warning" />
<Rule Id="S4210" Action="Warning" />
<Rule Id="S4211" Action="Warning" />
<Rule Id="S4212" Action="None" />
<Rule Id="S4214" Action="Warning" />
<Rule Id="S4220" Action="Warning" />
<Rule Id="S4225" Action="None" />
<Rule Id="S4226" Action="None" />
<Rule Id="S4260" Action="Warning" />
<Rule Id="S4261" Action="None" />
<Rule Id="S4275" Action="Warning" />
<Rule Id="S4277" Action="Warning" />
<Rule Id="S4426" Action="Warning" />
<Rule Id="S4428" Action="Warning" />
<Rule Id="S4432" Action="None" />
<Rule Id="S4433" Action="Warning" />
<Rule Id="S4456" Action="Warning" />
<Rule Id="S4457" Action="Warning" />
<Rule Id="S4462" Action="None" />
<Rule Id="S4524" Action="Warning" />
<Rule Id="S4564" Action="None" />
<Rule Id="S4581" Action="Warning" />
<Rule Id="S4586" Action="Warning" />
<Rule Id="S818" Action="Warning" />
<Rule Id="S881" Action="None" />
<Rule Id="S907" Action="Warning" />
<Rule Id="S927" Action="Warning" />
</Rules>
<Rules AnalyzerId="SonarAnalyzer.Security" RuleNamespace="SonarAnalyzer.Security">
<Rule Id="S2076" Action="Warning" />
<Rule Id="S2078" Action="Warning" />
<Rule Id="S2083" Action="Warning" />
<Rule Id="S2091" Action="Warning" />
<Rule Id="S2631" Action="Warning" />
<Rule Id="S3649" Action="Warning" />
</Rules>
</RuleSet>

1
.squad/.first-run Normal file
View File

@@ -0,0 +1 @@
2026-04-16T14:02:25.652Z

View File

@@ -0,0 +1,20 @@
# Ralph — Ralph
Persistent memory agent that maintains context across sessions.
## Project Context
**Project:** WireMock.Net
## Responsibilities
- Collaborate with team members on assigned work
- Maintain code quality and project standards
- Document decisions and progress in history
## Work Style
- Read project context and team decisions before starting work
- Communicate clearly with team members
- Follow established patterns and conventions

View File

@@ -0,0 +1,16 @@
# Project Context
- **Project:** WireMock.Net
- **Created:** 2026-04-16
## Core Context
Agent Ralph initialized and ready for work.
## Recent Updates
📌 Team initialized on 2026-04-16
## Learnings
Initial setup complete.

View File

@@ -0,0 +1,20 @@
# Scribe — Scribe
Documentation specialist maintaining history, decisions, and technical records.
## Project Context
**Project:** WireMock.Net
## Responsibilities
- Collaborate with team members on assigned work
- Maintain code quality and project standards
- Document decisions and progress in history
## Work Style
- Read project context and team decisions before starting work
- Communicate clearly with team members
- Follow established patterns and conventions

View File

@@ -0,0 +1,16 @@
# Project Context
- **Project:** WireMock.Net
- **Created:** 2026-04-16
## Core Context
Agent Scribe initialized and ready for work.
## Recent Updates
📌 Team initialized on 2026-04-16
## Learnings
Initial setup complete.

41
.squad/ceremonies.md Normal file
View File

@@ -0,0 +1,41 @@
# Ceremonies
> Team meetings that happen before or after work. Each squad configures their own.
## Design Review
| Field | Value |
|-------|-------|
| **Trigger** | auto |
| **When** | before |
| **Condition** | multi-agent task involving 2+ agents modifying shared systems |
| **Facilitator** | lead |
| **Participants** | all-relevant |
| **Time budget** | focused |
| **Enabled** | ✅ yes |
**Agenda:**
1. Review the task and requirements
2. Agree on interfaces and contracts between components
3. Identify risks and edge cases
4. Assign action items
---
## Retrospective
| Field | Value |
|-------|-------|
| **Trigger** | auto |
| **When** | after |
| **Condition** | build failure, test failure, or reviewer rejection |
| **Facilitator** | lead |
| **Participants** | all-involved |
| **Time budget** | focused |
| **Enabled** | ✅ yes |
**Agenda:**
1. What happened? (facts only)
2. Root cause analysis
3. What should change?
4. Action items for next iteration

3
.squad/config.json Normal file
View File

@@ -0,0 +1,3 @@
{
"version": 1
}

11
.squad/decisions.md Normal file
View File

@@ -0,0 +1,11 @@
# Squad Decisions
## Active Decisions
No decisions recorded yet.
## Governance
- All meaningful changes require team consensus
- Document architectural decisions here
- Keep history focused on work, decisions focused on direction

9
.squad/identity/now.md Normal file
View File

@@ -0,0 +1,9 @@
---
updated_at: 2026-04-16T14:02:25.009Z
focus_area: Initial setup
active_issues: []
---
# What We're Focused On
Getting started. Updated by coordinator at session start.

11
.squad/identity/wisdom.md Normal file
View File

@@ -0,0 +1,11 @@
---
last_updated: 2026-04-16T14:02:25.009Z
---
# Team Wisdom
Reusable patterns and heuristics learned through work. NOT transcripts — each entry is a distilled, actionable insight.
## Patterns
<!-- Append entries below. Format: **Pattern:** description. **Context:** when it applies. -->

39
.squad/routing.md Normal file
View File

@@ -0,0 +1,39 @@
# Work Routing
How to decide who handles what.
## Routing Table
| Work Type | Route To | Examples |
|-----------|----------|----------|
| {domain 1} | {Name} | {example tasks} |
| {domain 2} | {Name} | {example tasks} |
| {domain 3} | {Name} | {example tasks} |
| Code review | {Name} | Review PRs, check quality, suggest improvements |
| Testing | {Name} | Write tests, find edge cases, verify fixes |
| Scope & priorities | {Name} | What to build next, trade-offs, decisions |
| Session logging | Scribe | Automatic — never needs routing |
## Issue Routing
| Label | Action | Who |
|-------|--------|-----|
| `squad` | Triage: analyze issue, assign `squad:{member}` label | Lead |
| `squad:{name}` | Pick up issue and complete the work | Named member |
### How Issue Assignment Works
1. When a GitHub issue gets the `squad` label, the **Lead** triages it — analyzing content, assigning the right `squad:{member}` label, and commenting with triage notes.
2. When a `squad:{member}` label is applied, that member picks up the issue in their next session.
3. Members can reassign by removing their label and adding another member's label.
4. The `squad` label is the "inbox" — untriaged issues waiting for Lead review.
## Rules
1. **Eager by default** — spawn all agents who could usefully start work, including anticipatory downstream work.
2. **Scribe always runs** after substantial work, always as `mode: "background"`. Never blocks.
3. **Quick facts → coordinator answers directly.** Don't spawn an agent for "what port does the server run on?"
4. **When two agents could handle it**, pick the one whose domain is the primary concern.
5. **"Team, ..." → fan-out.** Spawn all relevant agents in parallel as `mode: "background"`.
6. **Anticipate downstream work.** If a feature is being built, spawn the tester to write test cases from requirements simultaneously.
7. **Issue-labeled work** — when a `squad:{member}` label is applied to an issue, route to that member. The Lead handles all `squad` (base label) triage.

19
.squad/team.md Normal file
View File

@@ -0,0 +1,19 @@
# Squad Team
> WireMock.Net
## Coordinator
| Name | Role | Notes |
|------|------|-------|
| Squad | Coordinator | Routes work, enforces handoffs and reviewer gates. |
## Members
| Name | Role | Charter | Status |
|------|------|---------|--------|
## Project Context
- **Project:** WireMock.Net
- **Created:** 2026-04-16

View File

@@ -0,0 +1,4 @@
{
"universe_usage_history": [],
"assignment_cast_snapshots": {}
}

View File

@@ -0,0 +1,37 @@
{
"casting_policy_version": "1.1",
"allowlist_universes": [
"The Usual Suspects",
"Reservoir Dogs",
"Alien",
"Ocean's Eleven",
"Arrested Development",
"Star Wars",
"The Matrix",
"Firefly",
"The Goonies",
"The Simpsons",
"Breaking Bad",
"Lost",
"Marvel Cinematic Universe",
"DC Universe",
"Futurama"
],
"universe_capacity": {
"The Usual Suspects": 6,
"Reservoir Dogs": 8,
"Alien": 8,
"Ocean's Eleven": 14,
"Arrested Development": 15,
"Star Wars": 12,
"The Matrix": 10,
"Firefly": 10,
"The Goonies": 8,
"The Simpsons": 20,
"Breaking Bad": 12,
"Lost": 18,
"Marvel Cinematic Universe": 25,
"DC Universe": 18,
"Futurama": 12
}
}

View File

@@ -0,0 +1,104 @@
# Casting Reference
On-demand reference for Squad's casting system. Loaded during Init Mode or when adding team members.
## Universe Table
| Universe | Capacity | Shape Tags | Resonance Signals |
|---|---|---|---|
| The Usual Suspects | 6 | small, noir, ensemble | crime, heist, mystery, deception |
| Reservoir Dogs | 8 | small, noir, ensemble | crime, heist, tension, loyalty |
| Alien | 8 | small, sci-fi, survival | space, isolation, threat, engineering |
| Ocean's Eleven | 14 | medium, heist, ensemble | planning, coordination, roles, charm |
| Arrested Development | 15 | medium, comedy, ensemble | dysfunction, business, family, satire |
| Star Wars | 12 | medium, sci-fi, epic | conflict, mentorship, legacy, rebellion |
| The Matrix | 10 | medium, sci-fi, cyberpunk | systems, reality, hacking, philosophy |
| Firefly | 10 | medium, sci-fi, western | frontier, crew, independence, smuggling |
| The Goonies | 8 | small, adventure, ensemble | exploration, treasure, kids, teamwork |
| The Simpsons | 20 | large, comedy, ensemble | satire, community, family, absurdity |
| Breaking Bad | 12 | medium, drama, tension | chemistry, transformation, consequence, power |
| Lost | 18 | large, mystery, ensemble | survival, mystery, groups, leadership |
| Marvel Cinematic Universe | 25 | large, action, ensemble | heroism, teamwork, powers, scale |
| DC Universe | 18 | large, action, ensemble | justice, duality, powers, mythology |
| Futurama | 12 | medium, sci-fi, comedy | future, robots, space, absurdity |
**Total: 15 universes** — capacity range 625.
## Selection Algorithm
Universe selection is deterministic. Score each universe and pick the highest:
```
score = size_fit + shape_fit + resonance_fit + LRU
```
| Factor | Description |
|---|---|
| `size_fit` | How well the universe capacity matches the team size. Prefer universes where capacity ≥ agent_count with minimal waste. |
| `shape_fit` | Match universe shape tags against the assignment shape derived from the project description. |
| `resonance_fit` | Match universe resonance signals against session and repo context signals. |
| `LRU` | Least-recently-used bonus — prefer universes not used in recent assignments (from `history.json`). |
Same inputs → same choice (unless LRU changes between assignments).
## Casting State File Schemas
### policy.json
Source template: `.squad/templates/casting-policy.json`
Runtime location: `.squad/casting/policy.json`
```json
{
"casting_policy_version": "1.1",
"allowlist_universes": ["Universe Name", "..."],
"universe_capacity": {
"Universe Name": 10
}
}
```
### registry.json
Source template: `.squad/templates/casting-registry.json`
Runtime location: `.squad/casting/registry.json`
```json
{
"agents": {
"agent-role-id": {
"persistent_name": "CharacterName",
"universe": "Universe Name",
"created_at": "ISO-8601",
"legacy_named": false,
"status": "active"
}
}
}
```
### history.json
Source template: `.squad/templates/casting-history.json`
Runtime location: `.squad/casting/history.json`
```json
{
"universe_usage_history": [
{
"universe": "Universe Name",
"assignment_id": "unique-id",
"used_at": "ISO-8601"
}
],
"assignment_cast_snapshots": {
"assignment-id": {
"universe": "Universe Name",
"agents": {
"role-id": "CharacterName"
},
"created_at": "ISO-8601"
}
}
}
```

View File

@@ -0,0 +1,3 @@
{
"agents": {}
}

View File

@@ -0,0 +1,10 @@
[
"Fry",
"Leela",
"Bender",
"Farnsworth",
"Zoidberg",
"Amy",
"Zapp",
"Kif"
]

View File

@@ -0,0 +1,41 @@
# Ceremonies
> Team meetings that happen before or after work. Each squad configures their own.
## Design Review
| Field | Value |
|-------|-------|
| **Trigger** | auto |
| **When** | before |
| **Condition** | multi-agent task involving 2+ agents modifying shared systems |
| **Facilitator** | lead |
| **Participants** | all-relevant |
| **Time budget** | focused |
| **Enabled** | ✅ yes |
**Agenda:**
1. Review the task and requirements
2. Agree on interfaces and contracts between components
3. Identify risks and edge cases
4. Assign action items
---
## Retrospective
| Field | Value |
|-------|-------|
| **Trigger** | auto |
| **When** | after |
| **Condition** | build failure, test failure, or reviewer rejection |
| **Facilitator** | lead |
| **Participants** | all-involved |
| **Time budget** | focused |
| **Enabled** | ✅ yes |
**Agenda:**
1. What happened? (facts only)
2. Root cause analysis
3. What should change?
4. Action items for next iteration

View File

@@ -0,0 +1,53 @@
# {Name} — {Role}
> {One-line personality statement — what makes this person tick}
## Identity
- **Name:** {Name}
- **Role:** {Role title}
- **Expertise:** {2-3 specific skills relevant to the project}
- **Style:** {How they communicate — direct? thorough? opinionated?}
## What I Own
- {Area of responsibility 1}
- {Area of responsibility 2}
- {Area of responsibility 3}
## How I Work
- {Key approach or principle 1}
- {Key approach or principle 2}
- {Pattern or convention I follow}
## Boundaries
**I handle:** {types of work this agent does}
**I don't handle:** {types of work that belong to other team members}
**When I'm unsure:** I say so and suggest who might know.
**If I review others' work:** On rejection, I may require a different agent to revise (not the original author) or request a new specialist be spawned. The Coordinator enforces this.
## Model
- **Preferred:** auto
- **Rationale:** Coordinator selects the best model based on task type — cost first unless writing code
- **Fallback:** Standard chain — the coordinator handles fallback automatically
## Collaboration
Before starting work, run `git rev-parse --show-toplevel` to find the repo root, or use the `TEAM ROOT` provided in the spawn prompt. All `.squad/` paths must be resolved relative to this root — do not assume CWD is the repo root (you may be in a worktree or subdirectory).
Before starting work, read `.squad/decisions.md` for team decisions that affect me.
After making a decision others should know, write it to `.squad/decisions/inbox/{my-name}-{brief-slug}.md` — the Scribe will merge it.
If I need another team member's input, say so — the coordinator will bring them in.
## Voice
{1-2 sentences describing personality. Not generic — specific. This agent has OPINIONS.
They have preferences. They push back. They have a style that's distinctly theirs.
Example: "Opinionated about test coverage. Will push back if tests are skipped.
Prefers integration tests over mocks. Thinks 80% coverage is the floor, not the ceiling."}

View File

@@ -0,0 +1,38 @@
# Constraint Budget Tracking
When the user or system imposes constraints (question limits, revision limits, time budgets), maintain a visible counter in your responses and in the artifact.
## Format
```
📊 Clarifying questions used: 2 / 3
```
## Rules
- Update the counter each time the constraint is consumed
- When a constraint is exhausted, state it: `📊 Question budget exhausted (3/3). Proceeding with current information.`
- If no constraints are active, do not display counters
- Include the final constraint status in multi-agent artifacts
## Example Session
```
Coordinator: Spawning agents to analyze requirements...
📊 Clarifying questions used: 0 / 3
Agent asks clarification: "Should we support OAuth?"
Coordinator: Checking with user...
📊 Clarifying questions used: 1 / 3
Agent asks clarification: "What's the rate limit?"
Coordinator: Checking with user...
📊 Clarifying questions used: 2 / 3
Agent asks clarification: "Do we need RBAC?"
Coordinator: Checking with user...
📊 Clarifying questions used: 3 / 3
Agent asks clarification: "Should we cache responses?"
Coordinator: 📊 Question budget exhausted (3/3). Proceeding without clarification.
```

View File

@@ -0,0 +1,229 @@
# Cooperative Rate Limiting for Multi-Agent Deployments
> Coordinate API quota across multiple Ralph instances to prevent cascading failures.
## Problem
The [circuit breaker template](ralph-circuit-breaker.md) handles single-instance rate limiting well. But when multiple Ralphs run across machines (or pods on K8s), each instance independently hits API limits:
- **No coordination** — 5 Ralphs each think they have full API quota
- **Thundering herd** — All Ralphs retry simultaneously after rate limit resets
- **Priority inversion** — Low-priority work exhausts quota before critical work runs
- **Reactive only** — Circuit opens AFTER 429, wasting the failed request
## Solution: 6-Pattern Architecture
These patterns layer on top of the existing circuit breaker. Each is independent — adopt one or all.
### Pattern 1: Traffic Light (RAAS — Rate-Aware Agent Scheduling)
Map GitHub API `X-RateLimit-Remaining` to traffic light states:
| State | Remaining % | Behavior |
|-------|------------|----------|
| 🟢 GREEN | >20% | Normal operation |
| 🟡 AMBER | 520% | Only P0 agents proceed |
| 🔴 RED | <5% | Block all except emergency P0 |
```typescript
type TrafficLight = 'green' | 'amber' | 'red';
function getTrafficLight(remaining: number, limit: number): TrafficLight {
const pct = remaining / limit;
if (pct > 0.20) return 'green';
if (pct > 0.05) return 'amber';
return 'red';
}
function shouldProceed(light: TrafficLight, agentPriority: number): boolean {
if (light === 'green') return true;
if (light === 'amber') return agentPriority === 0; // P0 only
return false; // RED — block all
}
```
### Pattern 2: Cooperative Token Pool (CMARP)
A shared JSON file (`~/.squad/rate-pool.json`) distributes API quota:
```json
{
"totalLimit": 5000,
"resetAt": "2026-03-22T20:00:00Z",
"allocations": {
"picard": { "priority": 0, "allocated": 2000, "used": 450, "leaseExpiry": "2026-03-22T19:55:00Z" },
"data": { "priority": 1, "allocated": 1750, "used": 200, "leaseExpiry": "2026-03-22T19:55:00Z" },
"ralph": { "priority": 2, "allocated": 1250, "used": 100, "leaseExpiry": "2026-03-22T19:55:00Z" }
}
}
```
**Rules:**
- P0 agents (Lead) get 40% of quota
- P1 agents (specialists) get 35%
- P2 agents (Ralph, Scribe) get 25%
- Stale leases (>5 minutes without heartbeat) are auto-recovered
- Each agent checks their remaining allocation before making API calls
```typescript
interface RatePoolAllocation {
priority: number;
allocated: number;
used: number;
leaseExpiry: string;
}
interface RatePool {
totalLimit: number;
resetAt: string;
allocations: Record<string, RatePoolAllocation>;
}
function canUseQuota(pool: RatePool, agentName: string): boolean {
const alloc = pool.allocations[agentName];
if (!alloc) return true; // Unknown agent — allow (graceful)
// Reclaim stale leases from crashed agents
const now = new Date();
for (const [name, a] of Object.entries(pool.allocations)) {
if (new Date(a.leaseExpiry) < now && name !== agentName) {
a.allocated = 0; // Reclaim
}
}
return alloc.used < alloc.allocated;
}
```
### Pattern 3: Predictive Circuit Breaker (PCB)
Opens the circuit BEFORE getting a 429 by predicting when quota will run out:
```typescript
interface RateSample {
timestamp: number; // Date.now()
remaining: number; // from X-RateLimit-Remaining header
}
class PredictiveCircuitBreaker {
private samples: RateSample[] = [];
private readonly maxSamples = 10;
private readonly warningThresholdSeconds = 120;
addSample(remaining: number): void {
this.samples.push({ timestamp: Date.now(), remaining });
if (this.samples.length > this.maxSamples) {
this.samples.shift();
}
}
/** Predict seconds until quota exhaustion using linear regression */
predictExhaustion(): number | null {
if (this.samples.length < 3) return null;
const n = this.samples.length;
const first = this.samples[0];
const last = this.samples[n - 1];
const elapsedMs = last.timestamp - first.timestamp;
if (elapsedMs === 0) return null;
const consumedPerMs = (first.remaining - last.remaining) / elapsedMs;
if (consumedPerMs <= 0) return null; // Not consuming — safe
const msUntilExhausted = last.remaining / consumedPerMs;
return msUntilExhausted / 1000;
}
shouldOpen(): boolean {
const eta = this.predictExhaustion();
if (eta === null) return false;
return eta < this.warningThresholdSeconds;
}
}
```
### Pattern 4: Priority Retry Windows (PWJG)
Non-overlapping jitter windows prevent thundering herd:
| Priority | Retry Window | Description |
|----------|-------------|-------------|
| P0 (Lead) | 500ms5s | Recovers first |
| P1 (Specialists) | 2s30s | Moderate delay |
| P2 (Ralph/Scribe) | 5s60s | Most patient |
```typescript
function getRetryDelay(priority: number, attempt: number): number {
const windows: Record<number, [number, number]> = {
0: [500, 5000], // P0: 500ms5s
1: [2000, 30000], // P1: 2s30s
2: [5000, 60000], // P2: 5s60s
};
const [min, max] = windows[priority] ?? windows[2];
const base = Math.min(min * Math.pow(2, attempt), max);
const jitter = Math.random() * base * 0.5;
return base + jitter;
}
```
### Pattern 5: Resource Epoch Tracker (RET)
Heartbeat-based lease system for multi-machine deployments:
```typescript
interface ResourceLease {
agent: string;
machine: string;
leaseStart: string;
leaseExpiry: string; // Typically 5 minutes from now
allocated: number;
}
// Each agent renews its lease every 2 minutes
// If lease expires (agent crashed), allocation is reclaimed
```
### Pattern 6: Cascade Dependency Detector (CDD)
Track downstream failures and apply backpressure:
```
Agent A (rate limited) → Agent B (waiting for A) → Agent C (waiting for B)
↑ Backpressure signal: "don't start new work"
```
When a dependency is rate-limited, upstream agents should pause new work rather than queuing requests that will fail.
## Kubernetes Integration
On K8s, cooperative rate limiting can use KEDA to scale pods based on API quota:
```yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
spec:
scaleTargetRef:
name: ralph-deployment
triggers:
- type: external
metadata:
scalerAddress: keda-copilot-scaler:6000
# Scaler returns 0 when rate limited → pods scale to zero
```
See [keda-copilot-scaler](https://github.com/tamirdresher/keda-copilot-scaler) for a complete implementation.
## Quick Start
1. **Minimum viable:** Adopt Pattern 1 (Traffic Light) — read `X-RateLimit-Remaining` from API responses
2. **Multi-machine:** Add Pattern 2 (Cooperative Pool) — shared `rate-pool.json`
3. **Production:** Add Pattern 3 (Predictive CB) — prevent 429s entirely
4. **Kubernetes:** Add KEDA scaler for automatic pod scaling
## References
- [Circuit Breaker Template](ralph-circuit-breaker.md) — Foundation patterns
- [Squad on AKS](https://github.com/tamirdresher/squad-on-aks) — Production K8s deployment
- [KEDA Copilot Scaler](https://github.com/tamirdresher/keda-copilot-scaler) — Custom KEDA external scaler

View File

@@ -0,0 +1,46 @@
# Copilot Coding Agent — Squad Instructions
You are working on a project that uses **Squad**, an AI team framework. When picking up issues autonomously, follow these guidelines.
## Team Context
Before starting work on any issue:
1. Read `.squad/team.md` for the team roster, member roles, and your capability profile.
2. Read `.squad/routing.md` for work routing rules.
3. If the issue has a `squad:{member}` label, read that member's charter at `.squad/agents/{member}/charter.md` to understand their domain expertise and coding style — work in their voice.
## Capability Self-Check
Before starting work, check your capability profile in `.squad/team.md` under the **Coding Agent → Capabilities** section.
- **🟢 Good fit** — proceed autonomously.
- **🟡 Needs review** — proceed, but note in the PR description that a squad member should review.
- **🔴 Not suitable** — do NOT start work. Instead, comment on the issue:
```
🤖 This issue doesn't match my capability profile (reason: {why}). Suggesting reassignment to a squad member.
```
## Branch Naming
Use the squad branch convention:
```
squad/{issue-number}-{kebab-case-slug}
```
Example: `squad/42-fix-login-validation`
## PR Guidelines
When opening a PR:
- Reference the issue: `Closes #{issue-number}`
- If the issue had a `squad:{member}` label, mention the member: `Working as {member} ({role})`
- If this is a 🟡 needs-review task, add to the PR description: `⚠️ This task was flagged as "needs review" — please have a squad member review before merging.`
- Follow any project conventions in `.squad/decisions.md`
## Decisions
If you make a decision that affects other team members, write it to:
```
.squad/decisions/inbox/copilot-{brief-slug}.md
```
The Scribe will merge it into the shared decisions file.

View File

@@ -0,0 +1,10 @@
# Project Context
- **Owner:** {user name}
- **Project:** {project description}
- **Stack:** {languages, frameworks, tools}
- **Created:** {timestamp}
## Learnings
<!-- Append new learnings below. Each entry is something lasting about the project. -->

View File

@@ -0,0 +1,9 @@
---
updated_at: {timestamp}
focus_area: {brief description}
active_issues: []
---
# What We're Focused On
{Narrative description of current focus — 1-3 sentences. Updated by coordinator at session start.}

View File

@@ -0,0 +1,15 @@
---
last_updated: {timestamp}
---
# Team Wisdom
Reusable patterns and heuristics learned through work. NOT transcripts — each entry is a distilled, actionable insight.
## Patterns
<!-- Append entries below. Format: **Pattern:** description. **Context:** when it applies. -->
## Anti-Patterns
<!-- Things we tried that didn't work. **Avoid:** description. **Why:** reason. -->

View File

@@ -0,0 +1,412 @@
# Issue Lifecycle — Repo Connection & PR Flow
Reference for connecting Squad to a repository and managing the issue→branch→PR→merge lifecycle.
## Repo Connection Format
When connecting Squad to an issue tracker, store the connection in `.squad/team.md`:
```markdown
## Issue Source
**Repository:** {owner}/{repo}
**Connected:** {date}
**Platform:** {GitHub | Azure DevOps | Planner}
**Filters:**
- Labels: `{label-filter}`
- Project: `{project-name}` (ADO/Planner only)
- Plan: `{plan-id}` (Planner only)
```
**Detection triggers:**
- User says "connect to {repo}"
- User says "monitor {repo} for issues"
- Ralph is activated without an issue source
## Platform-Specific Issue States
Each platform tracks issue lifecycle differently. Squad normalizes these into a common board state.
### GitHub
| GitHub State | GitHub API Fields | Squad Board State |
|--------------|-------------------|-------------------|
| Open, no assignee | `state: open`, `assignee: null` | `untriaged` |
| Open, assigned, no branch | `state: open`, `assignee: @user`, no linked PR | `assigned` |
| Open, branch exists | `state: open`, linked branch exists | `inProgress` |
| Open, PR opened | `state: open`, PR exists, `reviewDecision: null` | `needsReview` |
| Open, PR approved | `state: open`, PR `reviewDecision: APPROVED` | `readyToMerge` |
| Open, changes requested | `state: open`, PR `reviewDecision: CHANGES_REQUESTED` | `changesRequested` |
| Open, CI failure | `state: open`, PR `statusCheckRollup: FAILURE` | `ciFailure` |
| Closed | `state: closed` | `done` |
**Issue labels used by Squad:**
- `squad` — Issue is in Squad backlog
- `squad:{member}` — Assigned to specific agent
- `squad:untriaged` — Needs triage
- `go:needs-research` — Needs investigation before implementation
- `priority:p{N}` — Priority level (0=critical, 1=high, 2=medium, 3=low)
- `next-up` — Queued for next agent pickup
**Branch naming convention:**
```
squad/{issue-number}-{kebab-case-slug}
```
Example: `squad/42-fix-login-validation`
### Azure DevOps
| ADO State | Squad Board State |
|-----------|-------------------|
| New | `untriaged` |
| Active, no branch | `assigned` |
| Active, branch exists | `inProgress` |
| Active, PR opened | `needsReview` |
| Active, PR approved | `readyToMerge` |
| Resolved | `done` |
| Closed | `done` |
**Work item tags used by Squad:**
- `squad` — Work item is in Squad backlog
- `squad:{member}` — Assigned to specific agent
**Branch naming convention:**
```
squad/{work-item-id}-{kebab-case-slug}
```
Example: `squad/1234-add-auth-module`
### Microsoft Planner
Planner does not have native Git integration. Squad uses Planner for task tracking and GitHub/ADO for code management.
| Planner Status | Squad Board State |
|----------------|-------------------|
| Not Started | `untriaged` |
| In Progress, no PR | `inProgress` |
| In Progress, PR opened | `needsReview` |
| Completed | `done` |
**Planner→Git workflow:**
1. Task created in Planner bucket
2. Agent reads task from Planner
3. Agent creates branch in GitHub/ADO repo
4. Agent opens PR referencing Planner task ID in description
5. Agent marks task as "Completed" when PR merges
## Issue → Branch → PR → Merge Lifecycle
### 1. Issue Assignment (Triage)
**Trigger:** Ralph detects an untriaged issue or user manually assigns work.
**Actions:**
1. Read `.squad/routing.md` to determine which agent should handle the issue
2. Apply `squad:{member}` label (GitHub) or tag (ADO)
3. Transition issue to `assigned` state
4. Optionally spawn agent immediately if issue is high-priority
**Issue read command:**
```bash
# GitHub
gh issue view {number} --json number,title,body,labels,assignees
# Azure DevOps
az boards work-item show --id {id} --output json
```
### 2. Branch Creation (Start Work)
**Trigger:** Agent accepts issue assignment and begins work.
**Actions:**
1. Ensure working on latest base branch (usually `main` or `dev`)
2. Create feature branch using Squad naming convention
3. Transition issue to `inProgress` state
**Branch creation commands:**
**Standard (single-agent, no parallelism):**
```bash
git checkout main && git pull && git checkout -b squad/{issue-number}-{slug}
```
**Worktree (parallel multi-agent):**
```bash
git worktree add ../worktrees/{issue-number} -b squad/{issue-number}-{slug}
cd ../worktrees/{issue-number}
```
> **Note:** Worktree support is in progress (#525). Current implementation uses standard checkout.
### 3. Implementation & Commit
**Actions:**
1. Agent makes code changes
2. Commits reference the issue number
3. Pushes branch to remote
**Commit message format:**
```
{type}({scope}): {description} (#{issue-number})
{detailed explanation if needed}
{breaking change notice if applicable}
Closes #{issue-number}
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
```
**Commit types:** `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf`, `style`, `build`, `ci`
**Push command:**
```bash
git push -u origin squad/{issue-number}-{slug}
```
### 4. PR Creation
**Trigger:** Agent completes implementation and is ready for review.
**Actions:**
1. Open PR from feature branch to base branch
2. Reference issue in PR description
3. Apply labels if needed
4. Transition issue to `needsReview` state
**PR creation commands:**
**GitHub:**
```bash
gh pr create --title "{title}" \
--body "Closes #{issue-number}\n\n{description}" \
--head squad/{issue-number}-{slug} \
--base main
```
**Azure DevOps:**
```bash
az repos pr create --title "{title}" \
--description "Closes #{work-item-id}\n\n{description}" \
--source-branch squad/{work-item-id}-{slug} \
--target-branch main
```
**PR description template:**
```markdown
Closes #{issue-number}
## Summary
{what changed}
## Changes
- {change 1}
- {change 2}
## Testing
{how this was tested}
{If working as a squad member:}
Working as {member} ({role})
{If needs human review:}
⚠️ This task was flagged as "needs review" — please have a squad member review before merging.
```
### 5. PR Review & Updates
**Review states:**
- **Approved** → `readyToMerge`
- **Changes requested** → `changesRequested`
- **CI failure** → `ciFailure`
**When changes are requested:**
1. Agent addresses feedback
2. Commits fixes to the same branch
3. Pushes updates
4. Requests re-review
**Update workflow:**
```bash
# Make changes
git add .
git commit -m "fix: address review feedback"
git push
```
**Re-request review (GitHub):**
```bash
gh pr ready {pr-number}
```
### 6. PR Merge
**Trigger:** PR is approved and CI passes.
**Merge strategies:**
**GitHub (merge commit):**
```bash
gh pr merge {pr-number} --merge --delete-branch
```
**GitHub (squash):**
```bash
gh pr merge {pr-number} --squash --delete-branch
```
**Azure DevOps:**
```bash
az repos pr update --id {pr-id} --status completed --delete-source-branch true
```
**Post-merge actions:**
1. Issue automatically closes (if "Closes #{number}" is in PR description)
2. Feature branch is deleted
3. Squad board state transitions to `done`
4. Worktree cleanup (if worktree was used — #525)
### 7. Cleanup
**Standard workflow cleanup:**
```bash
git checkout main
git pull
git branch -d squad/{issue-number}-{slug}
```
**Worktree cleanup (future, #525):**
```bash
cd {original-cwd}
git worktree remove ../worktrees/{issue-number}
```
## Spawn Prompt Additions for Issue Work
When spawning an agent to work on an issue, include this context block:
```markdown
## ISSUE CONTEXT
**Issue:** #{number} — {title}
**Platform:** {GitHub | Azure DevOps | Planner}
**Repository:** {owner}/{repo}
**Assigned to:** {member}
**Description:**
{issue body}
**Labels/Tags:**
{labels}
**Acceptance Criteria:**
{criteria if present in issue}
**Branch:** `squad/{issue-number}-{slug}`
**Your task:**
{specific directive to the agent}
**After completing work:**
1. Commit with message referencing issue number
2. Push branch
3. Open PR using:
```
gh pr create --title "{title}" --body "Closes #{number}\n\n{description}" --head squad/{issue-number}-{slug} --base {base-branch}
```
4. Report PR URL to coordinator
```
## Ralph's Role in Issue Lifecycle
Ralph (the work monitor) continuously checks issue and PR state:
1. **Triage:** Detects untriaged issues, assigns `squad:{member}` labels
2. **Spawn:** Launches agents for assigned issues
3. **Monitor:** Tracks PR state transitions (needsReview → changesRequested → readyToMerge)
4. **Merge:** Automatically merges approved PRs
5. **Cleanup:** Marks issues as done when PRs merge
**Ralph's work-check cycle:**
```
Scan → Categorize → Dispatch → Watch → Report → Loop
```
See `.squad/templates/ralph-reference.md` for Ralph's full lifecycle.
## PR Review Handling
### Automated Approval (CI-only projects)
If the project has no human reviewers configured:
1. PR opens
2. CI runs
3. If CI passes, Ralph auto-merges
4. Issue closes
### Human Review Required
If the project requires human approval:
1. PR opens
2. Human reviewer is notified (GitHub/ADO notifications)
3. Reviewer approves or requests changes
4. If approved + CI passes, Ralph merges
5. If changes requested, agent addresses feedback
### Squad Member Review
If the issue was assigned to a squad member and they authored the PR:
1. Another squad member reviews (conflict of interest avoidance)
2. Original author is locked out from re-working rejected code (rejection lockout)
3. Reviewer can approve edits or reject outright
## Common Issue Lifecycle Patterns
### Pattern 1: Quick Fix (Single Agent, No Review)
```
Issue created → Assigned to agent → Branch created → Code fixed →
PR opened → CI passes → Auto-merged → Issue closed
```
### Pattern 2: Feature Development (Human Review)
```
Issue created → Assigned to agent → Branch created → Feature implemented →
PR opened → Human reviews → Changes requested → Agent fixes →
Re-reviewed → Approved → Merged → Issue closed
```
### Pattern 3: Research-Then-Implement
```
Issue created → Labeled `go:needs-research` → Research agent spawned →
Research documented → Research PR merged → Implementation issue created →
Implementation agent spawned → Feature built → PR merged
```
### Pattern 4: Parallel Multi-Agent (Future, #525)
```
Epic issue created → Decomposed into sub-issues → Each sub-issue assigned →
Multiple agents work in parallel worktrees → PRs opened concurrently →
All PRs reviewed → All PRs merged → Epic closed
```
## Anti-Patterns
- ❌ Creating branches without linking to an issue
- ❌ Committing without issue reference in message
- ❌ Opening PRs without "Closes #{number}" in description
- ❌ Merging PRs before CI passes
- ❌ Leaving feature branches undeleted after merge
- ❌ Using `checkout -b` when parallel agents are active (causes working directory conflicts)
- ❌ Manually transitioning issue states — let the platform and Squad automation handle it
- ❌ Skipping the branch naming convention — breaks Ralph's tracking logic
## Migration Notes
**v0.8.x → v0.9.x (Worktree Support):**
- `checkout -b``git worktree add` for parallel agents
- Worktree cleanup added to post-merge flow
- `TEAM_ROOT` passing to agents to support worktree-aware state resolution
This template will be updated as worktree lifecycle support lands in #525.

View File

@@ -0,0 +1,164 @@
# KEDA External Scaler for GitHub Issue-Driven Agent Autoscaling
> Scale agent pods to zero when idle, up when work arrives — driven by GitHub Issues.
## Overview
When running Squad on Kubernetes, agent pods sit idle when no work exists. [KEDA](https://keda.sh) (Kubernetes Event-Driven Autoscaler) solves this for queue-based workloads, but GitHub Issues isn't a native KEDA trigger.
The `keda-copilot-scaler` is a KEDA External Scaler (gRPC) that bridges this gap:
1. Polls GitHub API for issues matching specific labels (e.g., `squad:copilot`)
2. Reports queue depth as a KEDA metric
3. Handles rate limits gracefully (Retry-After, exponential backoff)
4. Supports composite scaling decisions
## Quick Start
### Prerequisites
- Kubernetes cluster with KEDA v2.x installed
- GitHub personal access token (PAT) with `repo` scope
- Helm 3.x
### 1. Install the Scaler
```bash
helm install keda-copilot-scaler oci://ghcr.io/tamirdresher/keda-copilot-scaler \
--namespace squad-scaler --create-namespace \
--set github.owner=YOUR_ORG \
--set github.repo=YOUR_REPO \
--set github.token=YOUR_TOKEN
```
Or with Kustomize:
```bash
kubectl apply -k https://github.com/tamirdresher/keda-copilot-scaler/deploy/kustomize
```
### 2. Create a ScaledObject
```yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: picard-scaler
namespace: squad
spec:
scaleTargetRef:
name: picard-deployment
minReplicaCount: 0 # Scale to zero when idle
maxReplicaCount: 3
pollingInterval: 30 # Check every 30 seconds
cooldownPeriod: 300 # Wait 5 minutes before scaling down
triggers:
- type: external
metadata:
scalerAddress: keda-copilot-scaler.squad-scaler.svc.cluster.local:6000
owner: your-org
repo: your-repo
labels: squad:copilot # Only count issues with this label
threshold: "1" # Scale up when >= 1 issue exists
```
### 3. Verify
```bash
# Check the scaler is running
kubectl get pods -n squad-scaler
# Check ScaledObject status
kubectl get scaledobject picard-scaler -n squad
# Watch scaling events
kubectl get events -n squad --watch
```
## Scaling Behavior
| Open Issues | Target Replicas | Behavior |
|------------|----------------|----------|
| 0 | 0 | Scale to zero — save resources |
| 13 | 1 | Single agent handles work |
| 410 | 2 | Scale up for parallel processing |
| 10+ | 3 (max) | Maximum parallelism |
The threshold and max replicas are configurable per ScaledObject.
## Rate Limit Awareness
The scaler tracks GitHub API rate limits:
- Reads `X-RateLimit-Remaining` from API responses
- Backs off when quota is low (< 100 remaining)
- Reports rate limit metrics as secondary KEDA triggers
- Never exhausts API quota from polling
## Integration with Squad
### Machine Capabilities (#514)
Combine with machine capability labels for intelligent scheduling:
```yaml
# Only scale pods on GPU-capable nodes
spec:
template:
spec:
nodeSelector:
node.squad.dev/gpu: "true"
triggers:
- type: external
metadata:
labels: squad:copilot,needs:gpu
```
### Cooperative Rate Limiting (#515)
The scaler exposes rate limit metrics that feed into the cooperative rate limiting system:
- Current `X-RateLimit-Remaining` value
- Predicted time to exhaustion (from predictive circuit breaker)
- Can return 0 target replicas when rate limited → pods scale to zero
## Architecture
```
GitHub API KEDA Kubernetes
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ Issues │◄── poll ──►│ Scaler │──metrics─►│ HPA / KEDA │
│ (REST) │ │ (gRPC) │ │ Controller │
└──────────┘ └──────────┘ └──────┬───────┘
scale up/down
┌──────▼───────┐
│ Agent Pods │
│ (0N replicas)│
└──────────────┘
```
## Configuration Reference
| Parameter | Default | Description |
|-----------|---------|-------------|
| `github.owner` | — | Repository owner |
| `github.repo` | — | Repository name |
| `github.token` | — | GitHub PAT with `repo` scope |
| `github.labels` | `squad:copilot` | Comma-separated label filter |
| `scaler.port` | `6000` | gRPC server port |
| `scaler.pollInterval` | `30s` | GitHub API polling interval |
| `scaler.rateLimitThreshold` | `100` | Stop polling below this remaining |
## Source & Contributing
- **Repository:** [tamirdresher/keda-copilot-scaler](https://github.com/tamirdresher/keda-copilot-scaler)
- **License:** MIT
- **Language:** Go
- **Tests:** 51 passing (unit + integration)
- **CI:** GitHub Actions
The scaler is maintained as a standalone project. PRs and issues welcome.
## References
- [KEDA External Scalers](https://keda.sh/docs/latest/concepts/external-scalers/) — KEDA documentation
- [Squad on AKS](https://github.com/tamirdresher/squad-on-aks) — Full Kubernetes deployment example
- [Machine Capabilities](machine-capabilities.md) — Capability-based routing (#514)
- [Cooperative Rate Limiting](cooperative-rate-limiting.md) — Multi-agent rate management (#515)

View File

@@ -0,0 +1,75 @@
# Machine Capability Discovery & Label-Based Routing
> Enable Ralph to skip issues requiring capabilities the current machine lacks.
## Overview
When running Squad across multiple machines (laptops, DevBoxes, GPU servers, Kubernetes nodes), each machine has different tooling. The capability system lets you declare what each machine can do, and Ralph automatically routes work accordingly.
## Setup
### 1. Create a Capabilities Manifest
Create `~/.squad/machine-capabilities.json` (user-wide) or `.squad/machine-capabilities.json` (project-local):
```json
{
"machine": "MY-LAPTOP",
"capabilities": ["browser", "personal-gh", "onedrive"],
"missing": ["gpu", "docker", "azure-speech"],
"lastUpdated": "2026-03-22T00:00:00Z"
}
```
### 2. Label Issues with Requirements
Add `needs:*` labels to issues that require specific capabilities:
| Label | Meaning |
|-------|---------|
| `needs:browser` | Requires Playwright / browser automation |
| `needs:gpu` | Requires NVIDIA GPU |
| `needs:personal-gh` | Requires personal GitHub account |
| `needs:emu-gh` | Requires Enterprise Managed User account |
| `needs:azure-cli` | Requires authenticated Azure CLI |
| `needs:docker` | Requires Docker daemon |
| `needs:onedrive` | Requires OneDrive sync |
| `needs:teams-mcp` | Requires Teams MCP tools |
Custom capabilities are supported — any `needs:X` label works if `X` is in the machine's `capabilities` array.
### 3. Run Ralph
```bash
squad watch --interval 5
```
Ralph will log skipped issues:
```
⏭️ Skipping #42 "Train ML model" — missing: gpu
✓ Triaged #43 "Fix CSS layout" → Picard (routing-rule)
```
## How It Works
1. Ralph loads `machine-capabilities.json` at startup
2. For each open issue, Ralph extracts `needs:*` labels
3. If any required capability is missing, the issue is skipped
4. Issues without `needs:*` labels are always processed (opt-in system)
## Kubernetes Integration
On Kubernetes, machine capabilities map to node labels:
```yaml
# Node labels (set by capability DaemonSet or manually)
node.squad.dev/gpu: "true"
node.squad.dev/browser: "true"
# Pod spec uses nodeSelector
spec:
nodeSelector:
node.squad.dev/gpu: "true"
```
A DaemonSet can run capability discovery on each node and maintain labels automatically. See the [squad-on-aks](https://github.com/tamirdresher/squad-on-aks) project for a complete Kubernetes deployment example.

View File

@@ -0,0 +1,90 @@
# MCP Integration — Configuration and Samples
MCP (Model Context Protocol) servers extend Squad with tools for external services — Trello, Aspire dashboards, Azure, Notion, and more. The user configures MCP servers in their environment; Squad discovers and uses them.
> **Full patterns:** Read `.squad/skills/mcp-tool-discovery/SKILL.md` for discovery patterns, domain-specific usage, and graceful degradation.
## Config File Locations
Users configure MCP servers at these locations (checked in priority order):
1. **Repository-level:** `.copilot/mcp-config.json` (team-shared, committed to repo)
2. **Workspace-level:** `.vscode/mcp.json` (VS Code workspaces)
3. **User-level:** `~/.copilot/mcp-config.json` (personal)
4. **CLI override:** `--additional-mcp-config` flag (session-specific)
## Sample Config — Trello
```json
{
"mcpServers": {
"trello": {
"command": "npx",
"args": ["-y", "@trello/mcp-server"],
"env": {
"TRELLO_API_KEY": "${TRELLO_API_KEY}",
"TRELLO_TOKEN": "${TRELLO_TOKEN}"
}
}
}
}
```
## Sample Config — GitHub
```json
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
```
## Sample Config — Azure
```json
{
"mcpServers": {
"azure": {
"command": "npx",
"args": ["-y", "@azure/mcp-server"],
"env": {
"AZURE_SUBSCRIPTION_ID": "${AZURE_SUBSCRIPTION_ID}",
"AZURE_CLIENT_ID": "${AZURE_CLIENT_ID}",
"AZURE_CLIENT_SECRET": "${AZURE_CLIENT_SECRET}",
"AZURE_TENANT_ID": "${AZURE_TENANT_ID}"
}
}
}
}
```
## Sample Config — Aspire
```json
{
"mcpServers": {
"aspire": {
"command": "npx",
"args": ["-y", "@aspire/mcp-server"],
"env": {
"ASPIRE_DASHBOARD_URL": "${ASPIRE_DASHBOARD_URL}"
}
}
}
}
```
## Authentication Notes
- **GitHub MCP requires a separate token** from the `gh` CLI auth. Generate at https://github.com/settings/tokens
- **Trello requires API key + token** from https://trello.com/power-ups/admin
- **Azure requires service principal credentials** — see Azure docs for setup
- **Aspire uses the dashboard URL** — typically `http://localhost:18888` during local dev
Auth is a real blocker for some MCP servers. Users need separate tokens for GitHub MCP, Azure MCP, Trello MCP, etc. This is a documentation problem, not a code problem.

View File

@@ -0,0 +1,28 @@
# Multi-Agent Artifact Format
When multiple agents contribute to a final artifact (document, analysis, design), use this format. The assembled result must include:
- Termination condition
- Constraint budgets (if active)
- Reviewer verdicts (if any)
- Raw agent outputs appendix
## Assembly Structure
The assembled result goes at the top. Below it, include:
```
## APPENDIX: RAW AGENT OUTPUTS
### {Name} ({Role}) — Raw Output
{Paste agent's verbatim response here, unedited}
### {Name} ({Role}) — Raw Output
{Paste agent's verbatim response here, unedited}
```
## Appendix Rules
This appendix is for diagnostic integrity. Do not edit, summarize, or polish the raw outputs. The Coordinator may not rewrite raw agent outputs; it may only paste them verbatim and assemble the final artifact above.
See `.squad/templates/run-output.md` for the complete output format template.

View File

@@ -0,0 +1,27 @@
# Orchestration Log Entry
> One file per agent spawn. Saved to `.squad/orchestration-log/{timestamp}-{agent-name}.md`
---
### {timestamp} — {task summary}
| Field | Value |
|-------|-------|
| **Agent routed** | {Name} ({Role}) |
| **Why chosen** | {Routing rationale — what in the request matched this agent} |
| **Mode** | {`background` / `sync`} |
| **Why this mode** | {Brief reason — e.g., "No hard data dependencies" or "User needs to approve architecture"} |
| **Files authorized to read** | {Exact file paths the agent was told to read} |
| **File(s) agent must produce** | {Exact file paths the agent is expected to create or modify} |
| **Outcome** | {Completed / Rejected by {Reviewer} / Escalated} |
---
## Rules
1. **One file per agent spawn.** Named `{timestamp}-{agent-name}.md`.
2. **Log BEFORE spawning.** The entry must exist before the agent runs.
3. **Update outcome AFTER the agent completes.** Fill in the Outcome field.
4. **Never delete or edit past entries.** Append-only.
5. **If a reviewer rejects work,** log the rejection as a new entry with the revision agent.

View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

View File

@@ -0,0 +1,49 @@
# Plugin Marketplace
Plugins are curated agent templates, skills, instructions, and prompts shared by the community via GitHub repositories (e.g., `github/awesome-copilot`, `anthropics/skills`). They provide ready-made expertise for common domains — cloud platforms, frameworks, testing strategies, etc.
## Marketplace State
Registered marketplace sources are stored in `.squad/plugins/marketplaces.json`:
```json
{
"marketplaces": [
{
"name": "awesome-copilot",
"source": "github/awesome-copilot",
"added_at": "2026-02-14T00:00:00Z"
}
]
}
```
## CLI Commands
Users manage marketplaces via the CLI:
- `squad plugin marketplace add {owner/repo}` — Register a GitHub repo as a marketplace source
- `squad plugin marketplace remove {name}` — Remove a registered marketplace
- `squad plugin marketplace list` — List registered marketplaces
- `squad plugin marketplace browse {name}` — List available plugins in a marketplace
## When to Browse
During the **Adding Team Members** flow, AFTER allocating a name but BEFORE generating the charter:
1. Read `.squad/plugins/marketplaces.json`. If the file doesn't exist or `marketplaces` is empty, skip silently.
2. For each registered marketplace, search for plugins whose name or description matches the new member's role or domain keywords.
3. Present matching plugins to the user: *"Found '{plugin-name}' in {marketplace} marketplace — want me to install it as a skill for {CastName}?"*
4. If the user accepts, install the plugin (see below). If they decline or skip, proceed without it.
## How to Install a Plugin
1. Read the plugin content from the marketplace repository (the plugin's `SKILL.md` or equivalent).
2. Copy it into the agent's skills directory: `.squad/skills/{plugin-name}/SKILL.md`
3. If the plugin includes charter-level instructions (role boundaries, tool preferences), merge those into the agent's `charter.md`.
4. Log the installation in the agent's `history.md`: *"📦 Plugin '{plugin-name}' installed from {marketplace}."*
## Graceful Degradation
- **No marketplaces configured:** Skip the marketplace check entirely. No warning, no prompt.
- **Marketplace unreachable:** Warn the user (*"⚠ Couldn't reach {marketplace} — continuing without it"*) and proceed with team member creation normally.
- **No matching plugins:** Inform the user (*"No matching plugins found in configured marketplaces"*) and proceed.

View File

@@ -0,0 +1,313 @@
# Ralph Circuit Breaker — Model Rate Limit Fallback
> Classic circuit breaker pattern (Hystrix / Polly / Resilience4j) applied to Copilot model selection.
> When the preferred model hits rate limits, Ralph automatically degrades to free-tier models, then self-heals.
## Problem
When running multiple Ralph instances across repos, Copilot model rate limits cause cascading failures.
All Ralphs fail simultaneously when the preferred model (e.g., `claude-sonnet-4.6`) hits quota.
Premium models burn quota fast:
| Model | Multiplier | Risk |
|-------|-----------|------|
| `claude-sonnet-4.6` | 1x | Moderate with many Ralphs |
| `claude-opus-4.6` | 10x | High |
| `gpt-5.4` | 50x | Very high |
| `gpt-5.4-mini` | **0x** | **Free — unlimited** |
| `gpt-5-mini` | **0x** | **Free — unlimited** |
| `gpt-4.1` | **0x** | **Free — unlimited** |
## Circuit Breaker States
```
┌─────────┐ rate limit error ┌────────┐
│ CLOSED │ ───────────────────► │ OPEN │
│ (normal)│ │(fallback)│
└────┬────┘ ◄──────────────── └────┬────┘
│ 2 consecutive │
│ successes │ cooldown expires
│ ▼
│ ┌──────────┐
└───── success ◄──────── │HALF-OPEN │
(close) │ (testing) │
└──────────┘
```
### CLOSED (normal operation)
- Use preferred model from config
- Every successful response confirms circuit stays closed
- On rate limit error → transition to OPEN
### OPEN (rate limited — fallback active)
- Fall back through the free-tier model chain:
1. `gpt-5.4-mini`
2. `gpt-5-mini`
3. `gpt-4.1`
- Start cooldown timer (default: 10 minutes)
- When cooldown expires → transition to HALF-OPEN
### HALF-OPEN (testing recovery)
- Try preferred model again
- If 2 consecutive successes → transition to CLOSED
- If rate limit error → back to OPEN, reset cooldown
## State File: `.squad/ralph-circuit-breaker.json`
```json
{
"state": "closed",
"preferredModel": "claude-sonnet-4.6",
"fallbackChain": ["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"],
"currentFallbackIndex": 0,
"cooldownMinutes": 10,
"openedAt": null,
"halfOpenSuccesses": 0,
"consecutiveFailures": 0,
"metrics": {
"totalFallbacks": 0,
"totalRecoveries": 0,
"lastFallbackAt": null,
"lastRecoveryAt": null
}
}
```
## PowerShell Functions
Paste these into your `ralph-watch.ps1` or source them from a shared module.
### `Get-CircuitBreakerState`
```powershell
function Get-CircuitBreakerState {
param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
if (-not (Test-Path $StateFile)) {
$default = @{
state = "closed"
preferredModel = "claude-sonnet-4.6"
fallbackChain = @("gpt-5.4-mini", "gpt-5-mini", "gpt-4.1")
currentFallbackIndex = 0
cooldownMinutes = 10
openedAt = $null
halfOpenSuccesses = 0
consecutiveFailures = 0
metrics = @{
totalFallbacks = 0
totalRecoveries = 0
lastFallbackAt = $null
lastRecoveryAt = $null
}
}
$default | ConvertTo-Json -Depth 3 | Set-Content $StateFile
return $default
}
return (Get-Content $StateFile -Raw | ConvertFrom-Json)
}
```
### `Save-CircuitBreakerState`
```powershell
function Save-CircuitBreakerState {
param(
[object]$State,
[string]$StateFile = ".squad/ralph-circuit-breaker.json"
)
$State | ConvertTo-Json -Depth 3 | Set-Content $StateFile
}
```
### `Get-CurrentModel`
Returns the model Ralph should use right now, based on circuit state.
```powershell
function Get-CurrentModel {
param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
$cb = Get-CircuitBreakerState -StateFile $StateFile
switch ($cb.state) {
"closed" {
return $cb.preferredModel
}
"open" {
# Check if cooldown has expired
if ($cb.openedAt) {
$opened = [DateTime]::Parse($cb.openedAt)
$elapsed = (Get-Date) - $opened
if ($elapsed.TotalMinutes -ge $cb.cooldownMinutes) {
# Transition to half-open
$cb.state = "half-open"
$cb.halfOpenSuccesses = 0
Save-CircuitBreakerState -State $cb -StateFile $StateFile
Write-Host " [circuit-breaker] Cooldown expired. Testing preferred model..." -ForegroundColor Yellow
return $cb.preferredModel
}
}
# Still in cooldown — use fallback
$idx = [Math]::Min($cb.currentFallbackIndex, $cb.fallbackChain.Count - 1)
return $cb.fallbackChain[$idx]
}
"half-open" {
return $cb.preferredModel
}
default {
return $cb.preferredModel
}
}
}
```
### `Update-CircuitBreakerOnSuccess`
Call after every successful model response.
```powershell
function Update-CircuitBreakerOnSuccess {
param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
$cb = Get-CircuitBreakerState -StateFile $StateFile
$cb.consecutiveFailures = 0
if ($cb.state -eq "half-open") {
$cb.halfOpenSuccesses++
if ($cb.halfOpenSuccesses -ge 2) {
# Recovery! Close the circuit
$cb.state = "closed"
$cb.openedAt = $null
$cb.halfOpenSuccesses = 0
$cb.currentFallbackIndex = 0
$cb.metrics.totalRecoveries++
$cb.metrics.lastRecoveryAt = (Get-Date).ToString("o")
Save-CircuitBreakerState -State $cb -StateFile $StateFile
Write-Host " [circuit-breaker] RECOVERED — back to preferred model ($($cb.preferredModel))" -ForegroundColor Green
return
}
Save-CircuitBreakerState -State $cb -StateFile $StateFile
Write-Host " [circuit-breaker] Half-open success $($cb.halfOpenSuccesses)/2" -ForegroundColor Yellow
return
}
# closed state — nothing to do
}
```
### `Update-CircuitBreakerOnRateLimit`
Call when a model response indicates rate limiting (HTTP 429 or error message containing "rate limit").
```powershell
function Update-CircuitBreakerOnRateLimit {
param([string]$StateFile = ".squad/ralph-circuit-breaker.json")
$cb = Get-CircuitBreakerState -StateFile $StateFile
$cb.consecutiveFailures++
if ($cb.state -eq "closed" -or $cb.state -eq "half-open") {
# Open the circuit
$cb.state = "open"
$cb.openedAt = (Get-Date).ToString("o")
$cb.halfOpenSuccesses = 0
$cb.currentFallbackIndex = 0
$cb.metrics.totalFallbacks++
$cb.metrics.lastFallbackAt = (Get-Date).ToString("o")
Save-CircuitBreakerState -State $cb -StateFile $StateFile
$fallbackModel = $cb.fallbackChain[0]
Write-Host " [circuit-breaker] RATE LIMITED — falling back to $fallbackModel (cooldown: $($cb.cooldownMinutes)m)" -ForegroundColor Red
return
}
if ($cb.state -eq "open") {
# Already open — try next fallback in chain if current one also fails
if ($cb.currentFallbackIndex -lt ($cb.fallbackChain.Count - 1)) {
$cb.currentFallbackIndex++
$nextModel = $cb.fallbackChain[$cb.currentFallbackIndex]
Write-Host " [circuit-breaker] Fallback also limited — trying $nextModel" -ForegroundColor Red
}
# Reset cooldown timer
$cb.openedAt = (Get-Date).ToString("o")
Save-CircuitBreakerState -State $cb -StateFile $StateFile
}
}
```
## Integration with ralph-watch.ps1
In your Ralph polling loop, wrap the model selection:
```powershell
# At the top of your polling loop
$model = Get-CurrentModel
# When invoking copilot CLI
$result = copilot-cli --model $model ...
# After the call
if ($result -match "rate.?limit" -or $LASTEXITCODE -eq 429) {
Update-CircuitBreakerOnRateLimit
} else {
Update-CircuitBreakerOnSuccess
}
```
### Full integration example
```powershell
# Source the circuit breaker functions
. .squad-templates/ralph-circuit-breaker-functions.ps1
while ($true) {
$model = Get-CurrentModel
Write-Host "Polling with model: $model"
try {
# Your existing Ralph logic here, but pass $model
$response = Invoke-RalphCycle -Model $model
# Success path
Update-CircuitBreakerOnSuccess
}
catch {
if ($_.Exception.Message -match "rate.?limit|429|quota|Too Many Requests") {
Update-CircuitBreakerOnRateLimit
# Retry immediately with fallback model
continue
}
# Other errors — handle normally
throw
}
Start-Sleep -Seconds $pollInterval
}
```
## Configuration
Override defaults by editing `.squad/ralph-circuit-breaker.json`:
| Field | Default | Description |
|-------|---------|-------------|
| `preferredModel` | `claude-sonnet-4.6` | Model to use when circuit is closed |
| `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"]` | Ordered fallback models (all free-tier) |
| `cooldownMinutes` | `10` | How long to wait before testing recovery |
## Metrics
The state file tracks operational metrics:
- **totalFallbacks** — How many times the circuit opened
- **totalRecoveries** — How many times it recovered to preferred model
- **lastFallbackAt** — ISO timestamp of last rate limit event
- **lastRecoveryAt** — ISO timestamp of last successful recovery
Query metrics with:
```powershell
$cb = Get-Content .squad/ralph-circuit-breaker.json | ConvertFrom-Json
Write-Host "Fallbacks: $($cb.metrics.totalFallbacks) | Recoveries: $($cb.metrics.totalRecoveries)"
```

View File

@@ -0,0 +1,543 @@
#!/usr/bin/env node
/**
* Ralph Triage Script — Standalone CJS implementation
*
* ⚠️ SYNC NOTICE: This file ports triage logic from the SDK source:
* packages/squad-sdk/src/ralph/triage.ts
*
* Any changes to routing/triage logic MUST be applied to BOTH files.
* The SDK module is the canonical implementation; this script exists
* for zero-dependency use in GitHub Actions workflows.
*
* To verify parity: npm test -- test/ralph-triage.test.ts
*/
'use strict';
const fs = require('node:fs');
const path = require('node:path');
const https = require('node:https');
const { execSync } = require('node:child_process');
function parseArgs(argv) {
let squadDir = '.squad';
let output = 'triage-results.json';
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
if (arg === '--squad-dir') {
squadDir = argv[i + 1];
i += 1;
continue;
}
if (arg === '--output') {
output = argv[i + 1];
i += 1;
continue;
}
if (arg === '--help' || arg === '-h') {
printUsage();
process.exit(0);
}
throw new Error(`Unknown argument: ${arg}`);
}
if (!squadDir) throw new Error('--squad-dir requires a value');
if (!output) throw new Error('--output requires a value');
return { squadDir, output };
}
function printUsage() {
console.log('Usage: node .squad/templates/ralph-triage.js --squad-dir .squad --output triage-results.json');
}
function normalizeEol(content) {
return content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
}
function parseRoutingRules(routingMd) {
const table = parseTableSection(routingMd, /^##\s*work\s*type\s*(?:→|->)\s*agent\b/i);
if (!table) return [];
const workTypeIndex = findColumnIndex(table.headers, ['work type', 'type']);
const agentIndex = findColumnIndex(table.headers, ['agent', 'route to', 'route']);
const examplesIndex = findColumnIndex(table.headers, ['examples', 'example']);
if (workTypeIndex < 0 || agentIndex < 0) return [];
const rules = [];
for (const row of table.rows) {
const workType = cleanCell(row[workTypeIndex] || '');
const agentName = cleanCell(row[agentIndex] || '');
const keywords = splitKeywords(examplesIndex >= 0 ? row[examplesIndex] : '');
if (!workType || !agentName) continue;
rules.push({ workType, agentName, keywords });
}
return rules;
}
function parseModuleOwnership(routingMd) {
const table = parseTableSection(routingMd, /^##\s*module\s*ownership\b/i);
if (!table) return [];
const moduleIndex = findColumnIndex(table.headers, ['module', 'path']);
const primaryIndex = findColumnIndex(table.headers, ['primary']);
const secondaryIndex = findColumnIndex(table.headers, ['secondary']);
if (moduleIndex < 0 || primaryIndex < 0) return [];
const modules = [];
for (const row of table.rows) {
const modulePath = normalizeModulePath(row[moduleIndex] || '');
const primary = cleanCell(row[primaryIndex] || '');
const secondaryRaw = cleanCell(secondaryIndex >= 0 ? row[secondaryIndex] || '' : '');
const secondary = normalizeOptionalOwner(secondaryRaw);
if (!modulePath || !primary) continue;
modules.push({ modulePath, primary, secondary });
}
return modules;
}
function parseRoster(teamMd) {
const table =
parseTableSection(teamMd, /^##\s*members\b/i) ||
parseTableSection(teamMd, /^##\s*team\s*roster\b/i);
if (!table) return [];
const nameIndex = findColumnIndex(table.headers, ['name']);
const roleIndex = findColumnIndex(table.headers, ['role']);
if (nameIndex < 0 || roleIndex < 0) return [];
const excluded = new Set(['scribe', 'ralph']);
const members = [];
for (const row of table.rows) {
const name = cleanCell(row[nameIndex] || '');
const role = cleanCell(row[roleIndex] || '');
if (!name || !role) continue;
if (excluded.has(name.toLowerCase())) continue;
members.push({
name,
role,
label: `squad:${name.toLowerCase()}`,
});
}
return members;
}
function triageIssue(issue, rules, modules, roster) {
const issueText = `${issue.title}\n${issue.body || ''}`.toLowerCase();
const normalizedIssueText = normalizeTextForPathMatch(issueText);
const bestModule = findBestModuleMatch(normalizedIssueText, modules);
if (bestModule) {
const primaryMember = findMember(bestModule.primary, roster);
if (primaryMember) {
return {
agent: primaryMember,
reason: `Matched module path "${bestModule.modulePath}" to primary owner "${bestModule.primary}"`,
source: 'module-ownership',
confidence: 'high',
};
}
if (bestModule.secondary) {
const secondaryMember = findMember(bestModule.secondary, roster);
if (secondaryMember) {
return {
agent: secondaryMember,
reason: `Matched module path "${bestModule.modulePath}" to secondary owner "${bestModule.secondary}"`,
source: 'module-ownership',
confidence: 'medium',
};
}
}
}
const bestRule = findBestRuleMatch(issueText, rules);
if (bestRule) {
const agent = findMember(bestRule.rule.agentName, roster);
if (agent) {
return {
agent,
reason: `Matched routing keyword(s): ${bestRule.matchedKeywords.join(', ')}`,
source: 'routing-rule',
confidence: bestRule.matchedKeywords.length >= 2 ? 'high' : 'medium',
};
}
}
const roleMatch = findRoleKeywordMatch(issueText, roster);
if (roleMatch) {
return {
agent: roleMatch.agent,
reason: roleMatch.reason,
source: 'role-keyword',
confidence: 'medium',
};
}
const lead = findLeadFallback(roster);
if (!lead) return null;
return {
agent: lead,
reason: 'No module, routing, or role keyword match — routed to Lead/Architect',
source: 'lead-fallback',
confidence: 'low',
};
}
function parseTableSection(markdown, sectionHeader) {
const lines = normalizeEol(markdown).split('\n');
let inSection = false;
const tableLines = [];
for (const line of lines) {
const trimmed = line.trim();
if (!inSection && sectionHeader.test(trimmed)) {
inSection = true;
continue;
}
if (inSection && /^##\s+/.test(trimmed)) break;
if (inSection && trimmed.startsWith('|')) tableLines.push(trimmed);
}
if (tableLines.length === 0) return null;
let headers = null;
const rows = [];
for (const line of tableLines) {
const cells = parseTableLine(line);
if (cells.length === 0) continue;
if (cells.every((cell) => /^:?-{2,}:?$/.test(cell))) continue;
if (!headers) {
headers = cells;
continue;
}
rows.push(cells);
}
if (!headers) return null;
return { headers, rows };
}
function parseTableLine(line) {
return line
.replace(/^\|/, '')
.replace(/\|$/, '')
.split('|')
.map((cell) => cell.trim());
}
function findColumnIndex(headers, candidates) {
const normalizedHeaders = headers.map((header) => cleanCell(header).toLowerCase());
for (const candidate of candidates) {
const index = normalizedHeaders.findIndex((header) => header.includes(candidate));
if (index >= 0) return index;
}
return -1;
}
function cleanCell(value) {
return value
.replace(/`/g, '')
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
.trim();
}
function splitKeywords(examplesCell) {
if (!examplesCell) return [];
return examplesCell
.split(',')
.map((keyword) => cleanCell(keyword))
.filter((keyword) => keyword.length > 0);
}
function normalizeOptionalOwner(owner) {
if (!owner) return null;
if (/^[-—–]+$/.test(owner)) return null;
return owner;
}
function normalizeModulePath(modulePath) {
return cleanCell(modulePath).replace(/\\/g, '/').toLowerCase();
}
function normalizeTextForPathMatch(text) {
return text.replace(/\\/g, '/').replace(/`/g, '');
}
function normalizeName(value) {
return cleanCell(value)
.toLowerCase()
.replace(/[^\w@\s-]/g, '')
.replace(/\s+/g, ' ')
.trim();
}
function findMember(target, roster) {
const normalizedTarget = normalizeName(target);
if (!normalizedTarget) return null;
for (const member of roster) {
if (normalizeName(member.name) === normalizedTarget) return member;
}
for (const member of roster) {
if (normalizeName(member.role) === normalizedTarget) return member;
}
for (const member of roster) {
const memberName = normalizeName(member.name);
if (normalizedTarget.includes(memberName) || memberName.includes(normalizedTarget)) {
return member;
}
}
for (const member of roster) {
const memberRole = normalizeName(member.role);
if (normalizedTarget.includes(memberRole) || memberRole.includes(normalizedTarget)) {
return member;
}
}
return null;
}
function findBestModuleMatch(issueText, modules) {
let best = null;
let bestLength = -1;
for (const module of modules) {
const modulePath = normalizeModulePath(module.modulePath);
if (!modulePath) continue;
if (!issueText.includes(modulePath)) continue;
if (modulePath.length > bestLength) {
best = module;
bestLength = modulePath.length;
}
}
return best;
}
function findBestRuleMatch(issueText, rules) {
let best = null;
let bestScore = 0;
for (const rule of rules) {
const matchedKeywords = rule.keywords
.map((keyword) => keyword.toLowerCase())
.filter((keyword) => keyword.length > 0 && issueText.includes(keyword));
if (matchedKeywords.length === 0) continue;
const score =
matchedKeywords.length * 100 + matchedKeywords.reduce((sum, keyword) => sum + keyword.length, 0);
if (score > bestScore) {
best = { rule, matchedKeywords };
bestScore = score;
}
}
return best;
}
function findRoleKeywordMatch(issueText, roster) {
for (const member of roster) {
const role = member.role.toLowerCase();
if (
(role.includes('frontend') || role.includes('ui')) &&
(issueText.includes('ui') || issueText.includes('frontend') || issueText.includes('css'))
) {
return { agent: member, reason: 'Matched frontend/UI role keywords' };
}
if (
(role.includes('backend') || role.includes('api') || role.includes('server')) &&
(issueText.includes('api') || issueText.includes('backend') || issueText.includes('database'))
) {
return { agent: member, reason: 'Matched backend/API role keywords' };
}
if (
(role.includes('test') || role.includes('qa')) &&
(issueText.includes('test') || issueText.includes('bug') || issueText.includes('fix'))
) {
return { agent: member, reason: 'Matched testing/QA role keywords' };
}
}
return null;
}
function findLeadFallback(roster) {
return (
roster.find((member) => {
const role = member.role.toLowerCase();
return role.includes('lead') || role.includes('architect');
}) || null
);
}
function parseOwnerRepoFromRemote(remoteUrl) {
const sshMatch = remoteUrl.match(/^git@[^:]+:([^/]+)\/(.+?)(?:\.git)?$/);
if (sshMatch) return { owner: sshMatch[1], repo: sshMatch[2] };
if (remoteUrl.startsWith('http://') || remoteUrl.startsWith('https://') || remoteUrl.startsWith('ssh://')) {
const parsed = new URL(remoteUrl);
const parts = parsed.pathname.replace(/^\/+/, '').replace(/\.git$/, '').split('/');
if (parts.length >= 2) {
return { owner: parts[0], repo: parts[1] };
}
}
throw new Error(`Unable to parse owner/repo from remote URL: ${remoteUrl}`);
}
function getOwnerRepoFromGit() {
const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf8' }).trim();
return parseOwnerRepoFromRemote(remoteUrl);
}
function githubRequestJson(pathname, token) {
return new Promise((resolve, reject) => {
const req = https.request(
{
hostname: 'api.github.com',
method: 'GET',
path: pathname,
headers: {
Accept: 'application/vnd.github+json',
Authorization: `Bearer ${token}`,
'User-Agent': 'squad-ralph-triage',
'X-GitHub-Api-Version': '2022-11-28',
},
},
(res) => {
let body = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
if ((res.statusCode || 500) >= 400) {
reject(new Error(`GitHub API ${res.statusCode}: ${body}`));
return;
}
try {
resolve(JSON.parse(body));
} catch (error) {
reject(new Error(`Failed to parse GitHub response: ${error.message}`));
}
});
},
);
req.on('error', reject);
req.end();
});
}
async function fetchSquadIssues(owner, repo, token) {
const all = [];
let page = 1;
const perPage = 100;
for (;;) {
const query = new URLSearchParams({
state: 'open',
labels: 'squad',
per_page: String(perPage),
page: String(page),
});
const issues = await githubRequestJson(`/repos/${owner}/${repo}/issues?${query.toString()}`, token);
if (!Array.isArray(issues) || issues.length === 0) break;
all.push(...issues);
if (issues.length < perPage) break;
page += 1;
}
return all;
}
function issueHasLabel(issue, labelName) {
const target = labelName.toLowerCase();
return (issue.labels || []).some((label) => {
if (!label) return false;
const name = typeof label === 'string' ? label : label.name;
return typeof name === 'string' && name.toLowerCase() === target;
});
}
function isUntriagedIssue(issue, memberLabels) {
if (issue.pull_request) return false;
if (!issueHasLabel(issue, 'squad')) return false;
return !memberLabels.some((label) => issueHasLabel(issue, label));
}
async function main() {
const args = parseArgs(process.argv.slice(2));
const token = process.env.GITHUB_TOKEN;
if (!token) {
throw new Error('GITHUB_TOKEN is required');
}
const squadDir = path.resolve(process.cwd(), args.squadDir);
const teamMd = fs.readFileSync(path.join(squadDir, 'team.md'), 'utf8');
const routingMd = fs.readFileSync(path.join(squadDir, 'routing.md'), 'utf8');
const roster = parseRoster(teamMd);
const rules = parseRoutingRules(routingMd);
const modules = parseModuleOwnership(routingMd);
const { owner, repo } = getOwnerRepoFromGit();
const openSquadIssues = await fetchSquadIssues(owner, repo, token);
const memberLabels = roster.map((member) => member.label);
const untriaged = openSquadIssues.filter((issue) => isUntriagedIssue(issue, memberLabels));
const results = [];
for (const issue of untriaged) {
const decision = triageIssue(
{
number: issue.number,
title: issue.title || '',
body: issue.body || '',
labels: [],
},
rules,
modules,
roster,
);
if (!decision) continue;
results.push({
issueNumber: issue.number,
assignTo: decision.agent.name,
label: decision.agent.label,
reason: decision.reason,
source: decision.source,
});
}
const outputPath = path.resolve(process.cwd(), args.output);
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, `${JSON.stringify(results, null, 2)}\n`, 'utf8');
}
main().catch((error) => {
console.error(error.message);
process.exit(1);
});

View File

@@ -0,0 +1,37 @@
# Raw Agent Output — Appendix Format
> This template defines the format for the `## APPENDIX: RAW AGENT OUTPUTS` section
> in any multi-agent artifact.
## Rules
1. **Verbatim only.** Paste the agent's response exactly as returned. No edits.
2. **No summarizing.** Do not condense, paraphrase, or rephrase any part of the output.
3. **No rewriting.** Do not fix typos, grammar, formatting, or style.
4. **No code fences around the entire output.** The raw output is pasted as-is, not wrapped in ``` blocks.
5. **One section per agent.** Each agent that contributed gets its own heading.
6. **Order matches work order.** List agents in the order they were spawned.
7. **Include all outputs.** Even if an agent's work was rejected, include their output for diagnostic traceability.
## Format
```markdown
## APPENDIX: RAW AGENT OUTPUTS
### {Name} ({Role}) — Raw Output
{Paste agent's verbatim response here, unedited}
### {Name} ({Role}) — Raw Output
{Paste agent's verbatim response here, unedited}
```
## Why This Exists
The appendix provides diagnostic integrity. It lets anyone verify:
- What each agent actually said (vs. what the Coordinator assembled)
- Whether the Coordinator faithfully represented agent work
- What was lost or changed in synthesis
Without raw outputs, multi-agent collaboration is unauditable.

View File

@@ -0,0 +1,60 @@
# Team Roster
> {One-line project description}
## Coordinator
| Name | Role | Notes |
|------|------|-------|
| Squad | Coordinator | Routes work, enforces handoffs and reviewer gates. Does not generate domain artifacts. |
## Members
| Name | Role | Charter | Status |
|------|------|---------|--------|
| {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active |
| {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active |
| {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active |
| {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active |
| Scribe | Session Logger | `.squad/agents/scribe/charter.md` | 📋 Silent |
| Ralph | Work Monitor | — | 🔄 Monitor |
## Coding Agent
<!-- copilot-auto-assign: false -->
| Name | Role | Charter | Status |
|------|------|---------|--------|
| @copilot | Coding Agent | — | 🤖 Coding Agent |
### Capabilities
**🟢 Good fit — auto-route when enabled:**
- Bug fixes with clear reproduction steps
- Test coverage (adding missing tests, fixing flaky tests)
- Lint/format fixes and code style cleanup
- Dependency updates and version bumps
- Small isolated features with clear specs
- Boilerplate/scaffolding generation
- Documentation fixes and README updates
**🟡 Needs review — route to @copilot but flag for squad member PR review:**
- Medium features with clear specs and acceptance criteria
- Refactoring with existing test coverage
- API endpoint additions following established patterns
- Migration scripts with well-defined schemas
**🔴 Not suitable — route to squad member instead:**
- Architecture decisions and system design
- Multi-system integration requiring coordination
- Ambiguous requirements needing clarification
- Security-critical changes (auth, encryption, access control)
- Performance-critical paths requiring benchmarking
- Changes requiring cross-team discussion
## Project Context
- **Owner:** {user name}
- **Stack:** {languages, frameworks, tools}
- **Description:** {what the project does, in one sentence}
- **Created:** {timestamp}

View File

@@ -0,0 +1,39 @@
# Work Routing
How to decide who handles what.
## Routing Table
| Work Type | Route To | Examples |
|-----------|----------|----------|
| {domain 1} | {Name} | {example tasks} |
| {domain 2} | {Name} | {example tasks} |
| {domain 3} | {Name} | {example tasks} |
| Code review | {Name} | Review PRs, check quality, suggest improvements |
| Testing | {Name} | Write tests, find edge cases, verify fixes |
| Scope & priorities | {Name} | What to build next, trade-offs, decisions |
| Session logging | Scribe | Automatic — never needs routing |
## Issue Routing
| Label | Action | Who |
|-------|--------|-----|
| `squad` | Triage: analyze issue, assign `squad:{member}` label | Lead |
| `squad:{name}` | Pick up issue and complete the work | Named member |
### How Issue Assignment Works
1. When a GitHub issue gets the `squad` label, the **Lead** triages it — analyzing content, assigning the right `squad:{member}` label, and commenting with triage notes.
2. When a `squad:{member}` label is applied, that member picks up the issue in their next session.
3. Members can reassign by removing their label and adding another member's label.
4. The `squad` label is the "inbox" — untriaged issues waiting for Lead review.
## Rules
1. **Eager by default** — spawn all agents who could usefully start work, including anticipatory downstream work.
2. **Scribe always runs** after substantial work, always as `mode: "background"`. Never blocks.
3. **Quick facts → coordinator answers directly.** Don't spawn an agent for "what port does the server run on?"
4. **When two agents could handle it**, pick the one whose domain is the primary concern.
5. **"Team, ..." → fan-out.** Spawn all relevant agents in parallel as `mode: "background"`.
6. **Anticipate downstream work.** If a feature is being built, spawn the tester to write test cases from requirements simultaneously.
7. **Issue-labeled work** — when a `squad:{member}` label is applied to an issue, route to that member. The Lead handles all `squad` (base label) triage.

View File

@@ -0,0 +1,50 @@
# Run Output — {task title}
> Final assembled artifact from a multi-agent run.
## Termination Condition
**Reason:** {One of: User accepted | Reviewer approved | Constraint budget exhausted | Deadlock — escalated to user | User cancelled}
## Constraint Budgets
<!-- Track all active constraints inline. Remove this section if no constraints are active. -->
| Constraint | Used | Max | Status |
|------------|------|-----|--------|
| Clarifying questions | 📊 {n} | {max} | {Active / Exhausted} |
| Revision cycles | 📊 {n} | {max} | {Active / Exhausted} |
## Result
{Assembled final artifact goes here. This is the Coordinator's synthesis of agent outputs.}
---
## Reviewer Verdict
<!-- Include one block per review. Remove this section if no review occurred. -->
### Review by {Name} ({Role})
| Field | Value |
|-------|-------|
| **Verdict** | {Approved / Rejected} |
| **What's wrong** | {Specific issue — not vague} |
| **Why it matters** | {Impact if not fixed} |
| **Who fixes it** | {Name of agent assigned to revise — MUST NOT be the original author} |
| **Revision budget** | 📊 {used} / {max} revision cycles remaining |
---
## APPENDIX: RAW AGENT OUTPUTS
<!-- Paste each agent's verbatim response below. Do NOT edit, summarize, rewrite, or wrap in code fences. One section per agent. -->
### {Name} ({Role}) — Raw Output
{Paste agent's verbatim response here, unedited}
### {Name} ({Role}) — Raw Output
{Paste agent's verbatim response here, unedited}

View File

@@ -0,0 +1,19 @@
{
"version": 1,
"schedules": [
{
"id": "ralph-heartbeat",
"name": "Ralph Heartbeat",
"enabled": true,
"trigger": {
"type": "interval",
"intervalSeconds": 300
},
"task": {
"type": "workflow",
"ref": ".github/workflows/squad-heartbeat.yml"
},
"providers": ["local-polling", "github-actions"]
}
]
}

View File

@@ -0,0 +1,119 @@
# Scribe
> The team's memory. Silent, always present, never forgets.
## Identity
- **Name:** Scribe
- **Role:** Session Logger, Memory Manager & Decision Merger
- **Style:** Silent. Never speaks to the user. Works in the background.
- **Mode:** Always spawned as `mode: "background"`. Never blocks the conversation.
## What I Own
- `.squad/log/` — session logs (what happened, who worked, what was decided)
- `.squad/decisions.md` — the shared decision log all agents read (canonical, merged)
- `.squad/decisions/inbox/` — decision drop-box (agents write here, I merge)
- Cross-agent context propagation — when one agent's decision affects another
## How I Work
**Worktree awareness:** Use the `TEAM ROOT` provided in the spawn prompt to resolve all `.squad/` paths. If no TEAM ROOT is given, run `git rev-parse --show-toplevel` as fallback. Do not assume CWD is the repo root (the session may be running in a worktree or subdirectory).
After every substantial work session:
1. **Log the session** to `.squad/log/{timestamp}-{topic}.md`:
- Who worked
- What was done
- Decisions made
- Key outcomes
- Brief. Facts only.
2. **Merge the decision inbox:**
- Read all files in `.squad/decisions/inbox/`
- APPEND each decision's contents to `.squad/decisions.md`
- Delete each inbox file after merging
3. **Deduplicate and consolidate decisions.md:**
- Parse the file into decision blocks (each block starts with `### `).
- **Exact duplicates:** If two blocks share the same heading, keep the first and remove the rest.
- **Overlapping decisions:** Compare block content across all remaining blocks. If two or more blocks cover the same area (same topic, same architectural concern, same component) but were written independently (different dates, different authors), consolidate them:
a. Synthesize a single merged block that combines the intent and rationale from all overlapping blocks.
b. Use today's date and a new heading: `### {today}: {consolidated topic} (consolidated)`
c. Credit all original authors: `**By:** {Name1}, {Name2}`
d. Under **What:**, combine the decisions. Note any differences or evolution.
e. Under **Why:**, merge the rationale, preserving unique reasoning from each.
f. Remove the original overlapping blocks.
- Write the updated file back. This handles duplicates and convergent decisions introduced by `merge=union` across branches.
4. **Propagate cross-agent updates:**
For any newly merged decision that affects other agents, append to their `history.md`:
```
📌 Team update ({timestamp}): {summary} — decided by {Name}
```
5. **Commit `.squad/` changes:**
**IMPORTANT — Windows compatibility:** Do NOT use `git -C {path}` (unreliable with Windows paths).
Do NOT embed newlines in `git commit -m` (backtick-n fails silently in PowerShell).
Instead:
- `cd` into the team root first.
- Stage all `.squad/` files: `git add .squad/`
- Check for staged changes: `git diff --cached --quiet`
If exit code is 0, no changes — skip silently.
- Write the commit message to a temp file, then commit with `-F`:
```
$msg = @"
docs(ai-team): {brief summary}
Session: {timestamp}-{topic}
Requested by: {user name}
Changes:
- {what was logged}
- {what decisions were merged}
- {what decisions were deduplicated}
- {what cross-agent updates were propagated}
"@
$msgFile = [System.IO.Path]::GetTempFileName()
Set-Content -Path $msgFile -Value $msg -Encoding utf8
git commit -F $msgFile
Remove-Item $msgFile
```
- **Verify the commit landed:** Run `git log --oneline -1` and confirm the
output matches the expected message. If it doesn't, report the error.
6. **Never speak to the user.** Never appear in responses. Work silently.
## The Memory Architecture
```
.squad/
├── decisions.md # Shared brain — all agents read this (merged by Scribe)
├── decisions/
│ └── inbox/ # Drop-box — agents write decisions here in parallel
│ ├── river-jwt-auth.md
│ └── kai-component-lib.md
├── orchestration-log/ # Per-spawn log entries
│ ├── 2025-07-01T10-00-river.md
│ └── 2025-07-01T10-00-kai.md
├── log/ # Session history — searchable record
│ ├── 2025-07-01-setup.md
│ └── 2025-07-02-api.md
└── agents/
├── kai/history.md # Kai's personal knowledge
├── river/history.md # River's personal knowledge
└── ...
```
- **decisions.md** = what the team agreed on (shared, merged by Scribe)
- **decisions/inbox/** = where agents drop decisions during parallel work
- **history.md** = what each agent learned (personal)
- **log/** = what happened (archive)
## Boundaries
**I handle:** Logging, memory, decision merging, cross-agent updates.
**I don't handle:** Any domain work. I don't write code, review PRs, or make decisions.
**I am invisible.** If a user notices me, something went wrong.

24
.squad/templates/skill.md Normal file
View File

@@ -0,0 +1,24 @@
---
name: "{skill-name}"
description: "{what this skill teaches agents}"
domain: "{e.g., testing, api-design, error-handling}"
confidence: "low|medium|high"
source: "{how this was learned: manual, observed, earned}"
tools:
# Optional — declare MCP tools relevant to this skill's patterns
# - name: "{tool-name}"
# description: "{what this tool does}"
# when: "{when to use this tool}"
---
## Context
{When and why this skill applies}
## Patterns
{Specific patterns, conventions, or approaches}
## Examples
{Code examples or references}
## Anti-Patterns
{What to avoid}

View File

@@ -0,0 +1,42 @@
---
name: "agent-collaboration"
description: "Standard collaboration patterns for all squad agents — worktree awareness, decisions, cross-agent communication"
domain: "team-workflow"
confidence: "high"
source: "extracted from charter boilerplate — identical content in 18+ agent charters"
---
## Context
Every agent on the team follows identical collaboration patterns for worktree awareness, decision recording, and cross-agent communication. These were previously duplicated in every charter's Collaboration section (~300 bytes × 18 agents = ~5.4KB of redundant context). Now centralized here.
The coordinator's spawn prompt already instructs agents to read decisions.md and their history.md. This skill adds the patterns for WRITING decisions and requesting help.
## Patterns
### Worktree Awareness
Use the `TEAM ROOT` path provided in your spawn prompt. All `.squad/` paths are relative to this root. If TEAM ROOT is not provided (rare), run `git rev-parse --show-toplevel` as fallback. Never assume CWD is the repo root.
### Decision Recording
After making a decision that affects other team members, write it to:
`.squad/decisions/inbox/{your-name}-{brief-slug}.md`
Format:
```
### {date}: {decision title}
**By:** {Your Name}
**What:** {the decision}
**Why:** {rationale}
```
### Cross-Agent Communication
If you need another team member's input, say so in your response. The coordinator will bring them in. Don't try to do work outside your domain.
### Reviewer Protocol
If you have reviewer authority and reject work: the original author is locked out from revising that artifact. A different agent must own the revision. State who should revise in your rejection response.
## Anti-Patterns
- Don't read all agent charters — you only need your own context + decisions.md
- Don't write directly to `.squad/decisions.md` — always use the inbox drop-box
- Don't modify other agents' history.md files — that's Scribe's job
- Don't assume CWD is the repo root — always use TEAM ROOT

View File

@@ -0,0 +1,24 @@
---
name: "agent-conduct"
description: "Shared hard rules enforced across all squad agents"
domain: "team-governance"
confidence: "high"
source: "reskill extraction — Product Isolation Rule and Peer Quality Check appeared in all 20 agent charters"
---
## Context
Every squad agent must follow these two hard rules. They were previously duplicated in every charter. Now they live here as a shared skill, loaded once.
## Patterns
### Product Isolation Rule (hard rule)
Tests, CI workflows, and product code must NEVER depend on specific agent names from any particular squad. "Our squad" must not impact "the squad." No hardcoded references to agent names (Flight, EECOM, FIDO, etc.) in test assertions, CI configs, or product logic. Use generic/parameterized values. If a test needs agent names, use obviously-fake test fixtures (e.g., "test-agent-1", "TestBot").
### Peer Quality Check (hard rule)
Before finishing work, verify your changes don't break existing tests. Run the test suite for files you touched. If CI has been failing, check your changes aren't contributing to the problem. When you learn from mistakes, update your history.md.
## Anti-Patterns
- Don't hardcode dev team agent names in product code or tests
- Don't skip test verification before declaring work done
- Don't ignore pre-existing CI failures that your changes may worsen

View File

@@ -0,0 +1,151 @@
---
name: "architectural-proposals"
description: "How to write comprehensive architectural proposals that drive alignment before code is written"
domain: "architecture, product-direction"
confidence: "high"
source: "earned (2026-02-21 interactive shell proposal)"
tools:
- name: "view"
description: "Read existing codebase, prior decisions, and team context before proposing changes"
when: "Always read .squad/decisions.md, relevant PRDs, and current architecture docs before writing proposal"
- name: "create"
description: "Create proposal in docs/proposals/ with structured format"
when: "After gathering context, before any implementation work begins"
---
## Context
Proposals create alignment before code is written. Cheaper to change a doc than refactor code. Use this pattern when:
- Architecture shifts invalidate existing assumptions
- Product direction changes require new foundation
- Multiple waves/milestones will be affected by a decision
- External dependencies (Copilot CLI, SDK APIs) change
## Patterns
### Proposal Structure (docs/proposals/)
**Required sections:**
1. **Problem Statement** — Why current state is broken (specific, measurable evidence)
2. **Proposed Architecture** — Solution with technical specifics (not hand-waving)
3. **What Changes** — Impact on existing work (waves, milestones, modules)
4. **What Stays the Same** — Preserve existing functionality (no regression)
5. **Key Decisions Needed** — Explicit choices with recommendations
6. **Risks and Mitigations** — Likelihood + impact + mitigation strategy
7. **Scope** — What's in v1, what's deferred (timeline clarity)
**Optional sections:**
- Implementation Plan (high-level milestones)
- Success Criteria (measurable outcomes)
- Open Questions (unresolved items)
- Appendix (prior art, alternatives considered)
### Tone Ceiling Enforcement
**Always:**
- Cite specific evidence (user reports, performance data, failure modes)
- Justify recommendations with technical rationale
- Acknowledge trade-offs (no perfect solutions)
- Be specific about APIs, libraries, file paths
**Never:**
- Hype ("revolutionary", "game-changing")
- Hand-waving ("we'll figure it out later")
- Unsubstantiated claims ("users will love this")
- Vague timelines ("soon", "eventually")
### Wave Restructuring Pattern
When a proposal invalidates existing wave structure:
1. **Acknowledge the shift:** "This becomes Wave 0 (Foundation)"
2. **Cascade impacts:** Adjust downstream waves (Wave 1, Wave 2, Wave 3)
3. **Preserve non-blocking work:** Identify what can proceed in parallel
4. **Update dependencies:** Document new blocking relationships
**Example (Interactive Shell):**
- Wave 0 (NEW): Interactive Shell — blocks all other waves
- Wave 1 (ADJUSTED): npm Distribution — shell bundled in cli.js
- Wave 2 (DEFERRED): SquadUI — waits for shell foundation
- Wave 3 (ADJUSTED): Public Docs — now documents shell as primary interface
### Decision Framing
**Format:** "Recommendation: X (recommended) or alternatives?"
**Components:**
- Recommendation (pick one, justify)
- Alternatives (what else was considered)
- Decision rationale (why recommended option wins)
- Needs sign-off from (which agents/roles must approve)
**Example:**
```
### 1. Terminal UI Library: `ink` (recommended) or alternatives?
**Recommendation:** `ink`
**Alternatives:** `blessed`, raw readline
**Decision rationale:** Component model enables testable UI. Battle-tested ecosystem.
**Needs sign-off from:** Brady (product direction), Fortier (runtime performance)
```
### Risk Documentation
**Format per risk:**
- **Risk:** Specific failure mode
- **Likelihood:** Low / Medium / High (not percentages)
- **Impact:** Low / Medium / High
- **Mitigation:** Concrete actions (measurable)
**Example:**
```
### Risk 2: SDK Streaming Reliability
**Risk:** SDK streaming events might drop messages or arrive out of order.
**Likelihood:** Low (SDK is production-grade).
**Impact:** High — broken streaming makes shell unusable.
**Mitigation:**
- Add integration test: Send 1000-message stream, verify all deltas arrive in order
- Implement fallback: If streaming fails, fall back to polling session state
- Log all SDK events to `.squad/orchestration-log/sdk-events.jsonl` for debugging
```
## Examples
**File references from interactive shell proposal:**
- Full proposal: `docs/proposals/squad-interactive-shell.md`
- User directive: `.squad/decisions/inbox/copilot-directive-2026-02-21T202535Z.md`
- Team decisions: `.squad/decisions.md`
- Current architecture: `docs/architecture/module-map.md`, `docs/prd-23-release-readiness.md`
**Key patterns demonstrated:**
1. Read user directive first (understand the "why")
2. Survey current architecture (module map, existing waves)
3. Research SDK APIs (exploration task to validate feasibility)
4. Document problem with specific evidence (unreliable handoffs, zero visibility, UX mismatch)
5. Propose solution with technical specifics (ink components, SDK session management, spawn.ts module)
6. Restructure waves when foundation shifts (Wave 0 becomes blocker)
7. Preserve backward compatibility (squad.agent.md still works, VS Code mode unchanged)
8. Frame decisions explicitly (5 key decisions with recommendations)
9. Document risks with mitigations (5 risks, each with concrete actions)
10. Define scope (what's in v1 vs. deferred)
## Anti-Patterns
**Avoid:**
- ❌ Proposals without problem statements (solution-first thinking)
- ❌ Vague architecture ("we'll use a shell") — be specific (ink components, session registry, spawn.ts)
- ❌ Ignoring existing work — always document impact on waves/milestones
- ❌ No risk analysis — every architecture has risks, document them
- ❌ Unbounded scope — draw the v1 line explicitly
- ❌ Missing decision ownership — always say "needs sign-off from X"
- ❌ No backward compatibility plan — users don't care about your replatform
- ❌ Hand-waving timelines ("a few weeks") — be specific (2-3 weeks, 1 engineer full-time)
**Red flags in proposal reviews:**
- "Users will love this" (citation needed)
- "We'll figure out X later" (scope creep incoming)
- "This is revolutionary" (tone ceiling violation)
- No section on "What Stays the Same" (regression risk)
- No risks documented (wishful thinking)

View File

@@ -0,0 +1,84 @@
---
name: "ci-validation-gates"
description: "Defensive CI/CD patterns: semver validation, token checks, retry logic, draft detection — earned from v0.8.22"
domain: "ci-cd"
confidence: "high"
source: "extracted from Drucker and Trejo charters — earned knowledge from v0.8.22 release incident"
---
## Context
CI workflows must be defensive. These patterns were learned from the v0.8.22 release disaster where invalid semver, wrong token types, missing retry logic, and draft releases caused a multi-hour outage. Both Drucker (CI/CD) and Trejo (Release Manager) carried this knowledge in their charters — now centralized here.
## Patterns
### Semver Validation Gate
Every publish workflow MUST validate version format before `npm publish`. 4-part versions (e.g., 0.8.21.4) are NOT valid semver — npm mangles them.
```yaml
- name: Validate semver
run: |
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}"
if ! npx semver "$VERSION" > /dev/null 2>&1; then
echo "❌ Invalid semver: $VERSION"
echo "Only 3-part versions (X.Y.Z) or prerelease (X.Y.Z-tag.N) are valid."
exit 1
fi
echo "✅ Valid semver: $VERSION"
```
### NPM Token Type Verification
NPM_TOKEN MUST be an Automation token, not a User token with 2FA:
- User tokens require OTP — CI can't provide it → EOTP error
- Create Automation tokens at npmjs.com → Settings → Access Tokens → Automation
- Verify before first publish in any workflow
### Retry Logic for npm Registry Propagation
npm registry uses eventual consistency. After `npm publish` succeeds, the package may not be immediately queryable.
- Propagation: typically 5-30s, up to 2min in rare cases
- All verify steps: 5 attempts, 15-second intervals
- Log each attempt: "Attempt 1/5: Checking package..."
- Exit loop on success, fail after max attempts
```yaml
- name: Verify package (with retry)
run: |
MAX_ATTEMPTS=5
WAIT_SECONDS=15
for attempt in $(seq 1 $MAX_ATTEMPTS); do
echo "Attempt $attempt/$MAX_ATTEMPTS: Checking $PACKAGE@$VERSION..."
if npm view "$PACKAGE@$VERSION" version > /dev/null 2>&1; then
echo "✅ Package verified"
exit 0
fi
[ $attempt -lt $MAX_ATTEMPTS ] && sleep $WAIT_SECONDS
done
echo "❌ Failed to verify after $MAX_ATTEMPTS attempts"
exit 1
```
### Draft Release Detection
Draft releases don't emit `release: published` event. Workflows MUST:
- Trigger on `release: published` (NOT `created`)
- If using workflow_dispatch: verify release is published via GitHub API before proceeding
### Build Script Protection
Set `SKIP_BUILD_BUMP=1` (or `$env:SKIP_BUILD_BUMP = "1"` on Windows) before ANY release build. bump-build.mjs is for dev builds ONLY — it silently mutates versions.
## Known Failure Modes (v0.8.22 Incident)
| # | What Happened | Root Cause | Prevention |
|---|---------------|-----------|------------|
| 1 | 4-part version published, npm mangled it | No semver validation gate | `npx semver` check before every publish |
| 2 | CI failed 5+ times with EOTP | User token with 2FA | Automation token only |
| 3 | Verify returned false 404 | No retry logic for propagation | 5 attempts, 15s intervals |
| 4 | Workflow never triggered | Draft release doesn't emit event | Never create draft releases |
| 5 | Version mutated during release | bump-build.mjs ran in release | SKIP_BUILD_BUMP=1 |
## Anti-Patterns
- ❌ Publishing without semver validation gate
- ❌ Single-shot verification without retry
- ❌ Hard-coded secrets in workflows
- ❌ Silent CI failures — every error needs actionable output with remediation
- ❌ Assuming npm publish is instantly queryable

View File

@@ -0,0 +1,47 @@
# Skill: CLI Command Wiring
**Bug class:** Commands implemented in `packages/squad-cli/src/cli/commands/` but never routed in `cli-entry.ts`.
## Checklist — Adding a New CLI Command
1. **Create command file** in `packages/squad-cli/src/cli/commands/<name>.ts`
- Export a `run<Name>(cwd, options)` async function (or class with static methods for utility modules)
2. **Add routing block** in `packages/squad-cli/src/cli-entry.ts` inside `main()`:
```ts
if (cmd === '<name>') {
const { run<Name> } = await import('./cli/commands/<name>.js');
// parse args, call function
await run<Name>(process.cwd(), options);
return;
}
```
3. **Add help text** in the help section of `cli-entry.ts` (search for `Commands:`):
```ts
console.log(` ${BOLD}<name>${RESET} <description>`);
console.log(` Usage: <name> [flags]`);
```
4. **Verify both exist** — the recurring bug is doing step 1 but missing steps 2-3.
## Wiring Patterns by Command Type
| Type | Example | How to wire |
|------|---------|-------------|
| Standard command | `export.ts`, `build.ts` | `run*()` function, parse flags from `args` |
| Placeholder command | `loop`, `hire` | Inline in cli-entry.ts, prints pending message |
| Utility/check module | `rc-tunnel.ts`, `copilot-bridge.ts` | Wire as diagnostic check (e.g., `isDevtunnelAvailable()`) |
| Subcommand of another | `init-remote.ts` | Already used inside parent + standalone alias |
## Common Import Pattern
```ts
import { BOLD, RESET, DIM, RED, GREEN, YELLOW } from './cli/core/output.js';
```
Use dynamic `await import()` for command modules to keep startup fast (lazy loading).
## History
- **#237 / PR #244:** 4 commands wired (rc, copilot-bridge, init-remote, rc-tunnel). aspire, link, loop, hire were already present.

Some files were not shown because too many files have changed in this diff Show More