@yottameta/yotta-logs 0.2.2 → 0.3.1

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/install.sh CHANGED
@@ -1,132 +1,132 @@
1
- #!/usr/bin/env bash
2
- # yotta-logs 多智能体安装脚本(YottaSkills 模板)
3
- # 用法:
4
- # bash install.sh --agent <name> # 按智能体默认用户级目录安装
5
- # bash install.sh --dir <path> # 装到指定目录(用户改过目录的智能体)
6
- # bash install.sh -g # 装到全部已知智能体用户级目录
7
- # bash install.sh # 检测并安装到已存在的项目级目录
8
- # bash install.sh --list # 列出智能体 -> 默认目录
9
- set -euo pipefail
10
-
11
- SKILL_NAME="yotta-logs"
12
- SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
- case "$(uname -s)" in
14
- MINGW*|MSYS*)
15
- SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -W)"
16
- ;;
17
- esac
18
-
19
- # 智能体 -> 用户级默认目录(--agent 装到第一个)
20
- # .agents/skills 并非通用目录:OpenCode / Cursor / Cline / Amp / Kimi / Gemini CLI / GitHub Copilot 读取。
21
- # 判断当前环境:Windows Git Bash 用 %USERPROFILE%,Unix 用 ~
22
- _IS_WINDOWS=0
23
- case "$(uname -s)" in
24
- MINGW*|MSYS*|CYGWIN*) _IS_WINDOWS=1 ;;
25
- esac
26
- dirs_for() {
27
- case "$1" in
28
- claude) echo ".claude/skills" ;;
29
- cursor) echo ".cursor/skills .agents/skills" ;;
30
- codex) echo "__CODEX__" ;;
31
- gemini) echo ".gemini/skills .agents/skills" ;;
32
- goose) echo ".config/goose/skills .agents/skills" ;;
33
- amp) echo ".config/agents/skills .agents/skills" ;;
34
- opencode) echo "__OPENCODE__" ;;
35
- windsurf) echo ".codeium/windsurf/skills" ;;
36
- workbuddy) echo ".workbuddy/skills" ;;
37
- kiro) echo ".kiro/skills" ;;
38
- trae) echo ".traecli/skills" ;;
39
- trae-cn) echo ".trae-cn/skills" ;;
40
- qwen) echo ".qwen/skills" ;;
41
- comate) echo ".comate/skills" ;;
42
- codebuddy) echo ".codebuddy/skills" ;;
43
- kimi) echo ".kimi/skills" ;;
44
- agents) echo ".agents/skills" ;;
45
- *) return 1 ;;
46
- esac
47
- }
48
-
49
- codex_dir() {
50
- if [ -n "${CODEX_HOME:-}" ]; then printf '%s' "$CODEX_HOME/skills"; else printf '%s' "$HOME/.codex/skills"; fi
51
- }
52
- opencode_dir() {
53
- if [ -n "${XDG_CONFIG_HOME:-}" ]; then printf '%s' "$XDG_CONFIG_HOME/opencode/skills"; else printf '%s' "$HOME/.config/opencode/skills"; fi
54
- }
55
- resolve_user() {
56
- case "$1" in
57
- __CODEX__) codex_dir ;;
58
- __OPENCODE__) opencode_dir ;;
59
- *) printf '%s' "$HOME/$1" ;;
60
- esac
61
- }
62
-
63
- install_to() {
64
- mkdir -p "$1/$SKILL_NAME"
65
- cp -r "$SOURCE_DIR/." "$1/$SKILL_NAME/"
66
- rm -rf "$1/$SKILL_NAME/.git"
67
- echo "installed -> $1/$SKILL_NAME"
68
- }
69
-
70
- list() {
71
- echo "智能体 -> 默认技能目录(--agent <name> 装到第一个,用户级):"
72
- for a in claude cursor codex gemini goose amp opencode windsurf workbuddy kiro trae trae-cn qwen comate codebuddy kimi agents; do
73
- local dirs first
74
- dirs="$(dirs_for "$a")"
75
- first="${dirs%% *}"
76
- case "$first" in
77
- __CODEX__) first=".codex/skills" ;;
78
- __OPENCODE__) first=".config/opencode/skills" ;;
79
- esac
80
- if [ "$_IS_WINDOWS" = "1" ]; then
81
- first="%USERPROFILE%\\${first//\//\\}"
82
- else
83
- first="~/$first"
84
- fi
85
- printf ' %-10s %s\n' "$a" "$first"
86
- done
87
- echo '说明:Windows 用 %USERPROFILE%,Linux/macOS 用 ~;仅收录有官方默认目录的智能体。'
88
- echo '改了目录的请用 --dir <路径>,不要依赖默认位置;若设置了 CODEX_HOME / XDG_CONFIG_HOME,安装自动以该变量为准。'
89
- }
90
-
91
- main() {
92
- local agent="" dir="" global=0 show_list=0
93
- while [ $# -gt 0 ]; do
94
- case "$1" in
95
- --agent) shift; agent="${1:-}" ;;
96
- --dir) shift; dir="${1:-}" ;;
97
- -g|--global) global=1 ;;
98
- --list|-l) show_list=1 ;;
99
- *) echo "未知参数: $1" >&2; exit 2 ;;
100
- esac
101
- shift
102
- done
103
-
104
- if [ "$show_list" = "1" ]; then list; return; fi
105
- if [ -n "$dir" ]; then install_to "$dir"; echo "完成。"; return; fi
106
- if [ -n "$agent" ]; then
107
- local dirs first
108
- if ! dirs="$(dirs_for "$agent")"; then
109
- echo "未收录智能体: $agent。请用 --dir <路径> 指定技能目录。" >&2; exit 2
110
- fi
111
- first="${dirs%% *}"
112
- install_to "$(resolve_user "$first")"; echo "完成。"; return
113
- fi
114
- if [ "$global" = "1" ]; then
115
- echo "安装到全部已知智能体用户级目录..."
116
- local dirs rel
117
- for a in claude cursor codex gemini goose amp opencode windsurf workbuddy kiro trae trae-cn qwen comate codebuddy kimi agents; do
118
- dirs="$(dirs_for "$a")"
119
- for rel in $dirs; do install_to "$(resolve_user "$rel")"; done
120
- done
121
- echo "完成。"; return
122
- fi
123
- local installed=0 d
124
- for d in .claude/skills .cursor/skills .codex/skills .config/goose/skills .config/agents/skills .opencode/skills .codeium/windsurf/skills .workbuddy/skills .kiro/skills .traecli/skills .gemini/skills .trae-cn/skills .qwen/skills .comate/skills .codebuddy/skills .kimi/skills .agents/skills; do
125
- if [ -d "$d" ]; then install_to "$d"; installed=1; fi
126
- done
127
- if [ "$installed" = "0" ]; then
128
- echo "未检测到项目级智能体目录。可用 --agent <name> / -g 装到用户级,或 --dir 指定。"
129
- fi
130
- }
131
-
1
+ #!/usr/bin/env bash
2
+ # yotta-logs 多智能体安装脚本(YottaSkills 模板)
3
+ # 用法:
4
+ # bash install.sh --agent <name> # 按智能体默认用户级目录安装
5
+ # bash install.sh --dir <path> # 装到指定目录(用户改过目录的智能体)
6
+ # bash install.sh -g # 装到全部已知智能体用户级目录
7
+ # bash install.sh # 检测并安装到已存在的项目级目录
8
+ # bash install.sh --list # 列出智能体 -> 默认目录
9
+ set -euo pipefail
10
+
11
+ SKILL_NAME="yotta-logs"
12
+ SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+ case "$(uname -s)" in
14
+ MINGW*|MSYS*)
15
+ SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -W)"
16
+ ;;
17
+ esac
18
+
19
+ # 智能体 -> 用户级默认目录(--agent 装到第一个)
20
+ # .agents/skills 并非通用目录:OpenCode / Cursor / Cline / Amp / Kimi / Gemini CLI / GitHub Copilot 读取。
21
+ # 判断当前环境:Windows Git Bash 用 %USERPROFILE%,Unix 用 ~
22
+ _IS_WINDOWS=0
23
+ case "$(uname -s)" in
24
+ MINGW*|MSYS*|CYGWIN*) _IS_WINDOWS=1 ;;
25
+ esac
26
+ dirs_for() {
27
+ case "$1" in
28
+ claude) echo ".claude/skills" ;;
29
+ cursor) echo ".cursor/skills .agents/skills" ;;
30
+ codex) echo "__CODEX__" ;;
31
+ gemini) echo ".gemini/skills .agents/skills" ;;
32
+ goose) echo ".config/goose/skills .agents/skills" ;;
33
+ amp) echo ".config/agents/skills .agents/skills" ;;
34
+ opencode) echo "__OPENCODE__" ;;
35
+ windsurf) echo ".codeium/windsurf/skills" ;;
36
+ workbuddy) echo ".workbuddy/skills" ;;
37
+ kiro) echo ".kiro/skills" ;;
38
+ trae) echo ".traecli/skills" ;;
39
+ trae-cn) echo ".trae-cn/skills" ;;
40
+ qwen) echo ".qwen/skills" ;;
41
+ comate) echo ".comate/skills" ;;
42
+ codebuddy) echo ".codebuddy/skills" ;;
43
+ kimi) echo ".kimi/skills" ;;
44
+ agents) echo ".agents/skills" ;;
45
+ *) return 1 ;;
46
+ esac
47
+ }
48
+
49
+ codex_dir() {
50
+ if [ -n "${CODEX_HOME:-}" ]; then printf '%s' "$CODEX_HOME/skills"; else printf '%s' "$HOME/.codex/skills"; fi
51
+ }
52
+ opencode_dir() {
53
+ if [ -n "${XDG_CONFIG_HOME:-}" ]; then printf '%s' "$XDG_CONFIG_HOME/opencode/skills"; else printf '%s' "$HOME/.config/opencode/skills"; fi
54
+ }
55
+ resolve_user() {
56
+ case "$1" in
57
+ __CODEX__) codex_dir ;;
58
+ __OPENCODE__) opencode_dir ;;
59
+ *) printf '%s' "$HOME/$1" ;;
60
+ esac
61
+ }
62
+
63
+ install_to() {
64
+ mkdir -p "$1/$SKILL_NAME"
65
+ cp -r "$SOURCE_DIR/." "$1/$SKILL_NAME/"
66
+ rm -rf "$1/$SKILL_NAME/.git"
67
+ echo "installed -> $1/$SKILL_NAME"
68
+ }
69
+
70
+ list() {
71
+ echo "智能体 -> 默认技能目录(--agent <name> 装到第一个,用户级):"
72
+ for a in claude cursor codex gemini goose amp opencode windsurf workbuddy kiro trae trae-cn qwen comate codebuddy kimi agents; do
73
+ local dirs first
74
+ dirs="$(dirs_for "$a")"
75
+ first="${dirs%% *}"
76
+ case "$first" in
77
+ __CODEX__) first=".codex/skills" ;;
78
+ __OPENCODE__) first=".config/opencode/skills" ;;
79
+ esac
80
+ if [ "$_IS_WINDOWS" = "1" ]; then
81
+ first="%USERPROFILE%\\${first//\//\\}"
82
+ else
83
+ first="~/$first"
84
+ fi
85
+ printf ' %-10s %s\n' "$a" "$first"
86
+ done
87
+ echo '说明:Windows 用 %USERPROFILE%,Linux/macOS 用 ~;仅收录有官方默认目录的智能体。'
88
+ echo '改了目录的请用 --dir <路径>,不要依赖默认位置;若设置了 CODEX_HOME / XDG_CONFIG_HOME,安装自动以该变量为准。'
89
+ }
90
+
91
+ main() {
92
+ local agent="" dir="" global=0 show_list=0
93
+ while [ $# -gt 0 ]; do
94
+ case "$1" in
95
+ --agent) shift; agent="${1:-}" ;;
96
+ --dir) shift; dir="${1:-}" ;;
97
+ -g|--global) global=1 ;;
98
+ --list|-l) show_list=1 ;;
99
+ *) echo "未知参数: $1" >&2; exit 2 ;;
100
+ esac
101
+ shift
102
+ done
103
+
104
+ if [ "$show_list" = "1" ]; then list; return; fi
105
+ if [ -n "$dir" ]; then install_to "$dir"; echo "完成。"; return; fi
106
+ if [ -n "$agent" ]; then
107
+ local dirs first
108
+ if ! dirs="$(dirs_for "$agent")"; then
109
+ echo "未收录智能体: $agent。请用 --dir <路径> 指定技能目录。" >&2; exit 2
110
+ fi
111
+ first="${dirs%% *}"
112
+ install_to "$(resolve_user "$first")"; echo "完成。"; return
113
+ fi
114
+ if [ "$global" = "1" ]; then
115
+ echo "安装到全部已知智能体用户级目录..."
116
+ local dirs rel
117
+ for a in claude cursor codex gemini goose amp opencode windsurf workbuddy kiro trae trae-cn qwen comate codebuddy kimi agents; do
118
+ dirs="$(dirs_for "$a")"
119
+ for rel in $dirs; do install_to "$(resolve_user "$rel")"; done
120
+ done
121
+ echo "完成。"; return
122
+ fi
123
+ local installed=0 d
124
+ for d in .claude/skills .cursor/skills .codex/skills .config/goose/skills .config/agents/skills .opencode/skills .codeium/windsurf/skills .workbuddy/skills .kiro/skills .traecli/skills .gemini/skills .trae-cn/skills .qwen/skills .comate/skills .codebuddy/skills .kimi/skills .agents/skills; do
125
+ if [ -d "$d" ]; then install_to "$d"; installed=1; fi
126
+ done
127
+ if [ "$installed" = "0" ]; then
128
+ echo "未检测到项目级智能体目录。可用 --agent <name> / -g 装到用户级,或 --dir 指定。"
129
+ fi
130
+ }
131
+
132
132
  main "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottameta/yotta-logs",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "Yuanshi — a skill for retrieving historical session / memory logs across AI agents: zero-dependency search and analysis of JSONL, JSON, SQLite and Markdown session & memory files (unified Record + field-alias normalization + config fallback), with full source discovery to recall past conversations and parent-session context. Triggers when the user asks about previously discussed content / a parent session / historical context, wants to look up an earlier conclusion, traces a past discussion across sessions, or needs to locate a decision in session logs or memory files. Boundaries: reads only the local agent's own session logs / memory files; never modifies or deletes records; local-only, never uploaded.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -18,7 +18,11 @@
