{"_id":"@lushly-dev/afd-testing","_rev":"9-2a3d46807816e200ad9462100b906a84","name":"@lushly-dev/afd-testing","dist-tags":{"latest":"1.0.0"},"versions":{"0.1.0":{"name":"@lushly-dev/afd-testing","version":"0.1.0","keywords":["afd","testing","mcp","validation","commands"],"author":{"name":"Falkicon"},"license":"MIT","_id":"@lushly-dev/afd-testing@0.1.0","maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"homepage":"https://github.com/lushly-dev/afd#readme","bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"dist":{"shasum":"e3b8380b1ef34c9dfc7862c8477a1da2d5a6a63f","tarball":"https://registry.npmjs.org/@lushly-dev/afd-testing/-/afd-testing-0.1.0.tgz","fileCount":115,"integrity":"sha512-cEzQYeKEd93VVS9gvFF//6fgKVAoz3lQ6Gr04S7J5OpdMQfhvOshNpODvNBLZyn0111ngQiB7Wm8VgmoI47ahQ==","signatures":[{"sig":"MEQCIBXHNgai4xV+U286NYUQbTTdd3V/J/p3Ssp2DaoGW43jAiBB8PnjWl2ZZiRDLfmHp+XF1hZhW+fertPEbXwQzZ4COg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":545221},"main":"./dist/index.js","type":"module","_from":"file:lushly-dev-afd-testing-0.1.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rimraf dist *.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"lushly-admin","email":"falkicon@gmail.com"},"_resolved":"C:\\Users\\jasfalk\\AppData\\Local\\Temp\\7b74e26d5f72ccfd9340015f3bb1d766\\lushly-dev-afd-testing-0.1.0.tgz","_integrity":"sha512-cEzQYeKEd93VVS9gvFF//6fgKVAoz3lQ6Gr04S7J5OpdMQfhvOshNpODvNBLZyn0111ngQiB7Wm8VgmoI47ahQ==","repository":{"url":"git+https://github.com/lushly-dev/afd.git","type":"git","directory":"packages/testing"},"_npmVersion":"11.2.0","description":"Testing utilities for Agent-First Development","directories":{},"_nodeVersion":"23.3.0","dependencies":{"yaml":"^2.7.0","@lushly-dev/afd-core":"0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","vitest":"^2.1.8","typescript":"^5.7.2"},"peerDependencies":{"vitest":">=1.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/afd-testing_0.1.0_1767824771702_0.9999461274595707","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@lushly-dev/afd-testing","version":"0.3.0","keywords":["afd","testing","mcp","validation","commands"],"author":{"name":"Falkicon"},"license":"MIT","_id":"@lushly-dev/afd-testing@0.3.0","maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"homepage":"https://github.com/lushly-dev/afd#readme","bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"dist":{"shasum":"5a2494bcddf6a48654a17add43c86c9b3fc5ee32","tarball":"https://registry.npmjs.org/@lushly-dev/afd-testing/-/afd-testing-0.3.0.tgz","fileCount":147,"integrity":"sha512-W9LaQzXGpfAbE9D7gYukkCb02sqXuD2t6zebOqrVf68p7Fx9xigU2KiLxot/KtqxwD1qhsbHsLDDbZ7aeKn1WA==","signatures":[{"sig":"MEUCIEtlKTogiNRtURq1PNhq01vjE54oU74VMFLtrPfHGNWXAiEAscIvnsZiG4aJspBSsBa10xclnQrnpu3fSFCgKeteQdw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@lushly-dev%2fafd-testing@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":655344},"main":"./dist/index.js","type":"module","_from":"file:lushly-dev-afd-testing-0.3.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rimraf dist *.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"lushly-admin","email":"falkicon@gmail.com"},"_resolved":"/tmp/0184d04e1a72d3eb6224cae25a69c896/lushly-dev-afd-testing-0.3.0.tgz","_integrity":"sha512-W9LaQzXGpfAbE9D7gYukkCb02sqXuD2t6zebOqrVf68p7Fx9xigU2KiLxot/KtqxwD1qhsbHsLDDbZ7aeKn1WA==","repository":{"url":"git+https://github.com/lushly-dev/afd.git","type":"git","directory":"packages/testing"},"_npmVersion":"10.9.4","description":"Testing utilities for Agent-First Development","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"yaml":"^2.7.0","@lushly-dev/afd-core":"0.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","vitest":"^2.1.8","typescript":"^5.7.2"},"peerDependencies":{"vitest":">=1.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/afd-testing_0.3.0_1772238045432_0.5657016564622828","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@lushly-dev/afd-testing","version":"0.3.1","keywords":["afd","testing","mcp","validation","commands"],"author":{"name":"Falkicon"},"license":"MIT","_id":"@lushly-dev/afd-testing@0.3.1","maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"homepage":"https://github.com/lushly-dev/afd#readme","bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"dist":{"shasum":"74e364a19ecb3039a44114001bd0bad8be2cd886","tarball":"https://registry.npmjs.org/@lushly-dev/afd-testing/-/afd-testing-0.3.1.tgz","fileCount":147,"integrity":"sha512-9HvOFH3OVB3gHXnt+hcLx5MkizL9Ogs9Xs3Wl5E11Lldv0NLFg/2TTe3mHYdhzn5m5INKuyG+kTaBM+zNcWhsQ==","signatures":[{"sig":"MEQCIF4LNNd4tm+RFxnmaWpUobV5zaZx7rFSQAMhXwVz5N7KAiBcx7g2g+6c0dkhhR7PUWN5O4OCJ0dTuebHizB3xioDkQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@lushly-dev%2fafd-testing@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":655383},"main":"./dist/index.js","type":"module","_from":"file:lushly-dev-afd-testing-0.3.1.tgz","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rimraf dist *.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"lushly-admin","email":"falkicon@gmail.com"},"_resolved":"/tmp/0c3f3b8ecbfb27019d97af4daa659744/lushly-dev-afd-testing-0.3.1.tgz","_integrity":"sha512-9HvOFH3OVB3gHXnt+hcLx5MkizL9Ogs9Xs3Wl5E11Lldv0NLFg/2TTe3mHYdhzn5m5INKuyG+kTaBM+zNcWhsQ==","repository":{"url":"git+https://github.com/lushly-dev/afd.git","type":"git","directory":"packages/testing"},"_npmVersion":"10.9.4","description":"Testing utilities for Agent-First Development","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"yaml":"^2.7.0","@lushly-dev/afd-core":"0.3.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","vitest":"^4.0.18","typescript":"^5.7.2","@vitest/coverage-v8":"^4.0.18"},"peerDependencies":{"vitest":">=1.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/afd-testing_0.3.1_1772250990045_0.9876000626146493","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@lushly-dev/afd-testing","version":"0.3.2","keywords":["afd","testing","mcp","validation","commands"],"author":{"name":"Falkicon"},"license":"MIT","_id":"@lushly-dev/afd-testing@0.3.2","maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"homepage":"https://github.com/lushly-dev/afd#readme","bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"dist":{"shasum":"7c8632fd2202ea75dbf5fe5806702e8b840db9d3","tarball":"https://registry.npmjs.org/@lushly-dev/afd-testing/-/afd-testing-0.3.2.tgz","fileCount":147,"integrity":"sha512-iToBE99LLbgXZVTGMqDrbm4/u8NNNYuCYiAzohsRq5+D+4ZxMIJnyD4+NT/w1ihRxFH4k5EHl5bO5Jmyt9fZUQ==","signatures":[{"sig":"MEYCIQCmLkFNGNfGO2Qoh7LOJ0q0eKu6fiJfktpLJeS8DRdErgIhAN1BkpqlNTVKau3PMQlB7lG2ezQhxPpW4lzjHpHtk4FT","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@lushly-dev%2fafd-testing@0.3.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":655383},"main":"./dist/index.js","type":"module","_from":"file:lushly-dev-afd-testing-0.3.2.tgz","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rimraf dist *.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"lushly-admin","email":"falkicon@gmail.com"},"_resolved":"/tmp/6b24b0874bdfb335305ddb49e3b837f8/lushly-dev-afd-testing-0.3.2.tgz","_integrity":"sha512-iToBE99LLbgXZVTGMqDrbm4/u8NNNYuCYiAzohsRq5+D+4ZxMIJnyD4+NT/w1ihRxFH4k5EHl5bO5Jmyt9fZUQ==","repository":{"url":"git+https://github.com/lushly-dev/afd.git","type":"git","directory":"packages/testing"},"_npmVersion":"10.9.4","description":"Testing utilities for Agent-First Development","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"yaml":"^2.7.0","@lushly-dev/afd-core":"0.3.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","vitest":"^4.0.18","typescript":"^5.7.2","@vitest/coverage-v8":"^4.0.18"},"peerDependencies":{"vitest":">=1.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/afd-testing_0.3.2_1772256950724_0.5869392043206798","host":"s3://npm-registry-packages-npm-production"}},"0.3.4":{"name":"@lushly-dev/afd-testing","version":"0.3.4","keywords":["afd","testing","mcp","validation","commands"],"author":{"name":"Falkicon"},"license":"MIT","_id":"@lushly-dev/afd-testing@0.3.4","maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"homepage":"https://github.com/lushly-dev/afd#readme","bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"dist":{"shasum":"5932ae56f558c717479bdbeb95b2f8a8dc3d3512","tarball":"https://registry.npmjs.org/@lushly-dev/afd-testing/-/afd-testing-0.3.4.tgz","fileCount":147,"integrity":"sha512-AZvAISuNTHGAM5H8n7eSfdgfab8emruJF99zhdJADAIxdfjOsleBKep6K5vmnLIriN0VQOpjC1Wv2uBa1NyyNg==","signatures":[{"sig":"MEQCIG3EFJQJbrfdK8RD3vGpr2iRBVguVPYwFD3tFI5c2XtTAiABRvyW3jaIwB4TPXNRs8SzTfW1idYAElXzfPK1t6Ih4Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@lushly-dev%2fafd-testing@0.3.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":655383},"main":"./dist/index.js","type":"module","_from":"file:lushly-dev-afd-testing-0.3.4.tgz","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rimraf dist *.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"lushly-admin","email":"falkicon@gmail.com"},"_resolved":"/tmp/126ad839e48a9d8142563542dbdef1ab/lushly-dev-afd-testing-0.3.4.tgz","_integrity":"sha512-AZvAISuNTHGAM5H8n7eSfdgfab8emruJF99zhdJADAIxdfjOsleBKep6K5vmnLIriN0VQOpjC1Wv2uBa1NyyNg==","repository":{"url":"git+https://github.com/lushly-dev/afd.git","type":"git","directory":"packages/testing"},"_npmVersion":"10.9.4","description":"Testing utilities for Agent-First Development","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"yaml":"^2.7.0","@lushly-dev/afd-core":"0.3.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","vitest":"^4.0.18","typescript":"^5.7.2","@vitest/coverage-v8":"^4.0.18"},"peerDependencies":{"vitest":">=1.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/afd-testing_0.3.4_1772258399527_0.014137828404710007","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@lushly-dev/afd-testing","version":"0.4.0","keywords":["afd","testing","mcp","validation","commands"],"author":{"name":"Falkicon"},"license":"MIT","_id":"@lushly-dev/afd-testing@0.4.0","maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"homepage":"https://github.com/lushly-dev/afd#readme","bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"dist":{"shasum":"ff5f824233ebf925f52b0b53aede292214b2ea2c","tarball":"https://registry.npmjs.org/@lushly-dev/afd-testing/-/afd-testing-0.4.0.tgz","fileCount":147,"integrity":"sha512-lMipxW19jt6DTpjnMa+JxKdJ0FT4v8FFsIGh033F1JDRnCStd4mdkg6ZS4Qy8d8gkA1Fvj5y8qORuFr7j/fwOw==","signatures":[{"sig":"MEYCIQD5MrdrXDH6EQgRREDMGsRGHiW2ZT0tF57ukzPRRXeUOgIhAJD/0rq1oTAqhQ8tOuxojT4zIfm/tGTmzsfkWxIjcJRc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@lushly-dev%2fafd-testing@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":655383},"main":"./dist/index.js","type":"module","_from":"file:lushly-dev-afd-testing-0.4.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rimraf dist *.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"lushly-admin","email":"falkicon@gmail.com"},"_resolved":"/tmp/597dd24d1b1c925645f7920687ef200a/lushly-dev-afd-testing-0.4.0.tgz","_integrity":"sha512-lMipxW19jt6DTpjnMa+JxKdJ0FT4v8FFsIGh033F1JDRnCStd4mdkg6ZS4Qy8d8gkA1Fvj5y8qORuFr7j/fwOw==","repository":{"url":"git+https://github.com/lushly-dev/afd.git","type":"git","directory":"packages/testing"},"_npmVersion":"10.9.4","description":"Testing utilities for Agent-First Development","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"yaml":"^2.7.0","@lushly-dev/afd-core":"0.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","vitest":"^4.0.18","typescript":"^5.7.2","@vitest/coverage-v8":"^4.0.18"},"peerDependencies":{"vitest":">=1.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/afd-testing_0.4.0_1772310067410_0.3573873023984364","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@lushly-dev/afd-testing","version":"0.5.0","keywords":["afd","testing","mcp","validation","commands"],"author":{"name":"Falkicon"},"license":"MIT","_id":"@lushly-dev/afd-testing@0.5.0","maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"homepage":"https://github.com/lushly-dev/afd#readme","bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"dist":{"shasum":"ec122707546f30281f68d269612e2f2ffdd4527a","tarball":"https://registry.npmjs.org/@lushly-dev/afd-testing/-/afd-testing-0.5.0.tgz","fileCount":147,"integrity":"sha512-5Vv6lPi2PJ7dokWB/UC9xVxUFb72grHxK1tEXSIjcXZO0NYwBrgRCF53Ex1RKF3Ld7x0wTK+gvFojBPLFroLTw==","signatures":[{"sig":"MEYCIQC3q9Foi0IG93xfvnHR5JtVyYgYxXsAOKIQlBC4AvvlqAIhANczKmkDnYn2/eRHARxpm/tcAFkzGAri6/bBy3oNzWuL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@lushly-dev%2fafd-testing@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":663707},"main":"./dist/index.js","type":"module","_from":"file:lushly-dev-afd-testing-0.5.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rimraf dist *.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"lushly-admin","email":"falkicon@gmail.com"},"_resolved":"/tmp/a390e6849a8bdaee71ab0f7b427c7ef9/lushly-dev-afd-testing-0.5.0.tgz","_integrity":"sha512-5Vv6lPi2PJ7dokWB/UC9xVxUFb72grHxK1tEXSIjcXZO0NYwBrgRCF53Ex1RKF3Ld7x0wTK+gvFojBPLFroLTw==","repository":{"url":"git+https://github.com/lushly-dev/afd.git","type":"git","directory":"packages/testing"},"_npmVersion":"10.9.4","description":"Testing utilities for Agent-First Development","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"yaml":"^2.7.0","@lushly-dev/afd-core":"0.5.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.0.1","vitest":"^4.0.18","typescript":"^5.7.2","@vitest/coverage-v8":"^4.0.18"},"peerDependencies":{"vitest":">=1.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/afd-testing_0.5.0_1773188890395_0.18885507636124532","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@lushly-dev/afd-testing","version":"0.6.0","keywords":["afd","testing","mcp","validation","commands"],"author":{"name":"Falkicon"},"license":"MIT","_id":"@lushly-dev/afd-testing@0.6.0","maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"homepage":"https://github.com/lushly-dev/afd#readme","bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"dist":{"shasum":"a61b0b98d0f0c8bb8b12e260c4121cee1d650dfc","tarball":"https://registry.npmjs.org/@lushly-dev/afd-testing/-/afd-testing-0.6.0.tgz","fileCount":151,"integrity":"sha512-tOEpg5SCfrxaw6gp+5jxabt5e+IU4BHUA+kC7AVkFEU6CaDa3y1WephLuSuna+b5zGFliqDTTzgCQalllQc1/Q==","signatures":[{"sig":"MEQCICwUw2OEZqkWWwK2zso0b15+Hdh6JD+yCbrmZ71mPYv5AiBfbBsKDy7wtdRs41f+SFCsotAl4NDgbVfuzyrOO82dhw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@lushly-dev%2fafd-testing@0.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":665037},"main":"./dist/index.js","type":"module","_from":"file:lushly-dev-afd-testing-0.6.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rimraf dist *.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"lushly-admin","email":"falkicon@gmail.com"},"_resolved":"/tmp/ce3f6bb3cdf447b12ce5a38b57168745/lushly-dev-afd-testing-0.6.0.tgz","_integrity":"sha512-tOEpg5SCfrxaw6gp+5jxabt5e+IU4BHUA+kC7AVkFEU6CaDa3y1WephLuSuna+b5zGFliqDTTzgCQalllQc1/Q==","repository":{"url":"git+https://github.com/lushly-dev/afd.git","type":"git","directory":"packages/testing"},"_npmVersion":"11.9.0","description":"Testing utilities for Agent-First Development","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","dependencies":{"yaml":"^2.8.2","@lushly-dev/afd-core":"0.6.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^6.1.3","vitest":"^4.1.0","typescript":"^5.9.3","@vitest/coverage-v8":"^4.1.0"},"peerDependencies":{"vitest":">=1.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/afd-testing_0.6.0_1773466660005_0.09158456665066028","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@lushly-dev/afd-testing","version":"1.0.0","description":"Testing utilities for Agent-First Development","type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"main":"./dist/index.js","types":"./dist/index.d.ts","keywords":["afd","testing","mcp","validation","commands"],"author":{"name":"Falkicon"},"license":"MIT","homepage":"https://github.com/lushly-dev/afd#readme","bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"repository":{"type":"git","url":"git+https://github.com/lushly-dev/afd.git","directory":"packages/testing"},"engines":{"node":">=22.0.0"},"sideEffects":false,"dependencies":{"yaml":"^2.8.2","@lushly-dev/afd-core":"1.0.0"},"devDependencies":{"@vitest/coverage-v8":"^4.1.0","rimraf":"^6.1.3","typescript":"^5.9.3","vitest":"^4.1.0"},"peerDependencies":{"vitest":">=1.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true}},"publishConfig":{"access":"public"},"scripts":{"build":"tsc","clean":"rimraf dist *.tsbuildinfo","dev":"tsc --watch","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest"},"_id":"@lushly-dev/afd-testing@1.0.0","_integrity":"sha512-FO0REfUDG1FgnFkhP/bzxVxyBugxZVnhe8FFE04IF5PfmzJin/K8XVxYww14hnRJKiifwF6Yg6j5NaXyCBcb3w==","_resolved":"/tmp/15dd4c071f9bfb0a3c641731eb6e10a1/lushly-dev-afd-testing-1.0.0.tgz","_from":"file:lushly-dev-afd-testing-1.0.0.tgz","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-FO0REfUDG1FgnFkhP/bzxVxyBugxZVnhe8FFE04IF5PfmzJin/K8XVxYww14hnRJKiifwF6Yg6j5NaXyCBcb3w==","shasum":"569c94632c35ce42f5a3a6eae3821fd4537bfac5","tarball":"https://registry.npmjs.org/@lushly-dev/afd-testing/-/afd-testing-1.0.0.tgz","fileCount":151,"unpackedSize":665037,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@lushly-dev%2fafd-testing@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCz9RlamysNF9XValvTve0V0gL8oMXnzuJ2kPefY66qcgIgJjsoqkkrwUSPlsxv8JT2HQBkXPrP1rlDDBoX7GhCERg="}]},"_npmUser":{"name":"lushly-admin","email":"falkicon@gmail.com"},"directories":{},"maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/afd-testing_1.0.0_1773633892791_0.03474162512951651"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-07T22:26:11.576Z","modified":"2026-03-16T04:04:53.333Z","0.1.0":"2026-01-07T22:26:11.868Z","0.3.0":"2026-02-28T00:20:45.624Z","0.3.1":"2026-02-28T03:56:30.219Z","0.3.2":"2026-02-28T05:35:50.976Z","0.3.4":"2026-02-28T05:59:59.695Z","0.4.0":"2026-02-28T20:21:07.622Z","0.5.0":"2026-03-11T00:28:10.546Z","0.6.0":"2026-03-14T05:37:40.247Z","1.0.0":"2026-03-16T04:04:52.955Z"},"bugs":{"url":"https://github.com/lushly-dev/afd/issues"},"author":{"name":"Falkicon"},"license":"MIT","homepage":"https://github.com/lushly-dev/afd#readme","keywords":["afd","testing","mcp","validation","commands"],"repository":{"type":"git","url":"git+https://github.com/lushly-dev/afd.git","directory":"packages/testing"},"description":"Testing utilities for Agent-First Development","maintainers":[{"name":"lushly-admin","email":"falkicon@gmail.com"}],"readme":"# @lushly-dev/afd-testing\n\nTesting utilities for Agent-First Development.\n\n## Installation\n\n```bash\nnpm install @lushly-dev/afd-testing --save-dev\n# or\npnpm add @lushly-dev/afd-testing -D\n```\n\n## Overview\n\nThis package provides utilities for testing AFD commands:\n\n- **Validators**: Validate command results and definitions\n- **Surface Validation**: Cross-command semantic quality analysis (similarity, schema overlap, naming, injection detection)\n- **Test Helpers**: Easy command testing with validation\n- **Assertions**: Custom assertions for command results\n- **Mock Server**: In-memory MCP server for testing\n- **JTBD Scenario Runner**: Jobs-to-be-Done scenario testing with YAML files\n- **Fixtures**: Pre-seeded test data with inheritance and overrides\n- **Step References**: Dynamic references between scenario steps\n- **Scenario Commands**: List, evaluate, coverage, create, and suggest scenarios\n- **MCP Agent Integration**: Expose commands as MCP tools with agent hints\n\n## Surface Validation (Semantic Quality)\n\nCross-command analysis that detects semantic collisions, naming ambiguities, schema overlaps, and prompt injection risks. Designed for command sets of 50+ where agents struggle to pick the right tool.\n\n### Basic Usage\n\n```typescript\nimport { validateCommandSurface } from '@lushly-dev/afd-testing';\n\nconst result = validateCommandSurface(commands, {\n  similarityThreshold: 0.7,\n  schemaOverlapThreshold: 0.8,\n  strict: false,\n});\n\nconsole.log(result.valid);          // true if no errors (or no errors+warnings in strict mode)\nconsole.log(result.summary);        // { commandCount, errorCount, warningCount, infoCount, ... }\nconsole.log(result.findings);       // SurfaceFinding[] with rule, severity, message, suggestion\n```\n\n### Input Types\n\n`validateCommandSurface()` accepts both `ZodCommandDefinition[]` (from `@lushly-dev/afd-server`) and `CommandDefinition[]` (from `@lushly-dev/afd-core`). Input is auto-detected via duck-typing.\n\n### Validation Rules\n\n| Rule | Severity | Description |\n|------|----------|-------------|\n| `similar-descriptions` | Warning | Command pairs with highly similar descriptions (cosine similarity) |\n| `schema-overlap` | Warning | Command pairs sharing a high percentage of input fields |\n| `naming-convention` | Error | Command names not matching kebab-case `domain-action` pattern |\n| `naming-collision` | Error | Command names that collide when separators are normalized |\n| `missing-category` | Info | Commands without a category field |\n| `description-injection` | Error | Descriptions containing prompt injection patterns |\n| `description-quality` | Warning | Descriptions that are too short or missing action verbs |\n| `orphaned-category` | Info | Categories containing only one command |\n| `schema-complexity` | Warning/Info | Input schemas too complex for agents (unions, nesting, constraints) |\n| `unresolved-prerequisite` | Error | `requires` entry references a command not in the surface |\n| `circular-prerequisite` | Error | Circular dependency cycle in the `requires` graph |\n| `missing-output-schema` | Info | Command does not declare an `output` schema |\n| `missing-context` | Info | Command has no `contexts` array (when server has configured contexts) |\n\n### Options\n\n```typescript\ninterface SurfaceValidationOptions {\n  similarityThreshold?: number;        // Default: 0.7 (70% similarity triggers warning)\n  schemaOverlapThreshold?: number;     // Default: 0.8 (80% field overlap triggers warning)\n  detectInjection?: boolean;           // Default: true\n  checkDescriptionQuality?: boolean;   // Default: true\n  minDescriptionLength?: number;       // Default: 20\n  enforceNaming?: boolean;             // Default: true\n  namingPattern?: RegExp;              // Default: /^[a-z][a-z0-9]*-[a-z][a-z0-9-]*$/\n  skipCategories?: string[];           // Categories to exclude from analysis\n  strict?: boolean;                    // Treat warnings as errors\n  suppressions?: string[];             // Suppress specific findings\n  additionalInjectionPatterns?: InjectionPattern[];\n  checkSchemaComplexity?: boolean;    // Default: true\n  schemaComplexityThreshold?: number; // Default: 13 (warning threshold)\n}\n```\n\n### Suppressions\n\nSuppress findings at the rule level or for specific command pairs:\n\n```typescript\nconst result = validateCommandSurface(commands, {\n  suppressions: [\n    'missing-category',                        // Suppress all missing-category findings\n    'schema-complexity:auth-sign-in',           // Suppress for a single command\n    'similar-descriptions:user-get:user-fetch', // Suppress only this pair (order-independent)\n  ],\n});\n```\n\n### CLI Integration\n\n```bash\n# Run surface validation against a connected MCP server\nafd validate --surface\n\n# With custom threshold\nafd validate --surface --similarity-threshold 0.8\n\n# Skip categories and suppress rules\nafd validate --surface --skip-category internal --suppress missing-category\n\n# Strict mode (warnings = errors)\nafd validate --surface --strict --verbose\n```\n\n## Agent Integration (Phase 3)\n\nMCP server and tools for AI agent integration.\n\n### MCP Server\n\nStart an MCP server exposing all scenario commands:\n\n```typescript\nimport { createMcpTestingServer, runStdioServer } from '@lushly-dev/afd-testing';\n\n// Create server with command handler\nconst server = createMcpTestingServer({\n  handler: async (command, input) => registry.execute(command, input),\n});\n\n// Start with stdio transport\nawait runStdioServer(server);\n```\n\n### MCP Tools\n\nAll scenario commands are exposed as MCP tools:\n\n| Tool | Description |\n|------|-------------|\n| `scenario_list` | List and filter scenarios |\n| `scenario_evaluate` | Run scenarios with reporting |\n| `scenario_coverage` | Calculate coverage metrics |\n| `scenario_create` | Generate scenario files |\n| `scenario_suggest` | AI-powered suggestions |\n\n### Agent Hints\n\nAll results include `_agentHints` for AI interpretation:\n\n```typescript\nimport { enhanceWithAgentHints } from '@lushly-dev/afd-testing';\n\nconst result = await scenarioEvaluate({ handler, directory });\nconst enhanced = enhanceWithAgentHints(result, 'scenario.evaluate');\n\n// Result includes:\n// _agentHints: {\n//   shouldRetry: false,\n//   relatedCommands: ['scenario.suggest --context failed'],\n//   nextSteps: ['Review failed scenarios', 'Run with --verbose'],\n//   interpretationConfidence: 0.95\n// }\n```\n\n### scenario.suggest\n\nAI-powered scenario suggestions based on context:\n\n```typescript\nimport { scenarioSuggest } from '@lushly-dev/afd-testing';\n\n// Suggest based on changed files\nconst changed = await scenarioSuggest({\n  context: 'changed-files',\n  files: ['src/commands/todo/create.ts'],\n});\n\n// Suggest for uncovered commands\nconst uncovered = await scenarioSuggest({\n  context: 'uncovered',\n  directory: './scenarios',\n  knownCommands: ['todo-create', 'todo-list', 'todo-delete'],\n});\n\n// Suggest for failed scenarios\nconst failed = await scenarioSuggest({\n  context: 'failed',\n  directory: './scenarios',\n});\n\n// Suggest test variations for a command\nconst command = await scenarioSuggest({\n  context: 'command',\n  command: 'todo-create',\n  includeSkeleton: true,  // Include generated scenario YAML\n});\n\n// Natural language query\nconst natural = await scenarioSuggest({\n  context: 'natural',\n  query: 'error handling for invalid input',\n});\n\nfor (const s of changed.data.suggestions) {\n  console.log(`${s.name} (${s.confidence}): ${s.reason}`);\n}\n```\n\n## App Adapters (Phase 4)\n\nAdapters enable the framework to work with different AFD applications.\n\n### Adapter Interface\n\nEach app provides an adapter implementing:\n\n```typescript\ninterface AppAdapter {\n  name: string;          // e.g., 'todo', 'violet', 'noisett'\n  version: string;\n  cli: CliConfig;        // CLI command configuration\n  fixture: FixtureConfig; // How to apply/reset fixtures\n  commands: CommandsConfig; // Available commands\n  errors: ErrorsConfig;   // Known error codes\n  jobs: JobsConfig;       // User goals/jobs\n}\n```\n\n### Using the Registry\n\n```typescript\nimport {\n  registerAdapter,\n  detectAdapter,\n  todoAdapter,\n  createGenericAdapter,\n} from '@lushly-dev/afd-testing';\n\n// Register built-in adapter\nregisterAdapter(todoAdapter);\n\n// Create and register custom adapter\nconst myAdapter = createGenericAdapter('myapp', {\n  commands: ['myapp-create', 'myapp-list'],\n  errors: ['NOT_FOUND', 'VALIDATION_ERROR'],\n});\nregisterAdapter(myAdapter);\n\n// Auto-detect adapter from fixture\nconst fixture = { app: 'todo', todos: [] };\nconst adapter = detectAdapter(fixture);\nconsole.log(adapter?.name); // 'todo'\n```\n\n### Built-in Adapters\n\n| Adapter | App | Description |\n|---------|-----|-------------|\n| `todoAdapter` | Todo | The AFD Todo example app |\n| `genericAdapter` | Generic | Fallback for unknown apps |\n\n### Creating a Custom Adapter\n\n```typescript\nimport { createGenericAdapter, type AppAdapter } from '@lushly-dev/afd-testing';\n\n// Simple approach: use factory\nconst myAdapter = createGenericAdapter('myapp', {\n  version: '1.0.0',\n  cliCommand: 'myapp-cli',\n  commands: ['myapp-create', 'myapp-list', 'myapp-delete'],\n  errors: ['NOT_FOUND', 'INVALID_INPUT'],\n  jobs: ['manage-items', 'cleanup'],\n});\n\n// Advanced: full custom adapter\nconst customAdapter: AppAdapter = {\n  name: 'custom',\n  version: '1.0.0',\n  cli: {\n    command: 'custom-cli',\n    inputFormat: 'json-arg',\n    outputFormat: 'json',\n  },\n  fixture: {\n    async apply(fixture, context) {\n      // Custom fixture application logic\n      return { appliedCommands: [] };\n    },\n    async reset(context) {\n      // Reset app state\n    },\n  },\n  commands: {\n    list: () => ['custom-create', 'custom-delete'],\n  },\n  errors: {\n    list: () => ['ERROR_ONE', 'ERROR_TWO'],\n  },\n  jobs: {\n    list: () => ['job-one'],\n  },\n};\n```\n\n## Scenario Commands (Phase 2)\n\nBatch operations and management commands for JTBD scenarios.\n\n### scenario.list\n\nList and filter scenarios in a directory.\n\n```typescript\nimport { scenarioList } from '@lushly-dev/afd-testing';\n\n// List all scenarios\nconst result = await scenarioList({ directory: './scenarios' });\n\n// Filter by job\nconst filtered = await scenarioList({\n  directory: './scenarios',\n  job: 'todo-management',\n});\n\n// Filter by tags\nconst tagged = await scenarioList({\n  directory: './scenarios',\n  tags: ['smoke', 'p0'],\n});\n\n// Search in scenario names\nconst searched = await scenarioList({\n  directory: './scenarios',\n  search: 'create',\n});\n\n// Sort results\nconst sorted = await scenarioList({\n  directory: './scenarios',\n  sortBy: 'stepCount',\n  sortOrder: 'desc',\n});\n\nconsole.log(`Found ${result.data.total} scenarios`);\nconsole.log(`Filtered: ${result.data.filtered}`);\nfor (const s of result.data.scenarios) {\n  console.log(`  ${s.name}: ${s.stepCount} steps [${s.tags.join(', ')}]`);\n}\n```\n\n### scenario.evaluate\n\nBatch execute scenarios with parallel support and multiple output formats.\n\n```typescript\nimport { scenarioEvaluate } from '@lushly-dev/afd-testing';\n\n// Basic evaluation\nconst result = await scenarioEvaluate({\n  handler: async (command, input) => registry.execute(command, input),\n  directory: './scenarios',\n});\n\nconsole.log(`Exit code: ${result.data.exitCode}`);\nconsole.log(`Passed: ${result.data.report.summary.passedScenarios}`);\n\n// With filtering and fail-fast\nconst filtered = await scenarioEvaluate({\n  handler,\n  directory: './scenarios',\n  job: 'todo-management',\n  tags: ['smoke'],\n  failFast: true,  // Stop on first failure\n});\n\n// Parallel execution\nconst parallel = await scenarioEvaluate({\n  handler,\n  directory: './scenarios',\n  concurrency: 4,  // Run 4 scenarios at once\n  timeout: 30000,  // 30s per scenario\n});\n\n// Output formats for CI\nconst junit = await scenarioEvaluate({\n  handler,\n  directory: './scenarios',\n  format: 'junit',\n  output: './test-results.xml',  // Write to file\n});\n\n// Available formats: 'terminal', 'json', 'junit', 'markdown'\n```\n\n### scenario.coverage\n\nCalculate coverage metrics across commands, errors, and jobs.\n\n```typescript\nimport { scenarioCoverage } from '@lushly-dev/afd-testing';\n\n// Basic coverage\nconst result = await scenarioCoverage({\n  directory: './scenarios',\n});\n\nconsole.log(`Commands tested: ${result.data.summary.commands.tested}`);\nconsole.log(`Jobs covered: ${result.data.summary.jobs.count}`);\n\n// Coverage against known commands\nconst detailed = await scenarioCoverage({\n  directory: './scenarios',\n  knownCommands: ['todo-create', 'todo-list', 'todo-get', 'todo-update', 'todo-delete'],\n  knownErrors: ['NOT_FOUND', 'VALIDATION_ERROR', 'UNAUTHORIZED'],\n});\n\nconsole.log(`Command coverage: ${detailed.data.summary.commands.coverage}%`);\nconsole.log(`Untested commands:`, detailed.data.summary.commands.untested);\n\n// Per-command details\nfor (const cmd of detailed.data.commandCoverage) {\n  console.log(`${cmd.command}: ${cmd.stepCount} steps, error tests: ${cmd.hasErrorTests}`);\n}\n\n// Format for reporting\nconst markdown = await scenarioCoverage({\n  directory: './scenarios',\n  format: 'markdown',\n});\nconsole.log(markdown.data.formattedOutput);\n```\n\n### scenario.create\n\nGenerate scenario files from templates.\n\n```typescript\nimport { scenarioCreate, listTemplates } from '@lushly-dev/afd-testing';\n\n// See available templates\nconst templates = listTemplates();\n// [\n//   { name: 'blank', description: 'Empty scenario with just job and description' },\n//   { name: 'crud', description: 'Create, Read, Update, Delete test pattern' },\n//   { name: 'error-handling', description: 'Tests for error cases and validation' },\n//   { name: 'workflow', description: 'Multi-step workflow with state verification' },\n// ]\n\n// Create blank scenario\nconst result = await scenarioCreate({\n  name: 'my-new-scenario',\n  job: 'My user job',\n  description: 'Tests the my user job workflow',\n  directory: './scenarios',\n  tags: ['smoke', 'p0'],\n});\n\n// Create from CRUD template\nconst crud = await scenarioCreate({\n  name: 'todo-crud',\n  job: 'Manage todo items',\n  directory: './scenarios',\n  template: 'crud',  // Generates create/read/update/delete/verify steps\n});\n\n// Create error handling tests\nconst errors = await scenarioCreate({\n  name: 'todo-errors',\n  job: 'Handle todo errors',\n  directory: './scenarios',\n  template: 'error-handling',  // Generates validation and not-found tests\n});\n\n// Create with custom steps\nconst custom = await scenarioCreate({\n  name: 'custom-workflow',\n  job: 'Custom workflow',\n  directory: './scenarios',\n  steps: [\n    { description: 'Step 1', command: 'action-first', expectSuccess: true },\n    { description: 'Step 2', command: 'action-second', expectData: { status: 'done' } },\n  ],\n});\n```\n\n## JTBD Scenario Runner\n\nTest user journeys and jobs-to-be-done through YAML scenario files.\n\n### Scenario Structure\n\n```yaml\n# scenarios/create-and-complete-todo.scenario.yaml\nscenario:\n  name: \"Create and complete a todo\"\n  description: \"Tests the complete lifecycle of a todo item\"\n  tags: [\"smoke\", \"crud\"]\n\nsetup:\n  fixture:\n    file: \"fixtures/seeded-todos.json\"\n\nsteps:\n  - name: \"Create a new todo\"\n    command: todo-create\n    input:\n      title: \"Buy groceries\"\n      priority: \"high\"\n    expect:\n      success: true\n      data:\n        title: \"Buy groceries\"\n        completed: false\n\n  - name: \"Complete the todo\"\n    command: todo-toggle\n    input:\n      id: \"${{ steps[0].data.id }}\"  # Reference previous step\n    expect:\n      success: true\n      data:\n        completed: true\n\n  - name: \"Delete the todo\"\n    command: todo-delete\n    input:\n      id: \"${{ steps[0].data.id }}\"\n    expect:\n      success: true\n```\n\n### Running Scenarios\n\n```typescript\nimport { parseScenarioString, InProcessExecutor, TerminalReporter } from '@lushly-dev/afd-testing';\nimport { readFile } from 'node:fs/promises';\n\n// Parse scenario file\nconst yaml = await readFile('scenarios/my-scenario.yaml', 'utf-8');\nconst parseResult = parseScenarioString(yaml);\n\nif (!parseResult.success) {\n  console.error('Parse error:', parseResult.error);\n  process.exit(1);\n}\n\n// Create executor with your command handler\nconst executor = new InProcessExecutor(\n  async (command, input) => {\n    // Execute command against your system\n    return myCommandRegistry.execute(command, input);\n  },\n  { basePath: './scenarios' }\n);\n\n// Run scenario\nconst result = await executor.run(parseResult.scenario);\n\n// Report results\nconst reporter = new TerminalReporter();\nreporter.report([result]);\n\n// Exit with appropriate code\nprocess.exit(result.status === 'passed' ? 0 : 1);\n```\n\n### Dry Run Mode\n\nValidate scenarios without executing them using `validateScenario()`:\n\n```typescript\nimport { parseScenarioString, validateScenario, InProcessExecutor } from '@lushly-dev/afd-testing';\n\n// Validate scenario structure before execution\nconst validation = validateScenario(parseResult.scenario, {\n  availableCommands: ['todo-create', 'todo-get', 'todo-list', 'todo-toggle'],\n  fixtures: fixtureIndex,  // Optional: Map<string, FixtureData>\n});\n\nif (!validation.valid) {\n  console.error('Validation errors:', validation.errors);\n  // Example: [\"Unknown command 'todo-unknown' in step 3\"]\n  process.exit(1);\n}\n\n// Or use dryRun option in executor\nconst executor = new InProcessExecutor(handler, {\n  basePath: './scenarios',\n  dryRun: true,  // Validates but doesn't execute\n});\n\nconst result = await executor.run(scenario);\n// result.steps will have status 'skipped' with reason\n```\n\n### Error Messages\n\nWhen assertions fail, detailed messages show expected vs actual values:\n\n```typescript\n// Example assertion failure output:\n// \"2 assertions failed: data.total: expected 99, got 2; data.completed: expected true, got false\"\n\n// In scenario results:\n{\n  name: \"Check stats\",\n  status: \"failed\",\n  error: \"2 assertions failed: data.total: expected 99, got 2; data.completed: expected true, got false\"\n}\n```\n\n### Fixtures\n\nFixtures pre-seed test data before scenario execution.\n\n#### JSON Fixture File\n\n```json\n// fixtures/seeded-todos.json\n{\n  \"app\": \"todo\",\n  \"clearFirst\": true,\n  \"todos\": [\n    { \"title\": \"Existing todo 1\", \"priority\": \"high\" },\n    { \"title\": \"Existing todo 2\", \"priority\": \"low\", \"completed\": true }\n  ]\n}\n```\n\n#### Using Fixtures in Scenarios\n\n```yaml\nsetup:\n  fixture:\n    file: \"fixtures/base.json\"        # Main fixture file\n    base: \"fixtures/common.json\"       # Optional base (inherited)\n    overrides:                         # Optional inline overrides\n      todos:\n        - title: \"Override todo\"\n```\n\n#### Fixture Inheritance\n\n```yaml\n# Override priority of merge: base → file → overrides\nsetup:\n  fixture:\n    base: \"common-setup.json\"          # Applied first\n    file: \"specific-setup.json\"        # Merged on top\n    overrides:                         # Highest priority\n      clearFirst: false\n```\n\n#### Supported Fixture Structures\n\n**Todo App:**\n```json\n{\n  \"app\": \"todo\",\n  \"clearFirst\": true,\n  \"todos\": [\n    { \"title\": \"string\", \"priority\": \"low|medium|high\", \"completed\": false }\n  ]\n}\n```\n\n**Violet Design System:**\n```json\n{\n  \"app\": \"violet\",\n  \"nodes\": [\n    { \"id\": \"string\", \"name\": \"string\", \"type\": \"root|product\", \"parentId\": \"string\" }\n  ],\n  \"operations\": [\n    { \"type\": \"add|override|subtract\", \"nodeId\": \"string\", \"token\": \"string\", \"value\": \"any\" }\n  ],\n  \"constraints\": [\n    { \"nodeId\": \"string\", \"id\": \"string\", \"type\": \"enum|range\", \"tokens\": [\"string\"] }\n  ]\n}\n```\n\n**Generic (Custom Apps):**\n```json\n{\n  \"app\": \"custom\",\n  \"setup\": [\n    { \"command\": \"custom-init\", \"input\": { \"key\": \"value\" } }\n  ]\n}\n```\n\n#### Programmatic Fixture Loading\n\nUse `loadFixture()` and `applyFixture()` for direct fixture handling:\n\n```typescript\nimport { loadFixture, applyFixture, AppliedCommand } from '@lushly-dev/afd-testing';\n\n// Load fixture from file\nconst fixture = await loadFixture('fixtures/test-data.json', {\n  basePath: './scenarios',\n  baseFile: 'fixtures/common.json',  // Optional inheritance\n  overrides: { clearFirst: true },    // Optional inline overrides\n});\n\n// Apply fixture to your system\nconst result = await applyFixture(fixture, async (command, input) => {\n  return myRegistry.execute(command, input);\n});\n\n// Result includes applied commands with full details\nconsole.log(result.appliedCommands);\n// [\n//   { command: 'store-clear', input: {} },\n//   { command: 'todo-create', input: { title: 'Test', priority: 'high' } }\n// ]\nconsole.log(`Applied ${result.appliedCommands.length} commands`);\n```\n\n### Step References\n\nReference data from previous steps using `${{ steps[N].path }}` syntax.\n\n#### Reference Syntax\n\n```yaml\n# Exact reference (preserves type)\ninput:\n  id: \"${{ steps[0].data.id }}\"           # Returns actual type (string, number, etc.)\n\n# Embedded reference (string interpolation)  \ninput:\n  message: \"Created todo ${{ steps[0].data.id }}\"  # Returns string\n\n# Nested paths\ninput:\n  name: \"${{ steps[0].data.user.profile.name }}\"\n\n# Array access\ninput:\n  firstItem: \"${{ steps[0].data.items[0].name }}\"\n```\n\n#### Reference Examples\n\n```yaml\nsteps:\n  - name: \"Create user\"\n    command: user-create\n    input:\n      email: \"test@example.com\"\n    # Result: { data: { id: \"user-123\", email: \"test@example.com\" } }\n\n  - name: \"Create todo for user\"\n    command: todo-create\n    input:\n      title: \"My todo\"\n      userId: \"${{ steps[0].data.id }}\"     # → \"user-123\"\n    # Result: { data: { id: \"todo-456\" } }\n\n  - name: \"Get todo\"\n    command: todo-get\n    input:\n      id: \"${{ steps[1].data.id }}\"         # → \"todo-456\"\n\n  - name: \"Verify ownership\"\n    command: todo-verify\n    input:\n      todoId: \"${{ steps[1].data.id }}\"     # → \"todo-456\"\n      userId: \"${{ steps[0].data.id }}\"     # → \"user-123\"\n```\n\n### Expectations\n\n#### Success Expectations\n\n```yaml\nexpect:\n  success: true\n  data:\n    title: \"Expected title\"\n    completed: false\n```\n\n#### Partial Data Matching\n\n```yaml\nexpect:\n  success: true\n  data:\n    title: \"Expected title\"     # Only checks title\n    # Other fields ignored\n```\n\n#### Error Expectations\n\n```yaml\nexpect:\n  success: false\n  error:\n    code: \"NOT_FOUND\"\n    message: \"Todo not found\"   # Optional\n```\n\n## Usage\n\n### Testing Commands\n\n```typescript\nimport { testCommand, assertSuccess, assertHasReasoning } from '@lushly-dev/afd-testing';\nimport { describe, it, expect } from 'vitest';\nimport { myCommand } from './my-command';\n\ndescribe('myCommand', () => {\n  it('returns a valid result', async () => {\n    const test = await testCommand(myCommand.handler, {\n      input: 'test value'\n    });\n\n    // Check basic validity\n    expect(test.isValid).toBe(true);\n    expect(test.isSuccess).toBe(true);\n\n    // Use assertions\n    assertSuccess(test.result);\n    assertHasReasoning(test.result);\n\n    // Check data\n    expect(test.result.data).toBeDefined();\n  });\n\n  it('handles errors correctly', async () => {\n    const test = await testCommand(myCommand.handler, {\n      input: '' // Invalid input\n    });\n\n    expect(test.isFailure).toBe(true);\n    expect(test.result.error?.suggestion).toBeDefined();\n  });\n});\n```\n\n### Validating Command Definitions\n\n```typescript\nimport { validateCommandDefinition, validateResult } from '@lushly-dev/afd-testing';\n\n// Validate a command definition\nconst defValidation = validateCommandDefinition(myCommand);\nconsole.log('Definition valid:', defValidation.valid);\nconsole.log('Errors:', defValidation.errors);\nconsole.log('Warnings:', defValidation.warnings);\n\n// Validate a result with options\nconst resultValidation = validateResult(result, {\n  requireConfidence: true,\n  requireReasoning: true,\n  requireSources: true,\n});\n```\n\n### Testing Multiple Cases\n\n```typescript\nimport { testCommandMultiple } from '@lushly-dev/afd-testing';\n\nconst results = await testCommandMultiple(myCommand.handler, [\n  {\n    input: { title: 'Valid Title' },\n    expectSuccess: true,\n    description: 'Valid input should succeed',\n  },\n  {\n    input: { title: '' },\n    expectSuccess: false,\n    expectError: 'VALIDATION_ERROR',\n    description: 'Empty title should fail',\n  },\n  {\n    input: {},\n    expectSuccess: false,\n    description: 'Missing title should fail',\n  },\n]);\n\n// Check all tests passed\nconst allPassed = results.every(r => r.passed);\n```\n\n### Custom Assertions\n\n```typescript\nimport {\n  assertSuccess,\n  assertFailure,\n  assertErrorCode,\n  assertConfidence,\n  assertHasSources,\n  assertAiResult,\n} from '@lushly-dev/afd-testing';\n\n// Basic assertions\nassertSuccess(result); // Throws if not success\nassertFailure(result); // Throws if not failure\n\n// Error assertions\nassertErrorCode(result, 'NOT_FOUND');\nassertHasSuggestion(result);\nassertRetryable(result, true);\n\n// UX field assertions\nassertConfidence(result, 0.8); // Minimum confidence\nassertHasReasoning(result);\nassertHasSources(result, 2); // At least 2 sources\nassertHasPlan(result);\nassertStepStatus(result, 'step-1', 'complete');\n\n// AI result assertion (checks all UX fields)\nassertAiResult(result, {\n  minConfidence: 0.7,\n  requireSources: true,\n  requireAlternatives: false,\n});\n```\n\n### Using Mock Server\n\n```typescript\nimport { createMockServer, createMockCommand } from '@lushly-dev/afd-testing';\n\n// Create server with mock commands\nconst server = createMockServer([\n  createMockCommand('document-get', (input) => ({\n    id: input.id,\n    title: 'Test Document',\n  })),\n]);\n\n// Add more commands\nserver.register(createMockCommand('document-create', (input) => ({\n  id: 'new-id',\n  title: input.title,\n})));\n\n// Handle requests\nconst response = await server.handleRequest({\n  jsonrpc: '2.0',\n  id: 1,\n  method: 'tools/call',\n  params: {\n    name: 'document-get',\n    arguments: { id: 'doc-123' },\n  },\n});\n\n// Check request log\nconst log = server.getRequestLog();\nconsole.log('Requests made:', log.length);\n```\n\n### Creating Mock Commands\n\n```typescript\nimport {\n  createMockCommand,\n  createSuccessCommand,\n  createFailureCommand,\n  createTestRegistry,\n} from '@lushly-dev/afd-testing';\n\n// Simple mock that returns static data\nconst getUser = createSuccessCommand('user-get', {\n  id: 'user-1',\n  name: 'Test User',\n});\n\n// Mock with dynamic behavior\nconst createDoc = createMockCommand('document-create', (input) => ({\n  id: `doc-${Date.now()}`,\n  title: input.title,\n  createdAt: new Date().toISOString(),\n}));\n\n// Mock that always fails\nconst deleteProtected = createFailureCommand('protected-delete', {\n  code: 'FORBIDDEN',\n  message: 'Cannot delete protected resource',\n});\n\n// Create registry with all mocks\nconst registry = createTestRegistry([getUser, createDoc, deleteProtected]);\n\n// Execute commands\nconst result = await registry.execute('document-create', { title: 'New Doc' });\n```\n\n## Validation Rules\n\n### Command Definition Validation\n\n| Rule | Severity | Description |\n|------|----------|-------------|\n| `MISSING_NAME` | Error | Command must have a name |\n| `INVALID_NAME_FORMAT` | Warning | Name should use kebab-case (`domain-action`) |\n| `MISSING_DESCRIPTION` | Error | Command must have a description |\n| `SHORT_DESCRIPTION` | Warning | Description should be detailed |\n| `MISSING_PARAMETERS` | Error | Command must have parameters array |\n| `MISSING_HANDLER` | Error | Command must have a handler function |\n| `MISSING_CATEGORY` | Warning | Command should have a category |\n| `MISSING_ERROR_DOCS` | Warning | Command should document error codes |\n\n### Result Validation\n\n| Rule | Severity | Description |\n|------|----------|-------------|\n| `INVALID_SUCCESS_TYPE` | Error | success must be boolean |\n| `MISSING_DATA` | Warning | Success result should have data |\n| `MISSING_ERROR` | Error | Failed result must have error |\n| `MISSING_CONFIDENCE` | Warning | AI commands should have confidence |\n| `INVALID_CONFIDENCE_RANGE` | Error | Confidence must be 0-1 |\n| `MISSING_REASONING` | Warning | AI commands should have reasoning |\n| `MISSING_SOURCES` | Warning | External data commands should have sources |\n\n### Error Validation\n\n| Rule | Severity | Description |\n|------|----------|-------------|\n| `INVALID_ERROR_CODE` | Error | Error code must be a string |\n| `INVALID_ERROR_MESSAGE` | Error | Error message must be a string |\n| `MISSING_SUGGESTION` | Warning | Errors should have suggestions |\n| `MISSING_RETRYABLE` | Warning | Errors should indicate if retryable |\n\n### Surface Validation Rules\n\n| Rule | Severity | Description |\n|------|----------|-------------|\n| `similar-descriptions` | Warning | Command pair descriptions exceed similarity threshold |\n| `schema-overlap` | Warning | Command pair input schemas share too many fields |\n| `naming-convention` | Error | Command name doesn't match naming pattern |\n| `naming-collision` | Error | Command names collide when separators are removed |\n| `missing-category` | Info | Command has no category assigned |\n| `description-injection` | Error | Description contains prompt injection patterns |\n| `description-quality` | Warning | Description too short or missing action verb |\n| `orphaned-category` | Info | Category contains only one command |\n| `schema-complexity` | Warning/Info | Input schema too complex for agents (scored by fields, depth, unions, constraints) |\n| `unresolved-prerequisite` | Error | `requires` entry references a command not in the surface |\n| `circular-prerequisite` | Error | Circular dependency cycle in the `requires` graph |\n| `missing-output-schema` | Info | Command does not declare an `output` schema |\n| `missing-context` | Info | Command has no `contexts` array (when server has configured contexts) |\n\n## License\n\nMIT\n","readmeFilename":"README.md"}