carrymem 0.1.2__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.
- carrymem-0.1.2/.description +1 -0
- carrymem-0.1.2/.github/workflows/ci.yml +480 -0
- carrymem-0.1.2/.gitignore +98 -0
- carrymem-0.1.2/.pre-commit-config.yaml +45 -0
- carrymem-0.1.2/CHANGELOG.md +137 -0
- carrymem-0.1.2/CONTRIBUTING.md +268 -0
- carrymem-0.1.2/LICENSE +21 -0
- carrymem-0.1.2/MANIFEST.in +4 -0
- carrymem-0.1.2/PKG-INFO +433 -0
- carrymem-0.1.2/README.md +375 -0
- carrymem-0.1.2/benchmarks/baseline_benchmark.py +392 -0
- carrymem-0.1.2/benchmarks/baseline_results.json +124 -0
- carrymem-0.1.2/benchmarks/classification_accuracy.py +1063 -0
- carrymem-0.1.2/benchmarks/dataset/en.json +117 -0
- carrymem-0.1.2/benchmarks/dataset/ja.json +69 -0
- carrymem-0.1.2/benchmarks/dataset/zh.json +69 -0
- carrymem-0.1.2/benchmarks/final_results.json +124 -0
- carrymem-0.1.2/benchmarks/leaderboard.py +76 -0
- carrymem-0.1.2/benchmarks/performance_benchmark.py +329 -0
- carrymem-0.1.2/benchmarks/run_benchmark.py +198 -0
- carrymem-0.1.2/ci_local_check.py +224 -0
- carrymem-0.1.2/docs/API_REFERENCE.md +302 -0
- carrymem-0.1.2/docs/ARCHITECTURE.md +448 -0
- carrymem-0.1.2/docs/QUICK_START_GUIDE.md +273 -0
- carrymem-0.1.2/docs/README.md +184 -0
- carrymem-0.1.2/docs/ROADMAP.md +110 -0
- carrymem-0.1.2/docs/USER_STORIES.md +374 -0
- carrymem-0.1.2/docs/guides/USER_PERSPECTIVE_REVIEW-CN.md +116 -0
- carrymem-0.1.2/docs/i18n/README-CN.md +372 -0
- carrymem-0.1.2/docs/i18n/README-JP.md +372 -0
- carrymem-0.1.2/docs/i18n/docs-hub-CN.md +372 -0
- carrymem-0.1.2/docs/internal/AI Agent/351/225/277/346/234/237/350/256/260/345/277/206/347/263/273/347/273/237/345/270/202/345/234/272/346/240/274/345/261/200_ima/350/204/221/345/233/276.jpeg +0 -0
- carrymem-0.1.2/docs/internal/COMPETITIVE_ANALYSIS-CN.md +210 -0
- carrymem-0.1.2/docs/internal/PRODUCT_VISION_ANALYSIS-CN.md +657 -0
- carrymem-0.1.2/docs/planning/OPTIMIZATION_ROADMAP-CN.md +257 -0
- carrymem-0.1.2/docs/planning/SEVEN_MEMBER_VOTE_CONSENSUS-CN.md +439 -0
- carrymem-0.1.2/docs/planning/V050_PHASE1_PLAN-CN.md +355 -0
- carrymem-0.1.2/docs/planning/V060_PHASE2_PLAN-CN.md +267 -0
- carrymem-0.1.2/docs/planning/V070_PHASE3_PLAN-CN.md +188 -0
- carrymem-0.1.2/docs/planning/V080_PHASE_PLAN.md +149 -0
- carrymem-0.1.2/examples/basic_usage.py +52 -0
- carrymem-0.1.2/examples/simple_assistant/README.md +86 -0
- carrymem-0.1.2/examples/simple_assistant/assistant.py +80 -0
- carrymem-0.1.2/examples/thread_safe_usage.py +57 -0
- carrymem-0.1.2/install.sh +130 -0
- carrymem-0.1.2/integrations/claude_code/mcp.json +9 -0
- carrymem-0.1.2/integrations/cursor/mcp.json +8 -0
- carrymem-0.1.2/mypy.ini +20 -0
- carrymem-0.1.2/pyproject.toml +105 -0
- carrymem-0.1.2/requirements.txt +8 -0
- carrymem-0.1.2/setup.cfg +4 -0
- carrymem-0.1.2/setup.py +82 -0
- carrymem-0.1.2/src/carrymem.egg-info/PKG-INFO +433 -0
- carrymem-0.1.2/src/carrymem.egg-info/SOURCES.txt +127 -0
- carrymem-0.1.2/src/carrymem.egg-info/dependency_links.txt +1 -0
- carrymem-0.1.2/src/carrymem.egg-info/entry_points.txt +7 -0
- carrymem-0.1.2/src/carrymem.egg-info/requires.txt +26 -0
- carrymem-0.1.2/src/carrymem.egg-info/top_level.txt +1 -0
- carrymem-0.1.2/src/memory_classification_engine/__init__.py +78 -0
- carrymem-0.1.2/src/memory_classification_engine/__main__.py +115 -0
- carrymem-0.1.2/src/memory_classification_engine/__version__.py +1 -0
- carrymem-0.1.2/src/memory_classification_engine/adapters/__init__.py +13 -0
- carrymem-0.1.2/src/memory_classification_engine/adapters/base.py +527 -0
- carrymem-0.1.2/src/memory_classification_engine/adapters/json_adapter.py +258 -0
- carrymem-0.1.2/src/memory_classification_engine/adapters/loader.py +61 -0
- carrymem-0.1.2/src/memory_classification_engine/adapters/obsidian_adapter.py +433 -0
- carrymem-0.1.2/src/memory_classification_engine/adapters/sqlite_adapter.py +1280 -0
- carrymem-0.1.2/src/memory_classification_engine/async_carrymem.py +164 -0
- carrymem-0.1.2/src/memory_classification_engine/backup.py +161 -0
- carrymem-0.1.2/src/memory_classification_engine/cache.py +121 -0
- carrymem-0.1.2/src/memory_classification_engine/carrymem.py +991 -0
- carrymem-0.1.2/src/memory_classification_engine/cli.py +1296 -0
- carrymem-0.1.2/src/memory_classification_engine/conflict_detector.py +417 -0
- carrymem-0.1.2/src/memory_classification_engine/context.py +245 -0
- carrymem-0.1.2/src/memory_classification_engine/coordinators/__init__.py +7 -0
- carrymem-0.1.2/src/memory_classification_engine/coordinators/classification_pipeline.py +257 -0
- carrymem-0.1.2/src/memory_classification_engine/engine.py +195 -0
- carrymem-0.1.2/src/memory_classification_engine/exceptions.py +51 -0
- carrymem-0.1.2/src/memory_classification_engine/integration/__init__.py +8 -0
- carrymem-0.1.2/src/memory_classification_engine/integration/layer2_mcp/__init__.py +21 -0
- carrymem-0.1.2/src/memory_classification_engine/integration/layer2_mcp/__main__.py +18 -0
- carrymem-0.1.2/src/memory_classification_engine/integration/layer2_mcp/handlers.py +396 -0
- carrymem-0.1.2/src/memory_classification_engine/integration/layer2_mcp/http_server.py +249 -0
- carrymem-0.1.2/src/memory_classification_engine/integration/layer2_mcp/server.py +296 -0
- carrymem-0.1.2/src/memory_classification_engine/integration/layer2_mcp/tools.py +501 -0
- carrymem-0.1.2/src/memory_classification_engine/layers/__init__.py +1 -0
- carrymem-0.1.2/src/memory_classification_engine/layers/feedback_loop.py +377 -0
- carrymem-0.1.2/src/memory_classification_engine/layers/pattern_analyzer.py +1434 -0
- carrymem-0.1.2/src/memory_classification_engine/layers/rule_matcher.py +100 -0
- carrymem-0.1.2/src/memory_classification_engine/layers/semantic_classifier.py +100 -0
- carrymem-0.1.2/src/memory_classification_engine/merge.py +124 -0
- carrymem-0.1.2/src/memory_classification_engine/py.typed +0 -0
- carrymem-0.1.2/src/memory_classification_engine/quality_scorer.py +414 -0
- carrymem-0.1.2/src/memory_classification_engine/scoring.py +84 -0
- carrymem-0.1.2/src/memory_classification_engine/security/__init__.py +33 -0
- carrymem-0.1.2/src/memory_classification_engine/security/audit.py +161 -0
- carrymem-0.1.2/src/memory_classification_engine/security/encryption.py +218 -0
- carrymem-0.1.2/src/memory_classification_engine/security/input_validator.py +494 -0
- carrymem-0.1.2/src/memory_classification_engine/semantic/__init__.py +16 -0
- carrymem-0.1.2/src/memory_classification_engine/semantic/data/__init__.py +15 -0
- carrymem-0.1.2/src/memory_classification_engine/semantic/data/synonyms_daily_en.yaml +383 -0
- carrymem-0.1.2/src/memory_classification_engine/semantic/data/synonyms_jp.yaml +501 -0
- carrymem-0.1.2/src/memory_classification_engine/semantic/data/synonyms_technical_cn.yaml +454 -0
- carrymem-0.1.2/src/memory_classification_engine/semantic/expander.py +320 -0
- carrymem-0.1.2/src/memory_classification_engine/semantic/merger.py +245 -0
- carrymem-0.1.2/src/memory_classification_engine/tui.py +320 -0
- carrymem-0.1.2/src/memory_classification_engine/utils/__init__.py +1 -0
- carrymem-0.1.2/src/memory_classification_engine/utils/config.py +93 -0
- carrymem-0.1.2/src/memory_classification_engine/utils/confirmation.py +39 -0
- carrymem-0.1.2/src/memory_classification_engine/utils/constants.py +68 -0
- carrymem-0.1.2/src/memory_classification_engine/utils/helpers.py +146 -0
- carrymem-0.1.2/src/memory_classification_engine/utils/language.py +293 -0
- carrymem-0.1.2/src/memory_classification_engine/utils/logger.py +55 -0
- carrymem-0.1.2/src/memory_classification_engine/utils/performance.py +303 -0
- carrymem-0.1.2/src/memory_classification_engine/utils/validators.py +261 -0
- carrymem-0.1.2/tests/test_async_carrymem.py +161 -0
- carrymem-0.1.2/tests/test_cache.py +202 -0
- carrymem-0.1.2/tests/test_carrymem.py +1537 -0
- carrymem-0.1.2/tests/test_environment.py +191 -0
- carrymem-0.1.2/tests/test_execution_context.py +142 -0
- carrymem-0.1.2/tests/test_helpers.py +278 -0
- carrymem-0.1.2/tests/test_mce_recall.py +43 -0
- carrymem-0.1.2/tests/test_scoring.py +148 -0
- carrymem-0.1.2/tests/test_security.py +106 -0
- carrymem-0.1.2/tests/test_v050.py +403 -0
- carrymem-0.1.2/tests/test_v060.py +317 -0
- carrymem-0.1.2/tests/test_v070.py +283 -0
- carrymem-0.1.2/tests/test_v080_cli.py +541 -0
- carrymem-0.1.2/tests/test_v080_quality.py +116 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
CarryMem - Portable AI Memory Layer | 便携式 AI 记忆层 | ポータブル AI メモリレイヤー
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
name: CI Quality Gates
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [new-main]
|
|
6
|
+
tags: ['v*']
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [new-main]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
reason:
|
|
12
|
+
description: 'Reason for manual run'
|
|
13
|
+
required: false
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
# ──────────────────────────────────────────────
|
|
21
|
+
# GATE 1: Code Quality (lint, format, type-check)
|
|
22
|
+
# ──────────────────────────────────────────────
|
|
23
|
+
quality:
|
|
24
|
+
name: Code Quality
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
- name: Set up Python
|
|
30
|
+
uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: '3.11'
|
|
33
|
+
|
|
34
|
+
- name: Install dependencies
|
|
35
|
+
run: |
|
|
36
|
+
python -m pip install --upgrade pip
|
|
37
|
+
pip install -e ".[dev]" flake8 black isort mypy
|
|
38
|
+
|
|
39
|
+
- name: Lint — fatal errors only
|
|
40
|
+
run: |
|
|
41
|
+
flake8 src/ tests/ \
|
|
42
|
+
--count --select=E9,F63,F7,F82 \
|
|
43
|
+
--show-source --statistics
|
|
44
|
+
|
|
45
|
+
- name: Lint — warnings (non-blocking)
|
|
46
|
+
run: |
|
|
47
|
+
flake8 src/ tests/ \
|
|
48
|
+
--count --exit-zero \
|
|
49
|
+
--max-complexity=12 \
|
|
50
|
+
--max-line-length=127 \
|
|
51
|
+
--statistics
|
|
52
|
+
|
|
53
|
+
- name: Format check — Black
|
|
54
|
+
run: |
|
|
55
|
+
black --check --diff --color src/ tests/
|
|
56
|
+
|
|
57
|
+
- name: Format check — isort
|
|
58
|
+
run: |
|
|
59
|
+
isort --check-only --diff --color src/ tests/
|
|
60
|
+
|
|
61
|
+
- name: Type check — MyPy
|
|
62
|
+
run: |
|
|
63
|
+
mypy src/ --ignore-missing-imports --no-error-summary || true
|
|
64
|
+
|
|
65
|
+
# ──────────────────────────────────────────────
|
|
66
|
+
# GATE 2: Internationalization (i18n) Compliance
|
|
67
|
+
# ──────────────────────────────────────────────
|
|
68
|
+
i18n:
|
|
69
|
+
name: i18n Compliance
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v4
|
|
73
|
+
|
|
74
|
+
- name: Set up Python
|
|
75
|
+
uses: actions/setup-python@v5
|
|
76
|
+
with:
|
|
77
|
+
python-version: '3.11'
|
|
78
|
+
|
|
79
|
+
- name: Check for Chinese comments in source code
|
|
80
|
+
run: |
|
|
81
|
+
python3 << 'EOF'
|
|
82
|
+
import os, re, sys
|
|
83
|
+
|
|
84
|
+
errors = []
|
|
85
|
+
# Files that are allowed to contain Chinese (i18n data)
|
|
86
|
+
i18n_whitelist = [
|
|
87
|
+
'language.py', # Multi-language keyword lists
|
|
88
|
+
'context.py', # Prompt templates for zh/ja locales
|
|
89
|
+
'pattern_analyzer.py',# CJK regex patterns & feedback phrases
|
|
90
|
+
'tools.py', # Schema label_zh / zh_name fields
|
|
91
|
+
'__main__.py', # Demo/test data with Chinese examples
|
|
92
|
+
'__init__.py', # Docstring examples showing multilingual recall
|
|
93
|
+
'feedback_loop.py', # Chinese indicator keywords for classification
|
|
94
|
+
'confirmation.py', # Chinese confirmation regex patterns
|
|
95
|
+
'expander.py', # Docstring examples for cross-language expansion
|
|
96
|
+
'merger.py', # Docstring examples for cross-language merging
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
for root, dirs, files in os.walk('src'):
|
|
100
|
+
dirs[:] = [d for d in dirs if d not in ('__pycache__', '.pytest_cache')]
|
|
101
|
+
for f in files:
|
|
102
|
+
if not f.endswith('.py'):
|
|
103
|
+
continue
|
|
104
|
+
filepath = os.path.join(root, f)
|
|
105
|
+
relpath = os.path.relpath(filepath)
|
|
106
|
+
|
|
107
|
+
# Skip whitelisted files
|
|
108
|
+
if any(w in relpath for w in i18n_whitelist):
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
with open(filepath, 'r', encoding='utf-8') as fh:
|
|
113
|
+
for lineno, line in enumerate(fh, 1):
|
|
114
|
+
# Skip strings inside i18n data structures
|
|
115
|
+
stripped = line.strip()
|
|
116
|
+
if (stripped.startswith("'zh-cn:") or
|
|
117
|
+
stripped.startswith('"zh-cn:') or
|
|
118
|
+
stripped.startswith('# i18n') or
|
|
119
|
+
'label_zh=' in stripped or
|
|
120
|
+
'zh_name=' in stripped):
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
# Check for Chinese characters in comments and code
|
|
124
|
+
if re.search(r'[\u4e00-\u9fff]', line):
|
|
125
|
+
# Allow Chinese in docstrings that are clearly i18n data
|
|
126
|
+
if '"""' in line or "'''" in line:
|
|
127
|
+
continue
|
|
128
|
+
errors.append(f"{relpath}:{lineno}: {line.rstrip()[:80]}")
|
|
129
|
+
except Exception as e:
|
|
130
|
+
print(f"Warning: Could not read {filepath}: {e}")
|
|
131
|
+
|
|
132
|
+
if errors:
|
|
133
|
+
print(f"\n❌ Found {len(errors)} lines with Chinese characters outside i18n data:")
|
|
134
|
+
for err in errors[:30]:
|
|
135
|
+
print(f" {err}")
|
|
136
|
+
if len(errors) > 30:
|
|
137
|
+
print(f" ... and {len(errors)-30} more")
|
|
138
|
+
sys.exit(1)
|
|
139
|
+
else:
|
|
140
|
+
print("✅ No Chinese characters found in non-i18n source code.")
|
|
141
|
+
EOF
|
|
142
|
+
|
|
143
|
+
- name: Check for mangled comments (# Comment in Chinese removed...)
|
|
144
|
+
run: |
|
|
145
|
+
if grep -r "# Comment in Chinese removed" src/ --include="*.py"; then
|
|
146
|
+
echo "::error::Found mangled '# Comment in Chinese removed' remnants"
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
echo "✅ No mangled comments found."
|
|
150
|
+
|
|
151
|
+
- name: Verify English display labels in helpers.py
|
|
152
|
+
run: |
|
|
153
|
+
python3 << 'EOF'
|
|
154
|
+
import ast, sys
|
|
155
|
+
|
|
156
|
+
with open('src/memory_classification_engine/utils/helpers.py') as f:
|
|
157
|
+
tree = ast.parse(f.read())
|
|
158
|
+
|
|
159
|
+
chinese_found = []
|
|
160
|
+
for node in ast.walk(tree):
|
|
161
|
+
if isinstance(node, ast.Assign):
|
|
162
|
+
for target in node.targets:
|
|
163
|
+
if isinstance(target, ast.Name) and target.id == 'MEMORY_TYPES':
|
|
164
|
+
for kv in node.value.keys:
|
|
165
|
+
if isinstance(kv.value, ast.Constant) and re.search(r'[\u4e00-\u9fff]', str(kv.value.s)):
|
|
166
|
+
chinese_found.append(f" MEMORY_TYPES[{kv.value.s}]")
|
|
167
|
+
if isinstance(target, ast.Name) and target.id == 'MEMORY_TIERS':
|
|
168
|
+
for kv in node.value.keys:
|
|
169
|
+
if isinstance(kv.value, ast.Constant) and re.search(r'[\u4e00-\u9fff]', str(kv.value.s)):
|
|
170
|
+
chinese_found.append(f" MEMORY_TIERS[{kv.value.s}]")
|
|
171
|
+
|
|
172
|
+
if chinese_found:
|
|
173
|
+
print(f"❌ Chinese labels found in helpers.py:")
|
|
174
|
+
for c in chinese_found:
|
|
175
|
+
print(c)
|
|
176
|
+
sys.exit(1)
|
|
177
|
+
else:
|
|
178
|
+
print("✅ All display labels in helpers.py are English.")
|
|
179
|
+
EOF
|
|
180
|
+
|
|
181
|
+
# ──────────────────────────────────────────────
|
|
182
|
+
# GATE 3: Unit Tests (matrix: Python × OS)
|
|
183
|
+
# ──────────────────────────────────────────────
|
|
184
|
+
test:
|
|
185
|
+
name: Tests (Python ${{ matrix.python-version }} on ${{ matrix.os }})
|
|
186
|
+
needs: [quality, i18n]
|
|
187
|
+
runs-on: ${{ matrix.os }}
|
|
188
|
+
strategy:
|
|
189
|
+
fail-fast: false
|
|
190
|
+
matrix:
|
|
191
|
+
python-version: ['3.9', '3.10', '3.11', '3.12']
|
|
192
|
+
os: [ubuntu-latest]
|
|
193
|
+
|
|
194
|
+
steps:
|
|
195
|
+
- uses: actions/checkout@v4
|
|
196
|
+
|
|
197
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
198
|
+
uses: actions/setup-python@v5
|
|
199
|
+
with:
|
|
200
|
+
python-version: ${{ matrix.python-version }}
|
|
201
|
+
|
|
202
|
+
- name: Cache pip
|
|
203
|
+
uses: actions/cache@v4
|
|
204
|
+
with:
|
|
205
|
+
path: ~/.cache/pip
|
|
206
|
+
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('setup.py', 'requirements.txt') }}
|
|
207
|
+
|
|
208
|
+
- name: Install dependencies
|
|
209
|
+
run: |
|
|
210
|
+
python -m pip install --upgrade pip
|
|
211
|
+
pip install -e ".[dev]"
|
|
212
|
+
|
|
213
|
+
- name: Run tests
|
|
214
|
+
run: |
|
|
215
|
+
python -m pytest tests/ \
|
|
216
|
+
--cov=memory_classification_engine \
|
|
217
|
+
--cov-report=xml \
|
|
218
|
+
--cov-report=term-missing \
|
|
219
|
+
-v \
|
|
220
|
+
--tb=short \
|
|
221
|
+
2>&1 | tee pytest_output.txt
|
|
222
|
+
|
|
223
|
+
- name: Enforce minimum coverage
|
|
224
|
+
run: |
|
|
225
|
+
python3 << 'EOF'
|
|
226
|
+
import re, sys
|
|
227
|
+
|
|
228
|
+
with open('pytest_output.txt') as f:
|
|
229
|
+
content = f.read()
|
|
230
|
+
|
|
231
|
+
match = re.search(r'TOTAL\s+\d+\s+\d+\s+(\d+)%', content)
|
|
232
|
+
if match:
|
|
233
|
+
coverage = int(match.group(1))
|
|
234
|
+
print(f"\n📊 Coverage: {coverage}%")
|
|
235
|
+
if coverage < 55:
|
|
236
|
+
print(f"❌ Coverage {coverage}% is below minimum threshold of 55%")
|
|
237
|
+
sys.exit(1)
|
|
238
|
+
print(f"✅ Coverage {coverage}% meets minimum threshold of 55%")
|
|
239
|
+
else:
|
|
240
|
+
print("⚠️ Could not parse coverage from output")
|
|
241
|
+
EOF
|
|
242
|
+
|
|
243
|
+
- name: Upload coverage to Codecov
|
|
244
|
+
if: matrix.python-version == '3.11' && matrix.os == 'ubuntu-latest'
|
|
245
|
+
uses: codecov/codecov-action@v4
|
|
246
|
+
with:
|
|
247
|
+
file: ./coverage.xml
|
|
248
|
+
fail_ci_if_error: false
|
|
249
|
+
|
|
250
|
+
- name: Upload test results
|
|
251
|
+
uses: actions/upload-artifact@v4
|
|
252
|
+
if: always()
|
|
253
|
+
with:
|
|
254
|
+
name: test-results-${{ matrix.os }}-py${{ matrix.python-version }}
|
|
255
|
+
path: |
|
|
256
|
+
./coverage.xml
|
|
257
|
+
pytest_output.txt
|
|
258
|
+
|
|
259
|
+
# ──────────────────────────────────────────────
|
|
260
|
+
# GATE 4: Package Build + Fresh Install Verification
|
|
261
|
+
# ──────────────────────────────────────────────
|
|
262
|
+
build-and-install:
|
|
263
|
+
name: Build & Install Test
|
|
264
|
+
needs: [test]
|
|
265
|
+
runs-on: ubuntu-latest
|
|
266
|
+
steps:
|
|
267
|
+
- uses: actions/checkout@v4
|
|
268
|
+
|
|
269
|
+
- name: Set up Python
|
|
270
|
+
uses: actions/setup-python@v5
|
|
271
|
+
with:
|
|
272
|
+
python-version: '3.11'
|
|
273
|
+
|
|
274
|
+
- name: Install build tools
|
|
275
|
+
run: |
|
|
276
|
+
python -m pip install --upgrade pip
|
|
277
|
+
pip install build twine
|
|
278
|
+
|
|
279
|
+
- name: Build sdist and wheel
|
|
280
|
+
run: |
|
|
281
|
+
rm -rf dist/ build/ *.egg-info/
|
|
282
|
+
python -m build
|
|
283
|
+
|
|
284
|
+
- name: List build artifacts
|
|
285
|
+
run: |
|
|
286
|
+
echo "=== Build Artifacts ==="
|
|
287
|
+
ls -lh dist/
|
|
288
|
+
echo ""
|
|
289
|
+
echo "=== Wheel Contents ==="
|
|
290
|
+
unzip -l dist/carrymem-*.whl | head -40
|
|
291
|
+
|
|
292
|
+
- name: twine check
|
|
293
|
+
run: |
|
|
294
|
+
twine check dist/*
|
|
295
|
+
|
|
296
|
+
- name: "Fresh venv install from wheel"
|
|
297
|
+
run: |
|
|
298
|
+
python -m venv /tmp/carrymem_test_venv
|
|
299
|
+
source /tmp/carrymem_test_venv/bin/activate
|
|
300
|
+
|
|
301
|
+
echo "=== Installing from wheel ==="
|
|
302
|
+
WHEEL=$(ls dist/carrymem-*.whl 2>/dev/null | head -1)
|
|
303
|
+
pip install "$WHEEL"
|
|
304
|
+
|
|
305
|
+
echo ""
|
|
306
|
+
echo "=== Verifying package metadata ==="
|
|
307
|
+
python -c "import memory_classification_engine; print('✅ Package importable')"
|
|
308
|
+
python -c "from memory_classification_engine.utils.helpers import MEMORY_TYPES; print('✅ utils importable')"
|
|
309
|
+
python -c "from memory_classification_engine.layers.pattern_analyzer import PatternAnalyzer; print('✅ layers importable')"
|
|
310
|
+
python -c "from memory_classification_engine.security.input_validator import InputValidator; print('✅ security importable')"
|
|
311
|
+
python -c "from memory_classification_engine.adapters.sqlite_adapter import SQLiteAdapter; print('✅ adapters importable')"
|
|
312
|
+
python -c "from memory_classification_engine.cli import main; print('✅ cli importable')"
|
|
313
|
+
|
|
314
|
+
echo ""
|
|
315
|
+
echo "=== Version check ==="
|
|
316
|
+
carrymem version
|
|
317
|
+
|
|
318
|
+
echo ""
|
|
319
|
+
echo "=== Init check ==="
|
|
320
|
+
carrymem init
|
|
321
|
+
|
|
322
|
+
echo ""
|
|
323
|
+
echo "=== Add + Search smoke test ==="
|
|
324
|
+
carrymem add "CI test: I prefer dark mode"
|
|
325
|
+
carrymem search "dark"
|
|
326
|
+
|
|
327
|
+
echo ""
|
|
328
|
+
echo "=== Whoami smoke test ==="
|
|
329
|
+
carrymem whoami
|
|
330
|
+
|
|
331
|
+
echo ""
|
|
332
|
+
echo "=== Doctor smoke test ==="
|
|
333
|
+
carrymem doctor
|
|
334
|
+
|
|
335
|
+
echo ""
|
|
336
|
+
echo "✅ ALL FRESH INSTALL CHECKS PASSED"
|
|
337
|
+
|
|
338
|
+
- name: Check all __init__.py exist in wheel
|
|
339
|
+
run: |
|
|
340
|
+
echo "=== Verifying __init__.py in wheel ==="
|
|
341
|
+
missing=$(unzip -l dist/carrymem-*.whl | grep "/__init__.py$" || true)
|
|
342
|
+
if [ -z "$missing" ]; then
|
|
343
|
+
echo "❌ No __init__.py files found in wheel!"
|
|
344
|
+
unzip -l dist/carrymem-*.whl | grep "/" | head -20
|
|
345
|
+
exit 1
|
|
346
|
+
fi
|
|
347
|
+
expected_dirs=("utils/" "layers/" "adapters/" "security/" "semantic/")
|
|
348
|
+
for dir in "${expected_dirs[@]}"; do
|
|
349
|
+
if ! unzip -l dist/carrymem-*.whl | grep -q "$dir/__init__.py$"; then
|
|
350
|
+
echo "❌ Missing __init__.py in $dir"
|
|
351
|
+
exit 1
|
|
352
|
+
fi
|
|
353
|
+
echo "✅ $dir has __init__.py"
|
|
354
|
+
done
|
|
355
|
+
|
|
356
|
+
- name: Upload build artifacts
|
|
357
|
+
uses: actions/upload-artifact@v4
|
|
358
|
+
with:
|
|
359
|
+
name: build-artifacts
|
|
360
|
+
path: dist/
|
|
361
|
+
|
|
362
|
+
# ──────────────────────────────────────────────
|
|
363
|
+
# GATE 5: Security Scan
|
|
364
|
+
# ──────────────────────────────────────────────
|
|
365
|
+
security:
|
|
366
|
+
name: Security Scan
|
|
367
|
+
runs-on: ubuntu-latest
|
|
368
|
+
steps:
|
|
369
|
+
- uses: actions/checkout@v4
|
|
370
|
+
|
|
371
|
+
- name: Set up Python
|
|
372
|
+
uses: actions/setup-python@v5
|
|
373
|
+
with:
|
|
374
|
+
python-version: '3.11'
|
|
375
|
+
|
|
376
|
+
- name: Check for secrets / hardcoded keys
|
|
377
|
+
run: |
|
|
378
|
+
echo "=== Checking for potential secrets ==="
|
|
379
|
+
# Common secret patterns (not exhaustive, but catches obvious cases)
|
|
380
|
+
if grep -rnE "(api_key|secret_key|password|token)\s*=\s*['\"][^']{8,}" src/ --include="*.py" | \
|
|
381
|
+
grep -v "test_" | \
|
|
382
|
+
grep -v "# " | \
|
|
383
|
+
grep -v "os.environ" | \
|
|
384
|
+
grep -v "getenv"; then
|
|
385
|
+
echo "::warning::Potential hardcoded secret detected above"
|
|
386
|
+
else
|
|
387
|
+
echo "✅ No obvious hardcoded secrets found"
|
|
388
|
+
fi
|
|
389
|
+
|
|
390
|
+
- name: "Check for bare except clauses"
|
|
391
|
+
run: |
|
|
392
|
+
if grep -rn "except:" src/ --include="*.py" | grep -v "except Exception" | grep -v "except ("; then
|
|
393
|
+
echo "::error::Found bare except: clause (should use except Exception)"
|
|
394
|
+
grep -rn "except:" src/ --include="*.py" | grep -v "except Exception" | grep -v "except ("
|
|
395
|
+
exit 1
|
|
396
|
+
fi
|
|
397
|
+
echo "✅ No bare except: clauses found"
|
|
398
|
+
|
|
399
|
+
- name: Check for SQL injection risk patterns
|
|
400
|
+
run: |
|
|
401
|
+
if grep -rnE '(f".*SELECT|f".*INSERT|f".*UPDATE|f".*DELETE).*(query|sql)' src/ --include="*.py" | \
|
|
402
|
+
grep -v "validate_query" | \
|
|
403
|
+
grep -v "_sanitize" | \
|
|
404
|
+
grep -v "# safe"; then
|
|
405
|
+
echo "::warning::Potential raw SQL query construction (review manually)"
|
|
406
|
+
else
|
|
407
|
+
echo "✅ No obvious SQL injection patterns"
|
|
408
|
+
fi
|
|
409
|
+
|
|
410
|
+
# ──────────────────────────────────────────────
|
|
411
|
+
# GATE 6: Documentation Build Check
|
|
412
|
+
# ──────────────────────────────────────────────
|
|
413
|
+
docs:
|
|
414
|
+
name: Documentation Check
|
|
415
|
+
runs-on: ubuntu-latest
|
|
416
|
+
steps:
|
|
417
|
+
- uses: actions/checkout@v4
|
|
418
|
+
|
|
419
|
+
- name: Check README links
|
|
420
|
+
run: |
|
|
421
|
+
pip install mdformat-gfm 2>/dev/null || true
|
|
422
|
+
echo "=== Checking required docs exist ==="
|
|
423
|
+
for doc in README.md CONTRIBUTING.md LICENSE CHANGELOG.md docs/README.md; do
|
|
424
|
+
if [ ! -f "$doc" ]; then
|
|
425
|
+
echo "::error::Missing required document: $doc"
|
|
426
|
+
exit 1
|
|
427
|
+
fi
|
|
428
|
+
echo "✅ $doc exists"
|
|
429
|
+
done
|
|
430
|
+
|
|
431
|
+
echo ""
|
|
432
|
+
echo "=== Checking i18n docs exist ==="
|
|
433
|
+
for doc in docs/i18n/README-CN.md docs/i18n/README-JP.md; do
|
|
434
|
+
if [ ! -f "$doc" ]; then
|
|
435
|
+
echo "::error::Missing i18n document: $doc"
|
|
436
|
+
exit 1
|
|
437
|
+
fi
|
|
438
|
+
echo "✅ $doc exists"
|
|
439
|
+
done
|
|
440
|
+
|
|
441
|
+
- name: Verify version consistency
|
|
442
|
+
run: |
|
|
443
|
+
python3 << 'EOF'
|
|
444
|
+
import re, sys
|
|
445
|
+
|
|
446
|
+
# Read version from source
|
|
447
|
+
with open('src/memory_classification_engine/__version__.py') as f:
|
|
448
|
+
src_ver = re.search(r'__version__\s*=\s*["\']([^"\']+)["\']', f.read()).group(1)
|
|
449
|
+
|
|
450
|
+
# Read version from setup.py
|
|
451
|
+
with open('setup.py') as f:
|
|
452
|
+
content = f.read()
|
|
453
|
+
setup_ver = None
|
|
454
|
+
if '__version__' in content:
|
|
455
|
+
pass # Uses dynamic version from source
|
|
456
|
+
else:
|
|
457
|
+
m = re.search(r'version=["\']([^"\']+)["\']', content)
|
|
458
|
+
if m:
|
|
459
|
+
setup_ver = m.group(1)
|
|
460
|
+
|
|
461
|
+
# Read version from README
|
|
462
|
+
with open('README.md') as f:
|
|
463
|
+
readme_ver = None
|
|
464
|
+
for line in f:
|
|
465
|
+
if 'v0.' in line and ('version' in line.lower() or '**v' in line):
|
|
466
|
+
m = re.search(r'v(\d+\.\d+\.\d+)', line)
|
|
467
|
+
if m:
|
|
468
|
+
readme_ver = m.group(1)
|
|
469
|
+
break
|
|
470
|
+
|
|
471
|
+
print(f"Source version: {src_ver}")
|
|
472
|
+
print(f"Setup.py version: {setup_ver or '(dynamic)'}")
|
|
473
|
+
print(f"README version: {readme_ver or '(not found)'}")
|
|
474
|
+
|
|
475
|
+
versions = [v for v in [src_ver, setup_ver, readme_ver] if v]
|
|
476
|
+
if len(set(versions)) > 1:
|
|
477
|
+
print(f"\n❌ Version inconsistency detected!")
|
|
478
|
+
sys.exit(1)
|
|
479
|
+
print(f"\n✅ Versions consistent: {src_ver}")
|
|
480
|
+
EOF
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Cache directories
|
|
2
|
+
.cache/
|
|
3
|
+
.test_cache/
|
|
4
|
+
.test_smart_cache/
|
|
5
|
+
.benchmarks/
|
|
6
|
+
|
|
7
|
+
# Model downloads (large, ~1GB+)
|
|
8
|
+
models/
|
|
9
|
+
|
|
10
|
+
# Profiling
|
|
11
|
+
profile.out
|
|
12
|
+
|
|
13
|
+
# Backups (contain user data)
|
|
14
|
+
*.bak
|
|
15
|
+
backup/
|
|
16
|
+
|
|
17
|
+
# Virtual environments
|
|
18
|
+
.venv/
|
|
19
|
+
env/
|
|
20
|
+
|
|
21
|
+
# IDE config
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
24
|
+
|
|
25
|
+
# Logs
|
|
26
|
+
*.log
|
|
27
|
+
logs/
|
|
28
|
+
src/logs/
|
|
29
|
+
|
|
30
|
+
# Test artifacts
|
|
31
|
+
.pytest_cache/
|
|
32
|
+
htmlcov/
|
|
33
|
+
.coverage
|
|
34
|
+
coverage.xml
|
|
35
|
+
test_results*.log
|
|
36
|
+
|
|
37
|
+
# OS files
|
|
38
|
+
.DS_Store
|
|
39
|
+
Thumbs.db
|
|
40
|
+
|
|
41
|
+
# Data directories (user data)
|
|
42
|
+
data/
|
|
43
|
+
src/data/
|
|
44
|
+
mce-mcp/data/
|
|
45
|
+
|
|
46
|
+
# Python compiled files
|
|
47
|
+
__pycache__/
|
|
48
|
+
*.pyc
|
|
49
|
+
|
|
50
|
+
# Egg info / build artifacts
|
|
51
|
+
*.egg-info/
|
|
52
|
+
dist/
|
|
53
|
+
build/
|
|
54
|
+
*.egg
|
|
55
|
+
|
|
56
|
+
# Sensitive: encryption keys
|
|
57
|
+
config/encryption_keys.json
|
|
58
|
+
|
|
59
|
+
# IDE-specific
|
|
60
|
+
.trae/
|
|
61
|
+
|
|
62
|
+
# Type checking cache
|
|
63
|
+
.mypy_cache/
|
|
64
|
+
|
|
65
|
+
# Development debug files (temporary)
|
|
66
|
+
debug_*.py
|
|
67
|
+
/test_*.py
|
|
68
|
+
verify_*.py
|
|
69
|
+
monkey_patch.py
|
|
70
|
+
simplest_test.py
|
|
71
|
+
ultimate_test.py
|
|
72
|
+
find_crash.py
|
|
73
|
+
inspect_benchmark.py
|
|
74
|
+
final_pipeline_test.py
|
|
75
|
+
run_bench_reload.py
|
|
76
|
+
force_reload.py
|
|
77
|
+
final_verify.py
|
|
78
|
+
|
|
79
|
+
# Temporary review/walkthrough reports
|
|
80
|
+
CARRYMEM_PROJECT_REVIEW_*.md
|
|
81
|
+
CODE_WALKTHROUGH_ISSUES_*.md
|
|
82
|
+
|
|
83
|
+
# Benchmark results
|
|
84
|
+
benchmarks/*.json
|
|
85
|
+
mce_bench_*.json
|
|
86
|
+
|
|
87
|
+
# Demo recordings
|
|
88
|
+
demo/*.cast
|
|
89
|
+
demo/demo.gif
|
|
90
|
+
|
|
91
|
+
# Deprecated MCP server
|
|
92
|
+
mce-mcp/mce_mcp_server/__pycache__/
|
|
93
|
+
|
|
94
|
+
# Image assets (kept locally, not in repo)
|
|
95
|
+
*.jpeg
|
|
96
|
+
*.jpg
|
|
97
|
+
!docs/**/*.jpeg
|
|
98
|
+
!docs/**/*.jpg
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Pre-commit hooks for CarryMem
|
|
2
|
+
# Install: pre-commit install
|
|
3
|
+
# Run manually: pre-commit run --all-files
|
|
4
|
+
|
|
5
|
+
repos:
|
|
6
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
7
|
+
rev: v4.5.0
|
|
8
|
+
hooks:
|
|
9
|
+
- id: trailing-whitespace
|
|
10
|
+
- id: end-of-file-fixer
|
|
11
|
+
- id: check-yaml
|
|
12
|
+
- id: check-added-large-files
|
|
13
|
+
args: ['--maxkb=1000']
|
|
14
|
+
- id: check-json
|
|
15
|
+
- id: check-toml
|
|
16
|
+
- id: check-merge-conflict
|
|
17
|
+
- id: debug-statements
|
|
18
|
+
- id: mixed-line-ending
|
|
19
|
+
|
|
20
|
+
- repo: https://github.com/psf/black
|
|
21
|
+
rev: 23.12.1
|
|
22
|
+
hooks:
|
|
23
|
+
- id: black
|
|
24
|
+
language_version: python3.9
|
|
25
|
+
args: ['--line-length=100']
|
|
26
|
+
|
|
27
|
+
- repo: https://github.com/pycqa/isort
|
|
28
|
+
rev: 5.13.2
|
|
29
|
+
hooks:
|
|
30
|
+
- id: isort
|
|
31
|
+
args: ['--profile=black', '--line-length=100']
|
|
32
|
+
|
|
33
|
+
- repo: https://github.com/pycqa/flake8
|
|
34
|
+
rev: 7.0.0
|
|
35
|
+
hooks:
|
|
36
|
+
- id: flake8
|
|
37
|
+
args: ['--max-line-length=100', '--extend-ignore=E203,W503']
|
|
38
|
+
additional_dependencies: [flake8-docstrings]
|
|
39
|
+
|
|
40
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
41
|
+
rev: v1.8.0
|
|
42
|
+
hooks:
|
|
43
|
+
- id: mypy
|
|
44
|
+
additional_dependencies: [types-PyYAML]
|
|
45
|
+
args: ['--ignore-missing-imports', '--no-strict-optional']
|