@turmind/halo 0.1.9

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 (296) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/admin-out/404.html +1 -0
  4. package/admin-out/_next/static/IBP_mwBDf-cPwZSZZGpi8/_buildManifest.js +1 -0
  5. package/admin-out/_next/static/IBP_mwBDf-cPwZSZZGpi8/_ssgManifest.js +1 -0
  6. package/admin-out/_next/static/chunks/1d2d5650-810a3ca1ae20cab5.js +18 -0
  7. package/admin-out/_next/static/chunks/200.3b8f8a74f005a134.js +1 -0
  8. package/admin-out/_next/static/chunks/220.3dc41cd6269b88e6.js +1 -0
  9. package/admin-out/_next/static/chunks/298-d6b224b569da4de0.js +1 -0
  10. package/admin-out/_next/static/chunks/304eda7d.b1ee7f523653e027.js +1 -0
  11. package/admin-out/_next/static/chunks/330.3b784cf0dcd69163.js +1 -0
  12. package/admin-out/_next/static/chunks/442.f4302087bddb1ec1.js +1 -0
  13. package/admin-out/_next/static/chunks/443.d1b289781793f8de.js +25 -0
  14. package/admin-out/_next/static/chunks/503-7c6d9935df6e0cea.js +1 -0
  15. package/admin-out/_next/static/chunks/533.5f2d4bfe3d076095.js +1 -0
  16. package/admin-out/_next/static/chunks/579.a2718ddd395a2f3a.js +1 -0
  17. package/admin-out/_next/static/chunks/635-3dd0043ed0520b9d.js +1 -0
  18. package/admin-out/_next/static/chunks/65.859665ba6717967a.js +1 -0
  19. package/admin-out/_next/static/chunks/661.a4fe93f3f2a5e871.js +1 -0
  20. package/admin-out/_next/static/chunks/688.4e014ac6cfc7e152.js +1 -0
  21. package/admin-out/_next/static/chunks/868.0c95dfe283400a00.js +1 -0
  22. package/admin-out/_next/static/chunks/app/_not-found/page-8e35399c8fd5016d.js +1 -0
  23. package/admin-out/_next/static/chunks/app/layout-58f773336e6fce61.js +1 -0
  24. package/admin-out/_next/static/chunks/app/page-cc8043335282d745.js +1 -0
  25. package/admin-out/_next/static/chunks/db534642.09d3c650ff587175.js +1 -0
  26. package/admin-out/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js +1 -0
  27. package/admin-out/_next/static/chunks/framework-c3031ae54a63fe16.js +1 -0
  28. package/admin-out/_next/static/chunks/main-14b48e3c2588354f.js +1 -0
  29. package/admin-out/_next/static/chunks/main-app-483b436873410ddc.js +1 -0
  30. package/admin-out/_next/static/chunks/pages/_app-b2fc6e5f920d1b1a.js +1 -0
  31. package/admin-out/_next/static/chunks/pages/_error-f3696a8f3d67d150.js +1 -0
  32. package/admin-out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  33. package/admin-out/_next/static/chunks/webpack-8bd7ef88a6527feb.js +1 -0
  34. package/admin-out/_next/static/css/659eccb5db697b76.css +1 -0
  35. package/admin-out/_next/static/css/8fce4e1a54dac12a.css +3 -0
  36. package/admin-out/_next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  37. package/admin-out/_next/static/media/21350d82a1f187e9-s.woff2 +0 -0
  38. package/admin-out/_next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  39. package/admin-out/_next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  40. package/admin-out/_next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  41. package/admin-out/_next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  42. package/admin-out/_next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  43. package/admin-out/favicon.ico +0 -0
  44. package/admin-out/icon.svg +18 -0
  45. package/admin-out/index.html +1 -0
  46. package/admin-out/index.txt +23 -0
  47. package/admin-out/monaco/vs/_commonjsHelpers-CT9FvmAN.js +1 -0
  48. package/admin-out/monaco/vs/abap-D-t0cyap.js +1 -0
  49. package/admin-out/monaco/vs/apex-CcIm7xu6.js +1 -0
  50. package/admin-out/monaco/vs/assets/css.worker-HnVq6Ewq.js +93 -0
  51. package/admin-out/monaco/vs/assets/editor.worker-Be8ye1pW.js +26 -0
  52. package/admin-out/monaco/vs/assets/html.worker-B51mlPHg.js +470 -0
  53. package/admin-out/monaco/vs/assets/json.worker-DKiEKt88.js +58 -0
  54. package/admin-out/monaco/vs/assets/ts.worker-CMbG-7ft.js +67731 -0
  55. package/admin-out/monaco/vs/azcli-BA0tQDCg.js +1 -0
  56. package/admin-out/monaco/vs/basic-languages/monaco.contribution.js +1 -0
  57. package/admin-out/monaco/vs/bat-C397hTD6.js +1 -0
  58. package/admin-out/monaco/vs/bicep-DF5aW17k.js +2 -0
  59. package/admin-out/monaco/vs/cameligo-plsz8qhj.js +1 -0
  60. package/admin-out/monaco/vs/clojure-Y2auQMzK.js +1 -0
  61. package/admin-out/monaco/vs/coffee-Bu45yuWE.js +1 -0
  62. package/admin-out/monaco/vs/cpp-CkKPQIni.js +1 -0
  63. package/admin-out/monaco/vs/csharp-CX28MZyh.js +1 -0
  64. package/admin-out/monaco/vs/csp-D8uWnyxW.js +1 -0
  65. package/admin-out/monaco/vs/css-CaeNmE3S.js +3 -0
  66. package/admin-out/monaco/vs/cssMode-CjiAH6dQ.js +1 -0
  67. package/admin-out/monaco/vs/cypher-DVThT8BS.js +1 -0
  68. package/admin-out/monaco/vs/dart-CmGfCvrO.js +1 -0
  69. package/admin-out/monaco/vs/dockerfile-CZqqYdch.js +1 -0
  70. package/admin-out/monaco/vs/ecl-30fUercY.js +1 -0
  71. package/admin-out/monaco/vs/editor/editor.main.css +1 -0
  72. package/admin-out/monaco/vs/editor/editor.main.js +5 -0
  73. package/admin-out/monaco/vs/editor.api-CalNCsUg.js +903 -0
  74. package/admin-out/monaco/vs/elixir-xjPaIfzF.js +1 -0
  75. package/admin-out/monaco/vs/flow9-DqtmStfK.js +1 -0
  76. package/admin-out/monaco/vs/freemarker2-Cz_sV6Md.js +3 -0
  77. package/admin-out/monaco/vs/fsharp-BOMdg4U1.js +1 -0
  78. package/admin-out/monaco/vs/go-D_hbi-Jt.js +1 -0
  79. package/admin-out/monaco/vs/graphql-CKUU4kLG.js +1 -0
  80. package/admin-out/monaco/vs/handlebars-OwglfO-1.js +1 -0
  81. package/admin-out/monaco/vs/hcl-DTaboeZW.js +1 -0
  82. package/admin-out/monaco/vs/html-Pa1xEWsY.js +1 -0
  83. package/admin-out/monaco/vs/htmlMode-Bz67EXwp.js +1 -0
  84. package/admin-out/monaco/vs/ini-CsNwO04R.js +1 -0
  85. package/admin-out/monaco/vs/java-CI4ZMsH9.js +1 -0
  86. package/admin-out/monaco/vs/javascript-PczUCGdz.js +1 -0
  87. package/admin-out/monaco/vs/jsonMode-DULH5oaX.js +7 -0
  88. package/admin-out/monaco/vs/julia-BwzEvaQw.js +1 -0
  89. package/admin-out/monaco/vs/kotlin-IUYPiTV8.js +1 -0
  90. package/admin-out/monaco/vs/language/css/monaco.contribution.js +1 -0
  91. package/admin-out/monaco/vs/language/html/monaco.contribution.js +1 -0
  92. package/admin-out/monaco/vs/language/json/monaco.contribution.js +1 -0
  93. package/admin-out/monaco/vs/language/typescript/monaco.contribution.js +1 -0
  94. package/admin-out/monaco/vs/less-C0eDYdqa.js +2 -0
  95. package/admin-out/monaco/vs/lexon-iON-Kj97.js +1 -0
  96. package/admin-out/monaco/vs/liquid-DqKjdPGy.js +1 -0
  97. package/admin-out/monaco/vs/loader.js +1368 -0
  98. package/admin-out/monaco/vs/lspLanguageFeatures-kM9O9rjY.js +4 -0
  99. package/admin-out/monaco/vs/lua-DtygF91M.js +1 -0
  100. package/admin-out/monaco/vs/m3-CsR4AuFi.js +1 -0
  101. package/admin-out/monaco/vs/markdown-C_rD0bIw.js +1 -0
  102. package/admin-out/monaco/vs/mdx-DEWtB1K5.js +1 -0
  103. package/admin-out/monaco/vs/mips-CiYP61RB.js +1 -0
  104. package/admin-out/monaco/vs/monaco.contribution-D2OdxNBt.js +1 -0
  105. package/admin-out/monaco/vs/monaco.contribution-DO3azKX8.js +1 -0
  106. package/admin-out/monaco/vs/monaco.contribution-EcChJV6a.js +1 -0
  107. package/admin-out/monaco/vs/monaco.contribution-qLAYrEOP.js +1 -0
  108. package/admin-out/monaco/vs/msdax-C38-sJlp.js +1 -0
  109. package/admin-out/monaco/vs/mysql-CdtbpvbG.js +1 -0
  110. package/admin-out/monaco/vs/nls.messages-loader.js +1 -0
  111. package/admin-out/monaco/vs/nls.messages.cs.js.js +17 -0
  112. package/admin-out/monaco/vs/nls.messages.de.js.js +17 -0
  113. package/admin-out/monaco/vs/nls.messages.es.js.js +17 -0
  114. package/admin-out/monaco/vs/nls.messages.fr.js.js +15 -0
  115. package/admin-out/monaco/vs/nls.messages.it.js.js +15 -0
  116. package/admin-out/monaco/vs/nls.messages.ja.js.js +17 -0
  117. package/admin-out/monaco/vs/nls.messages.js.js +10 -0
  118. package/admin-out/monaco/vs/nls.messages.ko.js.js +25 -0
  119. package/admin-out/monaco/vs/nls.messages.pl.js.js +17 -0
  120. package/admin-out/monaco/vs/nls.messages.pt-br.js.js +6 -0
  121. package/admin-out/monaco/vs/nls.messages.ru.js.js +17 -0
  122. package/admin-out/monaco/vs/nls.messages.tr.js.js +15 -0
  123. package/admin-out/monaco/vs/nls.messages.zh-cn.js.js +17 -0
  124. package/admin-out/monaco/vs/nls.messages.zh-tw.js.js +15 -0
  125. package/admin-out/monaco/vs/objective-c-CntZFaHX.js +1 -0
  126. package/admin-out/monaco/vs/pascal-r6kuqfl_.js +1 -0
  127. package/admin-out/monaco/vs/pascaligo-BiXoTmXh.js +1 -0
  128. package/admin-out/monaco/vs/perl-DABw_TcH.js +1 -0
  129. package/admin-out/monaco/vs/pgsql-me_jFXeX.js +1 -0
  130. package/admin-out/monaco/vs/php-D_kh-9LK.js +1 -0
  131. package/admin-out/monaco/vs/pla-VfZjczW0.js +1 -0
  132. package/admin-out/monaco/vs/postiats-BBSzz8Pk.js +1 -0
  133. package/admin-out/monaco/vs/powerquery-Dt-g_2cc.js +1 -0
  134. package/admin-out/monaco/vs/powershell-B-7ap1zc.js +1 -0
  135. package/admin-out/monaco/vs/protobuf-BmtuEB1A.js +2 -0
  136. package/admin-out/monaco/vs/pug-BRpRNeEb.js +1 -0
  137. package/admin-out/monaco/vs/python-Cr0UkIbn.js +1 -0
  138. package/admin-out/monaco/vs/qsharp-BzsFaUU9.js +1 -0
  139. package/admin-out/monaco/vs/r-f8dDdrp4.js +1 -0
  140. package/admin-out/monaco/vs/razor-BYAHOTkz.js +1 -0
  141. package/admin-out/monaco/vs/redis-fvZQY4PI.js +1 -0
  142. package/admin-out/monaco/vs/redshift-45Et0LQi.js +1 -0
  143. package/admin-out/monaco/vs/restructuredtext-C7UUFKFD.js +1 -0
  144. package/admin-out/monaco/vs/ruby-CZO8zYTz.js +1 -0
  145. package/admin-out/monaco/vs/rust-Bfetafyc.js +1 -0
  146. package/admin-out/monaco/vs/sb-3GYllVck.js +1 -0
  147. package/admin-out/monaco/vs/scala-foMgrKo1.js +1 -0
  148. package/admin-out/monaco/vs/scheme-CHdMtr7p.js +1 -0
  149. package/admin-out/monaco/vs/scss-C1cmLt9V.js +3 -0
  150. package/admin-out/monaco/vs/shell-ClXCKCEW.js +1 -0
  151. package/admin-out/monaco/vs/solidity-MZ6ExpPy.js +1 -0
  152. package/admin-out/monaco/vs/sophia-DWkuSsPQ.js +1 -0
  153. package/admin-out/monaco/vs/sparql-AUGFYSyk.js +1 -0
  154. package/admin-out/monaco/vs/sql-32GpJSV2.js +1 -0
  155. package/admin-out/monaco/vs/st-CuDFIVZ_.js +1 -0
  156. package/admin-out/monaco/vs/swift-n-2HociN.js +3 -0
  157. package/admin-out/monaco/vs/systemverilog-Ch4vA8Yt.js +1 -0
  158. package/admin-out/monaco/vs/tcl-D74tq1nH.js +1 -0
  159. package/admin-out/monaco/vs/tsMode-CZz1Umrk.js +11 -0
  160. package/admin-out/monaco/vs/twig-C6taOxMV.js +1 -0
  161. package/admin-out/monaco/vs/typescript-DfOrAzoV.js +1 -0
  162. package/admin-out/monaco/vs/typespec-D-PIh9Xw.js +1 -0
  163. package/admin-out/monaco/vs/vb-Dyb2648j.js +1 -0
  164. package/admin-out/monaco/vs/wgsl-BhLXMOR0.js +298 -0
  165. package/admin-out/monaco/vs/workers-DcJshg-q.js +1 -0
  166. package/admin-out/monaco/vs/xml-CdsdnY8S.js +1 -0
  167. package/admin-out/monaco/vs/yaml-DYGvmE88.js +1 -0
  168. package/bin/halo.js +9 -0
  169. package/bundled-docs/design/agent.md +145 -0
  170. package/bundled-docs/design/architecture.md +270 -0
  171. package/bundled-docs/design/background-dispatch.md +151 -0
  172. package/bundled-docs/design/command.md +58 -0
  173. package/bundled-docs/design/cron.md +88 -0
  174. package/bundled-docs/design/evolution.md +383 -0
  175. package/bundled-docs/design/express-self.md +145 -0
  176. package/bundled-docs/design/feishu.md +224 -0
  177. package/bundled-docs/design/halo-city.md +112 -0
  178. package/bundled-docs/design/prompt-system.md +411 -0
  179. package/bundled-docs/design/session.md +321 -0
  180. package/bundled-docs/design/slack.md +212 -0
  181. package/bundled-docs/design/storage.md +578 -0
  182. package/bundled-docs/design/telegram.md +161 -0
  183. package/bundled-docs/design/web.md +222 -0
  184. package/bundled-docs/design/wechat.md +146 -0
  185. package/bundled-docs/design/ws.md +144 -0
  186. package/bundled-docs/dev/acp-adapter.md +337 -0
  187. package/bundled-docs/dev/add-channel.md +376 -0
  188. package/bundled-docs/dev/add-model-provider.md +386 -0
  189. package/bundled-docs/dev/api.md +686 -0
  190. package/bundled-docs/dev/deploy.md +198 -0
  191. package/bundled-docs/dev/desktop-packaging.md +327 -0
  192. package/bundled-docs/dev/env.md +158 -0
  193. package/bundled-docs/dev/logging.md +85 -0
  194. package/bundled-docs/dev/previews.md +225 -0
  195. package/bundled-docs/dev/tools.md +331 -0
  196. package/bundled-docs/guide/README.md +29 -0
  197. package/bundled-docs/guide/agents.md +264 -0
  198. package/bundled-docs/guide/channels/README.md +62 -0
  199. package/bundled-docs/guide/channels/acp.md +150 -0
  200. package/bundled-docs/guide/channels/feishu.md +154 -0
  201. package/bundled-docs/guide/channels/slack.md +156 -0
  202. package/bundled-docs/guide/channels/telegram.md +112 -0
  203. package/bundled-docs/guide/channels/web.md +149 -0
  204. package/bundled-docs/guide/channels/wechat.md +110 -0
  205. package/bundled-docs/guide/chat.md +112 -0
  206. package/bundled-docs/guide/cli.md +197 -0
  207. package/bundled-docs/guide/getting-started.md +93 -0
  208. package/bundled-docs/guide/secrets-and-credentials.md +161 -0
  209. package/bundled-docs/guide/sessions.md +104 -0
  210. package/bundled-docs/guide/skills.md +302 -0
  211. package/bundled-docs/guide/testing-agents-and-skills.md +136 -0
  212. package/bundled-docs/guide/workspace.md +131 -0
  213. package/bundled-docs/plans/cron-tasks.md +362 -0
  214. package/bundled-docs/plans/design-suggestions.md +203 -0
  215. package/bundled-docs/plans/issue-session-deep-nesting.md +208 -0
  216. package/bundled-docs/plans/launch-plan.md +111 -0
  217. package/bundled-docs/plans/md-and-agent-refactor.md +197 -0
  218. package/bundled-docs/plans/remote-mac-worker.md +196 -0
  219. package/bundled-docs/plans/roadmap.md +40 -0
  220. package/bundled-docs/plans/self-evolution.md +1379 -0
  221. package/bundled-docs/plans/todo.md +32 -0
  222. package/bundled-docs/plans/weixin-channel.md +255 -0
  223. package/bundled-docs/requirements/agent.md +92 -0
  224. package/bundled-docs/requirements/chat.md +81 -0
  225. package/bundled-docs/requirements/command.md +130 -0
  226. package/bundled-docs/requirements/editor.md +82 -0
  227. package/bundled-docs/requirements/explorer.md +89 -0
  228. package/bundled-docs/requirements/overview.md +62 -0
  229. package/bundled-docs/requirements/session.md +73 -0
  230. package/bundled-docs/requirements/settings.md +224 -0
  231. package/bundled-docs/requirements/skill.md +125 -0
  232. package/bundled-docs/requirements/source-control.md +82 -0
  233. package/bundled-docs/requirements/terminal.md +118 -0
  234. package/bundled-docs/requirements/workspace.md +80 -0
  235. package/bundled-docs/test/docs-seeding.md +101 -0
  236. package/bundled-docs/test/session.md +374 -0
  237. package/dist/index.js +26750 -0
  238. package/package.json +67 -0
  239. package/templates/INSTRUCTIONS.md +25 -0
  240. package/templates/agents/__apply_agent__/AGENT.md +229 -0
  241. package/templates/agents/__apply_agent__/agent.yaml +30 -0
  242. package/templates/agents/__evo_agent__/AGENT.md +323 -0
  243. package/templates/agents/__evo_agent__/agent.yaml +29 -0
  244. package/templates/agents/__score__/AGENT.md +177 -0
  245. package/templates/agents/__score__/agent.yaml +29 -0
  246. package/templates/agents/deep-executor/AGENT.md +60 -0
  247. package/templates/agents/deep-executor/agent.yaml +25 -0
  248. package/templates/agents/default/AGENT.md +38 -0
  249. package/templates/agents/default/agent.yaml +51 -0
  250. package/templates/agents/executor/AGENT.md +38 -0
  251. package/templates/agents/executor/agent.yaml +25 -0
  252. package/templates/aliases.yaml +26 -0
  253. package/templates/canvas/self.html +660 -0
  254. package/templates/config.yaml +69 -0
  255. package/templates/models/anthropic.yaml +47 -0
  256. package/templates/models/aws-bedrock-claude-invoke.yaml +156 -0
  257. package/templates/models/aws-bedrock-mantle.yaml +89 -0
  258. package/templates/models/deepseek.yaml +62 -0
  259. package/templates/models/doubao.yaml +137 -0
  260. package/templates/models/hunyuan.yaml +56 -0
  261. package/templates/models/kimi.yaml +41 -0
  262. package/templates/models/mimo-token-plan-china.yaml +61 -0
  263. package/templates/models/minimax.yaml +80 -0
  264. package/templates/models/openai.yaml +51 -0
  265. package/templates/models/qwen.yaml +78 -0
  266. package/templates/optional-skills/tavily-web-search/SKILL.md +91 -0
  267. package/templates/optional-skills/tavily-web-search/config.yaml +10 -0
  268. package/templates/prompts/all/TOOL_GUIDELINES.md +35 -0
  269. package/templates/prompts/all/TOOL_SHELL.md +4 -0
  270. package/templates/prompts/all/TOOL_SHELL.windows.md +7 -0
  271. package/templates/prompts/bootstrap/BOOTSTRAP.md +26 -0
  272. package/templates/prompts/root/WORKSPACE_MEMORY.md +11 -0
  273. package/templates/schema.sql +30 -0
  274. package/templates/skills/acp/SKILL.md +236 -0
  275. package/templates/skills/acp/templates/SKILL.md.tmpl +74 -0
  276. package/templates/skills/acp/templates/ask.py +176 -0
  277. package/templates/skills/acp/templates/config.yaml.tmpl +33 -0
  278. package/templates/skills/agent/SKILL.md +128 -0
  279. package/templates/skills/aws-knowledge/SKILL.md +82 -0
  280. package/templates/skills/aws-knowledge/scripts/aws_kb.py +61 -0
  281. package/templates/skills/cron/SKILL.md +217 -0
  282. package/templates/skills/cron/manage-cron.py +434 -0
  283. package/templates/skills/halo/SKILL.md +98 -0
  284. package/templates/skills/nova-web-search/SKILL.md +65 -0
  285. package/templates/skills/self/SKILL.md +139 -0
  286. package/templates/skills/send-file/SKILL.md +28 -0
  287. package/templates/skills/send-file/feishu.md +16 -0
  288. package/templates/skills/send-file/slack.md +7 -0
  289. package/templates/skills/send-file/telegram.md +9 -0
  290. package/templates/skills/send-file/web.md +6 -0
  291. package/templates/skills/send-file/wechat.md +8 -0
  292. package/templates/skills/skill/SKILL.md +131 -0
  293. package/templates/skills/workspace/SKILL.md +28 -0
  294. package/templates/skills/workspace/setup-tidy.md +265 -0
  295. package/templates/skills/workspace/share.md +187 -0
  296. package/templates/skills/workspace/stage.py +487 -0
