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,312 @@
1
+ #!/usr/bin/env python3
2
+ """The one reader and writer of a person's card.
3
+
4
+ A card is markdown with frontmatter. It is the only thing that says who somebody
5
+ is, what they answer for and what they are aiming at, and it is read by the tmux
6
+ session, the seat, the wizard, the repo picker, the seeder and every round.
7
+
8
+ There were five ways to read it and three to write it, in one repo:
9
+
10
+ * `read-card.py`, a CLI, one field at a time, by subprocess
11
+ * a repo picker with its own frontmatter parse, for `repos` only — which nothing
12
+ ever called, and which is gone
13
+ * `bin/seat`, all three of the above plus two loose regexes of its own
14
+ * `bin/setup.py` and `bin/seat` each writing the whole card their own way
15
+
16
+ That is how the seat window ended up reading a field called `agente` while every
17
+ card in the repo carries `agent`, and how a card written by one door came out
18
+ different from the same card written by the other. One file, so there is one answer.
19
+
20
+ Here, and not next to the wizard, for the same reason as `parcels.py`: this
21
+ directory is what gets installed on everybody's machine.
22
+ """
23
+ import re
24
+
25
+ # The frontmatter fields, in the order they are written. Order is not cosmetic — a
26
+ # card is read by people, and a stable order means a diff shows what changed rather
27
+ # than that something moved.
28
+ CAMPOS = ('user', 'name', 'role', 'agent', 'repos', 'goals_defined')
29
+ ROLE_ID = re.compile(r'^[a-z0-9][a-z0-9-]{0,63}$')
30
+
31
+
32
+ def frontmatter(texto):
33
+ """The frontmatter block of a card, or '' if it has none."""
34
+ if not texto.startswith('---'):
35
+ return ''
36
+ partes = texto.split('---')
37
+ return partes[1] if len(partes) > 2 else ''
38
+
39
+
40
+ def lista(valor):
41
+ """`[a, "b", 'c']` -> ['a', 'b', 'c']. Quotes are optional in these files."""
42
+ return [x.strip().strip('"\'') for x in valor.strip().strip('[]').split(',') if x.strip()]
43
+
44
+
45
+ def campo(texto, clave, defecto=''):
46
+ """One frontmatter field, by name.
47
+
48
+ `agent` also answers to `agente`. Every card in this repo says `agent`, but a
49
+ hand-written one may not, and the version of this that only accepted `agente`
50
+ silently returned nothing for all twelve demo cards.
51
+ """
52
+ fm = frontmatter(texto)
53
+ if not fm:
54
+ return defecto
55
+ for nombre in ((clave, 'agente') if clave == 'agent' else (clave,)):
56
+ m = re.search(rf'^{re.escape(nombre)}:[ \t]*(.*)$', fm, re.M)
57
+ if m and m.group(1).strip():
58
+ return m.group(1).strip()
59
+ return defecto
60
+
61
+
62
+ def lee(ruta):
63
+ """A whole card as a dict, including its goal. Missing file reads as empty."""
64
+ try:
65
+ with open(ruta, encoding='utf-8') as f:
66
+ texto = f.read()
67
+ except (OSError, IsADirectoryError):
68
+ return {}
69
+ usuario = campo(texto, 'user')
70
+ repos = lista(campo(texto, 'repos'))
71
+ return {
72
+ 'user': usuario,
73
+ 'name': campo(texto, 'name', usuario),
74
+ 'role': campo(texto, 'role', 'dev'),
75
+ 'agent': campo(texto, 'agent'),
76
+ 'repos': repos,
77
+ 'repo_roles': roles_repos(texto, repos),
78
+ 'goals_defined': campo(texto, 'goals_defined') == 'true',
79
+ 'objetivo': objetivo(texto, usuario),
80
+ 'texto': texto,
81
+ }
82
+
83
+
84
+ def rol_seguro(valor, defecto='blank'):
85
+ """One inert role id suitable for a card key, environment and prompt.
86
+
87
+ Role text is configuration, never a shell fragment. A hand-edited malformed
88
+ value therefore degrades to the explicit blank role rather than crossing the
89
+ runtime boundary verbatim.
90
+ """
91
+ valor = str(valor or '').strip().lower()
92
+ return valor if ROLE_ID.fullmatch(valor) else defecto
93
+
94
+
95
+ def roles_repos(texto, repos=None):
96
+ """``{original repo name: operating role}`` from dynamic ``role.<actor>`` keys.
97
+
98
+ Old cards have no such keys. Returning ``blank`` is intentional: it preserves
99
+ the old behaviour without pretending that Agents City knew why that repo was
100
+ present. The key suffix uses the same canonical actor/window slug as tmux and
101
+ the bus.
102
+ """
103
+ repos = list(repos if repos is not None else lista(campo(texto, 'repos')))
104
+ return {repo: rol_seguro(campo(texto, f'role.{ventana(repo)}')) for repo in repos}
105
+
106
+
107
+ def normaliza_roles_repos(repos, asignados=None):
108
+ """Validate one explicit role per selected repo and reject actor collisions."""
109
+ asignados = dict(asignados or {})
110
+ fuera, actores = {}, {}
111
+ limpios = [str(r).strip() for r in repos if str(r).strip()]
112
+ for repo in sorted(dict.fromkeys(limpios)):
113
+ actor = ventana(repo)
114
+ if actor in actores and actores[actor] != repo:
115
+ raise ValueError(
116
+ f"repo names {actores[actor]!r} and {repo!r} collide as agent {actor!r}"
117
+ )
118
+ actores[actor] = repo
119
+ crudo = asignados.get(repo, asignados.get(actor, 'blank'))
120
+ rol = str(crudo or 'blank').strip().lower()
121
+ if not ROLE_ID.fullmatch(rol):
122
+ raise ValueError(f"invalid role {crudo!r} for repo {repo!r}")
123
+ fuera[repo] = rol
124
+ return fuera
125
+
126
+
127
+ def linea_rol(rol, oficio=''):
128
+ """The one human-readable role line shared by new cards and surgery."""
129
+ if rol == 'blank':
130
+ return 'Role: **blank** — no predefined seat responsibility or role knowledge.'
131
+ nombre = (oficio or rol.replace('-', ' ')).lower()
132
+ return (
133
+ f'Role: **{rol}** — in the city, the **{nombre}**. '
134
+ f'Role knowledge, when present: `roles/{rol}.md`.'
135
+ )
136
+
137
+
138
+ def objetivo(texto, usuario=''):
139
+ """The first goal off a card, in the shape both writers emit."""
140
+ if '### O1 —' not in texto:
141
+ return None
142
+ m = re.search(r'^### O1 — (.+)$', texto, re.M)
143
+ titulo = m.group(1).strip() if m else ''
144
+ if not titulo:
145
+ return None
146
+
147
+ def linea(etiqueta, defecto=''):
148
+ m = re.search(rf'^- \*\*{etiqueta}\*\*:\s*`?(.+?)`?\.?$', texto, re.M)
149
+ return m.group(1).strip() if m else defecto
150
+
151
+ medida = linea('Measure')
152
+ # A measure is one of two honest things: a command that returns a number, or a
153
+ # person's judgement written down — "the architect reads the AGENTS.md files on
154
+ # Fridays". The second is not a lesser goal; plenty of quality is prose. It
155
+ # round-trips as `manual`, never as a command a round might try to execute.
156
+ manual = ''
157
+ if medida.startswith('manual —'):
158
+ manual = medida[len('manual —'):].strip()
159
+ if manual == 'who looks at what, and how often':
160
+ manual = ''
161
+ medida = ''
162
+ return {'user': usuario, 'title': titulo,
163
+ 'signal': linea('How it is measured'),
164
+ 'command': medida, 'manual': manual,
165
+ 'baseline': linea('Baseline'), 'target': linea('Target'),
166
+ 'by': linea('By when', 'this quarter')}
167
+
168
+
169
+ def bloque_objetivo(obj):
170
+ """The goal, as the lines that go on a card. One emitter, so a round reads the
171
+ same shape whichever door wrote it."""
172
+ if not obj:
173
+ return ['> **Pending.** A round works without them, but it cannot contrast '
174
+ 'the work against anything.', '',
175
+ '> To set one: `./bin/seat --goal`, or `/city:goals`.', '']
176
+ medida = obj.get('command') or ('manual — ' + (
177
+ obj.get('manual') or 'who looks at what, and how often'))
178
+ return [f"### O1 — {obj['title']}",
179
+ f"- **What**: {obj['title']}.",
180
+ f"- **How it is measured**: {obj.get('signal') or 'to be defined'}.",
181
+ f"- **Measure**: `{medida}`",
182
+ f"- **Baseline**: {obj.get('baseline') or 'not measured yet'}",
183
+ f"- **Target**: {obj.get('target') or 'to be defined'}",
184
+ f"- **By when**: {obj.get('by') or 'this quarter'}",
185
+ '- **State**: in progress', '']
186
+
187
+
188
+ def _cambia_seccion(texto, titulo, lineas_nuevas):
189
+ """Replace one `##` section's body, touching nothing else on the card."""
190
+ patron = re.compile(rf'(^## {re.escape(titulo)}\n)(.*?)(?=^## |\Z)', re.M | re.S)
191
+ if not patron.search(texto):
192
+ return None
193
+ cuerpo = '\n' + '\n'.join(lineas_nuevas).rstrip('\n') + '\n\n'
194
+ return patron.sub(lambda m: m.group(1) + cuerpo, texto, count=1)
195
+
196
+
197
+ def cambia_objetivo(ruta, obj):
198
+ """Rewrite only the goals section — and the `goals_defined` flag with it.
199
+
200
+ Surgical on purpose: the card also carries the round history, and the first
201
+ version of "change your goal" rewrote the whole card from a template, wiping
202
+ every round summary anyone had left there. Changing what you aim at must not
203
+ erase what already happened.
204
+
205
+ `obj` of None clears the goal back to pending. Returns False when the card has
206
+ no goals section to replace.
207
+ """
208
+ with open(ruta, encoding='utf-8') as f:
209
+ texto = f.read()
210
+ nuevo = _cambia_seccion(texto, 'Current goals', bloque_objetivo(obj))
211
+ if nuevo is None:
212
+ return False
213
+ bandera = 'true' if obj else 'false'
214
+ nuevo = re.sub(r'^goals_defined:.*$', f'goals_defined: {bandera}', nuevo,
215
+ count=1, flags=re.M)
216
+ with open(ruta, 'w', encoding='utf-8') as f:
217
+ f.write(nuevo)
218
+ return True
219
+
220
+
221
+ def cambia_rol(ruta, rol, agente, oficio=''):
222
+ """Rewrite the role — frontmatter, agent name, and the one body line that says
223
+ it out loud, so the card does not end up claiming two different roles."""
224
+ with open(ruta, encoding='utf-8') as f:
225
+ texto = f.read()
226
+ nuevo, n = re.subn(r'^role:.*$', f'role: {rol}', texto, count=1, flags=re.M)
227
+ if n == 0:
228
+ return False
229
+ nuevo = re.sub(r'^agent:.*$', f'agent: {agente}', nuevo, count=1, flags=re.M)
230
+ if oficio or rol == 'blank':
231
+ nuevo = re.sub(r'^Role:.*$', linea_rol(rol, oficio), nuevo, count=1, flags=re.M)
232
+ with open(ruta, 'w', encoding='utf-8') as f:
233
+ f.write(nuevo)
234
+ return True
235
+
236
+
237
+ def ventana(repo):
238
+ """The tmux window a repo becomes — which doubles as the engine-key suffix
239
+ on the card (`model.<window>`, `runs.<window>`), so the charset stays
240
+ key-safe and valid as a bus actor. A worktree `MiApp@feature/X` becomes
241
+ `miapp-feature-x`. Shell callers delegate here through read-card.py."""
242
+ limpio = re.sub(r'[^a-z0-9-]+', '-', str(repo).lower()).strip('-')
243
+ return (limpio or 'repo')[:80]
244
+
245
+
246
+ def pon_campo(ruta, clave, valor):
247
+ """Set one frontmatter field, surgically: replace it if it exists, add it just
248
+ before the closing --- if not, remove it when the value is empty.
249
+
250
+ This is how a card carries the engine its agents start with — `model:` and
251
+ `effort:` as the person's default, `model.<window>:` / `effort.<window>:` to
252
+ tune one window ("the docs repo runs on haiku, the monorepo on opus"). The
253
+ session script resolves specific-then-default, and an absent key means
254
+ "whatever this person's Claude defaults to", which is the right silence.
255
+ """
256
+ if not re.match(r'^[a-z][a-z0-9._-]*$', clave):
257
+ return False
258
+ with open(ruta, encoding='utf-8') as f:
259
+ texto = f.read()
260
+ if not texto.startswith('---'):
261
+ return False
262
+ patron = re.compile(rf'^{re.escape(clave)}:.*\n', re.M)
263
+ fin = texto.index('---', 3)
264
+ if valor == '' or valor is None:
265
+ nuevo = patron.sub('', texto[:fin], count=1) + texto[fin:]
266
+ elif patron.search(texto[:fin]):
267
+ nuevo = patron.sub(f'{clave}: {valor}\n', texto[:fin], count=1) + texto[fin:]
268
+ else:
269
+ nuevo = texto[:fin] + f'{clave}: {valor}\n' + texto[fin:]
270
+ with open(ruta, 'w', encoding='utf-8') as f:
271
+ f.write(nuevo)
272
+ return True
273
+
274
+
275
+ def cambia_repos(ruta, elegidos):
276
+ """Rewrite only the `repos` field of an existing card, leaving the rest alone.
277
+
278
+ Used by the repo picker, which has no business rewriting somebody's goals to
279
+ change which folders they answer for.
280
+ """
281
+ with open(ruta, encoding='utf-8') as f:
282
+ texto = f.read()
283
+ linea = 'repos: [' + ', '.join(sorted(elegidos)) + ']'
284
+ nuevo, n = re.subn(r'^repos:.*$', linea, texto, count=1, flags=re.M)
285
+ if n == 0:
286
+ return False
287
+ with open(ruta, 'w', encoding='utf-8') as f:
288
+ f.write(nuevo)
289
+ return True
290
+
291
+
292
+ def cambia_roles_repos(ruta, elegidos, asignados):
293
+ """Atomically persist one explicit operating role per selected repo.
294
+
295
+ Stale ``role.<actor>`` keys are removed when a repo leaves the city. Other
296
+ per-window settings (model, effort and runtime) remain untouched.
297
+ """
298
+ roles = normaliza_roles_repos(elegidos, asignados)
299
+ with open(ruta, encoding='utf-8') as f:
300
+ texto = f.read()
301
+ if not texto.startswith('---'):
302
+ return False
303
+ fin = texto.find('---', 3)
304
+ if fin < 0:
305
+ return False
306
+ cabeza = re.sub(r'^role\.[a-z0-9._-]+:.*\n', '', texto[:fin], flags=re.M)
307
+ lineas = ''.join(f'role.{ventana(repo)}: {rol}\n' for repo, rol in roles.items())
308
+ if lineas:
309
+ cabeza += lineas
310
+ with open(ruta, 'w', encoding='utf-8') as f:
311
+ f.write(cabeza + texto[fin:])
312
+ return True