18
18
  "bin",
19
19
  "NOTICE",
20
20
  "CHANGELOG.md",
21
- "README.zh-CN.md"
21
+ "README.zh-CN.md",
22
+ "!scripts/test_*.py",
23
+ "!scripts/__pycache__",
24
+ "!**/__pycache__",
25
+ "!**/*.pyc"
22
26
  ],
23
27
  "repository": {
24
28
  "type": "git",
@@ -29,5 +33,8 @@
29
33
  },
30
34
  "bin": {
31
35
  "yotta-logs": "bin/install.js"
36
+ },
37
+ "scripts": {
38
+ "test": "node --test \"test/*.test.js\" && python scripts/test_yotta_logs.py"
32
39
  }
33
40
  }
@@ -48,8 +48,8 @@
48
48
 
49
49
  ### 3.3 SQLite(sqlite)
50
50
 
51
- - 代表:opencode(~/.local/share/opencode/opencode.db;本机实测 D:\AI_WorkDir\.OpenCodeData\data\opencode\opencode.db,走 XDG_DATA_HOME / OPENCODE_DATA)、Cursor state.vscdb、Trae、Copilot CLI session-store、CodeBuddy。
52
- - opencode 实测 schema(2026-08-27,D:\AI_WorkDir\.OpenCodeData\data\opencode\opencode.db):
51
+ - 代表:opencode(~/.local/share/opencode/opencode.db;XDG_DATA_HOME / OPENCODE_DATA 可覆盖)、Cursor state.vscdb、Trae、Copilot CLI session-store、CodeBuddy。
52
+ - opencode 实测 schema(2026-08-27):
53
53
  - `session(id, project_id, title, cost, tokens_input, tokens_output, time_created[毫秒], ...)`
54
54
  - `message(id, session_id, time_created[毫秒], data[JSON: role, time, agent, model, ...])`
55
55
  - `part(id, message_id, session_id, time_created[毫秒], data[JSON: type=text/tool/reasoning/step-start...])`——text 部分取 `text` 字段;tool 部分取 `tool` 字段为工具名。
@@ -65,7 +65,7 @@
65
65
 
66
66
  - 代表:yotta-memory(记忆库 facts / private / archive 下的 *.md)、agent-code、opencode-agent-memory。
67
67
  - frontmatter:`type`(FACT/PREF/BOUND/COMMIT → role)、`subject` → title、`statement` → text、`created / updated / date` → time、`tags / confidence / scope / owner / immutable` → meta。
68
- - 本机实测样本(2026-08-27):D:\AI_WorkDir\.yottamemory\facts\2026-08-25-0002.md(记忆库位置由 ~/.yottamemory/config.json 的 `memory_home` 决定)。
68
+ - 实测样本路径:`<memory_home>/facts/*.md`。`memory_home` 由 `YOTTA_MEMORY_HOME` 或 `~/.yottamemory/config.json` 决定。
69
69
  - frontmatter 解析为零依赖 YAML 子集(key: value / key: [a, b] / 引号),非完整 YAML。
70
70
 
