tawn 0.1.0__tar.gz

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 (250) hide show
  1. tawn-0.1.0/LICENSE +21 -0
  2. tawn-0.1.0/PKG-INFO +201 -0
  3. tawn-0.1.0/README.md +131 -0
  4. tawn-0.1.0/pyproject.toml +94 -0
  5. tawn-0.1.0/setup.cfg +4 -0
  6. tawn-0.1.0/setup.py +51 -0
  7. tawn-0.1.0/src/tawn/__init__.py +3 -0
  8. tawn-0.1.0/src/tawn/_webserver.py +66 -0
  9. tawn-0.1.0/src/tawn/branding.py +82 -0
  10. tawn-0.1.0/src/tawn/capability/__init__.py +1 -0
  11. tawn-0.1.0/src/tawn/capability/audit.py +87 -0
  12. tawn-0.1.0/src/tawn/capability/fs.py +67 -0
  13. tawn-0.1.0/src/tawn/capability/grants.py +53 -0
  14. tawn-0.1.0/src/tawn/capability/integrity.py +38 -0
  15. tawn-0.1.0/src/tawn/cli.py +2183 -0
  16. tawn-0.1.0/src/tawn/compiler/__init__.py +0 -0
  17. tawn-0.1.0/src/tawn/compiler/classifier.py +79 -0
  18. tawn-0.1.0/src/tawn/compiler/compiler.py +409 -0
  19. tawn-0.1.0/src/tawn/compiler/conflicts.py +49 -0
  20. tawn-0.1.0/src/tawn/compiler/delta.py +179 -0
  21. tawn-0.1.0/src/tawn/compiler/embedder.py +208 -0
  22. tawn-0.1.0/src/tawn/compiler/entities.py +126 -0
  23. tawn-0.1.0/src/tawn/compiler/parser.py +237 -0
  24. tawn-0.1.0/src/tawn/compiler/wiki.py +197 -0
  25. tawn-0.1.0/src/tawn/config.py +26 -0
  26. tawn-0.1.0/src/tawn/db.py +107 -0
  27. tawn-0.1.0/src/tawn/dbsetup.py +60 -0
  28. tawn-0.1.0/src/tawn/domains/__init__.py +1 -0
  29. tawn-0.1.0/src/tawn/domains/academic/__init__.py +22 -0
  30. tawn-0.1.0/src/tawn/domains/base.py +33 -0
  31. tawn-0.1.0/src/tawn/domains/creation.py +104 -0
  32. tawn-0.1.0/src/tawn/domains/hobby/__init__.py +16 -0
  33. tawn-0.1.0/src/tawn/domains/records.py +112 -0
  34. tawn-0.1.0/src/tawn/domains/registry.py +116 -0
  35. tawn-0.1.0/src/tawn/domains/research/__init__.py +21 -0
  36. tawn-0.1.0/src/tawn/domains/stub.py +32 -0
  37. tawn-0.1.0/src/tawn/domains/wealth/__init__.py +11 -0
  38. tawn-0.1.0/src/tawn/domains/wealth/api.py +51 -0
  39. tawn-0.1.0/src/tawn/domains/wealth/cli.py +105 -0
  40. tawn-0.1.0/src/tawn/domains/wealth/dashboard.py +62 -0
  41. tawn-0.1.0/src/tawn/domains/wealth/holdings.py +76 -0
  42. tawn-0.1.0/src/tawn/domains/wealth/prices.py +101 -0
  43. tawn-0.1.0/src/tawn/domains/wealth/schedule.py +63 -0
  44. tawn-0.1.0/src/tawn/domains/wealth/snapshot.py +118 -0
  45. tawn-0.1.0/src/tawn/domains/work/__init__.py +17 -0
  46. tawn-0.1.0/src/tawn/federation/__init__.py +0 -0
  47. tawn-0.1.0/src/tawn/federation/adapters/__init__.py +0 -0
  48. tawn-0.1.0/src/tawn/federation/adapters/base.py +35 -0
  49. tawn-0.1.0/src/tawn/federation/adapters/chatgpt.py +59 -0
  50. tawn-0.1.0/src/tawn/federation/adapters/claude_ai.py +43 -0
  51. tawn-0.1.0/src/tawn/federation/adapters/claude_code.py +84 -0
  52. tawn-0.1.0/src/tawn/federation/adapters/codex.py +71 -0
  53. tawn-0.1.0/src/tawn/federation/adapters/gemini.py +46 -0
  54. tawn-0.1.0/src/tawn/federation/adapters/gemini_cli.py +109 -0
  55. tawn-0.1.0/src/tawn/federation/adapters/generic.py +49 -0
  56. tawn-0.1.0/src/tawn/federation/config.py +41 -0
  57. tawn-0.1.0/src/tawn/federation/discovery.py +122 -0
  58. tawn-0.1.0/src/tawn/federation/dispatcher.py +43 -0
  59. tawn-0.1.0/src/tawn/federation/exporter.py +112 -0
  60. tawn-0.1.0/src/tawn/federation/merge.py +172 -0
  61. tawn-0.1.0/src/tawn/federation/normalizer.py +151 -0
  62. tawn-0.1.0/src/tawn/federation/schema.py +38 -0
  63. tawn-0.1.0/src/tawn/federation/systemd.py +128 -0
  64. tawn-0.1.0/src/tawn/federation/watcher.py +98 -0
  65. tawn-0.1.0/src/tawn/history.py +80 -0
  66. tawn-0.1.0/src/tawn/home.py +93 -0
  67. tawn-0.1.0/src/tawn/ignore.py +183 -0
  68. tawn-0.1.0/src/tawn/mcp_server.py +75 -0
  69. tawn-0.1.0/src/tawn/memory/__init__.py +0 -0
  70. tawn-0.1.0/src/tawn/memory/brief.py +72 -0
  71. tawn-0.1.0/src/tawn/memory/note.py +62 -0
  72. tawn-0.1.0/src/tawn/memory/recall.py +175 -0
  73. tawn-0.1.0/src/tawn/memory/schema.py +154 -0
  74. tawn-0.1.0/src/tawn/model/__init__.py +1 -0
  75. tawn-0.1.0/src/tawn/model/breaker.py +57 -0
  76. tawn-0.1.0/src/tawn/model/catalog.py +106 -0
  77. tawn-0.1.0/src/tawn/model/directory.py +95 -0
  78. tawn-0.1.0/src/tawn/model/identity.py +44 -0
  79. tawn-0.1.0/src/tawn/model/keys.py +52 -0
  80. tawn-0.1.0/src/tawn/model/ledger.py +93 -0
  81. tawn-0.1.0/src/tawn/model/personality.py +59 -0
  82. tawn-0.1.0/src/tawn/model/providers/__init__.py +1 -0
  83. tawn-0.1.0/src/tawn/model/providers/anthropic.py +127 -0
  84. tawn-0.1.0/src/tawn/model/providers/gemini.py +138 -0
  85. tawn-0.1.0/src/tawn/model/providers/ollama.py +149 -0
  86. tawn-0.1.0/src/tawn/model/providers/openai_compat.py +156 -0
  87. tawn-0.1.0/src/tawn/model/router.py +346 -0
  88. tawn-0.1.0/src/tawn/model/types.py +72 -0
  89. tawn-0.1.0/src/tawn/updater.py +179 -0
  90. tawn-0.1.0/src/tawn/user_config.py +124 -0
  91. tawn-0.1.0/src/tawn/web/__init__.py +3 -0
  92. tawn-0.1.0/src/tawn/web/app.py +290 -0
  93. tawn-0.1.0/src/tawn/web/dist/assets/abnfDiagram-VRR7QNED-C5F_AKgo.js +1 -0
  94. tawn-0.1.0/src/tawn/web/dist/assets/arc-DfuEWumi.js +1 -0
  95. tawn-0.1.0/src/tawn/web/dist/assets/architectureDiagram-ZJ3FMSHR-CMdbMQ-v.js +36 -0
  96. tawn-0.1.0/src/tawn/web/dist/assets/blockDiagram-677ZJIJ3-BvWKvlkz.js +132 -0
  97. tawn-0.1.0/src/tawn/web/dist/assets/c4Diagram-LMCZKHZV-cmtcJnEh.js +10 -0
  98. tawn-0.1.0/src/tawn/web/dist/assets/channel-1wBTkNV8.js +1 -0
  99. tawn-0.1.0/src/tawn/web/dist/assets/chunk-2Q5K7J3B-CVkZvg6J.js +1 -0
  100. tawn-0.1.0/src/tawn/web/dist/assets/chunk-32BRIVSS-BJnUTK_p.js +1 -0
  101. tawn-0.1.0/src/tawn/web/dist/assets/chunk-5VM5RSS4-BgCFFy0k.js +15 -0
  102. tawn-0.1.0/src/tawn/web/dist/assets/chunk-EX3LRPZG-D3opRKGp.js +231 -0
  103. tawn-0.1.0/src/tawn/web/dist/assets/chunk-JWPE2WC7-BQBJybxG.js +1 -0
  104. tawn-0.1.0/src/tawn/web/dist/assets/chunk-MOJQB5TN-DPCuIYGK.js +88 -0
  105. tawn-0.1.0/src/tawn/web/dist/assets/chunk-RYQCIY6F-BGrEIzMk.js +1 -0
  106. tawn-0.1.0/src/tawn/web/dist/assets/chunk-V7JOEXUC-16qlVcj5.js +206 -0
  107. tawn-0.1.0/src/tawn/web/dist/assets/chunk-VR4S4FIN-Dri-A7dn.js +1 -0
  108. tawn-0.1.0/src/tawn/web/dist/assets/chunk-XXDRQBXY-BZJRsKXY.js +1 -0
  109. tawn-0.1.0/src/tawn/web/dist/assets/classDiagram-OUVF2IWQ-CidNYYIR.js +1 -0
  110. tawn-0.1.0/src/tawn/web/dist/assets/classDiagram-v2-EOCWNBFH-CidNYYIR.js +1 -0
  111. tawn-0.1.0/src/tawn/web/dist/assets/cose-bilkent-JH36ORCC-B7ClDWJL.js +1 -0
  112. tawn-0.1.0/src/tawn/web/dist/assets/cynefin-VYW2F7L2-DdT7ZLm1.js +178 -0
  113. tawn-0.1.0/src/tawn/web/dist/assets/cynefinDiagram-TSTJHNR4-D3XVPeTI.js +62 -0
  114. tawn-0.1.0/src/tawn/web/dist/assets/cytoscape.esm-CUqq0XTU.js +331 -0
  115. tawn-0.1.0/src/tawn/web/dist/assets/dagre-VKFMJZFB-C7hI6zFs.js +4 -0
  116. tawn-0.1.0/src/tawn/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  117. tawn-0.1.0/src/tawn/web/dist/assets/diagram-FQU43EPY-BwkzyqxA.js +3 -0
  118. tawn-0.1.0/src/tawn/web/dist/assets/diagram-G47NLZAW-BNQLclaM.js +24 -0
  119. tawn-0.1.0/src/tawn/web/dist/assets/diagram-NH7WQ7WH-C89oedn8.js +24 -0
  120. tawn-0.1.0/src/tawn/web/dist/assets/diagram-OA4YK3LP-CM_ra1CZ.js +30 -0
  121. tawn-0.1.0/src/tawn/web/dist/assets/diagram-WEI45ONY-CIyDX7LQ.js +41 -0
  122. tawn-0.1.0/src/tawn/web/dist/assets/ebnfDiagram-CCIWWBDH-CStWDurL.js +1 -0
  123. tawn-0.1.0/src/tawn/web/dist/assets/erDiagram-Q63AITRT-BwAeP1e2.js +85 -0
  124. tawn-0.1.0/src/tawn/web/dist/assets/flowDiagram-23GEKE2U-CAA-JOsf.js +156 -0
  125. tawn-0.1.0/src/tawn/web/dist/assets/ganttDiagram-NO4QXBWP-7bajtVpE.js +292 -0
  126. tawn-0.1.0/src/tawn/web/dist/assets/gitGraphDiagram-IHSO6WYX-qW76mQDo.js +106 -0
  127. tawn-0.1.0/src/tawn/web/dist/assets/graph-C2PyVmSI.js +1 -0
  128. tawn-0.1.0/src/tawn/web/dist/assets/index-BaAvK0kS.js +162 -0
  129. tawn-0.1.0/src/tawn/web/dist/assets/index-CPMCxrJ6.js +3 -0
  130. tawn-0.1.0/src/tawn/web/dist/assets/index-DMnyYUiC.css +1 -0
  131. tawn-0.1.0/src/tawn/web/dist/assets/infoDiagram-FWYZ7A6U-BN3TAAMm.js +2 -0
  132. tawn-0.1.0/src/tawn/web/dist/assets/init-Gi6I4Gst.js +1 -0
  133. tawn-0.1.0/src/tawn/web/dist/assets/ishikawaDiagram-FXEZZL3T-DIjUQHYT.js +70 -0
  134. tawn-0.1.0/src/tawn/web/dist/assets/journeyDiagram-5HDEW3XC-Cd_0i_MI.js +139 -0
  135. tawn-0.1.0/src/tawn/web/dist/assets/kanban-definition-HUTT4EX6-EJykSLKC.js +89 -0
  136. tawn-0.1.0/src/tawn/web/dist/assets/katex-C5jXJg4s.js +257 -0
  137. tawn-0.1.0/src/tawn/web/dist/assets/layout-HEHzrzvT.js +1 -0
  138. tawn-0.1.0/src/tawn/web/dist/assets/linear-DZV_74YI.js +1 -0
  139. tawn-0.1.0/src/tawn/web/dist/assets/map-BQHxRNoK.js +1 -0
  140. tawn-0.1.0/src/tawn/web/dist/assets/mermaid.core-Cs_D-MaZ.js +314 -0
  141. tawn-0.1.0/src/tawn/web/dist/assets/mindmap-definition-LN4V7U3C-Dayioghr.js +96 -0
  142. tawn-0.1.0/src/tawn/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  143. tawn-0.1.0/src/tawn/web/dist/assets/pegDiagram-2B236MQR-CEg98Wsy.js +1 -0
  144. tawn-0.1.0/src/tawn/web/dist/assets/pieDiagram-ENE6RG2P-BWzY0gy2.js +39 -0
  145. tawn-0.1.0/src/tawn/web/dist/assets/quadrantDiagram-ABIIQ3AL-GoCE2r_5.js +7 -0
  146. tawn-0.1.0/src/tawn/web/dist/assets/railroadDiagram-RFXS5EU6-BxTtKfWT.js +1 -0
  147. tawn-0.1.0/src/tawn/web/dist/assets/requirementDiagram-TGXJPOKE-CD0t2ic5.js +84 -0
  148. tawn-0.1.0/src/tawn/web/dist/assets/sankeyDiagram-HTMAVEWB-Om9xv51_.js +40 -0
  149. tawn-0.1.0/src/tawn/web/dist/assets/sequenceDiagram-DBY2YBRQ-Ct9pa12Y.js +162 -0
  150. tawn-0.1.0/src/tawn/web/dist/assets/sizeCapture-X5ZJPWSS-D0lniTrU.js +1 -0
  151. tawn-0.1.0/src/tawn/web/dist/assets/stateDiagram-2N3HPSRC-C3JQoYro.js +1 -0
  152. tawn-0.1.0/src/tawn/web/dist/assets/stateDiagram-v2-6OUMAXLB-s_yFnFGS.js +1 -0
  153. tawn-0.1.0/src/tawn/web/dist/assets/swimlanes-5IMT3BWC-B0FKBUkt.js +2 -0
  154. tawn-0.1.0/src/tawn/web/dist/assets/swimlanesDiagram-G3AALYLV-aVA3YxQh.js +8 -0
  155. tawn-0.1.0/src/tawn/web/dist/assets/timeline-definition-FHXFAJF6-MhKDYH6H.js +120 -0
  156. tawn-0.1.0/src/tawn/web/dist/assets/vennDiagram-L72KCM5P-CqvlQmOm.js +34 -0
  157. tawn-0.1.0/src/tawn/web/dist/assets/wardleyDiagram-EHGQE667-DdS-6LAr.js +78 -0
  158. tawn-0.1.0/src/tawn/web/dist/assets/xychartDiagram-FW5EYKEG-DpAi9dnU.js +7 -0
  159. tawn-0.1.0/src/tawn/web/dist/favicon.svg +10 -0
  160. tawn-0.1.0/src/tawn/web/dist/index.html +31 -0
  161. tawn-0.1.0/src/tawn/web/routes/__init__.py +0 -0
  162. tawn-0.1.0/src/tawn/web/routes/chat.py +231 -0
  163. tawn-0.1.0/src/tawn/web/routes/domains.py +85 -0
  164. tawn-0.1.0/src/tawn/web/routes/federation.py +411 -0
  165. tawn-0.1.0/src/tawn/web/routes/grants.py +118 -0
  166. tawn-0.1.0/src/tawn/web/routes/history.py +23 -0
  167. tawn-0.1.0/src/tawn/web/routes/memory.py +276 -0
  168. tawn-0.1.0/src/tawn/web/routes/profile.py +29 -0
  169. tawn-0.1.0/src/tawn/web/routes/setup.py +100 -0
  170. tawn-0.1.0/src/tawn/web/routes/update.py +47 -0
  171. tawn-0.1.0/src/tawn.egg-info/PKG-INFO +201 -0
  172. tawn-0.1.0/src/tawn.egg-info/SOURCES.txt +248 -0
  173. tawn-0.1.0/src/tawn.egg-info/dependency_links.txt +1 -0
  174. tawn-0.1.0/src/tawn.egg-info/entry_points.txt +9 -0
  175. tawn-0.1.0/src/tawn.egg-info/requires.txt +50 -0
  176. tawn-0.1.0/src/tawn.egg-info/top_level.txt +1 -0
  177. tawn-0.1.0/tests/test_anthropic.py +126 -0
  178. tawn-0.1.0/tests/test_audit.py +19 -0
  179. tawn-0.1.0/tests/test_branding.py +28 -0
  180. tawn-0.1.0/tests/test_breaker.py +61 -0
  181. tawn-0.1.0/tests/test_catalog.py +34 -0
  182. tawn-0.1.0/tests/test_cli.py +48 -0
  183. tawn-0.1.0/tests/test_cli_federation.py +60 -0
  184. tawn-0.1.0/tests/test_cli_memory_commands.py +95 -0
  185. tawn-0.1.0/tests/test_compiler_compiler.py +88 -0
  186. tawn-0.1.0/tests/test_compiler_conflicts.py +65 -0
  187. tawn-0.1.0/tests/test_compiler_delta.py +73 -0
  188. tawn-0.1.0/tests/test_compiler_embedder.py +81 -0
  189. tawn-0.1.0/tests/test_compiler_entities.py +85 -0
  190. tawn-0.1.0/tests/test_compiler_orchestrator.py +116 -0
  191. tawn-0.1.0/tests/test_compiler_parser.py +90 -0
  192. tawn-0.1.0/tests/test_compiler_wiki.py +85 -0
  193. tawn-0.1.0/tests/test_config.py +15 -0
  194. tawn-0.1.0/tests/test_dashboard.py +41 -0
  195. tawn-0.1.0/tests/test_db.py +28 -0
  196. tawn-0.1.0/tests/test_db_setup.py +52 -0
  197. tawn-0.1.0/tests/test_directory.py +81 -0
  198. tawn-0.1.0/tests/test_domain_base.py +33 -0
  199. tawn-0.1.0/tests/test_domain_cli.py +38 -0
  200. tawn-0.1.0/tests/test_domain_creation.py +53 -0
  201. tawn-0.1.0/tests/test_domain_records.py +73 -0
  202. tawn-0.1.0/tests/test_domain_registry.py +80 -0
  203. tawn-0.1.0/tests/test_domain_stubs.py +38 -0
  204. tawn-0.1.0/tests/test_domain_wealth_register.py +10 -0
  205. tawn-0.1.0/tests/test_federation_adapters.py +320 -0
  206. tawn-0.1.0/tests/test_federation_config.py +32 -0
  207. tawn-0.1.0/tests/test_federation_discovery.py +50 -0
  208. tawn-0.1.0/tests/test_federation_dispatcher.py +56 -0
  209. tawn-0.1.0/tests/test_federation_exporter.py +83 -0
  210. tawn-0.1.0/tests/test_federation_merge.py +103 -0
  211. tawn-0.1.0/tests/test_federation_normalizer.py +66 -0
  212. tawn-0.1.0/tests/test_federation_routes.py +102 -0
  213. tawn-0.1.0/tests/test_federation_systemd.py +58 -0
  214. tawn-0.1.0/tests/test_federation_watcher.py +116 -0
  215. tawn-0.1.0/tests/test_fs.py +86 -0
  216. tawn-0.1.0/tests/test_gemini.py +135 -0
  217. tawn-0.1.0/tests/test_grants.py +49 -0
  218. tawn-0.1.0/tests/test_holdings.py +71 -0
  219. tawn-0.1.0/tests/test_home.py +24 -0
  220. tawn-0.1.0/tests/test_home_skeleton.py +24 -0
  221. tawn-0.1.0/tests/test_identity.py +31 -0
  222. tawn-0.1.0/tests/test_integrity.py +35 -0
  223. tawn-0.1.0/tests/test_keys.py +75 -0
  224. tawn-0.1.0/tests/test_ledger.py +67 -0
  225. tawn-0.1.0/tests/test_mcp_tools.py +32 -0
  226. tawn-0.1.0/tests/test_memory_brief.py +103 -0
  227. tawn-0.1.0/tests/test_memory_note.py +79 -0
  228. tawn-0.1.0/tests/test_memory_recall.py +121 -0
  229. tawn-0.1.0/tests/test_memory_schema.py +72 -0
  230. tawn-0.1.0/tests/test_memory_verbs.py +193 -0
  231. tawn-0.1.0/tests/test_model_types.py +35 -0
  232. tawn-0.1.0/tests/test_ollama.py +75 -0
  233. tawn-0.1.0/tests/test_ollama_setup.py +88 -0
  234. tawn-0.1.0/tests/test_openai_compat.py +122 -0
  235. tawn-0.1.0/tests/test_prices.py +99 -0
  236. tawn-0.1.0/tests/test_router.py +260 -0
  237. tawn-0.1.0/tests/test_schedule.py +23 -0
  238. tawn-0.1.0/tests/test_snapshot.py +60 -0
  239. tawn-0.1.0/tests/test_stage0_e2e.py +31 -0
  240. tawn-0.1.0/tests/test_stage1_e2e.py +48 -0
  241. tawn-0.1.0/tests/test_stage2_e2e.py +280 -0
  242. tawn-0.1.0/tests/test_types_stream_chunk.py +20 -0
  243. tawn-0.1.0/tests/test_url_providers.py +54 -0
  244. tawn-0.1.0/tests/test_user_config.py +100 -0
  245. tawn-0.1.0/tests/test_web.py +72 -0
  246. tawn-0.1.0/tests/test_web_chat_routes.py +47 -0
  247. tawn-0.1.0/tests/test_web_domain_routes.py +70 -0
  248. tawn-0.1.0/tests/test_web_grants_routes.py +54 -0
  249. tawn-0.1.0/tests/test_web_memory_routes.py +120 -0
  250. tawn-0.1.0/tests/test_web_setup_routes.py +53 -0
