codexmate 0.0.48 → 0.0.49

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.49",
4
4
  "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
5
  "main": "cli.js",
6
6
  "bin": {
package/web-ui/app.js CHANGED
@@ -74,6 +74,9 @@ document.addEventListener('DOMContentLoaded', () => {
74
74
  showConfigTemplateModal: false,
75
75
  showAgentsModal: false,
76
76
  promptsSubTab: 'codex',
77
+ projectClaudeMdPath: '',
78
+ projectPathOptions: [],
79
+ projectPathOptionsLoading: false,
77
80
  showSkillsModal: false,
78
81
  showHealthCheckModal: false,
79
82
  showCodexBridgePoolModal: false,
@@ -574,6 +577,18 @@ document.addEventListener('DOMContentLoaded', () => {
574
577
  this.sessionTrashEnabled = this.normalizeSessionTrashEnabled(localStorage.getItem('codexmateSessionTrashEnabled'));
575
578
  this.sessionTrashRetentionDays = this.normalizeSessionTrashRetentionDays(localStorage.getItem('codexmateSessionTrashRetentionDays'));
576
579
  this.configTemplateDiffConfirmEnabled = loadConfigTemplateDiffConfirmEnabledFromStorage(localStorage);
580
+ try {
581
+ var savedProjectPath = localStorage.getItem('codexmate_project_claude_md_path');
582
+ if (savedProjectPath) {
583
+ this.projectClaudeMdPath = savedProjectPath;
584
+ }
585
+ } catch (_) {}
586
+ try {
587
+ var savedSubTab = localStorage.getItem('codexmate_prompts_sub_tab');
588
+ if (savedSubTab === 'codex' || savedSubTab === 'claude-project') {
589
+ this.promptsSubTab = savedSubTab;
590
+ }
591
+ } catch (_) {}
577
592
  window.addEventListener('resize', this.onWindowResize);
578
593
  window.addEventListener('keydown', this.handleGlobalKeydown);
579
594
  window.addEventListener('beforeunload', this.handleBeforeUnload);
@@ -740,10 +755,22 @@ document.addEventListener('DOMContentLoaded', () => {
740
755
  this.loadPromptsContent();
741
756
  }
742
757
  },
743
- promptsSubTab() {
758
+ promptsSubTab(newVal) {
759
+ try {
760
+ localStorage.setItem('codexmate_prompts_sub_tab', newVal);
761
+ } catch (_) {}
744
762
  if (this.mainTab === 'prompts' && typeof this.loadPromptsContent === 'function') {
745
763
  this.loadPromptsContent();
746
764
  }
765
+ },
766
+ projectClaudeMdPath(newPath) {
767
+ try {
768
+ if (newPath) {
769
+ localStorage.setItem('codexmate_project_claude_md_path', newPath);
770
+ } else {
771
+ localStorage.removeItem('codexmate_project_claude_md_path');
772
+ }
773
+ } catch (_) {}
747
774
  }
748
775
  },
749
776
 
@@ -336,6 +336,12 @@ export function buildAgentsDiffPreviewRequest(options = {}) {
336
336
  params.fileName = fileName;
337
337
  }
338
338
  }
339
+ if (context === 'claude-project') {
340
+ const baseDir = typeof options.baseDir === 'string' ? options.baseDir.trim() : '';
341
+ if (baseDir) {
342
+ params.baseDir = baseDir;
343
+ }
344
+ }
339
345
 
340
346
  const maxRequestBytes = Number.isFinite(options.maxRequestBytes)
341
347
  ? Math.max(1024, Math.floor(options.maxRequestBytes))
@@ -37,11 +37,16 @@ export function createAgentsMethods(options = {}) {
37
37
 
38
38
  return {
39
39
  async openClaudeMdEditor() {
40
- this.setAgentsModalContext('claude-md');
40
+ this.setAgentsModalContext('claude-project');
41
41
  const requestToken = issueLatestRequestToken(this, '_agentsOpenRequestToken');
42
42
  this.agentsLoading = true;
43
43
  try {
44
- const res = await api('get-claude-md-file');
44
+ const rpcParams = {};
45
+ var projectPath = (this.projectClaudeMdPath || '').trim();
46
+ if (projectPath) {
47
+ rpcParams.baseDir = projectPath;
48
+ }
49
+ const res = await api('get-claude-md-file', rpcParams);
45
50
  if (!isLatestRequestToken(this, '_agentsOpenRequestToken', requestToken)) {
46
51
  return;
47
52
  }
@@ -182,11 +187,18 @@ export function createAgentsMethods(options = {}) {
182
187
  setAgentsModalContext(context, options = {}) {
183
188
  const t = typeof this.t === 'function' ? this.t : null;
184
189
  const tr = (key, fallback, params = null) => (t ? t(key, params) : fallback);
185
- if (context === 'claude-md') {
186
- this.agentsContext = 'claude-md';
190
+ if (context === 'claude-project') {
191
+ var projectPath = (options.projectPath || this.projectClaudeMdPath || '').trim();
192
+ this.agentsContext = 'claude-project';
187
193
  this.agentsWorkspaceFileName = '';
188
- this.agentsModalTitle = tr('modal.agents.title.claudeMd', 'CLAUDE.md 编辑器');
189
- this.agentsModalHint = tr('modal.agents.hint.claudeMd', '保存后会写入 ~/.claude/CLAUDE.md。');
194
+ this.projectClaudeMdPath = projectPath;
195
+ if (projectPath) {
196
+ this.agentsModalTitle = tr('modal.agents.title.claudeProject', 'Project CLAUDE.md: ' + projectPath, { path: projectPath });
197
+ this.agentsModalHint = tr('modal.agents.hint.claudeProject', 'Saved content will be written to CLAUDE.md in: ' + projectPath, { path: projectPath });
198
+ } else {
199
+ this.agentsModalTitle = tr('modal.agents.title.claudeProjectGlobal', 'Global CLAUDE.md');
200
+ this.agentsModalHint = tr('modal.agents.hint.claudeProjectGlobal', 'Saved content will be written to ~/.claude/CLAUDE.md.');
201
+ }
190
202
  return;
191
203
  }
192
204
  if (context === 'openclaw-workspace') {
@@ -420,6 +432,41 @@ export function createAgentsMethods(options = {}) {
420
432
  this.resetAgentsDiffState();
421
433
  }
422
434
  },
435
+ selectProjectClaudeMdPath(pathValue) {
436
+ this.projectClaudeMdPath = (pathValue || '').trim();
437
+ if (this.promptsSubTab === 'claude-project' && this.mainTab === 'prompts') {
438
+ this.loadPromptsContent();
439
+ }
440
+ },
441
+ setProjectClaudeMdPathManual(pathValue) {
442
+ var trimmed = (pathValue || '').trim();
443
+ if (trimmed === (this.projectClaudeMdPath || '').trim()) {
444
+ return;
445
+ }
446
+ this.projectClaudeMdPath = trimmed;
447
+ if (this.promptsSubTab === 'claude-project' && this.mainTab === 'prompts') {
448
+ this.loadPromptsContent();
449
+ }
450
+ },
451
+ async loadProjectPathOptions() {
452
+ if (this.projectPathOptionsLoading) {
453
+ return;
454
+ }
455
+ this.projectPathOptionsLoading = true;
456
+ try {
457
+ const res = await api('list-session-paths', { source: 'claude', limit: 100 });
458
+ if (res && Array.isArray(res.paths)) {
459
+ this.projectPathOptions = res.paths
460
+ .map(function (p) { return (p && p.cwd) || p || ''; })
461
+ .filter(Boolean)
462
+ .filter(function (v, i, a) { return a.indexOf(v) === i; });
463
+ }
464
+ } catch (_) {
465
+ // silent
466
+ } finally {
467
+ this.projectPathOptionsLoading = false;
468
+ }
469
+ },
423
470
  async pasteAgentsContent() {
424
471
  if (this.agentsLoading || this.agentsSaving || this.agentsDiffVisible) {
425
472
  return;
@@ -444,10 +491,11 @@ export function createAgentsMethods(options = {}) {
444
491
  const fileName = context === 'openclaw-workspace'
445
492
  ? (this.agentsWorkspaceFileName || '')
446
493
  : '';
494
+ const projectPath = context === 'claude-project' ? (this.projectClaudeMdPath || '') : '';
447
495
  const lineEnding = this.agentsLineEnding || '\n';
448
496
  const content = typeof this.agentsContent === 'string' ? this.agentsContent : '';
449
497
  const original = typeof this.agentsOriginalContent === 'string' ? this.agentsOriginalContent : '';
450
- return `${context}::${fileName}::${lineEnding}::${content.length}::${content}::${original.length}::${original}`;
498
+ return `${context}::${fileName}::${projectPath}::${lineEnding}::${content.length}::${content}::${original.length}::${original}`;
451
499
  },
452
500
  async prepareAgentsDiff() {
453
501
  const requestFingerprint = this.buildAgentsDiffFingerprint();
@@ -504,7 +552,8 @@ export function createAgentsMethods(options = {}) {
504
552
  content: this.agentsContent,
505
553
  lineEnding: this.agentsLineEnding,
506
554
  context: this.agentsContext,
507
- fileName: this.agentsWorkspaceFileName
555
+ fileName: this.agentsWorkspaceFileName,
556
+ baseDir: this.agentsContext === 'claude-project' ? this.projectClaudeMdPath : undefined
508
557
  });
509
558
  if (previewRequest.exceedsBodyLimit) {
510
559
  applyPreviewState(buildAgentsDiffPreview({
@@ -621,8 +670,12 @@ export function createAgentsMethods(options = {}) {
621
670
  content: this.agentsContent,
622
671
  lineEnding: this.agentsLineEnding
623
672
  };
624
- if (this.agentsContext === 'claude-md') {
673
+ if (this.agentsContext === 'claude-project') {
625
674
  action = 'apply-claude-md-file';
675
+ const projectPath = (this.projectClaudeMdPath || '').trim();
676
+ if (projectPath) {
677
+ params.baseDir = projectPath;
678
+ }
626
679
  } else if (this.agentsContext === 'openclaw') {
627
680
  action = 'apply-openclaw-agents-file';
628
681
  } else if (this.agentsContext === 'openclaw-workspace') {
@@ -636,8 +689,8 @@ export function createAgentsMethods(options = {}) {
636
689
  }
637
690
  const successLabel = this.agentsContext === 'openclaw-workspace'
638
691
  ? this.t('toast.agents.saved.workspace', { name: this.agentsWorkspaceFileName || '' }).replace(/:\s*$/, '')
639
- : (this.agentsContext === 'claude-md'
640
- ? this.t('toast.agents.saved.claudeMd')
692
+ : (this.agentsContext === 'claude-project'
693
+ ? this.t('toast.agents.saved.claudeProject')
641
694
  : (this.agentsContext === 'openclaw' ? this.t('toast.agents.saved.openclaw') : this.t('toast.agents.saved.agents')));
642
695
  this.showMessage(successLabel, 'success');
643
696
  if (this.mainTab === 'prompts') {
@@ -653,7 +706,10 @@ export function createAgentsMethods(options = {}) {
653
706
  },
654
707
 
655
708
  switchPromptsSubTab(subTab) {
656
- const normalized = subTab === 'claude-md' ? 'claude-md' : 'codex';
709
+ const normalized = subTab === 'claude-project' ? 'claude-project' : 'codex';
710
+ if (normalized === 'claude-project' && !this.projectPathOptions.length && !this.projectPathOptionsLoading) {
711
+ this.loadProjectPathOptions();
712
+ }
657
713
  if (this.promptsSubTab === normalized) {
658
714
  this.loadPromptsContent();
659
715
  return;
@@ -666,9 +722,19 @@ export function createAgentsMethods(options = {}) {
666
722
  this.agentsLoading = true;
667
723
  this.resetAgentsDiffState();
668
724
  try {
669
- const isClaude = this.promptsSubTab === 'claude-md';
670
- const action = isClaude ? 'get-claude-md-file' : 'get-agents-file';
671
- const res = await api(action);
725
+ const subTab = this.promptsSubTab;
726
+ let action;
727
+ const rpcParams = {};
728
+ if (subTab === 'claude-project') {
729
+ action = 'get-claude-md-file';
730
+ const projectPath = (this.projectClaudeMdPath || '').trim();
731
+ if (projectPath) {
732
+ rpcParams.baseDir = projectPath;
733
+ }
734
+ } else {
735
+ action = 'get-agents-file';
736
+ }
737
+ const res = await api(action, rpcParams);
672
738
  if (!isLatestRequestToken(this, '_agentsOpenRequestToken', requestToken)) {
673
739
  return;
674
740
  }
@@ -681,7 +747,7 @@ export function createAgentsMethods(options = {}) {
681
747
  this.agentsPath = res.path || '';
682
748
  this.agentsExists = !!res.exists;
683
749
  this.agentsLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
684
- this.agentsContext = isClaude ? 'claude-md' : 'codex';
750
+ this.agentsContext = subTab === 'claude-project' ? 'claude-project' : 'codex';
685
751
  } catch (e) {
686
752
  if (!isLatestRequestToken(this, '_agentsOpenRequestToken', requestToken)) {
687
753
  return;
@@ -220,7 +220,11 @@ const en = Object.freeze({
220
220
  'subtitle.settings': 'Manage downloads, directories, and trash.',
221
221
  'subtitle.prompts': 'Edit AGENTS.md and CLAUDE.md.',
222
222
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
223
- 'prompts.subTab.claude': 'CLAUDE.md (Claude)',
223
+ 'prompts.subTab.project': 'CLAUDE.md',
224
+ 'prompts.project.pathLabel': 'Project path (optional)',
225
+ 'prompts.project.selectPlaceholder': '-- Global (~/.claude/CLAUDE.md) --',
226
+ 'prompts.project.manualPlaceholder': 'Or enter a project root path...',
227
+ 'prompts.project.detected': 'Editing CLAUDE.md in: {path}',
224
228
  'dashboard.doctor.title': 'Doctor',
225
229
  'dashboard.doctor.runChecks': 'Run checks',
226
230
  'dashboard.doctor.checking': 'Checking...',
@@ -401,7 +405,7 @@ const en = Object.freeze({
401
405
  'toast.save.ok': 'Saved',
402
406
  'toast.save.fail': 'Save failed',
403
407
  'toast.agents.saved.agents': 'AGENTS.md saved',
404
- 'toast.agents.saved.claudeMd': 'CLAUDE.md saved',
408
+ 'toast.agents.saved.claudeProject': 'Project CLAUDE.md saved',
405
409
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md saved',
406
410
  'toast.agents.saved.workspace': 'Workspace file saved: {name}',
407
411
  'toast.delete.ok': 'Deleted',
@@ -872,12 +876,14 @@ const en = Object.freeze({
872
876
  'config.autoCompact.hint': 'Auto-compaction threshold (default 185000).',
873
877
  'config.agents.open': 'Open AGENTS.md',
874
878
  'modal.agents.title.default': 'AGENTS.md editor',
875
- 'modal.agents.title.claudeMd': 'CLAUDE.md editor',
876
879
  'modal.agents.title.openclaw': 'OpenClaw AGENTS.md editor',
877
880
  'modal.agents.hint.default': 'Saved content will be written to AGENTS.md (next to config.toml).',
878
- 'modal.agents.hint.claudeMd': 'Saved content will be written to ~/.claude/CLAUDE.md.',
879
- 'modal.agents.contentLabel.claudeMd': 'CLAUDE.md content',
880
- 'modal.agents.placeholder.claudeMd': 'Edit CLAUDE.md here',
881
+ 'modal.agents.title.claudeProject': 'Project CLAUDE.md: {path}',
882
+ 'modal.agents.hint.claudeProject': 'Saved content will be written to CLAUDE.md in: {path}',
883
+ 'modal.agents.title.claudeProjectGlobal': 'Global CLAUDE.md',
884
+ 'modal.agents.hint.claudeProjectGlobal': 'Saved content will be written to ~/.claude/CLAUDE.md.',
885
+ 'modal.agents.contentLabel.claudeProject': 'CLAUDE.md content',
886
+ 'modal.agents.placeholder.claudeProject': 'Edit CLAUDE.md here',
881
887
  'modal.agents.hint.openclaw': 'Saved content will be written to OpenClaw workspace AGENTS.md.',
882
888
  'modal.agents.title.openclawWorkspaceFile': 'OpenClaw workspace file: {fileName}',
883
889
  'modal.agents.hint.openclawWorkspaceFile': 'Saved content will be written to OpenClaw workspace {fileName}.',
@@ -221,7 +221,11 @@ const ja = Object.freeze({
221
221
  'subtitle.settings': 'ダウンロード・ディレクトリ・ゴミ箱の管理。',
222
222
  'subtitle.prompts': 'AGENTS.md と CLAUDE.md を編集。',
223
223
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
224
- 'prompts.subTab.claude': 'CLAUDE.md (Claude)',
224
+ 'prompts.subTab.project': 'CLAUDE.md',
225
+ 'prompts.project.pathLabel': 'プロジェクトパス(任意)',
226
+ 'prompts.project.selectPlaceholder': '-- グローバル (~/.claude/CLAUDE.md) --',
227
+ 'prompts.project.manualPlaceholder': 'またはプロジェクトルートパスを入力...',
228
+ 'prompts.project.detected': '編集中: {path}',
225
229
 
226
230
  'dashboard.doctor.title': 'Doctor',
227
231
  'dashboard.doctor.runChecks': 'チェックを実行',
@@ -403,7 +407,7 @@ const ja = Object.freeze({
403
407
  'toast.save.ok': '保存しました',
404
408
  'toast.save.fail': '保存に失敗しました',
405
409
  'toast.agents.saved.agents': 'AGENTS.md 保存しました',
406
- 'toast.agents.saved.claudeMd': 'CLAUDE.md 保存しました',
410
+ 'toast.agents.saved.claudeProject': 'プロジェクト CLAUDE.md 保存しました',
407
411
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md 保存しました',
408
412
  'toast.agents.saved.workspace': 'ワークスペースファイル保存済み: {name}',
409
413
  'toast.delete.ok': '削除しました',
@@ -861,12 +865,14 @@ const ja = Object.freeze({
861
865
  'config.autoCompact.hint': '自動圧縮閾値、デフォルト 185000。',
862
866
  'config.agents.open': 'AGENTS.md を開く',
863
867
  'modal.agents.title.default': 'AGENTS.md エディタ',
864
- 'modal.agents.title.claudeMd': 'CLAUDE.md エディタ',
865
868
  'modal.agents.title.openclaw': 'OpenClaw AGENTS.md エディタ',
866
869
  'modal.agents.hint.default': '保存すると対象の AGENTS.md(config.toml と同階層)に書き込まれます。',
867
- 'modal.agents.hint.claudeMd': '保存すると ~/.claude/CLAUDE.md に書き込まれます。',
868
- 'modal.agents.contentLabel.claudeMd': 'CLAUDE.md 内容',
869
- 'modal.agents.placeholder.claudeMd': 'ここに CLAUDE.md の内容を編集してください',
870
+ 'modal.agents.title.claudeProject': 'プロジェクト CLAUDE.md: {path}',
871
+ 'modal.agents.hint.claudeProject': '保存するとプロジェクトの CLAUDE.md に書き込まれます: {path}',
872
+ 'modal.agents.title.claudeProjectGlobal': 'グローバル CLAUDE.md',
873
+ 'modal.agents.hint.claudeProjectGlobal': '保存すると ~/.claude/CLAUDE.md に書き込まれます。',
874
+ 'modal.agents.contentLabel.claudeProject': 'CLAUDE.md 内容',
875
+ 'modal.agents.placeholder.claudeProject': 'CLAUDE.md を編集',
870
876
  'modal.agents.hint.openclaw': '保存すると OpenClaw Workspace の AGENTS.md に書き込まれます。',
871
877
  'modal.agents.title.openclawWorkspaceFile': 'OpenClaw ワークスペースファイル: {fileName}',
872
878
  'modal.agents.hint.openclawWorkspaceFile': '保存すると OpenClaw Workspace の {fileName} に書き込まれます。',
@@ -234,7 +234,11 @@ const vi = Object.freeze({
234
234
  'subtitle.settings': 'Quản lý tải xuống, thư mục và dữ liệu.',
235
235
  'subtitle.prompts': 'Chỉnh sửa AGENTS.md và CLAUDE.md.',
236
236
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
237
- 'prompts.subTab.claude': 'CLAUDE.md (Claude)',
237
+ 'prompts.subTab.project': 'CLAUDE.md',
238
+ 'prompts.project.pathLabel': 'Đường dẫn dự án (tùy chọn)',
239
+ 'prompts.project.selectPlaceholder': '-- Toàn cục (~/.claude/CLAUDE.md) --',
240
+ 'prompts.project.manualPlaceholder': 'Hoặc nhập đường dẫn gốc dự án...',
241
+ 'prompts.project.detected': 'Đang chỉnh sửa CLAUDE.md trong: {path}',
238
242
 
239
243
 
240
244
  // Task orchestration readiness
@@ -317,7 +321,7 @@ const vi = Object.freeze({
317
321
  'toast.save.ok': 'Đã lưu',
318
322
  'toast.save.fail': 'Lưu thất bại',
319
323
  'toast.agents.saved.agents': 'AGENTS.md đã lưu',
320
- 'toast.agents.saved.claudeMd': 'CLAUDE.md đã lưu',
324
+ 'toast.agents.saved.claudeProject': 'CLAUDE.md dự án đã lưu',
321
325
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md đã lưu',
322
326
  'toast.agents.saved.workspace': 'Tệp workspace đã lưu: {name}',
323
327
  'toast.delete.ok': 'Đã xóa',
@@ -385,6 +389,12 @@ const vi = Object.freeze({
385
389
  'diff.hint.previewMode': 'Đang xem trước. Nhấp "Lưu" để ghi hoặc "Quay lại chỉnh sửa" để tiếp tục.',
386
390
  'modal.agents.unsaved.detectedHint': 'Phát hiện thay đổi chưa lưu: lưu trước khi đóng hoặc áp dụng.',
387
391
  'modal.agents.hint.twoStepSave': 'Lưu hai bước: "Xem trước" để kiểm tra diff, rồi "Lưu" để ghi.',
392
+ 'modal.agents.title.claudeProject': 'CLAUDE.md dự án: {path}',
393
+ 'modal.agents.hint.claudeProject': 'Nội dung sẽ được ghi vào CLAUDE.md trong dự án: {path}',
394
+ 'modal.agents.title.claudeProjectGlobal': 'CLAUDE.md toàn cục',
395
+ 'modal.agents.hint.claudeProjectGlobal': 'Nội dung sẽ được ghi vào ~/.claude/CLAUDE.md.',
396
+ 'modal.agents.contentLabel.claudeProject': 'Nội dung CLAUDE.md',
397
+ 'modal.agents.placeholder.claudeProject': 'Chỉnh sửa CLAUDE.md tại đây',
388
398
  'diff.viewHint.preview': 'Đang xem trước. Nhấp "Lưu" để ghi hoặc "Quay lại chỉnh sửa" để tiếp tục.',
389
399
  'diff.viewHint.truncated': 'Bỏ qua xem trước do nội dung quá lớn. Nhấp "Lưu" để ghi hoặc "Quay lại chỉnh sửa" để tiếp tục.',
390
400
  });
@@ -220,7 +220,11 @@ const zhTw = Object.freeze({
220
220
  'subtitle.settings': '管理下載、目錄與回收站。',
221
221
  'subtitle.prompts': '編輯 AGENTS.md 與 CLAUDE.md。',
222
222
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
223
- 'prompts.subTab.claude': 'CLAUDE.md (Claude)',
223
+ 'prompts.subTab.project': 'CLAUDE.md',
224
+ 'prompts.project.pathLabel': '專案路徑(可選)',
225
+ 'prompts.project.selectPlaceholder': '-- 全域 (~/.claude/CLAUDE.md) --',
226
+ 'prompts.project.manualPlaceholder': '或手動輸入專案根路徑...',
227
+ 'prompts.project.detected': '正在編輯: {path}',
224
228
  'dashboard.doctor.title': 'Doctor',
225
229
  'dashboard.doctor.runChecks': '運行檢查',
226
230
  'dashboard.doctor.checking': '檢查中...',
@@ -401,7 +405,7 @@ const zhTw = Object.freeze({
401
405
  'toast.save.ok': '已保存',
402
406
  'toast.save.fail': '保存失敗',
403
407
  'toast.agents.saved.agents': 'AGENTS.md 已保存',
404
- 'toast.agents.saved.claudeMd': 'CLAUDE.md 已保存',
408
+ 'toast.agents.saved.claudeProject': '專案 CLAUDE.md 已保存',
405
409
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md 已保存',
406
410
  'toast.agents.saved.workspace': '工作區文件已保存: {name}',
407
411
  'toast.delete.ok': '已刪除',
@@ -871,12 +875,14 @@ const zhTw = Object.freeze({
871
875
  'config.autoCompact.hint': '自動壓縮閾值,預設 185000。',
872
876
  'config.agents.open': '打開 AGENTS.md',
873
877
  'modal.agents.title.default': 'AGENTS.md 編輯器',
874
- 'modal.agents.title.claudeMd': 'CLAUDE.md 編輯器',
875
878
  'modal.agents.title.openclaw': 'OpenClaw AGENTS.md 編輯器',
876
879
  'modal.agents.hint.default': '保存後會寫入目標 AGENTS.md(與 config.toml 同級)。',
877
- 'modal.agents.hint.claudeMd': '保存後會寫入 ~/.claude/CLAUDE.md',
878
- 'modal.agents.contentLabel.claudeMd': 'CLAUDE.md 內容',
879
- 'modal.agents.placeholder.claudeMd': '在這裡編輯 CLAUDE.md 內容',
880
+ 'modal.agents.title.claudeProject': '專案 CLAUDE.md: {path}',
881
+ 'modal.agents.hint.claudeProject': '保存後會寫入專案目錄下的 CLAUDE.md: {path}',
882
+ 'modal.agents.title.claudeProjectGlobal': '全域 CLAUDE.md',
883
+ 'modal.agents.hint.claudeProjectGlobal': '保存後會寫入 ~/.claude/CLAUDE.md。',
884
+ 'modal.agents.contentLabel.claudeProject': 'CLAUDE.md 內容',
885
+ 'modal.agents.placeholder.claudeProject': '在這裡編輯 CLAUDE.md',
880
886
  'modal.agents.hint.openclaw': '保存後會寫入 OpenClaw Workspace 下的 AGENTS.md。',
881
887
  'modal.agents.title.openclawWorkspaceFile': 'OpenClaw 工作區文件: {fileName}',
882
888
  'modal.agents.hint.openclawWorkspaceFile': '保存後會寫入 OpenClaw Workspace 下的 {fileName}。',
@@ -220,7 +220,11 @@ const zh = Object.freeze({
220
220
  'subtitle.settings': '管理下载、目录与回收站。',
221
221
  'subtitle.prompts': '编辑 AGENTS.md 与 CLAUDE.md。',
222
222
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
223
- 'prompts.subTab.claude': 'CLAUDE.md (Claude)',
223
+ 'prompts.subTab.project': 'CLAUDE.md',
224
+ 'prompts.project.pathLabel': '项目路径(可选)',
225
+ 'prompts.project.selectPlaceholder': '-- 全局 (~/.claude/CLAUDE.md) --',
226
+ 'prompts.project.manualPlaceholder': '或手动输入项目根路径...',
227
+ 'prompts.project.detected': '正在编辑: {path}',
224
228
  'dashboard.doctor.title': 'Doctor',
225
229
  'dashboard.doctor.runChecks': '运行检查',
226
230
  'dashboard.doctor.checking': '检查中...',
@@ -401,7 +405,7 @@ const zh = Object.freeze({
401
405
  'toast.save.ok': '已保存',
402
406
  'toast.save.fail': '保存失败',
403
407
  'toast.agents.saved.agents': 'AGENTS.md 已保存',
404
- 'toast.agents.saved.claudeMd': 'CLAUDE.md 已保存',
408
+ 'toast.agents.saved.claudeProject': '项目 CLAUDE.md 已保存',
405
409
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md 已保存',
406
410
  'toast.agents.saved.workspace': '工作区文件已保存: {name}',
407
411
  'toast.delete.ok': '已删除',
@@ -871,12 +875,14 @@ const zh = Object.freeze({
871
875
  'config.autoCompact.hint': '自动压缩阈值,默认 185000。',
872
876
  'config.agents.open': '打开 AGENTS.md',
873
877
  'modal.agents.title.default': 'AGENTS.md 编辑器',
874
- 'modal.agents.title.claudeMd': 'CLAUDE.md 编辑器',
875
878
  'modal.agents.title.openclaw': 'OpenClaw AGENTS.md 编辑器',
876
879
  'modal.agents.hint.default': '保存后会写入目标 AGENTS.md(与 config.toml 同级)。',
877
- 'modal.agents.hint.claudeMd': '保存后会写入 ~/.claude/CLAUDE.md',
878
- 'modal.agents.contentLabel.claudeMd': 'CLAUDE.md 内容',
879
- 'modal.agents.placeholder.claudeMd': '在这里编辑 CLAUDE.md 内容',
880
+ 'modal.agents.title.claudeProject': '项目 CLAUDE.md: {path}',
881
+ 'modal.agents.hint.claudeProject': '保存后会写入项目目录下的 CLAUDE.md: {path}',
882
+ 'modal.agents.title.claudeProjectGlobal': '全局 CLAUDE.md',
883
+ 'modal.agents.hint.claudeProjectGlobal': '保存后会写入 ~/.claude/CLAUDE.md。',
884
+ 'modal.agents.contentLabel.claudeProject': 'CLAUDE.md 内容',
885
+ 'modal.agents.placeholder.claudeProject': '在这里编辑 CLAUDE.md',
880
886
  'modal.agents.hint.openclaw': '保存后会写入 OpenClaw Workspace 下的 AGENTS.md。',
881
887
  'modal.agents.title.openclawWorkspaceFile': 'OpenClaw 工作区文件: {fileName}',
882
888
  'modal.agents.hint.openclawWorkspaceFile': '保存后会写入 OpenClaw Workspace 下的 {fileName}。',
@@ -255,13 +255,13 @@
255
255
  </div>
256
256
  </button>
257
257
  <button
258
- id="side-tab-prompts-claude"
258
+ id="side-tab-prompts-project"
259
259
  data-main-tab="prompts"
260
- data-prompts-sub-tab="claude-md"
261
- :aria-current="mainTab === 'prompts' && promptsSubTab === 'claude-md' ? 'page' : null"
262
- :class="['side-item', { active: isMainTabNavActive('prompts') && promptsSubTab === 'claude-md' }]"
260
+ data-prompts-sub-tab="claude-project"
261
+ :aria-current="mainTab === 'prompts' && promptsSubTab === 'claude-project' ? 'page' : null"
262
+ :class="['side-item', { active: isMainTabNavActive('prompts') && promptsSubTab === 'claude-project' }]"
263
263
  @pointerdown="onMainTabPointerDown('prompts', $event)"
264
- @click="switchPromptsSubTab('claude-md'); onMainTabClick('prompts')">
264
+ @click="switchPromptsSubTab('claude-project'); onMainTabClick('prompts')">
265
265
  <div class="side-item-title">{{ t('side.prompts.claude') }}</div>
266
266
  <div class="side-item-meta">
267
267
  <span>{{ t('side.prompts.claude.meta') }}</span>
@@ -130,7 +130,7 @@
130
130
 
131
131
 
132
132
  <div class="form-group">
133
- <label class="form-label">{{ t(agentsContext === 'claude-md' ? 'modal.agents.contentLabel.claudeMd' : 'modal.agents.contentLabel') }}</label>
133
+ <label class="form-label">{{ t(agentsContext === 'claude-project' ? 'modal.agents.contentLabel.claudeProject' : 'modal.agents.contentLabel') }}</label>
134
134
  <div
135
135
  v-if="!agentsLoading && (hasAgentsContentChanged() || agentsDiffVisible)"
136
136
  class="agents-diff-save-alert">
@@ -165,7 +165,7 @@
165
165
  spellcheck="false"
166
166
  :readonly="agentsLoading || agentsSaving || agentsDiffVisible"
167
167
  @input="onAgentsContentInput"
168
- :placeholder="t(agentsContext === 'claude-md' ? 'modal.agents.placeholder.claudeMd' : 'modal.agents.placeholder')"></textarea>
168
+ :placeholder="t(agentsContext === 'claude-project' ? 'modal.agents.placeholder.claudeProject' : 'modal.agents.placeholder')"></textarea>
169
169
  <div class="template-editor-warning">
170
170
  {{ agentsModalHint }}
171
171
  <div class="agents-diff-hint">{{ t('modal.agents.hint.shortcuts') }}</div>
@@ -7,7 +7,29 @@
7
7
  aria-labelledby="tab-prompts">
8
8
  <div class="segmented-control">
9
9
  <button type="button" :class="['segment', { active: promptsSubTab === 'codex' }]" @click="switchPromptsSubTab('codex')">{{ t('prompts.subTab.codex') }}</button>
10
- <button type="button" :class="['segment', { active: promptsSubTab === 'claude-md' }]" @click="switchPromptsSubTab('claude-md')">{{ t('prompts.subTab.claude') }}</button>
10
+ <button type="button" :class="['segment', { active: promptsSubTab === 'claude-project' }]" @click="switchPromptsSubTab('claude-project')">{{ t('prompts.subTab.project') }}</button>
11
+ </div>
12
+
13
+ <div v-if="promptsSubTab === 'claude-project'" class="project-path-selector">
14
+ <label class="project-path-label">{{ t('prompts.project.pathLabel') }}</label>
15
+ <div class="project-path-row">
16
+ <select
17
+ class="form-input project-path-select"
18
+ :value="projectClaudeMdPath"
19
+ @change="selectProjectClaudeMdPath($event.target.value)">
20
+ <option value="">{{ t('prompts.project.selectPlaceholder') }}</option>
21
+ <option v-for="p in projectPathOptions" :key="p" :value="p">{{ p }}</option>
22
+ </select>
23
+ <input
24
+ type="text"
25
+ class="form-input project-path-manual"
26
+ :placeholder="t('prompts.project.manualPlaceholder')"
27
+ :value="projectClaudeMdPath"
28
+ @change="setProjectClaudeMdPathManual($event.target.value)">
29
+ </div>
30
+ <div v-if="projectClaudeMdPath" class="form-hint project-path-detected">
31
+ {{ t('prompts.project.detected', { path: projectClaudeMdPath }) }}
32
+ </div>
11
33
  </div>
12
34
 
13
35
  <div class="prompts-editor">
@@ -89,7 +111,7 @@
89
111
  spellcheck="false"
90
112
  :readonly="agentsLoading || agentsSaving || agentsDiffVisible"
91
113
  @input="onAgentsContentInput"
92
- :placeholder="t(promptsSubTab === 'claude-md' ? 'modal.agents.placeholder.claudeMd' : 'modal.agents.placeholder')"></textarea>
114
+ :placeholder="t(promptsSubTab === 'claude-project' ? 'modal.agents.placeholder.claudeProject' : 'modal.agents.placeholder')"></textarea>
93
115
  </div>
94
116
  <div v-if="promptsContextHint" :class="['prompts-context-hint', { 'prompts-context-hint--warn': promptsContextHint.warn }]">
95
117
  {{ promptsContextHint.text }}
@@ -351,13 +351,13 @@ return function render(_ctx, _cache) {
351
351
  ])
352
352
  ], 42 /* CLASS, PROPS, NEED_HYDRATION */, ["aria-current", "onPointerdown", "onClick"]),
353
353
  _createElementVNode("button", {
354
- id: "side-tab-prompts-claude",
354
+ id: "side-tab-prompts-project",
355
355
  "data-main-tab": "prompts",
356
- "data-prompts-sub-tab": "claude-md",
357
- "aria-current": _ctx.mainTab === 'prompts' && _ctx.promptsSubTab === 'claude-md' ? 'page' : null,
358
- class: _normalizeClass(['side-item', { active: _ctx.isMainTabNavActive('prompts') && _ctx.promptsSubTab === 'claude-md' }]),
356
+ "data-prompts-sub-tab": "claude-project",
357
+ "aria-current": _ctx.mainTab === 'prompts' && _ctx.promptsSubTab === 'claude-project' ? 'page' : null,
358
+ class: _normalizeClass(['side-item', { active: _ctx.isMainTabNavActive('prompts') && _ctx.promptsSubTab === 'claude-project' }]),
359
359
  onPointerdown: $event => (_ctx.onMainTabPointerDown('prompts', $event)),
360
- onClick: $event => {_ctx.switchPromptsSubTab('claude-md'); _ctx.onMainTabClick('prompts')}
360
+ onClick: $event => {_ctx.switchPromptsSubTab('claude-project'); _ctx.onMainTabClick('prompts')}
361
361
  }, [
362
362
  _createElementVNode("div", { class: "side-item-title" }, _toDisplayString(_ctx.t('side.prompts.claude')), 1 /* TEXT */),
363
363
  _createElementVNode("div", { class: "side-item-meta" }, [
@@ -5977,10 +5977,46 @@ return function render(_ctx, _cache) {
5977
5977
  }, _toDisplayString(_ctx.t('prompts.subTab.codex')), 11 /* TEXT, CLASS, PROPS */, ["onClick"]),
5978
5978
  _createElementVNode("button", {
5979
5979
  type: "button",
5980
- class: _normalizeClass(['segment', { active: _ctx.promptsSubTab === 'claude-md' }]),
5981
- onClick: $event => (_ctx.switchPromptsSubTab('claude-md'))
5982
- }, _toDisplayString(_ctx.t('prompts.subTab.claude')), 11 /* TEXT, CLASS, PROPS */, ["onClick"])
5980
+ class: _normalizeClass(['segment', { active: _ctx.promptsSubTab === 'claude-project' }]),
5981
+ onClick: $event => (_ctx.switchPromptsSubTab('claude-project'))
5982
+ }, _toDisplayString(_ctx.t('prompts.subTab.project')), 11 /* TEXT, CLASS, PROPS */, ["onClick"])
5983
5983
  ]),
5984
+ (_ctx.promptsSubTab === 'claude-project')
5985
+ ? (_openBlock(), _createElementBlock("div", {
5986
+ key: 0,
5987
+ class: "project-path-selector"
5988
+ }, [
5989
+ _createElementVNode("label", { class: "project-path-label" }, _toDisplayString(_ctx.t('prompts.project.pathLabel')), 1 /* TEXT */),
5990
+ _createElementVNode("div", { class: "project-path-row" }, [
5991
+ _createElementVNode("select", {
5992
+ class: "form-input project-path-select",
5993
+ value: _ctx.projectClaudeMdPath,
5994
+ onChange: $event => (_ctx.selectProjectClaudeMdPath($event.target.value))
5995
+ }, [
5996
+ _createElementVNode("option", { value: "" }, _toDisplayString(_ctx.t('prompts.project.selectPlaceholder')), 1 /* TEXT */),
5997
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.projectPathOptions, (p) => {
5998
+ return (_openBlock(), _createElementBlock("option", {
5999
+ key: p,
6000
+ value: p
6001
+ }, _toDisplayString(p), 9 /* TEXT, PROPS */, ["value"]))
6002
+ }), 128 /* KEYED_FRAGMENT */))
6003
+ ], 40 /* PROPS, NEED_HYDRATION */, ["value", "onChange"]),
6004
+ _createElementVNode("input", {
6005
+ type: "text",
6006
+ class: "form-input project-path-manual",
6007
+ placeholder: _ctx.t('prompts.project.manualPlaceholder'),
6008
+ value: _ctx.projectClaudeMdPath,
6009
+ onChange: $event => (_ctx.setProjectClaudeMdPathManual($event.target.value))
6010
+ }, null, 40 /* PROPS, NEED_HYDRATION */, ["placeholder", "value", "onChange"])
6011
+ ]),
6012
+ (_ctx.projectClaudeMdPath)
6013
+ ? (_openBlock(), _createElementBlock("div", {
6014
+ key: 0,
6015
+ class: "form-hint project-path-detected"
6016
+ }, _toDisplayString(_ctx.t('prompts.project.detected', { path: _ctx.projectClaudeMdPath })), 1 /* TEXT */))
6017
+ : _createCommentVNode("v-if", true)
6018
+ ]))
6019
+ : _createCommentVNode("v-if", true),
5984
6020
  _createElementVNode("div", { class: "prompts-editor" }, [
5985
6021
  _createElementVNode("div", { class: "prompts-editor-toolbar" }, [
5986
6022
  _createElementVNode("div", { class: "form-hint" }, [
@@ -6099,7 +6135,7 @@ return function render(_ctx, _cache) {
6099
6135
  spellcheck: "false",
6100
6136
  readonly: _ctx.agentsLoading || _ctx.agentsSaving || _ctx.agentsDiffVisible,
6101
6137
  onInput: _ctx.onAgentsContentInput,
6102
- placeholder: _ctx.t(_ctx.promptsSubTab === 'claude-md' ? 'modal.agents.placeholder.claudeMd' : 'modal.agents.placeholder')
6138
+ placeholder: _ctx.t(_ctx.promptsSubTab === 'claude-project' ? 'modal.agents.placeholder.claudeProject' : 'modal.agents.placeholder')
6103
6139
  }, null, 40 /* PROPS, NEED_HYDRATION */, ["onUpdate:modelValue", "readonly", "onInput", "placeholder"]), [
6104
6140
  [_vModelText, _ctx.agentsContent]
6105
6141
  ])
@@ -7970,7 +8006,7 @@ return function render(_ctx, _cache) {
7970
8006
  ])
7971
8007
  ]),
7972
8008
  _createElementVNode("div", { class: "form-group" }, [
7973
- _createElementVNode("label", { class: "form-label" }, _toDisplayString(_ctx.t(_ctx.agentsContext === 'claude-md' ? 'modal.agents.contentLabel.claudeMd' : 'modal.agents.contentLabel')), 1 /* TEXT */),
8009
+ _createElementVNode("label", { class: "form-label" }, _toDisplayString(_ctx.t(_ctx.agentsContext === 'claude-project' ? 'modal.agents.contentLabel.claudeProject' : 'modal.agents.contentLabel')), 1 /* TEXT */),
7974
8010
  (!_ctx.agentsLoading && (_ctx.hasAgentsContentChanged() || _ctx.agentsDiffVisible))
7975
8011
  ? (_openBlock(), _createElementBlock("div", {
7976
8012
  key: 0,
@@ -8030,7 +8066,7 @@ return function render(_ctx, _cache) {
8030
8066
  spellcheck: "false",
8031
8067
  readonly: _ctx.agentsLoading || _ctx.agentsSaving || _ctx.agentsDiffVisible,
8032
8068
  onInput: _ctx.onAgentsContentInput,
8033
- placeholder: _ctx.t(_ctx.agentsContext === 'claude-md' ? 'modal.agents.placeholder.claudeMd' : 'modal.agents.placeholder')
8069
+ placeholder: _ctx.t(_ctx.agentsContext === 'claude-project' ? 'modal.agents.placeholder.claudeProject' : 'modal.agents.placeholder')
8034
8070
  }, null, 40 /* PROPS, NEED_HYDRATION */, ["onUpdate:modelValue", "readonly", "onInput", "placeholder"]), [
8035
8071
  [_vModelText, _ctx.agentsContent]
8036
8072
  ]),
@@ -580,6 +580,39 @@
580
580
  border-left: 1px solid var(--color-border-soft);
581
581
  }
582
582
 
583
+ .project-path-selector {
584
+ margin-top: 8px;
585
+ margin-bottom: 4px;
586
+ }
587
+
588
+ .project-path-label {
589
+ display: block;
590
+ font-size: 0.85em;
591
+ color: var(--color-text-secondary);
592
+ margin-bottom: 4px;
593
+ }
594
+
595
+ .project-path-row {
596
+ display: flex;
597
+ gap: 8px;
598
+ align-items: center;
599
+ }
600
+
601
+ .project-path-select {
602
+ flex: 1;
603
+ min-width: 0;
604
+ }
605
+
606
+ .project-path-manual {
607
+ flex: 1;
608
+ min-width: 0;
609
+ }
610
+
611
+ .project-path-detected {
612
+ margin-top: 4px;
613
+ font-size: 0.82em;
614
+ }
615
+
583
616
  .editor-frame {
584
617
  position: relative;
585
618
  border-radius: var(--radius-md);
@@ -55,14 +55,14 @@
55
55
  color: var(--color-text-secondary);
56
56
  cursor: pointer;
57
57
  border-radius: var(--radius-sm);
58
- transition: color var(--transition-fast) var(--ease-smooth), background-color var(--transition-fast) var(--ease-smooth);
59
58
  position: relative;
60
59
  z-index: 2;
61
60
  letter-spacing: 0;
62
61
  }
63
62
 
64
- .segment:hover {
63
+ .segment:not(.active):hover {
65
64
  color: var(--color-text-primary);
65
+ transition: color var(--transition-fast) var(--ease-smooth), background-color var(--transition-fast) var(--ease-smooth);
66
66
  }
67
67
 
68
68
  .segment.active {