71
71
  ### 3.6 二进制 / 专有 / 加密(binary)
@@ -83,20 +83,20 @@
83
83
  | opencode-sessions | ~/.config/opencode/sessions | jsonl | session | 开 |
84
84
  | gemini-sessions | ~/.gemini/sessions | jsonl | session | 开 |
85
85
  | agents-sessions | ~/.agents/sessions | jsonl | session | 开 |
86
- | opencode-db | ~/.local/share/opencode/opencode.db;$XDG_DATA_HOME/opencode/opencode.db;$OPENCODE_DATA;~/.OpenCodeData/data/opencode/opencode.db | sqlite | session | 开 |
86
+ | opencode-db | ~/.local/share/opencode/opencode.db;$XDG_DATA_HOME/opencode/opencode.db;$OPENCODE_DATA | sqlite | session | 开 |
87
87
  | cursor-state / code-state | VS Code / Cursor globalStorage 下 state.vscdb(Windows / Linux / macOS) | sqlite | session | 开 |
88
88
  | continue-sessions | ~/.continue/sessions、~/.config/continue/sessions | json | session | 开 |
89
89
  | yottamemory-facts | 记忆库 facts(memory_home 配置) | markdown | memory | 开 |
90
90
  | yottamemory-private | 记忆库 private(memory_home 配置) | markdown | memory | 开 |