tawn-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Testimony Adekoya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tawn-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,201 @@
1
+ Metadata-Version: 2.4
2
+ Name: tawn
3
+ Version: 0.1.0
4
+ Summary: Tawn — the personal digital twin. Local-first memory and context core for your work, wealth, research, and academic life.
5
+ Author-email: Testimony Adekoya <testimonyadekoya.02@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/tawn-hq/tawn
8
+ Project-URL: Repository, https://github.com/tawn-hq/tawn
9
+ Project-URL: Bug Tracker, https://github.com/tawn-hq/tawn/issues
10
+ Keywords: ai,personal-ai,digital-twin,memory,mcp,rag,local-first,llm
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Environment :: Web Environment
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Operating System :: MacOS
19
+ Classifier: Operating System :: Microsoft :: Windows
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Office/Business
24
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
25
+ Requires-Python: >=3.12
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: pydantic>=2.7
29
+ Requires-Dist: pydantic-settings>=2.2
30
+ Requires-Dist: PyYAML>=6.0
31
+ Requires-Dist: typer>=0.12
32
+ Requires-Dist: SQLAlchemy>=2.0
33
+ Requires-Dist: psycopg[binary]>=3.1
34
+ Requires-Dist: httpx>=0.27
35
+ Requires-Dist: rich>=13.7
36
+ Requires-Dist: fastapi>=0.111
37
+ Requires-Dist: uvicorn>=0.30
38
+ Requires-Dist: keyring>=25.0
39
+ Requires-Dist: python-frontmatter>=1.1
40
+ Requires-Dist: rapidfuzz>=3.0
41
+ Requires-Dist: watchfiles>=0.21
42
+ Provides-Extra: anthropic
43
+ Requires-Dist: anthropic>=0.100; extra == "anthropic"
44
+ Provides-Extra: openai
45
+ Requires-Dist: openai>=2.0; extra == "openai"
46
+ Provides-Extra: gemini
47
+ Requires-Dist: google-genai>=1.0; extra == "gemini"
48
+ Provides-Extra: ollama
49
+ Requires-Dist: ollama>=0.4; extra == "ollama"
50
+ Provides-Extra: vectors
51
+ Requires-Dist: pgvector>=0.3; extra == "vectors"
52
+ Provides-Extra: mcp
53
+ Requires-Dist: fastmcp>=2.0; extra == "mcp"
54
+ Provides-Extra: providers
55
+ Requires-Dist: anthropic>=0.100; extra == "providers"
56
+ Requires-Dist: openai>=2.0; extra == "providers"
57
+ Requires-Dist: google-genai>=1.0; extra == "providers"
58
+ Requires-Dist: ollama>=0.4; extra == "providers"
59
+ Provides-Extra: full
60
+ Requires-Dist: anthropic>=0.100; extra == "full"
61
+ Requires-Dist: openai>=2.0; extra == "full"
62
+ Requires-Dist: google-genai>=1.0; extra == "full"
63
+ Requires-Dist: ollama>=0.4; extra == "full"
64
+ Requires-Dist: pgvector>=0.3; extra == "full"
65
+ Requires-Dist: fastmcp>=2.0; extra == "full"
66
+ Provides-Extra: dev
67
+ Requires-Dist: pytest>=8.0; extra == "dev"
68
+ Requires-Dist: respx>=0.21; extra == "dev"
69
+ Dynamic: license-file
70
+
71
+ # Tawn — the twin you own
72
+
73
+ > **taw (ת) + own** — Local-first personal digital twin. Your data, your machine, your intelligence.
74
+
75
+ Tawn is a self-hosted memory and context core that unifies your work, wealth, research, and academic life into one searchable brain — shared across every AI agent you use (Claude Code, Cursor, Gemini CLI, and others).
76
+
77
+ ```
78
+ pip install tawn
79
+ tawn init
80
+ tawn web start # opens the web dashboard at http://tawn:8787
81
+ ```
82
+
83
+ ## What it does
84
+
85
+ - **Memory federation** — indexes conversations from Claude, ChatGPT, Gemini, Obsidian, and local files into one semantic store
86
+ - **Domain modules** — Work, Wealth (read-only), Research, and Academic — each with its own compiled wiki and entity graph
87
+ - **Multi-provider model routing** — Anthropic, OpenAI, Gemini, DeepSeek, OpenRouter, Ollama; local by default, cloud when needed
88
+ - **MCP server** — any MCP-compatible agent gets `recall`, `note`, and `brief` verbs against your twin's memory
89
+ - **Web dashboard** — streaming chat, memory browser, audit log, settings — at `tawn:8787`
90
+ - **Capability grants** — deny-all filesystem access by default; every read and write is audited
91
+
92
+ ## Quick start
93
+
94
+ ### Requirements
95
+
96
+ - Python 3.12+
97
+ - PostgreSQL (for memory and federation tables)
98
+ - Node.js 18+ (only needed if installing from source)
99
+
100
+ ### Install
101
+
102
+ ```bash
103
+ # fast core install (add providers when needed)
104
+ pipx install tawn
105
+
106
+ # or install everything at once
107
+ pipx install "tawn[full]"
108
+
109
+ # individual provider extras
110
+ pip install "tawn[anthropic]" # Claude
111
+ pip install "tawn[openai]" # OpenAI / OpenRouter
112
+ pip install "tawn[gemini]" # Google Gemini
113
+ pip install "tawn[ollama]" # local models
114
+ pip install "tawn[vectors]" # pgvector for semantic search
115
+ pip install "tawn[mcp]" # MCP server
116
+ ```
117
+
118
+ ### Setup
119
+
120
+ ```bash
121
+ tawn init # creates ~/.tawn/
122
+ tawn db setup # initialises PostgreSQL
123
+ tawn web start # starts the web server at tawn:8787
124
+ ```
125
+
126
+ Configure model providers:
127
+
128
+ ```bash
129
+ tawn setup # interactive wizard — sets API keys, DB, local model
130
+ ```
131
+
132
+ Or set keys directly:
133
+
134
+ ```bash
135
+ # keys are stored in the OS keyring, never in files
136
+ tawn setup # interactive
137
+ ```
138
+
139
+ ### MCP integration (Claude Code)
140
+
141
+ Add to your `~/.claude/claude.json`:
142
+
143
+ ```json
144
+ {
145
+ "mcpServers": {
146
+ "tawn": {
147
+ "command": "tawn",
148
+ "args": ["mcp"]
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ Then from any Claude session: `tawn.recall("…")`, `tawn.note("…")`, `tawn.brief("work")`.
155
+
156
+ ## Domain modules
157
+
158
+ | Domain | What it tracks |
159
+ |--------|---------------|
160
+ | **work** | Tasks, projects, decisions — per employer |
161
+ | **wealth** | Portfolio, net worth, holdings — **read-only, never trades** |
162
+ | **research** | Papers, experiments, entity graph, morning brief |
163
+ | **academic** | Applications, deadlines, proposal drafts |
164
+
165
+ Extend with your own domain:
166
+
167
+ ```bash
168
+ tawn domain add health # scaffolds a new domain plugin
169
+ ```
170
+
171
+ ## CLI reference
172
+
173
+ ```
174
+ tawn # chat with your twin
175
+ tawn note "…" # append a note to today's raw file
176
+ tawn recall "query" # semantic search over compiled memory
177
+ tawn brief work # daily summary for a domain
178
+ tawn compile # compile raw/ into wiki + vectors
179
+ tawn federation sources # list federated AI tool sources
180
+ tawn federation merge # ingest pending federation records
181
+ tawn web start/stop/status # web server control
182
+ tawn update # check and install latest release
183
+ tawn config list # view all settings
184
+ ```
185
+
186
+ ## Architecture
187
+
188
+ ```
189
+ ~/.tawn/
190
+ ├── raw/ # immutable ingested sources
191
+ ├── wiki/ # compiled markdown knowledge
192
+ ├── federation/ # AI tool exports + adapters
193
+ ├── grants.yaml # capability grants (deny-all default)
194
+ └── audit.log # every write, every cross-domain action
195
+ ```
196
+
197
+ API server binds to `127.0.0.1` only. Wealth core is read-only. No withdrawal credentials ever stored.
198
+
199
+ ## License
200
+
201
+ MIT — see [LICENSE](LICENSE).
tawn-0.1.0/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # Tawn — the twin you own
2
+
3
+ > **taw (ת) + own** — Local-first personal digital twin. Your data, your machine, your intelligence.
4
+
5
+ Tawn is a self-hosted memory and context core that unifies your work, wealth, research, and academic life into one searchable brain — shared across every AI agent you use (Claude Code, Cursor, Gemini CLI, and others).
6
+
7
+ ```
8
+ pip install tawn
9
+ tawn init
10
+ tawn web start # opens the web dashboard at http://tawn:8787
11
+ ```
12
+
13
+ ## What it does
14
+
15
+ - **Memory federation** — indexes conversations from Claude, ChatGPT, Gemini, Obsidian, and local files into one semantic store
16
+ - **Domain modules** — Work, Wealth (read-only), Research, and Academic — each with its own compiled wiki and entity graph
17
+ - **Multi-provider model routing** — Anthropic, OpenAI, Gemini, DeepSeek, OpenRouter, Ollama; local by default, cloud when needed
18
+ - **MCP server** — any MCP-compatible agent gets `recall`, `note`, and `brief` verbs against your twin's memory
19
+ - **Web dashboard** — streaming chat, memory browser, audit log, settings — at `tawn:8787`
20
+ - **Capability grants** — deny-all filesystem access by default; every read and write is audited
21
+
22
+ ## Quick start
23
+
24
+ ### Requirements
25
+
26
+ - Python 3.12+
27
+ - PostgreSQL (for memory and federation tables)
28
+ - Node.js 18+ (only needed if installing from source)
29
+
30
+ ### Install
31
+
32
+ ```bash
33
+ # fast core install (add providers when needed)
34
+ pipx install tawn
35
+
36
+ # or install everything at once
37
+ pipx install "tawn[full]"
38
+
39
+ # individual provider extras
40
+ pip install "tawn[anthropic]" # Claude
41
+ pip install "tawn[openai]" # OpenAI / OpenRouter
42
+ pip install "tawn[gemini]" # Google Gemini
43
+ pip install "tawn[ollama]" # local models
44
+ pip install "tawn[vectors]" # pgvector for semantic search
45
+ pip install "tawn[mcp]" # MCP server
46
+ ```
47
+
48
+ ### Setup
49
+
50
+ ```bash
51
+ tawn init # creates ~/.tawn/
52
+ tawn db setup # initialises PostgreSQL
53
+ tawn web start # starts the web server at tawn:8787
54
+ ```
55
+
56
+ Configure model providers:
57
+
58
+ ```bash
59
+ tawn setup # interactive wizard — sets API keys, DB, local model
60
+ ```
61
+
62
+ Or set keys directly:
63
+
64
+ ```bash
65
+ # keys are stored in the OS keyring, never in files
66
+ tawn setup # interactive
67
+ ```
68
+
69
+ ### MCP integration (Claude Code)
70
+
71
+ Add to your `~/.claude/claude.json`:
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "tawn": {
77
+ "command": "tawn",
78
+ "args": ["mcp"]
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ Then from any Claude session: `tawn.recall("…")`, `tawn.note("…")`, `tawn.brief("work")`.
85
+
86
+ ## Domain modules
87
+
88
+ | Domain | What it tracks |
89
+ |--------|---------------|
90
+ | **work** | Tasks, projects, decisions — per employer |
91
+ | **wealth** | Portfolio, net worth, holdings — **read-only, never trades** |
92
+ | **research** | Papers, experiments, entity graph, morning brief |
93
+ | **academic** | Applications, deadlines, proposal drafts |
94
+
95
+ Extend with your own domain:
96
+
97
+ ```bash
98
+ tawn domain add health # scaffolds a new domain plugin
99
+ ```
100
+
101
+ ## CLI reference
102
+
103
+ ```
104
+ tawn # chat with your twin
105
+ tawn note "…" # append a note to today's raw file
106
+ tawn recall "query" # semantic search over compiled memory
107
+ tawn brief work # daily summary for a domain
108
+ tawn compile # compile raw/ into wiki + vectors
109
+ tawn federation sources # list federated AI tool sources
110
+ tawn federation merge # ingest pending federation records
111
+ tawn web start/stop/status # web server control
112
+ tawn update # check and install latest release
113
+ tawn config list # view all settings
114
+ ```
115
+
116
+ ## Architecture
117
+
118
+ ```
119
+ ~/.tawn/
120
+ ├── raw/ # immutable ingested sources
121
+ ├── wiki/ # compiled markdown knowledge
122
+ ├── federation/ # AI tool exports + adapters
123
+ ├── grants.yaml # capability grants (deny-all default)
124
+ └── audit.log # every write, every cross-domain action
125
+ ```
126
+
127
+ API server binds to `127.0.0.1` only. Wealth core is read-only. No withdrawal credentials ever stored.
128
+
129
+ ## License
130
+
131
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,94 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tawn"
7
+ version = "0.1.0"
8
+ description = "Tawn — the personal digital twin. Local-first memory and context core for your work, wealth, research, and academic life."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [
12
+ { name = "Testimony Adekoya", email = "testimonyadekoya.02@gmail.com" },
13
+ ]
14
+ keywords = ["ai", "personal-ai", "digital-twin", "memory", "mcp", "rag", "local-first", "llm"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Environment :: Console",
18
+ "Environment :: Web Environment",
19
+ "Intended Audience :: Developers",
20
+ "Intended Audience :: End Users/Desktop",
21
+ "License :: OSI Approved :: MIT License",
22
+ "Operating System :: POSIX :: Linux",
23
+ "Operating System :: MacOS",
24
+ "Operating System :: Microsoft :: Windows",
25
+ "Programming Language :: Python :: 3",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Topic :: Office/Business",
29
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
30
+ ]
31
+ requires-python = ">=3.12"
32
+ dependencies = [
33
+ # core — always installed
34
+ "pydantic>=2.7",
35
+ "pydantic-settings>=2.2",
36
+ "PyYAML>=6.0",
37
+ "typer>=0.12",
38
+ "SQLAlchemy>=2.0",
39
+ "psycopg[binary]>=3.1",
40
+ "httpx>=0.27",
41
+ "rich>=13.7",
42
+ "fastapi>=0.111",
43
+ "uvicorn>=0.30",
44
+ "keyring>=25.0",
45
+ "python-frontmatter>=1.1",
46
+ "rapidfuzz>=3.0",
47
+ "watchfiles>=0.21",
48
+ ]
49
+
50
+ [project.optional-dependencies]
51
+ # individual provider extras
52
+ anthropic = ["anthropic>=0.100"]
53
+ openai = ["openai>=2.0"]
54
+ gemini = ["google-genai>=1.0"]
55
+ ollama = ["ollama>=0.4"]
56
+ vectors = ["pgvector>=0.3"]
57
+ mcp = ["fastmcp>=2.0"]
58
+ # convenience meta-extras
59
+ providers = ["anthropic>=0.100", "openai>=2.0", "google-genai>=1.0", "ollama>=0.4"]
60
+ full = [
61
+ "anthropic>=0.100", "openai>=2.0", "google-genai>=1.0",
62
+ "ollama>=0.4", "pgvector>=0.3", "fastmcp>=2.0",
63
+ ]
64
+ dev = ["pytest>=8.0", "respx>=0.21"]
65
+
66
+ [project.urls]
67
+ Homepage = "https://github.com/tawn-hq/tawn"
68
+ Repository = "https://github.com/tawn-hq/tawn"
69
+ "Bug Tracker" = "https://github.com/tawn-hq/tawn/issues"
70
+
71
+ [project.scripts]
72
+ tawn = "tawn.cli:app"
73
+
74
+ [project.entry-points."tawn.domains"]
75
+ wealth = "tawn.domains.wealth:register"
76
+ work = "tawn.domains.work:register"
77
+ research = "tawn.domains.research:register"
78
+ academic = "tawn.domains.academic:register"
79
+ hobby = "tawn.domains.hobby:register"
80
+
81
+ [tool.setuptools.packages.find]
82
+ where = ["src"]
83
+
84
+ [tool.setuptools.package-data]
85
+ tawn = [
86
+ "web/dist/**/*",
87
+ "web/dist/*",
88
+ ]
89
+
90
+ [tool.pytest.ini_options]
91
+ testpaths = ["tests"]
92
+
93
+ [tool.tawn.scripts]
94
+ build-web = "cd frontend && npm install && npm run build"
tawn-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
tawn-0.1.0/setup.py ADDED
@@ -0,0 +1,51 @@
1
+ import shutil
2
+ import subprocess
3
+ from pathlib import Path
4
+
5
+ from setuptools import setup
6
+ from setuptools.command.build_py import build_py
7
+
8
+ HERE = Path(__file__).parent
9
+ FRONTEND = HERE / "frontend"
10
+ DIST_SRC = FRONTEND / "dist"
11
+ # frontend dist lands inside the package so it's included in the wheel
12
+ DIST_DST = HERE / "src" / "tawn" / "web" / "dist"
13
+
14
+
15
+ class BuildFrontend(build_py):
16
+ def run(self):
17
+ # `python -m build`'s default flow builds the wheel from an
18
+ # isolated copy of the *sdist*, not the working tree — and the
19
+ # sdist never contains the top-level frontend/ dir (it's not part
20
+ # of the Python package, so nothing declares it for inclusion).
21
+ # `src/tawn/web/dist` DOES ship in the sdist (it's inside the
22
+ # package, matched by package-data), so if the workflow already
23
+ # populated it — build the frontend, then `cp -r dist/. ../src/
24
+ # tawn/web/dist/`, exactly like publish.yml — there's nothing left
25
+ # to do here and no reason to expect frontend/ to exist at all.
26
+ already_bundled = DIST_DST.is_dir() and any(DIST_DST.iterdir())
27
+ if not already_bundled:
28
+ if FRONTEND.is_dir():
29
+ if not DIST_SRC.is_dir():
30
+ if shutil.which("npm"):
31
+ subprocess.run(["npm", "ci"], cwd=str(FRONTEND), check=True)
32
+ subprocess.run(["npm", "run", "build"], cwd=str(FRONTEND), check=True)
33
+ else:
34
+ import warnings
35
+ warnings.warn("npm not found — web dashboard will not be bundled", stacklevel=2)
36
+ if DIST_SRC.is_dir():
37
+ if DIST_DST.exists():
38
+ shutil.rmtree(DIST_DST)
39
+ shutil.copytree(DIST_SRC, DIST_DST)
40
+ else:
41
+ import warnings
42
+ warnings.warn(
43
+ "frontend/ not present (isolated sdist build?) and no "
44
+ "pre-built web/dist — web dashboard will not be bundled",
45
+ stacklevel=2,
46
+ )
47
+
48
+ super().run()
49
+
50
+
51
+ setup(cmdclass={"build_py": BuildFrontend})
@@ -0,0 +1,3 @@
1
+ """Tawn — the personal digital twin. Capability-gated memory core."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,66 @@
1
+ """Internal entrypoint for the background web server process.
2
+
3
+ Run as: python -m tawn._webserver <port>
4
+ Not intended for direct user invocation — use `tawn web start/stop`.
5
+ """
6
+
7
+ import sys
8
+
9
+
10
+ def _start_auto_compiler(engine) -> None:
11
+ import threading
12
+ import time
13
+ from tawn.db import session as db_session
14
+ from tawn.compiler.compiler import run_compile as run_compiler
15
+ from tawn.federation.merge import scan_all_sources, merge_pending
16
+ from tawn.home import tawn_home
17
+
18
+ def _bootstrap_then_loop():
19
+ # Bootstrap: ingest + merge all existing federation files on startup.
20
+ # Runs in this background thread, not before uvicorn.run() — a
21
+ # source directory can hold thousands of files (a size cap on
22
+ # individual files exists, but the walk itself over many entries
23
+ # still takes real time), and the web server must bind its port
24
+ # immediately regardless of how long that one-time scan takes.
25
+ try:
26
+ with db_session(engine) as s:
27
+ n = scan_all_sources(tawn_home(), s)
28
+ if n > 0:
29
+ merge_pending(tawn_home(), s)
30
+ except Exception:
31
+ pass
32
+
33
+ while True:
34
+ try:
35
+ with db_session(engine) as session:
36
+ run_compiler(tawn_home(), session)
37
+ except Exception:
38
+ pass
39
+ time.sleep(30 * 60) # 30-minute interval
40
+
41
+ t = threading.Thread(target=_bootstrap_then_loop, name="tawn-auto-compiler", daemon=True)
42
+ t.start()
43
+
44
+
45
+ def main(port: int = 8787) -> None:
46
+ import uvicorn
47
+ from tawn.db import make_engine
48
+ from tawn.web import create_app
49
+
50
+ from tawn.db import init_db
51
+ from tawn.updater import start_daily_updater
52
+ engine = make_engine(pooled=True)
53
+ init_db(engine)
54
+ start_daily_updater()
55
+ _start_auto_compiler(engine)
56
+ uvicorn.run(
57
+ create_app(engine),
58
+ host="127.0.0.1",
59
+ port=port,
60
+ log_level="warning",
61
+ )
62
+
63
+
64
+ if __name__ == "__main__":
65
+ port = int(sys.argv[1]) if len(sys.argv) > 1 else 8787
66
+ main(port)
@@ -0,0 +1,82 @@
1
+ """Branded terminal identity (brand/README.md: Cairn × Sandstone & Lapis).
2
+
3
+ The cairn: three stones, lapis capstone. The wordmark lettering follows
4
+ the drawn monoline mark — rounded, lowercase, final n in lapis.
5
+ Colors are the brand tokens; Rich truecolor degrades gracefully.
6
+ """
7
+
8
+ from rich.columns import Columns
9
+ from rich.console import Group
10
+ from rich.padding import Padding
11
+ from rich.table import Table
12
+ from rich.text import Text
13
+
14
+ # brand tokens (brand/tokens.css) — dark-ground values; terminals are
15
+ # usually dark, and both lapis values pass on light too.
16
+ LAPIS = "#4A7BC8"
17
+ BONE = "#EFE9DC"
18
+ MUTED = "#ADA28C"
19
+
20
+ _CAIRN = [
21
+ (" ▄██▄ ", LAPIS),
22
+ (" ▀██▀ ", LAPIS),
23
+ (" ▄████▄ ", BONE),
24
+ (" ▀████▀ ", BONE),
25
+ (" ▄██████▄ ", BONE),
26
+ (" ▀██████▀ ", BONE),
27
+ ]
28
+
29
+ # monoline block lettering — t a w in bone, n in lapis (the letter that
30
+ # makes it *own*), matching the wordmark rule in brand/README.md
31
+ _LETTERS = [
32
+ ("▄█▄ ▄▀▀▄ █ █ ", "█▀▀▄"),
33
+ (" █ █▄▄█ █ ▄ █ ", "█ █"),
34
+ (" ▀▄ █ █ ▀▄▀▄▀ ", "█ █"),
35
+ ]
36
+
37
+
38
+ def cairn() -> Text:
39
+ art = Text()
40
+ for i, (line, color) in enumerate(_CAIRN):
41
+ art.append(line, style=color)
42
+ if i < len(_CAIRN) - 1:
43
+ art.append("\n")
44
+ return art
45
+
46
+
47
+ def wordmark() -> Text:
48
+ art = Text()
49
+ for i, (taw, n) in enumerate(_LETTERS):
50
+ art.append(taw, style=f"bold {BONE}")
51
+ art.append(n, style=f"bold {LAPIS}")
52
+ if i < len(_LETTERS) - 1:
53
+ art.append("\n")
54
+ return art
55
+
56
+
57
+ def banner(version: str) -> Group:
58
+ lockup = Columns([cairn(), Padding(wordmark(), (1, 0, 0, 2))], padding=(0, 2))
59
+ tag = Text()
60
+ tag.append("the twin you own", style=f"italic {MUTED}")
61
+ tag.append(f" · v{version}", style=MUTED)
62
+ return Group(lockup, Padding(tag, (0, 0, 0, 1)))
63
+
64
+
65
+ def status_table(rows: list[tuple[str, bool, str]]) -> Table:
66
+ t = Table(show_header=False, box=None, padding=(0, 1))
67
+ t.add_column(style=MUTED)
68
+ t.add_column()
69
+ t.add_column(style=MUTED)
70
+ for name, ok, detail in rows:
71
+ mark = Text("●", style="green" if ok else "red")
72
+ t.add_row(name, mark, detail)
73
+ return t
74
+
75
+
76
+ def commands_table(commands: list[tuple[str, str]]) -> Table:
77
+ t = Table(show_header=False, box=None, padding=(0, 1))
78
+ t.add_column(style=f"bold {LAPIS}")
79
+ t.add_column(style=MUTED)
80
+ for cmd, desc in commands:
81
+ t.add_row(cmd, desc)
82
+ return t
@@ -0,0 +1 @@
1
+ """Capability layer: grants, integrity, audit, and the FS chokepoint."""