@funnycode/myclaude 0.1.55 → 0.1.60

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 (499) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +63 -0
  3. package/README.zh-CN.md +63 -0
  4. package/dist/SKILL-r7zmg5v7.md +3 -0
  5. package/dist/cli-mvr5k580.md +3 -0
  6. package/dist/myclaude.js +14230 -8799
  7. package/dist/myclaude.mjs +14230 -8799
  8. package/dist/server-zyhc2a9z.md +3 -0
  9. package/package.json +128 -128
  10. package/seed/marketplaces/ecc/.opencode/package-lock.json +169 -169
  11. package/seed/marketplaces/ecc/commands/aside.md +164 -164
  12. package/seed/marketplaces/ecc/commands/auto-update.md +28 -28
  13. package/seed/marketplaces/ecc/commands/build-fix.md +66 -66
  14. package/seed/marketplaces/ecc/commands/checkpoint.md +78 -78
  15. package/seed/marketplaces/ecc/commands/code-review.md +289 -289
  16. package/seed/marketplaces/ecc/commands/cost-report.md +107 -107
  17. package/seed/marketplaces/ecc/commands/cpp-build.md +173 -173
  18. package/seed/marketplaces/ecc/commands/cpp-review.md +132 -132
  19. package/seed/marketplaces/ecc/commands/cpp-test.md +251 -251
  20. package/seed/marketplaces/ecc/commands/ecc-guide.md +93 -93
  21. package/seed/marketplaces/ecc/commands/evolve.md +178 -178
  22. package/seed/marketplaces/ecc/commands/fastapi-review.md +39 -39
  23. package/seed/marketplaces/ecc/commands/feature-dev.md +49 -49
  24. package/seed/marketplaces/ecc/commands/flutter-build.md +164 -164
  25. package/seed/marketplaces/ecc/commands/flutter-review.md +116 -116
  26. package/seed/marketplaces/ecc/commands/flutter-test.md +144 -144
  27. package/seed/marketplaces/ecc/commands/gan-build.md +103 -103
  28. package/seed/marketplaces/ecc/commands/gan-design.md +39 -39
  29. package/seed/marketplaces/ecc/commands/go-build.md +183 -183
  30. package/seed/marketplaces/ecc/commands/go-review.md +148 -148
  31. package/seed/marketplaces/ecc/commands/go-test.md +268 -268
  32. package/seed/marketplaces/ecc/commands/gradle-build.md +70 -70
  33. package/seed/marketplaces/ecc/commands/harness-audit.md +84 -84
  34. package/seed/marketplaces/ecc/commands/hookify-configure.md +14 -14
  35. package/seed/marketplaces/ecc/commands/hookify-help.md +46 -46
  36. package/seed/marketplaces/ecc/commands/hookify-list.md +21 -21
  37. package/seed/marketplaces/ecc/commands/hookify.md +50 -50
  38. package/seed/marketplaces/ecc/commands/instinct-export.md +66 -66
  39. package/seed/marketplaces/ecc/commands/instinct-import.md +114 -114
  40. package/seed/marketplaces/ecc/commands/instinct-status.md +59 -59
  41. package/seed/marketplaces/ecc/commands/jira.md +106 -106
  42. package/seed/marketplaces/ecc/commands/kotlin-build.md +174 -174
  43. package/seed/marketplaces/ecc/commands/kotlin-review.md +140 -140
  44. package/seed/marketplaces/ecc/commands/kotlin-test.md +312 -312
  45. package/seed/marketplaces/ecc/commands/learn-eval.md +116 -116
  46. package/seed/marketplaces/ecc/commands/learn.md +74 -74
  47. package/seed/marketplaces/ecc/commands/loop-start.md +36 -36
  48. package/seed/marketplaces/ecc/commands/loop-status.md +77 -77
  49. package/seed/marketplaces/ecc/commands/marketing-campaign.md +129 -129
  50. package/seed/marketplaces/ecc/commands/model-route.md +30 -30
  51. package/seed/marketplaces/ecc/commands/multi-backend.md +162 -162
  52. package/seed/marketplaces/ecc/commands/multi-execute.md +319 -319
  53. package/seed/marketplaces/ecc/commands/multi-frontend.md +162 -162
  54. package/seed/marketplaces/ecc/commands/multi-plan.md +272 -272
  55. package/seed/marketplaces/ecc/commands/multi-workflow.md +195 -195
  56. package/seed/marketplaces/ecc/commands/plan-prd.md +160 -160
  57. package/seed/marketplaces/ecc/commands/plan.md +200 -200
  58. package/seed/marketplaces/ecc/commands/pm2.md +276 -276
  59. package/seed/marketplaces/ecc/commands/pr.md +184 -184
  60. package/seed/marketplaces/ecc/commands/project-init.md +86 -86
  61. package/seed/marketplaces/ecc/commands/projects.md +39 -39
  62. package/seed/marketplaces/ecc/commands/promote.md +41 -41
  63. package/seed/marketplaces/ecc/commands/prp-commit.md +112 -112
  64. package/seed/marketplaces/ecc/commands/prp-implement.md +385 -385
  65. package/seed/marketplaces/ecc/commands/prp-plan.md +502 -502
  66. package/seed/marketplaces/ecc/commands/prp-pr.md +184 -184
  67. package/seed/marketplaces/ecc/commands/prp-prd.md +447 -447
  68. package/seed/marketplaces/ecc/commands/prune.md +31 -31
  69. package/seed/marketplaces/ecc/commands/python-review.md +297 -297
  70. package/seed/marketplaces/ecc/commands/quality-gate.md +33 -33
  71. package/seed/marketplaces/ecc/commands/refactor-clean.md +84 -84
  72. package/seed/marketplaces/ecc/commands/resume-session.md +156 -156
  73. package/seed/marketplaces/ecc/commands/review-pr.md +37 -37
  74. package/seed/marketplaces/ecc/commands/rust-build.md +187 -187
  75. package/seed/marketplaces/ecc/commands/rust-review.md +142 -142
  76. package/seed/marketplaces/ecc/commands/rust-test.md +308 -308
  77. package/seed/marketplaces/ecc/commands/santa-loop.md +175 -175
  78. package/seed/marketplaces/ecc/commands/save-session.md +275 -275
  79. package/seed/marketplaces/ecc/commands/security-scan.md +92 -92
  80. package/seed/marketplaces/ecc/commands/sessions.md +339 -339
  81. package/seed/marketplaces/ecc/commands/setup-pm.md +80 -80
  82. package/seed/marketplaces/ecc/commands/skill-create.md +174 -174
  83. package/seed/marketplaces/ecc/commands/skill-health.md +54 -54
  84. package/seed/marketplaces/ecc/commands/test-coverage.md +73 -73
  85. package/seed/marketplaces/ecc/commands/update-codemaps.md +76 -76
  86. package/seed/marketplaces/ecc/commands/update-docs.md +88 -88
  87. package/seed/marketplaces/ecc/skills/accessibility/SKILL.md +146 -146
  88. package/seed/marketplaces/ecc/skills/agent-architecture-audit/SKILL.md +256 -256
  89. package/seed/marketplaces/ecc/skills/agent-eval/SKILL.md +145 -145
  90. package/seed/marketplaces/ecc/skills/agent-harness-construction/SKILL.md +73 -73
  91. package/seed/marketplaces/ecc/skills/agent-introspection-debugging/SKILL.md +153 -153
  92. package/seed/marketplaces/ecc/skills/agent-payment-x402/SKILL.md +224 -224
  93. package/seed/marketplaces/ecc/skills/agent-sort/SKILL.md +215 -215
  94. package/seed/marketplaces/ecc/skills/agentic-engineering/SKILL.md +63 -63
  95. package/seed/marketplaces/ecc/skills/agentic-os/SKILL.md +387 -387
  96. package/seed/marketplaces/ecc/skills/ai-first-engineering/SKILL.md +51 -51
  97. package/seed/marketplaces/ecc/skills/ai-regression-testing/SKILL.md +385 -385
  98. package/seed/marketplaces/ecc/skills/android-clean-architecture/SKILL.md +339 -339
  99. package/seed/marketplaces/ecc/skills/angular-developer/SKILL.md +154 -154
  100. package/seed/marketplaces/ecc/skills/angular-developer/references/angular-animations.md +160 -160
  101. package/seed/marketplaces/ecc/skills/angular-developer/references/angular-aria.md +410 -410
  102. package/seed/marketplaces/ecc/skills/angular-developer/references/cli.md +86 -86
  103. package/seed/marketplaces/ecc/skills/angular-developer/references/component-harnesses.md +59 -59
  104. package/seed/marketplaces/ecc/skills/angular-developer/references/component-styling.md +91 -91
  105. package/seed/marketplaces/ecc/skills/angular-developer/references/components.md +117 -117
  106. package/seed/marketplaces/ecc/skills/angular-developer/references/creating-services.md +97 -97
  107. package/seed/marketplaces/ecc/skills/angular-developer/references/data-resolvers.md +69 -69
  108. package/seed/marketplaces/ecc/skills/angular-developer/references/define-routes.md +67 -67
  109. package/seed/marketplaces/ecc/skills/angular-developer/references/defining-providers.md +72 -72
  110. package/seed/marketplaces/ecc/skills/angular-developer/references/di-fundamentals.md +120 -120
  111. package/seed/marketplaces/ecc/skills/angular-developer/references/e2e-testing.md +56 -56
  112. package/seed/marketplaces/ecc/skills/angular-developer/references/effects.md +83 -83
  113. package/seed/marketplaces/ecc/skills/angular-developer/references/hierarchical-injectors.md +43 -43
  114. package/seed/marketplaces/ecc/skills/angular-developer/references/host-elements.md +80 -80
  115. package/seed/marketplaces/ecc/skills/angular-developer/references/injection-context.md +63 -63
  116. package/seed/marketplaces/ecc/skills/angular-developer/references/inputs.md +101 -101
  117. package/seed/marketplaces/ecc/skills/angular-developer/references/linked-signal.md +59 -59
  118. package/seed/marketplaces/ecc/skills/angular-developer/references/loading-strategies.md +61 -61
  119. package/seed/marketplaces/ecc/skills/angular-developer/references/mcp.md +108 -108
  120. package/seed/marketplaces/ecc/skills/angular-developer/references/navigate-to-routes.md +69 -69
  121. package/seed/marketplaces/ecc/skills/angular-developer/references/outputs.md +86 -86
  122. package/seed/marketplaces/ecc/skills/angular-developer/references/reactive-forms.md +122 -122
  123. package/seed/marketplaces/ecc/skills/angular-developer/references/rendering-strategies.md +44 -44
  124. package/seed/marketplaces/ecc/skills/angular-developer/references/resource.md +77 -77
  125. package/seed/marketplaces/ecc/skills/angular-developer/references/route-animations.md +56 -56
  126. package/seed/marketplaces/ecc/skills/angular-developer/references/route-guards.md +52 -52
  127. package/seed/marketplaces/ecc/skills/angular-developer/references/router-lifecycle.md +45 -45
  128. package/seed/marketplaces/ecc/skills/angular-developer/references/router-testing.md +87 -87
  129. package/seed/marketplaces/ecc/skills/angular-developer/references/show-routes-with-outlets.md +68 -68
  130. package/seed/marketplaces/ecc/skills/angular-developer/references/signal-forms.md +795 -795
  131. package/seed/marketplaces/ecc/skills/angular-developer/references/signals-overview.md +94 -94
  132. package/seed/marketplaces/ecc/skills/angular-developer/references/tailwind-css.md +69 -69
  133. package/seed/marketplaces/ecc/skills/angular-developer/references/template-driven-forms.md +114 -114
  134. package/seed/marketplaces/ecc/skills/angular-developer/references/testing-fundamentals.md +65 -65
  135. package/seed/marketplaces/ecc/skills/api-connector-builder/SKILL.md +120 -120
  136. package/seed/marketplaces/ecc/skills/api-design/SKILL.md +523 -523
  137. package/seed/marketplaces/ecc/skills/architecture-decision-records/SKILL.md +179 -179
  138. package/seed/marketplaces/ecc/skills/article-writing/SKILL.md +79 -79
  139. package/seed/marketplaces/ecc/skills/automation-audit-ops/SKILL.md +142 -142
  140. package/seed/marketplaces/ecc/skills/autonomous-agent-harness/SKILL.md +273 -273
  141. package/seed/marketplaces/ecc/skills/autonomous-loops/SKILL.md +610 -610
  142. package/seed/marketplaces/ecc/skills/backend-patterns/SKILL.md +561 -561
  143. package/seed/marketplaces/ecc/skills/benchmark/SKILL.md +93 -93
  144. package/seed/marketplaces/ecc/skills/benchmark-optimization-loop/SKILL.md +69 -69
  145. package/seed/marketplaces/ecc/skills/blender-motion-state-inspection/SKILL.md +164 -164
  146. package/seed/marketplaces/ecc/skills/blueprint/SKILL.md +105 -105
  147. package/seed/marketplaces/ecc/skills/brand-voice/SKILL.md +97 -97
  148. package/seed/marketplaces/ecc/skills/brand-voice/references/voice-profile-schema.md +55 -55
  149. package/seed/marketplaces/ecc/skills/browser-qa/SKILL.md +87 -87
  150. package/seed/marketplaces/ecc/skills/bun-runtime/SKILL.md +84 -84
  151. package/seed/marketplaces/ecc/skills/canary-watch/SKILL.md +107 -107
  152. package/seed/marketplaces/ecc/skills/carrier-relationship-management/SKILL.md +212 -212
  153. package/seed/marketplaces/ecc/skills/cisco-ios-patterns/SKILL.md +163 -163
  154. package/seed/marketplaces/ecc/skills/ck/SKILL.md +147 -147
  155. package/seed/marketplaces/ecc/skills/ck/commands/forget.mjs +44 -44
  156. package/seed/marketplaces/ecc/skills/ck/commands/info.mjs +24 -24
  157. package/seed/marketplaces/ecc/skills/ck/commands/init.mjs +143 -143
  158. package/seed/marketplaces/ecc/skills/ck/commands/list.mjs +40 -40
  159. package/seed/marketplaces/ecc/skills/ck/commands/migrate.mjs +202 -202
  160. package/seed/marketplaces/ecc/skills/ck/commands/resume.mjs +36 -36
  161. package/seed/marketplaces/ecc/skills/ck/commands/save.mjs +210 -210
  162. package/seed/marketplaces/ecc/skills/ck/commands/shared.mjs +387 -387
  163. package/seed/marketplaces/ecc/skills/ck/hooks/session-start.mjs +224 -224
  164. package/seed/marketplaces/ecc/skills/claude-devfleet/SKILL.md +103 -103
  165. package/seed/marketplaces/ecc/skills/click-path-audit/SKILL.md +244 -244
  166. package/seed/marketplaces/ecc/skills/clickhouse-io/SKILL.md +439 -439
  167. package/seed/marketplaces/ecc/skills/code-tour/SKILL.md +236 -236
  168. package/seed/marketplaces/ecc/skills/codebase-onboarding/SKILL.md +233 -233
  169. package/seed/marketplaces/ecc/skills/coding-standards/SKILL.md +549 -549
  170. package/seed/marketplaces/ecc/skills/compose-multiplatform-patterns/SKILL.md +299 -299
  171. package/seed/marketplaces/ecc/skills/configure-ecc/SKILL.md +384 -384
  172. package/seed/marketplaces/ecc/skills/connections-optimizer/SKILL.md +189 -189
  173. package/seed/marketplaces/ecc/skills/content-engine/SKILL.md +131 -131
  174. package/seed/marketplaces/ecc/skills/content-hash-cache-pattern/SKILL.md +161 -161
  175. package/seed/marketplaces/ecc/skills/context-budget/SKILL.md +135 -135
  176. package/seed/marketplaces/ecc/skills/continuous-agent-loop/SKILL.md +45 -45
  177. package/seed/marketplaces/ecc/skills/continuous-learning/SKILL.md +131 -131
  178. package/seed/marketplaces/ecc/skills/continuous-learning/config.json +18 -18
  179. package/seed/marketplaces/ecc/skills/continuous-learning/evaluate-session.sh +69 -69
  180. package/seed/marketplaces/ecc/skills/continuous-learning-v2/SKILL.md +360 -360
  181. package/seed/marketplaces/ecc/skills/continuous-learning-v2/agents/observer-loop.sh +322 -322
  182. package/seed/marketplaces/ecc/skills/continuous-learning-v2/agents/observer.md +198 -198
  183. package/seed/marketplaces/ecc/skills/continuous-learning-v2/agents/session-guardian.sh +150 -150
  184. package/seed/marketplaces/ecc/skills/continuous-learning-v2/agents/start-observer.sh +248 -248
  185. package/seed/marketplaces/ecc/skills/continuous-learning-v2/config.json +8 -8
  186. package/seed/marketplaces/ecc/skills/continuous-learning-v2/hooks/observe.sh +498 -498
  187. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/detect-project.sh +322 -322
  188. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/instinct-cli.py +1826 -1826
  189. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/lib/homunculus-dir.sh +31 -31
  190. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/migrate-homunculus.sh +62 -62
  191. package/seed/marketplaces/ecc/skills/continuous-learning-v2/scripts/test_parse_instinct.py +1018 -1018
  192. package/seed/marketplaces/ecc/skills/cost-aware-llm-pipeline/SKILL.md +183 -183
  193. package/seed/marketplaces/ecc/skills/cost-tracking/SKILL.md +147 -147
  194. package/seed/marketplaces/ecc/skills/council/SKILL.md +203 -203
  195. package/seed/marketplaces/ecc/skills/cpp-coding-standards/SKILL.md +723 -723
  196. package/seed/marketplaces/ecc/skills/cpp-testing/SKILL.md +324 -324
  197. package/seed/marketplaces/ecc/skills/crosspost/SKILL.md +111 -111
  198. package/seed/marketplaces/ecc/skills/csharp-testing/SKILL.md +321 -321
  199. package/seed/marketplaces/ecc/skills/customer-billing-ops/SKILL.md +140 -140
  200. package/seed/marketplaces/ecc/skills/customs-trade-compliance/SKILL.md +263 -263
  201. package/seed/marketplaces/ecc/skills/dart-flutter-patterns/SKILL.md +563 -563
  202. package/seed/marketplaces/ecc/skills/dashboard-builder/SKILL.md +108 -108
  203. package/seed/marketplaces/ecc/skills/data-scraper-agent/SKILL.md +764 -764
  204. package/seed/marketplaces/ecc/skills/data-throughput-accelerator/SKILL.md +72 -72
  205. package/seed/marketplaces/ecc/skills/database-migrations/SKILL.md +429 -429
  206. package/seed/marketplaces/ecc/skills/deep-research/SKILL.md +159 -159
  207. package/seed/marketplaces/ecc/skills/defi-amm-security/SKILL.md +166 -166
  208. package/seed/marketplaces/ecc/skills/deployment-patterns/SKILL.md +427 -427
  209. package/seed/marketplaces/ecc/skills/design-system/SKILL.md +82 -82
  210. package/seed/marketplaces/ecc/skills/django-celery/SKILL.md +457 -457
  211. package/seed/marketplaces/ecc/skills/django-patterns/SKILL.md +734 -734
  212. package/seed/marketplaces/ecc/skills/django-security/SKILL.md +593 -593
  213. package/seed/marketplaces/ecc/skills/django-tdd/SKILL.md +729 -729
  214. package/seed/marketplaces/ecc/skills/django-verification/SKILL.md +469 -469
  215. package/seed/marketplaces/ecc/skills/dmux-workflows/SKILL.md +191 -191
  216. package/seed/marketplaces/ecc/skills/docker-patterns/SKILL.md +364 -364
  217. package/seed/marketplaces/ecc/skills/documentation-lookup/SKILL.md +90 -90
  218. package/seed/marketplaces/ecc/skills/dotnet-patterns/SKILL.md +321 -321
  219. package/seed/marketplaces/ecc/skills/e2e-testing/SKILL.md +326 -326
  220. package/seed/marketplaces/ecc/skills/ecc-guide/SKILL.md +189 -189
  221. package/seed/marketplaces/ecc/skills/ecc-tools-cost-audit/SKILL.md +160 -160
  222. package/seed/marketplaces/ecc/skills/email-ops/SKILL.md +121 -121
  223. package/seed/marketplaces/ecc/skills/energy-procurement/SKILL.md +228 -228
  224. package/seed/marketplaces/ecc/skills/enterprise-agent-ops/SKILL.md +50 -50
  225. package/seed/marketplaces/ecc/skills/error-handling/SKILL.md +376 -376
  226. package/seed/marketplaces/ecc/skills/eval-harness/SKILL.md +270 -270
  227. package/seed/marketplaces/ecc/skills/evm-token-decimals/SKILL.md +130 -130
  228. package/seed/marketplaces/ecc/skills/exa-search/SKILL.md +107 -107
  229. package/seed/marketplaces/ecc/skills/fal-ai-media/SKILL.md +288 -288
  230. package/seed/marketplaces/ecc/skills/fastapi-patterns/SKILL.md +327 -327
  231. package/seed/marketplaces/ecc/skills/finance-billing-ops/SKILL.md +127 -127
  232. package/seed/marketplaces/ecc/skills/flox-environments/SKILL.md +496 -496
  233. package/seed/marketplaces/ecc/skills/flutter-dart-code-review/SKILL.md +435 -435
  234. package/seed/marketplaces/ecc/skills/foundation-models-on-device/SKILL.md +243 -243
  235. package/seed/marketplaces/ecc/skills/frontend-a11y/SKILL.md +446 -446
  236. package/seed/marketplaces/ecc/skills/frontend-design-direction/SKILL.md +92 -92
  237. package/seed/marketplaces/ecc/skills/frontend-patterns/SKILL.md +642 -642
  238. package/seed/marketplaces/ecc/skills/frontend-slides/SKILL.md +184 -184
  239. package/seed/marketplaces/ecc/skills/frontend-slides/STYLE_PRESETS.md +330 -330
  240. package/seed/marketplaces/ecc/skills/frontend-slides/animation-patterns.md +122 -122
  241. package/seed/marketplaces/ecc/skills/frontend-slides/html-template.md +419 -419
  242. package/seed/marketplaces/ecc/skills/frontend-slides/scripts/export-pdf.sh +418 -418
  243. package/seed/marketplaces/ecc/skills/frontend-slides/scripts/extract-pptx.py +96 -96
  244. package/seed/marketplaces/ecc/skills/frontend-slides/viewport-base.css +153 -153
  245. package/seed/marketplaces/ecc/skills/fsharp-testing/SKILL.md +280 -280
  246. package/seed/marketplaces/ecc/skills/gan-style-harness/SKILL.md +278 -278
  247. package/seed/marketplaces/ecc/skills/gateguard/SKILL.md +125 -125
  248. package/seed/marketplaces/ecc/skills/git-workflow/SKILL.md +715 -715
  249. package/seed/marketplaces/ecc/skills/github-ops/SKILL.md +144 -144
  250. package/seed/marketplaces/ecc/skills/golang-patterns/SKILL.md +674 -674
  251. package/seed/marketplaces/ecc/skills/golang-testing/SKILL.md +720 -720
  252. package/seed/marketplaces/ecc/skills/google-workspace-ops/SKILL.md +95 -95
  253. package/seed/marketplaces/ecc/skills/healthcare-cdss-patterns/SKILL.md +245 -245
  254. package/seed/marketplaces/ecc/skills/healthcare-emr-patterns/SKILL.md +159 -159
  255. package/seed/marketplaces/ecc/skills/healthcare-eval-harness/SKILL.md +207 -207
  256. package/seed/marketplaces/ecc/skills/healthcare-phi-compliance/SKILL.md +145 -145
  257. package/seed/marketplaces/ecc/skills/hermes-imports/SKILL.md +88 -88
  258. package/seed/marketplaces/ecc/skills/hexagonal-architecture/SKILL.md +276 -276
  259. package/seed/marketplaces/ecc/skills/hipaa-compliance/SKILL.md +78 -78
  260. package/seed/marketplaces/ecc/skills/homelab-network-readiness/SKILL.md +169 -169
  261. package/seed/marketplaces/ecc/skills/homelab-network-setup/SKILL.md +129 -129
  262. package/seed/marketplaces/ecc/skills/homelab-pihole-dns/SKILL.md +274 -274
  263. package/seed/marketplaces/ecc/skills/homelab-vlan-segmentation/SKILL.md +311 -311
  264. package/seed/marketplaces/ecc/skills/homelab-wireguard-vpn/SKILL.md +305 -305
  265. package/seed/marketplaces/ecc/skills/hookify-rules/SKILL.md +128 -128
  266. package/seed/marketplaces/ecc/skills/inventory-demand-planning/SKILL.md +247 -247
  267. package/seed/marketplaces/ecc/skills/investor-materials/SKILL.md +96 -96
  268. package/seed/marketplaces/ecc/skills/investor-outreach/SKILL.md +91 -91
  269. package/seed/marketplaces/ecc/skills/ios-icon-gen/SKILL.md +157 -157
  270. package/seed/marketplaces/ecc/skills/ios-icon-gen/scripts/generate_icons.swift +258 -258
  271. package/seed/marketplaces/ecc/skills/ios-icon-gen/scripts/iconify_gen.sh +235 -235
  272. package/seed/marketplaces/ecc/skills/iterative-retrieval/SKILL.md +211 -211
  273. package/seed/marketplaces/ecc/skills/ito-basket-compare/SKILL.md +63 -63
  274. package/seed/marketplaces/ecc/skills/ito-data-atlas-agent/SKILL.md +63 -63
  275. package/seed/marketplaces/ecc/skills/ito-market-intelligence/SKILL.md +60 -60
  276. package/seed/marketplaces/ecc/skills/ito-trade-planner/SKILL.md +67 -67
  277. package/seed/marketplaces/ecc/skills/java-coding-standards/SKILL.md +383 -383
  278. package/seed/marketplaces/ecc/skills/jira-integration/SKILL.md +293 -293
  279. package/seed/marketplaces/ecc/skills/jpa-patterns/SKILL.md +151 -151
  280. package/seed/marketplaces/ecc/skills/knowledge-ops/SKILL.md +154 -154
  281. package/seed/marketplaces/ecc/skills/kotlin-coroutines-flows/SKILL.md +284 -284
  282. package/seed/marketplaces/ecc/skills/kotlin-exposed-patterns/SKILL.md +719 -719
  283. package/seed/marketplaces/ecc/skills/kotlin-ktor-patterns/SKILL.md +689 -689
  284. package/seed/marketplaces/ecc/skills/kotlin-patterns/SKILL.md +711 -711
  285. package/seed/marketplaces/ecc/skills/kotlin-testing/SKILL.md +824 -824
  286. package/seed/marketplaces/ecc/skills/laravel-patterns/SKILL.md +415 -415
  287. package/seed/marketplaces/ecc/skills/laravel-plugin-discovery/SKILL.md +229 -229
  288. package/seed/marketplaces/ecc/skills/laravel-security/SKILL.md +285 -285
  289. package/seed/marketplaces/ecc/skills/laravel-tdd/SKILL.md +283 -283
  290. package/seed/marketplaces/ecc/skills/laravel-verification/SKILL.md +179 -179
  291. package/seed/marketplaces/ecc/skills/latency-critical-systems/SKILL.md +73 -73
  292. package/seed/marketplaces/ecc/skills/lead-intelligence/SKILL.md +321 -321
  293. package/seed/marketplaces/ecc/skills/lead-intelligence/agents/enrichment-agent.md +85 -85
  294. package/seed/marketplaces/ecc/skills/lead-intelligence/agents/mutual-mapper.md +75 -75
  295. package/seed/marketplaces/ecc/skills/lead-intelligence/agents/outreach-drafter.md +98 -98
  296. package/seed/marketplaces/ecc/skills/lead-intelligence/agents/signal-scorer.md +60 -60
  297. package/seed/marketplaces/ecc/skills/liquid-glass-design/SKILL.md +279 -279
  298. package/seed/marketplaces/ecc/skills/llm-trading-agent-security/SKILL.md +146 -146
  299. package/seed/marketplaces/ecc/skills/logistics-exception-management/SKILL.md +222 -222
  300. package/seed/marketplaces/ecc/skills/make-interfaces-feel-better/SKILL.md +151 -151
  301. package/seed/marketplaces/ecc/skills/manim-video/SKILL.md +89 -89
  302. package/seed/marketplaces/ecc/skills/manim-video/assets/network_graph_scene.py +52 -52
  303. package/seed/marketplaces/ecc/skills/market-research/SKILL.md +75 -75
  304. package/seed/marketplaces/ecc/skills/marketing-campaign/SKILL.md +113 -113
  305. package/seed/marketplaces/ecc/skills/mcp-server-patterns/SKILL.md +69 -69
  306. package/seed/marketplaces/ecc/skills/messages-ops/SKILL.md +104 -104
  307. package/seed/marketplaces/ecc/skills/mle-workflow/SKILL.md +346 -346
  308. package/seed/marketplaces/ecc/skills/motion-advanced/SKILL.md +596 -596
  309. package/seed/marketplaces/ecc/skills/motion-foundations/SKILL.md +299 -299
  310. package/seed/marketplaces/ecc/skills/motion-patterns/SKILL.md +435 -435
  311. package/seed/marketplaces/ecc/skills/motion-ui/SKILL.md +575 -575
  312. package/seed/marketplaces/ecc/skills/mysql-patterns/SKILL.md +412 -412
  313. package/seed/marketplaces/ecc/skills/nanoclaw-repl/SKILL.md +33 -33
  314. package/seed/marketplaces/ecc/skills/nestjs-patterns/SKILL.md +230 -230
  315. package/seed/marketplaces/ecc/skills/netmiko-ssh-automation/SKILL.md +173 -173
  316. package/seed/marketplaces/ecc/skills/network-bgp-diagnostics/SKILL.md +167 -167
  317. package/seed/marketplaces/ecc/skills/network-config-validation/SKILL.md +210 -210
  318. package/seed/marketplaces/ecc/skills/network-interface-health/SKILL.md +152 -152
  319. package/seed/marketplaces/ecc/skills/nextjs-turbopack/SKILL.md +57 -57
  320. package/seed/marketplaces/ecc/skills/nodejs-keccak256/SKILL.md +102 -102
  321. package/seed/marketplaces/ecc/skills/nutrient-document-processing/SKILL.md +167 -167
  322. package/seed/marketplaces/ecc/skills/nuxt4-patterns/SKILL.md +100 -100
  323. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/SKILL.md +288 -288
  324. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/gacha.py +224 -224
  325. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/gacha.sh +5 -5
  326. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/avatar-style.md +124 -124
  327. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/boundary-rules.md +53 -53
  328. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/error-handling.md +53 -53
  329. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/identity-tension.md +48 -48
  330. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/naming-system.md +39 -39
  331. package/seed/marketplaces/ecc/skills/openclaw-persona-forge/references/output-template.md +166 -166
  332. package/seed/marketplaces/ecc/skills/opensource-pipeline/SKILL.md +255 -255
  333. package/seed/marketplaces/ecc/skills/parallel-execution-optimizer/SKILL.md +72 -72
  334. package/seed/marketplaces/ecc/skills/perl-patterns/SKILL.md +504 -504
  335. package/seed/marketplaces/ecc/skills/perl-security/SKILL.md +503 -503
  336. package/seed/marketplaces/ecc/skills/perl-testing/SKILL.md +475 -475
  337. package/seed/marketplaces/ecc/skills/plan-orchestrate/SKILL.md +262 -262
  338. package/seed/marketplaces/ecc/skills/plankton-code-quality/SKILL.md +236 -236
  339. package/seed/marketplaces/ecc/skills/postgres-patterns/SKILL.md +147 -147
  340. package/seed/marketplaces/ecc/skills/prediction-market-oracle-research/SKILL.md +63 -63
  341. package/seed/marketplaces/ecc/skills/prediction-market-risk-review/SKILL.md +60 -60
  342. package/seed/marketplaces/ecc/skills/prisma-patterns/SKILL.md +371 -371
  343. package/seed/marketplaces/ecc/skills/product-capability/SKILL.md +141 -141
  344. package/seed/marketplaces/ecc/skills/product-lens/SKILL.md +92 -92
  345. package/seed/marketplaces/ecc/skills/production-audit/SKILL.md +206 -206
  346. package/seed/marketplaces/ecc/skills/production-scheduling/SKILL.md +238 -238
  347. package/seed/marketplaces/ecc/skills/project-flow-ops/SKILL.md +111 -111
  348. package/seed/marketplaces/ecc/skills/prompt-optimizer/SKILL.md +398 -398
  349. package/seed/marketplaces/ecc/skills/python-patterns/SKILL.md +750 -750
  350. package/seed/marketplaces/ecc/skills/python-testing/SKILL.md +816 -816
  351. package/seed/marketplaces/ecc/skills/pytorch-patterns/SKILL.md +396 -396
  352. package/seed/marketplaces/ecc/skills/quality-nonconformance/SKILL.md +260 -260
  353. package/seed/marketplaces/ecc/skills/quarkus-patterns/SKILL.md +722 -722
  354. package/seed/marketplaces/ecc/skills/quarkus-security/SKILL.md +467 -467
  355. package/seed/marketplaces/ecc/skills/quarkus-tdd/SKILL.md +811 -811
  356. package/seed/marketplaces/ecc/skills/quarkus-verification/SKILL.md +479 -479
  357. package/seed/marketplaces/ecc/skills/ralphinho-rfc-pipeline/SKILL.md +67 -67
  358. package/seed/marketplaces/ecc/skills/recsys-pipeline-architect/SKILL.md +114 -114
  359. package/seed/marketplaces/ecc/skills/recursive-decision-ledger/SKILL.md +79 -79
  360. package/seed/marketplaces/ecc/skills/redis-patterns/SKILL.md +403 -403
  361. package/seed/marketplaces/ecc/skills/regex-vs-llm-structured-text/SKILL.md +220 -220
  362. package/seed/marketplaces/ecc/skills/remotion-video-creation/SKILL.md +43 -43
  363. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/3d.md +86 -86
  364. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/animations.md +29 -29
  365. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/assets/charts-bar-chart.tsx +173 -173
  366. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/assets/text-animations-typewriter.tsx +100 -100
  367. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/assets/text-animations-word-highlight.tsx +108 -108
  368. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/assets.md +78 -78
  369. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/audio.md +172 -172
  370. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/calculate-metadata.md +104 -104
  371. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/can-decode.md +75 -75
  372. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/charts.md +58 -58
  373. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/compositions.md +146 -146
  374. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/display-captions.md +126 -126
  375. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/extract-frames.md +229 -229
  376. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/fonts.md +152 -152
  377. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/get-audio-duration.md +58 -58
  378. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/get-video-dimensions.md +68 -68
  379. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/get-video-duration.md +58 -58
  380. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/gifs.md +138 -138
  381. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/images.md +130 -130
  382. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/import-srt-captions.md +67 -67
  383. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/lottie.md +67 -67
  384. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/measuring-dom-nodes.md +34 -34
  385. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/measuring-text.md +143 -143
  386. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/sequencing.md +106 -106
  387. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/tailwind.md +11 -11
  388. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/text-animations.md +20 -20
  389. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/timing.md +179 -179
  390. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/transcribe-captions.md +19 -19
  391. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/transitions.md +122 -122
  392. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/trimming.md +52 -52
  393. package/seed/marketplaces/ecc/skills/remotion-video-creation/rules/videos.md +171 -171
  394. package/seed/marketplaces/ecc/skills/repo-scan/SKILL.md +78 -78
  395. package/seed/marketplaces/ecc/skills/research-ops/SKILL.md +112 -112
  396. package/seed/marketplaces/ecc/skills/returns-reverse-logistics/SKILL.md +240 -240
  397. package/seed/marketplaces/ecc/skills/rules-distill/SKILL.md +264 -264
  398. package/seed/marketplaces/ecc/skills/rules-distill/scripts/scan-rules.sh +58 -58
  399. package/seed/marketplaces/ecc/skills/rules-distill/scripts/scan-skills.sh +129 -129
  400. package/seed/marketplaces/ecc/skills/rust-patterns/SKILL.md +499 -499
  401. package/seed/marketplaces/ecc/skills/rust-testing/SKILL.md +500 -500
  402. package/seed/marketplaces/ecc/skills/safety-guard/SKILL.md +75 -75
  403. package/seed/marketplaces/ecc/skills/santa-method/SKILL.md +306 -306
  404. package/seed/marketplaces/ecc/skills/scientific-db-pubmed-database/SKILL.md +175 -175
  405. package/seed/marketplaces/ecc/skills/scientific-db-uspto-database/SKILL.md +177 -177
  406. package/seed/marketplaces/ecc/skills/scientific-pkg-gget/SKILL.md +166 -166
  407. package/seed/marketplaces/ecc/skills/scientific-thinking-literature-review/SKILL.md +192 -192
  408. package/seed/marketplaces/ecc/skills/scientific-thinking-scholar-evaluation/SKILL.md +160 -160
  409. package/seed/marketplaces/ecc/skills/search-first/SKILL.md +182 -182
  410. package/seed/marketplaces/ecc/skills/security-bounty-hunter/SKILL.md +99 -99
  411. package/seed/marketplaces/ecc/skills/security-review/SKILL.md +503 -503
  412. package/seed/marketplaces/ecc/skills/security-review/cloud-infrastructure-security.md +361 -361
  413. package/seed/marketplaces/ecc/skills/security-scan/SKILL.md +165 -165
  414. package/seed/marketplaces/ecc/skills/seo/SKILL.md +154 -154
  415. package/seed/marketplaces/ecc/skills/skill-comply/SKILL.md +58 -58
  416. package/seed/marketplaces/ecc/skills/skill-comply/fixtures/compliant_trace.jsonl +5 -5
  417. package/seed/marketplaces/ecc/skills/skill-comply/fixtures/noncompliant_trace.jsonl +3 -3
  418. package/seed/marketplaces/ecc/skills/skill-comply/fixtures/tdd_spec.yaml +44 -44
  419. package/seed/marketplaces/ecc/skills/skill-comply/prompts/classifier.md +24 -24
  420. package/seed/marketplaces/ecc/skills/skill-comply/prompts/scenario_generator.md +62 -62
  421. package/seed/marketplaces/ecc/skills/skill-comply/prompts/spec_generator.md +42 -42
  422. package/seed/marketplaces/ecc/skills/skill-comply/pyproject.toml +15 -15
  423. package/seed/marketplaces/ecc/skills/skill-comply/scripts/classifier.py +85 -85
  424. package/seed/marketplaces/ecc/skills/skill-comply/scripts/grader.py +124 -124
  425. package/seed/marketplaces/ecc/skills/skill-comply/scripts/parser.py +107 -107
  426. package/seed/marketplaces/ecc/skills/skill-comply/scripts/report.py +170 -170
  427. package/seed/marketplaces/ecc/skills/skill-comply/scripts/run.py +127 -127
  428. package/seed/marketplaces/ecc/skills/skill-comply/scripts/runner.py +186 -186
  429. package/seed/marketplaces/ecc/skills/skill-comply/scripts/scenario_generator.py +70 -70
  430. package/seed/marketplaces/ecc/skills/skill-comply/scripts/spec_generator.py +72 -72
  431. package/seed/marketplaces/ecc/skills/skill-comply/scripts/utils.py +13 -13
  432. package/seed/marketplaces/ecc/skills/skill-comply/tests/test_grader.py +197 -197
  433. package/seed/marketplaces/ecc/skills/skill-comply/tests/test_parser.py +90 -90
  434. package/seed/marketplaces/ecc/skills/skill-comply/tests/test_runner.py +172 -172
  435. package/seed/marketplaces/ecc/skills/skill-scout/SKILL.md +140 -140
  436. package/seed/marketplaces/ecc/skills/skill-stocktake/SKILL.md +194 -194
  437. package/seed/marketplaces/ecc/skills/skill-stocktake/scripts/quick-diff.sh +87 -87
  438. package/seed/marketplaces/ecc/skills/skill-stocktake/scripts/save-results.sh +56 -56
  439. package/seed/marketplaces/ecc/skills/skill-stocktake/scripts/scan.sh +170 -170
  440. package/seed/marketplaces/ecc/skills/social-graph-ranker/SKILL.md +154 -154
  441. package/seed/marketplaces/ecc/skills/social-publisher/SKILL.md +115 -115
  442. package/seed/marketplaces/ecc/skills/springboot-patterns/SKILL.md +314 -314
  443. package/seed/marketplaces/ecc/skills/springboot-security/SKILL.md +272 -272
  444. package/seed/marketplaces/ecc/skills/springboot-tdd/SKILL.md +158 -158
  445. package/seed/marketplaces/ecc/skills/springboot-verification/SKILL.md +231 -231
  446. package/seed/marketplaces/ecc/skills/strategic-compact/SKILL.md +131 -131
  447. package/seed/marketplaces/ecc/skills/swift-actor-persistence/SKILL.md +143 -143
  448. package/seed/marketplaces/ecc/skills/swift-concurrency-6-2/SKILL.md +216 -216
  449. package/seed/marketplaces/ecc/skills/swift-protocol-di-testing/SKILL.md +190 -190
  450. package/seed/marketplaces/ecc/skills/swiftui-patterns/SKILL.md +259 -259
  451. package/seed/marketplaces/ecc/skills/tdd-workflow/SKILL.md +463 -463
  452. package/seed/marketplaces/ecc/skills/team-builder/SKILL.md +168 -168
  453. package/seed/marketplaces/ecc/skills/terminal-ops/SKILL.md +109 -109
  454. package/seed/marketplaces/ecc/skills/tinystruct-patterns/SKILL.md +203 -203
  455. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/architecture.md +90 -90
  456. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/data-handling.md +60 -60
  457. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/database.md +99 -99
  458. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/routing.md +64 -64
  459. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/system-usage.md +97 -97
  460. package/seed/marketplaces/ecc/skills/tinystruct-patterns/references/testing.md +72 -72
  461. package/seed/marketplaces/ecc/skills/token-budget-advisor/SKILL.md +133 -133
  462. package/seed/marketplaces/ecc/skills/ui-demo/SKILL.md +465 -465
  463. package/seed/marketplaces/ecc/skills/ui-to-vue/SKILL.md +134 -134
  464. package/seed/marketplaces/ecc/skills/uncloud/SKILL.md +343 -343
  465. package/seed/marketplaces/ecc/skills/unified-notifications-ops/SKILL.md +187 -187
  466. package/seed/marketplaces/ecc/skills/verification-loop/SKILL.md +126 -126
  467. package/seed/marketplaces/ecc/skills/video-editing/SKILL.md +310 -310
  468. package/seed/marketplaces/ecc/skills/videodb/SKILL.md +374 -374
  469. package/seed/marketplaces/ecc/skills/videodb/reference/api-reference.md +550 -550
  470. package/seed/marketplaces/ecc/skills/videodb/reference/capture-reference.md +407 -407
  471. package/seed/marketplaces/ecc/skills/videodb/reference/capture.md +101 -101
  472. package/seed/marketplaces/ecc/skills/videodb/reference/editor.md +443 -443
  473. package/seed/marketplaces/ecc/skills/videodb/reference/generative.md +331 -331
  474. package/seed/marketplaces/ecc/skills/videodb/reference/rtstream-reference.md +564 -564
  475. package/seed/marketplaces/ecc/skills/videodb/reference/rtstream.md +65 -65
  476. package/seed/marketplaces/ecc/skills/videodb/reference/search.md +230 -230
  477. package/seed/marketplaces/ecc/skills/videodb/reference/streaming.md +406 -406
  478. package/seed/marketplaces/ecc/skills/videodb/reference/use-cases.md +118 -118
  479. package/seed/marketplaces/ecc/skills/videodb/scripts/ws_listener.py +282 -282
  480. package/seed/marketplaces/ecc/skills/visa-doc-translate/README.md +86 -86
  481. package/seed/marketplaces/ecc/skills/visa-doc-translate/SKILL.md +117 -117
  482. package/seed/marketplaces/ecc/skills/vite-patterns/SKILL.md +449 -449
  483. package/seed/marketplaces/ecc/skills/windows-desktop-e2e/SKILL.md +887 -887
  484. package/seed/marketplaces/ecc/skills/workspace-surface-audit/SKILL.md +125 -125
  485. package/seed/marketplaces/ecc/skills/x-api/SKILL.md +234 -234
  486. package/seed/marketplaces/ecc/.claude/commands/add-language-rules.md +0 -39
  487. package/seed/marketplaces/ecc/.claude/commands/database-migration.md +0 -36
  488. package/seed/marketplaces/ecc/.claude/commands/feature-development.md +0 -38
  489. package/seed/marketplaces/ecc/.claude/ecc-tools.json +0 -334
  490. package/seed/marketplaces/ecc/.claude/enterprise/controls.md +0 -15
  491. package/seed/marketplaces/ecc/.claude/homunculus/instincts/inherited/everything-claude-code-instincts.yaml +0 -162
  492. package/seed/marketplaces/ecc/.claude/identity.json +0 -14
  493. package/seed/marketplaces/ecc/.claude/package-manager.json +0 -4
  494. package/seed/marketplaces/ecc/.claude/research/everything-claude-code-research-playbook.md +0 -21
  495. package/seed/marketplaces/ecc/.claude/rules/everything-claude-code-guardrails.md +0 -43
  496. package/seed/marketplaces/ecc/.claude/rules/node.md +0 -56
  497. package/seed/marketplaces/ecc/.claude/skills/everything-claude-code/SKILL.md +0 -442
  498. package/seed/marketplaces/ecc/.claude/team/everything-claude-code-team-config.json +0 -15
  499. package/seed/marketplaces/ecc/.vscode/settings.json +0 -17
