empathy-framework 3.3.0__py3-none-any.whl → 3.3.3__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: empathy-framework
3
- Version: 3.3.0
3
+ Version: 3.3.3
4
4
  Summary: AI collaboration framework with persistent memory, anticipatory intelligence, code inspection, and multi-agent orchestration
5
5
  Author-email: Patrick Roebuck <patrick.roebuck@smartAImemory.com>
6
6
  Maintainer-email: Smart-AI-Memory <patrick.roebuck@smartAImemory.com>
@@ -271,7 +271,7 @@ Dynamic: license-file
271
271
  **The AI collaboration framework that predicts problems before they happen.**
272
272
 
273
273
  [![PyPI](https://img.shields.io/pypi/v/empathy-framework)](https://pypi.org/project/empathy-framework/)
274
- [![Tests](https://img.shields.io/badge/tests-2%2C365%20passing-brightgreen)](https://github.com/Smart-AI-Memory/empathy-framework/actions)
274
+ [![Tests](https://img.shields.io/badge/tests-3%2C564%20passing-brightgreen)](https://github.com/Smart-AI-Memory/empathy-framework/actions)
275
275
  [![Coverage](https://img.shields.io/badge/coverage-55%25-yellow)](https://github.com/Smart-AI-Memory/empathy-framework)
276
276
  [![License](https://img.shields.io/badge/license-Fair%20Source%200.9-blue)](LICENSE)
277
277
  [![Python](https://img.shields.io/badge/python-3.10+-blue)](https://www.python.org)
@@ -368,17 +368,21 @@ print(result.prevention_steps) # How to prevent it
368
368
  ## Become a Power User
369
369
 
370
370
  ### Level 1: Basic Usage
371
+
371
372
  ```bash
372
373
  pip install empathy-framework
373
374
  ```
375
+
374
376
  - Works out of the box with sensible defaults
375
377
  - Auto-detects your API keys
376
378
 
377
379
  ### Level 2: Cost Optimization
380
+
378
381
  ```bash
379
382
  # Enable hybrid mode for 80-96% cost savings
380
383
  python -m empathy_os.models.cli provider --set hybrid
381
384
  ```
385
+
382
386
  | Tier | Model | Use Case | Cost |
383
387
  |------|-------|----------|------|
384
388
  | Cheap | GPT-4o-mini / Haiku | Summarization, simple tasks | $0.15-0.25/M |
@@ -386,6 +390,7 @@ python -m empathy_os.models.cli provider --set hybrid
386
390
  | Premium | o1 / Opus | Architecture, complex decisions | $15/M |
387
391
 
388
392
  ### Level 3: Multi-Model Workflows
393
+
389
394
  ```python
390
395
  from empathy_llm_toolkit import EmpathyLLM
391
396
 
@@ -398,13 +403,16 @@ await llm.interact(user_id="dev", user_input="Design system", task_type="coordin
398
403
  ```
399
404
 
400
405
  ### Level 4: VSCode Integration
406
+
401
407
  Install the Empathy VSCode extension for:
408
+
402
409
  - **Real-time Dashboard** — Health score, costs, patterns
403
410
  - **One-Click Workflows** — Research, code review, debugging
404
411
  - **Visual Cost Tracking** — See savings in real-time
405
- - See also: `docs/dashboard-costs-by-tier.md` for interpreting the **By tier (7 days)** cost breakdown.
412
+ - See also: `docs/dashboard-costs-by-tier.md` for interpreting the **By tier (7 days)** cost breakdown.
406
413
 
407
414
  ### Level 5: Custom Agents
415
+
408
416
  ```python
409
417
  from empathy_os.agents import AgentFactory
410
418
 
@@ -421,6 +429,7 @@ security_agent = AgentFactory.create(
421
429
  ## CLI Reference
422
430
 
423
431
  ### Provider Configuration
432
+
424
433
  ```bash
425
434
  python -m empathy_os.models.cli provider # Show current config
426
435
  python -m empathy_os.models.cli provider --set anthropic # Single provider
@@ -430,6 +439,7 @@ python -m empathy_os.models.cli provider -f json # JSON output
430
439
  ```
431
440
 
432
441
  ### Model Registry
442
+
433
443
  ```bash
434
444
  python -m empathy_os.models.cli registry # Show all models
435
445
  python -m empathy_os.models.cli registry --provider openai # Filter by provider
@@ -437,6 +447,7 @@ python -m empathy_os.models.cli costs --input-tokens 50000 # Estimate costs
437
447
  ```
438
448
 
439
449
  ### Telemetry & Analytics
450
+
440
451
  ```bash
441
452
  python -m empathy_os.models.cli telemetry # Summary
442
453
  python -m empathy_os.models.cli telemetry --costs # Cost savings report
@@ -445,6 +456,7 @@ python -m empathy_os.models.cli telemetry --fallbacks # Fallback stats
445
456
  ```
446
457
 
447
458
  ### Memory Control
459
+
448
460
  ```bash
449
461
  empathy-memory serve # Start Redis + API server
450
462
  empathy-memory status # Check system status
@@ -453,6 +465,7 @@ empathy-memory patterns # List stored patterns
453
465
  ```
454
466
 
455
467
  ### Code Inspection
468
+
456
469
  ```bash
457
470
  empathy-inspect . # Run full inspection
458
471
  empathy-inspect . --format sarif # GitHub Actions format
@@ -726,8 +739,8 @@ cd empathy-framework && pip install -e .[dev]
726
739
 
727
740
  ```bash
728
741
  # Required: At least one provider
729
- export ANTHROPIC_API_KEY="sk-ant-..." # For Claude models
730
- export OPENAI_API_KEY="sk-..." # For GPT models
742
+ export ANTHROPIC_API_KEY="sk-ant-..." # For Claude models # pragma: allowlist secret
743
+ export OPENAI_API_KEY="sk-..." # For GPT models # pragma: allowlist secret
731
744
 
732
745
  # Optional: Redis for memory
733
746
  export REDIS_URL="redis://localhost:6379"
@@ -32,7 +32,7 @@ coach_wizards/refactoring_wizard.py,sha256=1AuRyX45KI63n_-fvvbRXamqvPbrB-O1B7TPP
32
32
  coach_wizards/scaling_wizard.py,sha256=yLULCkflLoBKS4hOSBPQuKKGBGHgKExnuEp5WLTIY-8,2596
33
33
  coach_wizards/security_wizard.py,sha256=tr1iq0egAMLCM-wOFhTDN5dHQRFuhSshXSkv17Jm7eM,2603
34
34
  coach_wizards/testing_wizard.py,sha256=M2RtaTa1WHsk42svJAEZpLySU3PXJJZn2jigouMJrG0,2561
35
- empathy_framework-3.3.0.dist-info/licenses/LICENSE,sha256=IJ9eeI5KSrD5P7alsn7sI_6_1bDihxBA5S4Sen4jf2k,4937
35
+ empathy_framework-3.3.3.dist-info/licenses/LICENSE,sha256=IJ9eeI5KSrD5P7alsn7sI_6_1bDihxBA5S4Sen4jf2k,4937
36
36
  empathy_healthcare_plugin/__init__.py,sha256=FvVcD7WQTlmCCLgSPfM-FPT2l-ma1oAACBZWhtYFAUA,296
37
37
  empathy_healthcare_plugin/protocols/cardiac.json,sha256=uShOvI2RQJYLZacLT2R_aHfsjvJdyCu_gYfpMfK3N74,2088
38
38
  empathy_healthcare_plugin/protocols/post_operative.json,sha256=nqh3ydPY8FNSLv-Q3QmH8Dsyc1c4LvQxUSP84B8W6xk,2021
@@ -52,10 +52,12 @@ empathy_llm_toolkit/pattern_summary.py,sha256=q3gPMZtk5TIG9hs61mEZzaBtpry0qVfbu2
52
52
  empathy_llm_toolkit/providers.py,sha256=M_DrZr7Yq5c2edEoUGH20LUI9nmi9KOkqDvTku2dhBw,13958
53
53
  empathy_llm_toolkit/session_status.py,sha256=pJwqHwbVwR2Q6coRkB_34CWRCMoF-r4-YBtQWEO1Mj8,25724
54
54
  empathy_llm_toolkit/state.py,sha256=oi8bPqUHkmfgkfT4_4eD1ndIGH_THyLQDYlIWZLUx5s,8051
55
- empathy_os/__init__.py,sha256=pvaca4oCfdL4MG5WO-RKJeXBOk0oj02Mhh_E0h7zSyY,5896
56
- empathy_os/cli.py,sha256=Kx7X_nynVO8EyRNPBkdFQzeoL-IW95PC5PLOzWgg9FA,86835
55
+ empathy_llm_toolkit/htmlcov/status.json,sha256=emOFm_dTJcNl_Bw_lh62qNbnU6yzhne8TWlQkrPVdrk,8544
56
+ empathy_llm_toolkit/security/htmlcov/status.json,sha256=ELS9bn59azqfEyyokI-nV4gQLPQh4bVwHsmhVbdrKYI,4402
57
+ empathy_os/__init__.py,sha256=4WGijLjIQ6xHlSWxl5hb9FxTU-EJB1-Kel7gf3LcQMI,7069
58
+ empathy_os/cli.py,sha256=k3r_0as7mtOPnLyp3cNKRQiLSKYJqFlmYuRuhdiRrwM,87001
57
59
  empathy_os/cli_unified.py,sha256=TvfsheONim8ETegZo3KJA2vBtFD0fn0rRikUz83w1Dg,14021
58
- empathy_os/config.py,sha256=GeYlCDl2zRP_sOoM2N-HCyH7AdCeVGOoyVWA1BltVXc,12670
60
+ empathy_os/config.py,sha256=tPM9gsVE0y8DsC_cxfZxyngq2cKiWN1F6ezKGKON5jI,13709
59
61
  empathy_os/coordination.py,sha256=0jKt2DzzJmFjpXJs4pMXBcUktCFHsa9i3rkXzXxykGk,28656
60
62
  empathy_os/core.py,sha256=kL_37DajqIV1_b0ldee8rGG0xUTrSzAqYuQ4dowSxuw,53229
61
63
  empathy_os/cost_tracker.py,sha256=VwjkyKEm-gbmyO7wQ88t82RNZfc-LCp0vpK699Giqp0,12575
@@ -69,14 +71,15 @@ empathy_os/logging_config.py,sha256=U_MylJblr2jMfYmsK6z4WKd9Z6ZZ1G0kxzq9cNPGLEk,
69
71
  empathy_os/monitoring.py,sha256=76Fiwqd8prqi6H_mMX79_yEPbfbPdx58E9ZfLld6fvw,13434
70
72
  empathy_os/pattern_library.py,sha256=jUeWRnRHbhB05Rm9kL-OFdMajRCOqOzOb9ow_23JdY0,14040
71
73
  empathy_os/persistence.py,sha256=2jNqPmW6TrCH2quYph2SVMQnAnhBDDVk9DqNuEhLhGE,17637
74
+ empathy_os/platform_utils.py,sha256=8R35nql5f1cuMwWz9JKM_Nx_Gf9rGhCiAleEmIk8WVY,7343
72
75
  empathy_os/redis_config.py,sha256=L8KoHFwhl-_twSswMfELUgOmANOTPRB9Yj8VXuxfSb4,5947
73
76
  empathy_os/redis_memory.py,sha256=lWS_F4FeDkmEI-jIgkPTzs3D8TTDB0627WsOxYMT-XM,23276
74
77
  empathy_os/templates.py,sha256=ap4u9i5O9KA83wWLfoUCS7phDHKb6wj8M1Zcm218lN0,17069
75
78
  empathy_os/trust_building.py,sha256=8ZvNwJmeDyKeUIkk_331M9jwKcqrsn6K43gnGtnIXbM,18790
76
- empathy_os/workflow_commands.py,sha256=Kqyr8ICTsx4S-_ThXB52SYzH1mYFsHGVDYiG2KgkQGo,21904
79
+ empathy_os/workflow_commands.py,sha256=ZfACBQKjaAfEZER4yRRsFVw9IfGhhPVGwQyBjD1HUAk,21904
77
80
  empathy_software_plugin/SOFTWARE_PLUGIN_README.md,sha256=RXIOB9Mt-8JrfGAA3ZUuRPT34sThubrwUgg5iNcSKIc,22591
78
81
  empathy_software_plugin/__init__.py,sha256=Ylyj95pSsoN9Zasam96DH61uBHoMJh3kbhO7k_VaCWo,310
79
- empathy_software_plugin/cli.py,sha256=GrZWpnFJ9allM9sYrh8rSxSlVDU6RZVnEy4FYg-dSG8,22366
82
+ empathy_software_plugin/cli.py,sha256=N48zwVs4EfGX-69PqhiMJ77H5TS_BpCbLVJ6JTTkypw,22414
80
83
  empathy_software_plugin/plugin.py,sha256=NNZTILE5Npo4SahA4F_3awIizLHI32_wWTFAutvmsqQ,6700
81
84
  wizards/__init__.py,sha256=5JJ6rtS5mwJtuZIgO2sYHZlCy0XJP2eyyCK5zMD6ZAc,2452
82
85
  wizards/admission_assessment_wizard.py,sha256=-Th9bwu6Sd6V2jA4fciK35QpoFc40U1quZHDMdOH93U,23609
@@ -95,8 +98,8 @@ wizards/sbar_wizard.py,sha256=CJ63JAXwcfBf6C3aYyxY2LODbARP9GPl0ZGJWLbx88E,21790
95
98
  wizards/shift_handoff_wizard.py,sha256=SkoNB0nLQGg92yz4j1j3NBR2mGVe_rw1pTjOFDy-JH0,19092
96
99
  wizards/soap_note_wizard.py,sha256=DBzuuuOvIONhwdfn8jaE4PCuGeKsFwM65XTb6gKFIy4,23572
97
100
  wizards/treatment_plan.py,sha256=t2Qk5eCa1gobEUaBztnwem_p9OuJK5BKqJ-Po8vXuns,512
98
- empathy_framework-3.3.0.dist-info/METADATA,sha256=EpWOk8nWEZbzMdK0h4tmIqBCGHwvUjlitJpGrrEyUvc,28714
99
- empathy_framework-3.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
100
- empathy_framework-3.3.0.dist-info/entry_points.txt,sha256=zMu7sKCiLndbEEXjTecltS-1P_JZoEUKrifuRBBbroc,1268
101
- empathy_framework-3.3.0.dist-info/top_level.txt,sha256=8zHB-_f0MI2K55LIEjCeaFNcog3_KgLBa_dDfzE8ESI,110
102
- empathy_framework-3.3.0.dist-info/RECORD,,
101
+ empathy_framework-3.3.3.dist-info/METADATA,sha256=RyG6jGb_KTSrLrIT5ueZ5tckAWXXSTToyR37ePnVPh8,28781
102
+ empathy_framework-3.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
103
+ empathy_framework-3.3.3.dist-info/entry_points.txt,sha256=zMu7sKCiLndbEEXjTecltS-1P_JZoEUKrifuRBBbroc,1268
104
+ empathy_framework-3.3.3.dist-info/top_level.txt,sha256=8zHB-_f0MI2K55LIEjCeaFNcog3_KgLBa_dDfzE8ESI,110
105
+ empathy_framework-3.3.3.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ {"note":"This file is an internal implementation detail to speed up HTML report generation. Its format can change at any time. You might be looking for the JSON report: https://coverage.rtfd.io/cmd.html#cmd-json","format":5,"version":"7.11.2","globals":"1647e1602d568c377a9cb0dc3296bc7b","files":{"__init___py":{"hash":"1e943cec927dee34f9523b2eb67d8922","index":{"url":"__init___py.html","file":"__init__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":6,"n_excluded":0,"n_missing":0,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"claude_memory_py":{"hash":"25f8fe4e69f6b1e4091924c070803e8a","index":{"url":"claude_memory_py.html","file":"claude_memory.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":169,"n_excluded":0,"n_missing":169,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"code_health_py":{"hash":"a78753eaebd08c9c2cb5f1cd5a14f723","index":{"url":"code_health_py.html","file":"code_health.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":503,"n_excluded":0,"n_missing":503,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"contextual_patterns_py":{"hash":"12004dba086f2def1d9e65bbf68a88f8","index":{"url":"contextual_patterns_py.html","file":"contextual_patterns.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":171,"n_excluded":0,"n_missing":171,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"core_py":{"hash":"aa25b266741f161b0fa2500af1db03da","index":{"url":"core_py.html","file":"core.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":177,"n_excluded":0,"n_missing":151,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"git_pattern_extractor_py":{"hash":"88e57b53ff683a92613ae343a55ad1bb","index":{"url":"git_pattern_extractor_py.html","file":"git_pattern_extractor.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":164,"n_excluded":0,"n_missing":164,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"levels_py":{"hash":"ef211f0c742ccdab452cbe36ac205815","index":{"url":"levels_py.html","file":"levels.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":33,"n_excluded":0,"n_missing":12,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"pattern_confidence_py":{"hash":"1806d001ef22f2f9c4f5cc24faa5724d","index":{"url":"pattern_confidence_py.html","file":"pattern_confidence.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":196,"n_excluded":0,"n_missing":196,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"pattern_resolver_py":{"hash":"af2687d544197eb576abe872270157a5","index":{"url":"pattern_resolver_py.html","file":"pattern_resolver.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":121,"n_excluded":0,"n_missing":121,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"pattern_summary_py":{"hash":"51c42611c0d7a57f4e5c9f487f3b0882","index":{"url":"pattern_summary_py.html","file":"pattern_summary.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":178,"n_excluded":0,"n_missing":178,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"providers_py":{"hash":"8b779f9132bea067bec66007bd492b45","index":{"url":"providers_py.html","file":"providers.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":106,"n_excluded":0,"n_missing":76,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_10414145323772df___init___py":{"hash":"dec456df46eda04d5d3381b09af05c36","index":{"url":"z_10414145323772df___init___py.html","file":"security/__init__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":3,"n_excluded":0,"n_missing":0,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_10414145323772df_audit_logger_py":{"hash":"9197b939a93f6b8dd8549ab6b73f0887","index":{"url":"z_10414145323772df_audit_logger_py.html","file":"security/audit_logger.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":247,"n_excluded":0,"n_missing":247,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_10414145323772df_audit_logger_example_py":{"hash":"ecc80543a0cac77b9b5a52d9d8451793","index":{"url":"z_10414145323772df_audit_logger_example_py.html","file":"security/audit_logger_example.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":59,"n_excluded":0,"n_missing":59,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_10414145323772df_pii_scrubber_py":{"hash":"0254532a873c529be428a4d88663e917","index":{"url":"z_10414145323772df_pii_scrubber_py.html","file":"security/pii_scrubber.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":140,"n_excluded":0,"n_missing":140,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_10414145323772df_secrets_detector_py":{"hash":"b160d5903a118f959559e32f7535d7e2","index":{"url":"z_10414145323772df_secrets_detector_py.html","file":"security/secrets_detector.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":199,"n_excluded":0,"n_missing":199,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_10414145323772df_secrets_detector_example_py":{"hash":"ba9b9af2e80e78ec21a15712e06dd279","index":{"url":"z_10414145323772df_secrets_detector_example_py.html","file":"security/secrets_detector_example.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":145,"n_excluded":0,"n_missing":145,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_10414145323772df_secure_memdocs_py":{"hash":"47c339d82b66068fb9a831fffb58bc8a","index":{"url":"z_10414145323772df_secure_memdocs_py.html","file":"security/secure_memdocs.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":350,"n_excluded":0,"n_missing":350,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_10414145323772df_secure_memdocs_example_py":{"hash":"3fd076ca92a0a84002c57d80cfed902f","index":{"url":"z_10414145323772df_secure_memdocs_example_py.html","file":"security/secure_memdocs_example.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":106,"n_excluded":0,"n_missing":106,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"session_status_py":{"hash":"4c8d6be4bbdac24bc27d0c27890532a6","index":{"url":"session_status_py.html","file":"session_status.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":312,"n_excluded":0,"n_missing":312,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"state_py":{"hash":"724ab29020a3a3870ac6b9c94c71127d","index":{"url":"state_py.html","file":"state.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":87,"n_excluded":0,"n_missing":40,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_984ef1e268fea449___init___py":{"hash":"10fa30f44c3688eb2de5302412aa33d6","index":{"url":"z_984ef1e268fea449___init___py.html","file":"wizards/__init__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":5,"n_excluded":0,"n_missing":5,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_984ef1e268fea449_base_wizard_py":{"hash":"590f2d248a77ea3e621b9f3c4ded2397","index":{"url":"z_984ef1e268fea449_base_wizard_py.html","file":"wizards/base_wizard.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":56,"n_excluded":0,"n_missing":56,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_984ef1e268fea449_customer_support_wizard_py":{"hash":"f92a98bbbec12fad579ac19787971b08","index":{"url":"z_984ef1e268fea449_customer_support_wizard_py.html","file":"wizards/customer_support_wizard.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":17,"n_excluded":0,"n_missing":17,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_984ef1e268fea449_healthcare_wizard_py":{"hash":"96977dfa18d691fdd84424311686d23a","index":{"url":"z_984ef1e268fea449_healthcare_wizard_py.html","file":"wizards/healthcare_wizard.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":57,"n_excluded":0,"n_missing":57,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_984ef1e268fea449_technology_wizard_py":{"hash":"6998c841130aa6e0982dc586006a5640","index":{"url":"z_984ef1e268fea449_technology_wizard_py.html","file":"wizards/technology_wizard.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":19,"n_excluded":0,"n_missing":19,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}}}}
@@ -0,0 +1 @@
1
+ {"note":"This file is an internal implementation detail to speed up HTML report generation. Its format can change at any time. You might be looking for the JSON report: https://coverage.rtfd.io/cmd.html#cmd-json","format":5,"version":"7.11.2","globals":"93e0a98f3da7f95e5ae873f233acfe8f","files":{"z_0da0bc3caf3df21d___init___py":{"hash":"678b97c61cc7e540c9ecbd0485aef62e","index":{"url":"z_0da0bc3caf3df21d___init___py.html","file":"/Users/patrickroebuck/empathy_11_6_2025/Empathy-framework/empathy_llm_toolkit/__init__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":6,"n_excluded":0,"n_missing":0,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_0da0bc3caf3df21d_claude_memory_py":{"hash":"9e1a909857359093b60c8b3641b9686f","index":{"url":"z_0da0bc3caf3df21d_claude_memory_py.html","file":"/Users/patrickroebuck/empathy_11_6_2025/Empathy-framework/empathy_llm_toolkit/claude_memory.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":169,"n_excluded":0,"n_missing":132,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_0da0bc3caf3df21d_core_py":{"hash":"45f6bf0d1f0f9b193700d313f3999f0a","index":{"url":"z_0da0bc3caf3df21d_core_py.html","file":"/Users/patrickroebuck/empathy_11_6_2025/Empathy-framework/empathy_llm_toolkit/core.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":124,"n_excluded":0,"n_missing":100,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_0da0bc3caf3df21d_levels_py":{"hash":"029cd19bbfaefe72a2df6fb26fafbb07","index":{"url":"z_0da0bc3caf3df21d_levels_py.html","file":"/Users/patrickroebuck/empathy_11_6_2025/Empathy-framework/empathy_llm_toolkit/levels.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":33,"n_excluded":0,"n_missing":12,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_0da0bc3caf3df21d_providers_py":{"hash":"8a61e565ded340098a9717471cb139cc","index":{"url":"z_0da0bc3caf3df21d_providers_py.html","file":"/Users/patrickroebuck/empathy_11_6_2025/Empathy-framework/empathy_llm_toolkit/providers.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":104,"n_excluded":0,"n_missing":74,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"__init___py":{"hash":"bc61a9c102480a73e01fb8f507fe604c","index":{"url":"__init___py.html","file":"__init__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":3,"n_excluded":0,"n_missing":0,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"audit_logger_py":{"hash":"288003e820abd1edb81b099a12e58af5","index":{"url":"audit_logger_py.html","file":"audit_logger.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":244,"n_excluded":0,"n_missing":72,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"audit_logger_example_py":{"hash":"8c491197716328c7faed6badacb3a68b","index":{"url":"audit_logger_example_py.html","file":"audit_logger_example.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":60,"n_excluded":0,"n_missing":60,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"pii_scrubber_py":{"hash":"9768c14c0e1e90619f7d80f059ca4395","index":{"url":"pii_scrubber_py.html","file":"pii_scrubber.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":140,"n_excluded":0,"n_missing":107,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"secrets_detector_py":{"hash":"36cd068d345d0328e6959132de4ade69","index":{"url":"secrets_detector_py.html","file":"secrets_detector.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":199,"n_excluded":0,"n_missing":199,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"test_audit_logger_py":{"hash":"a5a92daff7da2027470aca6a728adbb8","index":{"url":"test_audit_logger_py.html","file":"test_audit_logger.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":185,"n_excluded":0,"n_missing":1,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_0da0bc3caf3df21d_state_py":{"hash":"3ed61e7760d5e588d4d6743dffa9094f","index":{"url":"z_0da0bc3caf3df21d_state_py.html","file":"/Users/patrickroebuck/empathy_11_6_2025/Empathy-framework/empathy_llm_toolkit/state.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":87,"n_excluded":0,"n_missing":40,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}}}}
empathy_os/__init__.py CHANGED
@@ -62,26 +62,26 @@ __email__ = "hello@deepstudy.ai"
62
62
 
63
63
  from .config import EmpathyConfig, load_config
64
64
  from .coordination import (
65
- AgentCoordinator,
66
- AgentTask,
67
- ConflictResolver,
68
- ResolutionResult,
69
- ResolutionStrategy,
70
- TeamPriorities,
71
- TeamSession,
65
+ AgentCoordinator,
66
+ AgentTask,
67
+ ConflictResolver,
68
+ ResolutionResult,
69
+ ResolutionStrategy,
70
+ TeamPriorities,
71
+ TeamSession,
72
72
  )
73
73
  from .core import EmpathyOS
74
74
  from .emergence import EmergenceDetector
75
75
  from .exceptions import (
76
- CollaborationStateError,
77
- ConfidenceThresholdError,
78
- EmpathyFrameworkError,
79
- EmpathyLevelError,
80
- FeedbackLoopError,
81
- LeveragePointError,
82
- PatternNotFoundError,
83
- TrustThresholdError,
84
- ValidationError,
76
+ CollaborationStateError,
77
+ ConfidenceThresholdError,
78
+ EmpathyFrameworkError,
79
+ EmpathyLevelError,
80
+ FeedbackLoopError,
81
+ LeveragePointError,
82
+ PatternNotFoundError,
83
+ TrustThresholdError,
84
+ ValidationError,
85
85
  )
86
86
  from .feedback_loops import FeedbackLoopDetector
87
87
  from .levels import Level1Reactive, Level2Guided, Level3Proactive, Level4Anticipatory, Level5Systems
@@ -90,41 +90,41 @@ from .logging_config import LoggingConfig, get_logger
90
90
 
91
91
  # Memory module (unified short-term + long-term + security)
92
92
  from .memory import (
93
- AccessTier,
94
- AgentCredentials, # Memory module imports
95
- AuditEvent,
96
- AuditLogger,
97
- Classification,
98
- ClassificationRules,
99
- ClaudeMemoryConfig,
100
- ClaudeMemoryLoader,
101
- ConflictContext,
102
- EncryptionManager,
103
- Environment,
104
- MemDocsStorage,
105
- MemoryConfig,
106
- MemoryPermissionError,
107
- PatternMetadata,
108
- PIIDetection,
109
- PIIPattern,
110
- PIIScrubber,
111
- RedisShortTermMemory,
112
- SecretDetection,
113
- SecretsDetector,
114
- SecretType,
115
- SecureMemDocsIntegration,
116
- SecurePattern,
117
- SecurityError,
118
- SecurityViolation,
119
- Severity,
120
- StagedPattern,
121
- TTLStrategy,
122
- UnifiedMemory,
123
- check_redis_connection,
124
- detect_secrets,
125
- get_railway_redis,
126
- get_redis_config,
127
- get_redis_memory,
93
+ AccessTier,
94
+ AgentCredentials, # Memory module imports
95
+ AuditEvent,
96
+ AuditLogger,
97
+ Classification,
98
+ ClassificationRules,
99
+ ClaudeMemoryConfig,
100
+ ClaudeMemoryLoader,
101
+ ConflictContext,
102
+ EncryptionManager,
103
+ Environment,
104
+ MemDocsStorage,
105
+ MemoryConfig,
106
+ MemoryPermissionError,
107
+ PatternMetadata,
108
+ PIIDetection,
109
+ PIIPattern,
110
+ PIIScrubber,
111
+ RedisShortTermMemory,
112
+ SecretDetection,
113
+ SecretsDetector,
114
+ SecretType,
115
+ SecureMemDocsIntegration,
116
+ SecurePattern,
117
+ SecurityError,
118
+ SecurityViolation,
119
+ Severity,
120
+ StagedPattern,
121
+ TTLStrategy,
122
+ UnifiedMemory,
123
+ check_redis_connection,
124
+ detect_secrets,
125
+ get_railway_redis,
126
+ get_redis_config,
127
+ get_redis_memory,
128
128
  )
129
129
  from .monitoring import AgentMetrics, AgentMonitor, TeamMetrics
130
130
  from .pattern_library import Pattern, PatternLibrary, PatternMatch
empathy_os/cli.py CHANGED
@@ -25,6 +25,7 @@ from empathy_os.discovery import show_tip_if_available
25
25
  from empathy_os.logging_config import get_logger
26
26
  from empathy_os.pattern_library import PatternLibrary
27
27
  from empathy_os.persistence import MetricsCollector, PatternPersistence, StateManager
28
+ from empathy_os.platform_utils import setup_asyncio_policy
28
29
  from empathy_os.templates import cmd_new
29
30
  from empathy_os.workflows import (
30
31
  WorkflowConfig,
@@ -1796,10 +1797,15 @@ def cmd_workflow(args):
1796
1797
  if duration_ms is None and hasattr(result, "duration_seconds"):
1797
1798
  duration_ms = int(result.duration_seconds * 1000)
1798
1799
 
1799
- # Get cost info if available
1800
+ # Get cost info if available (check cost_report first, then direct cost attribute)
1800
1801
  cost_report = getattr(result, "cost_report", None)
1801
- total_cost = cost_report.total_cost if cost_report else 0.0
1802
- savings = cost_report.savings if cost_report else 0.0
1802
+ if cost_report and hasattr(cost_report, "total_cost"):
1803
+ total_cost = cost_report.total_cost
1804
+ savings = getattr(cost_report, "savings", 0.0)
1805
+ else:
1806
+ # Fall back to direct cost attribute (e.g., CodeReviewPipelineResult)
1807
+ total_cost = getattr(result, "cost", 0.0)
1808
+ savings = 0.0
1803
1809
 
1804
1810
  if args.json:
1805
1811
  # Extract error from various result types
@@ -1829,11 +1835,6 @@ def cmd_workflow(args):
1829
1835
  print(f"\n{output_content}\n")
1830
1836
  else:
1831
1837
  print("\n✓ Workflow completed successfully.\n")
1832
-
1833
- # Brief footer with timing (detailed costs available via 'empathy costs')
1834
- print("-" * 50)
1835
- ms = duration_ms or 0
1836
- print(f"Completed in {ms}ms | Cost: ${total_cost:.4f} (saved ${savings:.4f})")
1837
1838
  else:
1838
1839
  # Extract error from various result types
1839
1840
  error_msg = getattr(result, "error", None)
@@ -1980,6 +1981,9 @@ def cmd_frameworks(args):
1980
1981
 
1981
1982
  def main():
1982
1983
  """Main CLI entry point"""
1984
+ # Configure Windows-compatible asyncio event loop policy
1985
+ setup_asyncio_policy()
1986
+
1983
1987
  parser = argparse.ArgumentParser(
1984
1988
  prog="empathy",
1985
1989
  description="Empathy - Build AI systems with 5 levels of empathy",
empathy_os/config.py CHANGED
@@ -95,6 +95,12 @@ class EmpathyConfig:
95
95
  Example:
96
96
  >>> config = EmpathyConfig.from_yaml("empathy.config.yml")
97
97
  >>> empathy = EmpathyOS(config=config)
98
+
99
+ Note:
100
+ Unknown fields in the YAML file are silently ignored.
101
+ This allows config files to contain settings for other
102
+ components (e.g., model_preferences, workflows) without
103
+ breaking EmpathyConfig loading.
98
104
  """
99
105
  if not YAML_AVAILABLE:
100
106
  raise ImportError(
@@ -104,7 +110,14 @@ class EmpathyConfig:
104
110
  with open(filepath) as f:
105
111
  data = yaml.safe_load(f)
106
112
 
107
- return cls(**data)
113
+ # Filter to only known fields (gracefully ignore unknown fields like
114
+ # 'provider', 'model_preferences', 'workflows', etc.)
115
+ from dataclasses import fields as dataclass_fields
116
+
117
+ valid_fields = {f.name for f in dataclass_fields(cls)}
118
+ filtered_data = {k: v for k, v in data.items() if k in valid_fields}
119
+
120
+ return cls(**filtered_data)
108
121
 
109
122
  @classmethod
110
123
  def from_json(cls, filepath: str) -> "EmpathyConfig":
@@ -120,11 +133,20 @@ class EmpathyConfig:
120
133
  Example:
121
134
  >>> config = EmpathyConfig.from_json("empathy.config.json")
122
135
  >>> empathy = EmpathyOS(config=config)
136
+
137
+ Note:
138
+ Unknown fields in the JSON file are silently ignored.
123
139
  """
124
140
  with open(filepath) as f:
125
141
  data = json.load(f)
126
142
 
127
- return cls(**data)
143
+ # Filter to only known fields (gracefully ignore unknown fields)
144
+ from dataclasses import fields as dataclass_fields
145
+
146
+ valid_fields = {f.name for f in dataclass_fields(cls)}
147
+ filtered_data = {k: v for k, v in data.items() if k in valid_fields}
148
+
149
+ return cls(**filtered_data)
128
150
 
129
151
  @classmethod
130
152
  def from_env(cls, prefix: str = "EMPATHY_") -> "EmpathyConfig":
@@ -343,12 +365,14 @@ class EmpathyConfig:
343
365
  )
344
366
 
345
367
  if not 0.0 <= self.pattern_confidence_threshold <= 1.0:
346
- val = self.pattern_confidence_threshold
347
- raise ValueError(f"pattern_confidence_threshold must be 0.0-1.0, got {val}")
368
+ threshold_val = self.pattern_confidence_threshold
369
+ raise ValueError(f"pattern_confidence_threshold must be 0.0-1.0, got {threshold_val}")
348
370
 
349
371
  if self.persistence_backend not in ("sqlite", "json", "none"):
350
- val = self.persistence_backend
351
- raise ValueError(f"persistence_backend must be 'sqlite', 'json', or 'none', got {val}")
372
+ backend_val = self.persistence_backend
373
+ raise ValueError(
374
+ f"persistence_backend must be 'sqlite', 'json', or 'none', got {backend_val}"
375
+ )
352
376
 
353
377
  return True
354
378
 
@@ -0,0 +1,262 @@
1
+ """
2
+ Cross-Platform Utilities for Empathy Framework
3
+
4
+ Provides platform-independent utilities for:
5
+ - File paths and directories
6
+ - File encoding
7
+ - Asyncio event loop handling
8
+ - Environment detection
9
+
10
+ Copyright 2025 Smart-AI-Memory
11
+ Licensed under Fair Source License 0.9
12
+ """
13
+
14
+ import asyncio
15
+ import os
16
+ import platform
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+
21
+ def is_windows() -> bool:
22
+ """Check if running on Windows."""
23
+ return platform.system() == "Windows"
24
+
25
+
26
+ def is_macos() -> bool:
27
+ """Check if running on macOS."""
28
+ return platform.system() == "Darwin"
29
+
30
+
31
+ def is_linux() -> bool:
32
+ """Check if running on Linux."""
33
+ return platform.system() == "Linux"
34
+
35
+
36
+ def get_default_log_dir() -> Path:
37
+ """
38
+ Get the default log directory for the current platform.
39
+
40
+ Returns:
41
+ Path: Platform-appropriate log directory
42
+ - Windows: %APPDATA%/empathy/logs
43
+ - macOS: ~/Library/Logs/empathy
44
+ - Linux: /var/log/empathy (if writable) or ~/.local/share/empathy/logs
45
+ """
46
+ if is_windows():
47
+ appdata = os.environ.get("APPDATA", os.path.expanduser("~"))
48
+ return Path(appdata) / "empathy" / "logs"
49
+ elif is_macos():
50
+ return Path.home() / "Library" / "Logs" / "empathy"
51
+ else: # Linux and other Unix
52
+ var_log = Path("/var/log/empathy")
53
+ if var_log.exists() or (var_log.parent.exists() and os.access(var_log.parent, os.W_OK)):
54
+ return var_log
55
+ # Fallback to user directory
56
+ return Path.home() / ".local" / "share" / "empathy" / "logs"
57
+
58
+
59
+ def get_default_data_dir() -> Path:
60
+ """
61
+ Get the default data directory for the current platform.
62
+
63
+ Returns:
64
+ Path: Platform-appropriate data directory
65
+ - Windows: %APPDATA%/empathy
66
+ - macOS: ~/Library/Application Support/empathy
67
+ - Linux: ~/.local/share/empathy
68
+ """
69
+ if is_windows():
70
+ appdata = os.environ.get("APPDATA", os.path.expanduser("~"))
71
+ return Path(appdata) / "empathy"
72
+ elif is_macos():
73
+ return Path.home() / "Library" / "Application Support" / "empathy"
74
+ else: # Linux and other Unix
75
+ xdg_data = os.environ.get("XDG_DATA_HOME", str(Path.home() / ".local" / "share"))
76
+ return Path(xdg_data) / "empathy"
77
+
78
+
79
+ def get_default_config_dir() -> Path:
80
+ """
81
+ Get the default configuration directory for the current platform.
82
+
83
+ Returns:
84
+ Path: Platform-appropriate config directory
85
+ - Windows: %APPDATA%/empathy
86
+ - macOS: ~/Library/Preferences/empathy
87
+ - Linux: ~/.config/empathy
88
+ """
89
+ if is_windows():
90
+ appdata = os.environ.get("APPDATA", os.path.expanduser("~"))
91
+ return Path(appdata) / "empathy"
92
+ elif is_macos():
93
+ return Path.home() / "Library" / "Preferences" / "empathy"
94
+ else: # Linux and other Unix
95
+ xdg_config = os.environ.get("XDG_CONFIG_HOME", str(Path.home() / ".config"))
96
+ return Path(xdg_config) / "empathy"
97
+
98
+
99
+ def get_default_cache_dir() -> Path:
100
+ """
101
+ Get the default cache directory for the current platform.
102
+
103
+ Returns:
104
+ Path: Platform-appropriate cache directory
105
+ - Windows: %LOCALAPPDATA%/empathy/cache
106
+ - macOS: ~/Library/Caches/empathy
107
+ - Linux: ~/.cache/empathy
108
+ """
109
+ if is_windows():
110
+ localappdata = os.environ.get(
111
+ "LOCALAPPDATA", os.environ.get("APPDATA", os.path.expanduser("~"))
112
+ )
113
+ return Path(localappdata) / "empathy" / "cache"
114
+ elif is_macos():
115
+ return Path.home() / "Library" / "Caches" / "empathy"
116
+ else: # Linux and other Unix
117
+ xdg_cache = os.environ.get("XDG_CACHE_HOME", str(Path.home() / ".cache"))
118
+ return Path(xdg_cache) / "empathy"
119
+
120
+
121
+ def setup_asyncio_policy() -> None:
122
+ """
123
+ Configure asyncio event loop policy for the current platform.
124
+
125
+ On Windows, this uses WindowsSelectorEventLoopPolicy to avoid issues
126
+ with the default ProactorEventLoop, particularly with subprocesses
127
+ and certain network operations.
128
+
129
+ This should be called early in the application startup, before
130
+ any asyncio.run() calls.
131
+ """
132
+ if is_windows():
133
+ # Windows requires WindowsSelectorEventLoopPolicy for compatibility
134
+ # with many libraries and subprocess operations
135
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
136
+
137
+
138
+ def safe_run_async(coro: Any, debug: bool = False) -> Any:
139
+ """
140
+ Run an async coroutine with platform-appropriate event loop handling.
141
+
142
+ This is a cross-platform wrapper for asyncio.run() that handles
143
+ Windows-specific event loop requirements.
144
+
145
+ Args:
146
+ coro: Coroutine to run
147
+ debug: Enable asyncio debug mode
148
+
149
+ Returns:
150
+ Result of the coroutine
151
+ """
152
+ setup_asyncio_policy()
153
+ return asyncio.run(coro, debug=debug)
154
+
155
+
156
+ def open_text_file(path: str | Path, mode: str = "r", **kwargs: Any):
157
+ """
158
+ Open a text file with UTF-8 encoding by default.
159
+
160
+ This ensures consistent encoding across platforms, as Windows
161
+ defaults to cp1252 while Unix defaults to UTF-8.
162
+
163
+ Args:
164
+ path: File path to open
165
+ mode: File mode (r, w, a, etc.)
166
+ **kwargs: Additional arguments passed to open()
167
+
168
+ Returns:
169
+ File object
170
+ """
171
+ kwargs.setdefault("encoding", "utf-8")
172
+ return open(path, mode, **kwargs)
173
+
174
+
175
+ def read_text_file(path: str | Path, encoding: str = "utf-8") -> str:
176
+ """
177
+ Read a text file with UTF-8 encoding by default.
178
+
179
+ Args:
180
+ path: File path to read
181
+ encoding: File encoding (default: utf-8)
182
+
183
+ Returns:
184
+ File contents as string
185
+ """
186
+ return Path(path).read_text(encoding=encoding)
187
+
188
+
189
+ def write_text_file(path: str | Path, content: str, encoding: str = "utf-8") -> int:
190
+ """
191
+ Write content to a text file with UTF-8 encoding by default.
192
+
193
+ Args:
194
+ path: File path to write
195
+ content: Content to write
196
+ encoding: File encoding (default: utf-8)
197
+
198
+ Returns:
199
+ Number of characters written
200
+ """
201
+ return Path(path).write_text(content, encoding=encoding)
202
+
203
+
204
+ def normalize_path(path: str | Path) -> Path:
205
+ """
206
+ Normalize a path for the current platform.
207
+
208
+ Converts forward slashes to backslashes on Windows and
209
+ resolves any relative path components.
210
+
211
+ Args:
212
+ path: Path to normalize
213
+
214
+ Returns:
215
+ Normalized Path object
216
+ """
217
+ return Path(path).resolve()
218
+
219
+
220
+ def get_temp_dir() -> Path:
221
+ """
222
+ Get the system temporary directory.
223
+
224
+ Returns:
225
+ Path to the system temp directory
226
+ """
227
+ import tempfile
228
+
229
+ return Path(tempfile.gettempdir())
230
+
231
+
232
+ def ensure_dir(path: str | Path) -> Path:
233
+ """
234
+ Ensure a directory exists, creating it if necessary.
235
+
236
+ Args:
237
+ path: Directory path to ensure
238
+
239
+ Returns:
240
+ Path object for the directory
241
+ """
242
+ dir_path = Path(path)
243
+ dir_path.mkdir(parents=True, exist_ok=True)
244
+ return dir_path
245
+
246
+
247
+ # Platform information for diagnostics
248
+ PLATFORM_INFO = {
249
+ "system": platform.system(),
250
+ "release": platform.release(),
251
+ "version": platform.version(),
252
+ "machine": platform.machine(),
253
+ "python_version": platform.python_version(),
254
+ "is_windows": is_windows(),
255
+ "is_macos": is_macos(),
256
+ "is_linux": is_linux(),
257
+ }
258
+
259
+
260
+ def get_platform_info() -> dict:
261
+ """Get platform information for diagnostics."""
262
+ return PLATFORM_INFO.copy()
@@ -347,8 +347,8 @@ def ship_workflow(
347
347
  # Import here to avoid circular imports
348
348
  try:
349
349
  from empathy_llm_toolkit.cli.sync_claude import (
350
- sync_patterns_to_claude,
351
- ) # type: ignore[attr-defined]
350
+ sync_patterns_to_claude, # type: ignore[attr-defined]
351
+ )
352
352
 
353
353
  result = sync_patterns_to_claude(
354
354
  patterns_dir=patterns_dir, output_dir=".claude/rules/empathy"
@@ -655,7 +655,9 @@ def scan_command():
655
655
  severity_icon = (
656
656
  "🔴"
657
657
  if issue.severity == "high"
658
- else "🟡" if issue.severity == "medium" else "🔵"
658
+ else "🟡"
659
+ if issue.severity == "medium"
660
+ else "🔵"
659
661
  )
660
662
  print(f" {severity_icon} Line {issue.line_number}: {issue.message}")
661
663
  if len(result.issues) > 3: