@hybridlabor-api/aos 4.6.6 → 4.7.0

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 (484) hide show
  1. package/.agents/agents.md +1 -0
  2. package/.agents/nodes.json +5 -1
  3. package/.agents/state.schema.json +2 -1
  4. package/.agents/vendor-manifest.json +50 -0
  5. package/.claude/agents/architect.md +4 -0
  6. package/.claude/agents/security-reviewer.md +1 -1
  7. package/.claude/hooks/memb-inject.mjs +257 -135
  8. package/.claude/hooks/trail-relay.mjs +18 -0
  9. package/.claude/settings.json +70 -0
  10. package/.claude/workflows/startcycle-dispatch.mjs +29 -1
  11. package/.opencode/agents/architect.md +4 -0
  12. package/.opencode/plugins/bdb-aos.js +75 -0
  13. package/THIRD_PARTY_NOTICES.md +48 -0
  14. package/bin/aos-uninstall.mjs +55 -1
  15. package/docs/handover/agenttrail-for-ao.md +58 -0
  16. package/docs/handover/deja-for-ao.md +51 -0
  17. package/installer.js +141 -37
  18. package/mcp_config.json +4 -0
  19. package/mcps/mcsc/README.md +24 -0
  20. package/mcps/mcsc/package-lock.json +1234 -0
  21. package/mcps/mcsc/package.json +14 -0
  22. package/mcps/mcsc/packages/core/bin/mcsc.js +162 -0
  23. package/mcps/mcsc/packages/core/rulebook.default.yaml +49 -0
  24. package/mcps/mcsc/packages/core/schema/rulebook.schema.json +56 -0
  25. package/mcps/mcsc/packages/core/src/adapters/agy.js +107 -0
  26. package/mcps/mcsc/packages/core/src/adapters/codex.js +56 -0
  27. package/mcps/mcsc/packages/core/src/adapters/native.js +14 -0
  28. package/mcps/mcsc/packages/core/src/adapters/opencode.js +183 -0
  29. package/mcps/mcsc/packages/core/src/adapters/types.js +21 -0
  30. package/mcps/mcsc/packages/core/src/capabilities/buildOfferTable.js +34 -0
  31. package/mcps/mcsc/packages/core/src/capabilities/registry.js +99 -0
  32. package/mcps/mcsc/packages/core/src/capabilities/types.js +24 -0
  33. package/mcps/mcsc/packages/core/src/detect/cache.js +47 -0
  34. package/mcps/mcsc/packages/core/src/detect/inventory.js +73 -0
  35. package/mcps/mcsc/packages/core/src/detect/probes.js +158 -0
  36. package/mcps/mcsc/packages/core/src/detect/types.js +26 -0
  37. package/mcps/mcsc/packages/core/src/rulebook/load.js +53 -0
  38. package/mcps/mcsc/packages/core/src/rulebook/resolve.js +227 -0
  39. package/mcps/mcsc/packages/core/src/rulebook/rulebook.types.js +89 -0
  40. package/mcps/mcsc/packages/core/src/trail.js +17 -0
  41. package/mcps/mcsc/packages/mcp/server.js +302 -0
  42. package/mcps/mcsc/server.js +2 -0
  43. package/package.json +3 -1
  44. package/skills/basic/bdbmediastorm/SKILL.md +1 -1
  45. package/skills/basic/godmode-ui-ux/SKILL.md +2 -2
  46. package/skills/basic/godmode-ui-ux/references/anti-slop-rules.md +44 -40
  47. package/skills/basic/godmode-ui-ux/references/taste-archetypes.md +49 -0
  48. package/skills/basic/startcycle/SKILL.md +7 -0
  49. package/skills/basic/startcycle-graph/SKILL.md +12 -0
  50. package/skills/basic/startcycle-graph-user/SKILL.md +3 -0
  51. package/skills/basic/teamwork-preview/SKILL.md +2 -0
  52. package/skills/bdbrainstorm/SKILL.md +1 -0
  53. package/skills/global_config/agenttrail/LICENSE +21 -0
  54. package/skills/global_config/agenttrail/SKILL.md +48 -0
  55. package/skills/global_config/agenttrail/bin/agenttrail.mjs +1002 -0
  56. package/skills/global_config/agenttrail/public/index.html +661 -0
  57. package/skills/global_config/aos-project-init/SKILL.md +25 -5
  58. package/skills/global_config/aos-setup/SKILL.md +34 -1
  59. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +115 -1
  60. package/skills/global_config/archify/LICENSE +22 -0
  61. package/skills/global_config/archify/SKILL.md +140 -0
  62. package/skills/global_config/archify/THIRD_PARTY_NOTICES.md +69 -0
  63. package/skills/global_config/archify/assets/JetBrainsMono-OFL.txt +93 -0
  64. package/skills/global_config/archify/assets/template.html +14937 -0
  65. package/skills/global_config/archify/bin/archify.mjs +2139 -0
  66. package/skills/global_config/archify/bin/open-artifact.mjs +86 -0
  67. package/skills/global_config/archify/bin/preview.mjs +653 -0
  68. package/skills/global_config/archify/bin/visual-check.mjs +829 -0
  69. package/skills/global_config/archify/brand-marks/README.md +31 -0
  70. package/skills/global_config/archify/brand-marks/catalog.json +131 -0
  71. package/skills/global_config/archify/delta/architecture-delta.mjs +1232 -0
  72. package/skills/global_config/archify/examples/agent-run.lifecycle.json +60 -0
  73. package/skills/global_config/archify/examples/agent-tool-call.workflow.json +94 -0
  74. package/skills/global_config/archify/examples/async-job-roundtrip.sequence.json +61 -0
  75. package/skills/global_config/archify/examples/brand-aware-delivery.architecture.json +47 -0
  76. package/skills/global_config/archify/examples/cache-miss-request.sequence.json +82 -0
  77. package/skills/global_config/archify/examples/checkout-platform.base.architecture.json +31 -0
  78. package/skills/global_config/archify/examples/checkout-platform.head.architecture.json +31 -0
  79. package/skills/global_config/archify/examples/deployment-release.lifecycle.json +49 -0
  80. package/skills/global_config/archify/examples/event-stream.dataflow.json +57 -0
  81. package/skills/global_config/archify/examples/incident-response.workflow.json +64 -0
  82. package/skills/global_config/archify/examples/product-analytics.dataflow.json +76 -0
  83. package/skills/global_config/archify/examples/production-deployment.architecture.json +71 -0
  84. package/skills/global_config/archify/examples/release-delivery.workflow.json +62 -0
  85. package/skills/global_config/archify/examples/web-app.architecture.json +46 -0
  86. package/skills/global_config/archify/migrations/workflow-v2.mjs +279 -0
  87. package/skills/global_config/archify/package.json +39 -0
  88. package/skills/global_config/archify/recipes/scenarios.mjs +391 -0
  89. package/skills/global_config/archify/references/authoring-contract.md +247 -0
  90. package/skills/global_config/archify/references/brand-marks.md +65 -0
  91. package/skills/global_config/archify/references/delivery-contract.md +138 -0
  92. package/skills/global_config/archify/references/viewer-runtime.md +45 -0
  93. package/skills/global_config/archify/renderers/architecture/grid.mjs +62 -0
  94. package/skills/global_config/archify/renderers/architecture/render-architecture.mjs +1078 -0
  95. package/skills/global_config/archify/renderers/dataflow/README.md +104 -0
  96. package/skills/global_config/archify/renderers/dataflow/render-dataflow.mjs +483 -0
  97. package/skills/global_config/archify/renderers/lifecycle/README.md +115 -0
  98. package/skills/global_config/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
  99. package/skills/global_config/archify/renderers/sequence/README.md +114 -0
  100. package/skills/global_config/archify/renderers/sequence/render-sequence.mjs +464 -0
  101. package/skills/global_config/archify/renderers/shared/brand-marks.mjs +563 -0
  102. package/skills/global_config/archify/renderers/shared/cli.mjs +218 -0
  103. package/skills/global_config/archify/renderers/shared/desktop-readability.mjs +26 -0
  104. package/skills/global_config/archify/renderers/shared/diagnostics.mjs +160 -0
  105. package/skills/global_config/archify/renderers/shared/engineering-profiles.mjs +157 -0
  106. package/skills/global_config/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
  107. package/skills/global_config/archify/renderers/shared/generated-validators.mjs +13 -0
  108. package/skills/global_config/archify/renderers/shared/geometry.mjs +1423 -0
  109. package/skills/global_config/archify/renderers/shared/i18n.mjs +595 -0
  110. package/skills/global_config/archify/renderers/shared/layout-report.mjs +40 -0
  111. package/skills/global_config/archify/renderers/shared/legend.mjs +217 -0
  112. package/skills/global_config/archify/renderers/shared/output-path.mjs +340 -0
  113. package/skills/global_config/archify/renderers/shared/repository-evidence.mjs +239 -0
  114. package/skills/global_config/archify/renderers/shared/repository-location.mjs +58 -0
  115. package/skills/global_config/archify/renderers/shared/text-fit.mjs +49 -0
  116. package/skills/global_config/archify/renderers/shared/utils.mjs +232 -0
  117. package/skills/global_config/archify/renderers/shared/validator.mjs +86 -0
  118. package/skills/global_config/archify/renderers/workflow/README.md +223 -0
  119. package/skills/global_config/archify/renderers/workflow/render-workflow.mjs +35 -0
  120. package/skills/global_config/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
  121. package/skills/global_config/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  122. package/skills/global_config/archify/schemas/README.md +211 -0
  123. package/skills/global_config/archify/schemas/architecture.schema.json +178 -0
  124. package/skills/global_config/archify/schemas/common.schema.json +115 -0
  125. package/skills/global_config/archify/schemas/dataflow.schema.json +243 -0
  126. package/skills/global_config/archify/schemas/lifecycle.schema.json +266 -0
  127. package/skills/global_config/archify/schemas/sequence.schema.json +223 -0
  128. package/skills/global_config/archify/schemas/workflow.schema.json +428 -0
  129. package/skills/global_config/archify/scripts/check-render-output.mjs +916 -0
  130. package/skills/global_config/archify/scripts/generate-brand-marks.mjs +141 -0
  131. package/skills/global_config/archify/scripts/generate-validators.mjs +66 -0
  132. package/skills/global_config/archify/scripts/render-examples.mjs +26 -0
  133. package/skills/global_config/archify/skill-release.json +10 -0
  134. package/skills/global_config/bdb-security-audit/SKILL.md +109 -0
  135. package/skills/global_config/bdb-security-audit/references/audit-checklists.md +172 -0
  136. package/skills/global_config/bdb-security-audit/references/skylos-guide.md +139 -0
  137. package/skills/global_config/bdb-security-audit/references/xalgorix-bridge.md +110 -0
  138. package/skills/global_config/bdbdesignpro/SKILL.md +4 -4
  139. package/skills/global_config/bdbdesignpro/references/bdb-motion-tokens.md +52 -31
  140. package/skills/global_config/bdbresilience/SKILL.md +10 -0
  141. package/skills/global_config/brag/SKILL.md +179 -0
  142. package/skills/global_config/brag/assets/music/README.md +20 -0
  143. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.json +3846 -0
  144. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.md +35 -0
  145. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.json +1826 -0
  146. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.md +35 -0
  147. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.json +2266 -0
  148. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.md +35 -0
  149. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.json +2806 -0
  150. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.md +35 -0
  151. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.json +2796 -0
  152. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.md +35 -0
  153. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-1-by-ende-dot-app.mp3 +0 -0
  154. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-10-by-ende-dot-app.mp3 +0 -0
  155. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-11-by-ende-dot-app.mp3 +0 -0
  156. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-12-by-ende-dot-app.mp3 +0 -0
  157. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-9-by-ende-dot-app.mp3 +0 -0
  158. package/skills/global_config/brag/assets/sfx/casino/card-fan-1.ogg +0 -0
  159. package/skills/global_config/brag/assets/sfx/casino/card-fan-2.ogg +0 -0
  160. package/skills/global_config/brag/assets/sfx/casino/card-place-1.ogg +0 -0
  161. package/skills/global_config/brag/assets/sfx/casino/card-place-2.ogg +0 -0
  162. package/skills/global_config/brag/assets/sfx/casino/card-place-3.ogg +0 -0
  163. package/skills/global_config/brag/assets/sfx/casino/card-place-4.ogg +0 -0
  164. package/skills/global_config/brag/assets/sfx/casino/card-shove-1.ogg +0 -0
  165. package/skills/global_config/brag/assets/sfx/casino/card-shove-2.ogg +0 -0
  166. package/skills/global_config/brag/assets/sfx/casino/card-shove-3.ogg +0 -0
  167. package/skills/global_config/brag/assets/sfx/casino/card-shove-4.ogg +0 -0
  168. package/skills/global_config/brag/assets/sfx/casino/card-shuffle.ogg +0 -0
  169. package/skills/global_config/brag/assets/sfx/casino/card-slide-1.ogg +0 -0
  170. package/skills/global_config/brag/assets/sfx/casino/card-slide-2.ogg +0 -0
  171. package/skills/global_config/brag/assets/sfx/casino/card-slide-3.ogg +0 -0
  172. package/skills/global_config/brag/assets/sfx/casino/card-slide-4.ogg +0 -0
  173. package/skills/global_config/brag/assets/sfx/casino/card-slide-5.ogg +0 -0
  174. package/skills/global_config/brag/assets/sfx/casino/card-slide-6.ogg +0 -0
  175. package/skills/global_config/brag/assets/sfx/casino/card-slide-7.ogg +0 -0
  176. package/skills/global_config/brag/assets/sfx/casino/card-slide-8.ogg +0 -0
  177. package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-1.ogg +0 -0
  178. package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-2.ogg +0 -0
  179. package/skills/global_config/brag/assets/sfx/casino/chip-lay-1.ogg +0 -0
  180. package/skills/global_config/brag/assets/sfx/casino/chip-lay-2.ogg +0 -0
  181. package/skills/global_config/brag/assets/sfx/casino/chip-lay-3.ogg +0 -0
  182. package/skills/global_config/brag/assets/sfx/casino/chips-collide-1.ogg +0 -0
  183. package/skills/global_config/brag/assets/sfx/casino/chips-collide-2.ogg +0 -0
  184. package/skills/global_config/brag/assets/sfx/casino/chips-collide-3.ogg +0 -0
  185. package/skills/global_config/brag/assets/sfx/casino/chips-collide-4.ogg +0 -0
  186. package/skills/global_config/brag/assets/sfx/casino/chips-handle-1.ogg +0 -0
  187. package/skills/global_config/brag/assets/sfx/casino/chips-handle-2.ogg +0 -0
  188. package/skills/global_config/brag/assets/sfx/casino/chips-handle-3.ogg +0 -0
  189. package/skills/global_config/brag/assets/sfx/casino/chips-handle-4.ogg +0 -0
  190. package/skills/global_config/brag/assets/sfx/casino/chips-handle-6.ogg +0 -0
  191. package/skills/global_config/brag/assets/sfx/casino/chips-stack-1.ogg +0 -0
  192. package/skills/global_config/brag/assets/sfx/casino/chips-stack-2.ogg +0 -0
  193. package/skills/global_config/brag/assets/sfx/casino/chips-stack-3.ogg +0 -0
  194. package/skills/global_config/brag/assets/sfx/casino/chips-stack-4.ogg +0 -0
  195. package/skills/global_config/brag/assets/sfx/casino/chips-stack-5.ogg +0 -0
  196. package/skills/global_config/brag/assets/sfx/casino/chips-stack-6.ogg +0 -0
  197. package/skills/global_config/brag/assets/sfx/casino/dice-grab-1.ogg +0 -0
  198. package/skills/global_config/brag/assets/sfx/casino/dice-grab-2.ogg +0 -0
  199. package/skills/global_config/brag/assets/sfx/casino/dice-shake-1.ogg +0 -0
  200. package/skills/global_config/brag/assets/sfx/casino/dice-shake-2.ogg +0 -0
  201. package/skills/global_config/brag/assets/sfx/casino/dice-shake-3.ogg +0 -0
  202. package/skills/global_config/brag/assets/sfx/casino/dice-throw-1.ogg +0 -0
  203. package/skills/global_config/brag/assets/sfx/casino/dice-throw-2.ogg +0 -0
  204. package/skills/global_config/brag/assets/sfx/casino/dice-throw-3.ogg +0 -0
  205. package/skills/global_config/brag/assets/sfx/casino/die-throw-1.ogg +0 -0
  206. package/skills/global_config/brag/assets/sfx/casino/die-throw-2.ogg +0 -0
  207. package/skills/global_config/brag/assets/sfx/casino/die-throw-3.ogg +0 -0
  208. package/skills/global_config/brag/assets/sfx/casino/die-throw-4.ogg +0 -0
  209. package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_000.ogg +0 -0
  210. package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_003.ogg +0 -0
  211. package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_004.ogg +0 -0
  212. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_000.ogg +0 -0
  213. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_001.ogg +0 -0
  214. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_002.ogg +0 -0
  215. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_003.ogg +0 -0
  216. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_004.ogg +0 -0
  217. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_000.ogg +0 -0
  218. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_001.ogg +0 -0
  219. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_002.ogg +0 -0
  220. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_003.ogg +0 -0
  221. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_004.ogg +0 -0
  222. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_000.ogg +0 -0
  223. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_001.ogg +0 -0
  224. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_002.ogg +0 -0
  225. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_003.ogg +0 -0
  226. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_004.ogg +0 -0
  227. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_000.ogg +0 -0
  228. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_001.ogg +0 -0
  229. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_002.ogg +0 -0
  230. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_003.ogg +0 -0
  231. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_004.ogg +0 -0
  232. package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_000.ogg +0 -0
  233. package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_003.ogg +0 -0
  234. package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_004.ogg +0 -0
  235. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_000.ogg +0 -0
  236. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_001.ogg +0 -0
  237. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_002.ogg +0 -0
  238. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_003.ogg +0 -0
  239. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_004.ogg +0 -0
  240. package/skills/global_config/brag/assets/sfx/impact/impactGlass_heavy_002.ogg +0 -0
  241. package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_001.ogg +0 -0
  242. package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_002.ogg +0 -0
  243. package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_003.ogg +0 -0
  244. package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_000.ogg +0 -0
  245. package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_002.ogg +0 -0
  246. package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_004.ogg +0 -0
  247. package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_000.ogg +0 -0
  248. package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_002.ogg +0 -0
  249. package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_004.ogg +0 -0
  250. package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_002.ogg +0 -0
  251. package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_003.ogg +0 -0
  252. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_000.ogg +0 -0
  253. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_001.ogg +0 -0
  254. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_002.ogg +0 -0
  255. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_003.ogg +0 -0
  256. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_004.ogg +0 -0
  257. package/skills/global_config/brag/assets/sfx/impact/impactMining_001.ogg +0 -0
  258. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_000.ogg +0 -0
  259. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_001.ogg +0 -0
  260. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_002.ogg +0 -0
  261. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_003.ogg +0 -0
  262. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_004.ogg +0 -0
  263. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_000.ogg +0 -0
  264. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_001.ogg +0 -0
  265. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_002.ogg +0 -0
  266. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_003.ogg +0 -0
  267. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_004.ogg +0 -0
  268. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_000.ogg +0 -0
  269. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_001.ogg +0 -0
  270. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_002.ogg +0 -0
  271. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_003.ogg +0 -0
  272. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_004.ogg +0 -0
  273. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_000.ogg +0 -0
  274. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_001.ogg +0 -0
  275. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_002.ogg +0 -0
  276. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_003.ogg +0 -0
  277. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_004.ogg +0 -0
  278. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_000.ogg +0 -0
  279. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_001.ogg +0 -0
  280. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_002.ogg +0 -0
  281. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_003.ogg +0 -0
  282. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_004.ogg +0 -0
  283. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_000.ogg +0 -0
  284. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_001.ogg +0 -0
  285. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_002.ogg +0 -0
  286. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_003.ogg +0 -0
  287. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_004.ogg +0 -0
  288. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_000.ogg +0 -0
  289. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_001.ogg +0 -0
  290. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_002.ogg +0 -0
  291. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_003.ogg +0 -0
  292. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_004.ogg +0 -0
  293. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_000.ogg +0 -0
  294. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_001.ogg +0 -0
  295. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_002.ogg +0 -0
  296. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_003.ogg +0 -0
  297. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_004.ogg +0 -0
  298. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_000.ogg +0 -0
  299. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_001.ogg +0 -0
  300. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_002.ogg +0 -0
  301. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_003.ogg +0 -0
  302. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_004.ogg +0 -0
  303. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_000.ogg +0 -0
  304. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_001.ogg +0 -0
  305. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_002.ogg +0 -0
  306. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_003.ogg +0 -0
  307. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_004.ogg +0 -0
  308. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_000.ogg +0 -0
  309. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_001.ogg +0 -0
  310. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_002.ogg +0 -0
  311. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_003.ogg +0 -0
  312. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_004.ogg +0 -0
  313. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_000.ogg +0 -0
  314. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_001.ogg +0 -0
  315. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_002.ogg +0 -0
  316. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_003.ogg +0 -0
  317. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_004.ogg +0 -0
  318. package/skills/global_config/brag/assets/sfx/interface/bong_001.ogg +0 -0
  319. package/skills/global_config/brag/assets/sfx/interface/click_001.ogg +0 -0
  320. package/skills/global_config/brag/assets/sfx/interface/click_002.ogg +0 -0
  321. package/skills/global_config/brag/assets/sfx/interface/click_003.ogg +0 -0
  322. package/skills/global_config/brag/assets/sfx/interface/click_004.ogg +0 -0
  323. package/skills/global_config/brag/assets/sfx/interface/click_005.ogg +0 -0
  324. package/skills/global_config/brag/assets/sfx/interface/drop_001.ogg +0 -0
  325. package/skills/global_config/brag/assets/sfx/interface/drop_002.ogg +0 -0
  326. package/skills/global_config/brag/assets/sfx/interface/drop_003.ogg +0 -0
  327. package/skills/global_config/brag/assets/sfx/interface/error_005.ogg +0 -0
  328. package/skills/global_config/brag/assets/sfx/interface/error_006.ogg +0 -0
  329. package/skills/global_config/brag/assets/sfx/interface/glitch_002.ogg +0 -0
  330. package/skills/global_config/brag/assets/sfx/interface/glitch_004.ogg +0 -0
  331. package/skills/global_config/brag/assets/sfx/interface/select_008.ogg +0 -0
  332. package/skills/global_config/brag/assets/sfx/interface/switch_001.ogg +0 -0
  333. package/skills/global_config/brag/assets/sfx/interface/switch_002.ogg +0 -0
  334. package/skills/global_config/brag/assets/sfx/interface/switch_004.ogg +0 -0
  335. package/skills/global_config/brag/assets/sfx/interface/switch_005.ogg +0 -0
  336. package/skills/global_config/brag/assets/sfx/interface/switch_006.ogg +0 -0
  337. package/skills/global_config/brag/assets/sfx/interface/switch_007.ogg +0 -0
  338. package/skills/global_config/brag/assets/sfx/keyboard/keypress-001.wav +0 -0
  339. package/skills/global_config/brag/assets/sfx/keyboard/keypress-002.wav +0 -0
  340. package/skills/global_config/brag/assets/sfx/keyboard/keypress-003.wav +0 -0
  341. package/skills/global_config/brag/assets/sfx/keyboard/keypress-004.wav +0 -0
  342. package/skills/global_config/brag/assets/sfx/keyboard/keypress-005.wav +0 -0
  343. package/skills/global_config/brag/assets/sfx/keyboard/keypress-006.wav +0 -0
  344. package/skills/global_config/brag/assets/sfx/keyboard/keypress-007.wav +0 -0
  345. package/skills/global_config/brag/assets/sfx/keyboard/keypress-008.wav +0 -0
  346. package/skills/global_config/brag/assets/sfx/keyboard/keypress-009.wav +0 -0
  347. package/skills/global_config/brag/assets/sfx/keyboard/keypress-010.wav +0 -0
  348. package/skills/global_config/brag/assets/sfx/keyboard/keypress-011.wav +0 -0
  349. package/skills/global_config/brag/assets/sfx/keyboard/keypress-012.wav +0 -0
  350. package/skills/global_config/brag/assets/sfx/keyboard/keypress-013.wav +0 -0
  351. package/skills/global_config/brag/assets/sfx/keyboard/keypress-014.wav +0 -0
  352. package/skills/global_config/brag/assets/sfx/keyboard/keypress-015.wav +0 -0
  353. package/skills/global_config/brag/assets/sfx/keyboard/keypress-016.wav +0 -0
  354. package/skills/global_config/brag/assets/sfx/keyboard/keypress-017.wav +0 -0
  355. package/skills/global_config/brag/assets/sfx/keyboard/keypress-018.wav +0 -0
  356. package/skills/global_config/brag/assets/sfx/keyboard/keypress-019.wav +0 -0
  357. package/skills/global_config/brag/assets/sfx/keyboard/keypress-020.wav +0 -0
  358. package/skills/global_config/brag/assets/sfx/keyboard/keypress-021.wav +0 -0
  359. package/skills/global_config/brag/assets/sfx/keyboard/keypress-022.wav +0 -0
  360. package/skills/global_config/brag/assets/sfx/keyboard/keypress-023.wav +0 -0
  361. package/skills/global_config/brag/assets/sfx/keyboard/keypress-024.wav +0 -0
  362. package/skills/global_config/brag/assets/sfx/keyboard/keypress-025.wav +0 -0
  363. package/skills/global_config/brag/assets/sfx/keyboard/keypress-026.wav +0 -0
  364. package/skills/global_config/brag/assets/sfx/keyboard/keypress-027.wav +0 -0
  365. package/skills/global_config/brag/assets/sfx/keyboard/keypress-028.wav +0 -0
  366. package/skills/global_config/brag/assets/sfx/keyboard/keypress-029.wav +0 -0
  367. package/skills/global_config/brag/assets/sfx/keyboard/keypress-030.wav +0 -0
  368. package/skills/global_config/brag/assets/sfx/keyboard/keypress-031.wav +0 -0
  369. package/skills/global_config/brag/assets/sfx/keyboard/keypress-032.wav +0 -0
  370. package/skills/global_config/brag/assets/sfx/sfx-analysis.json +15584 -0
  371. package/skills/global_config/brag/assets/sfx/sfx-analysis.md +75 -0
  372. package/skills/global_config/brag/assets/sfx/ui/click1.ogg +0 -0
  373. package/skills/global_config/brag/assets/sfx/ui/click2.ogg +0 -0
  374. package/skills/global_config/brag/assets/sfx/ui/click3.ogg +0 -0
  375. package/skills/global_config/brag/assets/sfx/ui/click4.ogg +0 -0
  376. package/skills/global_config/brag/assets/sfx/ui/click5.ogg +0 -0
  377. package/skills/global_config/brag/assets/sfx/ui/mouseclick1.ogg +0 -0
  378. package/skills/global_config/brag/assets/sfx/ui/rollover1.ogg +0 -0
  379. package/skills/global_config/brag/assets/sfx/ui/rollover2.ogg +0 -0
  380. package/skills/global_config/brag/assets/sfx/ui/rollover4.ogg +0 -0
  381. package/skills/global_config/brag/assets/sfx/ui/rollover5.ogg +0 -0
  382. package/skills/global_config/brag/assets/sfx/ui/switch1.ogg +0 -0
  383. package/skills/global_config/brag/assets/sfx/ui/switch10.ogg +0 -0
  384. package/skills/global_config/brag/assets/sfx/ui/switch11.ogg +0 -0
  385. package/skills/global_config/brag/assets/sfx/ui/switch12.ogg +0 -0
  386. package/skills/global_config/brag/assets/sfx/ui/switch13.ogg +0 -0
  387. package/skills/global_config/brag/assets/sfx/ui/switch14.ogg +0 -0
  388. package/skills/global_config/brag/assets/sfx/ui/switch15.ogg +0 -0
  389. package/skills/global_config/brag/assets/sfx/ui/switch16.ogg +0 -0
  390. package/skills/global_config/brag/assets/sfx/ui/switch17.ogg +0 -0
  391. package/skills/global_config/brag/assets/sfx/ui/switch18.ogg +0 -0
  392. package/skills/global_config/brag/assets/sfx/ui/switch19.ogg +0 -0
  393. package/skills/global_config/brag/assets/sfx/ui/switch2.ogg +0 -0
  394. package/skills/global_config/brag/assets/sfx/ui/switch20.ogg +0 -0
  395. package/skills/global_config/brag/assets/sfx/ui/switch21.ogg +0 -0
  396. package/skills/global_config/brag/assets/sfx/ui/switch22.ogg +0 -0
  397. package/skills/global_config/brag/assets/sfx/ui/switch23.ogg +0 -0
  398. package/skills/global_config/brag/assets/sfx/ui/switch24.ogg +0 -0
  399. package/skills/global_config/brag/assets/sfx/ui/switch25.ogg +0 -0
  400. package/skills/global_config/brag/assets/sfx/ui/switch26.ogg +0 -0
  401. package/skills/global_config/brag/assets/sfx/ui/switch27.ogg +0 -0
  402. package/skills/global_config/brag/assets/sfx/ui/switch28.ogg +0 -0
  403. package/skills/global_config/brag/assets/sfx/ui/switch29.ogg +0 -0
  404. package/skills/global_config/brag/assets/sfx/ui/switch3.ogg +0 -0
  405. package/skills/global_config/brag/assets/sfx/ui/switch30.ogg +0 -0
  406. package/skills/global_config/brag/assets/sfx/ui/switch31.ogg +0 -0
  407. package/skills/global_config/brag/assets/sfx/ui/switch32.ogg +0 -0
  408. package/skills/global_config/brag/assets/sfx/ui/switch33.ogg +0 -0
  409. package/skills/global_config/brag/assets/sfx/ui/switch34.ogg +0 -0
  410. package/skills/global_config/brag/assets/sfx/ui/switch35.ogg +0 -0
  411. package/skills/global_config/brag/assets/sfx/ui/switch36.ogg +0 -0
  412. package/skills/global_config/brag/assets/sfx/ui/switch37.ogg +0 -0
  413. package/skills/global_config/brag/assets/sfx/ui/switch38.ogg +0 -0
  414. package/skills/global_config/brag/assets/sfx/ui/switch4.ogg +0 -0
  415. package/skills/global_config/brag/assets/sfx/ui/switch5.ogg +0 -0
  416. package/skills/global_config/brag/assets/sfx/ui/switch6.ogg +0 -0
  417. package/skills/global_config/brag/assets/sfx/ui/switch7.ogg +0 -0
  418. package/skills/global_config/brag/assets/sfx/ui/switch8.ogg +0 -0
  419. package/skills/global_config/brag/assets/sfx/ui/switch9.ogg +0 -0
  420. package/skills/global_config/brag/references/audio.md +307 -0
  421. package/skills/global_config/brag/references/step-1-inspect.md +133 -0
  422. package/skills/global_config/brag/references/step-2-plan.md +234 -0
  423. package/skills/global_config/brag/references/step-3-compose.md +210 -0
  424. package/skills/global_config/brag/references/step-4-deliver.md +168 -0
  425. package/skills/global_config/brag/references/tones.md +231 -0
  426. package/skills/global_config/brag/scripts/analyze_music_cues.py +309 -0
  427. package/skills/global_config/brag/scripts/pyproject.toml +14 -0
  428. package/skills/global_config/brag/scripts/uv.lock +819 -0
  429. package/skills/global_config/deja-memory/SKILL.md +42 -0
  430. package/skills/global_config/editable-design/LICENSE +202 -0
  431. package/skills/global_config/editable-design/README.md +34 -0
  432. package/skills/global_config/editable-design/SKILL.md +630 -0
  433. package/skills/global_config/editable-design/THIRD_PARTY_NOTICES.md +10 -0
  434. package/skills/global_config/editable-design/agents/openai.yaml +4 -0
  435. package/skills/global_config/editable-design/assets/editor/layer-editor.css +262 -0
  436. package/skills/global_config/editable-design/assets/editor/layer-editor.js +1929 -0
  437. package/skills/global_config/editable-design/assets/poster-starter/README.md +92 -0
  438. package/skills/global_config/editable-design/assets/poster-starter/assets/.gitkeep +0 -0
  439. package/skills/global_config/editable-design/assets/poster-starter/index.html +91 -0
  440. package/skills/global_config/editable-design/assets/poster-starter/poster.json +6 -0
  441. package/skills/global_config/editable-design/assets/poster-starter/reference/.gitkeep +1 -0
  442. package/skills/global_config/editable-design/assets/replay/icons/assets.png +0 -0
  443. package/skills/global_config/editable-design/assets/replay/icons/html.png +0 -0
  444. package/skills/global_config/editable-design/assets/replay/icons/input.png +0 -0
  445. package/skills/global_config/editable-design/assets/replay/icons/layers.png +0 -0
  446. package/skills/global_config/editable-design/assets/replay/icons/plan.png +0 -0
  447. package/skills/global_config/editable-design/assets/replay/icons/reference.png +0 -0
  448. package/skills/global_config/editable-design/assets/replay/icons/review.png +0 -0
  449. package/skills/global_config/editable-design/assets/replay/index.html +59 -0
  450. package/skills/global_config/editable-design/assets/replay/replay-motion.js +410 -0
  451. package/skills/global_config/editable-design/assets/replay/replay.css +380 -0
  452. package/skills/global_config/editable-design/assets/replay/replay.js +374 -0
  453. package/skills/global_config/editable-design/font-kit/package-lock.json +115 -0
  454. package/skills/global_config/editable-design/font-kit/package.json +19 -0
  455. package/skills/global_config/editable-design/pack.sh +20 -0
  456. package/skills/global_config/editable-design/references/asset-architecture.md +72 -0
  457. package/skills/global_config/editable-design/references/editor-pitfalls.md +70 -0
  458. package/skills/global_config/editable-design/references/editor-runtime.md +131 -0
  459. package/skills/global_config/editable-design/references/font-system.md +89 -0
  460. package/skills/global_config/editable-design/references/imagery.md +145 -0
  461. package/skills/global_config/editable-design/references/layout-typography.md +116 -0
  462. package/skills/global_config/editable-design/references/replay-contract.md +169 -0
  463. package/skills/global_config/editable-design/scripts/_browser.mjs +247 -0
  464. package/skills/global_config/editable-design/scripts/bake.mjs +87 -0
  465. package/skills/global_config/editable-design/scripts/build-replay.mjs +316 -0
  466. package/skills/global_config/editable-design/scripts/check-contract.mjs +293 -0
  467. package/skills/global_config/editable-design/scripts/check-fonts.sh +252 -0
  468. package/skills/global_config/editable-design/scripts/check-poster.sh +70 -0
  469. package/skills/global_config/editable-design/scripts/doctor.sh +78 -0
  470. package/skills/global_config/editable-design/scripts/explode.mjs +115 -0
  471. package/skills/global_config/editable-design/scripts/font-kit.mjs +187 -0
  472. package/skills/global_config/editable-design/scripts/import-asset.sh +23 -0
  473. package/skills/global_config/editable-design/scripts/init-poster.sh +64 -0
  474. package/skills/global_config/editable-design/scripts/install-font-kit.sh +13 -0
  475. package/skills/global_config/editable-design/scripts/package-lock.json +334 -0
  476. package/skills/global_config/editable-design/scripts/package.json +14 -0
  477. package/skills/global_config/editable-design/scripts/remove-chroma-key.py +201 -0
  478. package/skills/global_config/editable-design/scripts/render-poster.sh +202 -0
  479. package/skills/global_config/editable-design/scripts/trace.sh +159 -0
  480. package/skills/global_config/editable-design/scripts/verify-replay.mjs +97 -0
  481. package/skills/global_config/editable-design/scripts/verify.mjs +782 -0
  482. package/skills/global_config/editable-design/scripts/wire-editor.mjs +104 -0
  483. package/skills/global_config/editable-design/tests/fixtures/small-explode/index.html +86 -0
  484. package/skills/global_config/plan-canvas/SKILL.md +8 -0
