@yottameta/yotta-logs 0.2.2 → 0.3.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## v0.3.0 (2026-09-08)
2
+
3
+ **评测驱动完善**:新增 FAQ,安装器错误处理与测试补齐。
4
+
5
+ - 新增 references/faq.md,覆盖格式、范围、脱敏、只读边界与安装排障。
6
+ - 安装器支持 --help、参数校验、统一退出码与人话错误提示。
7
+
1
8
  # 更新日志
2
9
 
3
10
  ## v0.2.2 (2026-08-29)
@@ -8,7 +15,7 @@
8
15
 
9
16
  ## v0.2.1 (2026-08-27)
10
17
 
11
- 文档中英双版(老张拍板「英文门面 + 中文全档」):
18
+ 文档中英双版:
12
19
 
13
20
  - **README.md 改为英文**:作为 GitHub / npm / ClawHub 首页的英文门面(翻译 + 精简,覆盖定位 / 核心价值 / 命令 / 快速使用 / 安装 / 使用示例 / 开发校验全流程)。
14
21
  - **新增 README.zh-CN.md**:原中文完整主文档整体平移,继续服务中文用户,顶部加语言切换链接。
@@ -18,7 +25,7 @@
18
25
 
19
26
  ## v0.2.0 (2026-08-27)
20
27
 
21
- 多格式通用化:不再只认 JSONL,按「格式族 × 字段别名归一 + 配置兜底」适配一切格式(老张拍板三点:方向认可 / 版本 v0.2.0 / 默认检索范围动作工时定):
28
+ 多格式通用化:不再只认 JSONL,按「格式族 × 字段别名归一 + 配置兜底」适配一切格式:
22
29
 
23
30
  - **五大格式族 reader**:JSONL(支持嵌套子目录与 Codex rollout payload 形态)/ 单文件 JSON(数组、dict-of-lists)/ SQLite(opencode schema 实测 + 通用列映射兜底,只读 mode=ro)/ Markdown(YAML frontmatter 结构化记忆 + 自由笔记)/ 二进制(只降级读标题不崩)。
24
31
  - **统一 Record 模型**:{source, format, kind, session, time, role, text, path, meta};字段别名归一(time / role / text / session / title),秒 / 毫秒自动推断,JSON 字符串解包。
@@ -30,9 +37,9 @@
30
37
 
31
38
  ## v0.1.0 (2026-08-27)
32
39
 
33
- YottaMeta 自有实现首版(历史会话日志检索方向参考开源社区 session-logs 类技能思路,已完全重写,无上游代码):
40
+ YottaMeta 自有实现首版(历史会话日志检索方向参考开源社区 session-logs 类技能思路,已完全重写):
34
41
 
35
42
  - 元史(yotta-logs)—— 零依赖跨智能体会话日志检索引擎(Python 3.8+ 标准库)。
36
43
  - 能力:locate / scan / search / session / stats / tools / version;关键词 / 正则 / 日期 / 会话 ID / 别名 / 角色过滤;默认脱敏(--no-redact 关闭);--json 结构化输出;只读安全;容错 JSONL 解析(含 sessions.json 索引别名)。
37
44
  - 测试:scripts/test_yotta_logs.py 75 项全绿。
38
- - 版权:YottaMeta 纯自有 MIT + NOTICE 品牌声明;README 一行上游致谢。
45
+ - 版权:YottaMeta 纯自有 MIT + NOTICE 品牌声明。
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 YottaMeta
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2026 YottaMeta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
package/README.md CHANGED
@@ -162,12 +162,18 @@ bash install.sh --list # list agents -> default directories
162
162
 
163
163
  ## Changelog
164
164
 
165
+ - v0.3.0 (2026-09-08): Evaluation-driven refinement — added FAQ, hardened installer error handling and exit codes, and added installer tests.
166
+
165
167
  - v0.2.2 (2026-08-29): Install docs alignment — unified four install methods (npx -y @yottameta/yotta-logs --agent/--dir, git clone, GitHub Download ZIP, install.sh --agent/--dir/--list), removed the legacy GitHub-clone installer and global-install (-g) recommendations; bilingual README install section synced to 发布规范 §3.3.1. No functional change.
166
168
 
167
169
  - v0.2.1 (2026-08-27): Bilingual documentation — English README as the GitHub / npm / ClawHub homepage, full Chinese doc moved to README.zh-CN.md, English npm description.
168
170
  - v0.2.0 (2026-08-27): Multi-format generalization — JSONL / single-file JSON / SQLite (opencode etc.) / Markdown (memory + free notes) / binary; unified Record + field-alias normalization + config fallback; discover; new --source / --kind / --format filters and default search scope (session + structured memory on, free notes / binary logs off). See CHANGELOG.md.
169
171
  - v0.1.0 (2026-08-27): Initial release — zero-dependency JSONL session log search engine (locate / scan / search / session / stats / tools / version + default redaction + sessions.json alias + read-only).
170
172
 
173
+ ## References
174
+
175
+ - references/faq.md
176
+
171
177
  ## License
172
178
 
173
179
  MIT © YottaMeta — see [LICENSE](./LICENSE).
package/README.zh-CN.md CHANGED
@@ -162,11 +162,17 @@ bash install.sh --list # 列出智能体 -> 默认目录
162
162
 
163
163
  ## 更新日志
164
164
 
165
+ - v0.3.0(2026-09-08):评测驱动完善——新增常见问题,安装器错误处理与退出码加固,并补齐安装器测试。
166
+
165
167
  - v0.2.2(2026-08-29):安装方式统一为四方式(对齐发布规范 §3.3.1)——方式一 npx -y @yottameta/yotta-logs --agent / --dir(推荐,走 npm 源);方式二 git clone;方式三 GitHub Download ZIP;方式四 bash install.sh --agent/--dir/--list。移除旧式 GitHub 克隆安装器与全局安装(-g)推荐;中英 README 安装节同步。无功能变更。
166
168
 
167
169
  - v0.2.0(2026-08-27):多格式通用化——JSONL / 单文件 JSON / SQLite(opencode 等)/ Markdown(记忆 + 自由笔记)/ 二进制五大格式族,统一 Record + 字段别名归一 + 配置兜底,discover 全源登记,新增 --source / --kind / --format 过滤与默认检索范围(会话 + 结构化记忆开、自由笔记 / 二进制日志关)。详见 CHANGELOG.md。
168
170
  - v0.1.0(2026-08-27):首版——零依赖 JSONL 会话日志检索引擎(locate / scan / search / session / stats / tools / version + 默认脱敏 + sessions.json 别名 + 只读)。
169
171
 
172
+ ## 参考文档
173
+
174
+ - references/faq.md
175
+
170
176
  ## 许可证
171
177
 
172
178
  MIT © YottaMeta —— 详见 [LICENSE](./LICENSE)。
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yotta-logs
3
- version: 0.2.2
3
+ version: 0.3.0
4
4
  description: 元史 —— 跨智能体的历史会话 / 记忆日志检索技能:零依赖检索 / 分析 JSONL、JSON、SQLite、Markdown 多格式会话与记忆文件,回溯旧对话与父会话上下文,为跨会话追溯提供原始日志依据。触发:用户问起先前聊过的内容 / 父会话 / 历史上下文、要查以前说过的结论、跨会话回溯某次讨论、需要从会话日志或记忆文件定位某段决策时。边界:仅读取本机自己的会话日志 / 记忆文件;不修改、不删除;只查本地不联网上传。
5
5
  license: MIT
6
6
  ---
@@ -84,6 +84,7 @@ python3 scripts/yotta_logs.py tools --dir /path/to/logs --format sqlite
84
84
  - references/format.md — 统一 Record 模型与各格式族读取规则
85
85
  - references/cli.md — CLI 子命令 / 参数 / 退出码 / JSON schema / 配置详解
86
86
  - references/security.md — 安全边界 / 脱敏规则 / 与元忆的差异化
87
+ - references/faq.md — 常见问题与安装排障
87
88
 