@@ -0,0 +1,82 @@
1
+ # Source Control — Requirements
2
+
3
+ A focused Git panel for **viewing changes, committing, pushing, and managing credentials** — not a full VSCode SCM clone. The guiding principle: this is *your workstation's* git view (what did I change, commit it, push it), not the *project's* full topology sandbox (that lives on GitHub/GitLab). Scope is deliberately trimmed to the "view + commit + credentials" core; branch operations, AI commit messages, clone, conflict resolution, stash and the DAG graph are explicitly out of this round.
4
+
5
+ Backed by `GET/POST/DELETE /api/git/*` (see [dev/api.md](../dev/api.md#source-control-git)). Core lives in `packages/core/src/workspace/git-manager.ts` (simple-git wrapper); routes in `packages/server/src/routes/git.ts`; panel UI in `packages/admin/src/features/source-control/`.
6
+
7
+ ## Entry visibility (git-gated)
8
+
9
+ The Source Control **activity-bar entry itself is hidden in non-git workspaces** — a non-developer's notes folder shouldn't carry a panel that doesn't apply to it. Visibility is a three-state signal (`useIsRepo`) derived from the *same* `GET /api/git/status` call that already drives the Explorer decorations (no extra fetch):
10
+
11
+ - **`'unknown'`** (status not yet resolved) → **show**. Defaulting to visible means no first-paint flicker, and a clean repo (no changes) is never momentarily mis-hidden while its status is in flight.
12
+ - **`true`** (confirmed repo, including a clean one) → **show**.
13
+ - **`false`** (confirmed non-repo) → **hide**. If `localStorage` had restored the active tab to Source Control, the layout falls back to Explorer — guarded on the confirmed-`false` state so an in-flight check can't kick the user off the tab.
14
+
15
+ **Auto-surface on `git init`**: creating a repo (panel **Initialize** *or* a terminal `git init` / `git clone`) makes the entry appear on its own, no page refresh. The backend's `GitDirWatcher` runs in a degraded "watch the workspace root for `.git` appearing" mode while the folder is a non-repo; the moment `.git` shows up it fires a `file:changed`, the frontend re-queries status, the repo signal flips `true`, and the entry surfaces. See [Auto-refresh](#auto-refresh-no-polling) for the two-phase watcher.
16
+
17
+ ## Panel states (three-gate onboarding)
18
+
19
+ Once the entry is shown, the panel resolves to one of these on open, driven by `GET /api/git/status`:
20
+
21
+ 1. **Not a repo** (`{isRepo: false}`) — the folder has no git work-tree at its root (either no repo at all, or it only sits *inside* an ancestor's repo). Shows an **Initialize Repository** empty state. Never a 500/console error — a non-repo folder is a normal state.
22
+ 2. **Repo, no remote** — `GET /api/git/remotes` is empty. Shows an **Add Remote** prompt to guide first publish. Adding a remote is a purely local op with **no commit precondition** — a freshly-`init`'d empty repo can set its remote URL right away (not gated behind "must have a commit first", which would be a mis-applied VSCode *Publish* gate).
23
+ 3. **Repo with remote** — the full panel: CHANGES list, commit box, history graph.
24
+
25
+ ### Ancestor-repo guard
26
+ A workspace placed *under* another repo (e.g. a dotfiles `$HOME`, or a monorepo subdirectory) must **not** show that ancestor's git state. `git` resolves `.git` by walking up the tree, so the backend guards with an explicit `isRepoRoot()` check — only the workspace root being a real work-tree root counts. Without this, a workspace would leak the enclosing repo's changes.
27
+
28
+ ## Changes (working tree)
29
+
30
+ - CHANGES section lists every modified/added/deleted/untracked/renamed file, split into **Staged** and **Changes** groups (mirrors the porcelain X/Y status chars).
31
+ - Each row shows a VSCode-style status badge: **M** modified (blue), **A** added / **U** untracked (green), **D** deleted (red), **R** renamed / **C** copied (blue), **U→Conflict** unmerged (red).
32
+ - Stage / unstage individual files or all at once (`POST /api/git/stage` · `unstage`).
33
+ - Click a file → opens a **Monaco side-by-side diff** (`GET /api/git/diff`); original side is HEAD (or the rename's old path), modified side is the working/staged copy. Binary files show git's "Binary files differ" rather than crashing.
34
+ - **Single-file discard is intentionally omitted** — it's an "operation", not a "view". Use the command line (`git checkout`).
35
+
36
+ ## Commit & sync
37
+
38
+ - Commit box: type a message, commit staged changes (`POST /api/git/commit`). Blank message is rejected.
39
+ - **Push** (`POST /api/git/push`): first push of an untracked branch sets upstream automatically (`-u origin <branch>`).
40
+ - **Push failure is surfaced as a friendly red banner**, never a raw leak. A missing-credentials push (`fatal: could not read Username for 'https://...'`) is detected and rephrased into a "configure credentials" guide rather than dumping git's stderr.
41
+ - Pull (`POST /api/git/pull`).
42
+
43
+ ## Auto-refresh (no polling)
44
+
45
+ The panel, the history graph, and the Explorer git decorations all refresh on the WebSocket `file:changed` event (debounced ~400ms), never by polling.
46
+
47
+ - **Panel-driven writes**: every git mutation route (stage/unstage/commit/push/pull/init/remote) re-broadcasts `file:changed` (path `.git`) itself, because the workspace file watcher deliberately ignores `.git`. So after a commit the CHANGES list auto-clears with no manual refresh.
48
+ - **Command-line writes**: a lightweight, **non-recursive** watch on the `.git` directory's top-level `HEAD` / `index` files (separate from the recursive workspace watcher, which excludes `.git` to avoid being overwhelmed by object-store inodes) catches terminal `git commit` / `checkout` / `add` / `reset` and broadcasts the same `file:changed`. `.lock` churn is filtered; the operation's events are debounced into a single refresh. So a commit made in the integrated terminal updates the panel without a manual refresh.
49
+ - **Two-phase watcher**: `GitDirWatcher` adapts to whether the workspace is a repo yet. A real work-tree watches `.git` directly (the steady state above). A **non-git folder** instead watches the workspace *root* (still non-recursive) for one thing only — `.git` appearing — so a terminal `git init` / `git clone` is noticed and upgrades the watcher onto `.git`, firing once so the [entry auto-surfaces](#entry-visibility-git-gated). The root-watch handler compares the event filename before any disk IO (ordinary file churn costs one string compare), and a real repo never enters this degraded mode.
50
+
51
+ ## Explorer git decorations
52
+
53
+ The file tree colors and badges files by git status (VSCode-style), driven by the same `/api/git/status` + `/api/git/ignored` data (see also [explorer.md](explorer.md)):
54
+
55
+ - Changed files get a status-colored name + letter badge; a folder bubbles up its subtree's status (a single color, or neutral gray when the subtree mixes kinds).
56
+ - **Ignored files/folders are dimmed.** `GET /api/git/ignored` collapses an ignored directory (e.g. `node_modules`) to one entry; the tree dims that node and everything under it (prefix match). Ignored paths run through `core.quotepath=false` so **non-ASCII paths (Chinese, emoji) dim correctly** — without it git octal-escapes them and the prefix match silently fails.
57
+ - A real change always wins over ignored (a force-added file that's both ignored and modified shows its change badge, not dimmed).
58
+ - A non-repo workspace (`isRepo:false`) carries no decorations — the tree stays clean.
59
+
60
+ ## History graph
61
+
62
+ - Commit list from `GET /api/git/log`, newest first; each commit expands to its changed files (`GET /api/git/commit-files`), each file drills into the commit's own diff (`GET /api/git/diff?commit=`).
63
+ - **Ref badges, tiered coloring**: local branch (e.g. `main`) solid/highlighted; remote branch (`origin/main`) dimmed outline, no fill; tag amber; current `HEAD` blue.
64
+ - **Node coloring by push state** (two independent dimensions, mirrors VSCode's graph):
65
+ - *Color* — a **local-only** commit (not yet on any remote) is bright `--primary` blue so it stands out; a **pushed** commit dims to `--muted-foreground` gray (it has "sunk" onto the remote). Driven by the `pushed: boolean` each commit carries from `GET /api/git/log`.
66
+ - *Shape* — the **newest** commit (`HEAD`, list head) is a hollow ring (colored border, background fill); every commit below is a solid dot. So the topmost local-only commit reads as a **blue hollow ring**, older unpushed ones as blue solid dots, pushed ones as gray solid dots.
67
+ - **Pushed判定**: `git-manager` computes unpushed hashes from the *current branch's upstream* when one is set; when the branch has **no upstream but a remote exists**, it falls back to `rev-list HEAD --not --remotes` (commits on HEAD but on no remote branch) — **not** `rev-list HEAD`, which would wrongly paint the entire history local-only and ignore the visible `origin/*` tracking branches.
68
+ - **Infinite scroll**: an IntersectionObserver sentinel at the bottom raises `limit` by a page (50) at a time (`log` is capped server-side at 2000). Stops automatically when fewer than `limit` commits come back (reached the root).
69
+
70
+ ## Credentials & SSH
71
+
72
+ - Managed from a gear (titled **Git Credentials**) on the panel.
73
+ - **HTTPS credentials, multiple per setup**: `~/.git-credentials` is the **single source of truth** (one `https://user:token@host` line per host, file mode 0600). No separate encrypted mirror.
74
+ - List shows configured `{host, username}` rows; **the token is never returned to the client / never re-displayed**.
75
+ - Add a new credential without closing the modal (supports adding several in a row).
76
+ - Delete is per-host with an **inline two-step confirm**; deleting actually removes the line from `~/.git-credentials` (idempotent).
77
+ - **SSH**: lists private keys in `~/.ssh` (with an `encrypted` flag, never key contents) and ssh-agent reachability + loaded-key count. An encrypted, not-yet-loaded key shows an **Unlock** button → inline passphrase field → on submit, loads the key into the shared ssh-agent (`POST /api/git/ssh/unlock`) and flips it to **Loaded** — no copying an `ssh-add` command into the terminal. The passphrase reaches `ssh-add` only through a throwaway `SSH_ASKPASS` helper (never argv / disk / log), and the helper is **answer-once** so a wrong passphrase fails fast instead of hanging on a retry prompt. "Restart needs re-unlock" note retained (the agent is process-scoped).
78
+ - **Remote protocol switch**: rewrite `origin` between HTTPS and scp-style SSH (`GET/POST /api/git/remote/protocol`).
79
+
80
+ ## Out of scope (this round)
81
+
82
+ Branch create / switch / merge, ahead/behind indicator, AI-generated commit messages, clone, merge-conflict resolution UI, stash, worktree, timeline, PR management, the **DAG rail graph** (branch fork/merge visualization), and single-file discard. The DAG graph + ahead/behind are deferred to the branch-operations round, where they have live branch actions to feed them — standalone they're decoration, not a viewing need.
@@ -0,0 +1,118 @@
1
+ # Terminal — Requirements
2
+
3
+ xterm.js terminal with a node-pty backend, multi-tab, reconnect-resilient.
4
+
5
+ ## Core behaviour
6
+
7
+ ### Multi-tab
8
+ - Each tab owns an independent PTY
9
+ - Every terminal has a unique `terminalId` for routing
10
+ - Tabs close independently
11
+ - All tabs on a client share one WebSocket connection (multiplexed by `terminalId`)
12
+
13
+ ### Working directory
14
+
15
+ | Scenario | Initial cwd |
16
+ |---|---|
17
+ | Default workspace terminal | Current project's workspace root (`activeProject.path`) |
18
+ | Skill mini-workspace terminal | The skill directory (cwd prop passed by caller) |
19
+ | No workspace bound | `?folder=` URL param, falling back to server `$HOME` |
20
+
21
+ Derivation in [packages/admin/src/features/terminal/terminal-panel.tsx:108](../../../packages/admin/src/features/terminal/terminal-panel.tsx#L108); backend resolution in [packages/server/src/ws/terminal-manager.ts:46](../../../packages/server/src/ws/terminal-manager.ts#L46).
22
+
23
+ Workspace switch does **not** migrate existing terminals — they keep their original cwd. Close and reopen to pick up the new workspace root.
24
+
25
+ ### Reconnect resilience
26
+
27
+ When the WebSocket drops:
28
+
29
+ 1. PTYs are **not killed** — they stay in the module-level `terminals` map with `currentWs = null` (`packages/server/src/ws/terminal-manager.ts`)
30
+ 2. Output during detach is buffered (ring buffer, up to `config.limits.terminalOutputBuffer` = 50 KB per terminal)
31
+ 3. Grace period: `config.timeout.terminalGrace` (default 5 min)
32
+ 4. On reconnect, the client sends `terminal:reattach` (sent on initial mount **and** on every subsequent `_connected` event)
33
+ 5. Server replays the entire output buffer, reattaches live I/O, and responds with `terminal:reattached { terminalIds: [...] }`
34
+ 6. If the grace timer expires first, the PTY is killed and the detach entry removed
35
+
36
+ Connection-level liveness and reconnect (server keepalive tolerance, client self-check timer, auth-expiry handling) are owned by the shared WS client — see [design/ws.md](../design/ws.md#client-side-liveness--reconnect).
37
+
38
+ On the reattach handler ([packages/admin/src/features/terminal/terminal-panel.tsx](../../../packages/admin/src/features/terminal/terminal-panel.tsx)), each id in `terminalIds` is dispatched by whether a local xterm instance already exists:
39
+
40
+ - **Already exists** (typical after a transient WS reconnect): only a `terminal:resize` is sent so server PTY dimensions resync; the existing instance keeps its scrollback and continues receiving live output.
41
+ - **Does not exist** (first mount, or the previous instance was disposed): a fresh xterm container is created and bound to that id. Bracketed paste mode is resynced by locally writing `\x1b[?2004h` into the new instance — bash enabled the mode on the PTY at spawn time, but that sequence went to the disposed instance; without the resync, a multi-line paste into the reattached terminal would be sent unbracketed and execute line by line.
42
+
43
+ ### Environment
44
+ - Shell: `$SHELL` or `/bin/bash`
45
+ - Terminal type: `xterm-256color`
46
+ - Default size: 80 × 24 (resize requests override)
47
+ - Strips `npm_config_prefix` env (avoids nvm warnings when starting node)
48
+
49
+ ### Lifecycle
50
+
51
+ ```
52
+ ┌─────────────┐
53
+ create ──▶│ in this WS │
54
+ │ session map │
55
+ └──────┬──────┘
56
+ │ WS drops
57
+ ▼
58
+ ┌─────────────┐
59
+ │ detached │ (grace timer = 5min, ring buffer active)
60
+ │ pool │
61
+ └──┬───────┬──┘
62
+ reattach │ │ grace expires
63
+ ▼ ▼
64
+ ┌─────────┐ kill PTY, clean up
65
+ │ replay +│
66
+ │ live IO │
67
+ └─────────┘
68
+ ```
69
+
70
+ Close semantics:
71
+ - Explicit `terminal:close` — PTY killed immediately, detach entry (if any) cleaned up
72
+ - WS drop → grace expires (default 5 min) without reattach → PTY killed
73
+ - Server shutdown — all PTYs die with the process
74
+
75
+ ## WebSocket protocol
76
+
77
+ | Direction | Type | Fields | Purpose |
78
+ |---|---|---|---|
79
+ | C→S | `terminal:start` | `terminalId?`, `cwd?`, `cols?`, `rows?` | Spawn a new PTY |
80
+ | C→S | `terminal:input` | `terminalId`, `data` | Send user keystrokes |
81
+ | C→S | `terminal:resize` | `terminalId`, `cols`, `rows` | Resize PTY (screen resize) |
82
+ | C→S | `terminal:close` | `terminalId` | Explicit close |
83
+ | C→S | `terminal:reattach` | — | Reattach all detached terminals after reconnect |
84
+ | S→C | `terminal:ready` | `terminalId` | PTY spawned and ready |
85
+ | S→C | `terminal:output` | `terminalId`, `data` | PTY stdout/stderr |
86
+ | S→C | `terminal:exit` | `terminalId`, `exitCode` | PTY exited |
87
+ | S→C | `terminal:reattached` | `terminalIds` | Reattach completed for these |
88
+
89
+ Source: [packages/server/src/ws/terminal-manager.ts](../../../packages/server/src/ws/terminal-manager.ts).
90
+
91
+ ## Config
92
+
93
+ | Config key | Default | Purpose |
94
+ |---|---|---|
95
+ | `config.timeout.terminalGrace` | 300,000 ms | Detach retention period |
96
+ | `config.limits.terminalOutputBuffer` | 50,000 bytes | Detach output ring buffer cap |
97
+
98
+ Defined in [packages/server/src/config.ts](../../../packages/server/src/config.ts).
99
+
100
+ ## Test cases
101
+
102
+ | # | Scenario | Expected |
103
+ |---|---|---|
104
+ | T1 | Start, run `ls` | Output arrives; prompt returns |
105
+ | T2 | Start in skill mini-workspace | cwd is the skill directory (`pwd` confirms) |
106
+ | T3 | Resize window | PTY cols/rows update; long-running process (e.g. `watch ls`) reflows |
107
+ | T4 | Disconnect mid-command (`sleep 5 && echo done`) → reconnect within grace period | Buffered output replayed; `done` visible |
108
+ | T5 | Disconnect → wait > grace period → reconnect | Terminal gone (PTY killed at grace expiry) |
109
+ | T6 | Open 3 tabs, close 1 explicitly | Other 2 keep their PTYs; closed one gets `terminal:exit` |
110
+ | T7 | `exit` from within shell | `terminal:exit` with exitCode=0; tab shows closed |
111
+ | T8 | Paste a 10 KB block | Sent as `terminal:input` without choking; shell echoes in chunks |
112
+
113
+ Follows the pattern of [test/session.md](../test/session.md).
114
+
115
+ ## Related design
116
+
117
+ - Detach / reattach plumbing: [design/architecture.md#terminalmanager](../design/architecture.md#terminalmanager--pty-management)
118
+ - WS envelope shape: [design/ws.md](../design/ws.md)
@@ -0,0 +1,80 @@
1
+ # Workspace Layout — Requirements
2
+
3
+ Overall workspace layout: Activity Bar + Sidebar + main content + a resizable bottom panel.
4
+
5
+ ## Layout structure
6
+
7
+ Most tabs use the standard layout (with bottom panel):
8
+
9
+ ```
10
+ +---+-------------+---------------------------+
11
+ | A | Sidebar | Main Content Area |
12
+ | c | | |
13
+ | t | (tab- | (Canvas / agent configs/ |
14
+ | i | dependent) | …) |
15
+ | v | +---------------------------+
16
+ | i | | Bottom Panel |
17
+ | t | | (Chat | Terminal) |
18
+ | y | | |
19
+ | B | | |
20
+ | a | | |
21
+ | r | | |
22
+ +---+--------------+---------------------------+
23
+ ```
24
+
25
+ **Skills** is an exception — SkillsMain (file tree + Canvas) uses the full height, no bottom panel.
26
+
27
+ The Bottom Panel (Chat + Terminal) can also be **floated** into a draggable window that survives activity-tab switching — see "Bottom panel" below.
28
+
29
+ ## Activity Bar tabs
30
+
31
+ | Icon | Tab | Sidebar | Main content | Bottom panel |
32
+ |---|---|---|---|---|
33
+ | FolderTree | Explorer | File tree + ops | Canvas (Monaco + previews) | Chat + Terminal |
34
+ | MessageSquare | Sessions | Session list | Session message viewer | Chat + Terminal |
35
+ | Zap | Skills | Skill list | SkillsMain (full height) | — |
36
+ | Bot | Agents | — | Agent config editor + Test (full width) | Chat + Terminal |
37
+ | MessageCircle | Channels | Channel list | Channel config editor | Chat + Terminal |
38
+ | Sparkles | Evolution | — | Evolution main (run/apply review) | Chat + Terminal |
39
+ | Clock | Cron | — | Cron job list + run audit | Chat + Terminal |
40
+ | Settings2 | Settings | — | Settings panel | Chat + Terminal |
41
+
42
+ ## Resizable panels
43
+ - **Sidebar width**: drag between sidebar and main content
44
+ - **Bottom panel height**: drag between main content and bottom panel
45
+ - Sizes persisted via localStorage
46
+
47
+ ## Bottom panel
48
+
49
+ Two tabs:
50
+ - **Chat**: the main chat panel
51
+ - **Terminal**: xterm multi-tab terminal
52
+
53
+ Current tab stored in `editorStore.bottomTab`.
54
+
55
+ ### Floating mode
56
+ The tab bar has a float button on the far right. Toggling it detaches the panel into a draggable window:
57
+ - **Default position/size**: bottom-right corner, 480×640 with a 24px margin
58
+ - **Drag**: grab the tab bar (anywhere except buttons) to reposition
59
+ - **Resize**: four edges (N/S/E/W) + four corners (NW/NE/SW/SE), minimum size 320×240
60
+ - **Visible from any activity tab** — Explorer's in-layout bottom panel is hidden while floating; Canvas takes full height
61
+ - **Only one instance** mounted at a time (xterm DOM gets rebuilt once on dock/undock; server-side terminal sessions are preserved and reattached, scroll buffer is lost)
62
+ - **State is in `sessionStorage`** (`halo_bottom_floating` + `halo_bottom_float_rect`) — page refresh reverts to docked mode
63
+ - **Agent "Test" button** dispatches `halo:navigate → explorer` to surface Chat; this jump is suppressed while floating since Chat is already globally visible
64
+ - **Close (✕) button** on the float window's tab bar docks it back
65
+
66
+ ## Shortcuts
67
+
68
+ | Shortcut | Action |
69
+ |---|---|
70
+ | Cmd/Ctrl + P | Quick Open (fuzzy file search) |
71
+ | Cmd/Ctrl + S | Save current file |
72
+ | Alt + W | Close current tab (Cmd+W cannot be overridden in browsers) |
73
+
74
+ ## Auth
75
+
76
+ Login page sits in front of the Activity Bar:
77
+ - Password login via `POST /api/auth/login`
78
+ - JWT in an HTTP-only cookie
79
+ - Auto-refresh after 24 hours
80
+ - Token lifetime up to 14 days
@@ -0,0 +1,101 @@
1
+ # Platform Docs Seeding Test Cases
2
+
3
+ Test URL: local server (default `http://localhost:9527/?folder=<absolute-workspace-path>`).
4
+ Password: whatever `HALO_PASSWORD` is set to, or the random password printed by the server on first startup.
5
+
6
+ Prerequisite: `~/.halo/global/` already exists from a prior run. Pick any workspace that is **not** the halo source repo (e.g. `/path/to/test-workspace`).
7
+
8
+ ---
9
+
10
+ ## Test matrix
11
+
12
+ ### A. First-time seed
13
+
14
+ | # | Scenario | Method | Expected |
15
+ |---|---|---|---|
16
+ | A1 | Fresh install simulation | `rm -rf ~/.halo/global/docs && restart server` | Startup log: `[Init] Seeding platform docs from ...`; `~/.halo/global/docs/` has 13 files |
17
+ | A2 | Source detection in monorepo | Start server from `/path/to/halo`, delete docs first | Log: `Seeding platform docs from /path/to/halo/.halo/docs` |
18
+ | A3 | Source detection for packaged install | Create `packages/server/bundled-docs/guide/skills.md` with "FAKE"; delete `~/.halo/global/docs/`; restart | Log says `Seeding platform docs from .../bundled-docs`; `~/.halo/global/docs/guide/skills.md` contains "FAKE" |
19
+ | A4 | No source at all | Move init.js somewhere isolated (no repo parent, no bundled-docs), run | Log: `[Init] No docs source found — skipping platform docs seed`; no crash; `~/.halo/global/docs/` not created |
20
+ | A5 | Allowlist boundary | After A1, check what's in `~/.halo/global/docs/` | Only 13 files: `guide/` (8), `dev/` (3), `requirements/` (2). **No** `INDEX.md`, `design/`, `test/`, `plans/`, or other `dev/`/`requirements/` files (the doc lookup table now lives inline in the `halo` skill) |
21
+
22
+ ### B. User edits and upgrades
23
+
24
+ | # | Scenario | Method | Expected |
25
+ |---|---|---|---|
26
+ | B1 | User edit preserved | Edit `~/.halo/global/docs/guide/skills.md` → restart server | Edit intact; no `[Init] Created` log for that file |
27
+ | B2 | Deleted file restored | `rm ~/.halo/global/docs/guide/agents.md` → restart | Log: `[Init] Created .../agents.md`; file restored from source |
28
+ | B3 | Version bump hint | Set `.template-version` to a value below the current `TEMPLATE_VERSION` (see `packages/server/src/init.ts`), restart | Log: `[Init] Seed templates updated: v<old> → v<current>. To refresh a specific file, delete it from ... and restart.` Plus `[Server] Templates outdated (v<old> → v<current>), refreshing ~/.halo/global/` from the startup version-check |
29
+ | B4 | Idempotent second run | Restart the server twice in a row | Second run has **no** `[Init] Created` logs; `.template-version` stays at the current `TEMPLATE_VERSION` |
30
+
31
+ ### C. Agent-facing integration
32
+
33
+ | # | Scenario | Input | Expected |
34
+ |---|---|---|---|
35
+ | C1 | `halo` skill carries the doc table | Open any chat with the default agent, Debug mode → first message → Prompt button | System prompt lists the `halo` skill in the available-skills block. Activate it (`activate_skill halo`) and the loaded body contains `### Platform Documentation` with the "User asks … / Read …" lookup table |
36
+ | C2 | Agent knows about bundled docs | Open chat in a non-halo workspace, ask: "what platform docs ship with halo?" | Agent activates the `halo` skill and answers from its lookup table (no `file_read` needed for the overview) |
37
+ | C3 | Answer to "how do I test my skill?" | New chat, ask: "I just made a skill. How do I test it?" | Agent reads `~/.halo/global/docs/guide/testing-agents-and-skills.md` directly (path from system prompt table), answers with the attach-to-agent → Test → trigger flow |
38
+ | C4 | Answer to "where do API keys go?" | New chat, ask: "I want my skill to use an API key. Where do I put it?" | Agent reads `guide/secrets-and-credentials.md`, explains schema in `config.yaml` + value in `settings.yaml` + `<<ENV>>` + skill body using `{{params.<key>}}` short form |
39
+ | C5 | Answer for a question NOT in bundled docs | New chat, ask: "how does the file watcher debounce work?" | Agent looks for it in the docs, doesn't find (bundled docs don't have `design/`), falls back to reading code or admits it doesn't know — **does not hallucinate** |
40
+ | C6 | Works in any workspace | Repeat C3 in a workspace that has **no** `.halo/docs/` of its own (e.g. `/path/to/test-workspace`) | Same answer — proves docs come from `~/.halo/global/`, not the workspace |
41
+
42
+ ### D. Negative / edge cases
43
+
44
+ | # | Scenario | Method | Expected |
45
+ |---|---|---|---|
46
+ | D1 | Permissions issue | `chmod -w ~/.halo/global/docs/` → delete one file → restart | `[Init] Failed to read template` or similar error logged; server still starts (seed is best-effort) |
47
+ | D2 | Concurrent starts | Start two server processes at the same time against the same home dir | Only one wins the pid lock; losers exit cleanly (not a seed-specific test, but confirms seed doesn't race) |
48
+ | D3 | Partially-populated docs | Remove half the files in `~/.halo/global/docs/guide/`; restart | Exactly the removed files get `[Init] Created` logs; others untouched |
49
+ | D4 | Garbage `.template-version` | Write `abc` to `~/.halo/global/.template-version`; restart | Treated as 0; seed runs; version is reset to the current `TEMPLATE_VERSION` at the end |
50
+
51
+ ---
52
+
53
+ ## Verification helpers
54
+
55
+ ### Check seeded file count
56
+
57
+ ```bash
58
+ find ~/.halo/global/docs -type f | wc -l
59
+ # Expected: 13
60
+ ```
61
+
62
+ ### Check allowlist boundary (A5)
63
+
64
+ ```bash
65
+ # These should all NOT exist
66
+ ls ~/.halo/global/docs/design/ 2>&1 # No such file
67
+ ls ~/.halo/global/docs/test/ 2>&1 # No such file
68
+ ls ~/.halo/global/docs/plans/ 2>&1 # No such file
69
+ ls ~/.halo/global/docs/requirements/terminal.md 2>&1 # No such file
70
+ ls ~/.halo/global/docs/dev/api.md 2>&1 # No such file
71
+ ```
72
+
73
+ ### Check `halo` skill carries the doc table (C1)
74
+
75
+ ```bash
76
+ grep -A 3 "Platform Documentation" ~/.halo/global/skills/halo/SKILL.md
77
+ # Expected: "### Platform Documentation" section with the inline "User asks … / Read …" lookup table
78
+ ```
79
+
80
+ ### Check source that was used
81
+
82
+ ```bash
83
+ grep "Seeding platform docs\|No docs source" /tmp/halo.log 2>/dev/null | tail -1
84
+ # Or check server's stdout — the log line is emitted once per startup
85
+ ```
86
+
87
+ ### Force a fresh seed
88
+
89
+ ```bash
90
+ rm -rf ~/.halo/global/docs ~/.halo/global/.template-version
91
+ # Restart server
92
+ # Expected: 13 [Init] Created log lines for docs/*
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Known limitations
98
+
99
+ - **Cross-links to non-bundled docs** (e.g. `guide/skills.md` → `design/prompt-system.md`) will 404 if an agent tries to follow them. Agents should answer from the doc body rather than chase links. Noted in the `halo` skill body.
100
+ - **Version downgrade** not detected: if you set `.template-version` higher than the current `TEMPLATE_VERSION`, nothing happens — no warning, no overwrite. Not a concern in practice.
101
+ - **Seed does not overwrite**. If a user edits a file and a newer Halo version ships a fix to the same doc, the user's edit wins. Workaround: `rm` the file and restart.