agents-city 0.3.0-beta.21

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 (440) hide show
  1. package/.claude-plugin/marketplace.json +16 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +5 -0
  4. package/README.es.md +2021 -0
  5. package/README.md +1999 -0
  6. package/benchmarks/committee/README.md +39 -0
  7. package/benchmarks/committee/metrics.py +51 -0
  8. package/benchmarks/committee/run.py +64 -0
  9. package/benchmarks/committee/traces/01-single.json +8 -0
  10. package/benchmarks/committee/traces/02-mesh.json +16 -0
  11. package/benchmarks/committee/traces/03-chair.json +15 -0
  12. package/benchmarks/latency/README.md +35 -0
  13. package/benchmarks/latency/fake-claude-cli.mjs +119 -0
  14. package/benchmarks/latency/fake-native-server.mjs +340 -0
  15. package/benchmarks/latency/fake-tui.py +91 -0
  16. package/benchmarks/latency/live.py +380 -0
  17. package/benchmarks/stress/README.md +20 -0
  18. package/benchmarks/stress/run.py +487 -0
  19. package/bin/agents +4 -0
  20. package/bin/agents-city.js +116 -0
  21. package/bin/benchmark +17 -0
  22. package/bin/bus +6 -0
  23. package/bin/cities +3 -0
  24. package/bin/city +86 -0
  25. package/bin/committee +6 -0
  26. package/bin/demo +200 -0
  27. package/bin/exit +5 -0
  28. package/bin/hall +73 -0
  29. package/bin/hall.html +426 -0
  30. package/bin/logs +3 -0
  31. package/bin/report.py +14 -0
  32. package/bin/reset +3 -0
  33. package/bin/road +3 -0
  34. package/bin/seat +10 -0
  35. package/bin/serve.py +1428 -0
  36. package/bin/setup.py +753 -0
  37. package/bin/skills +3 -0
  38. package/bin/test +131 -0
  39. package/bin/test-adapter.py +183 -0
  40. package/bin/test-admision.py +99 -0
  41. package/bin/test-avatar.py +58 -0
  42. package/bin/test-benchmark.py +40 -0
  43. package/bin/test-broker.py +334 -0
  44. package/bin/test-cage.py +206 -0
  45. package/bin/test-card.py +402 -0
  46. package/bin/test-channel.py +379 -0
  47. package/bin/test-cities.py +453 -0
  48. package/bin/test-claude-runtime.py +252 -0
  49. package/bin/test-committee.py +340 -0
  50. package/bin/test-contracts.py +544 -0
  51. package/bin/test-crecimiento.py +164 -0
  52. package/bin/test-demo.py +308 -0
  53. package/bin/test-doctor.py +106 -0
  54. package/bin/test-domains.py +151 -0
  55. package/bin/test-evidencia.py +50 -0
  56. package/bin/test-exit.py +128 -0
  57. package/bin/test-hall-protocol.py +93 -0
  58. package/bin/test-launch.py +169 -0
  59. package/bin/test-live-feed.py +508 -0
  60. package/bin/test-pairing.py +99 -0
  61. package/bin/test-parcels.py +283 -0
  62. package/bin/test-runtime-failures.py +252 -0
  63. package/bin/test-runtime-ui.py +488 -0
  64. package/bin/test-runtime.py +268 -0
  65. package/bin/test-rutas.py +80 -0
  66. package/bin/test-seat.py +1386 -0
  67. package/bin/test-security.py +150 -0
  68. package/bin/test-serve.py +1057 -0
  69. package/bin/test-stress.py +64 -0
  70. package/bin/test-widgets.py +143 -0
  71. package/bin/test-workspace.py +188 -0
  72. package/bin/testlib.py +95 -0
  73. package/bin/tokens.py +14 -0
  74. package/bus/scripts/deploy.sh +60 -0
  75. package/bus/scripts/mint-token.sh +68 -0
  76. package/bus/scripts/setup-dev.sh +79 -0
  77. package/bus/scripts/test-channel.ts +8 -0
  78. package/bus/scripts/test-hub.ts +266 -0
  79. package/bus/scripts/test-local.sh +40 -0
  80. package/bus/scripts/test-queue.ts +132 -0
  81. package/bus/worker/package-lock.json +1971 -0
  82. package/bus/worker/package.json +17 -0
  83. package/bus/worker/src/index.ts +485 -0
  84. package/bus/worker/tsconfig.json +13 -0
  85. package/bus/worker/wrangler.toml +32 -0
  86. package/city/oven/README.md +59 -0
  87. package/city/oven/collect.py +43 -0
  88. package/city/oven/oven.html +4 -0
  89. package/city/scripts/history.py +120 -0
  90. package/city/scripts/seed.py +442 -0
  91. package/city/web/assets/modelos/License.txt +28 -0
  92. package/city/web/assets/modelos/PROCEDENCIA.txt +3 -0
  93. package/city/web/assets/modelos/building-a.glb +0 -0
  94. package/city/web/assets/modelos/building-b.glb +0 -0
  95. package/city/web/assets/modelos/building-c.glb +0 -0
  96. package/city/web/assets/modelos/building-d.glb +0 -0
  97. package/city/web/assets/modelos/building-e.glb +0 -0
  98. package/city/web/assets/modelos/building-f.glb +0 -0
  99. package/city/web/assets/modelos/building-g.glb +0 -0
  100. package/city/web/assets/modelos/building-h.glb +0 -0
  101. package/city/web/assets/sprites/building-a.png +0 -0
  102. package/city/web/assets/sprites/building-b.png +0 -0
  103. package/city/web/assets/sprites/building-c.png +0 -0
  104. package/city/web/assets/sprites/building-d.png +0 -0
  105. package/city/web/assets/sprites/building-e.png +0 -0
  106. package/city/web/assets/sprites/building-f.png +0 -0
  107. package/city/web/assets/sprites/building-g.png +0 -0
  108. package/city/web/assets/sprites/building-h.png +0 -0
  109. package/city/web/assets/sprites/building-i.png +0 -0
  110. package/city/web/assets/sprites/building-j.png +0 -0
  111. package/city/web/assets/sprites/building-k.png +0 -0
  112. package/city/web/assets/sprites/building-l.png +0 -0
  113. package/city/web/assets/sprites/building-m.png +0 -0
  114. package/city/web/assets/sprites/building-n.png +0 -0
  115. package/city/web/assets/sprites/building-skyscraper-a.png +0 -0
  116. package/city/web/assets/sprites/building-skyscraper-b.png +0 -0
  117. package/city/web/assets/sprites/building-skyscraper-c.png +0 -0
  118. package/city/web/assets/sprites/building-skyscraper-d.png +0 -0
  119. package/city/web/assets/sprites/building-skyscraper-e.png +0 -0
  120. package/city/web/assets/sprites/catalogo.json +47 -0
  121. package/city/web/assets/sprites/detail-awning-wide.png +0 -0
  122. package/city/web/assets/sprites/detail-awning.png +0 -0
  123. package/city/web/assets/sprites/detail-overhang-wide.png +0 -0
  124. package/city/web/assets/sprites/detail-overhang.png +0 -0
  125. package/city/web/assets/sprites/detail-parasol-a.png +0 -0
  126. package/city/web/assets/sprites/detail-parasol-b.png +0 -0
  127. package/city/web/assets/sprites/low-detail-building-a.png +0 -0
  128. package/city/web/assets/sprites/low-detail-building-b.png +0 -0
  129. package/city/web/assets/sprites/low-detail-building-c.png +0 -0
  130. package/city/web/assets/sprites/low-detail-building-d.png +0 -0
  131. package/city/web/assets/sprites/low-detail-building-e.png +0 -0
  132. package/city/web/assets/sprites/low-detail-building-f.png +0 -0
  133. package/city/web/assets/sprites/low-detail-building-g.png +0 -0
  134. package/city/web/assets/sprites/low-detail-building-h.png +0 -0
  135. package/city/web/assets/sprites/low-detail-building-i.png +0 -0
  136. package/city/web/assets/sprites/low-detail-building-j.png +0 -0
  137. package/city/web/assets/sprites/low-detail-building-k.png +0 -0
  138. package/city/web/assets/sprites/low-detail-building-l.png +0 -0
  139. package/city/web/assets/sprites/low-detail-building-m.png +0 -0
  140. package/city/web/assets/sprites/low-detail-building-n.png +0 -0
  141. package/city/web/assets/sprites/low-detail-building-wide-a.png +0 -0
  142. package/city/web/assets/sprites/low-detail-building-wide-b.png +0 -0
  143. package/city/web/assets/sprites/medidas.json +248 -0
  144. package/city/web/dist/city.js +939 -0
  145. package/city/web/dist/index.html +294 -0
  146. package/city/web/dist/modelos/License.txt +28 -0
  147. package/city/web/dist/modelos/PROCEDENCIA.txt +3 -0
  148. package/city/web/dist/modelos/building-a.glb +0 -0
  149. package/city/web/dist/modelos/building-b.glb +0 -0
  150. package/city/web/dist/modelos/building-c.glb +0 -0
  151. package/city/web/dist/modelos/building-d.glb +0 -0
  152. package/city/web/dist/modelos/building-e.glb +0 -0
  153. package/city/web/dist/modelos/building-f.glb +0 -0
  154. package/city/web/dist/modelos/building-g.glb +0 -0
  155. package/city/web/dist/modelos/building-h.glb +0 -0
  156. package/city/web/dist/sprites/building-a.png +0 -0
  157. package/city/web/dist/sprites/building-b.png +0 -0
  158. package/city/web/dist/sprites/building-c.png +0 -0
  159. package/city/web/dist/sprites/building-d.png +0 -0
  160. package/city/web/dist/sprites/building-e.png +0 -0
  161. package/city/web/dist/sprites/building-f.png +0 -0
  162. package/city/web/dist/sprites/building-g.png +0 -0
  163. package/city/web/dist/sprites/building-h.png +0 -0
  164. package/city/web/dist/sprites/building-i.png +0 -0
  165. package/city/web/dist/sprites/building-j.png +0 -0
  166. package/city/web/dist/sprites/building-k.png +0 -0
  167. package/city/web/dist/sprites/building-l.png +0 -0
  168. package/city/web/dist/sprites/building-m.png +0 -0
  169. package/city/web/dist/sprites/building-n.png +0 -0
  170. package/city/web/dist/sprites/building-skyscraper-a.png +0 -0
  171. package/city/web/dist/sprites/building-skyscraper-b.png +0 -0
  172. package/city/web/dist/sprites/building-skyscraper-c.png +0 -0
  173. package/city/web/dist/sprites/building-skyscraper-d.png +0 -0
  174. package/city/web/dist/sprites/building-skyscraper-e.png +0 -0
  175. package/city/web/dist/sprites/catalogo.json +47 -0
  176. package/city/web/dist/sprites/detail-awning-wide.png +0 -0
  177. package/city/web/dist/sprites/detail-awning.png +0 -0
  178. package/city/web/dist/sprites/detail-overhang-wide.png +0 -0
  179. package/city/web/dist/sprites/detail-overhang.png +0 -0
  180. package/city/web/dist/sprites/detail-parasol-a.png +0 -0
  181. package/city/web/dist/sprites/detail-parasol-b.png +0 -0
  182. package/city/web/dist/sprites/low-detail-building-a.png +0 -0
  183. package/city/web/dist/sprites/low-detail-building-b.png +0 -0
  184. package/city/web/dist/sprites/low-detail-building-c.png +0 -0
  185. package/city/web/dist/sprites/low-detail-building-d.png +0 -0
  186. package/city/web/dist/sprites/low-detail-building-e.png +0 -0
  187. package/city/web/dist/sprites/low-detail-building-f.png +0 -0
  188. package/city/web/dist/sprites/low-detail-building-g.png +0 -0
  189. package/city/web/dist/sprites/low-detail-building-h.png +0 -0
  190. package/city/web/dist/sprites/low-detail-building-i.png +0 -0
  191. package/city/web/dist/sprites/low-detail-building-j.png +0 -0
  192. package/city/web/dist/sprites/low-detail-building-k.png +0 -0
  193. package/city/web/dist/sprites/low-detail-building-l.png +0 -0
  194. package/city/web/dist/sprites/low-detail-building-m.png +0 -0
  195. package/city/web/dist/sprites/low-detail-building-n.png +0 -0
  196. package/city/web/dist/sprites/low-detail-building-wide-a.png +0 -0
  197. package/city/web/dist/sprites/low-detail-building-wide-b.png +0 -0
  198. package/city/web/dist/sprites/medidas.json +248 -0
  199. package/city/web/dist-hall/hall.js +264 -0
  200. package/city/web/index.html +294 -0
  201. package/city/web/package.json +18 -0
  202. package/city/web/sella.py +21 -0
  203. package/city/web/src/activity-actors.ts +47 -0
  204. package/city/web/src/activity.ts +172 -0
  205. package/city/web/src/ayuntamiento.ts +580 -0
  206. package/city/web/src/draw.ts +640 -0
  207. package/city/web/src/game-speech.ts +79 -0
  208. package/city/web/src/hall.ts +1867 -0
  209. package/city/web/src/main.ts +2225 -0
  210. package/city/web/src/oven.ts +124 -0
  211. package/city/web/src/people.ts +244 -0
  212. package/city/web/src/presencia.ts +193 -0
  213. package/city/web/src/puertas.ts +180 -0
  214. package/city/web/src/repo-roles.ts +59 -0
  215. package/city/worker/package.json +16 -0
  216. package/city/worker/schema.sql +117 -0
  217. package/city/worker/src/index.ts +702 -0
  218. package/city/worker/src/square.ts +218 -0
  219. package/city/worker/wrangler.toml +58 -0
  220. package/demo/ada.md +50 -0
  221. package/demo/bruno.md +62 -0
  222. package/demo/camila.md +56 -0
  223. package/demo/city/ada.md +40 -0
  224. package/demo/city/city.yml +8 -0
  225. package/demo/city/parcels.yml +15 -0
  226. package/demo/city/roads.json +4 -0
  227. package/demo/city/units.yml +10 -0
  228. package/demo/clinica/city.yml +8 -0
  229. package/demo/clinica/parcels.yml +15 -0
  230. package/demo/clinica/roads.json +4 -0
  231. package/demo/clinica/units.yml +9 -0
  232. package/demo/clinica/vera.md +46 -0
  233. package/demo/dante.md +54 -0
  234. package/demo/despacho/city.yml +8 -0
  235. package/demo/despacho/marta.md +46 -0
  236. package/demo/despacho/parcels.yml +15 -0
  237. package/demo/despacho/roads.json +4 -0
  238. package/demo/despacho/units.yml +9 -0
  239. package/demo/elsa.md +50 -0
  240. package/demo/farid.md +52 -0
  241. package/demo/greta.md +50 -0
  242. package/demo/hugo.md +49 -0
  243. package/demo/iris.md +49 -0
  244. package/demo/jonas.md +51 -0
  245. package/demo/kira.md +51 -0
  246. package/demo/luca.md +49 -0
  247. package/demo/parcels.yml +92 -0
  248. package/demo/seed.py +20 -0
  249. package/demo/show.py +161 -0
  250. package/demo/stories.py +824 -0
  251. package/demo/units.yml +33 -0
  252. package/docs/agents-first.md +99 -0
  253. package/docs/glossary.md +55 -0
  254. package/docs/map-live-layers.md +73 -0
  255. package/docs/security.md +231 -0
  256. package/docs/self-host.md +151 -0
  257. package/docs/testing.md +205 -0
  258. package/package.json +73 -0
  259. package/plugin/.claude-plugin/plugin.json +62 -0
  260. package/plugin/.mcp.json +15 -0
  261. package/plugin/channel/activity-cli.ts +16 -0
  262. package/plugin/channel/adapter-prompts.ts +113 -0
  263. package/plugin/channel/adapter.js +4292 -0
  264. package/plugin/channel/adapter.ts +133 -0
  265. package/plugin/channel/bus.js +20188 -0
  266. package/plugin/channel/bus.ts +131 -0
  267. package/plugin/channel/city-config.ts +125 -0
  268. package/plugin/channel/claude-channel.ts +58 -0
  269. package/plugin/channel/cli-args.ts +75 -0
  270. package/plugin/channel/client.js +4200 -0
  271. package/plugin/channel/client.ts +38 -0
  272. package/plugin/channel/committee/activity.ts +256 -0
  273. package/plugin/channel/committee/collection.ts +154 -0
  274. package/plugin/channel/committee/decision.ts +175 -0
  275. package/plugin/channel/committee/floor.ts +135 -0
  276. package/plugin/channel/committee/guards.ts +30 -0
  277. package/plugin/channel/committee/history.ts +38 -0
  278. package/plugin/channel/committee/render.ts +102 -0
  279. package/plugin/channel/committee/service.ts +72 -0
  280. package/plugin/channel/committee/storage.ts +92 -0
  281. package/plugin/channel/committee/types.ts +163 -0
  282. package/plugin/channel/committee/view.ts +63 -0
  283. package/plugin/channel/committee-cli.ts +109 -0
  284. package/plugin/channel/delivery-metrics.ts +61 -0
  285. package/plugin/channel/delivery-queue.ts +118 -0
  286. package/plugin/channel/hub/activity-controller.ts +72 -0
  287. package/plugin/channel/hub/activity-feed.ts +169 -0
  288. package/plugin/channel/hub/committee-controller.ts +60 -0
  289. package/plugin/channel/hub/connections.ts +48 -0
  290. package/plugin/channel/hub/diagnostics.ts +67 -0
  291. package/plugin/channel/hub/envelope-validity.ts +109 -0
  292. package/plugin/channel/hub/envelopes.ts +66 -0
  293. package/plugin/channel/hub/lifecycle.ts +91 -0
  294. package/plugin/channel/hub/local-roads.ts +89 -0
  295. package/plugin/channel/hub/remote-roads.ts +165 -0
  296. package/plugin/channel/hub/road-controller.ts +99 -0
  297. package/plugin/channel/hub-client.ts +149 -0
  298. package/plugin/channel/local-hub.js +5995 -0
  299. package/plugin/channel/local-hub.ts +349 -0
  300. package/plugin/channel/map-reporter.ts +48 -0
  301. package/plugin/channel/package-lock.json +1731 -0
  302. package/plugin/channel/package.json +19 -0
  303. package/plugin/channel/protocol.ts +98 -0
  304. package/plugin/channel/road-cli.ts +18 -0
  305. package/plugin/channel/run.sh +15 -0
  306. package/plugin/channel/runtime/claude.ts +458 -0
  307. package/plugin/channel/runtime/codex-config.ts +77 -0
  308. package/plugin/channel/runtime/codex.ts +669 -0
  309. package/plugin/channel/runtime/command.ts +62 -0
  310. package/plugin/channel/runtime/factory.ts +15 -0
  311. package/plugin/channel/runtime/json-rpc.ts +154 -0
  312. package/plugin/channel/runtime/kimi.ts +277 -0
  313. package/plugin/channel/runtime/opencode.ts +248 -0
  314. package/plugin/channel/runtime/process.ts +84 -0
  315. package/plugin/channel/runtime/types.ts +45 -0
  316. package/plugin/channel/runtime-files.ts +73 -0
  317. package/plugin/channel/runtime-gateway.js +6396 -0
  318. package/plugin/channel/runtime-gateway.ts +358 -0
  319. package/plugin/channel/runtime-metrics.ts +52 -0
  320. package/plugin/channel/runtime-subscription.ts +180 -0
  321. package/plugin/channel/terminal-delivery.ts +131 -0
  322. package/plugin/channel/untrusted.ts +60 -0
  323. package/plugin/commands/committee.md +31 -0
  324. package/plugin/commands/exit.md +32 -0
  325. package/plugin/commands/goals.md +20 -0
  326. package/plugin/commands/join.md +18 -0
  327. package/plugin/commands/notice.md +20 -0
  328. package/plugin/commands/propose.md +19 -0
  329. package/plugin/commands/round.md +17 -0
  330. package/plugin/commands/session.md +22 -0
  331. package/plugin/commands/settings.md +25 -0
  332. package/plugin/commands/setup.md +21 -0
  333. package/plugin/commands/team.md +23 -0
  334. package/plugin/domains/custom.md +27 -0
  335. package/plugin/domains/finance.md +31 -0
  336. package/plugin/domains/healthcare.md +34 -0
  337. package/plugin/domains/legal.md +31 -0
  338. package/plugin/domains/marketing.md +33 -0
  339. package/plugin/domains/operations.md +31 -0
  340. package/plugin/domains/research.md +32 -0
  341. package/plugin/domains/sales.md +30 -0
  342. package/plugin/domains/software.md +37 -0
  343. package/plugin/hooks/activity.sh +6 -0
  344. package/plugin/hooks/digging.sh +68 -0
  345. package/plugin/hooks/growth.sh +42 -0
  346. package/plugin/hooks/hooks.json +106 -0
  347. package/plugin/hooks/notice-on-pr.sh +35 -0
  348. package/plugin/hooks/notice-on-stop.sh +80 -0
  349. package/plugin/hooks/notice-pending.sh +58 -0
  350. package/plugin/hooks/solo-en-ciudad.sh +23 -0
  351. package/plugin/hooks/tokens.sh +47 -0
  352. package/plugin/roles/examples/account-executive.md +25 -0
  353. package/plugin/roles/examples/ai-manager.md +34 -0
  354. package/plugin/roles/examples/associate.md +24 -0
  355. package/plugin/roles/examples/brand-lead.md +20 -0
  356. package/plugin/roles/examples/cfo.md +20 -0
  357. package/plugin/roles/examples/city-lead.md +25 -0
  358. package/plugin/roles/examples/clinical-director.md +26 -0
  359. package/plugin/roles/examples/clinical-ops.md +25 -0
  360. package/plugin/roles/examples/clinician.md +25 -0
  361. package/plugin/roles/examples/compliance.md +27 -0
  362. package/plugin/roles/examples/content.md +24 -0
  363. package/plugin/roles/examples/controller.md +27 -0
  364. package/plugin/roles/examples/cpto.md +41 -0
  365. package/plugin/roles/examples/customer-success.md +25 -0
  366. package/plugin/roles/examples/data-engineer.md +37 -0
  367. package/plugin/roles/examples/data.md +60 -0
  368. package/plugin/roles/examples/dev.md +37 -0
  369. package/plugin/roles/examples/devops.md +39 -0
  370. package/plugin/roles/examples/enablement.md +24 -0
  371. package/plugin/roles/examples/ethics.md +24 -0
  372. package/plugin/roles/examples/fin-analytics.md +27 -0
  373. package/plugin/roles/examples/health-compliance.md +25 -0
  374. package/plugin/roles/examples/health-data.md +26 -0
  375. package/plugin/roles/examples/knowledge.md +25 -0
  376. package/plugin/roles/examples/lifecycle.md +26 -0
  377. package/plugin/roles/examples/llm-engineer.md +34 -0
  378. package/plugin/roles/examples/managing-partner.md +20 -0
  379. package/plugin/roles/examples/methods.md +23 -0
  380. package/plugin/roles/examples/operations-lead.md +23 -0
  381. package/plugin/roles/examples/ops.md +24 -0
  382. package/plugin/roles/examples/patient-safety.md +26 -0
  383. package/plugin/roles/examples/performance.md +27 -0
  384. package/plugin/roles/examples/po.md +36 -0
  385. package/plugin/roles/examples/process-owner.md +23 -0
  386. package/plugin/roles/examples/product-design.md +39 -0
  387. package/plugin/roles/examples/program-manager.md +24 -0
  388. package/plugin/roles/examples/quality.md +25 -0
  389. package/plugin/roles/examples/research-director.md +24 -0
  390. package/plugin/roles/examples/research-ops.md +25 -0
  391. package/plugin/roles/examples/researcher.md +25 -0
  392. package/plugin/roles/examples/revenue-lead.md +25 -0
  393. package/plugin/roles/examples/revops.md +26 -0
  394. package/plugin/roles/examples/seo.md +27 -0
  395. package/plugin/roles/examples/specialist.md +24 -0
  396. package/plugin/scripts/admision.py +165 -0
  397. package/plugin/scripts/apaga.py +232 -0
  398. package/plugin/scripts/avatar.py +211 -0
  399. package/plugin/scripts/broker.py +531 -0
  400. package/plugin/scripts/cage.py +252 -0
  401. package/plugin/scripts/capabilities.py +197 -0
  402. package/plugin/scripts/card.py +312 -0
  403. package/plugin/scripts/cities.py +520 -0
  404. package/plugin/scripts/city-env.sh +90 -0
  405. package/plugin/scripts/city-runtime.sh +87 -0
  406. package/plugin/scripts/city-session.sh +526 -0
  407. package/plugin/scripts/city_env.py +67 -0
  408. package/plugin/scripts/crecimiento.py +140 -0
  409. package/plugin/scripts/deliberations.py +48 -0
  410. package/plugin/scripts/doctor.py +172 -0
  411. package/plugin/scripts/domains.py +201 -0
  412. package/plugin/scripts/evidencia.py +64 -0
  413. package/plugin/scripts/find-repos.sh +111 -0
  414. package/plugin/scripts/gh.py +159 -0
  415. package/plugin/scripts/hall_protocol.py +147 -0
  416. package/plugin/scripts/hook_activity.py +139 -0
  417. package/plugin/scripts/launch.py +94 -0
  418. package/plugin/scripts/logs.py +98 -0
  419. package/plugin/scripts/pairing.py +185 -0
  420. package/plugin/scripts/parcels.py +132 -0
  421. package/plugin/scripts/read-card.py +51 -0
  422. package/plugin/scripts/report.py +201 -0
  423. package/plugin/scripts/reset.py +180 -0
  424. package/plugin/scripts/roads.py +230 -0
  425. package/plugin/scripts/roles.py +274 -0
  426. package/plugin/scripts/runtime_log.py +69 -0
  427. package/plugin/scripts/runtime_processes.py +132 -0
  428. package/plugin/scripts/rutas.py +83 -0
  429. package/plugin/scripts/seat.py +1349 -0
  430. package/plugin/scripts/tokens.py +194 -0
  431. package/plugin/scripts/trust-repos.py +59 -0
  432. package/plugin/scripts/ui.py +280 -0
  433. package/plugin/scripts/units.py +80 -0
  434. package/plugin/scripts/workspace.py +364 -0
  435. package/plugin/skills/city/SKILL.md +191 -0
  436. package/templates/blank.md +39 -0
  437. package/templates/finance.md +44 -0
  438. package/templates/legal.md +45 -0
  439. package/templates/marketing.md +48 -0
  440. package/templates/product.md +45 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * The oven: bakes 3D models into isometric sprites.