@@ -0,0 +1,661 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="theme-color" content="#000000">
7
+ <meta name="description" content="A live map of your coding agent's plan, progress, and drift.">
8
+ <title>agenttrail</title>
9
+ <link id="fav" rel="icon" href="data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%201000%201000%22%3E%3Crect%20width=%221000%22%20height=%221000%22%20rx=%22210%22%20fill=%22%2317171a%22/%3E%3Cg%20fill=%22none%22%20stroke=%22%23fff%22%20stroke-width=%2292%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Cpath%20d=%22M180%20810C278%20617%20363%20405%20445%20180C467%20120%20535%20120%20560%20180C650%20414%20733%20625%20820%20810%22/%3E%3Cpath%20d=%22M180%20810C300%20704%20430%20646%20610%20590%22/%3E%3C/g%3E%3Ccircle%20cx=%22180%22%20cy=%22810%22%20r=%2274%22%20fill=%22%23fff%22/%3E%3Ccircle%20cx=%22610%22%20cy=%22590%22%20r=%2274%22%20fill=%22%23fff%22/%3E%3Ccircle%20cx=%22820%22%20cy=%22810%22%20r=%2274%22%20fill=%22%23fff%22/%3E%3Ccircle%20cx=%22820%22%20cy=%22810%22%20r=%2238%22%20fill=%22%23FFB21A%22/%3E%3C/svg%3E">
10
+ <script>try{document.documentElement.dataset.theme=localStorage.getItem('at-theme')==='light'?'light':'dark'}catch{document.documentElement.dataset.theme='dark'}</script>
11
+ <style>
12
+ :root{color-scheme:dark;--bg:#000;--surface:#181818;--raised:#1f1f1f;--hover:#272727;--line:#313131;--text:#fff;--soft:#d1d1d1;--dim:#9b9b9b;--faint:#686868;--accent:#e9a23b;--accent-bg:#2e220f;--danger:#db6b67;--success:#7aa36a;--complete-text:#70a784;--complete-bg:rgba(63,127,87,.18);--complete-weight:500;--done-agent-opacity:.45;--working-border:#1d2b22;--working-bg:#141f18;--watch-ring:#172015;--region:rgba(24,24,24,.5);--minimap-bg:rgba(24,24,24,.92);--overlay:#181818;--card:#181818;--card-hover:#1f1f1f;--human:#5fb8c9;--knowledge:#a189e0;--knowledge-bg:rgba(122,95,201,.14);--grid:#313131;--shadow-soft:rgba(0,0,0,.24);--shadow-strong:#000;--agent-openai:#e8e8ea;--font:"Geist","Manrope",sans-serif;--mono:"Geist Mono",monospace;--ease:cubic-bezier(.32,.72,0,1);--ease-out:cubic-bezier(.23,1,.32,1)}
13
+ :root[data-theme="light"]{color-scheme:light;--bg:#fff;--surface:#f9f8f7;--raised:#f3f2f0;--hover:#eeecea;--line:rgb(0 0 0/.14);--text:#000;--soft:rgb(0 0 0/.78);--dim:#666;--faint:rgb(0 0 0/.48);--accent:color-mix(in srgb,#e9a23b 64%,#000);--accent-bg:rgb(233 162 59/.16);--success:#3f7f57;--complete-text:#3f7f57;--complete-bg:rgb(63 127 87/.18);--complete-weight:600;--done-agent-opacity:.72;--working-border:rgb(63 127 87/.24);--working-bg:rgb(63 127 87/.10);--watch-ring:rgb(63 127 87/.14);--region:rgb(0 0 0/.015);--minimap-bg:rgb(255 255 255/.94);--overlay:#fbfaf8;--card:#f6f5f2;--card-hover:#efeeea;--human:#2e7f91;--knowledge:#7a5fc9;--knowledge-bg:rgb(122 95 201/.10);--grid:rgb(0 0 0/.16);--shadow-soft:rgb(0 0 0/.10);--shadow-strong:rgb(0 0 0/.14);--agent-openai:#313131}
14
+ *{box-sizing:border-box;margin:0}html{background:var(--bg);scroll-behavior:smooth}body{min-width:320px;min-height:100dvh;background:var(--bg);color:var(--text);font:400 14px/20px var(--font);letter-spacing:-.01em}body:before{content:"";position:fixed;inset:0;pointer-events:none;opacity:.025;z-index:4;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.8'/%3E%3C/svg%3E")}button{font:inherit}button,[role=button]{-webkit-tap-highlight-color:transparent}button:focus-visible,[role=button]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.skip{position:fixed;top:8px;left:8px;z-index:5;transform:translateY(-160%);padding:8px 12px;color:var(--text);background:var(--raised);border-radius:8px;transition:transform 700ms var(--ease)}.skip:focus{transform:none}
15
+ .app{display:grid;grid-template-rows:auto minmax(0,1fr);height:100dvh;min-height:100dvh}.topbar{position:relative;z-index:3;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:24px;min-height:64px;padding:12px 24px;background:var(--bg);border-bottom:1px solid var(--hover)}.brand,.status,.project,.phase-copy,.task-copy{min-width:0}.brand{display:flex;align-items:center;gap:12px}.brand:before{content:"";display:none;flex:none;width:32px;height:32px;background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iQWdlbnQgVHJhaWwgbWFyayBmb3IgZGFyayBiYWNrZ3JvdW5kcyI+CiAgPGRlZnM+CiAgICA8bWFzayBpZD0ibm9kZS1ob2xlcyI+CiAgICAgIDxyZWN0IHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjEwMDAiIGZpbGw9IndoaXRlIi8+CiAgICAgIDxjaXJjbGUgY3g9IjE4MCIgY3k9IjgxMCIgcj0iMzQiIGZpbGw9ImJsYWNrIi8+CiAgICAgIDxjaXJjbGUgY3g9IjYxMCIgY3k9IjU5MCIgcj0iMzQiIGZpbGw9ImJsYWNrIi8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxnIGZpbGw9IiNmZmYiIG1hc2s9InVybCgjbm9kZS1ob2xlcykiPgogICAgPHBhdGggZD0iTTE4MCA4MTBDMjc4IDYxNyAzNjMgNDA1IDQ0NSAxODBDNDY3IDEyMCA1MzUgMTIwIDU2MCAxODBDNjUwIDQxNCA3MzMgNjI1IDgyMCA4MTAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSI5MiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CiAgICA8cGF0aCBkPSJNMTgwIDgxMEMzMDAgNzA0IDQzMCA2NDYgNjEwIDU5MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjkyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KICAgIDxjaXJjbGUgY3g9IjE4MCIgY3k9IjgxMCIgcj0iNzQiLz4KICAgIDxjaXJjbGUgY3g9IjYxMCIgY3k9IjU5MCIgcj0iNzQiLz4KICAgIDxjaXJjbGUgY3g9IjgyMCIgY3k9IjgxMCIgcj0iNzQiLz4KICA8L2c+CiAgPGNpcmNsZSBjeD0iODIwIiBjeT0iODEwIiByPSIzOCIgZmlsbD0iI0ZGQjIxQSIvPgo8L3N2Zz4K") center/contain no-repeat}:root:not([data-theme="light"]) .brand:before{display:block}:root:not([data-theme="light"]) .mark{display:none}.mark{display:block;flex:none;width:32px;height:32px;border-radius:8px;object-fit:cover}.project-name,.file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.project-name{font-weight:600}.file-name{color:var(--dim);font-size:12px;line-height:16px}.switcher{display:flex;gap:2px;padding:4px;background:var(--surface);border:1px solid var(--hover);border-radius:12px}.tab{display:flex;align-items:center;gap:8px;min-height:32px;padding:4px 12px;border:0;border-radius:8px;background:transparent;color:var(--dim);cursor:pointer;font-weight:500;transition:color 700ms var(--ease),background 700ms var(--ease),transform 700ms var(--ease)}.tab:hover,.tab.on{color:var(--text)}.tab.on{background:var(--hover)}.tab:active{transform:scale(.98)}a.tab{text-decoration:none}.tab svg,.icon svg,.chev svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}.status,#state,.theme-toggle{display:flex;align-items:center}.status{justify-self:end;gap:12px}#state{gap:8px}.theme-toggle{gap:8px;min-height:32px;padding:4px 8px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--dim);cursor:pointer;font-size:12px;line-height:16px;font-weight:500;transition:color 250ms var(--ease-out),background 250ms var(--ease-out),border-color 250ms var(--ease-out),transform 250ms var(--ease-out)}.theme-toggle:hover{color:var(--text);background:var(--hover)}.theme-toggle:active{transform:scale(.98)}.theme-track{position:relative;width:32px;height:16px;border-radius:8px;background:var(--hover);box-shadow:inset 0 0 0 1px var(--line);transition:background 250ms var(--ease-out)}.theme-thumb{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:4px;background:var(--dim);transition:transform 250ms var(--ease-out),background 250ms var(--ease-out)}:root[data-theme="light"] .theme-track{background:var(--accent-bg)}:root[data-theme="light"] .theme-thumb{transform:translateX(16px);background:var(--accent)}.badge{display:flex;align-items:center;gap:8px;padding:4px 8px;border:1px solid var(--line);border-radius:8px;color:var(--dim);white-space:nowrap;font-size:12px;line-height:16px}.badge.changed{border-color:var(--accent-bg);color:var(--accent);background:var(--accent-bg)}.badge.working{border-color:var(--working-border);color:var(--success);background:var(--working-bg)}.badge.working .dot{background:var(--success);animation:working-pulse 1.6s ease-in-out infinite}@keyframes working-pulse{50%{opacity:.35}}.dot{width:6px;height:6px;border-radius:50%;background:var(--faint)}.changed .dot{background:var(--accent)}.watching{display:flex;align-items:center;gap:8px;color:var(--faint);font-size:12px}.watching:before{content:"";width:6px;height:6px;border-radius:50%;background:var(--success);box-shadow:0 0 0 4px var(--watch-ring)}
16
+ :root:not([data-theme="light"]) .brand:before{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAQKADAAQAAAABAAAAQAAAAABGUUKwAAAb7klEQVRoBX1aB7Bc1Xnee/eW7ft6VXl6klAXKggBEsWICCRAoEITGjDYcYnHxh7GGduxx45JMhOXEI8nkzjgcTCmyHYAgwtgI7DBYIyNKDIwgBBCSHpPr+zbevtuvu8/dwVMMjlv9+y5p35/Pf8592malmi1Eu9PhmEZZiqTznX3DjWbrTBqRmGgaXoYBeimJRLNVrPVamkoakh6PB4TIbXYFkWRepLHJgbp8oxR7IEvJ2hygoQWRUHv8OIVG/c4TsNt1MIo9H0n8L1WAn9NPalriWb5xMGJd19xaye8Rvn9ULGkrNquM007lSnqug1csgzWfl9CrXrCD2G0x7arpfJ9/VlEN35lhJSFW4oSNDWjKFvsXXnO9ZpmOo1q4Dth4PuBB6rwaTXRHpJfCXBAazUDtzpWPvayW5/m3DJtG0QikUrlrFQRtcJdNL7XpLqySUokTgrMUQaPVA2f4yQQVVnahJ+KQjK/GaItmTSTpr1o7WXZ4mCtOhX6LtCHoUeRQ+5Nyh5d0V8lYkoaFMjxF6uTh9TsMUrbzlqpAvrFz230FDJBEoSUhYFCekwhWj5AqZqWObq21bMNH9CjKJk0ir1zegZPyXb0aboFBriNqu81mqEPfkP9gJuMRwLvRdkwngmSwGo6CLcqx1+oTR/hI75Q+nSmE91P6gir2ZlKzp//J7V7kp6YOs7KaplBYecT0YcdfXPnLN6YzvWEvhcGYLkXkPFeGPqADZUBbBAQlzmISYZjCpoTMdEytOkjz0KliC+T6RBOyIIx1jbw/wP/B6ra/YTe90/AeeJnQcDlh+efNjhvje97nlMB6GYYCFrBHaOPwHHhPQapX5Ku6FclTgwaklboTJSPHzAg0ISWxEQxrhjRSSyKr0qVOBZzSU8YAh3RyX6qjc1t2CyzljJA7axT1vcML6mUxpW2QMVFR4AeCAE2Qqb+3tN6tkgidqVHnJGpGelmHt7SgDyUoVAHTqoOuwjOGA4gsDnGxlYkTikFIRLt78cej2c36EP/nOWd/QuqpTGgh1NuiocRHiuAyGPYH0CPGfHMXNQfBS4oYFpaSzeSVs4YHBqdmWmAlc0QiujCies6qEoSnJBAiO1hCi9rYuiqiJ5A31R10icWDsqAn851gveN6pTn1OBkmiF4j0+E/tJBwAkhRK9AC9S4jpNA+zlxm0WQPrxRK981ZCxddcPYxIxpmCAz8Cu18tulyVfr1TGoGWwFY4QK4MMYWRCz/C9ZnGwRgrgWqOEYLtnqGlgYBK4HNx+4sGOij8I2U4mJQCVTvyBIHjmZkkzcKM+sJQANvM729CdT2VM8FyblQkqm3VHsWdI3fHq+Y5bTmPCcEqjgAIESD+RwTkBmxKah+qh+MXUwUPTQ9aSd7egaXOA1KnDz2JugPEQfK4wyU/Ad2s+PACV+SaxXNRQvMbAa6k/6mk1N1y0jp81btANeCzpD35S0DCNl2rl0bsC0UscP7zt26AniRChAeHES5FKO9f79Jk6d0XSje2hh19AioE/CzgzLdyrliSO1mfEW0cfw0VUhwlyCTDKWBWn8pGArNVMdqbGF4nBPz8K0XUxmC3OiyG82g4g7H5wx9kLXc8qIfDr7Tk1lesuTr0FYIraYALFnkUK7Qn5BF9Gnsp0L1l7cO7JKS9rcWeHjA19LmtmOfsO06tUpGLEAFPQCto2XM3ASJhGLSIDBl8ytKsHZ2bPW9XQvxE7iO6VkJjcMpWy1aFWSU0GjCKs6vlfNdY5i0ylPvoppgZuqg78PUCNrsh7fpp0pzF97sWFnaqXjjeo0Yi/PqfpONXBrvlc3Ea2k805lkityJtIg+No56WkToEiRidXs5E6mY/7oubaVr1fGoOGh7yQzWYScQI+5QAD2EYQfihLIJAhAQ3EEqlydeQuKFINVPye1iktyFOgaWrTBtHL18oRbL1crM06j5rkN14HrhGyx27oQgm6YTnVa2KwwKn6TA5KkhYQpvrOOD82mnSqOjpyNHaBWOe671Sh0YFEGHAZsi1qMP1iCpjW1SGvCJJS7CGBcxZ6llenXG9Xjyi9xPpEFRgC3njRyXXNyHQNgvJku1sonGvWqZWhbLzjzrPWrspn04SPHHt339IFX3kyl0lHgWXbGSuVcFRi3OS7QyRLKRP0JcJJFF91CADRn1mmt0G/UTvh+A3oOvqO31tG3GhSIdiADJNirzhw2rRti1ul0vi/wSsfefER1E45wRXAlU+zvn7c2levCdAxs3HqtOjN/ZPAbt9y8Yf1qOArprFWrtf/4/r3f+u4PkknLsklGZepd2XibwgpORoZzAawuwpUKLgMlazaHhlZ25Ifr1fHAr0MxYKvUw1YraaV6qDMcT0GReHKAD5JYA+ux051O7VjoVds9WZvvnj244EyollMv+XDzbs2pVwf6ind//5urT13quR5OJ2HAr2WaZ29YB97se+LpZFLHJFCqQr6/f2DJwPDy3v5FnV0j2Vwv8Ad+A05FaGiT0WplMp0DvYud+pTv1SLG20QvBERaoWspYj8hXdhPRcc8qmxwV9YNhBx2uisMyqFfQ1MU1BrVMRjG4IKzoH6QKh0AP2HDqf/7rV+75spLXcfBPDoEqeMoB/NIQKAI2C658uPPv/Da0NAp3V0jqVSB0Sd8BjYNrop9s+U4pfGx1yYnDsIrYpQulUODKzN2oVGbCgKQx80ErdzLEy3YABSBKtSC2kDXqEIsM8Nml2ghVKKuJabhv+xsH6LxpGHne05JtOgf4alkz6LXQ5C5bPHopVs+hHCTiI2k67jTpZnhoQEG+c1mKp368LW7jh27v797FFtnZeo4IgtQLlAwAijAFmOwf1ku2zc9fSif7zPNLBQIfHCdcgS9V+gRStGDwdmICkEWGE1LQYotSBSJtUioRRMPq2HowHNBDrJjQOcbjIqVNMH+Rv3mz9y4ccPpQRiCzGqtftWeT37177/ZP9C3bu2poAGsmjU0tG/fgamJcd+tQB/kSOBGOBiEcY75YaMmeJTrM5OpBEIPCMitgfeh7FfYskR5cNQUCUBZBJ/QT+cicTIiDXgjZNi3IRON7lXTfLCICZqBUz5HYoMkG1AIwmB4sHf7tgvDgGd/07Lu+fHdjz7yuGaa37r1ezsv25LJpMGG3t6Os06fd8cPX0mnTMGBOhgzdQxJGR7sOPTrQALJQPzI0ROaQ18sjxjCwIOhR1MXQTDkEAnIdNIsVFG4nIKCw3nPpwHJR3GO51eEltz4yP7t2zYPDw+C06C4Xqv/150/Sdp2KpV6/Y2Djz3+FEiidJvNrVvWG0kEjo5MhTkhWB6F+cFxHq4swK1EA54+8LD9NWBjWE7QM44iJG47hAoydDxDyQQuWIkVuEibmJgBwgbcEUB2/HAuRY/cHdCOwyCfTe+5ejs8Mhhp2fZv9j25/4UDFkDTCbbu/vGDWA4yC4Jg5YqFK5aPuE4dqoGpZDZBhjCJEwpJPGdCr3A9IblwTW4oAInmpPgLmXFzBWTJBLcotEjqJK1CMYlGDNyWCQoSGIt8QqDZdN6ZK1csAT5MCCHc9oO7KWJJqXRm/5/fev2Nw4YBnwHyrEsvPRfmz1UgW5mWM78XaYMMQBeZU7zK52B1ASC7vlIcjBKfA74xQI8QjtpmNm0X0nbeStqMuIUlokiKbsUAlIU8oQet0Jkbr78KtgEZmpb55+dffPyJp8l+EWkuXaxXvIcefAweDLyC1m3efNbgQDecGBlJlQCxmJCqSAkLVawhKjgcWY5hDjjPzuqDyVGAm09m013FXH8+3ZOxO1JWDpslKIGNBpFX90pTlaMTpUOOX03qRoI7q9pf6PN4ZmslwMs1q5efd+4GOFVUwhH+4I69jVo9lctCAKiACwcfHnjg0Y99/Kp0yga3Bgf7PnT+6T+84/502obwJakDGjHh264EYtonK/nDJimfLLSSi+Zu6inMzae7Dc0ArUEI5XOQgzFm0i5m+2d1L5rdtyxl52rOFBwcdomYAqCj18BR1P3i335644b1YRhCSQ4fPnLzF77uhxAL9ZMOMd2ptaJjx46tWbt88ZIF4DM242w2fd9/PyxAgYaWA2i08dgCCZQLCL+VwyENsfaLZNkBLG1prleuO5N1d7rhlhxvhh+3VGtMVOpjpfKRqZl3Aq8+0rtiw7Kr5vavFIFCjpwaHzjN2XNn7dxxMQogJ2kY9/z4gRPHxrEBC30tXL7FShI19977IAaC/jAIT1u3cuXKRZ6HjUzYyZxKIhgxP09tTEKSVEpROuBRPhSU7nhlz6/h4wf4IE6q+0EDBXxAWMOdrruTM7Xjk6W3fbe2bPbGlfM2QcHUeGhk5Dau3HXp4OAA+arrpdLMnXf9VLdM8AZJApKk8n2WZf72iWfefPNtmnKrlUrZO3ZeSFOOQSuosDGhBz2o7jQ5rsUzDZIoEuQQqxPJ030cXMIGdCaAG4485EGIGkfIYO56VcctN7yZamOyVDk2UBw5dXQTrBbTpYzM3FmLP7znasazsnk99uun3j00gchJvDU1DFqAMj7YIcfHJ+6/75cwZTTAlLduPa+/vweiI6PbTBWsJ2kQxDFJsXzinlRfEgILHpDFhFwQCVrpuPEoZc6rOEEvhst27PnFTG8mVcha+a7U4LZLL7j2+m3wnjBfwPzG129vVqxZ3XPTVtrF5t8MLMNOagYOqdD7/r7u6enStss2IzgF6M6uzldffWP/8wdMwwCaOAkyQSkVxIMCTZkf6QbGo4ANO4nL3maLt9hkFT0GDBTMpeSbCezSMETEvowp6dSQQU3p6Zodqe5klKg2pi/dea5yS/Cef3h6/3N/eCGfLiZb+uziyGB++O3SwbJbdpz62ees+8pXPr1kyYKZmbL4AK4GXFdcecnee35GfHEiSoVP4MfoUUY72tBk62aHXSikipZOb2mA/fC+xIxvC9sCYh76SkCHvbUYBeEmFTQosVIaIAamUq2Xl64cWX/mKt9TLz60e+78meM4tmljIT9wkwljwJ6V8JLz1wzfcce33z06dsst31mwYOSa3ZcRDew0CM5Yv3r5ysUv7v8L6GeVMJiNUoAuKHmgPziXMVNzc0N9qS7AcyPPgdoHLgiQsI4uHSSAE8xBAxjBHFIC3xPgKaWmFLSpw4vAd9V2XbMFEbLnurDLN9449OjDTximDn43onqhUJi3ZHT12uXLVy1YffpSRKZX7PrEwYOHwaxypfK5z/01rhQAOJ1J79y55bk/7jctBvZMkgu/iZ6PPPY35xeGTynOhQiqQa2Oa7LI97HltSKoEHhME+StAy/YUYzRqzgVJwNUJJq8peNsOB/gOBUEQ7N7Nl90tsQO0ETjJ3t/fuzoeCGXX7xk4cXbLth04VkLF42kMylOnND3Pfb7d945ns/narXqH599AWzCapgN4t+2bfO/fPt7lQqPSkKBaAuVXi3YwvF8Xc+iOem+klet+DU38l0IGDsWgGAnxtGGnks4TxowNXnP6TEfZYIzEbgPtaJ4sAR/avXaJZfv6untBvvhVcbHTtx9531Qp0986roLLjy72FGAZiJUw6kSKHA2WLVq2brTVjzzzPMIqq+44hLPw31RALWBTxiZN2fTBRvvvut+NBFynMhSJENLbuxf3m91nGhMV4OGE3lgfMjr1VA8blMr5kdhlOQ7lQc/6ktTBufxJAYAM+AHtoAq9E+lrV88esf8hXOhTIiTH/nV44fffve6G64ECN/3lVfg0c3khtCoNwAlCCPIYXR0zqrVyz77mS9dtGXThRedj+gDsd2vH/3dju03Qg+xJBiEzoIefG1t6FsyK9UzodCHnoebHkGPwFUYHzE8FFshBSJuykDER72Rc42wX6bUYQwJrdFobN123sJFo9BjjAn84Oxz1m/Zej7fzrkuSLZTiHC048fGnvvD/meefG7/n15CwH39R69ed8bq6amZj1x3050/2us6PmhAN1zenbVh3fIVi1568VVsdm309BVLO+fMTnUL+jo0h7yH5oAAbKBUmwh6phwwtQLMBuUCH1EaoEN/wG6qDt0sBEH24ACWMExt957LhUrR00QLC0MrwD+cXxAdPP6bp+7b+/OnfvvsieMTcNW2ZWOxL372FjtlwQnj8NJd6N73698deefoEI/LYTab2bXrkj//6UXTBB6qDqy2aGUX54ZLbqUWNLyQmgO9Dwgd52sSEMlhEL6d4atYAuJBGatyomUJSeZkAVzxXGfNuhVnnLUWjKfMFB2tlmWb0K+HHnh41yU37N7x8Z/e9VB1ulbMFYv5joydyaZynfmOlJHOpfNd+a60lTlxfPIXDz2Kgz94Bzzbt2/t7eumadIrUovm5/pxTqr44L1Cz7MfcAI9OmCbApn4GoO9pzJq8KsIFny/BkBJ3STvoUaUCJkf08BruKjWqO3es922bWgL5UbZ8Aj28kuv/MNXv/3YI7/DvpvNZM20YSQNERtliy+uaEgtPWMCxmSZ1k/3PnjDR3fLdUs0On/kry4478F7fr50cG7BSGPelGaUvAqcvQ/GU21gtcL4WH9AAzmbXDB7c0duuL9nyVDfiq4OXIPqDacEmYj+YEVMJQg0DR4jm0t//gt/87GP7YHfxFGJ6HUNlvr92370yY98/tWXX8+mcyk7JcbOo5LYEkVHWaovFZIshIN49+jRjeecMW/eHFzmgG0rly3Ll5rB0arN+CCqB04jdGC1VB4abqw8ZL/6kBuJZCE7y/Orvl+H38ule2YPrgIxUdOt1k4QfjuJ57HvvOvfrt2z8+FfPT5+/MScOcNgLCb70hf+8R+/diusJpPO4DRD3EwKMXOsR+0TzQYXZXns6M16vZ4rZDdfdH6rcTxx9JGevq4zd+x45S+vvPTiAS+BQMrDbgXo+IjSU/UxluaLZalCnBzRKGLpKs4AdWdieubQ+InXwdrl8y9Zs2yXZWUY5xFLC1fMO3ZuPX/Thmt3f2rH5Tduueja22+7C7z/8pf+6bu33p5OZ0wTDoQ9OTXpIqcj/qeF+sB6od80NkUD+uIN6dLlSxPemP7U1uRzV7Z+s9GsPXvFTdeFVqvR3mtFeeS8S6uNoWNqLoQvwmmE0BJFN3AkcL0KTjYzlXcmpt4qZGatW35NLtMDybBjMzzt9FPfOngY7M/kMhi8d++DwPvygdegzdB1mgyZrRjcljLVBZZP6ZN5KICjeIfSDCGmfDa7as3axORTWuOlRD6TaE213t47ODIn1ZEBAbJhYe1YeTC8DTqWrTC2xf9B4euM0JcjgePxHAMypqZKh7RWctWiy7OZLjAS4w+/fWRwaGB0/txqte767pq1K6BhlXIN19iYS7kF4QsYBAIUaOCOdRfogRu61Z0uLOwYXNw53GvmpsfGEh1LE2Yx4TYSYULrWV+eLk3OlOAueWMhOT2mmo0CBh0MUamjeIQR5zKDjPhZpWoYVkhPHDk828r1dI6cmH4Dh4EjR45edfXlO3ZebFvW9su3/N2Xb7r//l/e/r0fIZqnm5FJea+nJlK/sgbmBXmo788WF3YM9Wc6TC0ZNIOqU5uYmth61fXJ/g0JPa8tvCkxd/e//vN3nnzsSWwIGAKscY6C4o3IQQEVzIlkLjsIsRKAIACJKDHmwVje8AS5dBf+FWSqfGhycur3v392/fo11+zefuqqZffee//nb/4qdgNeYXAMElkSU8IyEtkBHCnDXNg1a1YWrxGimt9wAhfePUxEr7z62huvvzm6+kJr3hVj7tCt37j1P79zG447vNMEAkqSmiPHKzKe1oWcc6v5E1p/zyrsFwDBUAcBD15/a7AuC2/BTSNlmzlcExUL/QcOPozTPeIcBMzDw0PYd8fHJizTxgtmBkzcpvFDjyu/UsYqgqNg5xZ2Dpu4YUYEL3eRyi1Cy53AK9cqqVyqs6ezXC5PT5YydhqvVTiN0hbgF1EANElBAmnM+UU3SGAABJBZSFQAhhMsMheaecwx8EJzonSQ8VZCL89U8M9VeNNC3ktnGYCxHKDKqgD+FezsvCK1tO47Ll4syq4k94lxOInNOAjCygxePoQIOhBFqnkEZszvNvST6LkMlgDfsD8y+gdqVFHR+EtW4mCGiVDQQm5tuWw3PBLuWtAfifVqJcwpK8qvVPGZraI51lC+1ydu/htTexNAtIAyFqNTBxIcl/HSWvFVGE9wbY6jGx9UJrhVK2oID/+qQm1DxNDUGfNDGcAthHmEQWzgiBvo2HfznfmhSn0cCoMGDJZWAiUx8pE6KYEPPJTqPZkOoMdHoCsPqzThpIG2gXI+4mRqF+iWuRaolDopSw2P9OyoIbLkLQ2MBBVitnJJxqAfB+WkjjeteqTDw+KaKJ/uwjWbBW0yoKaIl3h/jz3E460Mr3fkzMCABwl8g/JAnI3A4VaAfQAr0ihjfOAYihgl0FBJQHFiE57jJpIm0OlV0AOTkx7W4sBBreYwVvDfCKUkPg8ywK1EizSEoY7NwbYypwyfYeIfFJKmoZlgMGwARgBFcIPqdH18snqs7s6IZsIV4DrFcvAGqb21YQEkIla/xMVHhUq1MpduCjE68omVTBwnBZblEUdWmBBuW+NqyF1pNFWirRuqjThCHMN5eeohQA/qyOUCDwwOrWSqK9s/1DFSSHd5oeMG9bSRgjPBDiiHD7AfuN97EwEWAjoZqWDhSQrIFPNJHB5k7XaTqmMek6RreOmk4bW9ZfWgkq6Q1yfgKXIWEJkhx6V07GRFrYUw0geTYQ45sA9CZxPCSVlZy7BOVI9MVd5B9EGmcT3F9RiSMDIG2gbHOmJmrjL1o2pOtkkjCWMNVg9bdexBLdPgPTgS3QkByq88yTQYJjYHIYg2Y1cWl4KX+IwQJUTjYS9o4qzuINQppDphMA2v4oYNcl4AIKMZUFNRlCRQBAxxtznMPvjj0vKRDu0q9pJGINUTQeQkIUr8vyv+7xQjgJ5Z/GWFJJlRdACIlU7L5sKQmLoh+tAO1/Afnzjl4iLRxJW9gwAxwOs6Lk9/iSTlGFlcgyf5U4vFOTuqhWV4DFoNZxcwXsONgsPbHtwYmGZOab80kQR05Y8aKFNxVSAWhsblNnoqNJISlBy6IQe4YLwZqPsVH28bOBGGx5AIWD3iFwVZSNqkyJp2JZtkrOrDnFsVVDyI6liXBEAxsBkYRprzfyDhUdaKZ3hv3nhVAYRMlIQZySMyUEPVgo2krXzNK1F0bRxtUXwQIldWq3NFeWIHVVSgpJmV2PaiFt7CyKWTasMbNdBgGpn38IP/1CiCFnYAlkx7cnJ5JmbWMFOuhnQwUSRwUPC5uB+qexW19bRBoEOM6mQBz6pWVpRlZPk2JEECTDrua3nvj3rgowRUAg24c8DWgHiOBkI1F3DtFdCNwPioqmRGKSoKVCvJVXKg3vMQY5kZbHZwefFKMgUmURjVD8eyuU0WWzm/VIvHoZOEe8fxpQ5XrqYSApC1ExwfTphRExdpfjqb6e7qwxjRCmUP7X78lUmlAmu+NwdsiyDiCgWH93m6jteEED4b26Okl3ABJfEf7UYqCUaZ+J/odCafLyQ0vHepBhF2Hv5jSTwFfrTE/wACHjDz6fFrRAAAAABJRU5ErkJggg==")}
17
+ .workspace{position:relative;display:grid;grid-template-columns:272px minmax(0,1fr);min-height:0;height:auto;overflow:hidden}
18
+ .sidebar{min-width:0;min-height:0;overflow:auto;display:flex;flex-direction:column;padding:12px 0 0;background:var(--surface);border-right:1px solid var(--hover)}.tree-head{padding:4px 16px 10px;color:var(--faint);font-size:11px;line-height:16px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.tree-row{display:flex;align-items:center;gap:6px;width:100%;min-height:24px;padding-right:12px;border:0;background:transparent;color:var(--soft);cursor:pointer;text-align:left;font-size:12.5px;line-height:16px;white-space:nowrap;transition:background 250ms var(--ease-out),color 250ms var(--ease-out)}.tree-row.file{color:var(--dim);cursor:default}.tree-row.dir:hover{background:var(--hover);color:var(--text)}.tree-chev{display:grid;flex:none;place-items:center;width:14px;height:14px;color:var(--faint)}.tree-chev svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;transition:transform 250ms var(--ease-out)}.tree-row.dir.open .tree-chev svg{transform:rotate(90deg)}.tree-name{overflow:hidden;text-overflow:ellipsis}.tree-row.hot .tree-name{color:var(--accent)}.tree-ago{margin-left:auto;color:var(--accent);font:400 10px/16px var(--mono);font-variant-numeric:tabular-nums}:root[data-theme="light"] .tree-row.hot{background:var(--accent-bg);box-shadow:inset 2px 0 #e9a23b}:root[data-theme="light"] .tree-row.hot .tree-name,:root[data-theme="light"] .tree-row.hot .tree-ago{font-weight:600}.tree-empty{padding:16px;color:var(--faint);font-size:12px;line-height:16px}
19
+ .tree-add{display:flex;align-items:center;gap:8px;width:100%;min-height:30px;margin-top:8px;padding:0 16px;border:0;background:transparent;color:var(--faint);cursor:pointer;text-align:left;font-size:12.5px;transition:color 250ms var(--ease-out)}
20
+ .tree-add:hover{color:var(--text)}
21
+ .tree-add-form{padding:6px 12px 10px;display:flex;flex-direction:column;gap:6px}
22
+ .tree-add-form input{width:100%;padding:7px 10px;border:1px solid var(--line);border-radius:8px;background:var(--raised);color:var(--text);font:400 12px var(--mono)}
23
+ .tree-add-form input:focus{outline:none;border-color:var(--faint)}
24
+ .tree-add-form .row{display:flex;gap:6px}
25
+ .tree-add-msg{color:var(--faint);font-size:11.5px;padding:0 2px}
26
+ .tree-sugg{display:flex;flex-direction:column;gap:2px;max-height:216px;overflow-y:auto}
27
+ .sugg-row{display:flex;flex-direction:column;gap:1px;width:100%;padding:6px 10px;border:0;border-radius:8px;background:transparent;color:var(--text);cursor:pointer;text-align:left;transition:background 150ms var(--ease-out)}
28
+ .sugg-row:hover{background:var(--hover)}
29
+ .sugg-name{font-size:12.5px;font-weight:550}
30
+ .sugg-dir{font:400 10.5px var(--mono);color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
31
+ #toast{position:fixed;left:50%;bottom:28px;transform:translate(-50%,12px);max-width:520px;padding:11px 18px;border-radius:12px;background:var(--raised);border:1px solid var(--line);color:var(--text);font-size:13px;line-height:18px;opacity:0;pointer-events:none;transition:opacity 250ms var(--ease-out),transform 250ms var(--ease-out);z-index:60}
32
+ #toast.show{opacity:1;transform:translate(-50%,0)}.firstrun{position:absolute;left:50%;bottom:40px;transform:translateX(-50%);z-index:2;display:flex;align-items:center;gap:16px;max-width:640px;padding:14px 20px;border:1px solid var(--line);border-radius:14px;background:var(--surface);box-shadow:0 12px 40px var(--shadow-strong)}.firstrun p{color:var(--dim);font-size:13px;line-height:18px;text-wrap:pretty;margin:0}.firstrun strong{color:var(--text);font-weight:600}.firstrun .btn{flex:none}.firstrun.inline{position:static;transform:none;max-width:none;margin-top:8px}
33
+ .zero{display:flex;flex-direction:column;justify-content:center;gap:18px;max-width:600px;min-height:100%;margin:0 auto;padding:48px 32px}
34
+ .zero h1{font-size:24px;line-height:32px;font-weight:600;letter-spacing:-.03em}
35
+ .zero .zero-sub{color:var(--dim);text-wrap:pretty}
36
+ .zero-feed{display:flex;flex-direction:column;gap:2px;padding:10px 4px;border-radius:14px;background:var(--surface)}
37
+ .zero-row{display:flex;align-items:center;gap:10px;min-height:30px;padding:0 12px;font:400 12.5px/16px var(--mono);color:var(--soft)}
38
+ .zero-row .dot{flex:none;background:var(--accent)}
39
+ .zero-row:first-child .dot{animation:working-pulse 1.6s ease-in-out infinite}
40
+ .zero-row .zf{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
41
+ .zero-row .za{margin-left:auto;color:var(--faint);flex:none;font-variant-numeric:tabular-nums}
42
+ .zero-empty{padding:14px 12px;color:var(--faint);font-size:12.5px}
43
+ .fleet{position:absolute;inset:0;z-index:3;display:none;overflow:auto;background:var(--bg);padding:56px 48px}
44
+ .fleet.on{display:block;animation:reveal 500ms var(--ease-out) both}
45
+ .fleet-head{max-width:1080px;margin:0 auto 22px;display:flex;align-items:baseline;gap:14px}
46
+ .fleet-head h1{font-size:22px;font-weight:600;letter-spacing:-.03em}
47
+ .fleet-head .fh-sub{color:var(--faint);font-size:12.5px}
48
+ .fleet-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px;max-width:1080px;margin:0 auto}
49
+ .fleet-card{position:relative;padding:16px 18px;border:1px solid var(--line);border-radius:16px;background:var(--surface);cursor:pointer;transition:border-color 300ms var(--ease-out),background 300ms var(--ease-out)}
50
+ .fleet-card:hover{background:var(--raised);border-color:var(--faint)}
51
+ .fleet-card.glow{border-color:rgba(233,162,59,.55);box-shadow:0 0 24px rgba(233,162,59,.08)}
52
+ .fleet-name{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:600}
53
+ .fleet-name .live-dot{width:6px;height:6px;border-radius:50%;background:var(--faint)}
54
+ .fleet-card.glow .live-dot{background:var(--success);animation:working-pulse 1.6s ease-in-out infinite}
55
+ .fleet-minimap{display:flex;flex-wrap:wrap;gap:5px;margin-top:12px}
56
+ .fleet-sq{width:12px;height:12px;border-radius:3.5px;border:1.6px solid var(--faint)}
57
+ .fleet-sq.done{border-color:transparent;background:#3f7f57}
58
+ .fleet-sq.active{border-color:var(--accent)}
59
+ .fleet-sq.blocked{border-color:transparent;background:var(--danger)}
60
+ .fleet-sq.hot{box-shadow:0 0 8px rgba(233,162,59,.7);border-color:var(--accent)}
61
+ .fleet-agents{display:flex;align-items:center;gap:8px;margin-top:12px;min-height:20px}
62
+ .fleet-agents svg{width:15px;height:15px}
63
+ .fleet-tool{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dim);font:400 11.5px/16px var(--mono)}
64
+ .fleet-idle{color:var(--faint);font-size:12px}
65
+ .fleet-back{position:absolute;right:24px;top:24px;z-index:4}
66
+ body.hide-activity .obs-ring,body.hide-activity .obs-label,body.hide-activity .obs-file,body.hide-activity .task-capsule.obsrow{display:none!important}
67
+ body.hide-runs .runs,body.hide-runs .task-capsule.sess{display:none!important}
68
+ .icon.ov-off{opacity:.35}
69
+ .file-ic{stroke:var(--faint);fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
70
+ .file-ic.hot{stroke:var(--accent)}
71
+ .gnode .file-label{fill:var(--dim);font:400 10.5px var(--mono)}
72
+ .gnode .file-label.hot{fill:var(--accent)}
73
+ .minimap{position:absolute;right:24px;bottom:24px;z-index:2;border:1px solid var(--line);border-radius:10px;background:var(--minimap-bg);box-shadow:0 12px 40px var(--shadow-strong);cursor:pointer}
74
+ .minimap svg{display:block}
75
+ .mm-node{fill:var(--faint)}.mm-node.active{fill:var(--accent)}.mm-node.blocked{fill:var(--danger)}.mm-node.done{fill:#3f7f57}
76
+ .mm-view{fill:none;stroke:var(--soft);stroke-width:1.2;opacity:.8}
77
+ .trail{fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1 7;opacity:.55}
78
+ .handoff-label{font:600 10px var(--mono);letter-spacing:.04em}
79
+ .runs{position:absolute;right:24px;top:24px;z-index:2;display:flex;flex-direction:column;gap:10px;width:340px;max-width:44vw}
80
+ .cycle-row{display:flex;gap:8px;align-items:baseline;padding:6px 10px;border:1px solid var(--line);border-radius:10px;background:var(--overlay);font-size:11.5px;line-height:15px;color:var(--dim)}
81
+ .cycle-when{flex:none;color:var(--faint);font:500 10px var(--mono)}
82
+ .cycle-what{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
83
+ .run-sub{display:flex;align-items:center;gap:7px;margin-top:6px;padding:4px 8px;border-radius:7px;background:var(--hover);font-size:11.5px;color:var(--dim)}
84
+ .run-sub.ended{background:transparent;opacity:.55}
85
+ .run-sub-dot{width:5px;height:5px;border-radius:50%;background:var(--accent);animation:working-pulse 1.6s ease-in-out infinite}
86
+ .run-sub-tick{color:var(--success);font-size:10px}
87
+ .run-sub-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
88
+ .run-sub-ago{color:var(--faint);font:400 10px var(--mono)}
89
+ .run-cycle{margin:4px 2px 0;color:var(--faint);font:600 10.5px var(--mono);letter-spacing:.08em;text-transform:uppercase}
90
+ .run-card{padding:12px 14px;border:1px solid var(--line);border-radius:14px;background:var(--overlay);box-shadow:0 12px 40px var(--shadow-strong);cursor:pointer;animation:reveal 500ms var(--ease) both}
91
+ .run-head{display:flex;align-items:center;gap:8px}
92
+ .run-agent{display:inline-flex;flex:none;width:16px;height:16px}
93
+ .run-name{font-weight:600;font-size:13px}
94
+ .run-elapsed{margin-left:auto;color:var(--faint);font-size:11.5px;font-variant-numeric:tabular-nums}
95
+ .run-live-dot{width:6px;height:6px;border-radius:50%;background:var(--success);animation:working-pulse 1.6s ease-in-out infinite}
96
+ .run-card.ended .run-live-dot{background:var(--faint);animation:none}
97
+ .run-task{margin-top:6px;color:var(--text);font-size:12.5px;line-height:17px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
98
+ .run-tool{display:flex;gap:8px;align-items:baseline;margin-top:5px;color:var(--dim);font:400 11.5px/16px var(--mono);overflow:hidden;white-space:nowrap}
99
+ .run-tool .tn{color:var(--accent);flex:none}
100
+ .run-tool .td{overflow:hidden;text-overflow:ellipsis}
101
+ .run-tool .ta{margin-left:auto;color:var(--faint);flex:none;font-variant-numeric:tabular-nums}
102
+ .run-detail{margin-top:8px;border-top:1px solid var(--hover);padding-top:8px;display:none}
103
+ .run-card.open .run-detail{display:block}
104
+ .run-todo{display:flex;gap:8px;align-items:center;min-height:20px;color:var(--dim);font-size:12px}
105
+ .run-todo.in_progress{color:var(--text)}
106
+ .run-todo.completed{color:var(--faint);text-decoration:line-through}
107
+ .run-todo .box{width:10px;height:10px}
108
+ .run-hist{margin-top:6px;color:var(--faint);font:400 11px/16px var(--mono);display:flex;justify-content:space-between;gap:8px}
109
+ .run-hist .hd{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-legend{position:sticky;bottom:0;margin-top:auto;padding:8px 16px;background:var(--surface);border-top:1px solid var(--hover);display:flex;align-items:center;gap:7px;color:var(--accent);opacity:.72;font-size:11px;line-height:15px}.tree-legend-dot{flex:none;width:6px;height:6px;border-radius:50%;background:var(--accent)}.primary{min-width:0;min-height:0;overflow:auto}.plan-view{padding:40px 48px 80px}.plan-inner{width:100%;max-width:960px;margin:auto}.intro{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:24px;margin-bottom:32px}.eyebrow{margin-bottom:8px;color:var(--dim);font-size:12px;font-weight:500;letter-spacing:.04em}.title{font-size:30px;line-height:36px;font-weight:600;letter-spacing:-.04em;text-wrap:balance}.subtitle{max-width:65ch;margin-top:8px;color:var(--dim);text-wrap:pretty}.summary{min-width:176px;text-align:right}.summary-value{font-size:24px;line-height:32px;font-weight:600;font-variant-numeric:tabular-nums}.summary-label{color:var(--dim);font-size:12px}.track{height:4px;margin-top:8px;overflow:hidden;border-radius:2px;background:var(--hover)}.fill{height:100%;transform-origin:left;background:var(--soft);transition:transform 700ms var(--ease)}
110
+ .phases{display:grid;gap:12px}.phase{overflow:hidden;background:var(--surface);border:1px solid transparent;border-radius:16px;transition:border-color 700ms var(--ease),background 700ms var(--ease)}.phase:hover{background:var(--raised)}.phase.open{border-color:var(--line);background:var(--raised)}.phase.selected{border-color:var(--faint)}.phase-head{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:center;gap:16px;width:100%;min-height:72px;padding:12px 16px;border:0;background:transparent;color:var(--text);text-align:left;cursor:pointer}.phase-index{color:var(--faint);font:500 12px/16px var(--mono);font-variant-numeric:tabular-nums}.phase-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:16px;line-height:24px;font-weight:550}.phase-detail{display:block;margin-top:2px;color:var(--dim);font-size:12px;line-height:16px}.phase-meta{display:flex;align-items:center;gap:12px;color:var(--dim);font-variant-numeric:tabular-nums}.meter{width:64px;height:4px;overflow:hidden;border-radius:2px;background:var(--hover)}.meter span{display:block;height:100%;background:var(--soft)}.flag{padding:2px 8px;border-radius:4px;color:var(--accent);background:var(--accent-bg);font-size:12px;line-height:16px;font-weight:500}.chev{display:grid;place-items:center;color:var(--faint);transition:transform 700ms var(--ease),color 700ms var(--ease)}.open .chev{transform:rotate(180deg);color:var(--soft)}.phase-body{display:none;padding:0 12px 16px 48px}.open .phase-body{display:block;animation:reveal 800ms var(--ease) both}@keyframes reveal{from{opacity:0;transform:translateY(16px);filter:blur(4px)}to{opacity:1;transform:none;filter:blur(0)}}.tasks{display:grid;gap:4px;padding:8px;background:var(--surface);border-radius:12px}.task{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;width:100%;min-height:44px;padding:8px 12px;border:0;border-radius:8px;background:transparent;color:var(--soft);text-align:left;cursor:pointer;transition:color 700ms var(--ease),background 700ms var(--ease),transform 700ms var(--ease)}.task:hover,.task.selected{background:var(--hover);color:var(--text)}.task:active{transform:scale(.99)}.task.done{color:var(--dim)}.task.active{color:var(--text)}.box{position:relative;z-index:1;width:12px;height:12px;border:1px solid var(--faint);border-radius:50%;background:var(--surface)}.done .box{border-color:var(--soft);background:var(--soft)}.active .box{border-color:var(--accent);background:var(--surface)}.active .box:after{content:"";position:absolute;inset:3px;border-radius:50%;background:var(--accent);animation:breathe 1800ms var(--ease-out) infinite alternate}.revised .box{border-color:var(--accent);border-style:dashed}@keyframes breathe{from{opacity:.4;transform:scale(.65)}to{opacity:1;transform:scale(1)}}.task-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.task-meta{color:var(--faint);font-size:12px}.task-meta.current{color:var(--accent)}.activity{margin:4px 0 8px 40px;padding:12px 16px;border-left:1px solid var(--line);color:var(--dim);font:400 12px/16px var(--mono)}.activity-line{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:12px;padding:4px 0}.pulse{color:var(--accent)}.activity-file{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.activity-time{color:var(--faint);font-variant-numeric:tabular-nums}.empty{display:grid;place-items:center;min-height:320px;color:var(--dim);text-align:center}
111
+ .graph-view{position:relative;overflow:hidden;background:var(--bg);touch-action:none}.graph-grid{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;opacity:.35}.graph-stage{position:absolute;inset:0;cursor:grab}.graph-stage.dragging{cursor:grabbing}.graph-svg{display:block;width:100%;height:100%;overflow:visible}.world{transform-origin:0 0}.edge{fill:none;stroke:var(--line);stroke-width:1.5;vector-effect:non-scaling-stroke}.edge.link{stroke:var(--faint);stroke-dasharray:2 7;stroke-linecap:round;opacity:.8}.edge.active{stroke:var(--accent);stroke-dasharray:5 8;animation:travel 1800ms linear infinite}.edge.handoff{stroke-width:2.5;stroke-dasharray:9 7;animation:travel 1400ms linear infinite;opacity:.9}@keyframes travel{to{stroke-dashoffset:-26}}.gnode{cursor:pointer}.gnode .node-box{fill:var(--card);stroke:var(--line);stroke-width:1;transition:fill 700ms var(--ease),stroke 700ms var(--ease)}.gnode:hover .node-box{fill:var(--card-hover);stroke:var(--faint)}.gnode.selected .node-box,.gnode.active .node-box{stroke:var(--accent);stroke-width:1.5}.gnode.blocked .node-box{stroke:var(--danger);stroke-width:1.5}.gnode.knowledge .node-box{stroke:var(--knowledge);stroke-width:1.5;fill:color-mix(in srgb,var(--knowledge) 9%,var(--card))}.gnode.human .node-box{stroke:var(--human);stroke-width:1.5;stroke-dasharray:7 5;fill:color-mix(in srgb,var(--human) 7%,var(--card))}.gnode.human .kicker{fill:var(--human)}.gnode.human.obs-live .node-box,:root[data-theme="light"] .gnode.human.obs-live .node-box{fill:color-mix(in srgb,var(--human) 12%,var(--card));stroke:var(--human)}.gnode.human .obs-ring{stroke:var(--human)}.gnode.human .obs-label{fill:var(--human)}.node-url{cursor:pointer}.node-url text{fill:var(--human);font:700 10.5px var(--mono);letter-spacing:.06em}.node-url:hover text{text-decoration:underline}.gnode:not(.human) .node-url text{fill:var(--accent)}.gnode.knowledge .kicker{fill:var(--knowledge)}.node-spin{animation:capsule-spin 1100ms linear infinite;transform-box:fill-box;transform-origin:center}.obs-ring,.obs-label{opacity:0;transition:opacity 400ms var(--ease-out)}.gnode.obs-live .obs-ring{opacity:1;animation:capsule-spin 1300ms linear infinite;transform-box:fill-box;transform-origin:center}.gnode.obs-live .obs-label{opacity:1}:root[data-theme="light"] .gnode.obs-live .node-box{fill:color-mix(in srgb,#e9a23b 11%,var(--card));stroke:var(--accent);stroke-width:2}:root[data-theme="light"] .gnode.obs-live .obs-label{font-weight:700}.gnode.knowledge.obs-live .node-box,:root[data-theme="light"] .gnode.knowledge.obs-live .node-box{fill:color-mix(in srgb,var(--knowledge) 13%,var(--card));stroke:var(--knowledge)}.gnode.knowledge .obs-ring{stroke:var(--knowledge)}.gnode.knowledge .obs-label{fill:var(--knowledge)}.gnode .kicker{fill:var(--faint);font:500 11px var(--mono);letter-spacing:.04em}.gnode .node-title{fill:var(--text);font:600 15px var(--font)}.gnode .node-meta{fill:var(--dim);font:400 12px var(--font);font-variant-numeric:tabular-nums}.gnode .progress-bg{fill:var(--hover)}.gnode .progress{fill:var(--soft)}.gnode.active .progress{fill:var(--accent)}.gnode .port{fill:var(--raised);stroke:var(--faint);stroke-width:1}.gnode .revision{fill:var(--accent)}.expanded-node-card{position:relative;width:100%;height:100%;padding:16px;color:var(--text);font:400 12px/16px var(--font);overflow:hidden;animation:reveal 800ms var(--ease) both}.expanded-node-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:start;padding-bottom:12px}.expanded-node-kicker{color:var(--faint);font:500 11px/16px var(--mono);letter-spacing:.04em}.expanded-node-title{margin-top:2px;font-size:16px;line-height:24px;font-weight:600;letter-spacing:-.02em}.expanded-node-summary{color:var(--dim);font-variant-numeric:tabular-nums}.expanded-node-tasks{display:grid;gap:4px;padding:8px;border-radius:8px;background:var(--raised)}.expanded-node-task{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;min-height:40px;padding:8px;border-radius:4px;color:var(--dim)}.expanded-node-task.active{color:var(--text);background:var(--hover)}.expanded-node-task .box{width:12px;height:12px;border-radius:50%}.expanded-node-task .task-title{font-size:12px;line-height:16px}.expanded-node-task .task-meta{white-space:nowrap}.expanded-node-activity{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:8px;margin:8px 0 0 32px;padding:8px 12px;border-radius:8px;background:var(--raised);color:var(--dim);font:400 11px/16px var(--mono)}.expanded-node-change{margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}.expanded-node-change-head{display:flex;justify-content:space-between;gap:12px;margin-bottom:4px;color:var(--dim);font-weight:500}.expanded-node-diff{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:400 11px/16px var(--mono)}.expanded-node-diff.rm{color:var(--danger)}.expanded-node-diff.ad{color:var(--success)}.expanded-node-stable{color:var(--faint)}.expanded-node-collapse{position:absolute;right:16px;bottom:12px;color:var(--faint);font:500 11px/16px var(--mono);letter-spacing:.04em}.canvas-tools{position:absolute;left:24px;top:24px;display:flex;align-items:center;gap:4px;padding:4px;border:1px solid var(--line);border-radius:12px;background:var(--overlay);box-shadow:0 12px 40px var(--shadow-strong)}.icon{display:grid;place-items:center;width:32px;height:32px;padding:0;border:0;border-radius:8px;background:transparent;color:var(--dim);cursor:pointer;transition:color 700ms var(--ease),background 700ms var(--ease),transform 700ms var(--ease)}.icon:hover{color:var(--text);background:var(--hover)}.icon:active{transform:scale(.96)}.zoom{min-width:48px;color:var(--dim);text-align:center;font:500 12px var(--mono);font-variant-numeric:tabular-nums}.divider{width:1px;height:20px;margin:0 4px;background:var(--line)}.canvas-hint{position:absolute;left:24px;bottom:24px;display:flex;gap:16px;color:var(--faint);font-size:12px;pointer-events:none}.hint{display:flex;align-items:center;gap:6px}.key{display:grid;place-items:center;min-width:20px;height:20px;padding:0 4px;border:1px solid var(--line);border-radius:4px;background:var(--surface);color:var(--dim);font:500 11px var(--mono)}
112
+ :root[data-theme="light"] .gnode.active{filter:drop-shadow(0 8px 12px var(--accent-bg))}:root[data-theme="light"] .gnode.active .node-box{fill:color-mix(in srgb,#e9a23b 11%,var(--card));stroke:var(--accent);stroke-width:2}
113
+ .inspector{position:relative;z-index:2;min-width:0;overflow:auto;padding:24px;background:var(--surface);border-left:1px solid var(--hover)}.inspector-top{display:flex;align-items:flex-start;gap:12px;margin-bottom:24px}.inspector-heading{min-width:0;flex:1}.inspector-kicker{color:var(--faint);font-size:12px;font-weight:500;letter-spacing:.04em}.inspector-title{margin-top:4px;font-size:18px;line-height:28px;font-weight:600;letter-spacing:-.02em;text-wrap:balance}.inspector-sub{margin-top:4px;color:var(--dim);font-size:12px;text-wrap:pretty}.section{padding:16px 0;border-top:1px solid var(--hover)}.section-label{margin-bottom:12px;color:var(--faint);font-size:12px;font-weight:500}.stats{display:grid;grid-template-columns:1fr 1fr;gap:8px}.stat{padding:12px;border-radius:8px;background:var(--raised)}.stat-value{font-size:16px;line-height:24px;font-weight:600;font-variant-numeric:tabular-nums}.stat-label{color:var(--faint);font-size:12px}.dependencies,.changes,.detail-tasks{display:grid;gap:8px}.dependency{display:flex;align-items:center;gap:8px;color:var(--soft)}.dependency:before{content:"";width:8px;height:1px;background:var(--faint)}.diff{padding-left:16px;text-indent:-16px;font:400 12px/20px var(--mono);overflow-wrap:anywhere}.diff.rm{color:var(--danger)}.diff.ad{color:var(--success)}.detail-task{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;width:100%;min-height:40px;padding:8px;border:0;border-radius:8px;background:var(--raised);color:var(--soft);text-align:left;cursor:pointer;transition:color 700ms var(--ease),background 700ms var(--ease),transform 700ms var(--ease)}.detail-task:hover{color:var(--text);background:var(--hover)}.detail-task:active{transform:scale(.98)}.detail-task .task-title{font-size:12px;line-height:16px}.detail-task .task-meta{white-space:nowrap}.activity-card{padding:12px;border-radius:8px;background:var(--raised);color:var(--dim);font:400 12px/16px var(--mono)}.activity-card strong{display:block;overflow:hidden;text-overflow:ellipsis;color:var(--soft);font-weight:500;white-space:nowrap}.all-clear{padding:24px 0}.clear-mark{display:grid;place-items:center;width:40px;height:40px;margin-bottom:16px;border:1px solid var(--line);border-radius:12px;color:var(--soft)}.all-clear h2{font-size:16px;line-height:24px;font-weight:600}.all-clear p{max-width:28ch;margin-top:4px;color:var(--dim);text-wrap:pretty}.gnode .node-action{fill:var(--faint);font:500 11px var(--mono);letter-spacing:.04em}.gnode:hover .node-action,.gnode.selected .node-action{fill:var(--accent)}
114
+ .detail-tether{fill:none;stroke:var(--faint);stroke-width:1;stroke-dasharray:3 4;vector-effect:non-scaling-stroke}.graph-detail .detail-box{fill:var(--surface);stroke:var(--faint);stroke-width:1}.expanded-plan-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:start;margin-bottom:12px}.expanded-plan-file{font-size:14px;line-height:20px;font-weight:600}.expanded-plan-subtitle{color:var(--dim);font-size:12px;line-height:16px}.expanded-plan-state{padding:2px 8px;border-radius:4px;color:var(--accent);background:var(--accent-bg);font-size:12px;line-height:16px}.expanded-phase-list{display:grid;gap:4px}.expanded-phase{position:relative;--segment-progress:0;padding:8px;border-radius:8px;color:var(--dim)}.expanded-phase:not(:last-child):before,.expanded-phase:not(:last-child):after{content:"";position:absolute;z-index:0;left:13px;top:20px;bottom:-24px;width:2px;border-radius:2px;transform-origin:top}.expanded-phase:not(:last-child):before{background:var(--line)}.expanded-phase:not(:last-child):after{background:var(--soft);transform:scaleY(var(--segment-progress))}.expanded-phase.active:not(:last-child):after{background:var(--accent);animation:rail-live 1800ms linear infinite alternate}.expanded-phase.open{background:var(--raised);color:var(--text);box-shadow:inset 0 0 0 1px var(--line)}.expanded-phase-row{position:relative;z-index:1;display:grid;grid-template-columns:12px minmax(0,1fr) auto;align-items:center;gap:8px;min-height:24px}.expanded-phase-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:16px}.expanded-phase-count{color:var(--faint);font-variant-numeric:tabular-nums}.expanded-phase .expanded-node-tasks{position:relative;z-index:1;margin:4px 0 0 20px;padding:4px;background:transparent}.expanded-phase .expanded-node-task{min-height:24px;padding:4px}.expanded-phase .expanded-node-task.active{background:var(--hover)}@keyframes rail-live{from{opacity:.5}to{opacity:1}}
115
+ .actions{display:flex;gap:8px;margin-top:16px}.btn{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:4px 14px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--soft);cursor:pointer;font-weight:500;transition:color 700ms var(--ease),background 700ms var(--ease),border-color 700ms var(--ease),transform 700ms var(--ease)}.btn:hover{color:var(--text);background:var(--hover)}.btn:active{transform:scale(.98)}.btn.accent{border-color:var(--accent-bg);color:var(--accent);background:var(--accent-bg)}.btn.accent:hover{color:var(--text);background:var(--accent-bg)}
116
+ .toast{position:fixed;left:50%;bottom:24px;z-index:6;transform:translate(-50%,8px);padding:8px 16px;border:1px solid var(--line);border-radius:8px;background:var(--raised);color:var(--soft);font-size:12px;opacity:0;pointer-events:none;transition:opacity 700ms var(--ease),transform 700ms var(--ease)}.toast.show{opacity:1;transform:translate(-50%,0)}
117
+ .task-capsules{display:flex;flex-direction:column;gap:8px;margin:8px 0 0 20px}.task-capsules.list{gap:0;overflow:hidden;border-radius:16px;background:var(--surface);box-shadow:0 2px 12px var(--shadow-soft)}.graph-task-card{width:100%;height:100%}.graph-task-card .task-capsules{width:100%;margin:0}.graph-task-empty{display:grid;place-items:center;min-height:64px;padding:16px;border-radius:16px;background:var(--surface);color:var(--dim)}.task-capsule{align-self:stretch;overflow:hidden;border-radius:22px;background:var(--surface);box-shadow:0 2px 12px var(--shadow-soft);animation:capsule-enter 450ms var(--ease-out) both;transition:border-radius 250ms var(--ease-out),background-color 250ms var(--ease-out)}.task-capsules.list .task-capsule,.task-capsules.list .task-capsule.open{border-bottom:1px solid var(--line);border-radius:0;background:transparent;box-shadow:none}.task-capsules.list .task-capsule:last-child{border-bottom:0}.task-capsule.open{border-radius:14px}.task-capsule-button{display:flex;align-items:center;gap:8px;width:100%;height:44px;padding:0 8px;border:0;background:transparent;color:var(--text);text-align:left;cursor:pointer}.task-capsule-button:active{transform:translateY(1px)}.task-capsule-marker-wrap{display:flex;flex:none;align-items:center;justify-content:center;width:24px;height:24px}.task-capsule-marker{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;width:24px;height:24px;color:var(--text);font-size:10px;line-height:16px;font-weight:600;font-variant-numeric:tabular-nums}.task-capsule-marker svg{position:absolute;inset:0;display:block;width:24px;height:24px;fill:none}.task-capsule-marker .ring-track{stroke:var(--line);stroke-width:2}.task-capsule-marker .ring-live{visibility:hidden;stroke:var(--dim);stroke-width:2;stroke-linecap:round;stroke-dasharray:19.35 49.76;animation:capsule-spin 1100ms linear infinite}.task-capsule.active .task-capsule-marker .ring-live,.task-capsule.obs-spin .task-capsule-marker .ring-live{visibility:visible}.task-capsule-badge{display:flex;flex:none;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;color:#fff;background:#3f7f57;animation:capsule-pop 300ms var(--ease-out) both}.task-capsule-badge svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round}.task-capsule-copy{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:16px;font-weight:500}.task-capsule-amount{flex:none;color:var(--dim);font-size:12px;line-height:16px;font-variant-numeric:tabular-nums}.task-capsule-pill{display:inline-flex;flex:none;align-items:center;height:22px;padding:0 8px;border-radius:999px;color:var(--complete-text);background:var(--complete-bg);font-size:11px;line-height:16px;font-weight:var(--complete-weight)}.task-capsule-pill.revised{color:var(--accent);background:var(--accent-bg)}.task-capsule-pill.blocked{color:#e79490;background:rgba(219,107,103,.16)}.task-capsule-pill.now{color:#e08bb8;background:rgba(215,111,163,.16)}.task-capsule-pill.backlog{color:var(--faint);background:var(--hover)}.task-capsule.sess-ended{opacity:.5}.task-capsule.sess-ended .ring-live{visibility:hidden!important}.task-capsule-badge.blocked{background:var(--danger)}.agent-chip{display:inline-flex;flex:none;align-items:center;justify-content:center;width:16px;height:16px;font-size:11px;line-height:1;font-weight:600}.agent-chip.faded{opacity:var(--done-agent-opacity)}.task-capsule-chevron{display:flex;flex:none;align-items:center;justify-content:center;width:28px;height:28px;margin-left:-8px;border-radius:50%;color:var(--faint)}.task-capsule-chevron svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;transition:transform 250ms var(--ease-out)}.task-capsule.open .task-capsule-chevron svg{transform:rotate(180deg)}.task-capsule-detail{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows 250ms var(--ease-out),opacity 200ms var(--ease-out)}.task-capsule.open .task-capsule-detail{grid-template-rows:1fr;opacity:1}.task-capsule-detail-clip{overflow:hidden}.task-capsule-detail-grid{display:grid;grid-template-columns:24px minmax(0,1fr);gap:8px;margin-bottom:12px;padding:0 8px}.task-capsule-detail-line{width:1px;height:100%;margin:auto;background:var(--line)}.task-capsule-detail-list{display:flex;min-width:0;flex-direction:column;gap:4px}.task-capsule-detail-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:20px;color:var(--dim);font-size:12px;line-height:16px;animation:capsule-detail-enter 300ms var(--ease-out) both}.task-capsule-detail-meta{overflow:hidden;color:var(--faint);font:400 11px/16px var(--mono);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap}.task-capsule.active .task-capsule-copy{color:var(--text)}@media(hover:hover) and (pointer:fine){.task-capsule:hover{background:var(--hover)}}@keyframes capsule-enter{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}@keyframes capsule-detail-enter{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}@keyframes capsule-pop{from{opacity:0;transform:scale(.95)}to{opacity:1;transform:none}}@keyframes capsule-spin{to{transform:rotate(360deg)}}
118
+ @media(max-width:960px){.topbar{grid-template-columns:minmax(0,1fr) auto}.switcher{grid-column:1/-1;grid-row:2}.tab{flex:1;justify-content:center}.workspace{height:calc(100dvh - 113px);grid-template-columns:minmax(0,1fr) 288px}.watching{display:none}.plan-view{padding:32px 24px 64px}}@media(max-width:720px){.topbar{padding:8px 12px;gap:12px}.badge{max-width:128px;overflow:hidden;text-overflow:ellipsis}.workspace{display:block;height:auto;min-height:calc(100dvh - 105px);overflow:visible}.primary{min-height:calc(100dvh - 105px)}.plan-view{padding:32px 16px 64px}.intro{display:block}.summary{margin-top:24px;text-align:left}.phase-head{grid-template-columns:auto minmax(0,1fr) auto;gap:12px}.phase-meta{display:none}.phase-body{padding-left:12px}.inspector{position:relative;left:auto;right:auto;bottom:auto;max-height:none;margin:0 12px 12px;border:1px solid var(--line);border-radius:16px}.graph-view{min-height:calc(100dvh - 105px)}.canvas-hint{display:none}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation-duration:1ms!important;animation-iteration-count:1!important;transition-duration:1ms!important}}
119
+ </style>
120
+ </head>
121
+ <body>
122
+ <a class="skip" href="#graph">Skip to diagram</a>
123
+ <div class="app"><header class="topbar">
124
+ <div class="brand"><img class="mark" src="data:image/png;base64,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" alt="" aria-hidden="true"><div class="project"><div class="project-name" id="proj">agenttrail</div><div class="file-name" id="plan-file">workspace / PLAN.md</div></div></div>
125
+ <div class="status"><button class="theme-toggle" id="theme-toggle" type="button" role="switch" aria-checked="false" aria-label="Use light theme" onclick="toggleTheme()"><span id="theme-toggle-label">Light</span><span class="theme-track" aria-hidden="true"><span class="theme-thumb"></span></span></button><span id="state"></span><span class="watching">watching</span></div>
126
+ </header>
127
+ <div class="workspace"><aside class="sidebar" id="tree" aria-label="Repository files"><div class="tree-empty">Connecting to the live monitor…</div></aside><section class="primary graph-view" id="graph" aria-label="Plan flow diagram"></section><div class="runs" id="runs"></div><div class="minimap" id="minimap" title="Jump around the map" style="display:none"></div></div></div>
128
+ <div class="toast" id="toast" role="status"></div>
129
+ <script>
130
+ let M=null,W=[],BP='',lastGraphKey='',graphPos={},regions={},minZoom=0.15,graphZoom=1,graphPan={x:0,y:0},graphSize={width:0,height:0},graphNeedsFit=true,panGesture=null
131
+ const openGraphDetails=new Set(),openGraphTasks=new Set(),closedGraphTasks=new Set(),closedGraphDetails=new Set()
132
+ function compLive(p){return p.status==='active'||(p.touchedAt&&Date.now()-p.touchedAt<60000)||((M&&M.runs)||[]).some(r=>!r.ended&&r.componentId===p.id)}
133
+ function isUnfolded(p){const k=ns(p.id);if(closedGraphDetails.has(k))return false;return openGraphDetails.has(k)||compLive(p)}
134
+ const openFolders=new Set(),closedFolders=new Set();let lastReveal=new Set()
135
+ const ns=id=>BP+':'+id
136
+ const expandedRoots=new Set(),treeCache={}
137
+ let addRepoOpen=false,addRepoFocus=false,addSuggestions=[]
138
+ async function openAddRepo(){addRepoOpen=true;addRepoFocus=true;try{addSuggestions=await(await fetch('/suggest')).json()}catch{addSuggestions=[]}renderTree()}
139
+ function renderAddSuggestions(){const box=document.getElementById('add-sugg');if(!box)return
140
+ const q=(document.getElementById('add-path')?.value||'').trim().toLowerCase()
141
+ const list=addSuggestions.filter(p=>!q||p.toLowerCase().includes(q)).slice(0,8)
142
+ box.innerHTML=list.map(p=>{const name=p.split('/').pop(),dir=p.slice(0,p.length-name.length)
143
+ return`<button type="button" class="sugg-row" onclick="pickSuggestion(this.dataset.p)" data-p="${esc(p)}"><span class="sugg-name">${esc(name)}</span><span class="sugg-dir">${esc(dir)}</span></button>`}).join('')||(q?`<div class="tree-add-msg">no known repo matches — full path works too</div>`:'')}
144
+ function pickSuggestion(p){const i=document.getElementById('add-path');if(i)i.value=p;submitAddRepo()}
145
+ function toast(msg,ms){let t=document.getElementById('toast');if(!t){t=document.createElement('div');t.id='toast';document.body.appendChild(t)}t.textContent=msg;t.classList.add('show');clearTimeout(t._h);t._h=setTimeout(()=>t.classList.remove('show'),ms||6000)}
146
+ let setupBusy=false
147
+ async function setupBoard(port){if(setupBusy)return;setupBusy=true;toast('setting up…')
148
+ try{const r=await(await fetch('/setup-board',{method:'POST',body:JSON.stringify({port})})).json()
149
+ if(r.ok&&r.prompt){try{await navigator.clipboard.writeText(r.prompt)
150
+ toast('done — a note for your agent is on your clipboard. paste it to the agent working in this repo and the map draws itself.',12000)}
151
+ catch{toast('set up. now ask the agent working in this repo to fill in PLAN.md — the map draws itself.',12000)}
152
+ setTimeout(pollWorld,1500)}
153
+ else toast(r.error||'could not set it up — that board may be running an older version; restart it and try again',9000)
154
+ }catch{toast('could not reach the board',6000)}
155
+ setTimeout(()=>setupBusy=false,4000)}
156
+ let addRepoBusy=false
157
+ async function submitAddRepo(){const i=document.getElementById('add-path'),msg=document.getElementById('add-msg');if(!i||!i.value.trim()||addRepoBusy)return
158
+ addRepoBusy=true;setTimeout(()=>addRepoBusy=false,8000)
159
+ msg.textContent='starting the board…'
160
+ try{const r=await(await fetch('/spawn',{method:'POST',body:JSON.stringify({path:i.value.trim()})})).json()
161
+ if(r.ok&&r.already)msg.textContent='already on the map — flying there';if(r.ok&&r.already)setTimeout(()=>{flyToRegion(r.already);addRepoOpen=false;renderTree()},600)
162
+ else if(r.ok){msg.textContent='started — appearing shortly…';setTimeout(()=>{addRepoOpen=false;pollWorld()},5000)}
163
+ else msg.textContent=r.error||'could not start it'
164
+ }catch{msg.textContent='could not reach the daemon'}}
165
+ const openRuns=new Set()
166
+ const $=id=>document.getElementById(id)
167
+ function syncTheme(){const light=document.documentElement.dataset.theme==='light',toggle=$('theme-toggle'),label=$('theme-toggle-label');if(toggle){toggle.setAttribute('aria-checked',String(light));toggle.setAttribute('aria-label',light?'Use dark theme':'Use light theme')}if(label)label.textContent=light?'Dark':'Light';document.querySelector('meta[name="theme-color"]')?.setAttribute('content',light?'#fff':'#000')}
168
+ function toggleTheme(){const next=document.documentElement.dataset.theme==='light'?'dark':'light';document.documentElement.dataset.theme=next;try{localStorage.setItem('at-theme',next)}catch{}syncTheme()}
169
+ syncTheme()
170
+ const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]))
171
+ const ago=ms=>{const s=Math.max(0,Math.round((Date.now()-ms)/1000));return s<60?`${s}s`:s<3600?`${Math.round(s/60)} min`:`${Math.round(s/3600)} h`}
172
+ const AGENT_COLORS={claude:'#d97757',codex:'var(--agent-openai)',chatgpt:'var(--agent-openai)',openai:'var(--agent-openai)',cursor:'#5b7fd6',human:'#8b8b93',k:'#8b8b93'}
173
+ const AGENT_ICONS={claude:'m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z',codex:'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z',chatgpt:'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z',openai:'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z'}
174
+ function agentIcon(n){return AGENT_ICONS[n.toLowerCase()]||null}
175
+ const AGENT_GLYPHS={cursor:'\u25c6'}
176
+ function agentGlyph(n){return AGENT_GLYPHS[n.toLowerCase()]||n[0].toUpperCase()}
177
+ function agentColor(n){const k=n.toLowerCase();if(AGENT_COLORS[k])return AGENT_COLORS[k];let h=0;for(const c of k)h=(h*31+c.charCodeAt(0))%360;return`hsl(${h} 45% 45%)`}
178
+ const statusLabel=s=>s==='done'?'Complete':s==='active'?'In progress':s==='blocked'?'Blocked':'Not started',pct=(done,total)=>total?Math.round(done/total*100):0
179
+ const es=new EventSource('/events');let connectionTimer=null
180
+ es.onopen=()=>clearTimeout(connectionTimer)
181
+ // AOS patch: a daemon restart mints a new session.id — reload this stale page instead of waiting for Cmd+R
182
+ let seenSessionId=null
183
+ function checkSessionId(id){if(!id)return;if(seenSessionId===null)seenSessionId=id;else if(id!==seenSessionId)location.reload()}
184
+ async function pollWorld(){try{const d=await(await fetch('/world')).json();if(Array.isArray(d)&&d.length){W=d;if(M){const self=d.find(x=>x.port===M.port);if(self)checkSessionId(self.session&&self.session.id)}render()}}catch{}}
185
+ setInterval(pollWorld,2000)
186
+ function syncSelfIntoW(){if(!M)return;const i=W.findIndex(b=>b.port===M.port);const lite={...M};delete lite.tree;if(i>=0)W[i]=lite;else{W.push(lite);W.sort((a,b)=>a.port-b.port)}}
187
+ // AOS patch: asks panel — open questions from the daemon, GO/Nein to answer, decisions log
188
+ let askDecisions=[],answering=new Set(),askSig=''
189
+ function selfAsks(){return((M&&M.asks)||[]).slice().sort((a,b)=>(a.askedAt||0)-(b.askedAt||0))}
190
+ function askStatusLabel(a){return a.expired||a.status==='expired'?'Abgelaufen':a.status==='go'?'GO':a.status==='deny'?'Nein':'Wartet auf dich'}
191
+ function askCardHtml(a){
192
+ const dim=!!(a.expired||a.status==='expired'||a.status==='go'||a.status==='deny'),busy=answering.has(String(a.id))
193
+ const btn=(d,cls)=>`<button class="btn ${cls}" type="button" data-id="${esc(String(a.id))}" data-decision="${d}" onclick="answerAsk(this.dataset.id,this.dataset.decision)"${dim||busy?' disabled':''}>${d==='go'?'GO':'Nein'}</button>`
194
+ return`<div class="run-card${dim?' ended':''}" data-ask="${esc(String(a.id))}"${dim?' style="opacity:.6;cursor:default"':''}>
195
+ <div class="run-head"><span class="run-live-dot"></span><span class="run-name">${esc(askStatusLabel(a))}</span>${a.askedBy?`<span style="color:var(--faint);font-size:11.5px">· ${esc(a.askedBy)}</span>`:''}<span class="run-elapsed"><span class="t-ago" data-at="${a.askedAt||Date.now()}"></span></span></div>
196
+ <div class="run-task" style="white-space:normal">${esc(a.question)}</div>
197
+ ${dim?'':`<div class="actions" style="margin-top:10px">${btn('go','accent')}${btn('deny','')}</div>`}
198
+ </div>`}
199
+ function decisionsHtml(){
200
+ return`<div class="run-card ended" style="cursor:default"><div class="run-task" style="margin-top:0;white-space:normal;color:var(--faint)">Entscheidungen</div>${askDecisions.map(d=>{const ts=d.at?Date.parse(d.at):NaN
201
+ return`<div class="run-hist"${d.question?` title="${esc(d.question)}"`:''}><span class="hd">${esc(String(d.by||'?'))} · ${isFinite(ts)?`<span class="t-ago" data-at="${ts}"></span>`:esc(String(d.at||''))} · ${esc(String(d.decision||''))}</span></div>`}).join('')}</div>`}
202
+ async function refreshDecisions(){
203
+ const sig=selfAsks().map(a=>`${String(a.id)}:${a.status}:${a.expired?1:0}`).join('|')
204
+ if(sig===askSig)return;askSig=sig
205
+ const asks=selfAsks(),newest=asks[asks.length-1]
206
+ if(!newest){askDecisions=[];renderRuns();return}
207
+ try{const d=await(await fetch('/ask/'+encodeURIComponent(String(newest.id)))).json();if(Array.isArray(d.decisions))askDecisions=d.decisions}catch{}
208
+ renderRuns();renderTimes()}
209
+ async function answerAsk(key,decision){
210
+ const ask=selfAsks().find(x=>String(x.id)===String(key));if(!ask)return
211
+ if(answering.has(String(ask.id)))return;answering.add(String(ask.id));renderRuns()
212
+ try{const r=await fetch('/answer',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({id:ask.id,decision})})
213
+ if(!r.ok){const e=await r.json().catch(()=>({}));toast(e.error||'answer rejected',6000)}}
214
+ catch{toast('could not reach the daemon',6000)}
215
+ answering.delete(String(ask.id));askSig='';renderRuns();renderTimes();refreshDecisions()}
216
+ es.onmessage=e=>{clearTimeout(connectionTimer);const d=JSON.parse(e.data)
217
+ if(d.partial&&M){M.activity=d.activity;M.recentActivity=d.recentActivity;M.runs=d.runs||M.runs||[];M.handoffs=d.handoffs||M.handoffs||[];M.hotFiles=d.hotFiles||{};M.asks=d.asks||M.asks||[];for(const n of M.plan)if(n.level==='component'){n.touchedAt=d.touched[n.id]||null;n.recent=(d.compRecent||{})[n.id]||n.recent||[]}syncSelfIntoW();render();refreshDecisions();return}
218
+ M=d;checkSessionId(d.session&&d.session.id);syncSelfIntoW();if(!W.length)pollWorld();render();refreshDecisions()}
219
+ es.onerror=()=>{if(!M){clearTimeout(connectionTimer);connectionTimer=setTimeout(renderConnectionError,1200)}}
220
+ setInterval(()=>{if(M)renderTimes()},1000)
221
+ function isOpen(p){if(userClosed.has(p.id))return false;if(userOpen.has(p.id))return true;return p.status==='active'}
222
+ function kids(id){return M.plan.filter(n=>n.parent===id)}
223
+ function renderBadge(){
224
+ const working=M.activity&&(Date.now()-M.activity.at)<60000
225
+ let html=working?`<span class="badge working"><span class="dot"></span>Working · <span class="t-ago" data-at="${M.activity.at}"></span> ago</span>`:'<span class="badge"><span class="dot"></span>Plan is live</span>'
226
+ if(M.planStale&&M.planStale.stale)html+=` <span class="badge changed" title="Code has moved for ${M.planStale.minutes} minutes without a plan update. Tell your agent: re-verify PLAN.md against the code."><span class="dot"></span>Plan behind · ${M.planStale.minutes}m</span>`
227
+ if(M.lints&&M.lints.length)html+=` <span class="badge" style="cursor:help" title="${esc(M.lints.join('\n'))}">⚠ ${M.lints.length} plan note${M.lints.length===1?'':'s'}</span>`
228
+ $('state').innerHTML=html
229
+ }
230
+ function render(){
231
+ $('proj').textContent=M.session.project;renderBadge()
232
+ // AOS patch: update the plan file subtitle when planFile is present
233
+ const planFileEl = $('plan-file')
234
+ if (planFileEl) {
235
+ if (M.planFile) {
236
+ planFileEl.textContent = M.planFile
237
+ } else {
238
+ planFileEl.textContent = 'workspace / PLAN.md'
239
+ }
240
+ }
241
+ document.title=`agenttrail · ${M.session.project}`
242
+ renderTree();renderRuns()
243
+ const bs=W.length?W:(M?[M]:[])
244
+ const graphKey=JSON.stringify(bs.map(b=>[b.port,JSON.stringify(b.plan,(k,v)=>(k==='touchedAt'||k==='recent')?undefined:v),(b.runs||[]).map(r=>[r.componentId,r.ended,r.todos,(r.path||[]).length,(r.subagents||[]).map(x=>x.name+(x.ended?1:0)).join()]),(b.handoffs||[]).length,b.plan.filter(n=>n.level==='component').map(n=>n.touchedAt&&Date.now()-n.touchedAt<60000?1:0).join('')]))+'|'+String(graphZoom<CARD_LOD)
245
+ if(graphKey!==lastGraphKey){lastGraphKey=graphKey;renderGraph()}
246
+ renderTimes()
247
+ }
248
+ function renderConnectionError(){
249
+ $('state').innerHTML='<span class="badge changed"><span class="dot"></span>Not connected</span>'
250
+ $('tree').innerHTML='<div class="tree-empty">Could not reach the live monitor.</div>'
251
+ $('graph').innerHTML='<div class="empty"><div><strong>Could not reach the live monitor</strong><br>Start agenttrail, then open this page from its local address.</div></div>'
252
+ }
253
+ function isFolderOpen(p){if(closedFolders.has(p))return false;if(openFolders.has(p))return true;return lastReveal.has(p)}
254
+ function renderTree(){
255
+ if(addRepoOpen&&document.getElementById('add-path'))return
256
+ const boards=boardsAll()
257
+ if(!boards.length){$('tree').innerHTML='<div class="tree-empty">Connecting to the live monitor…</div>';return}
258
+ if(!expandedRoots.size&&M)expandedRoots.add(M.port)
259
+ let html=''
260
+ for(const b of boards){
261
+ const open=expandedRoots.has(b.port)
262
+ html+=`<button class="tree-row dir root ${open?'open':''}" type="button" data-root="${b.port}" style="padding-left:8px;min-height:28px"><span class="tree-chev"><svg viewBox="0 0 24 24"><path d="m9 6 6 6-6 6"/></svg></span><span class="tree-name" style="font-weight:600;color:var(--text)">${esc(b.project)}</span></button>`
263
+ if(open)html+=renderRootTree(b)
264
+ }
265
+ html+=addRepoOpen?`<div class="tree-add-form"><input id="add-path" placeholder="/path/to/repo — or pick below" autocomplete="off" spellcheck="false" oninput="renderAddSuggestions()" onkeydown="if(event.key==='Enter')submitAddRepo()"><div class="tree-sugg" id="add-sugg"></div><div class="row"><button class="btn small primary" type="button" onclick="submitAddRepo()">Add board</button><button class="btn small" type="button" onclick="addRepoOpen=false;renderTree()">Cancel</button></div><div class="tree-add-msg" id="add-msg">Its region appears on the map within a few seconds.</div></div>`:`<button class="tree-add" type="button" onclick="openAddRepo()">+ add repo</button>`
266
+ html+=`<div class="tree-legend"><span class="tree-legend-dot"></span>most recent edit · time since last write</div>`
267
+ $('tree').innerHTML=html
268
+ if(addRepoOpen){renderAddSuggestions();const i=document.getElementById('add-path');if(i&&addRepoFocus){i.focus();addRepoFocus=false}}
269
+ }
270
+ function renderRootTree(b){
271
+ const isSelf=M&&b.port===M.port
272
+ const tr=isSelf?M.tree:(treeCache[b.port]&&treeCache[b.port].tree)
273
+ if(!tr){if(!isSelf)loadTree(b.port);return '<div class="tree-empty">loading…</div>'}
274
+ const act=new Map((b.recentActivity||[]).map(a=>[a.file,a.at]))
275
+ lastReveal=new Set()
276
+ if(b.recentActivity&&b.recentActivity[0]){const parts=b.recentActivity[0].file.split('/');let p='';for(let i=0;i<parts.length-1;i++){p=p?p+'/'+parts[i]:parts[i];lastReveal.add(b.port+'|'+p)}}
277
+ const rows=[]
278
+ const walk=(nodes,depth)=>{for(const n of nodes){const pad=22+depth*14
279
+ const key=b.port+'|'+n.path
280
+ if(n.dir){const open=isFolderOpen(key)
281
+ rows.push(`<button class="tree-row dir ${open?'open':''}" type="button" data-path="${esc(key)}" style="padding-left:${pad}px"><span class="tree-chev"><svg viewBox="0 0 24 24"><path d="m9 6 6 6-6 6"/></svg></span><span class="tree-name">${esc(n.name)}</span></button>`)
282
+ if(open)walk(n.children||[],depth+1)
283
+ }else{const at=act.get(n.path)
284
+ rows.push(`<div class="tree-row file${at?' hot':''}" style="padding-left:${pad+20}px"><span class="tree-name">${esc(n.name)}</span>${at?`<span class="tree-ago"><span class="t-ago" data-at="${at}"></span></span>`:''}</div>`)}}}
285
+ walk(tr,0)
286
+ return rows.join('')
287
+ }
288
+ async function loadTree(port){if(treeCache[port])return;treeCache[port]={}
289
+ try{const d=await(await fetch('/tree-of?port='+port)).json();treeCache[port]={tree:d.tree||[]};renderTree();renderTimes()}catch{treeCache[port]={tree:[]}}}
290
+ $('tree').addEventListener('click',e=>{
291
+ const root=e.target.closest('.tree-row.root')
292
+ if(root){const p=+root.dataset.root
293
+ if(e.target.closest('.tree-chev')){expandedRoots.has(p)?expandedRoots.delete(p):expandedRoots.add(p)}
294
+ else{expandedRoots.add(p);flyToRegion(p)}
295
+ renderTree();renderTimes();return}
296
+ const bEl=e.target.closest('.tree-row.dir');if(!bEl)return;const p=bEl.dataset.path
297
+ if(isFolderOpen(p)){openFolders.delete(p);closedFolders.add(p)}else{closedFolders.delete(p);openFolders.add(p)}
298
+ renderTree();renderTimes()})
299
+ const overlays={activity:true,runs:true}
300
+ try{Object.assign(overlays,JSON.parse(localStorage.getItem('at-overlays')||'{}'))}catch{}
301
+ function applyOverlays(){document.body.classList.toggle('hide-activity',!overlays.activity);document.body.classList.toggle('hide-runs',!overlays.runs)
302
+ const a=$('ov-activity'),r=$('ov-runs');if(a)a.classList.toggle('ov-off',!overlays.activity);if(r)r.classList.toggle('ov-off',!overlays.runs)}
303
+ function toggleOverlay(k){overlays[k]=!overlays[k];try{localStorage.setItem('at-overlays',JSON.stringify(overlays))}catch{};applyOverlays()}
304
+ function fitWorld(){const stage=$('graph-stage');if(!stage||!graphSize.width)return;const box=stage.getBoundingClientRect();graphZoom=Math.max(minZoom,Math.min((box.width-80)/graphSize.width,(box.height-80)/graphSize.height));graphPan={x:(box.width-graphSize.width*graphZoom)/2,y:(box.height-graphSize.height*graphZoom)/2};updateTransform()}
305
+ function flyToRegion(port){const r=regions[port];if(!r)return;const stage=$('graph-stage');if(!stage)return;const box=stage.getBoundingClientRect()
306
+ const z=Math.max(.45,Math.min(1.1,(box.width-128)/r.w,(box.height-128)/r.h))
307
+ const world=$('world');if(world){world.style.transition='transform 600ms cubic-bezier(.23,1,.32,1)';setTimeout(()=>{if(world)world.style.transition=''},650)}
308
+ graphZoom=z;graphPan={x:(box.width-r.w*z)/2-r.ox*z,y:(box.height-r.h*z)/2-r.oy*z};updateTransform()}
309
+ function renderZero(){
310
+ const feed=(M.recentActivity||[]).slice(0,8).map(a=>`<div class="zero-row"><span class="dot"></span><span class="zf">${esc(a.file)}${a.n>1?` ×${a.n}`:''}</span><span class="za t-ago" data-at="${a.at}"></span></div>`).join('')
311
+ // AOS patch: show AOS-specific message when aosPlan is true, otherwise show upstream init/backfill block
312
+ const emptyStateBlock = M.aosPlan
313
+ ? `<div class="firstrun inline"><p>Waiting for the plan — the pipeline's Architect writes ${esc(M.planFile||'the plan file')}; the map appears as soon as it exists.</p></div>`
314
+ : `<div class="firstrun inline"><p><strong>Level 2 — the map.</strong> You never write PLAN.md by hand: copy this prompt, paste it to your agent in this repo, and it draws the component map the board runs on.</p><button class="btn accent" type="button" onclick="copyBackfill()">Copy backfill prompt</button></div>`
315
+ $('graph').innerHTML=`<div class="zero">
316
+ <div><h1>Watching ${esc(M.session.project)}</h1><p class="zero-sub">Every file an agent touches shows up below, live — and hooked Claude Code sessions appear as run cards with their plan and current tool. No setup needed for any of that.</p></div>
317
+ <div class="zero-feed">${feed||'<div class="zero-empty">Waiting for the first file write — start your agent and watch this fill in.</div>'}</div>
318
+ ${emptyStateBlock}
319
+ </div>`
320
+ }
321
+ function isSkeletonPlan(){return M.plan.filter(n=>n.level==='task').length<=1}
322
+ function boardLayout(b){
323
+ const phases=b.plan.filter(n=>n.level==='component')
324
+ const byId=Object.fromEntries(phases.map(p=>[p.id,p])),depth={},visiting=new Set()
325
+ function getDepth(p){if(depth[p.id]!=null)return depth[p.id];if(visiting.has(p.id))return 0;visiting.add(p.id);const preds=p.needs.filter(id=>byId[id]);depth[p.id]=preds.length?1+Math.max(...preds.map(id=>getDepth(byId[id]))):0;visiting.delete(p.id);return depth[p.id]}
326
+ phases.forEach(getDepth);const cols={};phases.forEach(p=>(cols[depth[p.id]]??=[]).push(p));const gx=120,gy=48,pad=64,pos={},metrics=Object.fromEntries(phases.map(p=>[p.id,graphNodeMetrics(p)])),columnWidths={}
327
+ for(const[col,list]of Object.entries(cols))columnWidths[col]=Math.max(...list.map(p=>metrics[p.id].w))
328
+ const orderedColumns=Object.keys(cols).map(Number).sort((a,b)=>a-b),columnX={};let nextX=pad
329
+ for(const col of orderedColumns){columnX[col]=nextX;nextX+=columnWidths[col]+gx}
330
+ let maxBottom=pad
331
+ for(const[col,list]of Object.entries(cols)){let nextY=pad;for(const p of list){const size=metrics[p.id],x=columnX[col]+(columnWidths[col]-size.w)/2;pos[p.id]={x,y:nextY,...size,nodeX:x+(size.w-size.nodeW)/2,nodeY:nextY};nextY+=size.h+gy}maxBottom=Math.max(maxBottom,nextY-gy+pad)}
332
+ return{phases,byId,pos,width:Math.max(nextX-gx+pad,560),height:Math.max(maxBottom,300)}
333
+ }
334
+ function boardInnerHtml(b,L){
335
+ const {phases,byId,pos}=L
336
+ const seenLinks=new Set(),linkPairs=[]
337
+ for(const p of phases)for(const l of p.links||[]){if(!byId[l])continue;const key=[p.id,l].sort().join('|');if(seenLinks.has(key))continue;seenLinks.add(key);linkPairs.push([byId[l],p])}
338
+ const traveled={}
339
+ for(const r of (b.runs||[])){
340
+ const path=(r.path||[])
341
+ for(let i=1;i<path.length;i++){
342
+ if(Date.now()-path[i].at>45*60e3)continue
343
+ traveled[path[i-1].c+'|'+path[i].c]={agent:r.agent,at:path[i].at}
344
+ }
345
+ }
346
+ const trails=(b.runs||[]).map(r=>{
347
+ const ids=[...(r.path||[]).map(x=>x.c)];if(r.componentId&&ids[ids.length-1]!==r.componentId)ids.push(r.componentId)
348
+ const pts=ids.map(id=>pos[id]).filter(Boolean).map(q=>`${q.nodeX+200},${q.nodeY+64}`)
349
+ return pts.length>1?`<polyline class="trail" points="${pts.join(' ')}" stroke="${agentColor(r.agent)}" style="opacity:${r.ended?0.2:0.55}"/>`:''
350
+ }).join('')
351
+ const edges=trails+phases.flatMap(p=>p.needs.map(need=>graphEdge(byId[need],p,pos,traveled[need+'|'+p.id]))).join('')+linkPairs.map(([a,b2])=>graphLinkEdge(a,b2,pos,traveled[a.id+'|'+b2.id]||traveled[b2.id+'|'+a.id])).join('')
352
+ const nodes=phases.map((p,i)=>graphNode(p,i,pos[p.id])).join('')
353
+ return edges+nodes
354
+ }
355
+ function regionCardHtml(b,L){
356
+ const w=L.width,h=L.height
357
+ const live=(b.runs||[]).filter(r=>!r.ended)
358
+ const sq=b.plan.filter(n=>n.level==='component').map((c,i)=>`<rect x="${40+i*30}" y="${h/2+4}" width="20" height="20" rx="5" fill="${c.status==='done'?'#3f7f57':c.status==='blocked'?'var(--danger)':'none'}" stroke="${c.status==='active'?'var(--accent)':'var(--faint)'}" stroke-width="2.5"/>`).join('')
359
+ const marks=live.map((r,i)=>{const ic=agentIcon(r.agent);return ic?`<g transform="translate(${40+i*34} ${h/2+44}) scale(1.1)"><path fill="${agentColor(r.agent)}" d="${ic}"/></g>`:''}).join('')
360
+ return`<text x="40" y="${h/2-36}" fill="var(--text)" font-size="44" font-weight="650" font-family="var(--font)" letter-spacing="-1">${esc(b.project)}</text>${sq}${marks}${!b.hasPlan?`<text x="40" y="${h/2+44}" fill="var(--faint)" font-size="16" font-family="var(--font)">nothing mapped yet</text><g class="setup-btn" onclick="setupBoard(${b.port})" style="cursor:pointer"><rect x="40" y="${h/2+64}" width="172" height="40" rx="11" fill="var(--accent-bg)" stroke="var(--accent)" stroke-width="1.2"/><text x="126" y="${h/2+89}" text-anchor="middle" fill="var(--accent)" font-size="15.5" font-weight="600" font-family="var(--font)">Set up the map</text></g>`:''}${live.length?`<text x="${w-40}" y="${h/2-36}" text-anchor="end" fill="var(--dim)" font-size="16" font-family="var(--font)">${live.length} running</text>`:''}`
361
+ }
362
+ const CARD_LOD=0.3
363
+ let _saved=null
364
+ function setBoardGlobals(b){_saved={M,BP};M={...b,session:{project:b.project},tree:(M&&M.port===b.port)?M.tree:[]};BP=String(b.port)}
365
+ function restoreBoardGlobals(){if(_saved){M=_saved.M;BP=_saved.BP;_saved=null}}
366
+ function renderGraph(){
367
+ const boards=boardsAll()
368
+ if(!boards.length)return
369
+ if(boards.length===1&&M&&(!M.hasPlan||isSkeletonPlan())){renderZero();return}
370
+ regions={};graphPos={}
371
+ const GAP=160,ROWMAX=3600;let cx=80,cy=80,rowH=0
372
+ let content=''
373
+ const realM=M,realBP=BP
374
+ for(const b of boards){
375
+ const eff=(realM&&b.port===realM.port)?{...realM,project:realM.session.project}:b
376
+ M=eff;BP=String(b.port)
377
+ const L=boardLayout(eff)
378
+ if(cx>80&&cx+L.width>ROWMAX){cx=80;cy+=rowH+GAP;rowH=0}
379
+ const ox=cx,oy=cy
380
+ regions[b.port]={ox,oy,w:L.width,h:L.height+56,project:eff.project||b.project}
381
+ for(const[id,q]of Object.entries(L.pos))graphPos[BP+':'+id]={...q,nodeX:q.nodeX+ox,nodeY:q.nodeY+56+oy}
382
+ const hot=eff.activity&&Date.now()-eff.activity.at<60000
383
+ const cardMode=graphZoom<CARD_LOD||!eff.hasPlan
384
+ content+=`<g transform="translate(${ox} ${oy})"><rect width="${L.width}" height="${L.height+56}" rx="26" fill="var(--region)" stroke="${hot?'rgba(233,162,59,.5)':'var(--line)'}" stroke-width="1.5"/><text x="28" y="38" fill="var(--faint)" font-size="15" font-weight="600" font-family="var(--mono)" letter-spacing=".08em">${esc((eff.project||'').toUpperCase())}${eff.planStale&&eff.planStale.stale?`<tspan fill="var(--accent)"> · PLAN BEHIND</tspan>`:''}</text>${cardMode?regionCardHtml(eff,L):`<g transform="translate(0 56)">${boardInnerHtml(eff,L)}</g>`}</g>`
385
+ cx+=L.width+GAP;rowH=Math.max(rowH,L.height+56)
386
+ }
387
+ M=realM;BP=realBP
388
+ const width=Math.max(...Object.values(regions).map(r=>r.ox+r.w))+80
389
+ const height=Math.max(...Object.values(regions).map(r=>r.oy+r.h))+80
390
+ graphSize={width,height}
391
+ minZoom=Math.max(0.06,Math.min(920/width,700/height)*0.75)
392
+ $('graph').innerHTML=`<svg class="graph-grid"><defs><pattern id="grid" width="24" height="24" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="1" fill="var(--grid)"/></pattern></defs><rect width="100%" height="100%" fill="url(#grid)"/></svg><div class="graph-stage" id="graph-stage" onpointerdown="startPan(event)" onpointermove="movePan(event)" onpointerup="endPan(event)" onpointercancel="endPan(event)"><svg class="graph-svg"><title>World map</title><defs><marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0 0 10 5 0 10z" fill="var(--grid)"/></marker></defs><g class="world" id="world" transform="translate(${graphPan.x} ${graphPan.y}) scale(${graphZoom})">${content}</g></svg></div><div class="canvas-tools"><button class="icon" aria-label="Zoom out" onclick="zoomBy(.85)"><svg viewBox="0 0 24 24"><path d="M6 12h12"/></svg></button><span class="zoom" id="zoom">${Math.round(graphZoom*100)}%</span><button class="icon" aria-label="Zoom in" onclick="zoomBy(1.18)"><svg viewBox="0 0 24 24"><path d="M12 6v12M6 12h12"/></svg></button><span class="divider"></span><button class="icon" id="ov-activity" aria-label="Activity overlay" title="Activity overlay" onclick="toggleOverlay('activity')"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="2.5" fill="currentColor" stroke="none"/></svg></button><button class="icon" id="ov-runs" aria-label="Runs overlay" title="Runs overlay" onclick="toggleOverlay('runs')"><svg viewBox="0 0 24 24"><rect x="4" y="5" width="16" height="6" rx="2"/><rect x="4" y="14" width="10" height="5" rx="2"/></svg></button><span class="divider"></span><button class="icon" aria-label="Fit world" title="Fit the whole world" onclick="fitWorld()"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg></button><button class="icon" aria-label="Fit my repo" title="Fit this repo" onclick="fitGraph()"><svg viewBox="0 0 24 24"><path d="M9 4H4v5M15 4h5v5M20 15v5h-5M4 15v5h5"/></svg></button></div>`
393
+ renderMinimap();applyOverlays();$('graph-stage').addEventListener('wheel',onGraphWheel,{passive:false});if(graphNeedsFit){graphNeedsFit=false;requestAnimationFrame(fitGraph)}
394
+ }
395
+ function graphDetailHeight(p){const list=kids(p.id),openRows=list.filter(graphTaskIsOpen).reduce((a,t)=>a+16+Math.max(graphTaskDetails(t).rows.length,5)*20,0);const sessN=(M.runs||[]).filter(r=>r.componentId===p.id).reduce((a,r)=>a+(r.todos||[]).length,0)+((p.touchedAt&&Date.now()-p.touchedAt<60000&&!list.some(k=>k.status==='active'||k.status==='blocked'))?1:0);return (list.length+sessN)?(list.length+sessN)*44+Math.max(0,list.length+sessN-1)+openRows:64}
396
+ function graphNodeMetrics(p){const nodeW=400;let nodeH=128;if(fileMode&&(p.filesList||[]).length)nodeH=110+Math.ceil(Math.min(p.filesList.length,8)/2)*24+(p.filesList.length>8?30:10);if(!isUnfolded(p))return{w:nodeW,h:nodeH,nodeW,nodeH,detailW:0,detailH:0};const detailW=400,detailH=graphDetailHeight(p);return{w:detailW,h:nodeH+24+detailH,nodeW,nodeH,detailW,detailH}}
397
+ function graphEdge(source,target,pos,handoff){if(!source||!pos[source.id]||!pos[target.id])return'';const a=pos[source.id],b=pos[target.id],x1=a.nodeX+a.nodeW,y1=a.nodeY+a.nodeH/2,x2=b.nodeX,y2=b.nodeY+b.nodeH/2,bend=Math.max(48,(x2-x1)/2),active=source.status==='active'||target.status==='active',d=`M${x1} ${y1}C${x1+bend} ${y1} ${x2-bend} ${y2} ${x2} ${y2}`
398
+ if(handoff){const c=agentColor(handoff.agent),mx=(x1+x2)/2,my=(y1+y2)/2-10
399
+ return`<path class="edge handoff" d="${d}" style="stroke:${c}" marker-end="url(#arrow)"/><text x="${mx}" y="${my}" text-anchor="middle" fill="${c}" font-size="10.5" font-weight="650" font-family="var(--mono)">${esc(handoff.agent)} ⇢</text>`}
400
+ return`<path class="edge ${active?'active':''}" d="${d}" marker-end="url(#arrow)"/>`}
401
+ function graphLinkEdge(a,b,pos){const A=pos[a.id],B=pos[b.id];if(!A||!B)return''
402
+ let x1,y1,x2,y2,d
403
+ if(Math.abs(A.nodeX-B.nodeX)>24){const L=A.nodeX<B.nodeX?A:B,R=A.nodeX<B.nodeX?B:A;x1=L.nodeX+L.nodeW;y1=L.nodeY+L.nodeH/2;x2=R.nodeX;y2=R.nodeY+R.nodeH/2;const mx=(x1+x2)/2;d=`M${x1} ${y1}C${mx} ${y1} ${mx} ${y2} ${x2} ${y2}`}
404
+ else{const T=A.nodeY<B.nodeY?A:B,Bt=A.nodeY<B.nodeY?B:A;x1=T.nodeX+T.nodeW/2;y1=T.y+T.h;x2=Bt.nodeX+Bt.nodeW/2;y2=Bt.nodeY;const my=(y1+y2)/2;d=`M${x1} ${y1}C${x1} ${my} ${x2} ${my} ${x2} ${y2}`}
405
+ if(arguments[3]){const h=arguments[3],c=agentColor(h.agent)
406
+ return`<path class="edge handoff" d="${d}" style="stroke:${c}"/><text x="${(x1+x2)/2}" y="${(y1+y2)/2-8}" text-anchor="middle" fill="${c}" font-size="10.5" font-weight="650" font-family="var(--mono)">${esc(h.agent)} ⇢</text>`}
407
+ return`<path class="edge link" d="${d}"/>`}
408
+ function graphNode(p,index,pos){return collapsedGraphNode(p,index,pos)+(!fileMode&&isUnfolded(p)?expandedGraphNode(p,pos):'')}
409
+ function graphTaskIsOpen(task){if(openGraphTasks.has(ns(task.id)))return true;if(closedGraphTasks.has(ns(task.id)))return false;return task.status==='active'}
410
+ function graphTaskMarker(task,index){if(task.status==='done')return`<span class="task-capsule-badge" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></span>`;if(task.status==='blocked')return`<span class="task-capsule-badge blocked" aria-hidden="true">!</span>`;return`<span class="task-capsule-marker" aria-hidden="true"><svg viewBox="0 0 24 24"><circle class="ring-track" cx="12" cy="12" r="11"/><circle class="ring-live" cx="12" cy="12" r="11"/></svg><span>${index+1}</span></span>`}
411
+ function graphTaskDetails(task){const comp=M.plan.find(x=>x.id===task.parent),compActive=M.plan.some(x=>x.parent===task.parent&&x.status==='active')
412
+ const live=!!(comp&&(comp.recent||[]).length&&(task.status==='active'||task.status==='blocked'||(task.status==='done'&&!compActive)))
413
+ const rows=live?comp.recent.slice(0,4).map(a=>({label:a.file+(a.n>1?` ×${a.n}`:''),meta:ago(a.at)})):[]
414
+ if(task.tech)rows.push({label:task.tech,meta:''})
415
+ if(!rows.length)rows.push({label:'No activity recorded yet',meta:''})
416
+ return{rows,live,comp}}
417
+ function graphSessionRow(t,i,ended){const st=t.status==='completed'?'done':t.status==='in_progress'?'active':'pending'
418
+ const marker=st==='done'?`<span class="task-capsule-badge" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></span>`:`<span class="task-capsule-marker" aria-hidden="true"><svg viewBox="0 0 24 24"><circle class="ring-track" cx="12" cy="12" r="11"/><circle class="ring-live" cx="12" cy="12" r="11"/></svg><span>${i+1}</span></span>`
419
+ return`<div class="task-capsule sess ${st}${ended?' sess-ended':''}" role="listitem"><button class="task-capsule-button" type="button" style="cursor:default"><span class="task-capsule-marker-wrap">${marker}</span><span class="task-capsule-copy">${esc(t.content)}</span><span class="task-capsule-pill now">Session plan</span></button></div>`}
420
+ function graphTaskRow(task,index){const open=graphTaskIsOpen(task),details=graphTaskDetails(task),pill=task.status==='blocked'?'<span class="task-capsule-pill blocked">Blocked</span>':task.status==='done'?'<span class="task-capsule-pill">Completed</span>':task.status==='pending'&&task.src==='agent'?'<span class="task-capsule-pill now">Session plan</span>':task.status==='pending'&&task.src==='roadmap'?'<span class="task-capsule-pill backlog">Roadmap</span>':'',amount=task.status==='active'?'Editing now':'';return`<div class="task-capsule ${task.status} ${open?'open':''}" data-task="${ns(task.id)}" role="listitem" style="animation-delay:${index*80}ms"><button class="task-capsule-button" type="button" aria-expanded="${open}" onclick="toggleGraphTask('${ns(task.id)}',event)"><span class="task-capsule-marker-wrap">${graphTaskMarker(task,index)}</span><span class="task-capsule-copy">${esc(task.title)}</span>${task.by?`<span class="agent-chip${task.status==='done'?' faded':''}" style="color:${agentColor(task.by)}" title="${esc(task.by)}">${agentIcon(task.by)?`<svg viewBox="0 0 24 24" width="14" height="14"><path fill="${agentColor(task.by)}" d="${agentIcon(task.by)}"/></svg>`:agentGlyph(task.by)}</span>`:''}${amount?`<span class="task-capsule-amount">${amount}</span>`:''}${pill}<span class="task-capsule-chevron" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="m6 9 6 6 6-6"/></svg></span></button><div class="task-capsule-detail"><div class="task-capsule-detail-clip"><div class="task-capsule-detail-grid"><span class="task-capsule-detail-line" aria-hidden="true"></span><div class="task-capsule-detail-list" data-dl-task="${ns(task.id)}" data-dl-comp="${task.parent?ns(task.parent):''}" data-tech="${esc(task.tech||'')}">${details.rows.map((detail,i)=>`<div class="task-capsule-detail-row" style="animation-delay:${120+i*80}ms"><span>${esc(detail.label)}</span><span class="task-capsule-detail-meta">${esc(detail.meta)}</span></div>`).join('')}</div></div></div></div></div>`}
421
+ function nodeStatusIcon(p,w){const cx=w-26,cy=26
422
+ if(p.kind==='human'&&p.status!=='done')return`<g><circle cx="${cx}" cy="${cy}" r="9" fill="var(--accent)"/><text x="${cx}" y="${cy+4}" text-anchor="middle" fill="#141414" font-size="12" font-weight="700" font-family="var(--font)">!</text><title>waiting on a human</title></g>`
423
+ if(p.status==='done')return`<g><circle cx="${cx}" cy="${cy}" r="9" fill="#3f7f57"/><path d="M${cx-4.5} ${cy} l3.2 3.2 l6-6.5" stroke="#fff" stroke-width="2.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></g>`
424
+ if(p.status==='active')return`<g><circle cx="${cx}" cy="${cy}" r="9" fill="none" stroke="var(--line)" stroke-width="2"/><circle class="node-spin" cx="${cx}" cy="${cy}" r="9" fill="none" stroke="var(--accent)" stroke-width="2" stroke-linecap="round" stroke-dasharray="16 41"/></g>`
425
+ if(p.status==='blocked')return`<g><circle cx="${cx}" cy="${cy}" r="9" fill="var(--danger)"/><text x="${cx}" y="${cy+4}" text-anchor="middle" fill="#fff" font-size="12" font-weight="700" font-family="var(--font)">!</text></g>`
426
+ return`<circle cx="${cx}" cy="${cy}" r="9" fill="none" stroke="var(--faint)" stroke-width="2"/>`}
427
+ function nodeAgentChips(p,w){const names=[...new Set(M.plan.filter(n=>n.parent===p.id&&(n.status==='active'||n.status==='blocked')&&n.by).map(n=>n.by))].slice(0,3)
428
+ return names.map((n,i)=>{const cx=w-26-24*(i+1),icon=agentIcon(n),c=agentColor(n)
429
+ const mark=icon?`<g transform="translate(${cx-6.5} ${19.5}) scale(0.5417)"><path fill="${c}" d="${icon}"/></g>`:`<text x="${cx}" y="30" text-anchor="middle" fill="${c}" font-size="11" font-weight="700" font-family="var(--font)">${agentGlyph(n)}</text>`
430
+ return`<g>${mark}<title>${esc(n)}</title></g>`}).join('')}
431
+ const FILE_ICONS={
432
+ code:'<path d="m9 8-4 4 4 4M15 8l4 4-4 4"/>',
433
+ image:'<rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="9" cy="10" r="1.6"/><path d="m5 17 5-5 4 4 2-2 3 3"/>',
434
+ page:'<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 9h18"/><path d="M6 6.5h.01M9 6.5h.01"/>',
435
+ doc:'<path d="M6 3h9l4 4v14H6z"/><path d="M9 11h7M9 15h7"/>',
436
+ data:'<path d="M9 4c-2 0-3 1-3 3v2c0 1-.7 2-2 2 1.3 0 2 1 2 2v3c0 2 1 3 3 3M15 4c2 0 3 1 3 3v2c0 1 .7 2 2 2-1.3 0-2 1-2 2v3c0 2-1 3-3 3"/>',
437
+ shell:'<rect x="3" y="4" width="18" height="16" rx="2"/><path d="m7 9 3 3-3 3M13 15h4"/>',
438
+ file:'<path d="M6 3h9l4 4v14H6z"/>'
439
+ }
440
+ function fileIcon(f){
441
+ const e=(f.split('.').pop()||'').toLowerCase()
442
+ if(['html','htm'].includes(e))return FILE_ICONS.page
443
+ if(['png','jpg','jpeg','gif','svg','webp','ico','icns'].includes(e))return FILE_ICONS.image
444
+ if(['py','js','mjs','ts','tsx','jsx','swift','go','rb','rs','c','cc','cpp','h','java','kt','css','scss'].includes(e))return FILE_ICONS.code
445
+ if(['md','txt','rst'].includes(e))return FILE_ICONS.doc
446
+ if(['json','yaml','yml','toml','plist','xml'].includes(e))return FILE_ICONS.data
447
+ if(['sh','bash','zsh','fish'].includes(e))return FILE_ICONS.shell
448
+ return FILE_ICONS.file
449
+ }
450
+ function fileFamily(f){
451
+ const base=f.split('/').pop(),di=base.lastIndexOf('.')
452
+ const ext=di>0?base.slice(di):'',stem=di>0?base.slice(0,di):base
453
+ const toks=stem.split(/[-_.]/)
454
+ while(toks.length>1&&/^(\d+x?\d*|dark|light|black|white|master|small|large|\dx|[0-9a-f]{6,})$/i.test(toks[toks.length-1]))toks.pop()
455
+ return toks.join('-')+ext
456
+ }
457
+ function fileConstellation(p,w){
458
+ const files=(p.filesList||[]).slice()
459
+ if(!files.length)return''
460
+ const hot=M.hotFiles||{}
461
+ const isHot=f=>hot[f]&&Date.now()-hot[f]<60000
462
+ // cluster variant families (3+ files sharing a stem) into one distinct row
463
+ const fam={}
464
+ for(const f of files)(fam[fileFamily(f)]=fam[fileFamily(f)]||[]).push(f)
465
+ const items=[]
466
+ for(const[key,members]of Object.entries(fam)){
467
+ if(members.length>=3){
468
+ const di=key.lastIndexOf('.'),label=(di>0?key.slice(0,di)+'-*'+key.slice(di):key+'-*')
469
+ items.push({label:`${label}`,count:members.length,files:members,heat:Math.max(...members.map(f=>hot[f]||0)),hot:members.some(isHot),icon:members[0]})
470
+ }else for(const f of members){
471
+ const base=f.split('/').pop()
472
+ items.push({label:base.length>21?base.slice(0,12)+'…'+base.slice(-7):base,count:1,files:[f],heat:hot[f]||0,hot:isHot(f),icon:f})
473
+ }
474
+ }
475
+ items.sort((a,b)=>b.heat-a.heat)
476
+ const shown=items.slice(0,8)
477
+ const restFiles=items.slice(8).reduce((a,x)=>a+x.count,0)
478
+ const run=(M.runs||[]).find(r=>!r.ended&&r.componentId===p.id)
479
+ const hottest=shown.filter(x=>x.hot).sort((a,b)=>b.heat-a.heat)[0]
480
+ let out=''
481
+ shown.forEach((it,i)=>{
482
+ const col=i%2,row=Math.floor(i/2),x=16+col*186,y=104+row*24
483
+ out+=`<g class="file-ic${it.hot?' hot':''}" transform="translate(${x-1} ${y-1}) scale(0.62)">${fileIcon(it.icon)}<title>${esc(it.files.join('\n'))}</title></g>`
484
+ out+=`<text class="file-label${it.hot?' hot':''}" x="${x+19}" y="${y+11}">${esc(it.label)}${it.count>1?` <tspan style="opacity:.6">×${it.count}</tspan>`:''}</text>`
485
+ if(it===hottest&&run){const ic=agentIcon(run.agent);if(ic)out+=`<g transform="translate(${x} ${y-15}) scale(0.5)"><path fill="${agentColor(run.agent)}" d="${ic}"/></g>`}
486
+ })
487
+ if(restFiles>0)out+=`<text class="file-label" x="16" y="${104+Math.ceil(shown.length/2)*24+16}" style="opacity:.55">+ ${restFiles} more files</text>`
488
+ return out
489
+ }
490
+ function handoffLabel(p,w,h){
491
+ const hs=(M.handoffs||[]).filter(x=>x.c===p.id&&Date.now()-x.at<10*60e3)
492
+ if(!hs.length)return''
493
+ const x=hs[hs.length-1]
494
+ return`<text class="handoff-label" x="${w-16}" y="${h-12}" text-anchor="end"><tspan fill="${agentColor(x.from)}">${esc(x.from)}</tspan><tspan fill="var(--faint)"> ⇄ </tspan><tspan fill="${agentColor(x.to)}">${esc(x.to)}</tspan></text>`
495
+ }
496
+ function collapsedGraphNode(p,index,pos){const list=kids(p.id),done=list.filter(n=>n.status==='done').length,progress=pct(done,list.length),label=p.title.length>28?`${p.title.slice(0,27)}…`:p.title,isOpen=isUnfolded(p),w=pos.nodeW,h=pos.nodeH,obsWord=p.status==='done'?'Revising':p.status==='blocked'?'Retrying':'Editing',chipN=new Set(M.plan.filter(n=>n.parent===p.id&&(n.status==='active'||n.status==='blocked')&&n.by).map(n=>n.by)).size;return`<g class="gnode ${p.status} ${p.kind==='knowledge'?'knowledge':p.kind==='human'?'human':''} ${isOpen?'selected':''}" data-id="${ns(p.id)}" transform="translate(${pos.nodeX} ${pos.nodeY})" role="button" tabindex="0" aria-expanded="${isOpen}" aria-label="${isOpen?'Hide':'Show'} tasks for ${esc(p.title)}" onclick="toggleGraphNode('${ns(p.id)}',event)" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();toggleGraphNode('${ns(p.id)}',event)}"><rect class="node-box" width="${w}" height="${h}" rx="12"/><circle class="port" cx="0" cy="${h/2}" r="4"/><circle class="port" cx="${w}" cy="${h/2}" r="4"/><text class="kicker" x="16" y="24">${esc((p.tech||'#'+p.id).slice(0,Math.min(42,Math.floor((w-170)/6.8))))}</text><text class="obs-label obs-file" x="${w-16}" y="76" text-anchor="end" fill="var(--accent)" font-size="11" font-family="var(--mono)"></text>${nodeStatusIcon(p,w)}<circle class="obs-ring" cx="${w-26}" cy="26" r="13" fill="none" stroke="var(--accent)" stroke-width="2" stroke-linecap="round" stroke-dasharray="23 59"/><text class="obs-label" x="${w-44-24*Math.min(chipN,3)}" y="30" text-anchor="end" fill="var(--accent)" font-size="11" font-weight="600" font-family="var(--font)">${obsWord}</text>${nodeAgentChips(p,w)}<text class="node-title" x="16" y="52">${esc(label)}</text><text class="node-meta" x="16" y="76">${done} of ${list.length} tasks · ${p.status==='pending'&&done>0?'Underway':statusLabel(p.status)}</text><rect class="progress-bg" x="16" y="92" width="${w-32}" height="4" rx="2"/><rect class="progress" x="16" y="92" width="${(w-32)*progress/100}" height="4" rx="2"/>${fileMode?fileConstellation(p,w):`<text class="node-action" x="16" y="116">${isOpen?'HIDE TASKS ↑':'SHOW TASKS ↓'}</text>`}${p.url?`<g class="node-url" onclick="event.stopPropagation();window.open(${JSON.stringify(p.url)},'_blank')"><rect x="${w-96}" y="104" width="84" height="18" rx="5" fill="transparent"/><text x="${w-16}" y="117" text-anchor="end">OPEN ↗</text></g>`:''}${handoffLabel(p,w,h)}${list.some(k=>k.status==='pending'&&k.src==='agent')?`<text x="${w-16}" y="116" text-anchor="end" fill="#e08bb8" font-size="10" font-weight="700" font-family="var(--mono)" letter-spacing=".06em">SESSION PLAN</text>`:''}</g>`}
497
+ function expandedGraphNode(p,pos){
498
+ const list=kids(p.id),detailY=pos.nodeY+pos.nodeH+24,centerX=pos.nodeX+pos.nodeW/2
499
+ const sessRows=(M.runs||[]).filter(r=>r.componentId===p.id).flatMap(r=>(r.todos||[]).map((t,i)=>graphSessionRow(t,i,r.ended))).join('')
500
+ const liveNow=p.touchedAt&&Date.now()-p.touchedAt<60000,declaredKid=list.some(k=>k.status==='active'||k.status==='blocked')
501
+ const liveSess=(M.runs||[]).some(r=>r.componentId===p.id&&!r.ended&&(r.todos||[]).length)
502
+ const a0=(p.recent||[])[0]
503
+ const obsRow=liveNow&&!declaredKid&&!liveSess&&a0?`<div class="task-capsule active obsrow" role="listitem"><button class="task-capsule-button" type="button" style="cursor:default"><span class="task-capsule-marker-wrap"><span class="task-capsule-marker" aria-hidden="true"><svg viewBox="0 0 24 24"><circle class="ring-track" cx="12" cy="12" r="11"/><circle class="ring-live" cx="12" cy="12" r="11"/></svg></span></span><span class="task-capsule-copy" data-obs-copy="${ns(p.id)}">Revising · ${esc(a0.file.split('/').pop())}</span><span class="task-capsule-amount" data-obs-ago="${ns(p.id)}">${ago(a0.at)}</span></button></div>`:''
504
+ const tasks=(list.length||sessRows||obsRow)?`<div class="task-capsules list" role="list" aria-label="${esc(p.title)} tasks">${list.map(graphTaskRow).join('')}${sessRows}${obsRow}</div>`:'<div class="graph-task-empty">No tasks in this phase.</div>'
505
+ return`<path class="detail-tether" d="M${centerX} ${pos.nodeY+pos.nodeH}V${detailY}"/><g class="graph-detail" data-detail="${ns(p.id)}" transform="translate(${pos.x} ${detailY})"><foreignObject width="${pos.detailW}" height="${pos.detailH}"><div xmlns="http://www.w3.org/1999/xhtml" class="graph-task-card">${tasks}</div></foreignObject></g>`
506
+ }
507
+ function toggleGraphNode(id,event){event?.stopPropagation()
508
+ const boards=boardsAll(),ci=id.indexOf(':'),b=boards.find(x=>String(x.port)===id.slice(0,ci))
509
+ const p=b&&b.plan.find(x=>x.id===id.slice(ci+1)&&x.level==='component')
510
+ const auto=p&&(p.status==='active'||(p.touchedAt&&Date.now()-p.touchedAt<60000)||((b.runs||[]).some(r=>!r.ended&&r.componentId===p.id)))
511
+ if(openGraphDetails.has(id)){openGraphDetails.delete(id);if(auto)closedGraphDetails.add(id)}
512
+ else if(closedGraphDetails.has(id)){closedGraphDetails.delete(id);openGraphDetails.add(id)}
513
+ else if(auto)closedGraphDetails.add(id)
514
+ else openGraphDetails.add(id)
515
+ renderGraph();renderTimes()}
516
+ function toggleGraphTask(id,event){event?.stopPropagation();const ci=id.indexOf(':'),port=id.slice(0,ci),raw=id.slice(ci+1);const b=W.find(x=>String(x.port)===port)||M;const task=b&&b.plan.find(n=>n.id===raw&&n.level==='task');if(!task)return
517
+ const save=BP;BP=port
518
+ if(graphTaskIsOpen(task)){openGraphTasks.delete(id);closedGraphTasks.add(id)}else{closedGraphTasks.delete(id);openGraphTasks.add(id)}
519
+ BP=save;renderGraph();renderTimes()}
520
+ let zoomUnfolded=false,fileMode=false,cardMode=false
521
+ function updateTransform(){const world=$('world');if(world)world.setAttribute('transform',`translate(${graphPan.x} ${graphPan.y}) scale(${graphZoom})`);if($('zoom'))$('zoom').textContent=`${Math.round(graphZoom*100)}%`
522
+ let dirty=false
523
+ if(!zoomUnfolded&&graphZoom>=1.05){zoomUnfolded=true;for(const b of (W.length?W:(M?[M]:[])))for(const n of b.plan)if(n.level==='component')openGraphDetails.add(String(b.port)+':'+n.id);dirty=true}
524
+ else if(zoomUnfolded&&graphZoom<=0.85){zoomUnfolded=false;openGraphDetails.clear();dirty=true}
525
+ if(!fileMode&&graphZoom>=1.5){fileMode=true;dirty=true}
526
+ else if(fileMode&&graphZoom<=1.3){fileMode=false;dirty=true}
527
+ const cm=graphZoom<CARD_LOD;if(cm!==cardMode){cardMode=cm;dirty=true}
528
+ if(dirty){renderGraph();renderTimes()}
529
+ renderMinimapViewport()}
530
+ function fitGraph(){if(!M||!regions[M.port]){fitWorld();return}const r=regions[M.port];const stage=$('graph-stage');if(!stage)return;const box=stage.getBoundingClientRect();graphZoom=Math.max(.45,Math.min(1.2,(box.width-128)/r.w,(box.height-128)/r.h));graphPan={x:(box.width-r.w*graphZoom)/2-r.ox*graphZoom,y:(box.height-r.h*graphZoom)/2-r.oy*graphZoom};updateTransform()}
531
+ function zoomBy(factor,origin){const stage=$('graph-stage');if(!stage)return;const box=stage.getBoundingClientRect(),point=origin||{x:box.width/2,y:box.height/2},next=Math.max(minZoom,Math.min(1.8,graphZoom*factor)),wx=(point.x-graphPan.x)/graphZoom,wy=(point.y-graphPan.y)/graphZoom;graphPan={x:point.x-wx*next,y:point.y-wy*next};graphZoom=next;updateTransform()}
532
+ function onGraphWheel(e){e.preventDefault();const box=e.currentTarget.getBoundingClientRect();if(e.metaKey||e.ctrlKey)zoomBy(e.deltaY<0?1.08:.92,{x:e.clientX-box.left,y:e.clientY-box.top});else{graphPan.x-=e.deltaX;graphPan.y-=e.deltaY;updateTransform()}}
533
+ function startPan(e){if(e.target.closest('.gnode,.graph-detail'))return;const stage=$('graph-stage');stage.setPointerCapture(e.pointerId);stage.classList.add('dragging');panGesture={id:e.pointerId,x:e.clientX,y:e.clientY,panX:graphPan.x,panY:graphPan.y}}
534
+ function movePan(e){if(!panGesture||panGesture.id!==e.pointerId)return;graphPan={x:panGesture.panX+e.clientX-panGesture.x,y:panGesture.panY+e.clientY-panGesture.y};updateTransform()}
535
+ function endPan(e){if(!panGesture||panGesture.id!==e.pointerId)return;$('graph-stage')?.classList.remove('dragging');panGesture=null}
536
+ function copy(q){navigator.clipboard.writeText(q).then(()=>{const t=$('toast');t.textContent='Prompt copied — paste it to your agent in this repo';t.classList.add('show');setTimeout(()=>t.classList.remove('show'),2600)})}
537
+ function copyBackfill(){copy('Read the "agenttrail plan convention" section in CLAUDE.md or AGENTS.md. Then study this repo in trust order — the code and directory layout first (what exists), git log for what is actually recent, decision logs and any in-progress build/handoff docs for what is in flight, and README/roadmap prose LAST and only for open intent (founding docs rot; cross-check their claims against git log) — and rewrite PLAN.md as the real map of this codebase: components with stable {#id}s, needs:/links: edges between them, files: globs for the paths each owns, and verb-led concrete titles with tech: sublines. Keep it to 5-9 components no matter how big the repo — a component is a part one agent could own for a session, with its own doneness and at least one edge; anything smaller is a task inside one, and stable plumbing folds into a neighbor while active or planned areas get their own card. Verify every status against the CODE, not the docs — READMEs and roadmaps rot; mark [x] only after finding the implementing source or artifact, and cite that evidence on its tech: line (e.g. tech: TurnView.swift) so a human can audit every tick; if you cannot find evidence, leave it unchecked and say so in the decisions note. Statuses must be honest — [x] only for what verifiably exists, [~] only for what you are working on right now, everything else [ ]. Tag open tasks with an indented from: line naming provenance — from: agent when an in-progress build/handoff doc or your own declared intent claims it imminently, from: roadmap when sourced only from planning docs; omit when unsure. Record the backfill under ## decisions.')}
538
+ let lastRunsHtml=''
539
+ function renderRuns(){
540
+ const boards=boardsAll()
541
+ // AOS patch: pin open asks ("Wartet auf dich") oldest-first, with the decisions log, above the session cards
542
+ const asksHtml=selfAsks().map(askCardHtml).join('')+(askDecisions.length?decisionsHtml():'')
543
+ const runs=boards.flatMap(b=>(b.runs||[]).map(x=>({...x,__port:b.port,__project:b.project,__plan:b.plan})))
544
+ const groups=[]
545
+ for(const b of boards){
546
+ const rs=(b.runs||[]).map(x=>runs.find(r=>r.__port===b.port&&r.id===x.id)).filter(Boolean).sort((a,c)=>a.startedAt-c.startedAt)
547
+ let g=null
548
+ for(const r of rs){
549
+ if(g&&r.startedAt-(g.end||g.start)<=30*60e3){g.runs.push(r);g.end=Math.max(g.end,r.lastEventAt||r.startedAt)}
550
+ else{g={project:b.project,start:r.startedAt,end:r.lastEventAt||r.startedAt,runs:[r]};groups.push(g)}
551
+ }
552
+ }
553
+ groups.sort((a,c)=>a.start-c.start)
554
+ const cycleLabel=g=>{
555
+ const d=new Date(g.start),now=new Date()
556
+ const day=d.toDateString()===now.toDateString()?'Today':d.toLocaleDateString([],{weekday:'short',month:'short',day:'numeric'})
557
+ const live=g.runs.some(r=>!r.ended)
558
+ return `${live?'Now':day} · ${esc(g.project)} · ${g.runs.length} sessions`
559
+ }
560
+ const html=groups.map(g=>(g.runs.length>1?`<div class="run-cycle">${cycleLabel(g)}</div>`:'')+g.runs.map(r=>{
561
+ const cur=(r.todos||[]).find(t=>t.status==='in_progress')
562
+ const tool=r.currentTool||r.recentTools[0]
563
+ const toolLine=tool?`<div class="run-tool"><span class="tn">${esc(tool.name)}</span><span class="td">${esc(tool.detail||'')}</span><span class="ta t-ago" data-at="${tool.at}"></span></div>`:''
564
+ const comp=r.componentId?r.__plan.find(x=>x.id===r.componentId):null
565
+ const todos=(r.todos||[]).map(t=>`<div class="run-todo ${t.status}"><span class="box ${t.status==='completed'?'done':t.status==='in_progress'?'active':''}"></span><span>${esc(t.content)}</span></div>`).join('')
566
+ const hist=r.recentTools.slice(0,5).map(t=>`<div class="run-hist"><span class="hd">${esc(t.name)} · ${esc(t.detail||'')}</span><span>${t.ms>1200?Math.round(t.ms/1000)+'s':t.ms+'ms'}</span></div>`).join('')
567
+ const icon=agentIcon(r.agent)
568
+ const key=r.__port+':'+r.id
569
+ // AOS patch: SessionEnd runs show Finished, Stop runs show Wartet auf dich (Notification runs append their waiting reason), and a live run with no tool line shows Working…
570
+ return`<div class="run-card ${r.ended?'ended':''} ${openRuns.has(key)?'open':''}" data-run="${esc(key)}">
571
+ <div class="run-head"><span class="run-agent"><svg viewBox="0 0 24 24"><path fill="${agentColor(r.agent)}" d="${icon||''}"/></svg></span><span class="run-name">${esc(r.agent)}</span><span style="color:var(--faint);font-size:11.5px">· ${esc(r.__project)}</span><span class="run-live-dot"></span><span class="run-elapsed"><span class="t-ago" data-at="${r.startedAt}"></span>${comp?` · in <a href="#" style="color:var(--accent);text-decoration:none" onclick="event.preventDefault();event.stopPropagation();focusComp('${r.__port}:${comp.id}')">${esc(comp.title)}</a>`:''}</span></div>
572
+ ${cur?`<div class="run-task">${esc(cur.content)}</div>`:r.ended&&r.done?`<div class="run-task" style="color:var(--dim)">Finished</div>`:r.ended?`<div class="run-task" style="color:var(--dim)">Wartet auf dich${r.waiting?': '+esc(r.waiting):''}</div>`:!toolLine?`<div class="run-task" style="color:var(--dim)">Working…</div>`:''}
573
+ ${toolLine}
574
+ ${(r.subagents||[]).filter(s=>!s.ended).map(s=>`<div class="run-sub"><span class="run-sub-dot"></span><span class="run-sub-name">${esc(s.name)}</span><span class="run-sub-ago t-ago" data-at="${s.startedAt}"></span></div>`).join('')}
575
+ <div class="run-detail">${(r.subagents||[]).filter(s=>s.ended).map(s=>`<div class="run-sub ended"><span class="run-sub-tick">✓</span><span class="run-sub-name">${esc(s.name)}</span></div>`).join('')}${todos}${hist?`<div style="margin-top:8px">${hist}</div>`:''}</div>
576
+ </div>`}).join('')).join('')
577
+ let hint=''
578
+ if(M){
579
+ const selfLive=(M.runs||[]).some(r=>!r.ended)
580
+ const hot=M.activity&&Date.now()-M.activity.at<120000
581
+ // AOS patch: AOS relays events via its global trail-relay hook, so the upstream init hint does not apply
582
+ if(M.hooksInstalled===false&&!M.aosPlan)hint=`<div class="run-card ended" style="cursor:default"><div class="run-task" style="margin-top:0;white-space:normal;color:var(--dim)">Watching file changes. For each session's live plan and tool calls too, run <b style="color:var(--text)">npx agenttrail init</b> here once — local only, gitignored.</div></div>`
583
+ }
584
+ const past=boards.flatMap(b=>(b.cycles||[]).map(c=>({...c,__project:b.project,__plan:b.plan||[]})))
585
+ .sort((a,c)=>c.start-a.start).slice(0,10)
586
+ .map(c=>{
587
+ const d=new Date(c.start)
588
+ const day=d.toDateString()===new Date().toDateString()?'Today':d.toLocaleDateString([],{weekday:'short',month:'short',day:'numeric'})
589
+ const stages=c.sessions.map(x=>{const comp=c.__plan.find(p=>p.id===x.componentId);return comp?comp.title:null}).filter(Boolean)
590
+ const path=stages.filter((t,i)=>t!==stages[i-1]).join(' → ')
591
+ return `<div class="cycle-row"><span class="cycle-when">${day}</span><span class="cycle-what">${esc(c.__project)} · ${c.sessions.length} session${c.sessions.length>1?'s':''}${path?` · ${esc(path)}`:''}</span></div>`
592
+ }).join('')
593
+ const out=asksHtml+html+hint+(past?`<div class="run-cycle" style="margin-top:10px">Past cycles</div>${past}`:'')
594
+ if(out!==lastRunsHtml){lastRunsHtml=out;$('runs').innerHTML=out}
595
+ }
596
+ $('runs').addEventListener('click',e=>{if(e.target.closest('[data-ask]'))return;const c=e.target.closest('.run-card');if(!c)return;const id=c.dataset.run
597
+ openRuns.has(id)?openRuns.delete(id):openRuns.add(id);renderRuns();renderTimes()})
598
+ const MM_W=150
599
+ function renderMinimap(){
600
+ const mm=$('minimap')
601
+ if(!mm||!graphSize.width||M.plan.filter(n=>n.level==='component').length<2){if(mm)mm.style.display='none';return}
602
+ mm.style.display=''
603
+ const sc=MM_W/graphSize.width,h=Math.max(40,Math.round(graphSize.height*sc))
604
+ const nodes=Object.entries(graphPos).map(([id,q])=>{
605
+ const c=M.plan.find(n=>n.id===id)
606
+ return`<rect class="mm-node ${c?c.status:''}" x="${q.nodeX*sc}" y="${q.nodeY*sc}" width="${q.nodeW*sc}" height="${Math.max(3,q.h*sc)}" rx="1.5"/>`
607
+ }).join('')
608
+ mm.innerHTML=`<svg width="${MM_W}" height="${h}" viewBox="0 0 ${MM_W} ${h}">${nodes}<rect class="mm-view" id="mm-view" x="0" y="0" width="10" height="10"/></svg>`
609
+ mm.onclick=e=>{const r=mm.getBoundingClientRect(),wx=(e.clientX-r.left)/sc,wy=(e.clientY-r.top)/sc
610
+ const stage=$('graph-stage'),box=stage.getBoundingClientRect()
611
+ graphPan={x:box.width/2-wx*graphZoom,y:box.height/2-wy*graphZoom};updateTransform()}
612
+ renderMinimapViewport()
613
+ }
614
+ function renderMinimapViewport(){
615
+ const v=document.getElementById('mm-view');if(!v||!graphSize.width)return
616
+ const stage=$('graph-stage');if(!stage)return
617
+ const box=stage.getBoundingClientRect(),sc=MM_W/graphSize.width
618
+ v.setAttribute('x',(-graphPan.x/graphZoom)*sc);v.setAttribute('y',(-graphPan.y/graphZoom)*sc)
619
+ v.setAttribute('width',(box.width/graphZoom)*sc);v.setAttribute('height',(box.height/graphZoom)*sc)
620
+ }
621
+ function focusComp(id){const p=graphPos[id];const stage=$('graph-stage');if(!p||!stage)return
622
+ const box=stage.getBoundingClientRect(),z=1.1
623
+ const world=$('world');if(world){world.style.transition='transform 600ms cubic-bezier(.23,1,.32,1)';setTimeout(()=>{if(world)world.style.transition=''},650)}
624
+ graphZoom=z;graphPan={x:box.width/2-(p.nodeX+200)*z,y:box.height/2-(p.nodeY+120)*z};updateTransform()}
625
+ function renderTimes(){if(M)renderBadge();if(M)renderObserved();for(const el of document.querySelectorAll('.t-ago'))el.textContent=ago(+el.dataset.at)}
626
+ function boardsAll(){return (W.length?W:(M?[M]:[])).map(b=>({...b,project:b.project||(b.session&&b.session.project)||''}))}
627
+ function renderObserved(){const now=Date.now(),boards=boardsAll()
628
+ const findB=port=>boards.find(x=>String(x.port)===port)
629
+ for(const g of document.querySelectorAll('g.gnode[data-id]')){const key=g.dataset.id,ci=key.indexOf(':'),b=findB(key.slice(0,ci));if(!b)continue
630
+ const comp=b.plan.find(x=>x.id===key.slice(ci+1)&&x.level==='component');if(!comp)continue
631
+ const live=comp.touchedAt&&now-comp.touchedAt<60000
632
+ g.classList.toggle('obs-live',!!live)
633
+ const ft=g.querySelector('.obs-file'),a=(comp.recent||[])[0]
634
+ if(ft)ft.textContent=live&&a?`${a.file.split('/').pop()}${a.n>1?` ×${a.n}`:''} · ${ago(a.at)}`:''}
635
+ for(const g of document.querySelectorAll('g.graph-detail[data-detail]')){const key=g.dataset.detail,ci=key.indexOf(':'),port=key.slice(0,ci),b=findB(port);if(!b)continue
636
+ const raw=key.slice(ci+1),comp=b.plan.find(x=>x.id===raw);if(!comp)continue
637
+ const kidsOf=b.plan.filter(x=>x.parent===raw)
638
+ const declared=kidsOf.some(x=>x.status==='active'||x.status==='blocked')
639
+ const live=comp.touchedAt&&now-comp.touchedAt<60000
640
+ const inferred=live&&!declared?(kidsOf.find(x=>x.status!=='done')||null):null
641
+ for(const cap of g.querySelectorAll('.task-capsule[data-task]'))cap.classList.toggle('obs-spin',!!inferred&&cap.dataset.task===port+':'+inferred.id)
642
+ for(const el of g.querySelectorAll('[data-dl-task]')){const traw=el.dataset.dlTask.slice(el.dataset.dlTask.indexOf(':')+1)
643
+ const t=kidsOf.find(x=>x.id===traw);if(!t)continue
644
+ const isLive=!!(comp.recent||[]).length&&(t.status==='active'||t.status==='blocked'||(inferred&&t.id===inferred.id)||(live&&!declared&&t.status==='done'&&!inferred))
645
+ if(isLive){const rows=(comp.recent||[]).slice(0,4).map(a=>`<div class="task-capsule-detail-row"><span>${esc(a.file)}${a.n>1?` ×${a.n}`:''}</span><span class="task-capsule-detail-meta">${ago(a.at)}</span></div>`)
646
+ if(el.dataset.tech)rows.push(`<div class="task-capsule-detail-row"><span style="opacity:.6">${esc(el.dataset.tech)}</span><span class="task-capsule-detail-meta"></span></div>`)
647
+ el.innerHTML=rows.join('');el.dataset.obsLines='1'}
648
+ else if(el.dataset.obsLines){const rows=[]
649
+ if(el.dataset.tech)rows.push(`<div class="task-capsule-detail-row"><span>${esc(el.dataset.tech)}</span><span class="task-capsule-detail-meta"></span></div>`)
650
+ if(!rows.length)rows.push('<div class="task-capsule-detail-row"><span>No activity recorded yet</span><span class="task-capsule-detail-meta"></span></div>')
651
+ el.innerHTML=rows.join('');delete el.dataset.obsLines}}}
652
+ for(const el of document.querySelectorAll('[data-obs-copy]')){const key=el.dataset.obsCopy,ci=key.indexOf(':'),b=findB(key.slice(0,ci))
653
+ const comp=b&&b.plan.find(x=>x.id===key.slice(ci+1)),a=comp&&(comp.recent||[])[0]
654
+ if(a)el.textContent=`Revising · ${a.file.split('/').pop()}${a.n>1?` ×${a.n}`:''}`}
655
+ for(const el of document.querySelectorAll('[data-obs-ago]')){const key=el.dataset.obsAgo,ci=key.indexOf(':'),b=findB(key.slice(0,ci))
656
+ const comp=b&&b.plan.find(x=>x.id===key.slice(ci+1)),a=comp&&(comp.recent||[])[0]
657
+ if(a)el.textContent=ago(a.at)}}
658
+ window.addEventListener('resize',()=>{renderMinimapViewport()})
659
+ </script>
660
+ </body>
661
+ </html>