universal-memory 0.1.4__tar.gz → 0.2.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 (93) hide show
  1. {universal_memory-0.1.4 → universal_memory-0.2.0}/PKG-INFO +106 -23
  2. {universal_memory-0.1.4 → universal_memory-0.2.0}/README.md +99 -17
  3. {universal_memory-0.1.4 → universal_memory-0.2.0}/pyproject.toml +8 -7
  4. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/__init__.py +1 -1
  5. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/onboarding/setup_project.py +1 -0
  6. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/bootstrap/mcp.py +127 -51
  7. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/interfaces/cli/init_command.py +9 -38
  8. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/__main__.py +0 -0
  9. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/__init__.py +0 -0
  10. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/diagnostics/__init__.py +0 -0
  11. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/diagnostics/doctor_use_case.py +0 -0
  12. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/host/__init__.py +0 -0
  13. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/host/drift_detector.py +0 -0
  14. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/host/setup_host_use_case.py +0 -0
  15. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/host/sync_instructions_use_case.py +0 -0
  16. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/memory/__init__.py +0 -0
  17. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/memory/assemble_context_summary_use_case.py +0 -0
  18. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/memory/context_hygiene_use_case.py +0 -0
  19. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/memory/get_memory_status_use_case.py +0 -0
  20. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/memory/list_facts_use_case.py +0 -0
  21. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/memory/purge_fact_use_case.py +0 -0
  22. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/memory/remember_fact_use_case.py +0 -0
  23. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/memory/search_facts_use_case.py +0 -0
  24. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/onboarding/__init__.py +0 -0
  25. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/security/__init__.py +0 -0
  26. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/security/list_audit_log_use_case.py +0 -0
  27. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/security/list_snapshots_use_case.py +0 -0
  28. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/security/rollback_use_case.py +0 -0
  29. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/security/safe_write_use_case.py +0 -0
  30. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/__init__.py +0 -0
  31. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/create_skill.py +0 -0
  32. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/generate_skill.py +0 -0
  33. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/import_skill.py +0 -0
  34. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/list_skills.py +0 -0
  35. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/native_skill_sync.py +0 -0
  36. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/promote_skill.py +0 -0
  37. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/propose_skill.py +0 -0
  38. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/recommend_skills.py +0 -0
  39. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/sync_skills.py +0 -0
  40. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/track_latent_skill.py +0 -0
  41. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/skills/update_skill.py +0 -0
  42. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/update/__init__.py +0 -0
  43. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/application/update/update_use_cases.py +0 -0
  44. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/bootstrap/__init__.py +0 -0
  45. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/bootstrap/cli.py +0 -0
  46. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/__init__.py +0 -0
  47. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/__init__.py +0 -0
  48. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/agent_skill.py +0 -0
  49. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/audit_event.py +0 -0
  50. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/base.py +0 -0
  51. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/context_summary.py +0 -0
  52. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/fact.py +0 -0
  53. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/host.py +0 -0
  54. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/instruction_target.py +0 -0
  55. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/latent_skill.py +0 -0
  56. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/rule.py +0 -0
  57. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/runtime.py +0 -0
  58. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/safe_write_result.py +0 -0
  59. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/entities/snapshot.py +0 -0
  60. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/exceptions.py +0 -0
  61. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/__init__.py +0 -0
  62. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/agent_skill_repository.py +0 -0
  63. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/audit_log_repository.py +0 -0
  64. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/config_validation_port.py +0 -0
  65. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/context_summary_repository.py +0 -0
  66. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/fact_repository.py +0 -0
  67. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/latent_skill_repository.py +0 -0
  68. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/project_layout_port.py +0 -0
  69. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/rule_repository.py +0 -0
  70. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/secret_scanner_port.py +0 -0
  71. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/ports/snapshot_repository.py +0 -0
  72. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/domain/project_layout.py +0 -0
  73. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/__init__.py +0 -0
  74. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/config/__init__.py +0 -0
  75. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/config/adapters.py +0 -0
  76. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/config/project_layout.py +0 -0
  77. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/config/toml_loader.py +0 -0
  78. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/security/__init__.py +0 -0
  79. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/security/entropy_secret_scanner.py +0 -0
  80. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/security/local_audit_log_repository.py +0 -0
  81. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/security/local_snapshot_repository.py +0 -0
  82. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/storage/__init__.py +0 -0
  83. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/storage/local_agent_skill_repository.py +0 -0
  84. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/storage/local_context_summary_repository.py +0 -0
  85. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/storage/local_fact_repository.py +0 -0
  86. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/storage/local_latent_skill_repository.py +0 -0
  87. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/infrastructure/storage/local_rule_repository.py +0 -0
  88. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/interfaces/__init__.py +0 -0
  89. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/interfaces/cli/__init__.py +0 -0
  90. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/interfaces/cli/message_catalog.py +0 -0
  91. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/interfaces/errors.py +0 -0
  92. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/interfaces/mcp/__init__.py +0 -0
  93. {universal_memory-0.1.4 → universal_memory-0.2.0}/src/universal_memory/interfaces/mcp/server.py +0 -0
@@ -1,24 +1,25 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: universal-memory
3
- Version: 0.1.4
3
+ Version: 0.2.0
4
4
  Summary: Vendor-agnostic cognitive persistence layer for AI agents.
5
5
  Keywords: agent-memory,ai,ai-agents,agent-skills,claude-code,codex,context-engineering,developer-tools,llm,mcp,memory
6
6
  Author: Yan L. Amorelli
7
7
  Author-email: Yan L. Amorelli <dev@amorelliaoyan.com>
8
- License: MIT
8
+ License: Apache-2.0
9
9
  Classifier: Development Status :: 3 - Alpha
10
10
  Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: Apache Software License
11
12
  Classifier: Programming Language :: Python :: 3
12
13
  Classifier: Programming Language :: Python :: 3 :: Only
13
14
  Classifier: Programming Language :: Python :: 3.12
14
15
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
16
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
17
  Classifier: Topic :: Utilities
17
- Requires-Dist: fastmcp>=0.1.0
18
- Requires-Dist: pydantic>=2.0
18
+ Requires-Dist: fastmcp>=3.3.1,<4
19
+ Requires-Dist: pydantic>=2.13.4,<3
19
20
  Requires-Dist: rich>=15.0.0
20
- Requires-Dist: tomli-w>=1.0.0
21
- Requires-Dist: typer>=0.9.0
21
+ Requires-Dist: tomli-w>=1.2.0
22
+ Requires-Dist: typer>=0.25.1
22
23
  Requires-Python: >=3.12
23
24
  Project-URL: Homepage, https://universal-memory.com
24
25
  Project-URL: Documentation, https://docs.universal-memory.com
@@ -26,20 +27,22 @@ Project-URL: Repository, https://github.com/YanAmorelli/universal-memory
26
27
  Description-Content-Type: text/markdown