91
91
  | yottamemory-archive | 记忆库 archive(memory_home 配置) | markdown | memory | 开 |
92
- | codex-notes | $CODEX_HOME/memories、~/.CodexData/memories | markdown | note | 关(显式开) |
92
+ | codex-notes | $CODEX_HOME/memories、~/.codex/memories | markdown | note | 关(显式开) |
93
93
  | aider-history | 当前目录 *.aider.*.md | markdown | session | 开 |
94
94
  | windsurf-conv | ~/.codeium/windsurf、~/.windsurf 下 *.pbtxt | binary | log | 关 |
95
95
  | 自定义 sources | 配置 sources[](见下) | 任意 | 任意 | 配置 default_scope |
96
96
 
97
97
  ## 五、配置兜底(config.json)
98
98
 
99
- 路径:`$YOTTA_LOGS_CONFIG` 或 `~/.config/yotta-logs/config.json`。
99
+ 路径:`$YOTTA_LOGS_CONFIG`;未设置时使用平台默认位置:Windows = `%APPDATA%\yotta-logs\config.json`,Unix = `$XDG_CONFIG_HOME/yotta-logs/config.json` 或 `~/.config/yotta-logs/config.json`。
100
100
 
101
101
  ```json
102
102
  {
@@ -120,7 +120,7 @@
120
120
 
121
121
  引擎零改动即可接入怪格式(个别 agent 私有 schema 用列映射接入)。
122
122
 
123
- ## 六、默认检索范围(2026-08-27 老张拍板)
123
+ ## 六、默认检索范围
124
124
 
125
125
  - 会话源(kind=session)+ 结构化记忆源(kind=memory)**默认开**;
126
126
  - 自由笔记(kind=note)与二进制日志(kind=log)**默认关**,可 `--kind note` / `--kind log` 显式开;
package/references/cli.md CHANGED
@@ -66,7 +66,7 @@
66
66
 
67
67
  ## 配置(配置兜底)
68
68
 
69
- - 配置文件:`$YOTTA_LOGS_CONFIG` 或 `~/.config/yotta-logs/config.json`;
69
+ - 配置文件:`$YOTTA_LOGS_CONFIG`;未设置时 Windows 用 `%APPDATA%\yotta-logs\config.json`,Unix 用 `$XDG_CONFIG_HOME/yotta-logs/config.json` 或 `~/.config/yotta-logs/config.json`;
70
70
  - `default_scope`:默认检索范围(默认 `["session", "memory"]`);
71
71
  - `sources[]`:自定义源(path / format / kind / name / table / col_time / col_role / col_text / col_session / col_title),引擎零改动接入怪格式。
72
72
  - 示例见 `agent-formats.md` 第五节。
@@ -0,0 +1,54 @@
1
+ # 元史常见问题
2
+
3
+ ## 速查索引
4
+
5
+ - **格式**:支持哪些格式 · SQLite 要装依赖吗
6
+ - **范围**:默认扫哪里 · 自由笔记怎么开
7
+ - **检索**:查不到怎么办 · 如何精确到某个会话
8
+ - **安全**:会修改日志吗 · 如何脱敏
9
+
10
+ ---
11
+
12
+ ## 1. 支持哪些格式?
13
+
14
+ JSONL、单文件 JSON、SQLite、Markdown(结构化记忆与自由笔记)、部分二进制文件的标题回退。
15
+
16
+ ## 2. SQLite 需要额外依赖吗?
17
+
18
+ 不需要,使用 Python 标准库 `sqlite3`,只读打开。
19
+
20
+ ## 3. 默认扫描范围是什么?
21
+
22
+ 会话源与结构化记忆源默认开启;自由笔记和二进制日志默认关闭,可显式开启。
23
+
24
+ ## 4. 如何只查某个目录?
25
+
26
+ 用 `--dir <path>` 指定目录或文件;目录会自动嗅探格式族。
27
+
28
+ ## 5. 自由笔记怎么开启?
29
+
30
+ 用 `--kind note` 或在配置中调整默认范围。
31
+
32
+ ## 6. 查不到怎么办?
33
+
34
+ 先 `locate` 确认源是否存在,再 `scan` 查看可检索内容;确认关键词、日期、会话过滤是否过窄。
35
+
36
+ ## 7. 如何精确提取某个会话?
37
+
38
+ 先 `search` 获取会话 ID,再 `session <sid>` 提取原文。
39
+
40
+ ## 8. 会修改或删除日志吗?
41
+
42
+ 不会。所有操作只读。
43
+
44
+ ## 9. 输出会泄露敏感信息吗?
45
+
46
+ 默认自动脱敏疑似密钥、token、口令;如需原文可用 `--no-redact`,但请谨慎外传。
47
+
48
+ ## 10. JSON 输出有什么用?
49
+
50
+ 包含来源、会话 ID、行号、时间戳、角色等结构化字段,便于程序核对和自动化处理。
51
+
52
+ ## 11. 安装失败怎么办?
53
+
54
+ 确认智能体目录是否正确;未收录智能体用 `--dir` 指定路径;权限问题请检查目标目录写权限。
@@ -64,7 +64,7 @@ try:
64
64
  except Exception:
65
65
  pass
66
66
 
67
- VERSION = "0.2.2"
67
+ VERSION = "0.3.1"
68
68
  TOOL_NAME = "yotta-logs"
69
69
  TOOL_CN = "元史"
70
70
  DEFAULT_LIMIT = 50
@@ -743,7 +743,6 @@ class SQLiteReader:
743
743
  cands += [
744
744
  ("opencode-db", base / ".local" / "share" / "opencode" / "opencode.db"),
745
745
  ("opencode-db", base / ".config" / "opencode" / "opencode.db"),
746
- ("opencode-db", base / ".OpenCodeData" / "data" / "opencode" / "opencode.db"),
747
746
  ]
748
747
  seen = set()
749
748
  for name, p in cands:
@@ -979,9 +978,8 @@ class MarkdownReader:
979
978
  if p.is_dir() and any(x.suffix.lower() in MD_SUFFIXES
980
979
  for x in p.iterdir()):
981
980
  out.append(_mk_source(name, "memory", "markdown", p))
982
- codex_home = os.environ.get("CODEX_HOME")
983
- codex_notes = Path(codex_home) / "memories" if codex_home \
984
- else base / ".CodexData" / "memories"
981
+ codex_home = os.environ.get("CODEX_HOME") or str(base / ".codex")
982
+ codex_notes = Path(codex_home) / "memories"
985
983
  if codex_notes.is_dir() and cls._has_md(codex_notes):
986
984
  out.append(_mk_source("codex-notes", "note", "markdown", codex_notes,
987
985
  default_on=False))
@@ -993,7 +991,10 @@ class MarkdownReader:
993
991
 
994
992
  @staticmethod
995
993
  def _memory_home(base):
996
- """yotta-memory 记忆库位置:优先读引擎 config.json 的 memory_home。"""
994
+ """yotta-memory 记忆库位置:优先环境变量,再读引擎 config.json。"""
995
+ env = os.environ.get("YOTTA_MEMORY_HOME")
996
+ if env:
997
+ return Path(env)
997
998
  try:
998
999
  cfg_p = base / ".yottamemory" / "config.json"
999
1000
  cfg = json.loads(cfg_p.read_text(encoding="utf-8", errors="replace"))
@@ -1284,11 +1285,23 @@ def sniff_source(path):
1284
1285
 
1285
1286
  # ── 配置兜底 + discover 全源登记 ─────────────────────────────────────────
1286
1287
 
1288
+ def default_config_path():
1289
+ """平台无关的默认配置路径;环境变量始终优先。"""
1290
+ env = os.environ.get("YOTTA_LOGS_CONFIG")
1291
+ if env:
1292
+ return Path(env)
1293
+ xdg = os.environ.get("XDG_CONFIG_HOME")
1294
+ if xdg:
1295
+ return Path(xdg) / "yotta-logs" / "config.json"
1296
+ if os.name == "nt":
1297
+ appdata = os.environ.get("APPDATA")
1298
+ if appdata:
1299
+ return Path(appdata) / "yotta-logs" / "config.json"
1300
+ return Path.home() / ".config" / "yotta-logs" / "config.json"
1301
+
1302
+
1287
1303
  def load_config():
1288
- p = os.environ.get("YOTTA_LOGS_CONFIG")
1289
- if not p:
1290
- p = str(Path.home() / ".config" / "yotta-logs" / "config.json")
1291
- cfg_path = Path(p)
1304
+ cfg_path = default_config_path()
1292
1305
  if not cfg_path.exists():
1293
1306
  return {}
1294
1307
  try: