codexmate 0.0.48 → 0.0.50

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 CHANGED
@@ -58,6 +58,7 @@ Unlike simple wrappers, Codex Mate acts as a **Local Agent Bridge**:
58
58
  - **Claude Provider Bridge**: Connect Claude Code to OpenAI Chat Completions-compatible providers and Ollama through the built-in local Claude-compatible proxy.
59
59
  - **OpenCode Provider Control**: Manage OpenCode provider/model selection with a CodexMate-owned provider store under `~/.codexmate`, projecting only the active provider into native OpenCode config to avoid polluting or deleting user-owned settings.
60
60
  - **Skills Marketplace**: A local-first market to share and import skills between different agent apps.
61
+ - **Prompt File Editor**: Unified editor for global and project-level `CLAUDE.md` and `AGENTS.md` with auto-detection of project paths.
61
62
  - **Task Orchestrator**: Plan and execute complex tasks with dependency tracking.
62
63
 
63
64
  ---
@@ -76,6 +77,7 @@ Unlike simple wrappers, Codex Mate acts as a **Local Agent Bridge**:
76
77
  | **Claude Provider Bridge** | ✅ | Connect Claude Code to OpenAI Chat Completions-compatible providers and Ollama via the built-in Claude-compatible proxy |
77
78
  | **OpenCode Provider Store** | ✅ | Keep multiple OpenCode providers in `~/.codexmate` while projecting only the selected provider to native OpenCode config |
78
79
  | **Prompt Templates** | ✅ | Reusable prompt plugins with variables |
80
+ | **Prompt File Editor** | ✅ | Edit global and project-level CLAUDE.md / AGENTS.md with auto-detect and path switching |
79
81
  | **MCP Integration** | ✅ | Expose local tools and resources via MCP stdio |
80
82
  | **Auto Update** | ✅ | Quick update CLI via `codexmate update` |
81
83
 
package/README.zh.md CHANGED
@@ -58,6 +58,7 @@
58
58
  - **Claude Provider 桥接**:通过内建本地 Claude 兼容代理,让 Claude Code 接入 OpenAI Chat Completions 兼容 provider 与 Ollama。
59
59
  - **OpenCode Provider 控制**:在 `~/.codexmate` 下维护 CodexMate 自有的 OpenCode 多 provider 存储,只将当前选中的 provider 投影到 OpenCode 原生配置,避免污染或误删用户已有配置。
60
60
  - **Skills 市场**:本地优先的市场,支持在不同的智能体应用之间共享和导入 Skills。
61
+ - **提示词文件编辑器**:统一编辑全局和项目级 `CLAUDE.md` 与 `AGENTS.md`,支持项目路径自动检测。
61
62
  - **任务编排器**:支持带有依赖跟踪的复杂任务规划与执行。
62
63
 
63
64
  ---
@@ -76,6 +77,7 @@
76
77
  | **Claude Provider 桥接** | ✅ | 通过内建 Claude 兼容代理,让 Claude Code 接入 OpenAI Chat Completions 兼容 provider 与 Ollama |
77
78
  | **OpenCode Provider 存储** | ✅ | 在 `~/.codexmate` 中保留多个 OpenCode provider,只将当前选中的 provider 投影到 OpenCode 原生配置 |
78
79
  | **提示词模板** | ✅ | 支持变量的可复用提示词插件 |
80
+ | **提示词文件编辑器** | ✅ | 编辑全局和项目级 CLAUDE.md / AGENTS.md,支持自动检测与路径切换 |
79
81
  | **MCP 集成** | ✅ | 通过 MCP stdio 暴露本地工具与资源 |
80
82
  | **自动更新** | ✅ | 通过 `codexmate update` 快速更新 CLI |
81
83
 
@@ -53,57 +53,119 @@ function createAgentsFileController(deps = {}) {
53
53
  return { ok: true, dirPath };
54
54
  }
55
55
 
56
- function resolveClaudeMdFilePath() {
57
- return path.join(CLAUDE_DIR, CLAUDE_MD_FILE_NAME);
56
+ function detectProjectClaudeMdDir(baseDir) {
57
+ if (typeof baseDir !== 'string' || !baseDir.trim()) {
58
+ return { error: 'project path is required' };
59
+ }
60
+ const root = baseDir.trim();
61
+ const rootPath = path.join(root, CLAUDE_MD_FILE_NAME);
62
+ const dotDirPath = path.join(root, '.claude', CLAUDE_MD_FILE_NAME);
63
+ try {
64
+ if (fs.statSync(rootPath).isFile()) {
65
+ return { path: rootPath, source: 'root', dir: root };
66
+ }
67
+ } catch (_) {}
68
+ try {
69
+ if (fs.statSync(dotDirPath).isFile()) {
70
+ return { path: dotDirPath, source: 'dotdir', dir: path.join(root, '.claude') };
71
+ }
72
+ } catch (_) {}
73
+ return { path: rootPath, source: 'root', dir: root };
74
+ }
75
+
76
+ function validateClaudeMdBaseDir(filePath) {
77
+ const dirPath = path.dirname(filePath);
78
+ try {
79
+ const stat = fs.statSync(dirPath);
80
+ if (!stat.isDirectory()) {
81
+ return { error: 'project directory is not a directory: ' + dirPath };
82
+ }
83
+ } catch (e) {
84
+ return { error: 'project directory does not exist: ' + dirPath };
85
+ }
86
+ return { ok: true, dirPath };
87
+ }
88
+
89
+ function resolveClaudeMdFilePath(params = {}) {
90
+ const baseDir = typeof params.baseDir === 'string' && params.baseDir.trim()
91
+ ? params.baseDir.trim()
92
+ : '';
93
+ if (!baseDir) {
94
+ return { filePath: path.join(CLAUDE_DIR, CLAUDE_MD_FILE_NAME), isProject: false };
95
+ }
96
+ var detected = detectProjectClaudeMdDir(baseDir);
97
+ if (detected.error) {
98
+ return { filePath: path.join(CLAUDE_DIR, CLAUDE_MD_FILE_NAME), isProject: false, detectionError: detected.error };
99
+ }
100
+ return { filePath: detected.path, isProject: true, detectionSource: detected.source, projectPath: baseDir };
58
101
  }
59
102
 
60
103
  function readClaudeMdFile(params = {}) {
61
- const filePath = resolveClaudeMdFilePath();
62
- const lineEndingFallback = os.EOL === '\r\n' ? '\r\n' : '\n';
104
+ var resolved = resolveClaudeMdFilePath(params);
105
+ var filePath = resolved.filePath;
106
+ var lineEndingFallback = os.EOL === '\r\n' ? '\r\n' : '\n';
107
+ var base = {
108
+ path: filePath,
109
+ lineEnding: lineEndingFallback
110
+ };
111
+ if (resolved.isProject) {
112
+ base.detectionSource = resolved.detectionSource;
113
+ base.projectPath = resolved.projectPath;
114
+ }
115
+ if (resolved.detectionError) {
116
+ base.detectionError = resolved.detectionError;
117
+ }
118
+ if (resolved.isProject) {
119
+ var dirCheck = validateClaudeMdBaseDir(filePath);
120
+ if (dirCheck.error) {
121
+ return { error: dirCheck.error };
122
+ }
123
+ }
63
124
  if (!fs.existsSync(filePath)) {
64
- return {
65
- exists: false,
66
- path: filePath,
67
- content: '',
68
- lineEnding: lineEndingFallback
69
- };
125
+ return Object.assign({ exists: false, content: '' }, base);
70
126
  }
71
127
  if (params.metaOnly) {
72
- return {
73
- exists: true,
74
- path: filePath,
75
- content: '',
76
- lineEnding: lineEndingFallback
77
- };
128
+ return Object.assign({ exists: true, content: '' }, base);
78
129
  }
79
130
  try {
80
- const raw = fs.readFileSync(filePath, 'utf-8');
81
- return {
131
+ var raw = fs.readFileSync(filePath, 'utf-8');
132
+ var result = {
82
133
  exists: true,
83
134
  path: filePath,
84
135
  content: stripUtf8Bom(raw),
85
136
  lineEnding: detectLineEnding(raw)
86
137
  };
138
+ if (resolved.isProject) {
139
+ result.detectionSource = resolved.detectionSource;
140
+ result.projectPath = resolved.projectPath;
141
+ }
142
+ return result;
87
143
  } catch (e) {
88
- return { error: `读取 CLAUDE.md 失败: ${e.message}` };
144
+ return { error: 'read CLAUDE.md failed: ' + e.message };
89
145
  }
90
146
  }
91
147
 
92
148
  function applyClaudeMdFile(params = {}) {
93
- const filePath = resolveClaudeMdFilePath();
94
- const content = typeof params.content === 'string' ? params.content : '';
149
+ var resolved = resolveClaudeMdFilePath(params);
150
+ var filePath = resolved.filePath;
151
+ var content = typeof params.content === 'string' ? params.content : '';
95
152
  if (content.length > 2 * 1024 * 1024) {
96
- return { error: '内容过大(最大 2MB' };
153
+ return { error: 'content too large (max 2MB)' };
97
154
  }
98
- const lineEnding = params.lineEnding === '\r\n' ? '\r\n' : '\n';
99
- const normalized = normalizeLineEnding(content, lineEnding);
100
- const finalContent = ensureUtf8Bom(normalized);
155
+ var lineEnding = params.lineEnding === '\r\n' ? '\r\n' : '\n';
156
+ var normalized = normalizeLineEnding(content, lineEnding);
157
+ var finalContent = ensureUtf8Bom(normalized);
101
158
  try {
102
- ensureDir(CLAUDE_DIR);
159
+ ensureDir(path.dirname(filePath));
103
160
  fs.writeFileSync(filePath, finalContent, 'utf-8');
104
- return { success: true, path: filePath };
161
+ var result = { success: true, path: filePath };
162
+ if (resolved.isProject) {
163
+ result.projectPath = resolved.projectPath;
164
+ result.detectionSource = resolved.detectionSource;
165
+ }
166
+ return result;
105
167
  } catch (e) {
106
- return { error: `写入 CLAUDE.md 失败: ${e.message}` };
168
+ return { error: 'write CLAUDE.md failed: ' + e.message };
107
169
  }
108
170
  }
109
171
 
@@ -181,6 +243,11 @@ function createAgentsFileController(deps = {}) {
181
243
  let readResult;
182
244
  if (context === 'claude-md') {
183
245
  readResult = readClaudeMdFile({ metaOnly });
246
+ } else if (context === 'claude-project') {
247
+ if (!params.baseDir || !String(params.baseDir).trim()) {
248
+ return { error: 'project path is required for claude-project context' };
249
+ }
250
+ readResult = readClaudeMdFile({ ...params, metaOnly });
184
251
  } else if (context === 'openclaw') {
185
252
  readResult = readOpenclawAgentsFile({ metaOnly });
186
253
  } else if (context === 'openclaw-workspace') {
@@ -215,6 +282,8 @@ function createAgentsFileController(deps = {}) {
215
282
  return {
216
283
  resolveAgentsFilePath,
217
284
  validateAgentsBaseDir,
285
+ detectProjectClaudeMdDir,
286
+ validateClaudeMdBaseDir,
218
287
  resolveClaudeMdFilePath,
219
288
  readClaudeMdFile,
220
289
  applyClaudeMdFile,
package/cli.js CHANGED
@@ -1828,6 +1828,8 @@ async function fetchProviderModels(providerName, overrides = {}) {
1828
1828
  const {
1829
1829
  resolveAgentsFilePath,
1830
1830
  validateAgentsBaseDir,
1831
+ detectProjectClaudeMdDir,
1832
+ validateClaudeMdBaseDir,
1831
1833
  resolveClaudeMdFilePath,
1832
1834
  readClaudeMdFile,
1833
1835
  applyClaudeMdFile,
@@ -11876,10 +11878,16 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
11876
11878
  case 'apply-claude-md-file':
11877
11879
  result = applyClaudeMdFile(params || {});
11878
11880
  if (result && !result.error) {
11879
- const mdTarget = (params && params.targetPath) ? String(params.targetPath) : 'CLAUDE.md';
11880
- notifyWebhook('claude-md-edit', 'CLAUDE.md modified: ' + mdTarget, { targetPath: mdTarget }).catch(function () { });
11881
+ const mdBaseDir = params && params.baseDir ? String(params.baseDir).trim() : '';
11882
+ const mdTarget = mdBaseDir
11883
+ ? path.join(mdBaseDir, 'CLAUDE.md')
11884
+ : ((params && params.targetPath) ? String(params.targetPath) : 'CLAUDE.md');
11885
+ notifyWebhook('claude-md-edit', 'CLAUDE.md modified: ' + mdTarget, { targetPath: mdTarget, projectPath: mdBaseDir }).catch(function () { });
11881
11886
  }
11882
11887
  break;
11888
+ case 'detect-project-claude-md':
11889
+ result = detectProjectClaudeMdDir((params && params.baseDir) || '');
11890
+ break;
11883
11891
  case 'preview-agents-diff':
11884
11892
  result = buildAgentsDiff(params || {});
11885
11893
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexmate",
3
- "version": "0.0.48",
3
+ "version": "0.0.50",
4
4
  "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -10,6 +10,7 @@
10
10
  "cli.js",
11
11
  "cli/",
12
12
  "plugins/",
13
+ "skills/",
13
14
  "web-ui.html",
14
15
  "lib/",
15
16
  "web-ui/",
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: codexmate-project-context-recovery
3
+ description: Recovers project handoff context from local Codex, Claude Code, Gemini, CodeBuddy, and codexmate-derived sessions. Use when the user asks what happened in prior project/PR/branch/file/error work, needs a handoff brief, wants old decisions or validations recovered, or asks to summarize cross-session project activity with evidence.
4
+ ---
5
+
6
+ # Codexmate Project Context Recovery
7
+
8
+ ## Overview
9
+
10
+ Use this skill to build an evidence-first project handoff brief from local agent sessions. It is not semantic memory or a live GitHub source of truth. It works best with hard identifiers: `owner/repo`, branch names, file paths, commit hashes, exact errors, PR/issue numbers plus repo filters, or unique commands.
11
+
12
+ Old sessions are historical evidence. Mutable facts such as PR state, CI, releases, deployments, and current files still require live checks before action.
13
+
14
+ ## Quick Start
15
+
16
+ Generate a context brief first. Resolve `scripts/search_sessions.py` relative to this skill directory; after npm installation the full package path is `node_modules/codexmate/skills/codexmate-project-context-recovery/scripts/search_sessions.py`.
17
+
18
+ ```bash
19
+ python3 scripts/search_sessions.py "SakuraByteCore/codexmate feat/task-orchestration-tab" --mode brief --source all --path-filter codexmate --match all --format text --limit 8
20
+ ```
21
+
22
+ Use plain search when you need raw candidates:
23
+
24
+ ```bash
25
+ python3 scripts/search_sessions.py "exact error text" --mode search --source all --path-filter codexmate --format text --limit 10
26
+ ```
27
+
28
+ ## Workflow
29
+
30
+ 1. **Confirm the object**: repo/project, PR/issue number, branch, file path, command, exact error text, person, or date range. If multiple projects match, state the chosen object before searching.
31
+ 2. **Prefer hard identifiers**: exact `owner/repo`, short repo name, PR/issue number with repo filter, branch, file path, commit hash, exact error, then user wording.
32
+ 3. **Generate a brief**: use `--mode brief`; add `--path-filter` for project/worktree isolation; use `--match all` when the query has strong identifiers.
33
+ 4. **Check confidence**: `high` means multiple hard signals appeared; `medium/weak` means re-query with stronger identifiers before relying on it; `none` means no hits were found.
34
+ 5. **Use the brief as a handoff**: extract timeline, decisions, validations, risks, files, commands, commits, and top evidence sessions.
35
+ 6. **Live-check mutable facts**: before commenting, merging, releasing, or claiming current status, check GitHub/current files directly.
36
+
37
+ ## What Good Output Looks Like
38
+
39
+ - **Target Object:** repo / PR / branch / file / error
40
+ - **Context Brief:** confidence, top sources, timeline, repos/branches/PRs/files
41
+ - **Historical Evidence:** session source/id/path + snippets
42
+ - **Handoff Summary:** decisions, validations, risks, commands, commits
43
+ - **Live Verification:** PR/check/review/release/current-file facts that still need live verification
44
+
45
+ ## Optional codexmate MCP Path
46
+
47
+ If codexmate MCP is configured and healthy, use it to inspect strong candidates:
48
+
49
+ - `codexmate.session.list` with `source`, `query`, `queryScope: "all"`, `limit`, and `forceRefresh: true`.
50
+ - `codexmate.session.detail` for candidate session inspection.
51
+ - `codexmate.session.export` only when a markdown export is useful.
52
+
53
+ If MCP is unavailable, do not block; run `scripts/search_sessions.py`.
54
+
55
+ ## Limits
56
+
57
+ - Generic natural-language queries can be noisy.
58
+ - Short PR numbers without repo/path filters are weak signals.
59
+ - Session logs may contain stale, failed, or speculative work.
60
+ - The brief should guide investigation; it must not replace current repo/GitHub verification.
61
+
62
+ ## Privacy
63
+
64
+ Share only context relevant to the current task. Do not quote credentials, unrelated personal details, private memory, or large transcript chunks. In group chats, summarize narrowly and avoid exposing unrelated sessions.