27
28
 
28
29
  <p align="center">
29
- <img src="https://docs.universal-memory.com/assets/umem-logo-transparent.png" alt="Universal Memory logo" width="720">
30
+ <img src="docs/assets/umem-logo-lockup.svg" alt="UMem logo" width="720">
30
31
  </p>
31
32
 
32
- # Universal Memory (umem)
33
+ # Universal Memory (UMem)
33
34
 
34
35
  [![PyPI version](https://img.shields.io/pypi/v/universal-memory.svg)](https://pypi.org/project/universal-memory/)
35
36
  [![Python Version](https://img.shields.io/pypi/pyversions/universal-memory.svg)](https://pypi.org/project/universal-memory/)
36
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/YanAmorelli/universal-memory/blob/dev/LICENSE)
37
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/YanAmorelli/universal-memory/blob/dev/LICENSE)
38
+
39
+ **[Website](https://universal-memory.com)** | **[Documentation](https://docs.universal-memory.com)**
37
40
 
38
41
  A vendor-agnostic cognitive persistence layer for AI agents. Eliminate the "repetition tax" by transporting your context, preferences, guidelines, and history seamlessly across sessions, IDEs, and LLM models.
39
42
 
40
43
  To see the core idea visually, check out the [Excalidraw design](https://excalidraw.com/#json=j3XjQIWMYEnkIzHpypuBb,rNJaVOECDGZ3WSuEYcCDjQ) or the proposal structure:
41
44
 
42
- ![Universal Memory MVP Proposal](https://docs.universal-memory.com/UNIVERSAL-MEMORY-MVP-PROPOSAL.png)
45
+ ![Universal Memory MVP Proposal](docs/assets/diagrams/UNIVERSAL-MEMORY-MVP-PROPOSAL.png)
43
46
 
44
47
  ### Diagram Breakdown
45
48
 
@@ -61,7 +64,7 @@ Every time you open a new session in Claude Code, start a new chat in Cursor, sp
61
64
  * Copy-pasting database connection schemas or module layouts.
62
65
  * Explaining workflow methodologies (e.g., "We follow Spec-Driven Development (SDD)").
63
66
 
64
- Universal Memory acts as a local "Cognitive USB Drive" that automatically connects to your AI runtimes, aligning them to your exact workflow, context, and rules with zero friction.
67
+ Universal Memory acts as a local persistence layer that automatically connects to your AI runtimes, aligning them to your exact workflow, context, and rules with zero friction.
65
68
 
66
69
  ---
67
70
 
@@ -78,7 +81,15 @@ Instead of copy-pasting instructions, `umem` monitors your session context and a
78
81
  Integrate `umem` natively with any client supporting the standard MCP (such as Claude Desktop or Cursor). AI agents can programmatically retrieve context, learn new facts, and suggest skills on the fly.
79
82
 
80
83
  ### 4. Agent Skills Standard
81
- Encapsulates complex, repetitive procedural instructions into formal Agent Skills (conforming to the [agentskills.io](https://agentskills.io) standard), complete with structured directories containing `SKILL.md` instructions, helper `scripts/`, and documentation `references/`.
84
+ Encapsulates complex, repetitive procedural instructions into formal Agent Skills
85
+ (conforming to the [agentskills.io](https://agentskills.io) standard), complete with
86
+ structured directories containing `SKILL.md` instructions, helper `scripts/`, and
87
+ documentation `references/`.
88
+
89
+ Universal Memory treats `.umem/skills/<slug>/SKILL.md` as the canonical source of
90
+ truth. Native runtime folders such as `.agents/skills/`, `.opencode/skills/`, and
91
+ `.antigravity/rules/` receive complete synchronized copies so each agent can consume
92
+ the same skill in its own expected layout.
82
93
 
83
94
  ---
84
95
 
@@ -158,7 +169,32 @@ Verify the consolidated context summary generated by combining short-term facts,
158
169
  umem context --scope project
159
170
  ```
160
171
 
161
- ### 4. Check status and health
172
+ ### 4. Adopt or create an Agent Skill
173
+
174
+ If a skill already exists in a native runtime directory, import it into UMEM and sync it
175
+ back out to configured runtimes:
176
+
177
+ ```bash
178
+ umem skills import .agents/skills/review-protocol --scope project --sync
179
+ umem skills detail review-protocol
180
+ ```
181
+
182
+ If you are starting from scratch, create the canonical skill first:
183
+
184
+ ```bash
185
+ umem skills create \
186
+ --name "Review Protocol" \
187
+ --description "Reusable review workflow" \
188
+ --trigger "when reviewing code"
189
+ ```
190
+
191
+ After editing `.umem/skills/review-protocol/SKILL.md`, refresh one runtime skill with:
192
+
193
+ ```bash
194
+ umem skills sync review-protocol
195
+ ```
196
+
197
+ ### 5. Check status and health
162
198
  ```bash
163
199
  umem status
164
200
  ```
@@ -183,20 +219,26 @@ umem status
183
219
 
184
220
  AI agents can interact directly with your memory over the Model Context Protocol.
185
221
 
186
- ### CLI Launch Command
222
+ ### One-off Launch Command
223
+ ```bash
224
+ uvx --from universal-memory umem-mcp
225
+ ```
226
+
227
+ ### Persistent Install Launch Command
187
228
  ```bash
188
229
  umem-mcp
189
230
  ```
190
231
 
191
232
  ### Example Config: Claude Desktop (`claude_desktop_config.json`)
233
+ Use the `uvx` form when Universal Memory is not installed as a persistent tool:
234
+
192
235
  ```json
193
236
  {
194
237
  "mcpServers": {
195
238
  "universal-memory": {
196
- "command": "uv",
239
+ "command": "uvx",
197
240
  "args": [
198
- "run",
199
- "--package",
241
+ "--from",
200
242
  "universal-memory",
201
243
  "umem-mcp"
202
244
  ]
@@ -205,6 +247,29 @@ umem-mcp
205
247
  }
206
248
  ```
207
249
 
250
+ If you installed Universal Memory with `uv tool install universal-memory` or `pipx install universal-memory`, use the stable entrypoint:
251
+
252
+ ```json
253
+ {
254
+ "mcpServers": {
255
+ "universal-memory": {
256
+ "command": "umem-mcp",
257
+ "args": []
258
+ }
259
+ }
260
+ }
261
+ ```
262
+
263
+ Troubleshoot startup with:
264
+
265
+ ```bash
266
+ uvx --from universal-memory umem doctor
267
+ uvx --from universal-memory umem-mcp --help
268
+ ```
269
+
270
+ For GUI-launched MCP hosts, use the absolute path to `uvx` if the host does not inherit
271
+ your shell `PATH`.
272
+
208
273
  ---
209
274
 
210
275
  ## Safety & Guardrails
@@ -218,26 +283,44 @@ umem-mcp
218
283
  # Revert last automated modification
219
284
  umem rollback --scope project
220
285
  ```
221
- * **Update Conflict Warnings:** When updating canonical skills, if `umem` detects manual edits in local runtime rule directories (e.g. `.cursor/rules/sdd-rules.md`), it prompts you interactivelly to choose whether to keep your local edits or overwrite them, preventing workflow disruption.
286
+ * **Skill Drift Protection:** `umem skills sync` detects managed native drift and
287
+ keeps local changes by default. Use `--drift-decision overwrite` only when you
288
+ intentionally want canonical UMEM content to replace the managed native copy.
222
289
 
223
290
  ---
224
291
 
225
292
  ## Managing Agent Skills
226
293
 
227
- You can create, list, and sync specialized behaviors:
294
+ You can create, import, list, inspect, and sync specialized behaviors:
228
295
  ```bash
229
296
  # List all active skills
230
297
  umem skills list
231
298
 
232
- # Synchronize skills into active native runtime folders
299
+ # Inspect one skill
300
+ umem skills detail review-protocol
301
+
302
+ # Create a new canonical skill
303
+ umem skills create --name "Review Protocol" --description "Reusable review workflow"
304
+
305
+ # Adopt an existing native skill and distribute complete runtime copies
306
+ umem skills import .agents/skills/review-protocol --scope project --sync
307
+
308
+ # Synchronize one canonical skill into active native runtime folders
309
+ umem skills sync review-protocol
310
+
311
+ # Synchronize all active canonical skills during maintenance
233
312
  umem update --skills
234
313
 
235
- # Generate a new skill template from a latent skill proposal
236
- umem skills generate --name build-standard
314
+ # Track and review recurring workflow candidates
315
+ umem skills track --name "Review Protocol" --description "Recurring review workflow"
316
+ umem skills recommend --scope project
317
+ umem skills propose <latent-skill-id> --decision yes
318
+ umem skills promote <recommendation-id> --yes
319
+ umem skills generate <latent-skill-id> --yes
237
320
  ```
238
321
 
239
322
  ---
240
323
 
241
324
  ## License
242
325
 
243
- Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
326
+ Distributed under the Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE) for more information.
@@ -1,18 +1,20 @@
1
1
  <p align="center">
2
- <img src="https://docs.universal-memory.com/assets/umem-logo-transparent.png" alt="Universal Memory logo" width="720">
2
+ <img src="docs/assets/umem-logo-lockup.svg" alt="UMem logo" width="720">
3
3
  </p>
4
4
 
5
- # Universal Memory (umem)
5
+ # Universal Memory (UMem)
6
6
 
7
7
  [![PyPI version](https://img.shields.io/pypi/v/universal-memory.svg)](https://pypi.org/project/universal-memory/)
8
8
  [![Python Version](https://img.shields.io/pypi/pyversions/universal-memory.svg)](https://pypi.org/project/universal-memory/)
9
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/YanAmorelli/universal-memory/blob/dev/LICENSE)
9
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/YanAmorelli/universal-memory/blob/dev/LICENSE)
10
+
11
+ **[Website](https://universal-memory.com)** | **[Documentation](https://docs.universal-memory.com)**
10
12
 
11
13
  A vendor-agnostic cognitive persistence layer for AI agents. Eliminate the "repetition tax" by transporting your context, preferences, guidelines, and history seamlessly across sessions, IDEs, and LLM models.
12
14
 
13
15
  To see the core idea visually, check out the [Excalidraw design](https://excalidraw.com/#json=j3XjQIWMYEnkIzHpypuBb,rNJaVOECDGZ3WSuEYcCDjQ) or the proposal structure:
14
16
 
15
- ![Universal Memory MVP Proposal](https://docs.universal-memory.com/UNIVERSAL-MEMORY-MVP-PROPOSAL.png)
17
+ ![Universal Memory MVP Proposal](docs/assets/diagrams/UNIVERSAL-MEMORY-MVP-PROPOSAL.png)
16
18
 
17
19
  ### Diagram Breakdown
18
20
 
@@ -34,7 +36,7 @@ Every time you open a new session in Claude Code, start a new chat in Cursor, sp
34
36
  * Copy-pasting database connection schemas or module layouts.
35
37
  * Explaining workflow methodologies (e.g., "We follow Spec-Driven Development (SDD)").
36
38
 
37
- Universal Memory acts as a local "Cognitive USB Drive" that automatically connects to your AI runtimes, aligning them to your exact workflow, context, and rules with zero friction.
39
+ Universal Memory acts as a local persistence layer that automatically connects to your AI runtimes, aligning them to your exact workflow, context, and rules with zero friction.
38
40
 
39
41
  ---
40
42
 
@@ -51,7 +53,15 @@ Instead of copy-pasting instructions, `umem` monitors your session context and a
51
53
  Integrate `umem` natively with any client supporting the standard MCP (such as Claude Desktop or Cursor). AI agents can programmatically retrieve context, learn new facts, and suggest skills on the fly.
52
54
 
53
55
  ### 4. Agent Skills Standard
54
- Encapsulates complex, repetitive procedural instructions into formal Agent Skills (conforming to the [agentskills.io](https://agentskills.io) standard), complete with structured directories containing `SKILL.md` instructions, helper `scripts/`, and documentation `references/`.
56
+ Encapsulates complex, repetitive procedural instructions into formal Agent Skills
57
+ (conforming to the [agentskills.io](https://agentskills.io) standard), complete with
58
+ structured directories containing `SKILL.md` instructions, helper `scripts/`, and
59
+ documentation `references/`.
60
+
61
+ Universal Memory treats `.umem/skills/<slug>/SKILL.md` as the canonical source of
62
+ truth. Native runtime folders such as `.agents/skills/`, `.opencode/skills/`, and
63
+ `.antigravity/rules/` receive complete synchronized copies so each agent can consume
64
+ the same skill in its own expected layout.
55
65
 
56
66
  ---
57
67
 
@@ -131,7 +141,32 @@ Verify the consolidated context summary generated by combining short-term facts,
131
141
  umem context --scope project
132
142
  ```
133
143
 
134
- ### 4. Check status and health
144
+ ### 4. Adopt or create an Agent Skill
145
+
146
+ If a skill already exists in a native runtime directory, import it into UMEM and sync it
147
+ back out to configured runtimes:
148
+
149
+ ```bash
150
+ umem skills import .agents/skills/review-protocol --scope project --sync
151
+ umem skills detail review-protocol
152
+ ```
153
+
154
+ If you are starting from scratch, create the canonical skill first:
155
+
156
+ ```bash
157
+ umem skills create \
158
+ --name "Review Protocol" \
159
+ --description "Reusable review workflow" \
160
+ --trigger "when reviewing code"
161
+ ```
162
+
163
+ After editing `.umem/skills/review-protocol/SKILL.md`, refresh one runtime skill with:
164
+
165
+ ```bash
166
+ umem skills sync review-protocol
167
+ ```
168
+
169
+ ### 5. Check status and health
135
170
  ```bash
136
171
  umem status
137
172
  ```
@@ -156,20 +191,26 @@ umem status
156
191
 
157
192
  AI agents can interact directly with your memory over the Model Context Protocol.
158
193
 
159
- ### CLI Launch Command
194
+ ### One-off Launch Command
195
+ ```bash
196
+ uvx --from universal-memory umem-mcp
197
+ ```
198
+
199
+ ### Persistent Install Launch Command
160
200
  ```bash
161
201
  umem-mcp
162
202
  ```
163
203
 
164
204
  ### Example Config: Claude Desktop (`claude_desktop_config.json`)
205
+ Use the `uvx` form when Universal Memory is not installed as a persistent tool:
206
+
165
207
  ```json
166
208
  {
167
209
  "mcpServers": {
168
210
  "universal-memory": {
169
- "command": "uv",
211
+ "command": "uvx",
170
212
  "args": [
171
- "run",
172
- "--package",
213
+ "--from",
173
214
  "universal-memory",
174
215
  "umem-mcp"
175
216
  ]
@@ -178,6 +219,29 @@ umem-mcp
178
219
  }
179
220
  ```
180
221
 
222
+ If you installed Universal Memory with `uv tool install universal-memory` or `pipx install universal-memory`, use the stable entrypoint:
223
+
224
+ ```json
225
+ {
226
+ "mcpServers": {
227
+ "universal-memory": {
228
+ "command": "umem-mcp",
229
+ "args": []
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ Troubleshoot startup with:
236
+
237
+ ```bash
238
+ uvx --from universal-memory umem doctor
239
+ uvx --from universal-memory umem-mcp --help
240
+ ```
241
+
242
+ For GUI-launched MCP hosts, use the absolute path to `uvx` if the host does not inherit
243
+ your shell `PATH`.
244
+
181
245
  ---
182
246
 
183
247
  ## Safety & Guardrails
@@ -191,26 +255,44 @@ umem-mcp
191
255
  # Revert last automated modification
192
256
  umem rollback --scope project
193
257
  ```
194
- * **Update Conflict Warnings:** When updating canonical skills, if `umem` detects manual edits in local runtime rule directories (e.g. `.cursor/rules/sdd-rules.md`), it prompts you interactivelly to choose whether to keep your local edits or overwrite them, preventing workflow disruption.
258
+ * **Skill Drift Protection:** `umem skills sync` detects managed native drift and
259
+ keeps local changes by default. Use `--drift-decision overwrite` only when you
260
+ intentionally want canonical UMEM content to replace the managed native copy.
195
261
 
196
262
  ---
197
263
 
198
264
  ## Managing Agent Skills
199
265
 
200
- You can create, list, and sync specialized behaviors:
266
+ You can create, import, list, inspect, and sync specialized behaviors:
201
267
  ```bash
202
268
  # List all active skills
203
269
  umem skills list
204
270
 
205
- # Synchronize skills into active native runtime folders
271
+ # Inspect one skill
272
+ umem skills detail review-protocol
273
+
274
+ # Create a new canonical skill
275
+ umem skills create --name "Review Protocol" --description "Reusable review workflow"
276
+
277
+ # Adopt an existing native skill and distribute complete runtime copies
278
+ umem skills import .agents/skills/review-protocol --scope project --sync
279
+
280
+ # Synchronize one canonical skill into active native runtime folders
281
+ umem skills sync review-protocol
282
+
283
+ # Synchronize all active canonical skills during maintenance
206
284
  umem update --skills
207
285
 
208
- # Generate a new skill template from a latent skill proposal
209
- umem skills generate --name build-standard
286
+ # Track and review recurring workflow candidates
287
+ umem skills track --name "Review Protocol" --description "Recurring review workflow"
288
+ umem skills recommend --scope project
289
+ umem skills propose <latent-skill-id> --decision yes
290
+ umem skills promote <recommendation-id> --yes
291
+ umem skills generate <latent-skill-id> --yes
210
292
  ```
211
293
 
212
294
  ---
213
295
 
214
296
  ## License
215
297
 
216
- Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
298
+ Distributed under the Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE) for more information.
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "universal-memory"
3
- version = "0.1.4"
3
+ version = "0.2.0"
4
4
  description = "Vendor-agnostic cognitive persistence layer for AI agents."
5
5
  readme = "README.md"
6
- license = { text = "MIT" }
6
+ license = { text = "Apache-2.0" }
7
7
  authors = [
8
8
  { name = "Yan L. Amorelli", email = "dev@amorelliaoyan.com" }
9
9
  ]
@@ -24,6 +24,7 @@ keywords = [
24
24
  classifiers = [
25
25
  "Development Status :: 3 - Alpha",
26
26
  "Intended Audience :: Developers",
27
+ "License :: OSI Approved :: Apache Software License",
27
28
  "Programming Language :: Python :: 3",
28
29
  "Programming Language :: Python :: 3 :: Only",
29
30
  "Programming Language :: Python :: 3.12",
@@ -32,11 +33,11 @@ classifiers = [
32
33
  "Topic :: Utilities",
33
34
  ]
34
35
  dependencies = [
35
- "fastmcp>=0.1.0",
36
- "pydantic>=2.0",
36
+ "fastmcp>=3.3.1,<4",
37
+ "pydantic>=2.13.4,<3",
37
38
  "rich>=15.0.0",
38
- "tomli-w>=1.0.0",
39
- "typer>=0.9.0",
39
+ "tomli-w>=1.2.0",
40
+ "typer>=0.25.1",
40
41
  ]
41
42
 
42
43
  [project.urls]
@@ -57,7 +58,7 @@ dev = [
57
58
  ]
58
59
  docs = [
59
60
  "mkdocs-material>=9.5.0",
60
- "mkdocs>=1.6.0",
61
+ "mkdocs>=1.6.0,<2.0",
61
62
  ]
62
63
 
63
64
  [build-system]
@@ -5,4 +5,4 @@ from importlib.metadata import PackageNotFoundError, version
5
5
  try:
6
6
  __version__ = version("universal-memory")
7
7
  except PackageNotFoundError:
8
- __version__ = "0.1.4"
8
+ __version__ = "0.2.0"
@@ -621,6 +621,7 @@ CLI commands with `--format json` and MCP tools should return equivalent payload
621
621
  | Track skill | `umem skills track ... --format json` | `track_latent_skill(...)` |
622
622
  | Recommend skills | `umem skills recommend --scope project --format json` | `recommend_skills(scope="project", dry_run=true)` |
623
623
  | Propose skill | `umem skills propose <latent-skill-id> --decision yes --format json` | `propose_skill(latent_skill_id="<latent-skill-id>", decision="yes")` |
624
+ | Promote skill recommendation | `umem skills promote <recommendation-id> --yes --format json` | `promote_skill_recommendation(recommendation_id="<recommendation-id>", confirm=true)` |
624
625
  | Generate skill | `umem skills generate <latent-skill-id> --yes --format json` | `generate_skill(latent_skill_id="<latent-skill-id>")` |
625
626
  | Activate skill | `umem skills activate <latent-skill-id> --format json` | `activate_skill(latent_skill_id="<latent-skill-id>")` |
626
627
  | Deactivate skill | `umem skills deactivate <latent-skill-id> --format json` | `deactivate_skill(latent_skill_id="<latent-skill-id>")` |
@@ -1,60 +1,81 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import re
4
+ import sys
5
+ from collections.abc import Sequence
3
6
  from pathlib import Path
7
+ from typing import TYPE_CHECKING
4
8
 
5
- from fastmcp import FastMCP
9
+ if TYPE_CHECKING:
10
+ from fastmcp import FastMCP
6
11
 
7
- from universal_memory.application.diagnostics import DoctorUseCase
8
- from universal_memory.application.host import (
9
- ConfigureHostUseCase,
10
- SyncInstructionsUseCase,
12
+ _SECRET_PATTERNS = (
13
+ re.compile(r"\b(?:sk|pk)-[A-Za-z0-9_-]{6,}\b"),
14
+ re.compile(r"\b[A-Za-z0-9_]*api[_-]?key[A-Za-z0-9_]*\s*[:=]\s*[^\s,;]+", re.IGNORECASE),
15
+ re.compile(r"\b[A-Za-z0-9_]*token[A-Za-z0-9_]*\s*[:=]\s*[^\s,;]+", re.IGNORECASE),
11
16
  )
12
- from universal_memory.application.memory import (
13
- AssembleContextSummaryUseCase,
14
- GetMemoryStatusUseCase,
15
- ListFactsUseCase,
16
- PurgeFactUseCase,
17
- RememberFactUseCase,
18
- )
19
- from universal_memory.application.onboarding.setup_project import setup_project
20
- from universal_memory.application.security import (
21
- ListAuditLogUseCase,
22
- ListSnapshotsUseCase,
23
- RollbackUseCase,
24
- SafeWriteUseCase,
25
- )
26
- from universal_memory.application.skills import (
27
- ActivateSkillUseCase,
28
- CreateSkillUseCase,
29
- DeactivateSkillUseCase,
30
- GenerateSkillUseCase,
31
- GetSkillDetailUseCase,
32
- ImportSkillUseCase,
33
- ListSkillsUseCase,
34
- PromoteSkillRecommendationUseCase,
35
- ProposeSkillUseCase,
36
- RecommendSkillsUseCase,
37
- SyncSkillsUseCase,
38
- TrackLatentSkillUseCase,
39
- UpdateSkillUseCase,
40
- )
41
- from universal_memory.infrastructure.config import LocalConfigValidationPort, LocalProjectLayoutPort
42
- from universal_memory.infrastructure.security import (
43
- EntropySecretScanner,
44
- LocalAuditLogRepository,
45
- LocalSnapshotRepository,
46
- )
47
- from universal_memory.infrastructure.storage import (
48
- LocalAgentSkillRepository,
49
- LocalContextSummaryRepository,
50
- LocalFactRepository,
51
- LocalLatentSkillRepository,
52
- LocalRuleRepository,
53
- )
54
- from universal_memory.interfaces.mcp import MCPUseCases, configure_server, create_mcp_server
17
+ _UNIX_ABSOLUTE_PATH = re.compile(r"(?<![\w.-])/(?:[^/\s:]+/)+[^/\s:]+")
18
+ _WINDOWS_ABSOLUTE_PATH = re.compile(r"(?<![\w.-])(?:[a-zA-Z]:\\|\\\\)(?:[^\\\s:]+\\)+[^\\\s:]+")
19
+ _DEFAULT_RECOVERY_HINT = "Try again. If the problem persists, check the diagnostic logs."
55
20
 
56
21
 
57
- def build_server(project_root: Path | None = None) -> FastMCP:
22
+ def build_server(project_root: Path | None = None) -> FastMCP: # noqa: PLR0915
23
+ from universal_memory.application.diagnostics import DoctorUseCase # noqa: PLC0415
24
+ from universal_memory.application.host import ( # noqa: PLC0415
25
+ ConfigureHostUseCase,
26
+ SyncInstructionsUseCase,
27
+ )
28
+ from universal_memory.application.memory import ( # noqa: PLC0415
29
+ AssembleContextSummaryUseCase,
30
+ GetMemoryStatusUseCase,
31
+ ListFactsUseCase,
32
+ PurgeFactUseCase,
33
+ RememberFactUseCase,
34
+ )
35
+ from universal_memory.application.onboarding.setup_project import setup_project # noqa: PLC0415
36
+ from universal_memory.application.security import ( # noqa: PLC0415
37
+ ListAuditLogUseCase,
38
+ ListSnapshotsUseCase,
39
+ RollbackUseCase,
40
+ SafeWriteUseCase,
41
+ )
42
+ from universal_memory.application.skills import ( # noqa: PLC0415
43
+ ActivateSkillUseCase,
44
+ CreateSkillUseCase,
45
+ DeactivateSkillUseCase,
46
+ GenerateSkillUseCase,
47
+ GetSkillDetailUseCase,
48
+ ImportSkillUseCase,
49
+ ListSkillsUseCase,
50
+ PromoteSkillRecommendationUseCase,
51
+ ProposeSkillUseCase,
52
+ RecommendSkillsUseCase,
53
+ SyncSkillsUseCase,
54
+ TrackLatentSkillUseCase,
55
+ UpdateSkillUseCase,
56
+ )
57
+ from universal_memory.infrastructure.config import ( # noqa: PLC0415
58
+ LocalConfigValidationPort,
59
+ LocalProjectLayoutPort,
60
+ )
61
+ from universal_memory.infrastructure.security import ( # noqa: PLC0415
62
+ EntropySecretScanner,
63
+ LocalAuditLogRepository,
64
+ LocalSnapshotRepository,
65
+ )
66
+ from universal_memory.infrastructure.storage import ( # noqa: PLC0415
67
+ LocalAgentSkillRepository,
68
+ LocalContextSummaryRepository,
69
+ LocalFactRepository,
70
+ LocalLatentSkillRepository,
71
+ LocalRuleRepository,
72
+ )
73
+ from universal_memory.interfaces.mcp import ( # noqa: PLC0415
74
+ MCPUseCases,
75
+ configure_server,
76
+ create_mcp_server,
77
+ )
78
+
58
79
  try:
59
80
  root = project_root or Path.cwd()
60
81
  except Exception:
@@ -256,8 +277,63 @@ def build_server(project_root: Path | None = None) -> FastMCP:
256
277
  )
257
278
 
258
279
 
259
- def main() -> None:
260
- build_server().run()
280
+ def main(argv: Sequence[str] | None = None) -> None:
281
+ args = list(sys.argv[1:] if argv is None else argv)
282
+ if any(arg in {"-h", "--help"} for arg in args):
283
+ print("Usage: umem-mcp [--help]")
284
+ print()
285
+ print("Run the Universal Memory MCP server over stdio.")
286
+ print("Troubleshoot environment issues with: umem doctor")
287
+ return
288
+
289
+ try:
290
+ build_server().run()
291
+ except Exception as error:
292
+ _write_startup_failure(error)
293
+ raise SystemExit(1) from None
294
+
295
+
296
+ def _write_startup_failure(error: Exception) -> None:
297
+ lines = (
298
+ f"Universal Memory MCP startup failed: {_safe_error_detail(error)}",
299
+ f"Recovery hint: {_safe_recovery_hint(error)}",
300
+ )
301
+ try:
302
+ print(*lines, sep="\n", file=sys.stderr)
303
+ except OSError:
304
+ return
305
+
306
+
307
+ def _safe_error_detail(error: Exception) -> str:
308
+ if isinstance(error, ImportError):
309
+ return _fallback_sanitize_error_detail(error)
310
+ try:
311
+ from universal_memory.interfaces.errors import sanitize_error_detail # noqa: PLC0415
312
+
313
+ return sanitize_error_detail(error)
314
+ except Exception:
315
+ return _fallback_sanitize_error_detail(error)
316
+
317
+
318
+ def _safe_recovery_hint(error: Exception) -> str:
319
+ try:
320
+ from universal_memory.interfaces.errors import recovery_hint # noqa: PLC0415
321
+
322
+ return recovery_hint(error)
323
+ except Exception:
324
+ return _DEFAULT_RECOVERY_HINT
325
+
326
+
327
+ def _fallback_sanitize_error_detail(error: Exception) -> str:
328
+ try:
329
+ detail = str(error)
330
+ except Exception:
331
+ return "Unexpected error."
332
+ detail = _UNIX_ABSOLUTE_PATH.sub("<path>", detail)
333
+ detail = _WINDOWS_ABSOLUTE_PATH.sub("<path>", detail)
334
+ for pattern in _SECRET_PATTERNS:
335
+ detail = pattern.sub("<secret>", detail)
336
+ return detail[:240] or "Unexpected error."
261
337
 
262
338
 
263
339
  if __name__ == "__main__":
@@ -131,32 +131,16 @@ from universal_memory.interfaces.errors import (
131
131
 
132
132
  DEFAULT_CONTEXT_MAX_SIZE_CHARS = 4000
133
133
  AUDIT_REFERENCE_PLACEHOLDER = "not-implemented-yet"
134
- INIT_SPLASH_MARKER = "USB"
134
+ INIT_SPLASH_MARKER = "UMem"
135
135
  INIT_SPLASH_LINES = (
136
- " umem",
137
- " ┌───┐┌───────────────────────────────────────┐\n │USB├┤ (o) ├┐\n └───┘└=======================================┘│\n ┘",
138
- " [USB] == portable memory for AI agents == ",
136
+ " _ _ __ __ \n"
137
+ "| | | | \\/ | \n"
138
+ "| | | | \\ / | ___ _ __ ___ \n"
139
+ "| | | | |\\/| |/ _ \\ '_ ` _ \\ \n"
140
+ "| |__| | | | | __/ | | | | |\n"
141
+ " \\____/|_| |_|\\___|_| |_| |_|",
142
+ " persistent context for AI agents",
139
143
  )
140
- INIT_SPLASH_ANSI = """\x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[m
141
- \x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[m
142
- \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[m
143
- \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[m
144
- \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[m
145
- \x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[38;5;254;48;5;255m▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄\x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[m
146
- \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄\x1b[38;5;255;48;5;254m▄\x1b[38;5;252;48;5;251m▄\x1b[38;5;188;48;5;188m▄\x1b[38;5;253;48;5;188m▄▄\x1b[38;5;254;48;5;253m▄▄\x1b[38;5;253;48;5;188m▄\x1b[38;5;254;48;5;253m▄▄\x1b[38;5;253;48;5;253m▄\x1b[38;5;253;48;5;188m▄\x1b[38;5;253;48;5;253m▄▄▄▄▄▄\x1b[38;5;188;48;5;253m▄\x1b[38;5;253;48;5;253m▄▄▄▄▄▄▄▄▄▄▄▄▄▄\x1b[38;5;253;48;5;188m▄\x1b[38;5;252;48;5;253m▄\x1b[38;5;253;48;5;253m▄\x1b[38;5;253;48;5;188m▄\x1b[38;5;253;48;5;253m▄▄▄\x1b[38;5;252;48;5;253m▄▄\x1b[38;5;254;48;5;254m▄\x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[m
147
- \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄▄\x1b[38;5;254;48;5;255m▄\x1b[38;5;188;48;5;252m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;251;48;5;252m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;252;48;5;252m▄\x1b[38;5;252;48;5;251m▄▄\x1b[38;5;252;48;5;252m▄\x1b[38;5;251;48;5;252m▄\x1b[38;5;252;48;5;252m▄\x1b[38;5;251;48;5;188m▄\x1b[38;5;7;48;5;252m▄\x1b[38;5;251;48;5;252m▄\x1b[38;5;252;48;5;251m▄▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;252;48;5;251m▄▄\x1b[38;5;251;48;5;251m▄▄\x1b[38;5;7;48;5;251m▄▄\x1b[38;5;250;48;5;252m▄\x1b[38;5;7;48;5;252m▄▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;252;48;5;251m▄\x1b[38;5;251;48;5;253m▄\x1b[38;5;7;48;5;252m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;251;48;5;252m▄\x1b[38;5;250;48;5;252m▄\x1b[38;5;252;48;5;252m▄▄▄\x1b[38;5;7;48;5;252m▄\x1b[38;5;251;48;5;252m▄\x1b[38;5;252;48;5;251m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;251;48;5;253m▄\x1b[38;5;255;48;5;255m▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[m
148
- \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄\x1b[38;5;247;48;5;251m▄\x1b[38;5;248;48;5;7m▄\x1b[38;5;247;48;5;7m▄\x1b[38;5;102;48;5;250m▄\x1b[38;5;247;48;5;250m▄\x1b[38;5;247;48;5;7m▄▄\x1b[38;5;248;48;5;251m▄\x1b[38;5;188;48;5;188m▄\x1b[38;5;251;48;5;251m▄▄\x1b[38;5;251;48;5;7m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;251;48;5;7m▄▄\x1b[38;5;251;48;5;251m▄▄▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;251;48;5;252m▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;246;48;5;251m▄\x1b[38;5;243;48;5;248m▄\x1b[38;5;246;48;5;250m▄\x1b[38;5;245;48;5;102m▄\x1b[38;5;247;48;5;243m▄\x1b[38;5;250;48;5;246m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;248;48;5;250m▄\x1b[38;5;7;48;5;250m▄\x1b[38;5;251;48;5;7m▄\x1b[38;5;7;48;5;251m▄▄\x1b[38;5;251;48;5;7m▄\x1b[38;5;252;48;5;251m▄\x1b[38;5;251;48;5;250m▄\x1b[38;5;251;48;5;249m▄\x1b[38;5;250;48;5;249m▄\x1b[38;5;251;48;5;7m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;251;48;5;7m▄\x1b[38;5;7;48;5;251m▄▄▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;7;48;5;251m▄▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;254;48;5;188m▄\x1b[38;5;246;48;5;249m▄\x1b[38;5;253;48;5;255m▄\x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m ▄\x1b[48;5;255m \x1b[m
149
- \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄\x1b[38;5;247;48;5;247m▄▄▄\x1b[38;5;247;48;5;245m▄\x1b[38;5;247;48;5;247m▄\x1b[38;5;246;48;5;247m▄\x1b[38;5;246;48;5;246m▄\x1b[38;5;248;48;5;248m▄\x1b[38;5;253;48;5;253m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;250;48;5;251m▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;250;48;5;7m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;251;48;5;252m▄\x1b[38;5;250;48;5;252m▄\x1b[38;5;250;48;5;251m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;250;48;5;7m▄\x1b[38;5;249;48;5;7m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;243;48;5;251m▄\x1b[38;5;248;48;5;247m▄\x1b[38;5;249;48;5;145m▄\x1b[38;5;7;48;5;245m▄\x1b[38;5;245;48;5;242m▄\x1b[38;5;247;48;5;247m▄\x1b[38;5;250;48;5;145m▄\x1b[38;5;250;48;5;251m▄\x1b[38;5;248;48;5;242m▄\x1b[38;5;247;48;5;245m▄\x1b[38;5;250;48;5;250m▄\x1b[38;5;249;48;5;249m▄\x1b[38;5;250;48;5;145m▄\x1b[38;5;250;48;5;250m▄\x1b[38;5;250;48;5;251m▄\x1b[38;5;249;48;5;7m▄\x1b[38;5;145;48;5;251m▄\x1b[38;5;248;48;5;7m▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;249;48;5;7m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;250;48;5;251m▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;249;48;5;250m▄\x1b[38;5;250;48;5;251m▄\x1b[38;5;249;48;5;251m▄\x1b[38;5;250;48;5;250m▄\x1b[38;5;251;48;5;7m▄\x1b[38;5;253;48;5;253m▄\x1b[38;5;247;48;5;246m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[m
150
- \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[38;5;246;48;5;247m▄\x1b[38;5;247;48;5;246m▄\x1b[38;5;247;48;5;247m▄\x1b[38;5;8;48;5;246m▄\x1b[38;5;246;48;5;246m▄\x1b[38;5;246;48;5;245m▄\x1b[38;5;246;48;5;246m▄\x1b[38;5;247;48;5;247m▄\x1b[38;5;253;48;5;254m▄\x1b[38;5;251;48;5;7m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;251;48;5;250m▄\x1b[38;5;251;48;5;7m▄\x1b[38;5;7;48;5;250m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;250;48;5;251m▄\x1b[38;5;249;48;5;251m▄\x1b[38;5;249;48;5;250m▄\x1b[38;5;250;48;5;250m▄\x1b[38;5;145;48;5;7m▄\x1b[38;5;250;48;5;250m▄\x1b[38;5;250;48;5;249m▄\x1b[38;5;250;48;5;246m▄\x1b[38;5;243;48;5;248m▄\x1b[38;5;248;48;5;8m▄\x1b[38;5;248;48;5;250m▄\x1b[38;5;8;48;5;8m▄\x1b[38;5;243;48;5;240m▄\x1b[38;5;243;48;5;250m▄\x1b[38;5;246;48;5;145m▄\x1b[38;5;248;48;5;246m▄\x1b[38;5;251;48;5;145m▄\x1b[38;5;250;48;5;249m▄\x1b[38;5;249;48;5;7m▄\x1b[38;5;250;48;5;249m▄▄\x1b[38;5;248;48;5;250m▄\x1b[38;5;145;48;5;250m▄\x1b[38;5;7;48;5;249m▄\x1b[38;5;249;48;5;250m▄▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;251;48;5;7m▄\x1b[38;5;145;48;5;7m▄\x1b[38;5;145;48;5;249m▄\x1b[38;5;250;48;5;7m▄\x1b[38;5;249;48;5;249m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;254;48;5;254m▄\x1b[38;5;248;48;5;248m▄\x1b[38;5;7;48;5;250m▄\x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[m
151
- \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄\x1b[38;5;246;48;5;247m▄\x1b[38;5;247;48;5;247m▄▄\x1b[38;5;248;48;5;248m▄\x1b[38;5;248;48;5;249m▄\x1b[38;5;247;48;5;247m▄\x1b[38;5;247;48;5;246m▄\x1b[38;5;248;48;5;247m▄\x1b[38;5;253;48;5;253m▄\x1b[38;5;251;48;5;251m▄▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;251;48;5;252m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;250;48;5;7m▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;249;48;5;250m▄\x1b[38;5;7;48;5;250m▄▄\x1b[38;5;251;48;5;250m▄\x1b[38;5;7;48;5;7m▄\x1b[38;5;7;48;5;250m▄\x1b[38;5;250;48;5;250m▄\x1b[38;5;250;48;5;249m▄\x1b[38;5;7;48;5;246m▄\x1b[38;5;250;48;5;247m▄\x1b[38;5;247;48;5;242m▄\x1b[38;5;243;48;5;241m▄\x1b[38;5;246;48;5;241m▄\x1b[38;5;250;48;5;247m▄\x1b[38;5;250;48;5;7m▄▄\x1b[38;5;249;48;5;7m▄\x1b[38;5;7;48;5;7m▄\x1b[48;5;250m \x1b[38;5;7;48;5;251m▄\x1b[38;5;7;48;5;250m▄▄▄\x1b[38;5;249;48;5;250m▄\x1b[38;5;251;48;5;250m▄\x1b[38;5;250;48;5;7m▄\x1b[38;5;249;48;5;251m▄\x1b[38;5;250;48;5;251m▄\x1b[38;5;250;48;5;249m▄\x1b[38;5;250;48;5;7m▄▄\x1b[38;5;250;48;5;249m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;251;48;5;255m▄\x1b[38;5;248;48;5;247m▄\x1b[38;5;254;48;5;252m▄\x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[m
152
- \x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[38;5;255;48;5;254m▄\x1b[38;5;253;48;5;188m▄\x1b[38;5;188;48;5;251m▄\x1b[38;5;252;48;5;7m▄\x1b[38;5;251;48;5;250m▄\x1b[38;5;251;48;5;249m▄\x1b[38;5;251;48;5;145m▄\x1b[38;5;251;48;5;250m▄\x1b[38;5;253;48;5;253m▄\x1b[38;5;7;48;5;7m▄▄\x1b[38;5;250;48;5;7m▄\x1b[38;5;251;48;5;250m▄\x1b[38;5;249;48;5;249m▄\x1b[38;5;250;48;5;250m▄\x1b[38;5;249;48;5;250m▄\x1b[38;5;249;48;5;7m▄\x1b[38;5;248;48;5;250m▄\x1b[38;5;247;48;5;250m▄▄\x1b[38;5;248;48;5;251m▄\x1b[38;5;145;48;5;7m▄\x1b[38;5;250;48;5;7m▄\x1b[38;5;249;48;5;249m▄\x1b[38;5;248;48;5;250m▄\x1b[38;5;7;48;5;251m▄\x1b[38;5;251;48;5;251m▄\x1b[38;5;250;48;5;250m▄\x1b[38;5;145;48;5;248m▄\x1b[38;5;145;48;5;249m▄\x1b[38;5;145;48;5;248m▄\x1b[38;5;249;48;5;249m▄\x1b[38;5;247;48;5;250m▄\x1b[38;5;247;48;5;7m▄\x1b[38;5;248;48;5;250m▄\x1b[38;5;248;48;5;248m▄\x1b[38;5;248;48;5;145m▄\x1b[38;5;248;48;5;250m▄\x1b[38;5;145;48;5;250m▄\x1b[38;5;145;48;5;249m▄\x1b[38;5;250;48;5;7m▄\x1b[38;5;249;48;5;251m▄\x1b[38;5;249;48;5;7m▄\x1b[38;5;248;48;5;7m▄\x1b[38;5;249;48;5;249m▄\x1b[38;5;145;48;5;250m▄\x1b[38;5;145;48;5;251m▄\x1b[38;5;249;48;5;250m▄\x1b[38;5;250;48;5;145m▄\x1b[38;5;252;48;5;248m▄\x1b[38;5;254;48;5;251m▄\x1b[38;5;254;48;5;254m▄\x1b[38;5;255;48;5;255m▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[m
153
- \x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[38;5;255;48;5;254m▄▄\x1b[38;5;255;48;5;253m▄▄\x1b[38;5;254;48;5;188m▄\x1b[38;5;254;48;5;253m▄\x1b[38;5;253;48;5;253m▄\x1b[38;5;248;48;5;251m▄\x1b[38;5;247;48;5;249m▄▄\x1b[38;5;247;48;5;145m▄▄\x1b[38;5;247;48;5;248m▄▄▄▄\x1b[38;5;247;48;5;247m▄\x1b[38;5;247;48;5;248m▄▄\x1b[38;5;247;48;5;249m▄▄▄\x1b[38;5;248;48;5;145m▄\x1b[38;5;247;48;5;248m▄\x1b[38;5;248;48;5;247m▄\x1b[38;5;248;48;5;248m▄\x1b[38;5;248;48;5;247m▄▄\x1b[38;5;248;48;5;248m▄\x1b[38;5;248;48;5;247m▄\x1b[38;5;248;48;5;246m▄\x1b[38;5;145;48;5;247m▄▄\x1b[38;5;248;48;5;247m▄\x1b[38;5;247;48;5;248m▄\x1b[38;5;145;48;5;249m▄\x1b[38;5;248;48;5;247m▄▄\x1b[38;5;248;48;5;246m▄▄\x1b[38;5;248;48;5;247m▄\x1b[38;5;248;48;5;250m▄\x1b[38;5;248;48;5;248m▄\x1b[38;5;145;48;5;248m▄\x1b[38;5;248;48;5;248m▄▄\x1b[38;5;247;48;5;247m▄\x1b[38;5;248;48;5;247m▄\x1b[38;5;252;48;5;251m▄\x1b[38;5;255;48;5;254m▄\x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[m
154
- \x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄\x1b[38;5;255;48;5;253m▄\x1b[38;5;254;48;5;252m▄▄\x1b[38;5;254;48;5;251m▄▄\x1b[38;5;253;48;5;251m▄▄▄▄▄▄▄▄▄▄▄▄▄▄\x1b[38;5;254;48;5;251m▄\x1b[38;5;254;48;5;252m▄▄\x1b[38;5;254;48;5;188m▄\x1b[38;5;255;48;5;254m▄\x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[m
155
- \x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄▄\x1b[38;5;255;48;5;254m▄▄▄▄▄▄▄▄▄▄▄▄▄▄\x1b[38;5;255;48;5;255m▄\x1b[38;5;255;48;5;254m▄▄▄▄▄▄▄▄▄▄▄▄▄▄\x1b[38;5;255;48;5;255m▄▄\x1b[38;5;255;48;5;254m▄\x1b[38;5;255;48;5;255m▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄\x1b[48;5;255m \x1b[m
156
- \x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[m
157
- \x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[m
158
- \x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[38;5;15;48;5;255m▄▄\x1b[48;5;255m \x1b[m
159
- \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄▄▄▄▄\x1b[48;5;255m \x1b[38;5;255;48;5;255m▄▄▄\x1b[48;5;255m \x1b[m"""
160
144
  SetupProjectCommand = (
161
145
  Callable[[Path, list[str] | None], SetupProjectResult] | Callable[[Path], SetupProjectResult]
162
146
  )
@@ -1357,13 +1341,6 @@ def _ci_environment_enabled() -> bool:
1357
1341
  return value.strip().lower() not in {"", "0", "false", "no", "off"}
1358
1342
 
1359
1343
 
1360
- def _terminal_color_enabled() -> bool:
1361
- if "NO_COLOR" in os.environ:
1362
- return False
1363
- term = os.environ.get("TERM", "")
1364
- return bool(term) and term != "dumb"
1365
-
1366
-
1367
1344
  def _should_render_init_splash(output_format: str) -> bool:
1368
1345
  return (
1369
1346
  output_format != "json"
@@ -1374,13 +1351,7 @@ def _should_render_init_splash(output_format: str) -> bool:
1374
1351
 
1375
1352
 
1376
1353
  def _render_init_splash() -> None:
1377
- if _terminal_color_enabled():
1378
- banner = (
1379
- f"\x1b[36m{INIT_SPLASH_LINES[0]}\x1b[0m\n{INIT_SPLASH_ANSI}\n{INIT_SPLASH_LINES[2]}\n"
1380
- )
1381
- else:
1382
- banner = "\n".join(INIT_SPLASH_LINES) + "\n"
1383
- sys.stdout.write(banner)
1354
+ sys.stdout.write("\n".join(INIT_SPLASH_LINES) + "\n")
1384
1355
 
1385
1356
 
1386
1357
  def _confirm(prompt: str, default: bool = False) -> bool: