{"_id":"@cjlapao/loop-state-mcp","_rev":"20-4a91fea9315a23d147663f4e44d48721","name":"@cjlapao/loop-state-mcp","dist-tags":{"latest":"0.5.2"},"versions":{"0.1.1":{"name":"@cjlapao/loop-state-mcp","version":"0.1.1","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.1","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"047a1d7c76382950b2fc9e724e8554cd93663928","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.1.tgz","fileCount":51,"integrity":"sha512-kPjpPBWE8DuGBLdPHnBRPr8WPow/ZjzD62fSXILN8L0UJiBXVVJSRe6Pa+bT0+unv7VfEjvqVQO13iNltyhy9A==","signatures":[{"sig":"MEUCIBBqPEJoGORJf3AkVk3P78Z/G2E5vMxXRSpwJswuvihmAiEAon4PGorIy67ctuhDJ5EQX1CaeQxpC+sIM5EVrZcqKGc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":145349},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"}},"gitHead":"b4349c38a2ca30cb83548bdc209db456183a98b3","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"_npmVersion":"11.12.1","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.1_1785962509093_0.6350094592457398","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@cjlapao/loop-state-mcp","version":"0.1.3","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.3","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"fb8dc85d47271e4af0313941303996b1f9248ec3","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.3.tgz","fileCount":51,"integrity":"sha512-s7NmP/4GlCP+Lixsyu8twpM2jcVPJ64D+GutBX36aXKu2iFN7QdSCxZUeJiMvKzsCNLoYQWciKpisDHbxhSZLQ==","signatures":[{"sig":"MEYCIQCKrs8Iu1pOxxKrawSuolJWn6Zbpz9yAuczHp6HJ6c8tgIhANHWPT7KYxNwUTNfUkbhAQQ4hPpJCcdB/+QTV3P4evQS","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":164392},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"}},"gitHead":"4c4d212aed3f4f2376dd7f5668be8e797026a53c","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.16.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.3_1786012221931_0.5823388631277204","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@cjlapao/loop-state-mcp","version":"0.1.4","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.4","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"20b8dc0bf4c3d14e07d1260acbf1b9dc719dbfcb","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.4.tgz","fileCount":51,"integrity":"sha512-qXSDOOcO16SxgKLR9Fyjd+lsrSmMbs8hIlWd/hRe1Vbwf/6GbgTXe726xL7FKOfjM0Efk+E8a1Fzg4neO1LPnQ==","signatures":[{"sig":"MEQCIETKPFrXIZ74Aki17Yq4c4TXH1lj+VgbqlHlWLgtP3chAiAUrrGk38yvAU0AgAuxbTgPa3h5nRucZJoo43h8F8SWaw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":165056},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"}},"gitHead":"0b6cd4ae852a51552fd59fdceffe0f960096bebb","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.16.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.4_1786022373166_0.12132954376721838","host":"s3://npm-registry-packages-npm-production"}},"0.1.6":{"name":"@cjlapao/loop-state-mcp","version":"0.1.6","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.6","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"ea9e90904a49b732b6eb1690aef56b5aa781a68b","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.6.tgz","fileCount":59,"integrity":"sha512-2iEcVSpPyUv5AAAN7GqjBXjeX/YVDHQzS9ojsmUgmz3XzAxuzac2zymn5znZHOr3ppwZDNRrqCx3SDzWzruf5Q==","signatures":[{"sig":"MEQCIEioejfQhJ1t5KaZ4Ez43UK82ixTuSfJEEnCRMyFpAe3AiAZ9fSdmfAMk4+gXsfpg6dmXU3Cr83B02C7aAi+Upuybw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":178629},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"588449954f38c349d334a59f89ce5a7e502cf760","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.16.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.6_1786081544543_0.42325837566966684","host":"s3://npm-registry-packages-npm-production"}},"0.1.7":{"name":"@cjlapao/loop-state-mcp","version":"0.1.7","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.7","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"0a80ad6feec62cc37bb1c0d6ff3cd68c89382093","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.7.tgz","fileCount":59,"integrity":"sha512-t3FEINVCsEN1VCay1H7PQfz+9k3FGa9X9RNHTcREV3CTlqOqrUxPWf5irgnWdmkA5vFieo9noLuTndrkza0CVA==","signatures":[{"sig":"MEQCIAfk4ZWXEMFzwuBV8jkAoHCUeWDkH05Hmw9CrC3v83yJAiBAINeunCM8CEpCBSeGo6LoJGBn+5qomiL5pRQGmVl1gg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180847},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"53b9ed3c83c880534bd3f8bd9ea5ccd83e1397c4","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.16.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.7_1786088435351_0.19693563096243172","host":"s3://npm-registry-packages-npm-production"}},"0.1.8":{"name":"@cjlapao/loop-state-mcp","version":"0.1.8","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.8","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"f1ba21666b49ca3a11c9c748bc119d6064b7bfc1","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.8.tgz","fileCount":59,"integrity":"sha512-lLHoD9xXg2gFPQlyey0uzZdG8K8BcTk+lLx+nor8uOxY1CQBRdEHfkIW5g8Ec9gVHcn3zr37cKL5folEiiV7iw==","signatures":[{"sig":"MEYCIQC8zmWqh5SzZFURieXodSABS2C5aEgv7D6nA8PVSvsB/QIhAKl0kG7r7nqjeA2sdeXOjJu00LexDn8vAEv4dFC36TXy","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":189935},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"11f26108e9d86d2fc1c997d418efca9eee583cc4","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.17.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.8_1786102529044_0.36969476366405507","host":"s3://npm-registry-packages-npm-production"}},"0.1.10":{"name":"@cjlapao/loop-state-mcp","version":"0.1.10","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.10","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"f11890f96fcea6cf927d44538b6517999a35834c","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.10.tgz","fileCount":59,"integrity":"sha512-nwBL/7ksMIHuzA5Q39EkdjSog5dc5gyueXrq5rKaav4FhV/1aofQYexTduO53Jzyx2AcxHhlA4UWSboPoYrO+A==","signatures":[{"sig":"MEUCIFS4ylNz1MrqW66/iipcpzxgsN5B3y/hdi0lN2owHpW1AiEAqgLgTDf7qw5bY19WqiJLIL6P9t6ehaEYwAhE5ODJYCM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":200904},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"20401ea99467bc20d1797a121ffcd2fac481512e","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.16.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.10_1786107072554_0.4459770759347057","host":"s3://npm-registry-packages-npm-production"}},"0.1.11":{"name":"@cjlapao/loop-state-mcp","version":"0.1.11","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.11","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"cc184e38aae07c00fa20077df96bae53b55c59b3","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.11.tgz","fileCount":63,"integrity":"sha512-J/dt0Poci2tqM5AMCOUiOHHqcGj7C2/ke2So0lPaEodNkR/pmPtEiSPTaIkvrGxnBGcUP87Pp6opn4zFV3BSPQ==","signatures":[{"sig":"MEQCIFJSIRMSVC4YbV4cQHX3o/TnfGI8wQG270n8Tv/uaY5CAiBRLsPqIQfkcSA8oeDsbL++Jp3S5+8yHUW6YwIQbenHbA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":209627},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"d4b0fb2929bfc4c6ad88907c3d2447d2154edd3e","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.17.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.11_1786111991685_0.7190705649083","host":"s3://npm-registry-packages-npm-production"}},"0.1.12":{"name":"@cjlapao/loop-state-mcp","version":"0.1.12","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.12","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"8f2c71fba1f4af2d70b359dc9b8bb4759008e873","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.12.tgz","fileCount":63,"integrity":"sha512-z9pybkXFeZPKzLPzMdzMAIIwhL2wiAdI4DwKs2Astfg3mCLkiLJy4PNEfzCNEFACVux1jIZqzCiUCs7fH/eALA==","signatures":[{"sig":"MEUCIFJOkrVqx3dbvRdZZD4nRPaKNFxZpHqM3dYXmTvPrw0VAiEA/aiTGGOn/nlm4xsJ7HIIUJHqckRfgEw/Tu9tNs2AzqM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":209627},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"18e4d19c903edbcd2d3db8f2442a74b4298fb5a8","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.16.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.12_1786114279197_0.21645518338176606","host":"s3://npm-registry-packages-npm-production"}},"0.1.13":{"name":"@cjlapao/loop-state-mcp","version":"0.1.13","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.13","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"57b80b983408c70335b16feab8edacdc4f006984","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.13.tgz","fileCount":63,"integrity":"sha512-vcIsDDDDxEFoTOdz0r0wRrU+sUunF8jctENf0BLohDSNsTaWoWXXnyIClF5+X3Pl+MZyZ3XN1X0gZvKyRDofDw==","signatures":[{"sig":"MEQCIF2X2jYIu5vgRtiWfKH0B98GhKhafHWfETScWP9S9udFAiAhmZ3LIcICulrhmqZz2NgGLqXKp1WZGr7Md/i39uo/lQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":213447},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"38916c9df2dcfdf6414007719e4b02abc525a7ea","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.16.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.13_1786211091685_0.6034594034851106","host":"s3://npm-registry-packages-npm-production"}},"0.1.15":{"name":"@cjlapao/loop-state-mcp","version":"0.1.15","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.15","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"69fb1574038cad221c139ba393b31bda2bb017c8","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.15.tgz","fileCount":71,"integrity":"sha512-OjYk+zwwVVIi6cMpOjDxR750DlTtWnkm1tgLVn7WisFQoHFahAtRukr4g/Q/r4MAr9HsowzbBLyF1Sk1PvJtqQ==","signatures":[{"sig":"MEYCIQCxMWPERY26ARL3Lw23QGG2uwXVTsUvNh2nayQifAzUwQIhALDZeFoNHtV0fgB3YITCfAurlyG/KebW0ehYD0dUDrEs","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":279727},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"92f00117ab94df8f643976d63fe03d7c6949ca3e","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:76c17113-402d-4782-ba86-30532c58359c"}},"_npmVersion":"11.16.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.15_1786438567966_0.9221681092130936","host":"s3://npm-registry-packages-npm-production"}},"0.1.17":{"name":"@cjlapao/loop-state-mcp","version":"0.1.17","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.17","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"03a76f7db95794fae4983c720cc26c7933ccf434","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.17.tgz","fileCount":79,"integrity":"sha512-vKD5uqfhuEZBvV0NzNNLyhC0C+j4VvBhq2+0hcA5T0DK6JLVVnvWS3+8Z3lCeYm5l/KludACwS/9wipXDWGMHA==","signatures":[{"sig":"MEQCIAJlmh0FgW/xobY3z+UbWNcc8EDt7n3J52Fmue8t6BEZAiBpPtNsug4UVfZZwhlcl7E0lTx84pig4x+Jbo+j2k6FAA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":356056},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"e5297a26a114bedab9fa1b6bf73ff52d600ecbe5","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"_npmVersion":"11.8.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.17_1786515069360_0.8376901186172729","host":"s3://npm-registry-packages-npm-production"}},"0.1.18":{"name":"@cjlapao/loop-state-mcp","version":"0.1.18","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.18","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"fed414a53eb98d57267d5bffb15b89226da1e43a","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.18.tgz","fileCount":79,"integrity":"sha512-o53JVH4lZeQSAGNNZStIP+u0uHBSGcJdIkfZIcjPF+f5Gd62eSdz7g2asu+5ewSKkHs9TLO9Jv2hpqpIUMLNcw==","signatures":[{"sig":"MEUCIQCncMif5g7n/5JXuEjax7yb+uZeNAoeIvJv1h51jE++lwIgabhxMqbAdaF+xySZqffzW1GoKVb1+KTwT5UP11a6cdI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":358940},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"e8a5428538f42681692d2d2f972951908d46e8e9","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"_npmVersion":"11.8.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.18_1786596545321_0.5410843598571584","host":"s3://npm-registry-packages-npm-production"}},"0.1.20":{"name":"@cjlapao/loop-state-mcp","version":"0.1.20","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.1.20","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"394696f2f1ba95a0c849d74cab7150a53a1247d6","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.1.20.tgz","fileCount":91,"integrity":"sha512-B1Dcd7A30w3B3m2mkqODJldpxABqI5aK5G3O8uDB7zNCxwKg1WqjhgI+IVbgPjEU0bSN5738uNzOngU9QPcfwA==","signatures":[{"sig":"MEQCIC2sNWYZ+pfV+RtPopeSMAU3LoR0swF2tr9ZtBIUHIKGAiAxZVGSICTklMpo+aQB5QreZTVPQBvmoBVm4beEfsS+0Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":444405},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"da933022bb5898555ace8459c033ea0bed990fb4","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"_npmVersion":"11.8.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.1.20_1786692086962_0.9878283228616","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@cjlapao/loop-state-mcp","version":"0.4.1","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.4.1","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"c2b5679af9a21dd47d8f22b2f4f003338e310f3d","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.4.1.tgz","fileCount":111,"integrity":"sha512-piEMt9zVl7RGSqcMywc6A9yxORFxCGJAYMOzFrH7U3SxMco0K2DC9TzlZQA1B5yxW4JlC0se0C7IADKGseYMXg==","signatures":[{"sig":"MEYCIQDfAQtot4gRDRzKnWaGcurr7rRW2M5MzmyYA1M2juZCHQIhAN8B6hDwp76qzzSB2xmEvP4/ApI5OaKFPWAKD/YmzY21","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":602080},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"55f5e3447ce203c1463bf0dbed7fce7d08dfc845","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"_npmVersion":"11.8.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.4.1_1787032062832_0.020007227396601657","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"name":"@cjlapao/loop-state-mcp","version":"0.4.2","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.4.2","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"e9302888480e601e08742775923d98a5b488a8a9","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.4.2.tgz","fileCount":111,"integrity":"sha512-z2PHCSiUJJMUAJV6/2ed1H2FDvjT6Q5TDP7HfSRKx9EMmv6vnW3gyNRu1lcXqFnLB9sAPb78Vvbk2EJM//MXmw==","signatures":[{"sig":"MEUCIA5nbtbrVjLTKojj07jtgkzB5k8zC7FJDtRafKwY8UAlAiEAimlS3wfY7rTjKfji7mfQ0XqKUhh7rleuITKqWoHNMEM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":613313},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"dbb3238451c5b1f70692632d613ba78260c1e1bb","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"_npmVersion":"11.6.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.10.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.4.2_1787054729707_0.5637569246411069","host":"s3://npm-registry-packages-npm-production"}},"0.4.3":{"name":"@cjlapao/loop-state-mcp","version":"0.4.3","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.4.3","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"c0b59fcca7dbc69280fd29f7f3435ab53e4e444f","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.4.3.tgz","fileCount":111,"integrity":"sha512-5YnIzfS39t+UzD4GC6G7RT5r2gPFsvN/P3By8nRjUuA9VYbZnATOuSQpNX84FQYFOxX70Sy+NR/Q+T0N6NucOQ==","signatures":[{"sig":"MEUCIEguj2VzyMO06k0KK70ZemJLZohyJj8KObMV9ls0Ni3cAiEA8XD65lQJWHxrl8mECaTb5oM2XLP78WszaDQe89kbF6o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":618331},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"b24db365483be16c6696d74f2f2538d586316e0a","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"_npmVersion":"11.6.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.10.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.4.3_1787056804954_0.037280029116021574","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@cjlapao/loop-state-mcp","version":"0.5.0","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.5.0","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"993d57c1f22d1dbaa29ea8a3999ae0c5de066b91","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.5.0.tgz","fileCount":115,"integrity":"sha512-zrC35ZCCurY+4PJdu0iQUKs/kssc/yQTUzcKnXY9J63KLON/r8zeI+hnKpPTNE7mgko4GmYnVD/hikjtoNOAFA==","signatures":[{"sig":"MEQCICt/HDUVxtQyOPSnoWxxPR3AJMa6gl0Z4BuGPotjZwS9AiAKE75npG+xfaLS8elvCz58kap+vd+4GC+XvENpHhdqtg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":660407},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"a69363a74af59b8b56dd4e000531d80d8e5a57e2","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"_npmVersion":"11.8.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.5.0_1788330797967_0.2701638678426663","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"@cjlapao/loop-state-mcp","version":"0.5.1","author":{"name":"Carlos Lapao"},"license":"MIT","_id":"@cjlapao/loop-state-mcp@0.5.1","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"bin":{"loop-state-mcp":"build/index.js"},"dist":{"shasum":"fd137abd0499fd9432ca14f18c404ccbdcb731b7","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.5.1.tgz","fileCount":115,"integrity":"sha512-5E6f2zEEk9El+iAPCXe4E/ME6nyDgm9pBUx7Ry7o3cyZDSFk/zTbDwz4wz3JAlyqV5p8inZ4j9s03UthMRfC8g==","signatures":[{"sig":"MEUCIA06m9mRGa2Qu62KXr8u3B8fp2jJgAY9jNOaTcxttQ2xAiEAlqTGIzP9sCG7tNEB+PStfe6CuB6SFSKhxcF4DuztLdc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":660407},"main":"./build/index.js","type":"module","types":"./build/index.d.ts","module":"./build/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"gitHead":"a69363a74af59b8b56dd4e000531d80d8e5a57e2","scripts":{"dev":"node build/index.js","lint":"eslint src test --ext .ts","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf build coverage","start":"node build/index.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"_npmVersion":"11.8.0","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^4.4.3","better-sqlite3":"^13.0.2","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9","vitest":"^3","prettier":"^3","@eslint/js":"^9","typescript":"^5.6","@types/node":"^24","typescript-eslint":"^8","@vitest/coverage-v8":"^3.2.7","@types/better-sqlite3":"^9.6.0"},"_npmOperationalInternal":{"tmp":"tmp/loop-state-mcp_0.5.1_1788330823368_0.7696212154146602","host":"s3://npm-registry-packages-npm-production"}},"0.5.2":{"name":"@cjlapao/loop-state-mcp","version":"0.5.2","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","license":"MIT","author":{"name":"Carlos Lapao"},"main":"./build/index.js","module":"./build/index.js","type":"module","exports":{".":{"types":"./build/index.d.ts","default":"./build/index.js"},"./lib":{"types":"./build/lib.d.ts","default":"./build/lib.js"}},"bin":{"loop-state-mcp":"build/index.js"},"publishConfig":{"access":"public"},"scripts":{"clean":"rm -rf build coverage","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","lint":"eslint src test --ext .ts","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test":"vitest run","test:coverage":"vitest run --coverage","start":"node build/index.js","dev":"node build/index.js"},"engines":{"node":">=24"},"dependencies":{"@modelcontextprotocol/server":"^2.0.0","better-sqlite3":"^13.0.2","zod":"^4.4.3"},"devDependencies":{"@eslint/js":"^9","@types/better-sqlite3":"^9.6.0","@types/node":"^24","@vitest/coverage-v8":"^3.2.7","eslint":"^9","prettier":"^3","typescript":"^5.6","typescript-eslint":"^8","vitest":"^3"},"gitHead":"a69363a74af59b8b56dd4e000531d80d8e5a57e2","types":"./build/index.d.ts","_id":"@cjlapao/loop-state-mcp@0.5.2","_nodeVersion":"24.12.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-Bhh/4EuW6Yv779Vn+mKnI6jjFfdd3qFDLgX6wc8CZuITqsiG/W32kZwUd15K5YOrmyE6kX6mwWQj3mg/oDHIaA==","shasum":"96b1e0b82e37015c773308268add215d9ae0a8de","tarball":"https://registry.npmjs.org/@cjlapao/loop-state-mcp/-/loop-state-mcp-0.5.2.tgz","fileCount":119,"unpackedSize":733778,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHcoozQdtyuDQ2dZMjCQXqPoB6jjUrShngLeSsckKd8IAiEAwWFbJQ2pxwu5nixTTN8UwRUYPHUOvzC8haPq69Lyj/g="}]},"_npmUser":{"name":"cjlapao","email":"cjlapao@gmail.com"},"directories":{},"maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/loop-state-mcp_0.5.2_1788331719838_0.44134114662648405"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-05T20:41:48.958Z","modified":"2026-09-02T06:48:40.137Z","0.1.1":"2026-08-05T20:41:49.244Z","0.1.3":"2026-08-06T10:30:22.090Z","0.1.4":"2026-08-06T13:19:33.396Z","0.1.6":"2026-08-07T05:45:44.689Z","0.1.7":"2026-08-07T07:40:35.565Z","0.1.8":"2026-08-07T11:35:29.190Z","0.1.10":"2026-08-07T12:51:12.699Z","0.1.11":"2026-08-07T14:13:11.830Z","0.1.12":"2026-08-07T14:51:19.342Z","0.1.13":"2026-08-08T17:44:51.820Z","0.1.15":"2026-08-11T08:56:08.110Z","0.1.17":"2026-08-12T06:11:09.598Z","0.1.18":"2026-08-13T04:49:05.491Z","0.1.20":"2026-08-14T07:21:27.164Z","0.4.1":"2026-08-18T05:47:42.990Z","0.4.2":"2026-08-18T12:05:29.868Z","0.4.3":"2026-08-18T12:40:05.094Z","0.5.0":"2026-09-02T06:33:18.122Z","0.5.1":"2026-09-02T06:33:43.504Z","0.5.2":"2026-09-02T06:48:39.980Z"},"author":{"name":"Carlos Lapao"},"license":"MIT","description":"SQLite-backed, schema-validated MCP server owning dev-loops bookkeeping state.","maintainers":[{"name":"cjlapao","email":"cjlapao@gmail.com"}],"readme":"# @cjlapao/loop-state-mcp\n\nA stdio Model Context Protocol server that owns the mutable bookkeeping state of a\n`dev-loops` run in SQLite and renders the human-readable markdown views after every mutation.\n\n## Install & run\n\nThis package is a **stdio-hosted MCP server**: it has no HTTP port, no daemon process,\nand no lifecycle of its own. It reads/writes the MCP JSON-RPC protocol over stdin/stdout\nand exits when the host closes the stream. In normal use opencode starts it as a child\nprocess for each conversation turn; direct execution is for testing or debugging only.\n\n**Preferred — let opencode start it via the config below.** If you need to run it\nstandalone (e.g. to inspect diagnostics), simply launch the binary directly:\n\n```sh\nnpx -y @cjlapao/loop-state-mcp\n```\n\nOr install globally first and invoke the bin entry:\n\n```sh\nnpm install -g @cjlapao/loop-state-mcp\nloop-state-mcp\n```\n\nTo build and run from source:\n\n```sh\nnpm install\nnpm run build\nnode build/index.js\n```\n\nWhen launched directly, diagnostic output goes to **stderr** so stdout stays clean for\nthe MCP protocol; see the `## How it works` section for what gets printed.\n\n## OpenCode MCP configuration\n\nAdd the following to your `opencode.json` under the `mcp` key:\n\n```json\n\"mcp\": {\n  \"loop-state\": {\n    \"type\": \"local\",\n    \"command\": [\"npx\", \"-y\", \"@cjlapao/loop-state-mcp\"],\n    \"enabled\": true\n  }\n}\n```\n\n> **Note:** this MCP is **not pre-registered** in any project config on this repository.\n> You must add the block above yourself before using it. The workspace at\n> `opencode-mcp/opencode.json` contains an example that pins a specific version\n> (`@0.1.6`); omit the version pin if you prefer to always pull the latest.\n\nNo `--root` flag is needed. The rendered views always live under `<cwd>/.projects/loops`,\nand the shared database defaults to `~/.dev-loop-state/loops.db` (override the directory\nwith the `DEV_LOOP_STATE_DIR` environment variable — used by the test suite so it never\ntouches the real home).\n\n## How it works\n\nThe MCP is started once by OpenCode. It resolves two locations:\n\n- the **project root** = the current working directory it was spawned in. This is the\n  stored `project_root` that scopes every loop to its project.\n- the **global database directory** = `~/.dev-loop-state` (or `$DEV_LOOP_STATE_DIR`).\n\n### Loop identity: id / slug / title / goal\n\nA loop's identity is split four ways:\n\n| Field   | What it is                                               | Used for                                                                                              |\n| ------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| `id`    | A UUID v4, minted at `loop_init` (`crypto.randomUUID()`) | The globally-unique, stable handle. The identifier the cross-project browser addresses a loop by.     |\n| `slug`  | A SHORT (≤64-char) kebab summary, supplied by the caller | The FOLDER NAME (`.projects/loops/{slug}/`) and the human/agent addressing key that every tool takes. |\n| `title` | A long human-readable string (defaults to `goal`)        | Display only — the `# Loop — {title}` heading.                                                        |\n| `goal`  | The raw user goal, verbatim                              | Recorded as-is.                                                                                       |\n\n**Tools address a loop by its `slug`, never its uuid.** Every tool takes `loopSlug`;\nthe server resolves `(project_root, slug) → the loop row` (and its uuid `id`) internally,\nso the orchestrator never has to pass a UUID around. The uuid is surfaced by `loop_get`\n(and by `loop.md`'s frontmatter `id:`) so a later cross-project browser can identify a\nloop unambiguously.\n\nA loop is scoped by the pair **(project_root, slug)** — the same slug can exist\nindependently in different projects (each gets its own uuid). Tools still take only\n`loopSlug`; the server derives `project_root` from its cwd and scopes every read and write\nto it.\n\n### Data layout\n\nThere is ONE **global** SQLite database, shared by every project on the machine:\n`~/.dev-loop-state/loops.db`. It holds loops from ALL projects (each row keyed by its uuid\n`id` and tagged with its `project_root`), which is the basis for a future cross-project\nvisual browser.\n\nThe rendered views and plain files stay **per-project**, in a directory named after the\nloop's **slug** — `{project_root}/.projects/loops/{slug}/` — rebuilt after every mutation:\n\n| Path                                                 | Contents                                                                            |\n| ---------------------------------------------------- | ----------------------------------------------------------------------------------- |\n| `~/.dev-loop-state/loops.db`                         | Single GLOBAL SQLite DB holding loops from every project (scoped by `project_root`) |\n| `{project_root}/.projects/loops/<slug>/loop.md`      | Consolidated loop snapshot (phase, counters, tasks checklist)                       |\n| `{project_root}/.projects/loops/<slug>/events.jsonl` | Append-only event log (one JSON object per line)                                    |\n| `{project_root}/.projects/loops/<slug>/memory.md`    | Dated lessons learned during the loop                                               |\n\nThe database is the source of truth; the markdown views are disposable renders rebuilt\nafter every commit. Use `loop_render` to force a full re-render if any view drifts.\n\n### Creating version (`mcp_version`)\n\nEach loop records the `loop-state-mcp` version that **created** it, captured once at\n`loop_init` from the shipped `package.json`. It is **immutable** — set at creation and\nnever updated afterward, even when a newer MCP re-renders the loop — so it always reflects\nthe version that generated the loop, which is useful for support and debugging. It surfaces\nin `loop.md` as the `- mcp_version: {version}` field in the `## Loop` block. Loops created before\nthis field existed render as `unknown`. Note the distinction from the `mcp:` key in that file's\n[frontmatter](#document-headers-yaml-frontmatter), which records the version that RENDERED it — the\nrunning server — and is therefore known even for a loop created before the column existed.\n\n### Concurrency\n\nBecause the DB is now shared across processes, it is opened in **WAL** journal mode with a\n**5 s busy-timeout** (on both the `better-sqlite3` primary driver and the `node:sqlite`\nfallback). Multiple project MCP instances can therefore read and write the one global\ndatabase concurrently and safely.\n\n## Multi-agent & multi-project usage\n\nMultiple agents can run independent loops in parallel by choosing distinct slugs; and the\nsame slug can be reused across different projects without collision. Each agent calls\n`loop_init` with its own slug and then operates only on that loop's tools:\n\n```text\nProject A (cwd = /work/app-a)      Project B (cwd = /work/app-b)\n──────────────────────────         ──────────────────────────\n\nloop_init(                         loop_init(\n  loopSlug=\"dup\",                    loopSlug=\"dup\",\n  goal=\"Implement feature X\")        goal=\"Fix bug Y\")\n\n→ renders                          → renders\n  /work/app-a/.projects/loops/        /work/app-b/.projects/loops/\n  dup/loop.md                        dup/loop.md\n\ntask_setStatus(                    task_setStatus(\n  loopSlug=\"dup\",                    loopSlug=\"dup\",\n  taskid=\"doc-setup\",                taskid=\"bugfix-core\",\n  status=\"in-progress\")              status=\"pr-open\")\n```\n\nBoth `dup` loops coexist in the ONE global `loops.db`, distinguished by their\n`project_root`; each agent's reads and writes are scoped to its own `(project_root, slug)`.\nWithin a single project, distinct slugs also coexist — the one-active-loop guard has been\nremoved so concurrent loops are fully supported.\n\n### Which project a loop belongs to\n\n`project_root` scopes the loop; it does not decide which **project** owns it. A project is a\n**repository**, and one repository is usually several directories — every git worktree of it is\nanother place to work in the same codebase. `ensureProject` resolves the cwd in this order:\n\n| Step | Signal                                                     | Why                                                                       |\n| ---- | ---------------------------------------------------------- | ------------------------------------------------------------------------- |\n| 1    | `project_root` alias table — this exact directory           | Already known. No git call at all, so the common case costs nothing.       |\n| 2    | `project.remote_key` — the normalized `origin` URL          | Identical in a repo and every worktree of it. Survives the repo moving.    |\n| 3    | `project.repo_key` — the shared git directory               | For a repository with no remote, where step 2 has nothing to match on.     |\n| 4    | `root_folder` — the directory itself                        | The original behaviour, for a plain directory that is not a repository.    |\n\nWhatever matches, the cwd is registered as a root of that project, so step 1 answers next time.\nA match on step 2/3/4 also back-fills the identity onto the project row, so a row written by an\nolder version stops being legacy the first time it is seen.\n\n`remote_key` is normalized so the spellings of one repository collapse to one key:\n`git@github.com:Acme/App.git`, `https://github.com/acme/app`, and\n`ssh://git@github.com/acme/app.git` all become `github.com/acme/app`. Credentials, ports and\ntrailing slashes are dropped. A `file:` or plain-path remote yields **no** key — two people\ncloning from `/srv/git/app.git` are not working on the same repository, and grouping them would\nmerge unrelated projects. The remote is read with `git config --get remote.origin.url` rather than\n`git remote get-url`, so a local `url.insteadOf` rewrite cannot change a repository's identity.\n\nProjects created before this existed keep their own identity — the migration back-fills one alias\nand a `remote_key` per row, but does **not** silently fold duplicates together, because choosing\nwhich name, icon and tags survive is a decision with taste in it. The API surfaces them via\n`GET /projects/duplicates` and merges on request via `POST /projects/:id/merge`.\n\n## Tools\n\nEvery tool accepts `loopSlug` as its first parameter — this string identifies which loop\nto operate on. Required parameters have no `?`; optional ones do. (The one exception is\n`loop_recover`, where `loopSlug` is optional so a session that has lost the slug can still\nfind its loop.)\n\n### Progress guards — the DB refusing to walk away from unfinished work\n\nValidation catches an illegal *call*. These catch an illegal *sequence* — the class of failure where\nevery individual call is legal but the loop has abandoned work. Observed in a real run: task T2\nreached `in-review`, the reviewer's `task_setStatus` was refused, and the orchestrator declared \"T2\ncomplete\" and opened an issue for T3. The status machine held (`complete` is reachable only from\n`merged`, so the DB never recorded it), but nothing stopped T3 from **starting**, and T2's PR was\nnever merged.\n\n**One task in flight at a time.** `task_setStatus` (when the destination is a working status) and\n`now_set` are refused while a *different* task sits in `issue-open`/`in-progress`/`verified`/\n`secure`/`pr-open`/`in-review`/`changes-requested`/`merged`. The error names the stranded task, its\nstatus, and the step it still owes (`in-review` → \"dispatch git-loop (phase=git-merge)\"). Advancing\nthe in-flight task is never blocked — that is how it finishes — and neither is **parking** another\ntask (`skipped`/`blocked`/`superseded`), which is how a loop legitimately steps past a dead end.\n\n**`loop_finish(\"done\")` is refused while any task is unfinished**, listing them. `\"aborted\"` is\nalways allowed: it is the honest exit, and refusing it would leave a stuck loop no way to close.\n\n**A refusal LATCHES.** When the MCP rejects a call it records `blocked_op`/`blocked_task`/\n`blocked_reason` on the loop and writes a `tool-refused` event. Until it resolves, other work is\nrefused with a message pointing back at it. Crucially, **calls on the task the refusal named stay\nallowed** — that is the corrective path, and it is why the latch cannot deadlock a loop that is\ngenuinely trying to recover. A successful call on that task clears the latch by itself. Reads\n(`loop_get`, `loop_recover`, `loop_list`, `loop_render`), the escalation tools\n(`loop_setAwaitingUser`, `event_append`, `memory_append`) and `loop_finish(\"aborted\")` are never\nblocked, so the loop can always reach the user. `loop_recover` surfaces the latch as a\n`LOOP BLOCKED` warning.\n\nFor the case only a human can settle, `loop_clearBlock(loopSlug, resolution)` lifts it and records\nthe decision on the timeline — waiving a guard leaves a trace naming what was waived.\n\n### Step timing\n\nEvery dispatched step is timed, and **there is no timing call to make.** A scheme built on\n`step_start`/`step_end` would measure only the steps an agent remembered to bracket — worse than no\ndata, because the gaps are invisible in the totals. So each write is a side effect of a call the\nprotocol already mandates:\n\n| Existing call | Side effect |\n| --- | --- |\n| `now_set` | close whatever was open, open a new step (with an `attempt` counter per task+phase) |\n| `now_clear` | close the step, `ended_by: 'explicit'` |\n| `event_append(event: 'agent-return')` | stamp `agent_returned_at` + `outcome` |\n| `task_complete` | end the task clock; close that task's step |\n| `loop_finish` | end the loop clock; close anything still running |\n\n**Two clocks.** `agent_ms` (`started_at → agent_returned_at`) is what the agent itself took;\n`wall_ms` (`started_at → ended_at`) is that plus the orchestrator's own time before it moved on. The\ngap between them is orchestrator overhead. `agent_ms` stays null when a subagent skips its\ncheckpoint, rather than quietly reporting a wall-clock number as agent time.\n\n**Historical loops are backfilled.** Loops that ran before timing existed have their steps\nreconstructed from the event log on first open — `now_set` writes a `dispatch` carrying the task,\nstep and agent, and each subagent writes an `agent-return`, which together bracket every step. What\ncannot be recovered is `now_clear`, so a reconstructed step is closed by the next dispatch and marked\n`source: \"backfill\"`; its `wall_ms` is an upper bound, and `quality.backfilled` counts them. Only\nloops with **no** steps at all are touched, so reconstruction can never interleave with measured data.\n\n**Missed calls repair themselves.** A step is closed by whatever happens next, so a forgotten\n`now_clear` costs an inflated `wall_ms` and an `ended_by` of `auto-*` — never a lost row or a step\nthat runs forever. `ended_by` is what keeps the data honest about which numbers include overhead.\n\n**It reports its own reliability.** `loop_timings` returns a `quality` block: steps closed\nexplicitly vs. auto-repaired, how many agents skipped their checkpoint, and `orphan_returns` — an\n`agent-return` that arrived with no open step, which means a dispatch was made without `now_set` and\nthe totals under-count. That is currently the only signal anywhere that a dispatch went unrecorded.\n\nSurfaced in four places: `loop_timings(loopSlug)` (full report), a `## Timings` block in `loop.md`\n(closed durations only, so the render stays deterministic — in-flight steps read `running`), a\none-line summary on `loop_audit`, and the in-flight step's elapsed time on `loop_recover`.\n\n### The final audit\n\n`loop_audit(loopSlug)` reconciles the whole loop against its own record. It is meant to run as the\n**last step before the feature-merge gate**, and it closes a gap the progress guards leave open: they\nprotect task-to-task progression and loop *closure*, but the feature→main merge sits between those\ntwo. A loop could reach the gate with a task stranded at `in-review`, bracket the phase, dispatch the\nmerge — every call legal — and only hit a refusal at `loop_finish(\"done\")`, long after the merge\nlanded on the default branch.\n\nReturns `{ verdict: 'pass' | 'warn' | 'fail', tasks[], findings[], selfCorrectable[], userDecisions[],\nsummary, guidance }`. Each finding carries a stable `code`, a `severity`, an `owner`, and the exact\n`fix`.\n\n**Enforcement:** a `fail` verdict makes `loop_setPhaseStatus('feature-merge', 'in-progress')` return\nan error listing the blocking findings. That call is the documented bracket before dispatching\n`git-feature`, so the audit sits in front of the merge without the orchestrator needing any new\ndiscipline. No other phase is gated.\n\n**What it checks** — tasks: mid-lifecycle at the gate, never started, parked-and-unresolved, skipped\n(warn), a placeholder or missing merge SHA, completed with no issue/PR (warn), counters over cap\n(warn). Loop: an unresolved refusal block, an unanswered `awaiting_user` gate, QA that never ran with\nno recorded skip, deferred findings with an unasked findings gate, and the non-gating phases (warn).\n\nThe merge SHA is read from the **timeline**, not `task.last_evidence` — `task_complete` overwrites\nthat column with its own `mergeEvidence`, so a completed task's row no longer holds the SHA at all.\n\n**Ownership is deliberately lopsided.** `selfCorrectable` (owner `orchestrator`) means a state an\nagent produced but failed to *record*, where the fix is mechanical and invents nothing — today only\n`merged-not-completed`, whose fix quotes the SHA already on the timeline. Everything else is\n`userDecisions`. Auto-recovering at this point — re-running a skipped task unattended at the moment\nthe user believes the loop has finished — is the failure the audit exists to surface, committed one\nstep later.\n\n**Scope:** this reads the database, not GitHub. It cannot know whether a PR really merged;\n`git-loop`'s completeness backstop (squash commits vs. completed tasks) covers that. The two catch\ndifferent classes and neither substitutes for the other.\n\n### Document headers (YAML frontmatter)\n\nEvery markdown document in a loop directory carries a small YAML header:\n\n```yaml\n---\ntype: plan\nloop: add-oauth\ntaskid: T3          # task specs only\ngenerated: \"2026-08-18T09:12:00.000Z\"\nupdated: \"2026-08-18T11:40:00.000Z\"\nmcp: 0.3.0\n---\n```\n\n**The MCP writes it, never the agent.** `loop_writeFile` stamps the header on every write — a caller\nsupplies only the document. Every rule an agent must remember is a rule that eventually gets skipped,\nand a header that is sometimes wrong is worse than none, so ownership sits where it cannot drift.\n`loop_readFile` strips it back off and returns it as a separate `frontmatter` field, which also stops\na model reading a file, editing it, and writing the old header back in as body text.\n\n**Identity, never state.** The header says what the document *is*. It must never carry `status`,\n`phase` or a counter — those change constantly, the database owns them, and a stale copy in a file\nheader is exactly the drift that \"DB is truth, views are disposable\" exists to prevent.\n\n**Why it earns its place:** a loop document is routinely read where the database is not — pulled out\nof an archive, restored years later, opened in an editor, landing in a git diff. `plan.md` is\nself-evident; `T22.md` and `qa-report-20260814-0912.md` are not, and a renamed file is anonymous.\n\n| | Header? |\n| --- | --- |\n| `plan.md`, `discovery.md`, `prompt.md`, `legal.md`, `security.md`, `unresolved-pr-comments.md`, `tasks/*.md`, qa-reports | yes — stamped by `loop_writeFile` |\n| `loop.md`, `memory.md` | yes — written by the renderer |\n| `events.jsonl` | **no** — it is line-delimited JSON; a header would break every line parser |\n| `scratch/*` | no — working notes, not documents |\n\nOn the rendered files the header **replaces** the old `<!-- loop id: … -->` anchors, so one mechanism\nstates each fact instead of two. Its `updated` is the loop's own `updated_at`, not the wall clock:\na render is a pure function of the database, and stamping render time would make identical state\nproduce different bytes on every call.\n\nThe parser is a deliberate YAML **subset** — flat `key: value` scalars, no nesting, no lists. No\ndependency, nothing surprising to mis-parse, and anything it does not understand is treated as\nabsent rather than as an error: a malformed header must never make a document unreadable.\n\n## Archive — cold storage\n\nA finished loop is still noise: it lengthens every list and buries the runs anyone cares about. But\nit is also the only record of what was built and why, and its plain files (`plan.md`, `discovery.md`,\nthe task specs) live outside the database entirely — so \"delete the rows\" loses the reasoning and\n\"keep everything\" never gets quieter.\n\n`loop_archive(loopSlug)` resolves that: it captures the loop's rows **and every file in its loop\ndirectory** as gzipped blobs, then removes it from the working tables. `plan.md` compresses roughly\n5–10×, so an archive is usually a fraction of the directory it captured, and a loop can be browsed —\nor restored whole — years later without the original working tree existing at all.\n\n**Capture, verify, then destroy.** Every stored file is read back and checked against its own sha256\nBEFORE the live rows are deleted, all inside one transaction. A verification failure rolls the whole\nthing back, so a half-written archive can never be paid for with deleted data.\n\n**Refuses on an active loop** (an orchestrator may be mid-dispatch), and **never deletes the loop\ndirectory** — archiving copies work, it does not destroy it. Files over 5 MB, a total over 100 MB and\n`.tmp/`/`node_modules/`/`.git/` are excluded, and every exclusion is NAMED in `skipped` rather than\nsilently dropped.\n\n| Tool | Does |\n| --- | --- |\n| `loop_archive(loopSlug)` | capture + verify + remove from the working tables |\n| `archive_list({ projectRoot?, search? })` | headers only, newest first — never inflates a blob |\n| `archive_get(archiveId)` | header + file manifest (path, size, sha256, mtime) |\n| `archive_readFile(archiveId, path)` | one file: text for markdown/JSONL, base64 otherwise, checksum-verified |\n| `archive_restore(archiveId, { asSlug?, targetDir? })` | rows back to live, files back to disk |\n| `archive_delete(archiveId, confirm)` | permanent; `confirm` repeats the id |\n| `db_vacuum()` | return freed pages to the filesystem |\n\n**Restoring** puts everything back into the archive's **own** `project_root`, not the current working\ndirectory — archives are global, and a loop from project A reappearing in project B would silently\ncorrupt both. It refuses if the slug is live again (`asSlug` overrides), and the archive is **kept**,\nso you can look at a loop and put it back without re-capturing it.\n\n**On disk size:** archiving shrinks the working set immediately, but SQLite does not return freed\npages to the filesystem — the file only gets smaller after `db_vacuum()`, and only if the archived\nrows outweighed their compressed files. What improves unconditionally is everything that queries the\nlive tables.\n\n## Retention\n\nThe database is global and append-only by design — every project's loops, every event, kept. These\ntwo operations are the way data leaves, and both are built to be hard to regret. **Neither touches\nthe filesystem:** `plan.md`, `discovery.md` and the task specs are the reasoning a loop produced, and\ndestroying work is not a cleanup operation.\n\n### `loop_prune(olderThanDays, dryRun?)`\n\nDrops the **timeline and step history** of loops finished longer ago than the window, and keeps the\n**record** — the loop row, its tasks, its phase tracker and its lessons all survive. So \"what did we\nship and what did we learn\" outlives \"every state transition that got us there\".\n\nOnly `done`/`aborted` loops are eligible; an active loop's history is load-bearing (recovery reads\nit, and the step backfill reconstructs from it). `dryRun` defaults to **true**, so the first call\nalways previews and returns the exact candidate list with per-loop event and step counts.\n\nIrreversible once applied: the timings of a pruned loop cannot be reconstructed, because the events\nthey were derived from are what was removed.\n\n### `loop_delete(loopSlug, confirm)`\n\nRemoves a loop and every row belonging to it. `confirm` must repeat the slug — not ceremony, but so\na mistyped slug deletes nothing rather than the wrong loop. **Refuses while the loop is `active`**:\nabort it first, so the removal follows a decision instead of surprising an orchestrator mid-dispatch.\n\nExposed over HTTP as `DELETE /loops/:id`.\n\n## Recovery tools\n\n- `loop_recover(loopSlug?, eventLimit?)` — **Rebuild an orchestrator's working context for a loop it has no memory of.** Call it first in any session that did not start the loop: after a context compaction, after a crash, or when taking over from a session that went rot. Read-only — it mutates nothing, so calling it twice is always safe. See [Recovery](#recovery) for the returned shape.\n- `loop_files(loopSlug)` — The plain files actually on disk in the loop dir (top level + `tasks/`), with sizes. **Use this, not `glob`, to check whether an agent wrote its deliverables.** `git-loop` adds `.projects/loops/` to `.gitignore` (so `git clean -fd` cannot destroy loop state), which makes every gitignore-aware glob silently return NOTHING for that directory — while `read` on an explicit path still works. That combination produces a baffling failure: `plan.md` reads fine, the `tasks/*.md` it points at look missing, and the caller concludes the writer lied. It bites from the SECOND loop in a repo onward, because the ignore is added partway through the first. This tool reads the filesystem and cannot be blinded that way.\n- `loop_list(includeFinished?)` — List this project's loops (slug, title, phase, status, task rollup, `updated_at`), newest-touched first. Answers \"which loop?\" before `loop_recover(loopSlug)` loads one. Active loops only unless `includeFinished`.\n\n### Loop tools\n\n- `loop_init(loopSlug, goal, title?, mode?, defaultBranch?, branchMode?, featureBranch?)` — Create a loop: mint a uuid `id`, store the short `slug` (folder name) and long `title` (display; defaults to `goal` when omitted), and render the initial views. Returns `{ id, loopSlug, title, loopdir }`, or the existing loop with `reused: true` (its original uuid) if the slug already exists.\n- `loop_get(loopSlug)` — Return the full structured state (loop + tasks) for the given slug. The loop row includes `id` (uuid), `slug`, and `title`.\n- `loop_setPhase(loopSlug, phase)` — Set the top-level phase pointer.\n- `loop_setPhaseStatus(loopSlug, phase, status)` — Set the durable status of one loop-level phase (the `## Phases` tracker). Bracket each phase: `in-progress` before dispatch, `done`/`skipped`/`failed` after.\n- `loop_setAwaitingUser(loopSlug, awaiting)` — Set/clear the \"needs you\" banner at a gate/escalation. Setting it is only **half** of a gate: it records that the loop is waiting, it does not ask anyone. The other half is the caller's `question` tool call. Because setting the banner and then merely describing the question in chat leaves the UI showing \"needs you\" for a prompt the user never saw, a SET returns a `reminder` field saying so; clearing it does not.\n- `loop_setField(loopSlug, field, value)` — Set a settable field (mode, default_branch, branch_mode, feature_branch, legal, memory_harvest, docs_sync, deferred_gate, qa_report, findings counts).\n- `loop_bumpCounter(loopSlug, counter, by?)` — Bump a loop-level counter (total_failed_gates, splits_done, qa_rounds). Returns `{ value, capExceeded }`.\n- `loop_finish(loopSlug, status)` — Flip the loop to done/aborted and write the final event. `\"done\"` is REFUSED while any task is not complete/skipped/superseded; `\"aborted\"` is always allowed.\n- `loop_render(loopSlug)` — Force a full re-render of all views from the DB (self-heal / repair).\n- `loop_timings(loopSlug)` — Per-step durations (`agent_ms` = what the agent took, `wall_ms` = that plus orchestrator overhead), per-task and per-phase rollups, slowest steps, anything still running, and a `quality` block saying how much to trust the totals. Recorded automatically — there is no timing call. Read-only. See [Step timing](#step-timing).\n- `loop_audit(loopSlug)` — **FINAL CHECK before the feature-merge gate.** Reconciles every task against its record plus the loop-level phases; returns a verdict and findings split by who can resolve them. A `fail` refuses `loop_setPhaseStatus(\"feature-merge\",\"in-progress\")`. Read-only. See [The final audit](#the-final-audit).\n- `loop_archive(loopSlug)` — Capture a finished loop's rows AND files into cold storage, verify every file's checksum, then remove it from the working tables. See [Archive](#archive--cold-storage).\n- `archive_list(...)` / `archive_get(id)` / `archive_readFile(id, path)` / `archive_restore(id, ...)` / `archive_delete(id, confirm)` — browse, read and restore archives.\n- `db_vacuum()` — Return freed pages to the filesystem. Archiving, pruning and deleting free space inside the file but never shrink it until this runs.\n- `loop_prune(olderThanDays, dryRun?)` — Drop the timeline + step history of loops finished beyond the window, keeping each loop's row, tasks and lessons. `dryRun` defaults to true. See [Retention](#retention).\n- `loop_delete(loopSlug, confirm)` — Delete a loop and all its rows; `confirm` must repeat the slug. Refuses while active. Files on disk are kept. See [Retention](#retention).\n- `loop_clearBlock(loopSlug, resolution)` — Lift the refusal block after the USER decided how to proceed. Making the corrective call clears it by itself; use this only when a human decision is needed. The `resolution` is required and is written to the timeline. See [Progress guards](#progress-guards--the-db-refusing-to-walk-away-from-unfinished-work).\n\n### Task tools\n\n- `tasks_derive(loopSlug, tasks)` — Create task rows from an approved plan; render the tasks section of loop.md. Idempotent (upsert by taskid). **Refuses the whole batch** when a `depends_on` names a task that does not exist, a task depends on itself, or a taskid appears twice — each of those produces a task that can never complete, and the failure would otherwise surface much later as `dependency X is not complete (status=missing)`, naming the symptom instead of the plan.\n- `tasks_split(loopSlug, taskid, subtasks)` — Replace an oversized task with sub-tasks; mark the original superseded.\n- `tasks_append(loopSlug, kind, tasks)` — Append QAT/cleanup tasks (qa-fix or deferred-findings modes). Continues the task-id sequence. Unlike `tasks_derive` this INSERTs, so reusing an existing taskid is refused by name rather than leaking a raw `UNIQUE constraint failed`. Same dependency checks as derive.\n- `task_setStatus(loopSlug, taskid, status, evidence?, issue?, pr?, branch?)` — Advance one task, validating the transition against the table. Records evidence/issue/pr/branch. **Benign work-step tolerance:** in a rework/fix/debug re-entry the orchestrator often records a re-verify straight from `issue-open`/`failed`/`changes-requested` to `verified` (skipping `in-progress`). Because `in-progress` bypasses no gate, if it is the *only* missing intermediate the call **auto-inserts** it (recording both hops on the timeline — the auto hop as `task_setStatus:auto`) instead of rejecting. Any skip that would jump a real gate (verify/security/review/merge — e.g. `secure -> complete`) is still rejected with the shortest-path hint.\n- `task_bumpCounter(loopSlug, taskid, counter, by?)` — Bump retries/security_rounds/review_rounds. Returns `{ value, capExceeded }`.\n- `task_complete(loopSlug, taskid, mergeEvidence)` — Atomic completion op: set status=complete, flip the checkbox, append the merge event — one transaction, one render. **Refusals RETURN `isError`, they do not throw**, matching every other tool. A `depends_on` still incomplete is refused and deliberately NOT latched — the fix is to finish the dependency, and a latch scoped to this task would refuse exactly that. Reaching `complete` from anything but `merged` IS latched: it means the merge never happened.\n\n### Now tools\n\n- `now_set(loopSlug, task, agent, step)` — Set the `## Now` live pointer (task/agent/step) before a dispatch.\n- `now_clear(loopSlug)` — Clear the `## Now` live pointer after a dispatch resolves.\n\n### Misc tools\n\n- `event_append(loopSlug, task?, phase?, from?, to?, agent?, event, note?)` — Append an explicit transition (dispatch/gate/escalate) not implied by a status change, or an agent's `agent-return` checkpoint. **Field consistency is enforced server-side:** \"absent\" spellings (`\"\"`/`n/a`/`none`/`null`) collapse to a single canonical `null`; well-known outcome synonyms in `from`/`to` (`completed`/`finished`/`success`/`succeeded` → `done`, `partial`/`unfinished` → `incomplete`, `stuck` → `blocked`) fold to the canonical agent vocabulary (real task statuses like `merged`/`in-review` are never rewritten); and omitted `task`/`agent`/`phase` are **backfilled from the loop's live now-pointer (`now_task`/`now_agent`) and `phase`** — so `dispatch`/`agent-return` events stay populated even when the caller drops a field (an explicitly-passed value always wins). The auto-generated `task_setStatus`/`task_complete` events likewise stamp the loop's current `phase`.\n- `memory_append(loopSlug, agent, taskid?, note)` — Append a lesson. Called by every loop agent (memory is now MCP-owned).\n\n## Recovery\n\nA loop outlives the session that runs it. When an orchestrator's context is compacted, its\nprocess dies mid-dispatch, or it simply drifts off-protocol and has to be restarted, the work\nis intact — the DB has every transition and the loop dir has every deliverable — but the new\nsession knows only its cwd.\n\n`loop_get` is not enough to restart from: it returns rows, not a resume point, and says nothing\nabout the `## Phases` tracker, the timeline, or what is on disk. `loop_recover` derives all of\nit, so recovery is a single read instead of a glob, a guess, and a re-run phase.\n\n**It never mutates anything.** A confused agent calling it repeatedly cannot make its situation\nworse.\n\n### What it returns\n\n| Field | What it carries |\n|---|---|\n| `loop` | the full loop row (phase, status, mode, branch fields, counters, rollups) |\n| `phases` | the `## Phases` tracker, in ladder order |\n| `tasks` | every task with status, counters, evidence, issue/pr/branch |\n| `now` | the `## Now` pointer **plus `stale: true`** when a dispatch was in flight when the session died |\n| `resume` | **the derived resume point** — `{ kind, point, taskid, agent, why, nextAction }` |\n| `context` | the **prioritized file manifest** — what to read, and why, before dispatching |\n| `recentEvents` | the timeline tail (default 25, oldest-first) — how the old session actually died |\n| `warnings` | everything off-nominal: stale pointer, open gate, blocked/failed tasks, exceeded caps, interrupted phases, missing must-read files, a missing loop dir, version drift |\n| `protocol` | the short list of rules a recovering orchestrator drops first |\n\n### The resume point\n\nDerived from the DB alone, in strict precedence order:\n\n1. `status` is `done`/`aborted` → `kind: \"done\"`; the loop must **not** be resumed.\n2. `awaiting_user` is set → `kind: \"gate\"`; re-ask the user, never answer for them.\n3. A task is not `complete`/`skipped`/`superseded` → `kind: \"task\"` on the first such task, with\n   the ONE next lifecycle step read backwards from its status (`secure` → `git-pr` via `git-loop`,\n   `pr-open` → `review` via `code-reviewer-loop`, `merged` → the orchestrator's own `task_complete`\n   with no dispatch, `blocked` → ESCALATE, …). A `build` step resolves the engineer from the task's\n   `tech` tag; an unknown tech says ESCALATE rather than naming a substitute.\n4. Every task closed → `kind: \"phase\"` on the first open post-build phase (qa → findings-gate →\n   memory → agents-sync → feature-merge).\n5. No tasks at all → `kind: \"phase\"` on the first open early phase (clarify → discovery → legal → plan).\n\nAn `in-progress` phase counts as **open**, never as finished: it was interrupted mid-flight, and\nassuming otherwise is exactly how a recovery silently skips a gate. The bundle says so explicitly.\n\n### The context manifest\n\nEntries are `{ path, exists, bytes, priority, why }`, sorted `must` → `should` → `optional`.\n\n- **Paths are project-root-relative, always** (`.projects/loops/{slug}/plan.md`). The MCP never\n  hands back an absolute path, so nothing can reintroduce the `/home/<user>` drift that\n  slug-addressing removed.\n- **Manifest, not content.** The recovering agent reads the files itself, in its own order, and\n  only what it needs — the server never pushes file bodies into the context that is already scarce.\n- **`why` is resume-point-specific**, not a generic description: `discovery.md` is a `must` when\n  resuming at `plan` (the planner refuses to plan a standard goal without it) and a `should`\n  otherwise.\n- **A missing must-read is still listed**, with `exists: false` — no `plan.md` at a build resume\n  point is itself the finding, and it also surfaces in `warnings`.\n\n### Finding the loop\n\nOmit `loopSlug` and recovery resolves it:\n\n- exactly one active loop in this project → recovered outright;\n- several → `{ recovered: false, reason: \"ambiguous\", candidates, guidance }` — the orchestrator\n  must ESCALATE and let the user pick, because resuming the wrong loop orphans the other's work;\n- none → `{ recovered: false, reason: \"no-active-loop\", candidates, guidance }`, listing **every**\n  loop including finished ones, so \"I'm in the wrong cwd\" and \"this loop is already done\" are\n  distinguishable.\n\nAn explicit slug that does not exist is an error (`loop not found: {slug}`) — the same message\n`loop_get` returns, so it lands in the caller's existing hard-stop branch.\n\n## Reference\n\n### Loop phases\n\nAccepted by `loop_setPhase`. Reflects the dev-loop-protocol stage gate.\n\n| Phase           | Meaning                                                              |\n| --------------- | -------------------------------------------------------------------- |\n| `preflight`     | Initial setup; goal recorded, no work dispatched yet                 |\n| `clarify`       | Intent-clarify stage; confirm the goal and constraints with the user |\n| `discovery`     | Investigating scope; reading code, researching constraints           |\n| `legal`         | License / security / privacy review gate                             |\n| `plan`          | Approved plan written; tasks derived                                 |\n| `gate`          | Pre-build approval checkpoint                                        |\n| `build`         | Implementation in progress                                           |\n| `qa`            | Testing and quality gates                                            |\n| `findings-gate` | Review QA findings; decide fix vs defer                              |\n| `memory`        | Harvest lessons into `memory.md`                                     |\n| `feature-gate`  | Post-QAT feature approval checkpoint                                 |\n| `done`          | Loop completed successfully                                          |\n| `blocked`       | Loop cannot proceed without external input                           |\n\n### Loop phase-status tracker\n\nThe `## Phases` block in `loop.md` is a durable per-phase ladder, one row per\nloop-level phase, seeded as `pending` at `loop_init`. Set each phase's status with\n`loop_setPhaseStatus(loopSlug, phase, status)`: bracket every phase `in-progress`\nbefore dispatch and `done`/`skipped`/`failed` after. This is the loop-level phase\nladder from the dev-loop-protocol — a superset of the `loop_setPhase` pointer enum\nabove; it additionally tracks `agents-sync` and `feature-merge`, which are not\ntop-level phase-pointer values.\n\nAccepted by `loop_setPhaseStatus.phase` (rendered in this order):\n\n| Phase           | Meaning                                  |\n| --------------- | ---------------------------------------- |\n| `clarify`       | Intent-clarify stage                     |\n| `discovery`     | Scope investigation                      |\n| `legal`         | License / security / privacy review gate |\n| `plan`          | Plan approved; tasks derived             |\n| `build`         | Implementation                           |\n| `qa`            | Testing and quality gates                |\n| `findings-gate` | Review QA findings; decide fix vs defer  |\n| `memory`        | Harvest lessons into `memory.md`         |\n| `agents-sync`   | Sync agent definitions / instructions    |\n| `feature-merge` | Merge the feature branch                 |\n\nAccepted by `loop_setPhaseStatus.status`:\n\n| Status        | Meaning                                 |\n| ------------- | --------------------------------------- |\n| `pending`     | Not started (seed value)                |\n| `in-progress` | Set before dispatching the phase        |\n| `done`        | Phase completed                         |\n| `skipped`     | Intentionally not run                   |\n| `failed`      | Phase failed                            |\n| `blocked`     | Cannot proceed without external input   |\n| `not-needed`  | Not applicable to this loop             |\n| `left-open`   | Deliberately deferred / left unresolved |\n\n### Loop finish status\n\nAccepted by `loop_finish`.\n\n| Status    | Effect                                 |\n| --------- | -------------------------------------- |\n| `done`    | Sets `status=done`, `phase=done`       |\n| `aborted` | Sets `status=aborted`, `phase=blocked` |\n\n### Loop settable fields\n\nAccepted by `loop_setField.field`. Value may be a string, number, or null.\n\n| Field                 | Purpose                                                                                            |\n| --------------------- | -------------------------------------------------------------------------------------------------- |\n| `mode`                | Execution mode (`simple`/`standard`/`poc`) — the value is validated; see [Loop modes](#loop-modes) |\n| `default_branch`      | Default branch name (e.g. `main`)                                                                  |\n| `branch_mode`         | Branch strategy (`new-feature-branch`/`existing-worktree`)                                         |\n| `feature_branch`      | Pre-existing worktree branch to target                                                             |\n| `legal`               | Legal review outcome / sign-off reference                                                          |\n| `memory_harvest`      | Summary of lessons captured                                                                        |\n| `docs_sync`           | Documentation sync status                                                                          |\n| `deferred_gate`       | Notes on a deferred gate decision                                                                  |\n| `qa_report`           | QA report reference or summary                                                                     |\n| `f_unresolved_pr`     | Count of unresolved PR comments (integer)                                                          |\n| `f_security_deferred` | Count of deferred security findings (integer)                                                      |\n\n### Loop counters\n\nAccepted by `loop_bumpCounter.counter`. The `by` parameter defaults to `1`. Only\n`qa_rounds` is capped (cap = 2); `capExceeded` is returned when exceeded (per the\ndev-loop-protocol: `qa_rounds > 2` escalates).\n\n| Counter              | Cap | Meaning                         |\n| -------------------- | --- | ------------------------------- |\n| `total_failed_gates` | —   | Number of gates that failed     |\n| `splits_done`        | —   | Number of task splits performed |\n| `qa_rounds`          | 2   | Number of QA iterations         |\n\n### Task counters\n\nAccepted by `task_bumpCounter.counter`. The `by` parameter defaults to `1`. All three\nare capped at 2; `capExceeded` is returned when the cap is exceeded (signals escalation).\n\n| Counter           | Cap | Meaning                           |\n| ----------------- | --- | --------------------------------- |\n| `retries`         | 2   | Implementation retries for a task |\n| `security_rounds` | 2   | Security review rounds            |\n| `review_rounds`   | 2   | Code review rounds                |\n\n### Loop documents\n\nA loop's deliverables — `prompt.md`, `discovery.md`, `plan.md`, `tasks/*.md`, `security.md`,\n`qa-report-*.md` — are stored as rows in `loop_document` at the moment an agent writes them, and\nmirrored into `{project_root}/.projects/loops/{slug}/`.\n\n**The row is the record; the file is a mirror.** The loop directory is gitignored and, in the\nworktree-per-loop workflow, deleted the moment the branch merges — so the reasoning behind a change\nused to survive exactly as long as the worktree that produced it. `loop_archive` could not rescue\nit either: it captured from that same directory, and a loop whose worktree was already gone\narchived successfully with `file_count: 0`.\n\n- **Revisions are append-only.** `plan.md` is rewritten on a task split and again when QA defects\n  become tasks; each write appends `rev + 1` and reads take the head. A write whose content hashes\n  identically to the head creates no revision, so the numbers keep meaning \"this changed\".\n- **Content is gzipped**, with `sha256` over the raw bytes — the same shape as `loop_archive_file`.\n  Markdown compresses 4–5×.\n- **`loop_readFile` reads the database first**, falling back to disk for loops that predate this.\n  A loop whose worktree is gone still answers for itself.\n- **`loop_writeFile` writes the row first**, then the mirror. A failed mirror write is reported,\n  not fatal — the document is already safe, and `loop_materialize` writes the files back later.\n- **`loop_materialize`** re-creates the mirror in a fresh worktree, for the agents that still read\n  `.projects/loops/{slug}/prompt.md` by path.\n- **`loop_archive`** captures documents from the database, carries every revision inside the state\n  snapshot, and now records `NO DOCUMENTS CAPTURED` in `skipped` instead of silently storing zero.\n- **`loop_delete` KEEPS documents** unless `purgeDocuments` is passed. Deleting state rows is a\n  cleanup; deleting the record of why the work was done is not.\n\n### Project key — finding a loop after its worktree is gone\n\n`project_root` is the cwd the MCP was spawned in, which in a worktree is the worktree's path. Once\nthat directory is removed, matching on it alone makes the loop invisible from the main checkout\neven though every row survives.\n\n`loop.project_key` is the REPOSITORY's identity, resolved at `loop_init` from the origin remote\n(normalised, so `git@`/`https://`/`.git` spellings collapse to one value) or, with no remote, from\n`git rev-parse --git-common-dir` — which resolves to the main repository's git directory from\ninside any linked worktree. `loop_list` and `loop_recover` match `project_root` OR `project_key`,\nso worktree loops keep showing up where someone would look for them. Loops created before the key\nexisted have it `NULL` and are still found by path.\n\nSuch a loop is reported with `detached: true` and its original `project_root`.\n\n### Loop modes\n\nSet at `loop_init(mode)` or via `loop_setField(loopSlug, \"mode\", …)`. Unlike every other\nsettable field, this one is **validated** — the transition table depends on it, and an\nunrecognised value would silently fall back to the strict ladder.\n\n| Mode       | Effect                                                                                                    |\n| ---------- | --------------------------------------------------------------------------------------------------------- |\n| `standard` | Default. The full PR lifecycle: `complete` is reachable only from `merged`.                               |\n| `simple`   | Same transition table as `standard`. The difference is orchestrator behaviour (discovery is skipped), not enforced here. |\n| `poc`      | Adds the edge `secure → complete`, so a task can finish on a local commit with no issue, PR, review or merge. |\n\n**What `poc` does NOT relax.** `secure` is still reachable only through `verified`, and both\nstill require non-empty `evidence`, so a POC task cannot complete without real verification and\nsecurity proof — only the PR rungs are gone. `task_complete` still gates on `depends_on`, and the\nPR states remain reachable for a poc loop that does open one. The overlay lives in\n`POC_TRANSITION_OVERRIDES` (`src/validation.ts`); `transitionsFor(mode)` returns the strict table\nby identity for every other mode.\n\n`loop_audit` reports `missing-issue` and `missing-pr` as **warnings**, not blockers, so a\ncompleted POC task does not fail the audit for artifacts it never had.\n\n### Task statuses\n\nAccepted by `task_setStatus.status`. Transitions are validated against the allowed\ntransition table for the loop's mode (see source: `validation.ts`).\n\n| Status              | Meaning                                      |\n| ------------------- | -------------------------------------------- |\n| `pending`           | Task created but not yet started             |\n| `issue-open`        | Tracking issue opened                        |\n| `in-progress`       | Work actively underway                       |\n| `verified`          | Local verification passed                    |\n| `secure`            | Security check passed                        |\n| `pr-open`           | Pull request opened                          |\n| `in-review`         | PR under review                              |\n| `changes-requested` | Review feedback received; needs work         |\n| `merged`            | PR merged; terminal pre-complete             |\n| `complete`          | Fully done; reached via `task_complete` only |\n| `failed`            | Task failed and is blocked                   |\n| `blocked`           | Cannot proceed without external input        |\n| `skipped`           | Intentionally not done                       |\n| `superseded`        | Replaced by sub-tasks via `tasks_split`      |\n\n### Append task kinds\n\nAccepted by `tasks_append.kind`.\n\n| Kind                | Meaning                                |\n| ------------------- | -------------------------------------- |\n| `qa-fix`            | Fix task generated from QA findings    |\n| `deferred-findings` | Findings carried forward to next cycle |\n\n## Development\n\nFrom the package directory:\n\n```sh\nnpm install          # install dependencies\nnpm run build        # compile TypeScript → build/\nnpm run typecheck    # type-check without emitting\nnpm run lint         # eslint src and test\nnpm run format       # prettier --write\nnpm test             # vitest run (unit + integration + golden)\n```\n\nTo regenerate golden fixtures after intentional renderer changes:\n\n```sh\nUPDATE_GOLDEN=1 npm test\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}