@memorax/memorax-code 0.1.11 → 0.1.12
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.
- package/README.md +7 -4
- package/bin/memorax-code-setup.mjs +136 -34
- package/bin/memorax-code-trae.mjs +4 -0
- package/docs/configuration.md +106 -52
- package/docs/troubleshooting.md +45 -6
- package/lib/memorax-code-backend/dist/clients/claude/transcript-turn.js +2 -2
- package/lib/memorax-code-backend/dist/clients/codex/rollout-turn.js +1 -1
- package/lib/memorax-code-backend/dist/clients/trae/lifecycle.js +53 -0
- package/lib/memorax-code-backend/dist/clients/trae/memory-hook-runtime.js +302 -0
- package/lib/memorax-code-backend/dist/clients/trae/turn-id.js +16 -0
- package/lib/memorax-code-backend/dist/config/memorax-code.js +14 -1
- package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +22 -4
- package/lib/memorax-code-backend/dist/lifecycle/active-clients.js +3 -0
- package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +14 -2
- package/lib/memorax-code-backend/dist/lifecycle/client-selection.js +5 -3
- package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +62 -9
- package/lib/memorax-code-backend/dist/memory/hook-command.js +34 -2
- package/lib/memorax-code-backend/dist/memory/reminder-trace-recorder.js +5 -1
- package/lib/memorax-code-backend/dist/memory/service.js +12 -0
- package/lib/memorax-code-backend/dist/trace/config.js +12 -1
- package/lib/memorax-code-backend/dist/trace/context.js +26 -1
- package/lib/memorax-code-backend/package.json +1 -1
- package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +3 -1
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +3 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-codebuddy-adapter/.codebuddy-plugin/plugin.json +1 -1
- package/lib/memorax-code-codebuddy-adapter/package.json +1 -1
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/SKILL.md +3 -1
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
- package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-codex-adapter/package.json +1 -1
- package/lib/memorax-code-codex-adapter/skills/memorax-code/SKILL.md +3 -1
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-dsh-adapter/package.json +1 -1
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/SKILL.md +3 -1
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-opencode-adapter/package.json +1 -1
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/SKILL.md +3 -1
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-trae-adapter/hooks/runtime-hook.mjs +340 -0
- package/lib/memorax-code-trae-adapter/package.json +9 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/SKILL.md +87 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/agents/claude.yaml +10 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/agents/openai.yaml +7 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/defaults.json +12 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/memorax-add.md +101 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/memorax-search.md +106 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/personal-read.md +46 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/personal-write.md +120 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-build.md +319 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-read.md +103 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-templates.md +390 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-update.md +127 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
- package/lib/memorax-code-trae-adapter/src/adapter-paths.mjs +69 -0
- package/lib/memorax-code-trae-adapter/src/cli.mjs +59 -0
- package/lib/memorax-code-trae-adapter/src/config.mjs +630 -0
- package/lib/memorax-code-trae-adapter/src/runtime-observation.mjs +55 -0
- package/lib/run-entrypoint.mjs +6 -0
- package/package.json +5 -3
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Extract local git commit facets without GitHub/GitLab provider access."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Iterable
|
|
13
|
+
from urllib.parse import urlparse
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run_git(repo_path: Path, args: list[str]) -> subprocess.CompletedProcess[str]:
|
|
17
|
+
if shutil.which("git") is None:
|
|
18
|
+
raise SystemExit("git is required for local commit facets")
|
|
19
|
+
return subprocess.run(["git", "-C", str(repo_path), *args], text=True, capture_output=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def git_stdout(repo_path: Path, args: list[str], message: str) -> str:
|
|
23
|
+
result = run_git(repo_path, args)
|
|
24
|
+
if result.returncode != 0:
|
|
25
|
+
raise SystemExit(f"{message}:\n{result.stderr.strip()}")
|
|
26
|
+
return result.stdout
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def resolve_snapshot_sha(repo_path: Path, snapshot_ref: str) -> str:
|
|
30
|
+
return git_stdout(
|
|
31
|
+
repo_path,
|
|
32
|
+
["rev-parse", "--verify", f"{snapshot_ref}^{{commit}}"],
|
|
33
|
+
f"git could not resolve snapshot ref {snapshot_ref!r} in {repo_path}",
|
|
34
|
+
).strip()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def normalize_remote_path(path: str) -> str:
|
|
38
|
+
repo_path = path.strip().lstrip("/").rstrip("/")
|
|
39
|
+
if repo_path.endswith(".git"):
|
|
40
|
+
repo_path = repo_path[:-4]
|
|
41
|
+
return repo_path
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def repo_name_from_remote(url: str) -> str:
|
|
45
|
+
if not url:
|
|
46
|
+
return ""
|
|
47
|
+
if "://" not in url:
|
|
48
|
+
match = re.match(r"^(?:[^@/\s]+@)?[^:/\s]+:(?P<path>.+)$", url)
|
|
49
|
+
return normalize_remote_path(match.group("path")) if match else ""
|
|
50
|
+
parsed = urlparse(url)
|
|
51
|
+
return normalize_remote_path(parsed.path)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def repo_name(repo_path: Path) -> str:
|
|
55
|
+
remote = run_git(repo_path, ["remote", "get-url", "origin"])
|
|
56
|
+
parsed = repo_name_from_remote(remote.stdout.strip()) if remote.returncode == 0 else ""
|
|
57
|
+
return parsed or repo_path.name
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def unique(values: Iterable[Any]) -> list[Any]:
|
|
61
|
+
seen: set[Any] = set()
|
|
62
|
+
out: list[Any] = []
|
|
63
|
+
for value in values:
|
|
64
|
+
if value in (None, "") or value in seen:
|
|
65
|
+
continue
|
|
66
|
+
seen.add(value)
|
|
67
|
+
out.append(value)
|
|
68
|
+
return out
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def path_module(path: str) -> str:
|
|
72
|
+
parts = [part for part in path.split("/") if part and part not in {".", ".."}]
|
|
73
|
+
if not parts:
|
|
74
|
+
return ""
|
|
75
|
+
if parts[0] == ".github":
|
|
76
|
+
return ".github/workflows"
|
|
77
|
+
if "." in parts[0] and len(parts) == 1:
|
|
78
|
+
return ""
|
|
79
|
+
return parts[0]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def path_modules(files: list[str]) -> list[str]:
|
|
83
|
+
return unique(module for module in (path_module(path) for path in files) if module)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def key_files(files: list[str], limit: int = 8) -> list[str]:
|
|
87
|
+
def score(path: str) -> tuple[int, int, str]:
|
|
88
|
+
lower = path.lower()
|
|
89
|
+
value = 0
|
|
90
|
+
if "readme" in lower or lower.endswith((".md", ".rst")):
|
|
91
|
+
value += 2
|
|
92
|
+
if any(token in lower for token in ["/src/", "/core/", "/server", "/api", "/train", "/rollout", "/loss", "/model", "/actor", "/data"]):
|
|
93
|
+
value += 4
|
|
94
|
+
if lower.endswith((".py", ".ts", ".tsx", ".rs", ".go", ".sh", ".yaml", ".yml", ".toml", ".json")):
|
|
95
|
+
value += 1
|
|
96
|
+
return (-value, len(path), path)
|
|
97
|
+
|
|
98
|
+
return sorted(files, key=score)[:limit]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def bounded_summary(parts: Iterable[str], max_chars: int) -> str:
|
|
102
|
+
text = "\n\n".join(part.strip() for part in parts if isinstance(part, str) and part.strip())
|
|
103
|
+
return text if len(text) <= max_chars else text[: max_chars - 3] + "..."
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def extract_symbols(text: str) -> list[str]:
|
|
107
|
+
symbols: list[str] = []
|
|
108
|
+
for match in re.finditer(r"\b[$A-Z_a-z][$\w]*\b", text):
|
|
109
|
+
value = match.group(0)
|
|
110
|
+
looks_symbolic = (
|
|
111
|
+
re.search(r"[a-z][A-Z]", value) is not None
|
|
112
|
+
or re.match(r"^[A-Z0-9_]{2,}$", value) is not None
|
|
113
|
+
or "_" in value
|
|
114
|
+
or "$" in value
|
|
115
|
+
)
|
|
116
|
+
if looks_symbolic:
|
|
117
|
+
symbols.append(value)
|
|
118
|
+
return unique(symbols)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def parse_numstat(text: str) -> tuple[list[str], int, int]:
|
|
122
|
+
files: list[str] = []
|
|
123
|
+
additions = 0
|
|
124
|
+
deletions = 0
|
|
125
|
+
for line in text.splitlines():
|
|
126
|
+
parts = line.split("\t")
|
|
127
|
+
if len(parts) < 3:
|
|
128
|
+
continue
|
|
129
|
+
add, delete, path = parts[0], parts[1], parts[2]
|
|
130
|
+
if add.isdigit():
|
|
131
|
+
additions += int(add)
|
|
132
|
+
if delete.isdigit():
|
|
133
|
+
deletions += int(delete)
|
|
134
|
+
if path:
|
|
135
|
+
files.append(path)
|
|
136
|
+
return unique(files), additions, deletions
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def commit_facet(repo: str, repo_path: Path, sha: str, summary_chars: int) -> dict[str, Any]:
|
|
140
|
+
fields = git_stdout(
|
|
141
|
+
repo_path,
|
|
142
|
+
["show", "-s", "--date=iso-strict", "--format=%H%x1f%h%x1f%P%x1f%an%x1f%ad%x1f%s", sha],
|
|
143
|
+
f"git could not read commit metadata for {sha}",
|
|
144
|
+
).rstrip("\n").split("\x1f")
|
|
145
|
+
full_sha, short_sha, parents, author, authored_at, title = (fields + [""] * 6)[:6]
|
|
146
|
+
body = git_stdout(repo_path, ["show", "-s", "--format=%B", sha], f"git could not read commit body for {sha}")
|
|
147
|
+
files, additions, deletions = parse_numstat(
|
|
148
|
+
git_stdout(repo_path, ["show", "--format=", "--numstat", "--no-renames", sha], f"git could not read commit diff for {sha}")
|
|
149
|
+
)
|
|
150
|
+
modules = path_modules(files)
|
|
151
|
+
keys = key_files(files)
|
|
152
|
+
summary = bounded_summary([body], summary_chars) or title
|
|
153
|
+
evidence = unique([
|
|
154
|
+
f"commit {short_sha}: {title}" if short_sha and title else "",
|
|
155
|
+
f"commit {short_sha} changed {', '.join(keys)}" if short_sha and keys else "",
|
|
156
|
+
])
|
|
157
|
+
return {
|
|
158
|
+
"facetId": f"commit.{short_sha or full_sha[:12]}",
|
|
159
|
+
"sourceType": "commit",
|
|
160
|
+
"provider": "local_git",
|
|
161
|
+
"repo": repo,
|
|
162
|
+
"sha": full_sha,
|
|
163
|
+
"short_sha": short_sha,
|
|
164
|
+
"parents": [parent for parent in parents.split() if parent],
|
|
165
|
+
"is_merge": len([parent for parent in parents.split() if parent]) > 1,
|
|
166
|
+
"title": title,
|
|
167
|
+
"summary": summary,
|
|
168
|
+
"author": author,
|
|
169
|
+
"authoredAt": authored_at,
|
|
170
|
+
"updatedAt": authored_at,
|
|
171
|
+
"files": files,
|
|
172
|
+
"path_modules": modules,
|
|
173
|
+
"key_files": keys,
|
|
174
|
+
"changed_files": len(files),
|
|
175
|
+
"additions": additions,
|
|
176
|
+
"deletions": deletions,
|
|
177
|
+
"symbols": extract_symbols("\n".join([title, summary, *files])),
|
|
178
|
+
"evidence": evidence,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def fetch_facets(repo_path: Path, snapshot_ref: str, limit: int, summary_chars: int) -> list[dict[str, Any]]:
|
|
183
|
+
snapshot_sha = resolve_snapshot_sha(repo_path, snapshot_ref)
|
|
184
|
+
shas = git_stdout(
|
|
185
|
+
repo_path,
|
|
186
|
+
["rev-list", "--max-count", str(limit), snapshot_sha],
|
|
187
|
+
f"git could not list commits from {snapshot_ref!r} in {repo_path}",
|
|
188
|
+
).splitlines()
|
|
189
|
+
name = repo_name(repo_path)
|
|
190
|
+
return [commit_facet(name, repo_path, sha.strip(), summary_chars) for sha in shas if sha.strip()]
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
194
|
+
parser = argparse.ArgumentParser(description="Extract local git commit SourceFacet JSON without provider login.")
|
|
195
|
+
parser.add_argument("--repo-path", default=".", help="Local git repository path")
|
|
196
|
+
parser.add_argument("--snapshot-ref", default="HEAD", help="Local git ref whose history should be collected")
|
|
197
|
+
parser.add_argument("--limit", type=int, default=30, help="Maximum commits retained from the snapshot history")
|
|
198
|
+
parser.add_argument("--summary-chars", type=int, default=4000)
|
|
199
|
+
parser.add_argument("--pretty", action="store_true", help="Pretty-print JSON")
|
|
200
|
+
parser.add_argument("--out", help="Write JSON to this file instead of stdout")
|
|
201
|
+
args = parser.parse_args(argv)
|
|
202
|
+
if args.limit < 1 or args.limit > 500:
|
|
203
|
+
parser.error("--limit must be from 1 to 500")
|
|
204
|
+
if args.summary_chars < 100:
|
|
205
|
+
parser.error("--summary-chars must be at least 100")
|
|
206
|
+
args.repo_path = Path(args.repo_path).expanduser().resolve()
|
|
207
|
+
return args
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def main(argv: list[str]) -> int:
|
|
211
|
+
args = parse_args(argv)
|
|
212
|
+
facets = fetch_facets(args.repo_path, args.snapshot_ref, args.limit, args.summary_chars)
|
|
213
|
+
text = json.dumps(facets, ensure_ascii=False, indent=2 if args.pretty else None)
|
|
214
|
+
if args.out:
|
|
215
|
+
Path(args.out).write_text(text + "\n", encoding="utf-8")
|
|
216
|
+
else:
|
|
217
|
+
print(text)
|
|
218
|
+
return 0
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
if __name__ == "__main__":
|
|
222
|
+
raise SystemExit(main(sys.argv[1:]))
|