3
+ *
4
+ * The good CC0 city kits are all 3D, and live 3D reads worse than 2D once there
5
+ * are 150 buildings — always one hiding another. So each model is rendered ONCE,
6
+ * from the same angle with the same light, and what is left are PNGs that Pixi
7
+ * paints as sprites: game art with isometric legibility.
8
+ *
9
+ * Baked in light grey on purpose. The colour comes from Pixi's tint at paint
10
+ * time, so one model serves every district instead of baking one image per
11
+ * district per model.
12
+ *
13
+ * Runs in a headless Chrome; the script outside collects window.__horneado.
14
+ */
15
+ import * as THREE from 'three';
16
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
17
+
18
+ const LADO = 512; // room to zoom in without pixelating
19
+ const MODELOS: string[] = (window as any).__modelos ?? [];
20
+
21
+ const rnd = new THREE.WebGLRenderer({ antialias: true, alpha: true, preserveDrawingBuffer: true });
22
+ rnd.setSize(LADO, LADO);
23
+ rnd.setPixelRatio(1);
24
+ rnd.shadowMap.enabled = true;
25
+ rnd.shadowMap.type = THREE.PCFSoftShadowMap;
26
+ document.body.appendChild(rnd.domElement);
27
+
28
+ const esc = new THREE.Scene();
29
+
30
+ // Orthographic camera at the classic isometric angle: 45° of yaw and ~35.26° of
31
+ // pitch. That is what makes the sprites line up with the map's grid.
32
+ const cam = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 100);
33
+ const ang = Math.atan(Math.SQRT1_2); // 35,264°
34
+ cam.position.set(10, 10 * Math.tan(ang) * Math.SQRT2, 10);
35
+ cam.lookAt(0, 0, 0);
36
+
37
+ // One fixed light for all of them: if it changes between models, the city ends
38
+ // iluminados desde sitios distintos y se nota inmediatamente.
39
+ const sol = new THREE.DirectionalLight(0xfff3e0, 2.9);
40
+ sol.position.set(9, 14, 5);
41
+ sol.castShadow = true;
42
+ Object.assign(sol.shadow.camera, { left: -9, right: 9, top: 9, bottom: -9 });
43
+ sol.shadow.mapSize.set(1024, 1024);
44
+ esc.add(sol);
45
+ esc.add(new THREE.HemisphereLight(0xdfe8f5, 0x6b6f78, 1.35));
46
+ esc.add(new THREE.AmbientLight(0xffffff, 0.35));
47
+
48
+ const cargador = new GLTFLoader();
49
+ const cargar = (n: string) =>
50
+ new Promise<THREE.Object3D>((ok, mal) =>
51
+ cargador.load(`/glb/${n}.glb`, (g) => ok(g.scene), undefined, mal),
52
+ );
53
+
54
+ async function cuece() {
55
+ // The four numbers medidas.json carries, plus the image. Declared in full
56
+ // because the collector and Pixi both read every one of them.
57
+ const salida: {
58
+ nombre: string;
59
+ alto: number;
60
+ ancho: number;
61
+ png: string;
62
+ ancla: number;
63
+ pxUnidad: number;
64
+ }[] = [];
65
+
66
+ for (const nombre of MODELOS) {
67
+ const obj = await cargar(nombre);
68
+
69
+ // To light grey: the colour will come from Pixi's tint.
70
+ obj.traverse((o) => {
71
+ const m = o as THREE.Mesh;
72
+ if (!m.isMesh) return;
73
+ m.castShadow = true;
74
+ m.receiveShadow = true;
75
+ const mat = (m.material as THREE.MeshStandardMaterial).clone();
76
+ const l = mat.color.getHSL({ h: 0, s: 0, l: 0 }).l;
77
+ mat.color.setHSL(0, 0, 0.55 + l * 0.4); // conserva el contraste interno
78
+ mat.roughness = 0.72;
79
+ mat.metalness = 0.04;
80
+ m.material = mat;
81
+ });
82
+
83
+ // Centred on its base and fitted in frame, with the same margin for
84
+ // todos: si cada uno se encaja a su medida, en el mapa salen a escalas
85
+ // distintas y el city parece de juguetes mezclados.
86
+ const caja = new THREE.Box3().setFromObject(obj);
87
+ const tam = caja.getSize(new THREE.Vector3());
88
+ const centro = caja.getCenter(new THREE.Vector3());
89
+ obj.position.set(-centro.x, -caja.min.y, -centro.z);
90
+
91
+ const grupo = new THREE.Group();
92
+ grupo.add(obj);
93
+ esc.add(grupo);
94
+
95
+ const radio = 3.4; // the same for every model
96
+ cam.left = -radio;
97
+ cam.right = radio;
98
+ cam.bottom = -radio * 0.5;
99
+ cam.top = radio * 1.5;
100
+ cam.updateProjectionMatrix();
101
+ // Looking at the ground, not at the centre of the building: that way the y=0
102
+ // plane always lands on the same pixel and the sprite can be anchored to the
103
+ // tile. Point the camera at the middle of each model and every sprite has its
104
+ // base at a different height, so buildings float or sink on the map.
105
+ cam.lookAt(0, 0, 0);
106
+
107
+ rnd.render(esc, cam);
108
+ salida.push({
109
+ nombre,
110
+ alto: +tam.y.toFixed(3),
111
+ ancho: +Math.max(tam.x, tam.z).toFixed(3),
112
+ png: rnd.domElement.toDataURL('image/png').split(',')[1],
113
+ // Where the ground sits in the image, and how many pixels one model unit
114
+ // measures: with these two, Pixi places and scales without guessing.
115
+ ancla: +(cam.top / (cam.top - cam.bottom)).toFixed(4),
116
+ pxUnidad: +(LADO / (cam.right - cam.left)).toFixed(3),
117
+ });
118
+ esc.remove(grupo);
119
+ }
120
+
121
+ (window as any).__horneado = salida;
122
+ document.title = `baked ${salida.length}`;
123
+ }
124
+ cuece();
@@ -0,0 +1,244 @@
1
+ /**
2
+ * The people on site.
3
+ *
4
+ * The architect draws the plan, the foremen answer for a property, and the workers
5
+ * dig. A worker is ONE window of ONE person: the seat window, one per repo, and
6
+ * one per worktree — same repo, different branches, different workers.
7
+ *
8
+ * Careful what this counts: crew size says what is happening, not how well anyone
9
+ * is doing. More workers is more work open at once, not more work finished — what
10
+ * measures finishing is floors.
11
+ */
12
+ import { Container, Graphics, Text, TextStyle } from 'pixi.js';
13
+ import { TH, tono } from './draw';
14
+
15
+ const CREMA = 0xe8e2d4;
16
+
17
+ /**
18
+ * How big the people are drawn, against a 62×31 tile.
19
+ *
20
+ * They used to be drawn at 1: about thirteen pixels tall, which at the zoom
21
+ * where the whole city fits is a speck. A map about who is working cannot have
22
+ * the people be the least visible thing on it.
23
+ */
24
+ export const ESCALA_GENTE = 1.6;
25
+
26
+ /** A stable colour per person: the same foreman is always the same colour. */
27
+ const PALETA = [0xf5c451, 0x6ee7b7, 0xf59ec4, 0x93c5fd, 0xfca868, 0xc4b5fd, 0x86efac, 0xfda4af];
28
+ export const colorDe = (u: string) => {
29
+ let n = 0;
30
+ const s = String(u ?? ''); // a missing name is a colour, not a crash
31
+ for (let i = 0; i < s.length; i++) n = (n * 31 + s.charCodeAt(i)) | 0;
32
+ return PALETA[Math.abs(n) % PALETA.length];
33
+ };
34
+
35
+ function casco(col: number, r = 3.1) {
36
+ return new Graphics()
37
+ .ellipse(0, 0, r, r * 0.78)
38
+ .fill({ color: col })
39
+ .rect(-r - 1, -0.2, (r + 1) * 2, 1.1)
40
+ .fill({ color: tono(col, -0.25) });
41
+ }
42
+
43
+ /** A worker: digs, and every strike kicks up dust. */
44
+ export function obrero(usuario: string, agente: string, orden = 0) {
45
+ const c = new Container();
46
+ const col = colorDe(usuario);
47
+
48
+ c.addChild(new Graphics().ellipse(0, 1, 5, 2).fill({ color: 0x000000, alpha: 0.3 }));
49
+ c.addChild(
50
+ new Graphics()
51
+ .rect(-1.6, -11, 3.2, 11)
52
+ .fill({ color: CREMA, alpha: 0.95 })
53
+ .rect(-1.6, -5.5, 3.2, 1.4)
54
+ .fill({ color: tono(col, -0.3), alpha: 0.9 }),
55
+ ); // el chaleco
56
+ const cab = casco(col);
57
+ cab.position.set(0, -13);
58
+ c.addChild(cab);
59
+
60
+ const pico = new Graphics()
61
+ .moveTo(0, -8)
62
+ .lineTo(7, -13)
63
+ .stroke({ color: 0xd9cdb4, width: 1.7 })
64
+ .moveTo(6, -14.4)
65
+ .lineTo(9, -12)
66
+ .stroke({ color: 0x9aa7b8, width: 1.7 });
67
+ pico.pivot.set(0, -8);
68
+ c.addChild(pico);
69
+
70
+ // Dust: three motes that rise and fade on each strike. It is what makes
71
+ // "digging" read as digging rather than as a figure standing still.
72
+ const polvo: Graphics[] = [];
73
+ for (let i = 0; i < 3; i++) {
74
+ const p = new Graphics().circle(0, 0, 1.1 + i * 0.4).fill({ color: 0xd9cdb4, alpha: 0.5 });
75
+ p.visible = false;
76
+ polvo.push(p);
77
+ c.addChild(p);
78
+ }
79
+
80
+ let t = Math.random() * 6;
81
+ c.onRender = () => {
82
+ t += 0.14;
83
+ pico.rotation = Math.sin(t) * 0.6;
84
+ const golpe = Math.sin(t) > 0.94;
85
+ polvo.forEach((p, i) => {
86
+ if (golpe && !p.visible) {
87
+ p.visible = true;
88
+ p.position.set(7 + i * 2, -6);
89
+ p.alpha = 0.55;
90
+ }
91
+ if (p.visible) {
92
+ p.y -= 0.5;
93
+ p.x += 0.16;
94
+ p.alpha -= 0.035;
95
+ if (p.alpha <= 0) {
96
+ p.visible = false;
97
+ }
98
+ }
99
+ });
100
+ };
101
+
102
+ // Several workers at the same house: the labels stack vertically, or they sit
103
+ // on top of each other and none of them can be read.
104
+ const cartel = etiqueta(usuario, `/${agente}`, col);
105
+ cartel.position.set(0, -28 - orden * 15);
106
+ c.addChild(cartel);
107
+ (c as any).__cartel = cartel;
108
+ (c as any).__agente = (cartel as any).__segunda;
109
+ return acaba(c, usuario, cartel);
110
+ }
111
+
112
+ /**
113
+ * Foreman: answers for a property and carries a crew. Drawn with a clipboard
114
+ * instead of a pick — they do not dig — and with their current crew size.
115
+ */
116
+ export function perito(usuario: string, oficio: string, cuantos?: number) {
117
+ const c = new Container();
118
+ const col = colorDe(usuario);
119
+
120
+ c.addChild(new Graphics().ellipse(0, 1, 6, 2.4).fill({ color: 0x000000, alpha: 0.32 }));
121
+ c.addChild(
122
+ new Graphics()
123
+ .rect(-2, -14, 4, 14)
124
+ .fill({ color: CREMA })
125
+ .rect(-2, -8, 4, 2)
126
+ .fill({ color: tono(col, -0.2) }),
127
+ );
128
+ const cab = casco(col, 3.6);
129
+ cab.position.set(0, -16.5);
130
+ c.addChild(cab);
131
+ // the clipboard
132
+ c.addChild(
133
+ new Graphics()
134
+ .rect(2.4, -10, 5, 6.5)
135
+ .fill({ color: 0xf2ecdd })
136
+ .rect(2.4, -10, 5, 1.2)
137
+ .fill({ color: tono(col, -0.3) }),
138
+ );
139
+
140
+ // No crew, no counter: a "×0" says nothing and adds noise.
141
+ const cartel = etiqueta(usuario, oficio, col, cuantos && cuantos > 0 ? cuantos : undefined);
142
+ cartel.position.set(0, -32);
143
+ c.addChild(cartel);
144
+ (c as any).__cartel = cartel;
145
+ (c as any).__agente = (cartel as any).__segunda;
146
+ return acaba(c, usuario, cartel);
147
+ }
148
+
149
+ /** The architect: does not dig and has no crew. Carries the plan. */
150
+ export function arquitecto(usuario: string) {
151
+ const c = new Container();
152
+ const col = colorDe(usuario);
153
+ c.addChild(new Graphics().ellipse(0, 1, 7, 2.8).fill({ color: 0x000000, alpha: 0.34 }));
154
+ c.addChild(new Graphics().rect(-2.2, -16, 4.4, 16).fill({ color: CREMA }));
155
+ const cab = casco(0xffffff, 4);
156
+ cab.position.set(0, -18.5);
157
+ c.addChild(cab);
158
+ // the plan under one arm
159
+ c.addChild(
160
+ new Graphics()
161
+ .rect(-9, -11, 9, 2.6)
162
+ .fill({ color: 0xf2ecdd })
163
+ .rect(-9, -11, 2, 2.6)
164
+ .fill({ color: col }),
165
+ );
166
+ const cartel = etiqueta(usuario, 'Architect', col);
167
+ cartel.position.set(0, -34);
168
+ c.addChild(cartel);
169
+ (c as any).__cartel = cartel;
170
+ (c as any).__agente = (cartel as any).__segunda;
171
+ return acaba(c, usuario, cartel);
172
+ }
173
+
174
+ /**
175
+ * What every figure gets before it leaves here: the size they are drawn at, a
176
+ * generous hit area — a person is a dozen pixels wide and a mouse is not — and
177
+ * the user they belong to, so a click can open their card.
178
+ *
179
+ * The label is scaled back down: at 1.6 the type would be bigger than the
180
+ * banners, and the figure has to read as a person, not as a sign.
181
+ */
182
+ function acaba(c: Container, usuario: string, cartel: Container) {
183
+ c.scale.set(ESCALA_GENTE);
184
+ cartel.scale.set(1 / ESCALA_GENTE);
185
+ c.eventMode = 'static';
186
+ c.cursor = 'pointer';
187
+ (c as any).__usuario = usuario;
188
+ return c;
189
+ }
190
+
191
+ /** A two-part label: who, and what they are. Plus a count if they have a crew. */
192
+ function etiqueta(principal: string, segunda: string, col: number, cuantos?: number) {
193
+ const c = new Container();
194
+ const a = new Text({
195
+ text: principal,
196
+ style: new TextStyle({
197
+ fontFamily: 'IBM Plex Mono, monospace',
198
+ fontSize: 9,
199
+ fontWeight: '500',
200
+ fill: 0x131a24,
201
+ }),
202
+ });
203
+ const b = new Text({
204
+ text: segunda,
205
+ style: new TextStyle({
206
+ fontFamily: 'IBM Plex Mono, monospace',
207
+ fontSize: 9,
208
+ fill: 0x131a24,
209
+ }),
210
+ });
211
+ b.alpha = 0.7;
212
+ const extra =
213
+ cuantos !== undefined
214
+ ? new Text({
215
+ text: `×${cuantos}`,
216
+ style: new TextStyle({
217
+ fontFamily: 'Chakra Petch, sans-serif',
218
+ fontSize: 10,
219
+ fontWeight: '700',
220
+ fill: 0x131a24,
221
+ }),
222
+ })
223
+ : null;
224
+
225
+ const ancho = a.width + b.width + 3 + (extra ? extra.width + 7 : 0);
226
+ a.position.set(-ancho / 2, -6.5);
227
+ b.position.set(-ancho / 2 + a.width + 3, -6.5);
228
+ const fondo = new Graphics()
229
+ .roundRect(-ancho / 2 - 5, -8.5, ancho + 10, 15, 2.5)
230
+ .fill({ color: col, alpha: 0.94 });
231
+ c.addChild(fondo, a, b);
232
+ if (extra) {
233
+ extra.position.set(ancho / 2 - extra.width, -7);
234
+ c.addChild(
235
+ new Graphics()
236
+ .roundRect(ancho / 2 - extra.width - 4, -8.5, extra.width + 9, 15, 2.5)
237
+ .fill({ color: tono(col, -0.4), alpha: 0.95 }),
238
+ );
239
+ extra.style.fill = 0xffffff;
240
+ c.addChild(extra);
241
+ }
242
+ (c as any).__segunda = b;
243
+ return c;
244
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Live presence: the lights say what is happening NOW.
3
+ *
4
+ * The windows a house is born with come from thirty days of history — a photo.
5
+ * This layer is the pulse on top of it: a house whose agent is mid-turn glows
6
+ * and breathes, one that just stopped cools down over a couple of minutes, and
7
+ * one with no session shows nothing extra. All of it is derived from events the
8
+ * feed already emits — a turn is `conversation.user` with no `conversation.agent`
9
+ * after it — so the map never claims more than the hooks reported.
10
+ *
11
+ * Houses and worker figures are rebuilt constantly (the replay redraws houses,
12
+ * the crew reconciles every state push), so nothing here holds a display object
13
+ * as truth: each frame re-resolves where the glow and the dots should live and
14
+ * re-parents them when the world changed underneath.
15
+ */
16
+ import { Container, Graphics } from 'pixi.js';
17
+ import { TH, TW } from './draw';
18
+ import type { ActivityEvent } from './activity';
19
+
20
+ const VENTANA = 0xffd98a;
21
+
22
+ /** The one slice of the map's house record this layer needs. */
23
+ export interface CasaEnVivo {
24
+ g: Container;
25
+ h?: { alto: number };
26
+ }
27
+
28
+ type Fase = 'turno' | 'enfria';
29
+
30
+ interface Estado {
31
+ fase: Fase;
32
+ actores: Set<string>;
33
+ halo: Graphics;
34
+ puntos: Graphics[];
35
+ puntosEn: Container | null;
36
+ t: number; // the breath
37
+ resto: number; // cooling time left, in ticker units
38
+ }
39
+
40
+ /** Cooling lasts about two minutes at 60fps: long enough that "it just stopped"
41
+ * is visible, short enough that an evening of stopped agents reads as quiet. */
42
+ const ENFRIAMIENTO = 60 * 120;
43
+
44
+ export class Presencia {
45
+ private readonly casas = new Map<string, Estado>();
46
+ private readonly actorCasa = new Map<string, string>();
47
+
48
+ constructor(
49
+ private readonly vivaDe: (actor: string) => { id: string; casa: CasaEnVivo } | null,
50
+ private readonly porId: (id: string) => CasaEnVivo | null,
51
+ private readonly figuraDe: (actor: string) => Container | null,
52
+ private readonly sinMovimiento: boolean,
53
+ ) {}
54
+
55
+ recibe(evento: ActivityEvent): void {
56
+ const actor = evento.actor.trim();
57
+ if (!actor || actor === 'system') return;
58
+ if (evento.kind === 'conversation.user') this.empieza(actor);
59
+ else if (evento.kind === 'conversation.agent') this.para(actor, false);
60
+ else if (evento.kind === 'runtime.session.ended') this.para(actor, true);
61
+ }
62
+
63
+ /** How many houses are mid-turn right now — the HUD's number. */
64
+ enTurno(): number {
65
+ let n = 0;
66
+ for (const e of this.casas.values()) if (e.fase === 'turno') n++;
67
+ return n;
68
+ }
69
+
70
+ private empieza(actor: string): void {
71
+ const viva = this.vivaDe(actor);
72
+ if (!viva) return;
73
+ this.actorCasa.set(actor, viva.id);
74
+ let estado = this.casas.get(viva.id);
75
+ if (!estado) {
76
+ const halo = new Graphics();
77
+ halo.blendMode = 'add';
78
+ estado = {
79
+ fase: 'turno',
80
+ actores: new Set(),
81
+ halo,
82
+ puntos: [],
83
+ puntosEn: null,
84
+ t: 0,
85
+ resto: 0,
86
+ };
87
+ this.casas.set(viva.id, estado);
88
+ }
89
+ estado.fase = 'turno';
90
+ estado.actores.add(actor);
91
+ }
92
+
93
+ private para(actor: string, seco: boolean): void {
94
+ const id = this.actorCasa.get(actor);
95
+ if (!id) return;
96
+ const estado = this.casas.get(id);
97
+ if (!estado) return;
98
+ estado.actores.delete(actor);
99
+ if (estado.actores.size > 0) return; // somebody else is still mid-turn here
100
+ if (seco) {
101
+ // The session died: no afterglow for a light that was switched off.
102
+ this.apaga(id, estado);
103
+ return;
104
+ }
105
+ estado.fase = 'enfria';
106
+ estado.resto = ENFRIAMIENTO;
107
+ }
108
+
109
+ /** One tick for the whole layer, from the map's single ticker. */
110
+ tick(dt: number): void {
111
+ for (const [id, estado] of this.casas) {
112
+ const viva = this.porId(id);
113
+ if (!viva) {
114
+ this.apaga(id, estado);
115
+ continue;
116
+ }
117
+ const alto = viva.h?.alto ?? 40;
118
+
119
+ // The world redraws houses under us; the glow follows its house.
120
+ if (estado.halo.parent !== viva.g) {
121
+ estado.halo.parent?.removeChild(estado.halo);
122
+ estado.halo
123
+ .clear()
124
+ .ellipse(0, TH / 2 - alto * 0.45, TW * 0.32, alto * 0.46)
125
+ .fill({ color: VENTANA, alpha: 1 });
126
+ viva.g.addChild(estado.halo);
127
+ }
128
+
129
+ if (estado.fase === 'turno') {
130
+ estado.t += 0.042 * dt;
131
+ estado.halo.alpha = this.sinMovimiento ? 0.24 : 0.16 + (Math.sin(estado.t) + 1) * 0.07;
132
+ } else {
133
+ estado.resto -= dt;
134
+ if (estado.resto <= 0 || this.sinMovimiento) {
135
+ this.apaga(id, estado);
136
+ continue;
137
+ }
138
+ estado.halo.alpha = 0.18 * (estado.resto / ENFRIAMIENTO);
139
+ }
140
+
141
+ this.puntea(estado, viva, alto);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * The thinking dots: three motes breathing over whoever is mid-turn. Over the
147
+ * worker figure when the crew feed knows one, over the house's roof when it
148
+ * does not — the signal must not depend on a second feed agreeing.
149
+ */
150
+ private puntea(estado: Estado, viva: CasaEnVivo, alto: number): void {
151
+ const pensando = estado.fase === 'turno';
152
+ if (!pensando) {
153
+ this.quitaPuntos(estado);
154
+ return;
155
+ }
156
+ const actor = [...estado.actores][0] ?? '';
157
+ const figura = this.figuraDe(actor);
158
+ const destino = figura ?? viva.g;
159
+ if (estado.puntosEn !== destino) {
160
+ this.quitaPuntos(estado);
161
+ for (let i = 0; i < 3; i++) {
162
+ const p = new Graphics().circle(0, 0, 1.4).fill({ color: 0xe8e2d4 });
163
+ if (figura) p.position.set(-4 + i * 4, -19);
164
+ else p.position.set(-5 + i * 5, TH / 2 - alto - 9);
165
+ destino.addChild(p);
166
+ estado.puntos.push(p);
167
+ }
168
+ estado.puntosEn = destino;
169
+ }
170
+ estado.puntos.forEach((p, i) => {
171
+ p.alpha = this.sinMovimiento
172
+ ? 0.8
173
+ : 0.25 + Math.max(0, Math.sin(estado.t * 1.6 - i * 0.9)) * 0.75;
174
+ });
175
+ }
176
+
177
+ private quitaPuntos(estado: Estado): void {
178
+ for (const p of estado.puntos) {
179
+ p.parent?.removeChild(p);
180
+ p.destroy();
181
+ }
182
+ estado.puntos = [];
183
+ estado.puntosEn = null;
184
+ }
185
+
186
+ private apaga(id: string, estado: Estado): void {
187
+ estado.halo.parent?.removeChild(estado.halo);
188
+ estado.halo.destroy();
189
+ this.quitaPuntos(estado);
190
+ this.casas.delete(id);
191
+ for (const [actor, casa] of this.actorCasa) if (casa === id) this.actorCasa.delete(actor);
192
+ }
193
+ }