@@ -1,498 +1,498 @@
1
- #!/bin/bash
2
- # Continuous Learning v2 - Observation Hook
3
- #
4
- # Captures tool use events for pattern analysis.
5
- # Claude Code passes hook data via stdin as JSON.
6
- #
7
- # v2.1: Project-scoped observations — detects current project context
8
- # and writes observations to project-specific directory.
9
- #
10
- # Registered via plugin hooks/hooks.json (auto-loaded when plugin is enabled).
11
- # Can also be registered manually in ~/.claude/settings.json.
12
-
13
- set -e
14
-
15
- # Hook phase from CLI argument: "pre" (PreToolUse) or "post" (PostToolUse).
16
- # Manual settings.json installs can call this script without the plugin
17
- # wrapper's positional phase argument, but Claude Code still exposes the hook
18
- # event name in CLAUDE_HOOK_EVENT_NAME. Fall back to that env var before
19
- # defaulting to post so manually registered PreToolUse hooks are recorded as
20
- # tool_start instead of being silently misclassified as tool_complete.
21
- HOOK_PHASE="${1:-}"
22
- if [ -z "$HOOK_PHASE" ]; then
23
- case "${CLAUDE_HOOK_EVENT_NAME:-}" in
24
- PreToolUse|pretooluse|pre_tool_use|pre) HOOK_PHASE="pre" ;;
25
- PostToolUse|posttooluse|post_tool_use|post) HOOK_PHASE="post" ;;
26
- *) HOOK_PHASE="post" ;;
27
- esac
28
- fi
29
-
30
- # ─────────────────────────────────────────────
31
- # Read stdin first (before project detection)
32
- # ─────────────────────────────────────────────
33
-
34
- # Read JSON from stdin (Claude Code hook format)
35
- INPUT_JSON=$(cat)
36
-
37
- # Exit if no input
38
- if [ -z "$INPUT_JSON" ]; then
39
- exit 0
40
- fi
41
-
42
- _is_windows_app_installer_stub() {
43
- # Windows 10/11 ships an "App Execution Alias" stub at
44
- # %LOCALAPPDATA%\Microsoft\WindowsApps\python.exe
45
- # %LOCALAPPDATA%\Microsoft\WindowsApps\python3.exe
46
- # Both are symlinks to AppInstallerPythonRedirector.exe which, when Python
47
- # is not installed from the Store, neither launches Python nor honors "-c".
48
- # Calls to it hang or print a bare "Python " line, silently breaking every
49
- # JSON-parsing step in this hook. Detect and skip such stubs here.
50
- local _candidate="$1"
51
- [ -z "$_candidate" ] && return 1
52
- local _resolved
53
- _resolved="$(command -v "$_candidate" 2>/dev/null || true)"
54
- [ -z "$_resolved" ] && return 1
55
- case "$_resolved" in
56
- *AppInstallerPythonRedirector.exe|*AppInstallerPythonRedirector.EXE) return 0 ;;
57
- esac
58
- # Also resolve one level of symlink on POSIX-like shells (Git Bash, WSL).
59
- if command -v readlink >/dev/null 2>&1; then
60
- local _target
61
- _target="$(readlink -f "$_resolved" 2>/dev/null || readlink "$_resolved" 2>/dev/null || true)"
62
- case "$_target" in
63
- *AppInstallerPythonRedirector.exe|*AppInstallerPythonRedirector.EXE) return 0 ;;
64
- esac
65
- fi
66
- return 1
67
- }
68
-
69
- resolve_python_cmd() {
70
- if [ -n "${CLV2_PYTHON_CMD:-}" ] && command -v "$CLV2_PYTHON_CMD" >/dev/null 2>&1; then
71
- printf '%s\n' "$CLV2_PYTHON_CMD"
72
- return 0
73
- fi
74
-
75
- if command -v python3 >/dev/null 2>&1 && ! _is_windows_app_installer_stub python3; then
76
- printf '%s\n' python3
77
- return 0
78
- fi
79
-
80
- if command -v python >/dev/null 2>&1 && ! _is_windows_app_installer_stub python; then
81
- printf '%s\n' python
82
- return 0
83
- fi
84
-
85
- return 1
86
- }
87
-
88
- PYTHON_CMD="$(resolve_python_cmd 2>/dev/null || true)"
89
- if [ -z "$PYTHON_CMD" ]; then
90
- echo "[observe] No python interpreter found, skipping observation" >&2
91
- exit 0
92
- fi
93
-
94
- # Propagate our stub-aware selection so detect-project.sh (which is sourced
95
- # below) does not re-resolve and silently fall back to the App Installer stub.
96
- # detect-project.sh honors an already-set CLV2_PYTHON_CMD.
97
- export CLV2_PYTHON_CMD="${CLV2_PYTHON_CMD:-$PYTHON_CMD}"
98
-
99
- # ─────────────────────────────────────────────
100
- # Extract cwd from stdin for project detection
101
- # ─────────────────────────────────────────────
102
-
103
- # Extract cwd from the hook JSON to use for project detection.
104
- # If cwd is a subdirectory inside a git repo, resolve it to the repo root so
105
- # observations attach to the project instead of a nested path.
106
- STDIN_CWD=$(echo "$INPUT_JSON" | "$PYTHON_CMD" -c '
107
- import json, sys
108
- try:
109
- data = json.load(sys.stdin)
110
- cwd = data.get("cwd", "")
111
- print(cwd)
112
- except(KeyError, TypeError, ValueError):
113
- print("")
114
- ' 2>/dev/null || echo "")
115
-
116
- # If cwd was provided in stdin, use it for project detection
117
- if [ -n "$STDIN_CWD" ] && [ -d "$STDIN_CWD" ]; then
118
- _GIT_ROOT=$(git -C "$STDIN_CWD" rev-parse --show-toplevel 2>/dev/null || true)
119
- if [ -n "$_GIT_ROOT" ]; then
120
- export CLAUDE_PROJECT_DIR="$_GIT_ROOT"
121
- unset CLV2_NO_PROJECT
122
- else
123
- unset CLAUDE_PROJECT_DIR
124
- export CLV2_NO_PROJECT=1
125
- fi
126
- fi
127
-
128
- # ─────────────────────────────────────────────
129
- # Lightweight config and automated session guards
130
- # ─────────────────────────────────────────────
131
- #
132
- # IMPORTANT: keep these guards above detect-project.sh.
133
- # Sourcing detect-project.sh creates project-scoped directories and updates
134
- # projects.json, so automated sessions must return before that point.
135
-
136
- # shellcheck disable=SC1091
137
- . "$(dirname "$0")/../scripts/lib/homunculus-dir.sh"
138
- CONFIG_DIR="$(_ecc_resolve_homunculus_dir)"
139
-
140
- # Skip if disabled (check both default and CLV2_CONFIG-derived locations)
141
- if [ -f "$CONFIG_DIR/disabled" ]; then
142
- exit 0
143
- fi
144
- if [ -n "${CLV2_CONFIG:-}" ] && [ -f "$(dirname "$CLV2_CONFIG")/disabled" ]; then
145
- exit 0
146
- fi
147
-
148
- # Prevent observe.sh from firing on non-human sessions to avoid:
149
- # - ECC observing its own Haiku observer sessions (self-loop)
150
- # - ECC observing other tools' automated sessions
151
- # - automated sessions creating project-scoped homunculus metadata
152
-
153
- # Layer 1: entrypoint. Only interactive terminal sessions should continue.
154
- # sdk-ts: Agent SDK sessions can be human-interactive (e.g. via Happy).
155
- # Non-interactive SDK automation is still filtered by Layers 2-5 below
156
- # (ECC_HOOK_PROFILE=minimal, ECC_SKIP_OBSERVE=1, agent_id, path exclusions).
157
- case "${CLAUDE_CODE_ENTRYPOINT:-cli}" in
158
- cli|sdk-ts|claude-desktop) ;;
159
- *) exit 0 ;;
160
- esac
161
-
162
- # Layer 2: minimal hook profile suppresses non-essential hooks.
163
- [ "${ECC_HOOK_PROFILE:-standard}" = "minimal" ] && exit 0
164
-
165
- # Layer 3: cooperative skip env var for automated sessions.
166
- [ "${ECC_SKIP_OBSERVE:-0}" = "1" ] && exit 0
167
-
168
- # Layer 4: subagent sessions are automated by definition.
169
- _ECC_AGENT_ID=$(echo "$INPUT_JSON" | "$PYTHON_CMD" -c "import json,sys; print(json.load(sys.stdin).get('agent_id',''))" 2>/dev/null || true)
170
- [ -n "$_ECC_AGENT_ID" ] && exit 0
171
-
172
- # Layer 5: known observer-session path exclusions.
173
- _ECC_SKIP_PATHS="${ECC_OBSERVE_SKIP_PATHS:-observer-sessions,.claude-mem}"
174
- if [ -n "$STDIN_CWD" ]; then
175
- IFS=',' read -ra _ECC_SKIP_ARRAY <<< "$_ECC_SKIP_PATHS"
176
- for _pattern in "${_ECC_SKIP_ARRAY[@]}"; do
177
- _pattern="${_pattern#"${_pattern%%[![:space:]]*}"}"
178
- _pattern="${_pattern%"${_pattern##*[![:space:]]}"}"
179
- [ -z "$_pattern" ] && continue
180
- case "$STDIN_CWD" in *"$_pattern"*) exit 0 ;; esac
181
- done
182
- fi
183
-
184
- # ─────────────────────────────────────────────
185
- # Project detection
186
- # ─────────────────────────────────────────────
187
-
188
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
189
- SKILL_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
190
-
191
- # Source shared project detection helper
192
- # This sets: PROJECT_ID, PROJECT_NAME, PROJECT_ROOT, PROJECT_DIR
193
- source "${SKILL_ROOT}/scripts/detect-project.sh"
194
- PYTHON_CMD="${CLV2_PYTHON_CMD:-$PYTHON_CMD}"
195
-
196
- # ─────────────────────────────────────────────
197
- # Configuration
198
- # ─────────────────────────────────────────────
199
-
200
- OBSERVATIONS_FILE="${PROJECT_DIR}/observations.jsonl"
201
- MAX_FILE_SIZE_MB=10
202
-
203
- # Auto-purge observation files older than 30 days (runs once per session)
204
- PURGE_MARKER="${PROJECT_DIR}/.last-purge"
205
- if [ ! -f "$PURGE_MARKER" ] || [ "$(find "$PURGE_MARKER" -mtime +1 2>/dev/null)" ]; then
206
- find "${PROJECT_DIR}" -name "observations-*.jsonl" -mtime +30 -delete 2>/dev/null || true
207
- touch "$PURGE_MARKER" 2>/dev/null || true
208
- fi
209
-
210
- # Parse using Python via stdin pipe (safe for all JSON payloads)
211
- # Pass HOOK_PHASE via env var since Claude Code does not include hook type in stdin JSON
212
- PARSED=$(echo "$INPUT_JSON" | HOOK_PHASE="$HOOK_PHASE" "$PYTHON_CMD" -c '
213
- import json
214
- import sys
215
- import os
216
-
217
- try:
218
- data = json.load(sys.stdin)
219
-
220
- # Determine event type from CLI argument passed via env var.
221
- # Claude Code does NOT include a "hook_type" field in the stdin JSON,
222
- # so we rely on the shell argument ("pre" or "post") instead.
223
- hook_phase = os.environ.get("HOOK_PHASE", "post")
224
- event = "tool_start" if hook_phase == "pre" else "tool_complete"
225
-
226
- # Extract fields - Claude Code hook format
227
- tool_name = data.get("tool_name", data.get("tool", "unknown"))
228
- tool_input = data.get("tool_input", data.get("input", {}))
229
- tool_output = data.get("tool_response")
230
- if tool_output is None:
231
- tool_output = data.get("tool_output", data.get("output", ""))
232
- session_id = data.get("session_id", "unknown")
233
- tool_use_id = data.get("tool_use_id", "")
234
- cwd = data.get("cwd", "")
235
-
236
- # Truncate large inputs/outputs
237
- if isinstance(tool_input, dict):
238
- tool_input_str = json.dumps(tool_input)[:5000]
239
- else:
240
- tool_input_str = str(tool_input)[:5000]
241
-
242
- if isinstance(tool_output, dict):
243
- tool_response_str = json.dumps(tool_output)[:5000]
244
- else:
245
- tool_response_str = str(tool_output)[:5000]
246
-
247
- print(json.dumps({
248
- "parsed": True,
249
- "event": event,
250
- "tool": tool_name,
251
- "input": tool_input_str if event == "tool_start" else None,
252
- "output": tool_response_str if event == "tool_complete" else None,
253
- "session": session_id,
254
- "tool_use_id": tool_use_id,
255
- "cwd": cwd
256
- }))
257
- except Exception as e:
258
- print(json.dumps({"parsed": False, "error": str(e)}))
259
- ')
260
-
261
- # Check if parsing succeeded
262
- PARSED_OK=$(echo "$PARSED" | "$PYTHON_CMD" -c "import json,sys; print(json.load(sys.stdin).get('parsed', False))" 2>/dev/null || echo "False")
263
-
264
- if [ "$PARSED_OK" != "True" ]; then
265
- # Fallback: log raw input for debugging (scrub secrets before persisting)
266
- timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
267
- export TIMESTAMP="$timestamp"
268
- echo "$INPUT_JSON" | "$PYTHON_CMD" -c '
269
- import json, sys, os, re
270
-
271
- _SECRET_RE = re.compile(
272
- r"(?i)(api[_-]?key|token|secret|password|authorization|credentials?|auth)"
273
- r"""(["'"'"'\s:=]+)"""
274
- r"([A-Za-z]+\s+)?"
275
- r"([A-Za-z0-9_\-/.+=]{8,})"
276
- )
277
-
278
- raw = sys.stdin.read()[:2000]
279
- raw = _SECRET_RE.sub(lambda m: m.group(1) + m.group(2) + (m.group(3) or "") + "[REDACTED]", raw)
280
- print(json.dumps({"timestamp": os.environ["TIMESTAMP"], "event": "parse_error", "raw": raw}))
281
- ' >> "$OBSERVATIONS_FILE"
282
- exit 0
283
- fi
284
-
285
- # Archive if file too large (atomic: rename with unique suffix to avoid race)
286
- if [ -f "$OBSERVATIONS_FILE" ]; then
287
- file_size_mb=$(du -m "$OBSERVATIONS_FILE" 2>/dev/null | cut -f1)
288
- if [ "${file_size_mb:-0}" -ge "$MAX_FILE_SIZE_MB" ]; then
289
- archive_dir="${PROJECT_DIR}/observations.archive"
290
- mkdir -p "$archive_dir"
291
- mv "$OBSERVATIONS_FILE" "$archive_dir/observations-$(date +%Y%m%d-%H%M%S)-$$.jsonl" 2>/dev/null || true
292
- fi
293
- fi
294
-
295
- # Build and write observation (now includes project context)
296
- # Scrub common secret patterns from tool I/O before persisting
297
- timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
298
-
299
- export PROJECT_ID_ENV="$PROJECT_ID"
300
- export PROJECT_NAME_ENV="$PROJECT_NAME"
301
- export TIMESTAMP="$timestamp"
302
-
303
- echo "$PARSED" | "$PYTHON_CMD" -c '
304
- import json, sys, os, re
305
-
306
- parsed = json.load(sys.stdin)
307
- observation = {
308
- "timestamp": os.environ["TIMESTAMP"],
309
- "event": parsed["event"],
310
- "tool": parsed["tool"],
311
- "session": parsed["session"],
312
- "project_id": os.environ.get("PROJECT_ID_ENV", "global"),
313
- "project_name": os.environ.get("PROJECT_NAME_ENV", "global")
314
- }
315
-
316
- # Scrub secrets: match common key=value, key: value, and key"value patterns
317
- # Includes optional auth scheme (e.g., "Bearer", "Basic") before token
318
- _SECRET_RE = re.compile(
319
- r"(?i)(api[_-]?key|token|secret|password|authorization|credentials?|auth)"
320
- r"""(["'"'"'\s:=]+)"""
321
- r"([A-Za-z]+\s+)?"
322
- r"([A-Za-z0-9_\-/.+=]{8,})"
323
- )
324
-
325
- def scrub(val):
326
- if val is None:
327
- return None
328
- return _SECRET_RE.sub(lambda m: m.group(1) + m.group(2) + (m.group(3) or "") + "[REDACTED]", str(val))
329
-
330
- if parsed["input"]:
331
- observation["input"] = scrub(parsed["input"])
332
- if parsed["output"] is not None:
333
- observation["output"] = scrub(parsed["output"])
334
-
335
- print(json.dumps(observation))
336
- ' >> "$OBSERVATIONS_FILE"
337
-
338
- # Lazy-start observer if enabled but not running (first-time setup)
339
- # Use flock for atomic check-then-act to prevent race conditions
340
- # Fallback for macOS (no flock): use lockfile or skip
341
- LAZY_START_LOCK="${PROJECT_DIR}/.observer-start.lock"
342
- _REMOVE_FILE_IF_PRESENT() {
343
- local target="$1"
344
- if [ -n "$target" ] && [ -e "$target" ]; then
345
- rm -- "$target" 2>/dev/null || true
346
- fi
347
- }
348
-
349
- _START_OBSERVER_LOGGED() {
350
- local bootstrap_log="${PROJECT_DIR}/observer-start.log"
351
- mkdir -p "$PROJECT_DIR"
352
- "${SKILL_ROOT}/agents/start-observer.sh" start >> "$bootstrap_log" 2>&1 || true
353
- }
354
-
355
- _CHECK_OBSERVER_RUNNING() {
356
- local pid_file="$1"
357
- if [ -f "$pid_file" ]; then
358
- local pid
359
- pid=$(cat "$pid_file" 2>/dev/null)
360
- # Validate PID is a positive integer (>1) to prevent signaling invalid targets
361
- case "$pid" in
362
- ''|*[!0-9]*|0|1)
363
- _REMOVE_FILE_IF_PRESENT "$pid_file"
364
- return 1
365
- ;;
366
- esac
367
- if kill -0 "$pid" 2>/dev/null; then
368
- return 0 # Process is alive
369
- fi
370
- # Stale PID file - remove it
371
- _REMOVE_FILE_IF_PRESENT "$pid_file"
372
- fi
373
- return 1 # No PID file or process dead
374
- }
375
-
376
- if [ -f "${CONFIG_DIR}/disabled" ]; then
377
- OBSERVER_ENABLED=false
378
- else
379
- OBSERVER_ENABLED=false
380
- if [ -n "${CLV2_CONFIG:-}" ]; then
381
- CONFIG_FILE="$CLV2_CONFIG"
382
- elif [ -f "${CONFIG_DIR}/config.json" ]; then
383
- CONFIG_FILE="${CONFIG_DIR}/config.json"
384
- else
385
- CONFIG_FILE="${SKILL_ROOT}/config.json"
386
- fi
387
- # Use effective config path for both existence check and reading
388
- EFFECTIVE_CONFIG="$CONFIG_FILE"
389
- if [ -f "$EFFECTIVE_CONFIG" ] && [ -n "$PYTHON_CMD" ]; then
390
- _enabled=$(CLV2_CONFIG_PATH="$EFFECTIVE_CONFIG" "$PYTHON_CMD" -c "
391
- import json, os
392
- with open(os.environ['CLV2_CONFIG_PATH']) as f:
393
- cfg = json.load(f)
394
- print(str(cfg.get('observer', {}).get('enabled', False)).lower())
395
- " 2>/dev/null || echo "false")
396
- if [ "$_enabled" = "true" ]; then
397
- OBSERVER_ENABLED=true
398
- fi
399
- fi
400
- fi
401
-
402
- # Check both project-scoped AND global PID files (with stale PID recovery)
403
- if [ "$OBSERVER_ENABLED" = "true" ]; then
404
- # Clean up stale PID files first
405
- _CHECK_OBSERVER_RUNNING "${PROJECT_DIR}/.observer.pid" || true
406
- _CHECK_OBSERVER_RUNNING "${CONFIG_DIR}/.observer.pid" || true
407
-
408
- # Check if observer is now running after cleanup
409
- if [ ! -f "${PROJECT_DIR}/.observer.pid" ] && [ ! -f "${CONFIG_DIR}/.observer.pid" ]; then
410
- # Use flock if available (Linux), fallback for macOS
411
- if command -v flock >/dev/null 2>&1; then
412
- (
413
- flock -n 9 || exit 0
414
- # Double-check PID files after acquiring lock
415
- _CHECK_OBSERVER_RUNNING "${PROJECT_DIR}/.observer.pid" || true
416
- _CHECK_OBSERVER_RUNNING "${CONFIG_DIR}/.observer.pid" || true
417
- if [ ! -f "${PROJECT_DIR}/.observer.pid" ] && [ ! -f "${CONFIG_DIR}/.observer.pid" ]; then
418
- _START_OBSERVER_LOGGED
419
- fi
420
- ) 9>"$LAZY_START_LOCK"
421
- else
422
- # macOS fallback: use lockfile if available, otherwise mkdir-based lock
423
- if command -v lockfile >/dev/null 2>&1; then
424
- # Use subshell to isolate exit and add trap for cleanup
425
- (
426
- trap '_REMOVE_FILE_IF_PRESENT "$LAZY_START_LOCK"' EXIT
427
- lockfile -r 1 -l 30 "$LAZY_START_LOCK" 2>/dev/null || exit 0
428
- _CHECK_OBSERVER_RUNNING "${PROJECT_DIR}/.observer.pid" || true
429
- _CHECK_OBSERVER_RUNNING "${CONFIG_DIR}/.observer.pid" || true
430
- if [ ! -f "${PROJECT_DIR}/.observer.pid" ] && [ ! -f "${CONFIG_DIR}/.observer.pid" ]; then
431
- _START_OBSERVER_LOGGED
432
- fi
433
- _REMOVE_FILE_IF_PRESENT "$LAZY_START_LOCK"
434
- )
435
- else
436
- # POSIX fallback: mkdir is atomic -- fails if dir already exists
437
- (
438
- trap 'rmdir "${LAZY_START_LOCK}.d" 2>/dev/null || true' EXIT
439
- mkdir "${LAZY_START_LOCK}.d" 2>/dev/null || exit 0
440
- _CHECK_OBSERVER_RUNNING "${PROJECT_DIR}/.observer.pid" || true
441
- _CHECK_OBSERVER_RUNNING "${CONFIG_DIR}/.observer.pid" || true
442
- if [ ! -f "${PROJECT_DIR}/.observer.pid" ] && [ ! -f "${CONFIG_DIR}/.observer.pid" ]; then
443
- _START_OBSERVER_LOGGED
444
- fi
445
- )
446
- fi
447
- fi
448
- fi
449
- fi
450
-
451
- # Throttle SIGUSR1: only signal observer every N observations (#521)
452
- # This prevents rapid signaling when tool calls fire every second,
453
- # which caused runaway parallel Claude analysis processes.
454
- SIGNAL_EVERY_N="${ECC_OBSERVER_SIGNAL_EVERY_N:-20}"
455
- SIGNAL_COUNTER_FILE="${PROJECT_DIR}/.observer-signal-counter"
456
- ACTIVITY_FILE="${PROJECT_DIR}/.observer-last-activity"
457
-
458
- touch "$ACTIVITY_FILE" 2>/dev/null || true
459
-
460
- should_signal=0
461
- if [ -f "$SIGNAL_COUNTER_FILE" ]; then
462
- counter=$(cat "$SIGNAL_COUNTER_FILE" 2>/dev/null || echo 0)
463
- counter=$((counter + 1))
464
- if [ "$counter" -ge "$SIGNAL_EVERY_N" ]; then
465
- should_signal=1
466
- counter=0
467
- fi
468
- echo "$counter" > "$SIGNAL_COUNTER_FILE"
469
- else
470
- echo "1" > "$SIGNAL_COUNTER_FILE"
471
- fi
472
-
473
- # Signal observer if running and throttle allows (check both project-scoped and global observer, deduplicate)
474
- if [ "$should_signal" -eq 1 ]; then
475
- signaled_pids=" "
476
- for pid_file in "${PROJECT_DIR}/.observer.pid" "${CONFIG_DIR}/.observer.pid"; do
477
- if [ -f "$pid_file" ]; then
478
- observer_pid=$(cat "$pid_file" 2>/dev/null || true)
479
- # Validate PID is a positive integer (>1)
480
- case "$observer_pid" in
481
- ''|*[!0-9]*|0|1)
482
- _REMOVE_FILE_IF_PRESENT "$pid_file"
483
- continue
484
- ;;
485
- esac
486
- # Deduplicate: skip if already signaled this pass
487
- case "$signaled_pids" in
488
- *" $observer_pid "*) continue ;;
489
- esac
490
- if kill -0 "$observer_pid" 2>/dev/null; then
491
- kill -USR1 "$observer_pid" 2>/dev/null || true
492
- signaled_pids="${signaled_pids}${observer_pid} "
493
- fi
494
- fi
495
- done
496
- fi
497
-
498
- exit 0
1
+ #!/bin/bash
2
+ # Continuous Learning v2 - Observation Hook
3
+ #
4
+ # Captures tool use events for pattern analysis.
5
+ # Claude Code passes hook data via stdin as JSON.
6
+ #
7
+ # v2.1: Project-scoped observations — detects current project context
8
+ # and writes observations to project-specific directory.
9
+ #
10
+ # Registered via plugin hooks/hooks.json (auto-loaded when plugin is enabled).
11
+ # Can also be registered manually in ~/.claude/settings.json.
12
+
13
+ set -e
14
+
15
+ # Hook phase from CLI argument: "pre" (PreToolUse) or "post" (PostToolUse).
16
+ # Manual settings.json installs can call this script without the plugin
17
+ # wrapper's positional phase argument, but Claude Code still exposes the hook
18
+ # event name in CLAUDE_HOOK_EVENT_NAME. Fall back to that env var before
19
+ # defaulting to post so manually registered PreToolUse hooks are recorded as
20
+ # tool_start instead of being silently misclassified as tool_complete.
21
+ HOOK_PHASE="${1:-}"
22
+ if [ -z "$HOOK_PHASE" ]; then
23
+ case "${CLAUDE_HOOK_EVENT_NAME:-}" in
24
+ PreToolUse|pretooluse|pre_tool_use|pre) HOOK_PHASE="pre" ;;
25
+ PostToolUse|posttooluse|post_tool_use|post) HOOK_PHASE="post" ;;
26
+ *) HOOK_PHASE="post" ;;
27
+ esac
28
+ fi
29
+
30
+ # ─────────────────────────────────────────────
31
+ # Read stdin first (before project detection)
32
+ # ─────────────────────────────────────────────
33
+
34
+ # Read JSON from stdin (Claude Code hook format)
35
+ INPUT_JSON=$(cat)
36
+
37
+ # Exit if no input
38
+ if [ -z "$INPUT_JSON" ]; then
39
+ exit 0
40
+ fi
41
+
42
+ _is_windows_app_installer_stub() {
43
+ # Windows 10/11 ships an "App Execution Alias" stub at
44
+ # %LOCALAPPDATA%\Microsoft\WindowsApps\python.exe
45
+ # %LOCALAPPDATA%\Microsoft\WindowsApps\python3.exe
46
+ # Both are symlinks to AppInstallerPythonRedirector.exe which, when Python
47
+ # is not installed from the Store, neither launches Python nor honors "-c".
48
+ # Calls to it hang or print a bare "Python " line, silently breaking every
49
+ # JSON-parsing step in this hook. Detect and skip such stubs here.
50
+ local _candidate="$1"
51
+ [ -z "$_candidate" ] && return 1
52
+ local _resolved
53
+ _resolved="$(command -v "$_candidate" 2>/dev/null || true)"
54
+ [ -z "$_resolved" ] && return 1
55
+ case "$_resolved" in
56
+ *AppInstallerPythonRedirector.exe|*AppInstallerPythonRedirector.EXE) return 0 ;;
57
+ esac
58
+ # Also resolve one level of symlink on POSIX-like shells (Git Bash, WSL).
59
+ if command -v readlink >/dev/null 2>&1; then
60
+ local _target
61
+ _target="$(readlink -f "$_resolved" 2>/dev/null || readlink "$_resolved" 2>/dev/null || true)"
62
+ case "$_target" in
63
+ *AppInstallerPythonRedirector.exe|*AppInstallerPythonRedirector.EXE) return 0 ;;
64
+ esac
65
+ fi
66
+ return 1
67
+ }
68
+
69
+ resolve_python_cmd() {
70
+ if [ -n "${CLV2_PYTHON_CMD:-}" ] && command -v "$CLV2_PYTHON_CMD" >/dev/null 2>&1; then
71
+ printf '%s\n' "$CLV2_PYTHON_CMD"
72
+ return 0
73
+ fi
74
+
75
+ if command -v python3 >/dev/null 2>&1 && ! _is_windows_app_installer_stub python3; then
76
+ printf '%s\n' python3
77
+ return 0
78
+ fi
79
+
80
+ if command -v python >/dev/null 2>&1 && ! _is_windows_app_installer_stub python; then
81
+ printf '%s\n' python
82
+ return 0
83
+ fi
84
+
85
+ return 1
86
+ }
87
+
88
+ PYTHON_CMD="$(resolve_python_cmd 2>/dev/null || true)"
89
+ if [ -z "$PYTHON_CMD" ]; then
90
+ echo "[observe] No python interpreter found, skipping observation" >&2
91
+ exit 0
92
+ fi
93
+
94
+ # Propagate our stub-aware selection so detect-project.sh (which is sourced
95
+ # below) does not re-resolve and silently fall back to the App Installer stub.
96
+ # detect-project.sh honors an already-set CLV2_PYTHON_CMD.
97
+ export CLV2_PYTHON_CMD="${CLV2_PYTHON_CMD:-$PYTHON_CMD}"
98
+
99
+ # ─────────────────────────────────────────────
100
+ # Extract cwd from stdin for project detection
101
+ # ─────────────────────────────────────────────
102
+
103
+ # Extract cwd from the hook JSON to use for project detection.
104
+ # If cwd is a subdirectory inside a git repo, resolve it to the repo root so
105
+ # observations attach to the project instead of a nested path.
106
+ STDIN_CWD=$(echo "$INPUT_JSON" | "$PYTHON_CMD" -c '
107
+ import json, sys
108
+ try:
109
+ data = json.load(sys.stdin)
110
+ cwd = data.get("cwd", "")
111
+ print(cwd)
112
+ except(KeyError, TypeError, ValueError):
113
+ print("")
114
+ ' 2>/dev/null || echo "")
115
+
116
+ # If cwd was provided in stdin, use it for project detection
117
+ if [ -n "$STDIN_CWD" ] && [ -d "$STDIN_CWD" ]; then
118
+ _GIT_ROOT=$(git -C "$STDIN_CWD" rev-parse --show-toplevel 2>/dev/null || true)
119
+ if [ -n "$_GIT_ROOT" ]; then
120
+ export CLAUDE_PROJECT_DIR="$_GIT_ROOT"
121
+ unset CLV2_NO_PROJECT
122
+ else
123
+ unset CLAUDE_PROJECT_DIR
124
+ export CLV2_NO_PROJECT=1
125
+ fi
126
+ fi
127
+
128
+ # ─────────────────────────────────────────────
129
+ # Lightweight config and automated session guards
130
+ # ─────────────────────────────────────────────
131
+ #
132
+ # IMPORTANT: keep these guards above detect-project.sh.
133
+ # Sourcing detect-project.sh creates project-scoped directories and updates
134
+ # projects.json, so automated sessions must return before that point.
135
+
136
+ # shellcheck disable=SC1091
137
+ . "$(dirname "$0")/../scripts/lib/homunculus-dir.sh"
138
+ CONFIG_DIR="$(_ecc_resolve_homunculus_dir)"
139
+
140
+ # Skip if disabled (check both default and CLV2_CONFIG-derived locations)
141
+ if [ -f "$CONFIG_DIR/disabled" ]; then
142
+ exit 0
143
+ fi
144
+ if [ -n "${CLV2_CONFIG:-}" ] && [ -f "$(dirname "$CLV2_CONFIG")/disabled" ]; then
145
+ exit 0
146
+ fi
147
+
148
+ # Prevent observe.sh from firing on non-human sessions to avoid:
149
+ # - ECC observing its own Haiku observer sessions (self-loop)
150
+ # - ECC observing other tools' automated sessions
151
+ # - automated sessions creating project-scoped homunculus metadata
152
+
153
+ # Layer 1: entrypoint. Only interactive terminal sessions should continue.
154
+ # sdk-ts: Agent SDK sessions can be human-interactive (e.g. via Happy).
155
+ # Non-interactive SDK automation is still filtered by Layers 2-5 below
156
+ # (ECC_HOOK_PROFILE=minimal, ECC_SKIP_OBSERVE=1, agent_id, path exclusions).
157
+ case "${CLAUDE_CODE_ENTRYPOINT:-cli}" in
158
+ cli|sdk-ts|claude-desktop) ;;
159
+ *) exit 0 ;;
160
+ esac
161
+
162
+ # Layer 2: minimal hook profile suppresses non-essential hooks.
163
+ [ "${ECC_HOOK_PROFILE:-standard}" = "minimal" ] && exit 0
164
+
165
+ # Layer 3: cooperative skip env var for automated sessions.
166
+ [ "${ECC_SKIP_OBSERVE:-0}" = "1" ] && exit 0
167
+
168
+ # Layer 4: subagent sessions are automated by definition.
169
+ _ECC_AGENT_ID=$(echo "$INPUT_JSON" | "$PYTHON_CMD" -c "import json,sys; print(json.load(sys.stdin).get('agent_id',''))" 2>/dev/null || true)
170
+ [ -n "$_ECC_AGENT_ID" ] && exit 0
171
+
172
+ # Layer 5: known observer-session path exclusions.
173
+ _ECC_SKIP_PATHS="${ECC_OBSERVE_SKIP_PATHS:-observer-sessions,.claude-mem}"
174
+ if [ -n "$STDIN_CWD" ]; then
175
+ IFS=',' read -ra _ECC_SKIP_ARRAY <<< "$_ECC_SKIP_PATHS"
176
+ for _pattern in "${_ECC_SKIP_ARRAY[@]}"; do
177
+ _pattern="${_pattern#"${_pattern%%[![:space:]]*}"}"
178
+ _pattern="${_pattern%"${_pattern##*[![:space:]]}"}"
179
+ [ -z "$_pattern" ] && continue
180
+ case "$STDIN_CWD" in *"$_pattern"*) exit 0 ;; esac
181
+ done
182
+ fi
183
+
184
+ # ─────────────────────────────────────────────
185
+ # Project detection
186
+ # ─────────────────────────────────────────────
187
+
188
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
189
+ SKILL_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
190
+
191
+ # Source shared project detection helper
192
+ # This sets: PROJECT_ID, PROJECT_NAME, PROJECT_ROOT, PROJECT_DIR
193
+ source "${SKILL_ROOT}/scripts/detect-project.sh"
194
+ PYTHON_CMD="${CLV2_PYTHON_CMD:-$PYTHON_CMD}"
195
+
196
+ # ─────────────────────────────────────────────
197
+ # Configuration
198
+ # ─────────────────────────────────────────────
199
+
200
+ OBSERVATIONS_FILE="${PROJECT_DIR}/observations.jsonl"
201
+ MAX_FILE_SIZE_MB=10
202
+
203
+ # Auto-purge observation files older than 30 days (runs once per session)
204
+ PURGE_MARKER="${PROJECT_DIR}/.last-purge"
205
+ if [ ! -f "$PURGE_MARKER" ] || [ "$(find "$PURGE_MARKER" -mtime +1 2>/dev/null)" ]; then
206
+ find "${PROJECT_DIR}" -name "observations-*.jsonl" -mtime +30 -delete 2>/dev/null || true
207
+ touch "$PURGE_MARKER" 2>/dev/null || true
208
+ fi
209
+
210
+ # Parse using Python via stdin pipe (safe for all JSON payloads)
211
+ # Pass HOOK_PHASE via env var since Claude Code does not include hook type in stdin JSON
212
+ PARSED=$(echo "$INPUT_JSON" | HOOK_PHASE="$HOOK_PHASE" "$PYTHON_CMD" -c '
213
+ import json
214
+ import sys
215
+ import os
216
+
217
+ try:
218
+ data = json.load(sys.stdin)
219
+
220
+ # Determine event type from CLI argument passed via env var.
221
+ # Claude Code does NOT include a "hook_type" field in the stdin JSON,
222
+ # so we rely on the shell argument ("pre" or "post") instead.
223
+ hook_phase = os.environ.get("HOOK_PHASE", "post")
224
+ event = "tool_start" if hook_phase == "pre" else "tool_complete"
225
+
226
+ # Extract fields - Claude Code hook format
227
+ tool_name = data.get("tool_name", data.get("tool", "unknown"))
228
+ tool_input = data.get("tool_input", data.get("input", {}))
229
+ tool_output = data.get("tool_response")
230
+ if tool_output is None:
231
+ tool_output = data.get("tool_output", data.get("output", ""))
232
+ session_id = data.get("session_id", "unknown")
233
+ tool_use_id = data.get("tool_use_id", "")
234
+ cwd = data.get("cwd", "")
235
+
236
+ # Truncate large inputs/outputs
237
+ if isinstance(tool_input, dict):
238
+ tool_input_str = json.dumps(tool_input)[:5000]
239
+ else:
240
+ tool_input_str = str(tool_input)[:5000]
241
+
242
+ if isinstance(tool_output, dict):
243
+ tool_response_str = json.dumps(tool_output)[:5000]
244
+ else:
245
+ tool_response_str = str(tool_output)[:5000]
246
+
247
+ print(json.dumps({
248
+ "parsed": True,
249
+ "event": event,
250
+ "tool": tool_name,
251
+ "input": tool_input_str if event == "tool_start" else None,
252
+ "output": tool_response_str if event == "tool_complete" else None,
253
+ "session": session_id,
254
+ "tool_use_id": tool_use_id,
255
+ "cwd": cwd
256
+ }))
257
+ except Exception as e:
258
+ print(json.dumps({"parsed": False, "error": str(e)}))
259
+ ')
260
+
261
+ # Check if parsing succeeded
262
+ PARSED_OK=$(echo "$PARSED" | "$PYTHON_CMD" -c "import json,sys; print(json.load(sys.stdin).get('parsed', False))" 2>/dev/null || echo "False")
263
+
264
+ if [ "$PARSED_OK" != "True" ]; then
265
+ # Fallback: log raw input for debugging (scrub secrets before persisting)
266
+ timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
267
+ export TIMESTAMP="$timestamp"
268
+ echo "$INPUT_JSON" | "$PYTHON_CMD" -c '
269
+ import json, sys, os, re
270
+
271
+ _SECRET_RE = re.compile(
272
+ r"(?i)(api[_-]?key|token|secret|password|authorization|credentials?|auth)"
273
+ r"""(["'"'"'\s:=]+)"""
274
+ r"([A-Za-z]+\s+)?"
275
+ r"([A-Za-z0-9_\-/.+=]{8,})"
276
+ )
277
+
278
+ raw = sys.stdin.read()[:2000]
279
+ raw = _SECRET_RE.sub(lambda m: m.group(1) + m.group(2) + (m.group(3) or "") + "[REDACTED]", raw)
280
+ print(json.dumps({"timestamp": os.environ["TIMESTAMP"], "event": "parse_error", "raw": raw}))
281
+ ' >> "$OBSERVATIONS_FILE"
282
+ exit 0
283
+ fi
284
+
285
+ # Archive if file too large (atomic: rename with unique suffix to avoid race)
286
+ if [ -f "$OBSERVATIONS_FILE" ]; then
287
+ file_size_mb=$(du -m "$OBSERVATIONS_FILE" 2>/dev/null | cut -f1)
288
+ if [ "${file_size_mb:-0}" -ge "$MAX_FILE_SIZE_MB" ]; then
289
+ archive_dir="${PROJECT_DIR}/observations.archive"
290
+ mkdir -p "$archive_dir"
291
+ mv "$OBSERVATIONS_FILE" "$archive_dir/observations-$(date +%Y%m%d-%H%M%S)-$$.jsonl" 2>/dev/null || true
292
+ fi
293
+ fi
294
+
295
+ # Build and write observation (now includes project context)
296
+ # Scrub common secret patterns from tool I/O before persisting
297
+ timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
298
+
299
+ export PROJECT_ID_ENV="$PROJECT_ID"
300
+ export PROJECT_NAME_ENV="$PROJECT_NAME"
301
+ export TIMESTAMP="$timestamp"
302
+
303
+ echo "$PARSED" | "$PYTHON_CMD" -c '
304
+ import json, sys, os, re
305
+
306
+ parsed = json.load(sys.stdin)
307
+ observation = {
308
+ "timestamp": os.environ["TIMESTAMP"],
309
+ "event": parsed["event"],
310
+ "tool": parsed["tool"],
311
+ "session": parsed["session"],
312
+ "project_id": os.environ.get("PROJECT_ID_ENV", "global"),
313
+ "project_name": os.environ.get("PROJECT_NAME_ENV", "global")
314
+ }
315
+
316
+ # Scrub secrets: match common key=value, key: value, and key"value patterns
317
+ # Includes optional auth scheme (e.g., "Bearer", "Basic") before token
318
+ _SECRET_RE = re.compile(
319
+ r"(?i)(api[_-]?key|token|secret|password|authorization|credentials?|auth)"
320
+ r"""(["'"'"'\s:=]+)"""
321
+ r"([A-Za-z]+\s+)?"
322
+ r"([A-Za-z0-9_\-/.+=]{8,})"
323
+ )
324
+
325
+ def scrub(val):
326
+ if val is None:
327
+ return None
328
+ return _SECRET_RE.sub(lambda m: m.group(1) + m.group(2) + (m.group(3) or "") + "[REDACTED]", str(val))
329
+
330
+ if parsed["input"]:
331
+ observation["input"] = scrub(parsed["input"])
332
+ if parsed["output"] is not None:
333
+ observation["output"] = scrub(parsed["output"])
334
+
335
+ print(json.dumps(observation))
336
+ ' >> "$OBSERVATIONS_FILE"
337
+
338
+ # Lazy-start observer if enabled but not running (first-time setup)
339
+ # Use flock for atomic check-then-act to prevent race conditions
340
+ # Fallback for macOS (no flock): use lockfile or skip
341
+ LAZY_START_LOCK="${PROJECT_DIR}/.observer-start.lock"
342
+ _REMOVE_FILE_IF_PRESENT() {
343
+ local target="$1"
344
+ if [ -n "$target" ] && [ -e "$target" ]; then
345
+ rm -- "$target" 2>/dev/null || true
346
+ fi
347
+ }
348
+
349
+ _START_OBSERVER_LOGGED() {
350
+ local bootstrap_log="${PROJECT_DIR}/observer-start.log"
351
+ mkdir -p "$PROJECT_DIR"
352
+ "${SKILL_ROOT}/agents/start-observer.sh" start >> "$bootstrap_log" 2>&1 || true
353
+ }
354
+
355
+ _CHECK_OBSERVER_RUNNING() {
356
+ local pid_file="$1"
357
+ if [ -f "$pid_file" ]; then
358
+ local pid
359
+ pid=$(cat "$pid_file" 2>/dev/null)
360
+ # Validate PID is a positive integer (>1) to prevent signaling invalid targets
361
+ case "$pid" in
362
+ ''|*[!0-9]*|0|1)
363
+ _REMOVE_FILE_IF_PRESENT "$pid_file"
364
+ return 1
365
+ ;;
366
+ esac
367
+ if kill -0 "$pid" 2>/dev/null; then
368
+ return 0 # Process is alive
369
+ fi
370
+ # Stale PID file - remove it
371
+ _REMOVE_FILE_IF_PRESENT "$pid_file"
372
+ fi
373
+ return 1 # No PID file or process dead
374
+ }
375
+
376
+ if [ -f "${CONFIG_DIR}/disabled" ]; then
377
+ OBSERVER_ENABLED=false
378
+ else
379
+ OBSERVER_ENABLED=false
380
+ if [ -n "${CLV2_CONFIG:-}" ]; then
381
+ CONFIG_FILE="$CLV2_CONFIG"
382
+ elif [ -f "${CONFIG_DIR}/config.json" ]; then
383
+ CONFIG_FILE="${CONFIG_DIR}/config.json"
384
+ else
385
+ CONFIG_FILE="${SKILL_ROOT}/config.json"
386
+ fi
387
+ # Use effective config path for both existence check and reading
388
+ EFFECTIVE_CONFIG="$CONFIG_FILE"
389
+ if [ -f "$EFFECTIVE_CONFIG" ] && [ -n "$PYTHON_CMD" ]; then
390
+ _enabled=$(CLV2_CONFIG_PATH="$EFFECTIVE_CONFIG" "$PYTHON_CMD" -c "
391
+ import json, os
392
+ with open(os.environ['CLV2_CONFIG_PATH']) as f:
393
+ cfg = json.load(f)
394
+ print(str(cfg.get('observer', {}).get('enabled', False)).lower())
395
+ " 2>/dev/null || echo "false")
396
+ if [ "$_enabled" = "true" ]; then
397
+ OBSERVER_ENABLED=true
398
+ fi
399
+ fi
400
+ fi
401
+
402
+ # Check both project-scoped AND global PID files (with stale PID recovery)
403
+ if [ "$OBSERVER_ENABLED" = "true" ]; then
404
+ # Clean up stale PID files first
405
+ _CHECK_OBSERVER_RUNNING "${PROJECT_DIR}/.observer.pid" || true
406
+ _CHECK_OBSERVER_RUNNING "${CONFIG_DIR}/.observer.pid" || true
407
+
408
+ # Check if observer is now running after cleanup
409
+ if [ ! -f "${PROJECT_DIR}/.observer.pid" ] && [ ! -f "${CONFIG_DIR}/.observer.pid" ]; then
410
+ # Use flock if available (Linux), fallback for macOS
411
+ if command -v flock >/dev/null 2>&1; then
412
+ (
413
+ flock -n 9 || exit 0
414
+ # Double-check PID files after acquiring lock
415
+ _CHECK_OBSERVER_RUNNING "${PROJECT_DIR}/.observer.pid" || true
416
+ _CHECK_OBSERVER_RUNNING "${CONFIG_DIR}/.observer.pid" || true
417
+ if [ ! -f "${PROJECT_DIR}/.observer.pid" ] && [ ! -f "${CONFIG_DIR}/.observer.pid" ]; then
418
+ _START_OBSERVER_LOGGED
419
+ fi
420
+ ) 9>"$LAZY_START_LOCK"
421
+ else
422
+ # macOS fallback: use lockfile if available, otherwise mkdir-based lock
423
+ if command -v lockfile >/dev/null 2>&1; then
424
+ # Use subshell to isolate exit and add trap for cleanup
425
+ (
426
+ trap '_REMOVE_FILE_IF_PRESENT "$LAZY_START_LOCK"' EXIT
427
+ lockfile -r 1 -l 30 "$LAZY_START_LOCK" 2>/dev/null || exit 0
428
+ _CHECK_OBSERVER_RUNNING "${PROJECT_DIR}/.observer.pid" || true
429
+ _CHECK_OBSERVER_RUNNING "${CONFIG_DIR}/.observer.pid" || true
430
+ if [ ! -f "${PROJECT_DIR}/.observer.pid" ] && [ ! -f "${CONFIG_DIR}/.observer.pid" ]; then
431
+ _START_OBSERVER_LOGGED
432
+ fi
433
+ _REMOVE_FILE_IF_PRESENT "$LAZY_START_LOCK"
434
+ )
435
+ else
436
+ # POSIX fallback: mkdir is atomic -- fails if dir already exists
437
+ (
438
+ trap 'rmdir "${LAZY_START_LOCK}.d" 2>/dev/null || true' EXIT
439
+ mkdir "${LAZY_START_LOCK}.d" 2>/dev/null || exit 0
440
+ _CHECK_OBSERVER_RUNNING "${PROJECT_DIR}/.observer.pid" || true
441
+ _CHECK_OBSERVER_RUNNING "${CONFIG_DIR}/.observer.pid" || true
442
+ if [ ! -f "${PROJECT_DIR}/.observer.pid" ] && [ ! -f "${CONFIG_DIR}/.observer.pid" ]; then
443
+ _START_OBSERVER_LOGGED
444
+ fi
445
+ )
446
+ fi
447
+ fi
448
+ fi
449
+ fi
450
+
451
+ # Throttle SIGUSR1: only signal observer every N observations (#521)
452
+ # This prevents rapid signaling when tool calls fire every second,
453
+ # which caused runaway parallel Claude analysis processes.
454
+ SIGNAL_EVERY_N="${ECC_OBSERVER_SIGNAL_EVERY_N:-20}"
455
+ SIGNAL_COUNTER_FILE="${PROJECT_DIR}/.observer-signal-counter"
456
+ ACTIVITY_FILE="${PROJECT_DIR}/.observer-last-activity"
457
+
458
+ touch "$ACTIVITY_FILE" 2>/dev/null || true
459
+
460
+ should_signal=0
461
+ if [ -f "$SIGNAL_COUNTER_FILE" ]; then
462
+ counter=$(cat "$SIGNAL_COUNTER_FILE" 2>/dev/null || echo 0)
463
+ counter=$((counter + 1))
464
+ if [ "$counter" -ge "$SIGNAL_EVERY_N" ]; then
465
+ should_signal=1
466
+ counter=0
467
+ fi
468
+ echo "$counter" > "$SIGNAL_COUNTER_FILE"
469
+ else
470
+ echo "1" > "$SIGNAL_COUNTER_FILE"
471
+ fi
472
+
473
+ # Signal observer if running and throttle allows (check both project-scoped and global observer, deduplicate)
474
+ if [ "$should_signal" -eq 1 ]; then
475
+ signaled_pids=" "
476
+ for pid_file in "${PROJECT_DIR}/.observer.pid" "${CONFIG_DIR}/.observer.pid"; do
477
+ if [ -f "$pid_file" ]; then
478
+ observer_pid=$(cat "$pid_file" 2>/dev/null || true)
479
+ # Validate PID is a positive integer (>1)
480
+ case "$observer_pid" in
481
+ ''|*[!0-9]*|0|1)
482
+ _REMOVE_FILE_IF_PRESENT "$pid_file"
483
+ continue
484
+ ;;
485
+ esac
486
+ # Deduplicate: skip if already signaled this pass
487
+ case "$signaled_pids" in
488
+ *" $observer_pid "*) continue ;;
489
+ esac
490
+ if kill -0 "$observer_pid" 2>/dev/null; then
491
+ kill -USR1 "$observer_pid" 2>/dev/null || true
492
+ signaled_pids="${signaled_pids}${observer_pid} "
493
+ fi
494
+ fi
495
+ done
496
+ fi
497
+
498
+ exit 0