88
89
  ## 责任声明
89
90
 
package/bin/install.js CHANGED
@@ -1,163 +1,245 @@
1
- #!/usr/bin/env node
2
- /**
3
- * yotta-logs 跨平台安装器(YottaSkills)
4
- * 用法:
5
- * npx -y @yottameta/yotta-logs --agent <name> # 按智能体默认用户级目录安装(推荐)
6
- * npx -y @yottameta/yotta-logs --dir PATH # 装到指定目录(用户改了目录的智能体)
7
- * npx -y @yottameta/yotta-logs -g # 安装到全部已知智能体用户级目录
8
- * npx -y @yottameta/yotta-logs # 安装到检测到的项目级目录
9
- * npx -y @yottameta/yotta-logs --list # 列出智能体 -> 默认目录
10
- */
11
- 'use strict';
12
- const fs = require('fs');
13
- const path = require('path');
14
- const os = require('os');
15
-
16
- const SKILL_NAME = 'yotta-logs';
17
- const PKG_ROOT = path.join(__dirname, '..');
18
-
19
- // 智能体 -> 用户级默认技能目录(dirs 按优先级排列;--agent 装到第一个)
20
- // 依据官方文档:.agents/skills 并非通用目录,被 OpenCode / Cursor / Cline / Amp /
21
- // Kimi / Gemini CLI / GitHub Copilot 等读取;Claude Code 与 Codex 默认不读 .agents。
22
- const AGENT_DIRS = {
23
- claude: { label: 'Claude Code', dirs: ['.claude/skills'] },
24
- cursor: { label: 'Cursor', dirs: ['.cursor/skills', '.agents/skills'] },
25
- codex: { label: 'Codex', dirs: ['.codex/skills'] }, // 特判:$CODEX_HOME/skills
26
- gemini: { label: 'Gemini CLI', dirs: ['.gemini/skills', '.agents/skills'] },
27
- goose: { label: 'Goose', dirs: ['.config/goose/skills', '.agents/skills'] },
28
- amp: { label: 'Amp', dirs: ['.config/agents/skills', '.agents/skills'] },
29
- opencode: { label: 'OpenCode', dirs: ['.config/opencode/skills'] }, // 特判:$XDG_CONFIG_HOME
30
- windsurf: { label: 'Windsurf', dirs: ['.codeium/windsurf/skills'] },
31
- workbuddy: { label: 'WorkBuddy', dirs: ['.workbuddy/skills'] },
32
- kiro: { label: 'Kiro', dirs: ['.kiro/skills'] },
33
- trae: { label: 'Trae Code CLI', dirs: ['.traecli/skills'] },
34
- 'trae-cn': { label: 'Trae IDE(国内)', dirs: ['.trae-cn/skills'] },
35
- qwen: { label: 'Qwen Code', dirs: ['.qwen/skills'] },
36
- comate: { label: 'Comate 文心快码', dirs: ['.comate/skills'] },
37
- codebuddy: { label: 'CodeBuddy Code', dirs: ['.codebuddy/skills'] },
38
- kimi: { label: 'Kimi Code CLI', dirs: ['.kimi/skills'] },
39
- agents: { label: '通用 AGENTS.md', dirs: ['.agents/skills'] },
40
- };
41
-
42
- // Codex 用户级目录特判:优先 $CODEX_HOME/skills,否则 ~/.codex/skills
43
- function codexUserDir() {
44
- const base = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
45
- return path.join(base, 'skills');
46
- }
47
-
48
- // OpenCode 用户级目录特判:优先 $XDG_CONFIG_HOME/opencode/skills,否则 ~/.config/opencode/skills
49
- function opencodeUserDir() {
50
- const base = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
51
- return path.join(base, 'opencode', 'skills');
52
- }
53
-
54
- function resolveUserDir(rel) {
55
- if (rel === '.codex/skills') return codexUserDir();
56
- if (rel === '.config/opencode/skills') return opencodeUserDir();
57
- return path.join(os.homedir(), rel);
58
- }
59
-
60
- function installTo(dest) {
61
- const target = path.join(dest, SKILL_NAME);
62
- fs.mkdirSync(target, { recursive: true });
63
- copyDir(PKG_ROOT, target, new Set(['package.json', 'bin', 'node_modules', '.git']));
64
- console.log('installed -> ' + target);
65
- }
66
-
67
- function copyDir(src, dst, skip) {
68
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
69
- if (skip.has(entry.name)) continue;
70
- const s = path.join(src, entry.name);
71
- const d = path.join(dst, entry.name);
72
- if (entry.isDirectory()) {
73
- fs.mkdirSync(d, { recursive: true });
74
- copyDir(s, d, skip);
75
- } else if (entry.isFile()) {
76
- fs.copyFileSync(s, d);
77
- }
78
- }
79
- }
80
-
81
- function displayDir(rel) {
82
- if (process.platform === 'win32') return '%USERPROFILE%\\' + rel.replace(/\//g, '\\');
83
- return '~/' + rel;
84
- }
85
-
86
- function main() {
87
- const args = process.argv.slice(2);
88
- const isGlobal = args.includes('-g') || args.includes('--global');
89
- const list = args.includes('--list') || args.includes('-l');
90
- let explicitDir = null;
91
- const di = args.indexOf('--dir');
92
- if (di !== -1 && args[di + 1]) explicitDir = args[di + 1];
93
- let agent = null;
94
- const ai = args.indexOf('--agent');
95
- if (ai !== -1 && args[ai + 1]) agent = String(args[ai + 1]).toLowerCase();
96
-
97
- if (list) {
98
- console.log('智能体 -> 默认技能目录(--agent <name> 装到第一个,用户级):');
99
- for (const [key, v] of Object.entries(AGENT_DIRS)) {
100
- const resolved = v.dirs.map(displayDir);
101
- console.log(' ' + key.padEnd(10) + v.label.padEnd(18) + resolved.join('、'));
102
- }
103
- console.log('\n说明:Windows 用 %USERPROFILE%,Linux/macOS 用 ~;仅收录有官方默认目录的智能体。');
104
- console.log('改了目录的请用 --dir <路径>,不要依赖默认位置;若设置了 CODEX_HOME / XDG_CONFIG_HOME,安装自动以该变量为准。');
105
- return;
106
- }
107
-
108
- if (explicitDir) { installTo(explicitDir); return; }
109
-
110
- if (agent) {
111
- const info = AGENT_DIRS[agent];
112
- if (!info) {
113
- console.log('未收录智能体: ' + agent + '。请用 --dir <路径> 指定技能目录。');
114
- console.log('可用: ' + Object.keys(AGENT_DIRS).join(', '));
115
- return;
116
- }
117
- installTo(resolveUserDir(info.dirs[0]));
118
- console.log('完成。');
119
- return;
120
- }
121
-
122
- if (isGlobal) {
123
- const seen = new Set();
124
- for (const v of Object.values(AGENT_DIRS)) {
125
- for (const d of v.dirs) {
126
- if (seen.has(d)) continue;
127
- seen.add(d);
128
- installTo(resolveUserDir(d));
129
- }
130
- }
131
- console.log('完成。');
132
- return;
133
- }
134
-
135
- const PROJECT_DIRS = [
136
- '.claude/skills',
137
- '.cursor/skills',
138
- '.codex/skills',
139
- '.config/goose/skills',
140
- '.config/agents/skills',
141
- '.opencode/skills',
142
- '.codeium/windsurf/skills',
143
- '.workbuddy/skills',
144
- '.kiro/skills',
145
- '.traecli/skills',
146
- '.gemini/skills',
147
- '.trae-cn/skills',
148
- '.qwen/skills',
149
- '.comate/skills',
150
- '.codebuddy/skills',
151
- '.kimi/skills',
152
- '.agents/skills',
153
- ];
154
- let installedAny = false;
155
- for (const d of PROJECT_DIRS) {
156
- if (fs.existsSync(d)) { installTo(d); installedAny = true; }
157
- }
158
- if (!installedAny) {
159
- console.log('未检测到项目级智能体目录。可手动复制,或用 --agent <name> / -g 装到用户级。');
160
- }
161
- }
162
-
163
- main();
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @yottameta/yotta-logs cross-platform installer.
4
+ *
5
+ * Usage:
6
+ * npx -y @yottameta/yotta-logs --agent <name>
7
+ * npx -y @yottameta/yotta-logs --dir <path>
8
+ * npx -y @yottameta/yotta-logs -g
9
+ * npx -y @yottameta/yotta-logs
10
+ * npx -y @yottameta/yotta-logs --list
11
+ */
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const os = require('os');
17
+
18
+ const SKILL_NAME = 'yotta-logs';
19
+ const PKG_ROOT = path.join(__dirname, '..');
20
+ const COPY_SKIP = new Set(['package.json', 'bin', 'node_modules', '.git', '.github', 'test']);
21
+
22
+ const AGENT_DIRS = {
23
+ claude: { label: 'Claude Code', dirs: ['.claude/skills'] },
24
+ cursor: { label: 'Cursor', dirs: ['.cursor/skills', '.agents/skills'] },
25
+ codex: { label: 'Codex', dirs: ['.codex/skills'] },
26
+ gemini: { label: 'Gemini CLI', dirs: ['.gemini/skills', '.agents/skills'] },
27
+ goose: { label: 'Goose', dirs: ['.config/goose/skills', '.agents/skills'] },
28
+ amp: { label: 'Amp', dirs: ['.config/agents/skills', '.agents/skills'] },
29
+ opencode: { label: 'OpenCode', dirs: ['.config/opencode/skills'] },
30
+ windsurf: { label: 'Windsurf', dirs: ['.codeium/windsurf/skills'] },
31
+ workbuddy: { label: 'WorkBuddy', dirs: ['.workbuddy/skills'] },
32
+ kiro: { label: 'Kiro', dirs: ['.kiro/skills'] },
33
+ trae: { label: 'Trae Code CLI', dirs: ['.traecli/skills'] },
34
+ 'trae-cn': { label: 'Trae IDE', dirs: ['.trae-cn/skills'] },
35
+ qwen: { label: 'Qwen Code', dirs: ['.qwen/skills'] },
36
+ comate: { label: 'Comate', dirs: ['.comate/skills'] },
37
+ codebuddy: { label: 'CodeBuddy Code', dirs: ['.codebuddy/skills'] },
38
+ kimi: { label: 'Kimi Code CLI', dirs: ['.kimi/skills'] },
39
+ agents: { label: 'Generic AGENTS.md', dirs: ['.agents/skills'] },
40
+ };
41
+
42
+ class UsageError extends Error {}
43
+ class TargetError extends Error {}
44
+ class InstallError extends Error {}
45
+
46
+ function usage() {
47
+ console.log('yotta-logs installer');
48
+ console.log('');
49
+ console.log('Usage:');
50
+ console.log(' npx -y @yottameta/yotta-logs --agent <name> Install to an agent default directory');
51
+ console.log(' npx -y @yottameta/yotta-logs --dir <path> Install to a custom directory');
52
+ console.log(' npx -y @yottameta/yotta-logs -g Install to all known agent directories');
53
+ console.log(' npx -y @yottameta/yotta-logs Auto-detect project-level directories');
54
+ console.log(' npx -y @yottameta/yotta-logs --list List supported agent directories');
55
+ console.log('');
56
+ console.log('Options:');
57
+ console.log(' --agent <name> Agent key, see --list');
58
+ console.log(' --dir <path> Custom skills directory');
59
+ console.log(' -g, --global Install to all known user-level directories');
60
+ console.log(' --list, -l List supported agents');
61
+ console.log(' -h, --help Show this help');
62
+ }
63
+
64
+ function parseArgs(argv) {
65
+ const opts = { help: false, list: false, global: false, dir: null, agent: null };
66
+ for (let i = 0; i < argv.length; i++) {
67
+ const arg = argv[i];
68
+ if (arg === '--help' || arg === '-h') opts.help = true;
69
+ else if (arg === '--list' || arg === '-l') opts.list = true;
70
+ else if (arg === '--global' || arg === '-g') opts.global = true;
71
+ else if (arg === '--dir') {
72
+ const value = argv[++i];
73
+ if (!value) throw new UsageError('--dir requires a non-empty path');
74
+ opts.dir = value;
75
+ } else if (arg === '--agent') {
76
+ const value = argv[++i];
77
+ if (!value) throw new UsageError('--agent requires a non-empty name');
78
+ opts.agent = value.toLowerCase();
79
+ } else {
80
+ throw new UsageError('Unknown argument: ' + arg);
81
+ }
82
+ }
83
+ if (!opts.help) {
84
+ const selected = [opts.dir, opts.agent, opts.global].filter(Boolean).length;
85
+ if (selected > 1) throw new UsageError('Use only one of --dir, --agent, or -g');
86
+ }
87
+ return opts;
88
+ }
89
+
90
+ function codexUserDir() {
91
+ const base = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
92
+ return path.join(base, 'skills');
93
+ }
94
+
95
+ function opencodeUserDir() {
96
+ const base = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
97
+ return path.join(base, 'opencode', 'skills');
98
+ }
99
+
100
+ function resolveUserDir(rel) {
101
+ if (rel === '.codex/skills') return codexUserDir();
102
+ if (rel === '.config/opencode/skills') return opencodeUserDir();
103
+ return path.join(os.homedir(), rel);
104
+ }
105
+
106
+ function displayDir(rel) {
107
+ if (process.platform === 'win32') return '%USERPROFILE%\\' + rel.replace(/\//g, '\\');
108
+ return '~/' + rel;
109
+ }
110
+
111
+ function printList() {
112
+ console.log('Agent -> default skill directory:');
113
+ for (const [key, value] of Object.entries(AGENT_DIRS)) {
114
+ console.log(' ' + key.padEnd(12) + value.label.padEnd(20) + value.dirs.map(displayDir).join(', '));
115
+ }
116
+ console.log('');
117
+ console.log('Use --dir <path> for agents not listed. CODEX_HOME and XDG_CONFIG_HOME are respected.');
118
+ }
119
+
120
+ function assertSafeTarget(target) {
121
+ const rel = path.relative(PKG_ROOT, target);
122
+ if (rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel))) {
123
+ throw new UsageError('Target directory must be outside the skill source directory');
124
+ }
125
+ }
126
+
127
+ function copyDir(src, dst, skip) {
128
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
129
+ if (skip.has(entry.name)) continue;
130
+ const from = path.join(src, entry.name);
131
+ const to = path.join(dst, entry.name);
132
+ try {
133
+ if (entry.isDirectory()) {
134
+ fs.mkdirSync(to, { recursive: true });
135
+ copyDir(from, to, skip);
136
+ } else if (entry.isFile()) {
137
+ fs.copyFileSync(from, to);
138
+ }
139
+ } catch (err) {
140
+ throw new InstallError('Failed to copy ' + from + ' -> ' + to + ': ' + err.message);
141
+ }
142
+ }
143
+ }
144
+
145
+ function installTo(dest) {
146
+ if (!dest || typeof dest !== 'string') throw new UsageError('Destination directory is required');
147
+ const target = path.resolve(dest, SKILL_NAME);
148
+ assertSafeTarget(target);
149
+ try {
150
+ fs.mkdirSync(target, { recursive: true });
151
+ copyDir(PKG_ROOT, target, COPY_SKIP);
152
+ if (!fs.existsSync(path.join(target, 'SKILL.md'))) {
153
+ throw new InstallError('Installed directory is missing SKILL.md');
154
+ }
155
+ } catch (err) {
156
+ if (err instanceof UsageError || err instanceof InstallError) throw err;
157
+ throw new InstallError('Cannot install to ' + target + ': ' + err.message);
158
+ }
159
+ console.log('installed -> ' + target);
160
+ return target;
161
+ }
162
+
163
+ function projectDirs() {
164
+ return [
165
+ '.claude/skills',
166
+ '.cursor/skills',
167
+ '.codex/skills',
168
+ '.config/goose/skills',
169
+ '.config/agents/skills',
170
+ '.opencode/skills',
171
+ '.codeium/windsurf/skills',
172
+ '.workbuddy/skills',
173
+ '.kiro/skills',
174
+ '.traecli/skills',
175
+ '.gemini/skills',
176
+ '.trae-cn/skills',
177
+ '.qwen/skills',
178
+ '.comate/skills',
179
+ '.codebuddy/skills',
180
+ '.kimi/skills',
181
+ '.agents/skills',
182
+ ].filter((dir) => fs.existsSync(dir));
183
+ }
184
+
185
+ function run() {
186
+ const opts = parseArgs(process.argv.slice(2));
187
+ if (opts.help) { usage(); return; }
188
+ if (opts.list) { printList(); return; }
189
+
190
+ if (opts.dir) { installTo(opts.dir); return; }
191
+
192
+ if (opts.agent) {
193
+ const info = AGENT_DIRS[opts.agent];
194
+ if (!info) {
195
+ throw new UsageError('Unknown agent: ' + opts.agent + '. Available: ' + Object.keys(AGENT_DIRS).join(', ') + '. Use --dir for a custom directory.');
196
+ }
197
+ installTo(resolveUserDir(info.dirs[0]));
198
+ return;
199
+ }
200
+
201
+ if (opts.global) {
202
+ const seen = new Set();
203
+ let count = 0;
204
+ for (const value of Object.values(AGENT_DIRS)) {
205
+ for (const dir of value.dirs) {
206
+ if (seen.has(dir)) continue;
207
+ seen.add(dir);
208
+ installTo(resolveUserDir(dir));
209
+ count++;
210
+ }
211
+ }
212
+ console.log('Installed to ' + count + ' directories.');
213
+ return;
214
+ }
215
+
216
+ const dirs = projectDirs();
217
+ if (!dirs.length) {
218
+ throw new TargetError('No project-level agent directory detected. Use --agent <name> or --dir <path>.');
219
+ }
220
+ for (const dir of dirs) installTo(dir);
221
+ }
222
+
223
+ function main() {
224
+ try {
225
+ run();
226
+ } catch (err) {
227
+ if (err instanceof UsageError) {
228
+ console.error('Usage error: ' + err.message);
229
+ usage();
230
+ process.exitCode = 2;
231
+ } else if (err instanceof TargetError) {
232
+ console.error('Target error: ' + err.message);
233
+ process.exitCode = 4;
234
+ } else if (err instanceof InstallError) {
235
+ console.error('Install failed: ' + err.message);
236
+ console.error('Fix: check directory permissions and free space, then retry. Use --dir to choose another directory.');
237
+ process.exitCode = 1;
238
+ } else {
239
+ console.error('Unexpected error: ' + (err && err.message ? err.message : String(err)));
240
+ process.exitCode = 1;
241
+ }
242
+ }
243
+ }
244
+
245
+ main();
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.0",
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,10 @@
18
18
  "bin",
19
19
  "NOTICE",
20
20
  "CHANGELOG.md",
21
- "README.zh-CN.md"
21
+ "README.zh-CN.md",
22
+ "!scripts/__pycache__",
23
+ "!**/__pycache__",
24
+ "!**/*.pyc"
22
25
  ],
23
26
  "repository": {
24
27
  "type": "git",
@@ -29,5 +32,8 @@
29
32
  },
30
33
  "bin": {
31
34
  "yotta-logs": "bin/install.js"
35
+ },
36
+ "scripts": {
37
+ "test": "node --test \"test/*.test.js\" && python scripts/test_yotta_logs.py"
32
38
  }
33
39
  }
@@ -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` 显式开;
@@ -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.0"
68
68
  TOOL_NAME = "yotta-logs"
69
69
  TOOL_CN = "元史"
70
70
  DEFAULT_LIMIT = 50