opensquid 0.5.441 → 0.5.449

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (391) hide show
  1. package/README.md +1 -0
  2. package/dist/functions/arm_scope.d.ts +27 -0
  3. package/dist/functions/arm_scope.d.ts.map +1 -0
  4. package/dist/functions/arm_scope.js +52 -0
  5. package/dist/functions/arm_scope.js.map +1 -0
  6. package/dist/functions/index.d.ts +1 -0
  7. package/dist/functions/index.d.ts.map +1 -1
  8. package/dist/functions/index.js +1 -0
  9. package/dist/functions/index.js.map +1 -1
  10. package/dist/functions/recall_pre_inject.d.ts.map +1 -1
  11. package/dist/functions/recall_pre_inject.js +12 -0
  12. package/dist/functions/recall_pre_inject.js.map +1 -1
  13. package/dist/rag/store_git.d.ts +23 -0
  14. package/dist/rag/store_git.d.ts.map +1 -0
  15. package/dist/rag/store_git.js +57 -0
  16. package/dist/rag/store_git.js.map +1 -0
  17. package/dist/runtime/bootstrap.d.ts.map +1 -1
  18. package/dist/runtime/bootstrap.js +2 -0
  19. package/dist/runtime/bootstrap.js.map +1 -1
  20. package/dist/runtime/handoff/render.d.ts +5 -4
  21. package/dist/runtime/handoff/render.d.ts.map +1 -1
  22. package/dist/runtime/handoff/render.js +7 -7
  23. package/dist/runtime/handoff/render.js.map +1 -1
  24. package/dist/runtime/hooks/active_task_mirror.js +0 -0
  25. package/dist/runtime/hooks/apply_patch.js +0 -0
  26. package/dist/runtime/hooks/dispatch.js +0 -0
  27. package/dist/runtime/hooks/hook_output.js +0 -0
  28. package/dist/runtime/hooks/memory_reconcile.js +0 -0
  29. package/dist/runtime/hooks/new_project_detect.js +0 -0
  30. package/dist/runtime/hooks/profession_resolver.js +0 -0
  31. package/dist/runtime/hooks/scope_intent.js +0 -0
  32. package/dist/runtime/hooks/session-end.js +11 -0
  33. package/dist/runtime/hooks/session-end.js.map +1 -1
  34. package/dist/runtime/hooks/session_id.js +0 -0
  35. package/dist/runtime/hooks/session_liveness.js +0 -0
  36. package/dist/runtime/hooks/stop_drive.js +0 -0
  37. package/dist/runtime/hooks/stop_stream.js +0 -0
  38. package/dist/runtime/hooks/subagent_guard.js +0 -0
  39. package/dist/runtime/hooks/transcript.js +0 -0
  40. package/dist/runtime/hooks/transcript_tasks.js +0 -0
  41. package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
  42. package/dist/runtime/ralph/orchestrator.js +2 -1
  43. package/dist/runtime/ralph/orchestrator.js.map +1 -1
  44. package/dist/setup/cli/limits_state.d.ts.map +1 -1
  45. package/dist/setup/cli/limits_state.js +6 -40
  46. package/dist/setup/cli/limits_state.js.map +1 -1
  47. package/dist/setup/cli/pack_walk.d.ts +32 -0
  48. package/dist/setup/cli/pack_walk.d.ts.map +1 -0
  49. package/dist/setup/cli/pack_walk.js +76 -0
  50. package/dist/setup/cli/pack_walk.js.map +1 -0
  51. package/dist/setup/cli/permissions_state.d.ts.map +1 -1
  52. package/dist/setup/cli/permissions_state.js +6 -37
  53. package/dist/setup/cli/permissions_state.js.map +1 -1
  54. package/dist/setup/cli/triggers_state.d.ts.map +1 -1
  55. package/dist/setup/cli/triggers_state.js +3 -29
  56. package/dist/setup/cli/triggers_state.js.map +1 -1
  57. package/dist/workgraph/events.d.ts.map +1 -1
  58. package/dist/workgraph/events.js +10 -0
  59. package/dist/workgraph/events.js.map +1 -1
  60. package/dist/workgraph/store.d.ts.map +1 -1
  61. package/dist/workgraph/store.js +5 -0
  62. package/dist/workgraph/store.js.map +1 -1
  63. package/dist/workgraph/types.d.ts +2 -1
  64. package/dist/workgraph/types.d.ts.map +1 -1
  65. package/docs/ARCHITECTURE.md +268 -0
  66. package/docs/pack-runtime.md +15 -12
  67. package/docs/skill-grammar-guide.md +4 -4
  68. package/package.json +5 -3
  69. package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
  70. package/dist/anti-drift/evaluator.d.ts +0 -88
  71. package/dist/anti-drift/evaluator.d.ts.map +0 -1
  72. package/dist/anti-drift/evaluator.js +0 -417
  73. package/dist/anti-drift/evaluator.js.map +0 -1
  74. package/dist/anti-drift/evaluator.test.js +0 -78
  75. package/dist/anti-drift/rules.d.ts +0 -80
  76. package/dist/anti-drift/rules.d.ts.map +0 -1
  77. package/dist/anti-drift/rules.js +0 -368
  78. package/dist/anti-drift/rules.js.map +0 -1
  79. package/dist/anti-drift/rules.test.js +0 -213
  80. package/dist/anti-drift/state.d.ts +0 -107
  81. package/dist/anti-drift/state.d.ts.map +0 -1
  82. package/dist/anti-drift/state.js +0 -177
  83. package/dist/anti-drift/state.js.map +0 -1
  84. package/dist/anti-drift/state.test.js +0 -120
  85. package/dist/chat/adapters/discord.d.ts +0 -41
  86. package/dist/chat/adapters/discord.d.ts.map +0 -1
  87. package/dist/chat/adapters/discord.js +0 -176
  88. package/dist/chat/adapters/discord.js.map +0 -1
  89. package/dist/chat/adapters/discord.test.js +0 -25
  90. package/dist/chat/adapters/slack.d.ts +0 -43
  91. package/dist/chat/adapters/slack.d.ts.map +0 -1
  92. package/dist/chat/adapters/slack.js +0 -172
  93. package/dist/chat/adapters/slack.js.map +0 -1
  94. package/dist/chat/adapters/slack.test.js +0 -30
  95. package/dist/chat/adapters/telegram.d.ts +0 -148
  96. package/dist/chat/adapters/telegram.d.ts.map +0 -1
  97. package/dist/chat/adapters/telegram.js +0 -498
  98. package/dist/chat/adapters/telegram.js.map +0 -1
  99. package/dist/chat/adapters/telegram.test.js +0 -94
  100. package/dist/chat/config.d.ts +0 -98
  101. package/dist/chat/config.d.ts.map +0 -1
  102. package/dist/chat/config.js +0 -185
  103. package/dist/chat/config.js.map +0 -1
  104. package/dist/chat/daemon/active-project.d.ts +0 -17
  105. package/dist/chat/daemon/active-project.d.ts.map +0 -1
  106. package/dist/chat/daemon/active-project.js +0 -23
  107. package/dist/chat/daemon/active-project.js.map +0 -1
  108. package/dist/chat/daemon/autospawn.d.ts +0 -40
  109. package/dist/chat/daemon/autospawn.d.ts.map +0 -1
  110. package/dist/chat/daemon/autospawn.js +0 -129
  111. package/dist/chat/daemon/autospawn.js.map +0 -1
  112. package/dist/chat/daemon/autospawn.test.js +0 -112
  113. package/dist/chat/daemon/cli.d.ts +0 -18
  114. package/dist/chat/daemon/cli.d.ts.map +0 -1
  115. package/dist/chat/daemon/cli.js +0 -71
  116. package/dist/chat/daemon/cli.js.map +0 -1
  117. package/dist/chat/daemon/collisions.js +0 -384
  118. package/dist/chat/daemon/health-check.d.ts +0 -69
  119. package/dist/chat/daemon/health-check.d.ts.map +0 -1
  120. package/dist/chat/daemon/health-check.js +0 -112
  121. package/dist/chat/daemon/health-check.js.map +0 -1
  122. package/dist/chat/daemon/inbox-read.d.ts +0 -35
  123. package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
  124. package/dist/chat/daemon/inbox-read.js +0 -75
  125. package/dist/chat/daemon/inbox-read.js.map +0 -1
  126. package/dist/chat/daemon/inbox-read.test.js +0 -97
  127. package/dist/chat/daemon/inbox.d.ts +0 -63
  128. package/dist/chat/daemon/inbox.d.ts.map +0 -1
  129. package/dist/chat/daemon/inbox.js +0 -56
  130. package/dist/chat/daemon/inbox.js.map +0 -1
  131. package/dist/chat/daemon/inbox.test.js +0 -110
  132. package/dist/chat/daemon/lifecycle.d.ts +0 -71
  133. package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
  134. package/dist/chat/daemon/lifecycle.js +0 -221
  135. package/dist/chat/daemon/lifecycle.js.map +0 -1
  136. package/dist/chat/daemon/lifecycle.test.js +0 -163
  137. package/dist/chat/daemon/protocol.d.ts +0 -107
  138. package/dist/chat/daemon/protocol.d.ts.map +0 -1
  139. package/dist/chat/daemon/protocol.js +0 -54
  140. package/dist/chat/daemon/protocol.js.map +0 -1
  141. package/dist/chat/daemon/routing.d.ts +0 -140
  142. package/dist/chat/daemon/routing.d.ts.map +0 -1
  143. package/dist/chat/daemon/routing.js +0 -198
  144. package/dist/chat/daemon/routing.js.map +0 -1
  145. package/dist/chat/daemon/routing.test.js +0 -259
  146. package/dist/chat/daemon/rpc-client.d.ts +0 -45
  147. package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
  148. package/dist/chat/daemon/rpc-client.js +0 -133
  149. package/dist/chat/daemon/rpc-client.js.map +0 -1
  150. package/dist/chat/daemon/rpc-server.d.ts +0 -39
  151. package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
  152. package/dist/chat/daemon/rpc-server.js +0 -385
  153. package/dist/chat/daemon/rpc-server.js.map +0 -1
  154. package/dist/chat/daemon/rpc.test.js +0 -177
  155. package/dist/chat/daemon/subscribers.js +0 -257
  156. package/dist/chat/daemon/worker.d.ts +0 -27
  157. package/dist/chat/daemon/worker.d.ts.map +0 -1
  158. package/dist/chat/daemon/worker.js +0 -313
  159. package/dist/chat/daemon/worker.js.map +0 -1
  160. package/dist/chat/daemon/workspace-topic.js +0 -324
  161. package/dist/chat/env-token.d.ts +0 -60
  162. package/dist/chat/env-token.d.ts.map +0 -1
  163. package/dist/chat/env-token.js +0 -137
  164. package/dist/chat/env-token.js.map +0 -1
  165. package/dist/chat/env-token.test.js +0 -160
  166. package/dist/chat/factory.d.ts +0 -30
  167. package/dist/chat/factory.d.ts.map +0 -1
  168. package/dist/chat/factory.js +0 -50
  169. package/dist/chat/factory.js.map +0 -1
  170. package/dist/chat/factory.test.js +0 -55
  171. package/dist/chat/gateway.d.ts +0 -176
  172. package/dist/chat/gateway.d.ts.map +0 -1
  173. package/dist/chat/gateway.js +0 -146
  174. package/dist/chat/gateway.js.map +0 -1
  175. package/dist/chat/gateway.test.js +0 -192
  176. package/dist/claude-md.d.ts +0 -39
  177. package/dist/claude-md.d.ts.map +0 -1
  178. package/dist/claude-md.js +0 -113
  179. package/dist/claude-md.js.map +0 -1
  180. package/dist/claude-md.test.js +0 -91
  181. package/dist/codex/activate.d.ts +0 -66
  182. package/dist/codex/activate.d.ts.map +0 -1
  183. package/dist/codex/activate.js +0 -329
  184. package/dist/codex/activate.js.map +0 -1
  185. package/dist/codex/activate.test.js +0 -229
  186. package/dist/codex/bundled-default/bundled-default.test.js +0 -161
  187. package/dist/codex/cli-publish.test.js +0 -133
  188. package/dist/codex/cli.d.ts +0 -35
  189. package/dist/codex/cli.d.ts.map +0 -1
  190. package/dist/codex/cli.js +0 -554
  191. package/dist/codex/cli.js.map +0 -1
  192. package/dist/codex/cli.test.js +0 -277
  193. package/dist/codex/import-skill-md.d.ts +0 -53
  194. package/dist/codex/import-skill-md.d.ts.map +0 -1
  195. package/dist/codex/import-skill-md.js +0 -236
  196. package/dist/codex/import-skill-md.js.map +0 -1
  197. package/dist/codex/import-skill-md.test.js +0 -225
  198. package/dist/codex/loader.d.ts +0 -27
  199. package/dist/codex/loader.d.ts.map +0 -1
  200. package/dist/codex/loader.js +0 -86
  201. package/dist/codex/loader.js.map +0 -1
  202. package/dist/codex/loader.test.js +0 -75
  203. package/dist/codex/parse.d.ts +0 -28
  204. package/dist/codex/parse.d.ts.map +0 -1
  205. package/dist/codex/parse.js +0 -309
  206. package/dist/codex/parse.js.map +0 -1
  207. package/dist/codex/parse.test.js +0 -241
  208. package/dist/codex/store.d.ts +0 -87
  209. package/dist/codex/store.d.ts.map +0 -1
  210. package/dist/codex/store.js +0 -205
  211. package/dist/codex/store.js.map +0 -1
  212. package/dist/codex/store.test.js +0 -242
  213. package/dist/codex/types.d.ts +0 -398
  214. package/dist/codex/types.d.ts.map +0 -1
  215. package/dist/codex/types.js +0 -21
  216. package/dist/codex/types.js.map +0 -1
  217. package/dist/config.d.ts +0 -53
  218. package/dist/config.d.ts.map +0 -1
  219. package/dist/config.js +0 -202
  220. package/dist/config.js.map +0 -1
  221. package/dist/config.test.js +0 -117
  222. package/dist/engine/cli.d.ts +0 -14
  223. package/dist/engine/cli.d.ts.map +0 -1
  224. package/dist/engine/cli.js +0 -171
  225. package/dist/engine/cli.js.map +0 -1
  226. package/dist/engine/client.d.ts +0 -219
  227. package/dist/engine/client.d.ts.map +0 -1
  228. package/dist/engine/client.js +0 -312
  229. package/dist/engine/client.js.map +0 -1
  230. package/dist/engine/config.d.ts +0 -62
  231. package/dist/engine/config.d.ts.map +0 -1
  232. package/dist/engine/config.js +0 -223
  233. package/dist/engine/config.js.map +0 -1
  234. package/dist/engine/index.d.ts +0 -17
  235. package/dist/engine/index.d.ts.map +0 -1
  236. package/dist/engine/index.js +0 -16
  237. package/dist/engine/index.js.map +0 -1
  238. package/dist/engine/resolver.d.ts +0 -62
  239. package/dist/engine/resolver.d.ts.map +0 -1
  240. package/dist/engine/resolver.js +0 -103
  241. package/dist/engine/resolver.js.map +0 -1
  242. package/dist/engine/singleton.d.ts +0 -95
  243. package/dist/engine/singleton.d.ts.map +0 -1
  244. package/dist/engine/singleton.js +0 -325
  245. package/dist/engine/singleton.js.map +0 -1
  246. package/dist/engine/types.d.ts +0 -402
  247. package/dist/engine/types.d.ts.map +0 -1
  248. package/dist/engine/types.js +0 -22
  249. package/dist/engine/types.js.map +0 -1
  250. package/dist/engine-binary-resolver.js +0 -110
  251. package/dist/engine-binary-resolver.test.js +0 -61
  252. package/dist/engine-cli.js +0 -60
  253. package/dist/engine-client.js +0 -301
  254. package/dist/engine-client.test.js +0 -118
  255. package/dist/functions/chain_state.d.ts +0 -51
  256. package/dist/functions/chain_state.d.ts.map +0 -1
  257. package/dist/functions/chain_state.js +0 -59
  258. package/dist/functions/chain_state.js.map +0 -1
  259. package/dist/hooks/drift-catalog.d.ts +0 -68
  260. package/dist/hooks/drift-catalog.d.ts.map +0 -1
  261. package/dist/hooks/drift-catalog.js +0 -184
  262. package/dist/hooks/drift-catalog.js.map +0 -1
  263. package/dist/hooks/drift-catalog.test.js +0 -154
  264. package/dist/hooks/drift-patterns.d.ts +0 -110
  265. package/dist/hooks/drift-patterns.d.ts.map +0 -1
  266. package/dist/hooks/drift-patterns.js +0 -289
  267. package/dist/hooks/drift-patterns.js.map +0 -1
  268. package/dist/hooks/drift-patterns.test.js +0 -325
  269. package/dist/hooks/engine-vocab-gate.d.ts +0 -108
  270. package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
  271. package/dist/hooks/engine-vocab-gate.js +0 -225
  272. package/dist/hooks/engine-vocab-gate.js.map +0 -1
  273. package/dist/hooks/engine-vocab-gate.test.js +0 -170
  274. package/dist/hooks/heartbeat.d.ts +0 -107
  275. package/dist/hooks/heartbeat.d.ts.map +0 -1
  276. package/dist/hooks/heartbeat.js +0 -316
  277. package/dist/hooks/heartbeat.js.map +0 -1
  278. package/dist/hooks/heartbeat.test.js +0 -393
  279. package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
  280. package/dist/hooks/honesty-ledger.d.ts +0 -123
  281. package/dist/hooks/honesty-ledger.d.ts.map +0 -1
  282. package/dist/hooks/honesty-ledger.js +0 -226
  283. package/dist/hooks/honesty-ledger.js.map +0 -1
  284. package/dist/hooks/honesty-ledger.test.js +0 -466
  285. package/dist/hooks/inline-report-check.d.ts +0 -63
  286. package/dist/hooks/inline-report-check.d.ts.map +0 -1
  287. package/dist/hooks/inline-report-check.js +0 -88
  288. package/dist/hooks/inline-report-check.js.map +0 -1
  289. package/dist/hooks/inline-report-check.test.js +0 -96
  290. package/dist/hooks/pre-tool-use.d.ts +0 -62
  291. package/dist/hooks/pre-tool-use.d.ts.map +0 -1
  292. package/dist/hooks/pre-tool-use.js +0 -342
  293. package/dist/hooks/pre-tool-use.js.map +0 -1
  294. package/dist/hooks/pre-tool-use.test.js +0 -134
  295. package/dist/hooks/session-end.d.ts +0 -15
  296. package/dist/hooks/session-end.d.ts.map +0 -1
  297. package/dist/hooks/session-end.js +0 -60
  298. package/dist/hooks/session-end.js.map +0 -1
  299. package/dist/hooks/session-end.test.js +0 -52
  300. package/dist/hooks/stop.d.ts +0 -35
  301. package/dist/hooks/stop.d.ts.map +0 -1
  302. package/dist/hooks/stop.js +0 -136
  303. package/dist/hooks/stop.js.map +0 -1
  304. package/dist/hooks/transcript-active-task.test.js +0 -342
  305. package/dist/hooks/transcript.d.ts +0 -26
  306. package/dist/hooks/transcript.d.ts.map +0 -1
  307. package/dist/hooks/transcript.js +0 -266
  308. package/dist/hooks/transcript.js.map +0 -1
  309. package/dist/hooks/transcript.test.js +0 -103
  310. package/dist/hooks/user-prompt-submit.d.ts +0 -74
  311. package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
  312. package/dist/hooks/user-prompt-submit.js +0 -256
  313. package/dist/hooks/user-prompt-submit.js.map +0 -1
  314. package/dist/hooks/user-prompt-submit.test.js +0 -118
  315. package/dist/hooks/versioning-gate.d.ts +0 -101
  316. package/dist/hooks/versioning-gate.d.ts.map +0 -1
  317. package/dist/hooks/versioning-gate.js +0 -245
  318. package/dist/hooks/versioning-gate.js.map +0 -1
  319. package/dist/hooks/versioning-gate.test.js +0 -368
  320. package/dist/hooks/workflow-gate.d.ts +0 -64
  321. package/dist/hooks/workflow-gate.d.ts.map +0 -1
  322. package/dist/hooks/workflow-gate.js +0 -152
  323. package/dist/hooks/workflow-gate.js.map +0 -1
  324. package/dist/hooks/workflow-gate.test.js +0 -197
  325. package/dist/hooks-cli.d.ts +0 -25
  326. package/dist/hooks-cli.d.ts.map +0 -1
  327. package/dist/hooks-cli.js +0 -286
  328. package/dist/hooks-cli.js.map +0 -1
  329. package/dist/hooks-cli.test.js +0 -148
  330. package/dist/origin.d.ts +0 -16
  331. package/dist/origin.d.ts.map +0 -1
  332. package/dist/origin.js +0 -92
  333. package/dist/origin.js.map +0 -1
  334. package/dist/packs/seed_lessons_ingest.d.ts +0 -30
  335. package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
  336. package/dist/packs/seed_lessons_ingest.js +0 -107
  337. package/dist/packs/seed_lessons_ingest.js.map +0 -1
  338. package/dist/project-cli.d.ts +0 -7
  339. package/dist/project-cli.d.ts.map +0 -1
  340. package/dist/project-cli.js +0 -145
  341. package/dist/project-cli.js.map +0 -1
  342. package/dist/project.d.ts +0 -127
  343. package/dist/project.d.ts.map +0 -1
  344. package/dist/project.js +0 -281
  345. package/dist/project.js.map +0 -1
  346. package/dist/project.test.js +0 -287
  347. package/dist/rag/backends/loop_engine.d.ts +0 -61
  348. package/dist/rag/backends/loop_engine.d.ts.map +0 -1
  349. package/dist/rag/backends/loop_engine.js +0 -160
  350. package/dist/rag/backends/loop_engine.js.map +0 -1
  351. package/dist/recall.d.ts +0 -82
  352. package/dist/recall.d.ts.map +0 -1
  353. package/dist/recall.js +0 -81
  354. package/dist/recall.js.map +0 -1
  355. package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
  356. package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
  357. package/dist/runtime/agent_bridge/autospawn.js +0 -251
  358. package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
  359. package/dist/runtime/chain_state.d.ts +0 -124
  360. package/dist/runtime/chain_state.d.ts.map +0 -1
  361. package/dist/runtime/chain_state.js +0 -189
  362. package/dist/runtime/chain_state.js.map +0 -1
  363. package/dist/runtime/hooks/permission_decision.d.ts +0 -34
  364. package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
  365. package/dist/runtime/hooks/permission_decision.js +0 -39
  366. package/dist/runtime/hooks/permission_decision.js.map +0 -1
  367. package/dist/runtime/workflow_fsm.d.ts +0 -21
  368. package/dist/runtime/workflow_fsm.d.ts.map +0 -1
  369. package/dist/runtime/workflow_fsm.js +0 -25
  370. package/dist/runtime/workflow_fsm.js.map +0 -1
  371. package/dist/runtime/workflow_map.d.ts +0 -26
  372. package/dist/runtime/workflow_map.d.ts.map +0 -1
  373. package/dist/runtime/workflow_map.js +0 -38
  374. package/dist/runtime/workflow_map.js.map +0 -1
  375. package/dist/scope.d.ts +0 -48
  376. package/dist/scope.d.ts.map +0 -1
  377. package/dist/scope.js +0 -111
  378. package/dist/scope.js.map +0 -1
  379. package/dist/setup/cli/topic_create_step.d.ts +0 -84
  380. package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
  381. package/dist/setup/cli/topic_create_step.js +0 -213
  382. package/dist/setup/cli/topic_create_step.js.map +0 -1
  383. package/dist/system-export.d.ts +0 -65
  384. package/dist/system-export.d.ts.map +0 -1
  385. package/dist/system-export.js +0 -194
  386. package/dist/system-export.js.map +0 -1
  387. package/dist/utterance/classifier.d.ts +0 -53
  388. package/dist/utterance/classifier.d.ts.map +0 -1
  389. package/dist/utterance/classifier.js +0 -184
  390. package/dist/utterance/classifier.js.map +0 -1
  391. package/dist/utterance/classifier.test.js +0 -147
@@ -1,289 +0,0 @@
1
- /**
2
- * Drift pattern catalog — known anti-patterns opensquid intercepts at
3
- * the Claude Code PreToolUse hook before the agent commits the action.
4
- *
5
- * Each pattern has:
6
- * - `id` — stable identifier for the rule
7
- * - `trigger` — matcher against tool call input
8
- * - `lesson` — short reference to the lesson that owns this rule
9
- * - `message` — what the agent sees in stderr when intercepted
10
- * - `severity` — "block" (exit 2 stops the call) or "warn" (stderr
11
- * only, call proceeds)
12
- *
13
- * Patterns are CONSERVATIVE on purpose: we'd rather miss a drift than
14
- * spam false positives. The catalog grows lesson-by-lesson as new
15
- * drifts are observed and the user endorses the rule.
16
- */
17
- // ---------------------------------------------------------------------
18
- // Catalog — start with drifts observed in actual build sessions
19
- // ---------------------------------------------------------------------
20
- export const DRIFT_PATTERNS = [
21
- // 1. git commit --amend — never amend (locked override, 2026-05-15)
22
- {
23
- id: "never-amend",
24
- tool: "Bash",
25
- trigger: { kind: "bash_regex", pattern: "git\\s+commit\\b[^\\n]*\\s--amend\\b" },
26
- lesson: "auto-commit",
27
- severity: "block",
28
- message: "BLOCKED: `git commit --amend` violates the never-amend rule (CLAUDE.md " +
29
- "claude-overrides:v1, feedback_auto_commit). Even on unpushed commits — " +
30
- "make a follow-up commit instead. Override only if the user explicitly " +
31
- "requested an amend in THIS turn.",
32
- },
33
- // 2. git push without explicit user request — block by default
34
- {
35
- id: "no-implicit-push",
36
- tool: "Bash",
37
- trigger: { kind: "bash_regex", pattern: "git\\s+push\\b" },
38
- lesson: "auto-commit",
39
- severity: "block",
40
- message: "BLOCKED: `git push` requires explicit user authorization. " +
41
- "Commits stay local until the user says push. If the user just said " +
42
- "to push (or has pre-authorized pushes per CLAUDE.md), bypass with " +
43
- "OPENSQUID_SKIP_DRIFT=1 for this command.",
44
- },
45
- // 3. Engine commit subject containing consumer-product strings
46
- // (substrate-purity rule). Heuristic: matches `git commit -m` in a
47
- // bash command whose body contains `codex`, `opensquid`, or
48
- // `MindCraftor` AND is running inside engine/.
49
- //
50
- // This one INTENTIONALLY peeks inside the -m "..." quoted message,
51
- // so it opts out of the default quote stripping.
52
- {
53
- id: "substrate-purity",
54
- tool: "Bash",
55
- trigger: {
56
- kind: "bash_regex",
57
- pattern: "loop/engine.*git\\s+commit[^\\n]*-m[^\\n]*(codex|opensquid|MindCraftor)",
58
- strip_quotes: false,
59
- },
60
- lesson: "code-quality",
61
- severity: "warn",
62
- message: "WARN: engine commit message appears to reference a consumer-product " +
63
- "concept (codex / opensquid / MindCraftor). Engine commit messages " +
64
- "must stay substrate-pure — engine speaks in engine types only. " +
65
- "Re-word using Pack provenance / lesson / authorship terminology.",
66
- },
67
- // 4. Force-push to main/master — extra protection on top of #2
68
- {
69
- id: "no-force-push-main",
70
- tool: "Bash",
71
- trigger: {
72
- kind: "bash_regex",
73
- pattern: "git\\s+push\\b[^\\n]*(--force|-f)\\b[^\\n]*\\b(main|master)\\b",
74
- },
75
- lesson: "auto-commit",
76
- severity: "block",
77
- message: "BLOCKED: force-push to main/master is destructive. Requires explicit " +
78
- "user request — and even then, prefer a regular push or a new branch.",
79
- },
80
- // 5. Telegram routing — task-completion reports sent to plugin:telegram
81
- // reply (DM) instead of opensquid chat_send (supergroup). 0.7.24 / D2.
82
- // Heuristic: the text starts with the agent's report marker `🦑 #<N>`.
83
- {
84
- id: "telegram-redirect-report",
85
- tool: "mcp__plugin_telegram_telegram__reply",
86
- trigger: { kind: "text_regex", pattern: "^\\s*🦑\\s+#\\d", field: "text" },
87
- lesson: "telegram-reports",
88
- severity: "warn",
89
- message: "WARN: this message looks like a task-completion report (starts with `🦑 #N`). " +
90
- "Per [[feedback_telegram_reports]], reports go via `mcp__opensquid__chat_send` " +
91
- "to the project's `report_channel` (supergroup + topic), not via " +
92
- "plugin:telegram reply (which is the user's DM). Re-route via chat_send if " +
93
- "this is meant to be a task report. Catches drift D2.",
94
- },
95
- // 6. Bundled multi-purpose commit — `git commit -m` message that
96
- // references 2+ distinct task numbers. The D4 incident was commit
97
- // bef7eff bundling "close #166 + defer #168 + section-header
98
- // rewrite" into one commit. The 2-task-ref heuristic is narrow
99
- // enough to avoid most legitimate single-purpose commits while
100
- // catching the typical bundle shape ("close #X and defer #Y").
101
- // 0.7.28 / D4. strip_quotes=false so the -m body is scanned.
102
- {
103
- id: "bundled-commit",
104
- tool: "Bash",
105
- trigger: {
106
- kind: "bash_regex",
107
- pattern: "git\\s+commit\\b[^\\n]*-m\\b[^\\n]*#\\d+[^\\n]*#\\d+",
108
- strip_quotes: false,
109
- },
110
- lesson: "auto-commit",
111
- severity: "warn",
112
- message: "WARN: commit message references 2+ task numbers (`#N`). Per the auto-commit " +
113
- "rule (CLAUDE.md), prefer multiple small logical commits over one large " +
114
- "catchall. If these task numbers are genuinely one logical unit (e.g. one " +
115
- "task closing two others as duplicates), proceed. Catches drift D4.",
116
- },
117
- ];
118
- /**
119
- * Run the catalog against a tool call. Returns every matching pattern;
120
- * caller decides block-vs-warn based on highest severity.
121
- */
122
- export function findDrifts(call) {
123
- const hits = [];
124
- for (const pattern of DRIFT_PATTERNS) {
125
- if (pattern.tool !== "*" && pattern.tool !== call.tool)
126
- continue;
127
- if (matches(pattern.trigger, call)) {
128
- hits.push({ pattern });
129
- }
130
- }
131
- return hits;
132
- }
133
- function matches(trigger, call) {
134
- switch (trigger.kind) {
135
- case "bash_contains": {
136
- const cmd = stringField(call.input, "command");
137
- if (cmd === null)
138
- return false;
139
- const haystack = trigger.strip_quotes === false ? cmd : stripQuotedStrings(cmd);
140
- return haystack.includes(trigger.needle);
141
- }
142
- case "bash_regex": {
143
- const cmd = stringField(call.input, "command");
144
- if (cmd === null)
145
- return false;
146
- const haystack = trigger.strip_quotes === false ? cmd : stripQuotedStrings(cmd);
147
- try {
148
- return new RegExp(trigger.pattern).test(haystack);
149
- }
150
- catch {
151
- return false;
152
- }
153
- }
154
- case "text_regex": {
155
- const text = stringField(call.input, trigger.field);
156
- if (text === null)
157
- return false;
158
- try {
159
- return new RegExp(trigger.pattern).test(text);
160
- }
161
- catch {
162
- return false;
163
- }
164
- }
165
- }
166
- }
167
- /**
168
- * Remove single- and double-quoted string contents PLUS HEREDOC bodies
169
- * from a shell command so drift patterns match REAL shell tokens, not
170
- * text that happens to appear inside `echo "..."`, `grep '...'`, or a
171
- * `git commit -m "$(cat <<'EOF' ... EOF)"` body.
172
- *
173
- * Approximate: doesn't handle backslash-escaped quotes or `$(...)`
174
- * nesting beyond the HEREDOC. Sufficient for the false-positive cases
175
- * observed in real Claude Code sessions; tighten if a real-world
176
- * counter-example surfaces.
177
- *
178
- * v0.6.5 (#136) — added HEREDOC body stripping. Previously, a
179
- * `git commit -m` with a HEREDOC message body containing the literal
180
- * string "git push" would false-fire the no-implicit-push drift block
181
- * because the entire HEREDOC body was part of the bash command string.
182
- * Now the body is stripped before pattern matching. Caught dogfooding
183
- * the v0.6.4 commit (commit message described regex patterns containing
184
- * the word "git push" and the drift-block fired against itself).
185
- *
186
- * Replacement is empty rather than a placeholder so adjacent tokens
187
- * still parse correctly (e.g. `cmd "literal" && more` → `cmd && more`).
188
- */
189
- function stripQuotedStrings(s) {
190
- return stripHeredocBodies(s)
191
- .replace(/'[^']*'/g, "")
192
- .replace(/"[^"]*"/g, "");
193
- }
194
- /**
195
- * Strip HEREDOC bodies (`<<DELIM ... DELIM` and variants) from a
196
- * shell command.
197
- *
198
- * Recognizes:
199
- * <<EOF ... \nEOF (unquoted delimiter, expansion-allowing)
200
- * <<'EOF' ... \nEOF (single-quoted, literal body)
201
- * <<"EOF" ... \nEOF (double-quoted, literal body)
202
- * <<-EOF ... \nEOF (tab-stripping mode)
203
- * <<-'EOF' ... \nEOF (combined)
204
- *
205
- * Delimiter is any word-char sequence (EOF, END, HERE, MARKER, etc.).
206
- * Lazy `[\s\S]*?` matches across newlines; `\b` after the backref
207
- * ensures `EOFX` doesn't close an `<<EOF` block.
208
- *
209
- * If a HEREDOC has no closing delimiter (truncated input), regex
210
- * doesn't match and the body stays intact — fail-open behavior.
211
- *
212
- * Exported for direct unit testing.
213
- */
214
- export function stripHeredocBodies(s) {
215
- // `\n[ \t]*\1\b` — allow leading whitespace before the closing
216
- // delimiter so the `<<-DELIM` (tab-stripping) variant matches its
217
- // indented closing line (`\t\tEOF`). The permissive whitespace
218
- // also covers the plain `<<DELIM` case where users sometimes
219
- // accidentally indent the closing line — no real harm.
220
- return s.replace(/<<-?\s*['"]?(\w+)['"]?[\s\S]*?\n[ \t]*\1\b/g, "");
221
- }
222
- function stringField(input, field) {
223
- const v = input[field];
224
- return typeof v === "string" ? v : null;
225
- }
226
- /**
227
- * Decide the final exit code + message from a list of hits.
228
- *
229
- * - Any "block" hit → exit 2 with all blocking messages
230
- * - Only "warn" hits → exit 0, print warnings to stderr
231
- * - No hits → exit 0 silently
232
- *
233
- * Emergency bypass: `OPENSQUID_SKIP_DRIFT=1` downgrades every block to
234
- * an audit-trail warning (exit 0, stderr explains the bypass). Two ways
235
- * to set it:
236
- *
237
- * 1. Parent process env — useful for whole-session bypass (e.g. set
238
- * before launching Claude Code).
239
- * 2. Inline command prefix — e.g. `OPENSQUID_SKIP_DRIFT=1 git push`.
240
- * The hook reads the COMMAND STRING from the Bash tool input and
241
- * sees the prefix even though the env var never reaches the hook's
242
- * own process.env (the hook is a sibling subprocess spawned by
243
- * Claude Code, not a child of the would-be Bash subprocess).
244
- *
245
- * Matches the shape of the version-gate (`OPENSQUID_SKIP_VERSION_GATE=1`)
246
- * and workflow-gate (`OPENSQUID_SKIP_WORKFLOW_GATE=1`) bypasses so the
247
- * operator only has one mental model — except those two only check
248
- * process.env (their hooks happen before any command runs); drift-
249
- * patterns additionally checks the command-string prefix so the bypass
250
- * can be requested per-command from within an existing session.
251
- */
252
- export function decide(hits, call) {
253
- if (hits.length === 0)
254
- return { exit: 0, stderr: "" };
255
- if (isDriftBypassed(call)) {
256
- const ids = hits.map((h) => h.pattern.id).join(", ");
257
- return {
258
- exit: 0,
259
- stderr: `🦑 [opensquid drift-patterns] BYPASSED via OPENSQUID_SKIP_DRIFT=1 (hits: ${ids})\n`,
260
- };
261
- }
262
- const blocks = hits.filter((h) => h.pattern.severity === "block");
263
- const warns = hits.filter((h) => h.pattern.severity === "warn");
264
- const lines = [];
265
- for (const h of blocks) {
266
- lines.push(`🦑 [opensquid drift-block] ${h.pattern.id}: ${h.pattern.message}`);
267
- }
268
- for (const h of warns) {
269
- lines.push(`🦑 [opensquid drift-warn] ${h.pattern.id}: ${h.pattern.message}`);
270
- }
271
- return {
272
- exit: blocks.length > 0 ? 2 : 0,
273
- stderr: lines.join("\n") + "\n",
274
- };
275
- }
276
- function isDriftBypassed(call) {
277
- if (process.env.OPENSQUID_SKIP_DRIFT === "1")
278
- return true;
279
- if (!call)
280
- return false;
281
- const cmd = stringField(call.input, "command");
282
- if (cmd === null)
283
- return false;
284
- // Inline prefix: zero or more env-var assignments may precede the
285
- // bypass var. Permissive on whitespace; strict on the value (must be
286
- // literally "1" to match the env-var semantics).
287
- return /(^|\s|;|&&)\s*OPENSQUID_SKIP_DRIFT=1(\s|$)/.test(cmd);
288
- }
289
- //# sourceMappingURL=drift-patterns.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"drift-patterns.js","sourceRoot":"","sources":["../../src.legacy/hooks/drift-patterns.ts"],"names":[],"mappings":"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"}
@@ -1,325 +0,0 @@
1
- import { afterEach, describe, expect, it } from "vitest";
2
- import { decide, findDrifts, stripHeredocBodies } from "./drift-patterns.js";
3
- function bash(command) {
4
- return { tool: "Bash", input: { command } };
5
- }
6
- function telegramReply(text) {
7
- return {
8
- tool: "mcp__plugin_telegram_telegram__reply",
9
- input: { chat_id: "8075471258", text },
10
- };
11
- }
12
- describe("drift catalog — never-amend", () => {
13
- it("blocks `git commit --amend`", () => {
14
- const hits = findDrifts(bash('git commit --amend -m "fix typo"'));
15
- expect(hits.map((h) => h.pattern.id)).toContain("never-amend");
16
- expect(decide(hits).exit).toBe(2);
17
- });
18
- it("blocks `git commit -a --amend`", () => {
19
- const hits = findDrifts(bash("git commit -a --amend"));
20
- expect(hits.map((h) => h.pattern.id)).toContain("never-amend");
21
- });
22
- it("doesn't fire on a normal `git commit -m`", () => {
23
- const hits = findDrifts(bash('git commit -m "regular commit"'));
24
- expect(hits.map((h) => h.pattern.id)).not.toContain("never-amend");
25
- });
26
- });
27
- describe("drift catalog — no-implicit-push", () => {
28
- it("blocks `git push`", () => {
29
- const hits = findDrifts(bash("git push origin main"));
30
- expect(hits.map((h) => h.pattern.id)).toContain("no-implicit-push");
31
- expect(decide(hits).exit).toBe(2);
32
- });
33
- it("blocks `git push -u origin feature`", () => {
34
- const hits = findDrifts(bash("git push -u origin feature/x"));
35
- expect(hits.map((h) => h.pattern.id)).toContain("no-implicit-push");
36
- });
37
- it("force-push to main is ALSO caught by both rules", () => {
38
- const hits = findDrifts(bash("git push --force origin main"));
39
- const ids = hits.map((h) => h.pattern.id);
40
- expect(ids).toContain("no-implicit-push");
41
- expect(ids).toContain("no-force-push-main");
42
- expect(decide(hits).exit).toBe(2);
43
- });
44
- it("doesn't fire on `git pull` or `git status`", () => {
45
- expect(findDrifts(bash("git pull")).length).toBe(0);
46
- expect(findDrifts(bash("git status")).length).toBe(0);
47
- });
48
- });
49
- describe("drift catalog — substrate-purity (engine commits)", () => {
50
- it("warns on engine commit message referencing 'codex'", () => {
51
- const hits = findDrifts(bash('cd /Users/slee/projects/loop/engine && git commit -m "v1.1: codex support"'));
52
- expect(hits.map((h) => h.pattern.id)).toContain("substrate-purity");
53
- // Severity = warn, so exit stays 0 (call proceeds).
54
- expect(decide(hits).exit).toBe(0);
55
- });
56
- it("warns on engine commit referencing 'opensquid'", () => {
57
- const hits = findDrifts(bash('cd ~/projects/loop/engine && git commit -m "support for opensquid pack"'));
58
- expect(hits.map((h) => h.pattern.id)).toContain("substrate-purity");
59
- });
60
- it("doesn't fire on substrate-pure engine commits", () => {
61
- const hits = findDrifts(bash('cd /Users/slee/projects/loop/engine && git commit -m "v1.1: Pack authorship"'));
62
- expect(hits.map((h) => h.pattern.id)).not.toContain("substrate-purity");
63
- });
64
- it("doesn't fire on opensquid commits mentioning codex (correct context)", () => {
65
- const hits = findDrifts(bash('cd /Users/slee/projects/opensquid && git commit -m "v0.4: codex CLI"'));
66
- expect(hits.map((h) => h.pattern.id)).not.toContain("substrate-purity");
67
- });
68
- });
69
- describe("decide — combining hits", () => {
70
- it("returns exit 0 + empty stderr on no hits", () => {
71
- const { exit, stderr } = decide([]);
72
- expect(exit).toBe(0);
73
- expect(stderr).toBe("");
74
- });
75
- it("returns exit 2 if any hit is severity=block", () => {
76
- const hits = findDrifts(bash("git push --force origin main"));
77
- const { exit, stderr } = decide(hits);
78
- expect(exit).toBe(2);
79
- expect(stderr).toContain("BLOCKED");
80
- });
81
- it("returns exit 0 + stderr on warn-only hits", () => {
82
- const hits = findDrifts(bash('cd /Users/slee/projects/loop/engine && git commit -m "codex stuff"'));
83
- const { exit, stderr } = decide(hits);
84
- expect(exit).toBe(0);
85
- expect(stderr).toContain("WARN");
86
- });
87
- });
88
- describe("non-bash tools are not matched by bash rules", () => {
89
- it("Edit calls bypass git rules", () => {
90
- const call = {
91
- tool: "Edit",
92
- input: { file_path: "/x/y", old_string: "git commit --amend", new_string: "" },
93
- };
94
- expect(findDrifts(call).length).toBe(0);
95
- });
96
- });
97
- describe("false-positive resistance — patterns inside quoted strings", () => {
98
- it("never-amend ignores --amend inside double-quoted string", () => {
99
- const hits = findDrifts(bash('echo "git commit --amend in a string literal"'));
100
- expect(hits.map((h) => h.pattern.id)).not.toContain("never-amend");
101
- });
102
- it("never-amend ignores --amend inside single-quoted string", () => {
103
- const hits = findDrifts(bash("grep 'git commit --amend' file.txt"));
104
- expect(hits.map((h) => h.pattern.id)).not.toContain("never-amend");
105
- });
106
- it("no-implicit-push ignores 'git push' in an echo literal", () => {
107
- const hits = findDrifts(bash('echo "to deploy run git push origin main"'));
108
- expect(hits.map((h) => h.pattern.id)).not.toContain("no-implicit-push");
109
- });
110
- it("never-amend STILL fires when amend is a real shell token", () => {
111
- const hits = findDrifts(bash('git commit --amend -m "fix typo"'));
112
- expect(hits.map((h) => h.pattern.id)).toContain("never-amend");
113
- });
114
- it("never-amend fires after shell continuation (&&, ;, |)", () => {
115
- expect(findDrifts(bash("cd /repo && git commit --amend")).map((h) => h.pattern.id)).toContain("never-amend");
116
- expect(findDrifts(bash("foo; git commit --amend")).map((h) => h.pattern.id)).toContain("never-amend");
117
- });
118
- });
119
- // =====================================================================
120
- // v0.6.5 (#136) — HEREDOC body stripping. Caught while dogfooding the
121
- // v0.6.4 commit: the no-implicit-push drift fired against a `git commit`
122
- // whose HEREDOC commit message body contained the literal string
123
- // describing a regex pattern (the words `git push` appeared in prose
124
- // describing a pattern). The hook scanned the entire bash command
125
- // string including HEREDOC bodies → false-positive block.
126
- //
127
- // Fix: stripHeredocBodies runs before stripQuotedStrings so the body
128
- // is removed before any drift regex sees it.
129
- // =====================================================================
130
- describe("stripHeredocBodies (v0.6.5)", () => {
131
- it("strips single-quoted-delimiter HEREDOC body", () => {
132
- const cmd = `git commit -m "$(cat <<'EOF'
133
- This body contains git push origin main
134
- EOF
135
- )"`;
136
- expect(stripHeredocBodies(cmd)).not.toContain("git push");
137
- });
138
- it("strips unquoted-delimiter HEREDOC body", () => {
139
- const cmd = `cat <<MARKER
140
- inner content with git push verbatim
141
- MARKER`;
142
- expect(stripHeredocBodies(cmd)).not.toContain("git push");
143
- });
144
- it("strips double-quoted-delimiter HEREDOC body", () => {
145
- const cmd = `cat <<"END"
146
- git push --force here
147
- END`;
148
- expect(stripHeredocBodies(cmd)).not.toContain("git push");
149
- });
150
- it("strips tab-stripping (<<-) variant", () => {
151
- const cmd = `cat <<-EOF
152
- \t\tgit push danger
153
- \tEOF`;
154
- expect(stripHeredocBodies(cmd)).not.toContain("git push");
155
- });
156
- it("strips multiple HEREDOCs in one command", () => {
157
- const cmd = `cat <<'A'
158
- contains git push
159
- A
160
- echo "between"
161
- cat <<'B'
162
- contains git commit --amend
163
- B`;
164
- const stripped = stripHeredocBodies(cmd);
165
- expect(stripped).not.toContain("git push");
166
- expect(stripped).not.toContain("git commit --amend");
167
- });
168
- it("leaves a truncated HEREDOC (no closing delimiter) intact (fail-open)", () => {
169
- const cmd = `cat <<EOF
170
- truncated body with git push but no EOF closing`;
171
- // No \nEOF\b on its own → regex doesn't match → fail-open
172
- expect(stripHeredocBodies(cmd)).toContain("git push");
173
- });
174
- });
175
- describe("drift catalog — HEREDOC false-positive resistance (v0.6.5 #136)", () => {
176
- it("no-implicit-push does NOT fire when 'git push' appears only in a HEREDOC commit message", () => {
177
- // This is the exact pattern that bit me during the v0.6.4 commit.
178
- const cmd = `git -c commit.gpgsign=false commit -m "$(cat <<'EOF'
179
- feat: blah
180
-
181
- - pushed (bash_regex git push) — this LITERAL string in the message
182
- body would have tripped the no-implicit-push drift block before
183
- the v0.6.5 fix.
184
- EOF
185
- )"`;
186
- const hits = findDrifts(bash(cmd));
187
- expect(hits.map((h) => h.pattern.id)).not.toContain("no-implicit-push");
188
- });
189
- it("never-amend does NOT fire on 'git commit --amend' in HEREDOC commit body", () => {
190
- const cmd = `git commit -m "$(cat <<'EOF'
191
- Mentioning git commit --amend in the message body for context.
192
- EOF
193
- )"`;
194
- const hits = findDrifts(bash(cmd));
195
- expect(hits.map((h) => h.pattern.id)).not.toContain("never-amend");
196
- });
197
- it("STILL fires when 'git push' is the actual command after a HEREDOC", () => {
198
- // The HEREDOC ends, then a real git push follows. Must still block.
199
- const cmd = `cat <<'EOF'
200
- some prose
201
- EOF
202
- git push origin main`;
203
- const hits = findDrifts(bash(cmd));
204
- expect(hits.map((h) => h.pattern.id)).toContain("no-implicit-push");
205
- });
206
- });
207
- // =====================================================================
208
- // v0.6.6 (#137) — OPENSQUID_SKIP_DRIFT emergency bypass. Mirrors
209
- // OPENSQUID_SKIP_VERSION_GATE / OPENSQUID_SKIP_WORKFLOW_GATE shape so
210
- // the operator only has one mental model for "this hook is wrong, get
211
- // out of my way". The documented uninstall-hooks workaround doesn't
212
- // actually work mid-session because Claude Code caches the settings.json
213
- // hook command at session start.
214
- // =====================================================================
215
- describe("OPENSQUID_SKIP_DRIFT bypass (v0.6.6)", () => {
216
- // Vitest runs tests serially within a file by default; we restore the
217
- // env var after each test so other tests aren't tainted.
218
- afterEach(() => {
219
- delete process.env.OPENSQUID_SKIP_DRIFT;
220
- });
221
- it("ALLOWS (exit 0) with bypass warning when OPENSQUID_SKIP_DRIFT=1 is in parent env and a block would fire", () => {
222
- process.env.OPENSQUID_SKIP_DRIFT = "1";
223
- const call = bash("git push origin main");
224
- const hits = findDrifts(call);
225
- expect(hits.length).toBeGreaterThan(0);
226
- const { exit, stderr } = decide(hits, call);
227
- expect(exit).toBe(0);
228
- expect(stderr).toContain("BYPASSED via OPENSQUID_SKIP_DRIFT=1");
229
- expect(stderr).toContain("no-implicit-push");
230
- });
231
- it("ALLOWS via INLINE prefix `OPENSQUID_SKIP_DRIFT=1 git push ...` even when parent env unset", () => {
232
- delete process.env.OPENSQUID_SKIP_DRIFT;
233
- const call = bash("OPENSQUID_SKIP_DRIFT=1 git push origin main");
234
- const hits = findDrifts(call);
235
- expect(hits.length).toBeGreaterThan(0);
236
- const { exit, stderr } = decide(hits, call);
237
- expect(exit).toBe(0);
238
- expect(stderr).toContain("BYPASSED via OPENSQUID_SKIP_DRIFT=1");
239
- });
240
- it("ALLOWS via inline prefix after `cd ... &&` chain", () => {
241
- const call = bash("cd /tmp && OPENSQUID_SKIP_DRIFT=1 git push origin main");
242
- const hits = findDrifts(call);
243
- const { exit } = decide(hits, call);
244
- expect(exit).toBe(0);
245
- });
246
- it("includes ALL hit ids in the bypass message (operator audit trail)", () => {
247
- process.env.OPENSQUID_SKIP_DRIFT = "1";
248
- const call = bash("git push --force origin main");
249
- const hits = findDrifts(call);
250
- const { stderr } = decide(hits, call);
251
- expect(stderr).toContain("no-implicit-push");
252
- expect(stderr).toContain("no-force-push-main");
253
- });
254
- it("does NOT bypass when env var is unset or != '1'", () => {
255
- process.env.OPENSQUID_SKIP_DRIFT = "true";
256
- const call = bash("git push origin main");
257
- const hits = findDrifts(call);
258
- const { exit } = decide(hits, call);
259
- expect(exit).toBe(2);
260
- });
261
- it("does NOT bypass when inline prefix value != '1'", () => {
262
- const call = bash("OPENSQUID_SKIP_DRIFT=true git push origin main");
263
- const hits = findDrifts(call);
264
- const { exit } = decide(hits, call);
265
- expect(exit).toBe(2);
266
- });
267
- it("does NOT bypass when var name appears only as substring", () => {
268
- // Defensive: `MY_OPENSQUID_SKIP_DRIFT=1` shouldn't match (leading word-boundary).
269
- const call = bash("MY_OPENSQUID_SKIP_DRIFT=1 git push origin main");
270
- const hits = findDrifts(call);
271
- const { exit } = decide(hits, call);
272
- expect(exit).toBe(2);
273
- });
274
- it("emits nothing on empty hits regardless of env var", () => {
275
- process.env.OPENSQUID_SKIP_DRIFT = "1";
276
- const { exit, stderr } = decide([]);
277
- expect(exit).toBe(0);
278
- expect(stderr).toBe("");
279
- });
280
- });
281
- describe("drift catalog — telegram-redirect-report (D2)", () => {
282
- it("warns when plugin:telegram reply starts with `🦑 #N` task-report marker", () => {
283
- const hits = findDrifts(telegramReply("🦑 #170 — engine-client startupAck fix\n\nshipped"));
284
- expect(hits.map((h) => h.pattern.id)).toContain("telegram-redirect-report");
285
- expect(decide(hits).exit).toBe(0); // warn-severity = non-blocking
286
- });
287
- it("warns with leading whitespace before the marker", () => {
288
- const hits = findDrifts(telegramReply(" 🦑 #4 — cleanup commit"));
289
- expect(hits.map((h) => h.pattern.id)).toContain("telegram-redirect-report");
290
- });
291
- it("does NOT fire on regular plugin:telegram replies", () => {
292
- const hits = findDrifts(telegramReply("ok, working on it now"));
293
- expect(hits.map((h) => h.pattern.id)).not.toContain("telegram-redirect-report");
294
- });
295
- it("does NOT fire on chat_send (the correct tool for reports)", () => {
296
- const hits = findDrifts({
297
- tool: "mcp__opensquid__chat_send",
298
- input: { text: "🦑 #170 — full 7-phase report goes here" },
299
- });
300
- expect(hits.map((h) => h.pattern.id)).not.toContain("telegram-redirect-report");
301
- });
302
- });
303
- describe("drift catalog — bundled-commit (D4)", () => {
304
- it("warns on a commit message that references 2+ task numbers", () => {
305
- const hits = findDrifts(bash('git commit -m "close #166 and defer #168"'));
306
- expect(hits.map((h) => h.pattern.id)).toContain("bundled-commit");
307
- expect(decide(hits).exit).toBe(0); // warn-severity = non-blocking
308
- });
309
- it('warns on inline `-m "..."` with 2 #N refs on one line', () => {
310
- const hits = findDrifts(bash('git commit -m "release: #166 + #168 + cleanup"'));
311
- expect(hits.map((h) => h.pattern.id)).toContain("bundled-commit");
312
- });
313
- // Known limitation: HEREDOC commit message bodies are stripped by
314
- // stripHeredocBodies BEFORE pattern matching, so refs inside the
315
- // HEREDOC body don't fire this pattern. Adding staged-content-aware
316
- // detection (via a dedicated bundled-commit-gate) is deferred.
317
- it("does NOT fire on a single-task commit", () => {
318
- const hits = findDrifts(bash('git commit -m "fix(workflow-gate): session_id mismatch (#166)"'));
319
- expect(hits.map((h) => h.pattern.id)).not.toContain("bundled-commit");
320
- });
321
- it("does NOT fire on commits with no task ref at all", () => {
322
- const hits = findDrifts(bash('git commit -m "refactor: drift-patterns.ts"'));
323
- expect(hits.map((h) => h.pattern.id)).not.toContain("bundled-commit");
324
- });
325
- });