claude-code-workflow 6.3.37 → 6.3.39

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.
Files changed (173) hide show
  1. package/.claude/commands/workflow/lite-execute.md +2 -0
  2. package/.codex/agents/action-planning-agent.md +885 -0
  3. package/.codex/agents/ccw-loop-b-complete.md +227 -0
  4. package/.codex/agents/ccw-loop-b-debug.md +172 -0
  5. package/.codex/agents/ccw-loop-b-develop.md +147 -0
  6. package/.codex/agents/ccw-loop-b-init.md +82 -0
  7. package/.codex/agents/ccw-loop-b-validate.md +204 -0
  8. package/.codex/agents/ccw-loop-executor.md +260 -0
  9. package/.codex/agents/cli-discuss-agent.md +391 -0
  10. package/.codex/agents/cli-execution-agent.md +333 -0
  11. package/.codex/agents/cli-explore-agent.md +186 -0
  12. package/.codex/agents/cli-lite-planning-agent.md +736 -0
  13. package/.codex/agents/cli-planning-agent.md +562 -0
  14. package/.codex/agents/code-developer.md +408 -0
  15. package/.codex/agents/conceptual-planning-agent.md +321 -0
  16. package/.codex/agents/context-search-agent.md +585 -0
  17. package/.codex/agents/debug-explore-agent.md +436 -0
  18. package/.codex/agents/doc-generator.md +334 -0
  19. package/.codex/agents/issue-plan-agent.md +417 -0
  20. package/.codex/agents/issue-queue-agent.md +311 -0
  21. package/.codex/agents/memory-bridge.md +96 -0
  22. package/.codex/agents/test-context-search-agent.md +402 -0
  23. package/.codex/agents/test-fix-agent.md +359 -0
  24. package/.codex/agents/ui-design-agent.md +595 -0
  25. package/.codex/agents/universal-executor.md +135 -0
  26. package/.codex/prompts/clean.md +409 -0
  27. package/.codex/prompts/issue-discover-by-prompt.md +364 -0
  28. package/.codex/prompts/issue-discover.md +261 -0
  29. package/.codex/prompts/issue-execute.md +10 -0
  30. package/.codex/prompts/issue-new.md +285 -0
  31. package/.codex/prompts/issue-plan.md +161 -63
  32. package/.codex/prompts/issue-queue.md +298 -288
  33. package/.codex/prompts/lite-execute.md +627 -133
  34. package/.codex/prompts/lite-fix.md +670 -0
  35. package/.codex/prompts/lite-plan-a.md +337 -0
  36. package/.codex/prompts/lite-plan-b.md +485 -0
  37. package/.codex/prompts/{lite-plan.md → lite-plan-c.md} +601 -469
  38. package/.codex/skills/ccw-loop/README.md +171 -0
  39. package/.codex/skills/ccw-loop/SKILL.md +349 -0
  40. package/.codex/skills/ccw-loop/phases/actions/action-complete.md +269 -0
  41. package/.codex/skills/ccw-loop/phases/actions/action-debug.md +286 -0
  42. package/.codex/skills/ccw-loop/phases/actions/action-develop.md +183 -0
  43. package/.codex/skills/ccw-loop/phases/actions/action-init.md +164 -0
  44. package/.codex/skills/ccw-loop/phases/actions/action-menu.md +205 -0
  45. package/.codex/skills/ccw-loop/phases/actions/action-validate.md +250 -0
  46. package/.codex/skills/ccw-loop/phases/orchestrator.md +416 -0
  47. package/.codex/skills/ccw-loop/phases/state-schema.md +388 -0
  48. package/.codex/skills/ccw-loop/specs/action-catalog.md +182 -0
  49. package/.codex/skills/ccw-loop-b/README.md +301 -0
  50. package/.codex/skills/ccw-loop-b/SKILL.md +322 -0
  51. package/.codex/skills/ccw-loop-b/phases/orchestrator.md +257 -0
  52. package/.codex/skills/ccw-loop-b/phases/state-schema.md +181 -0
  53. package/.codex/skills/ccw-loop-b/specs/action-catalog.md +383 -0
  54. package/.codex/skills/parallel-dev-cycle/README.md +382 -0
  55. package/.codex/skills/parallel-dev-cycle/SKILL.md +512 -0
  56. package/.codex/skills/parallel-dev-cycle/phases/agents/code-developer.md +242 -0
  57. package/.codex/skills/parallel-dev-cycle/phases/agents/exploration-planner.md +285 -0
  58. package/.codex/skills/parallel-dev-cycle/phases/agents/requirements-analyst.md +285 -0
  59. package/.codex/skills/parallel-dev-cycle/phases/agents/validation-archivist.md +381 -0
  60. package/.codex/skills/parallel-dev-cycle/phases/orchestrator.md +696 -0
  61. package/.codex/skills/parallel-dev-cycle/phases/state-schema.md +436 -0
  62. package/.codex/skills/parallel-dev-cycle/specs/communication-optimization.md +423 -0
  63. package/.codex/skills/parallel-dev-cycle/specs/coordination-protocol.md +391 -0
  64. package/.codex/skills/parallel-dev-cycle/specs/versioning-strategy.md +330 -0
  65. package/ccw/dist/cli.d.ts.map +1 -1
  66. package/ccw/dist/cli.js +4 -0
  67. package/ccw/dist/cli.js.map +1 -1
  68. package/ccw/dist/commands/install.d.ts.map +1 -1
  69. package/ccw/dist/commands/install.js +39 -8
  70. package/ccw/dist/commands/install.js.map +1 -1
  71. package/ccw/dist/commands/issue.d.ts +3 -0
  72. package/ccw/dist/commands/issue.d.ts.map +1 -1
  73. package/ccw/dist/commands/issue.js +107 -0
  74. package/ccw/dist/commands/issue.js.map +1 -1
  75. package/ccw/dist/commands/upgrade.js +1 -1
  76. package/ccw/dist/commands/upgrade.js.map +1 -1
  77. package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -1
  78. package/ccw/dist/config/litellm-api-config-manager.js +3 -2
  79. package/ccw/dist/config/litellm-api-config-manager.js.map +1 -1
  80. package/ccw/dist/core/memory-embedder-bridge.d.ts.map +1 -1
  81. package/ccw/dist/core/memory-embedder-bridge.js +2 -5
  82. package/ccw/dist/core/memory-embedder-bridge.js.map +1 -1
  83. package/ccw/dist/core/routes/cli-routes.js.map +1 -1
  84. package/ccw/dist/core/routes/codexlens/config-handlers.d.ts.map +1 -1
  85. package/ccw/dist/core/routes/codexlens/config-handlers.js +7 -6
  86. package/ccw/dist/core/routes/codexlens/config-handlers.js.map +1 -1
  87. package/ccw/dist/core/routes/codexlens/semantic-handlers.d.ts.map +1 -1
  88. package/ccw/dist/core/routes/codexlens/semantic-handlers.js +2 -2
  89. package/ccw/dist/core/routes/codexlens/semantic-handlers.js.map +1 -1
  90. package/ccw/dist/core/routes/graph-routes.d.ts.map +1 -1
  91. package/ccw/dist/core/routes/graph-routes.js +17 -2
  92. package/ccw/dist/core/routes/graph-routes.js.map +1 -1
  93. package/ccw/dist/core/routes/issue-routes.d.ts.map +1 -1
  94. package/ccw/dist/core/routes/issue-routes.js +280 -33
  95. package/ccw/dist/core/routes/issue-routes.js.map +1 -1
  96. package/ccw/dist/core/routes/loop-v2-routes.d.ts +9 -0
  97. package/ccw/dist/core/routes/loop-v2-routes.d.ts.map +1 -1
  98. package/ccw/dist/core/routes/loop-v2-routes.js +56 -4
  99. package/ccw/dist/core/routes/loop-v2-routes.js.map +1 -1
  100. package/ccw/dist/core/routes/system-routes.d.ts.map +1 -1
  101. package/ccw/dist/core/routes/system-routes.js +3 -2
  102. package/ccw/dist/core/routes/system-routes.js.map +1 -1
  103. package/ccw/dist/core/server.d.ts.map +1 -1
  104. package/ccw/dist/core/server.js +5 -3
  105. package/ccw/dist/core/server.js.map +1 -1
  106. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
  107. package/ccw/dist/tools/claude-cli-tools.js +4 -3
  108. package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
  109. package/ccw/dist/tools/cli-config-manager.d.ts +1 -0
  110. package/ccw/dist/tools/cli-config-manager.d.ts.map +1 -1
  111. package/ccw/dist/tools/cli-config-manager.js +2 -1
  112. package/ccw/dist/tools/cli-config-manager.js.map +1 -1
  113. package/ccw/dist/tools/codex-lens-lsp.d.ts.map +1 -1
  114. package/ccw/dist/tools/codex-lens-lsp.js +2 -5
  115. package/ccw/dist/tools/codex-lens-lsp.js.map +1 -1
  116. package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
  117. package/ccw/dist/tools/codex-lens.js +22 -32
  118. package/ccw/dist/tools/codex-lens.js.map +1 -1
  119. package/ccw/dist/tools/litellm-client.d.ts +6 -0
  120. package/ccw/dist/tools/litellm-client.d.ts.map +1 -1
  121. package/ccw/dist/tools/litellm-client.js +15 -2
  122. package/ccw/dist/tools/litellm-client.js.map +1 -1
  123. package/ccw/dist/tools/loop-task-manager.d.ts +13 -2
  124. package/ccw/dist/tools/loop-task-manager.d.ts.map +1 -1
  125. package/ccw/dist/tools/loop-task-manager.js.map +1 -1
  126. package/ccw/dist/tools/native-session-discovery.d.ts.map +1 -1
  127. package/ccw/dist/tools/native-session-discovery.js +35 -7
  128. package/ccw/dist/tools/native-session-discovery.js.map +1 -1
  129. package/ccw/dist/utils/codexlens-path.d.ts +36 -0
  130. package/ccw/dist/utils/codexlens-path.d.ts.map +1 -0
  131. package/ccw/dist/utils/codexlens-path.js +56 -0
  132. package/ccw/dist/utils/codexlens-path.js.map +1 -0
  133. package/ccw/dist/utils/uv-manager.d.ts.map +1 -1
  134. package/ccw/dist/utils/uv-manager.js +3 -2
  135. package/ccw/dist/utils/uv-manager.js.map +1 -1
  136. package/ccw/src/cli.ts +4 -0
  137. package/ccw/src/commands/install.ts +51 -8
  138. package/ccw/src/commands/issue.ts +119 -0
  139. package/ccw/src/commands/upgrade.ts +1 -1
  140. package/ccw/src/config/litellm-api-config-manager.ts +3 -2
  141. package/ccw/src/core/memory-embedder-bridge.ts +2 -6
  142. package/ccw/src/core/routes/cli-routes.ts +1 -1
  143. package/ccw/src/core/routes/codexlens/config-handlers.ts +7 -6
  144. package/ccw/src/core/routes/codexlens/semantic-handlers.ts +2 -2
  145. package/ccw/src/core/routes/graph-routes.ts +18 -2
  146. package/ccw/src/core/routes/issue-routes.ts +308 -33
  147. package/ccw/src/core/routes/loop-v2-routes.ts +64 -6
  148. package/ccw/src/core/routes/system-routes.ts +3 -2
  149. package/ccw/src/core/server.ts +6 -3
  150. package/ccw/src/templates/dashboard-css/02-session.css +2 -0
  151. package/ccw/src/templates/dashboard-css/04-lite-tasks.css +103 -1
  152. package/ccw/src/templates/dashboard-css/32-issue-manager.css +32 -0
  153. package/ccw/src/templates/dashboard-js/components/cli-history.js +48 -48
  154. package/ccw/src/templates/dashboard-js/components/navigation.js +6 -0
  155. package/ccw/src/templates/dashboard-js/components/notifications.js +6 -0
  156. package/ccw/src/templates/dashboard-js/components/version-check.js +38 -0
  157. package/ccw/src/templates/dashboard-js/i18n.js +126 -0
  158. package/ccw/src/templates/dashboard-js/state.js +2 -0
  159. package/ccw/src/templates/dashboard-js/views/cli-manager.js +1 -1
  160. package/ccw/src/templates/dashboard-js/views/issue-manager.js +183 -1
  161. package/ccw/src/templates/dashboard-js/views/lite-tasks.js +55 -11
  162. package/ccw/src/templates/dashboard-js/views/loop-monitor.js +112 -11
  163. package/ccw/src/templates/dashboard.html +48 -2
  164. package/ccw/src/tools/claude-cli-tools.ts +4 -3
  165. package/ccw/src/tools/cli-config-manager.ts +3 -1
  166. package/ccw/src/tools/codex-lens-lsp.ts +2 -5
  167. package/ccw/src/tools/codex-lens.ts +27 -38
  168. package/ccw/src/tools/litellm-client.ts +16 -2
  169. package/ccw/src/tools/loop-task-manager.ts +13 -2
  170. package/ccw/src/tools/native-session-discovery.ts +38 -7
  171. package/ccw/src/utils/codexlens-path.ts +60 -0
  172. package/ccw/src/utils/uv-manager.ts +3 -2
  173. package/package.json +1 -1
@@ -223,6 +223,10 @@ interface IssueOptions {
223
223
  data?: string; // JSON data for create
224
224
  fromQueue?: boolean | string; // Sync statuses from queue (true=active, string=specific queue ID)
225
225
  queue?: string; // Target queue ID for multi-queue operations
226
+ // GitHub pull options
227
+ state?: string; // Issue state: open, closed, all
228
+ limit?: number; // Maximum number of issues to pull
229
+ labels?: string; // Filter by labels (comma-separated)
226
230
  }
227
231
 
228
232
  const ISSUES_DIR = '.workflow/issues';
@@ -1003,6 +1007,113 @@ async function createAction(options: IssueOptions): Promise<void> {
1003
1007
  }
1004
1008
  }
1005
1009
 
1010
+ /**
1011
+ * pull - Pull issues from GitHub
1012
+ * Usage: ccw issue pull [--state open|closed|all] [--limit N] [--labels label1,label2]
1013
+ */
1014
+ async function pullAction(options: IssueOptions): Promise<void> {
1015
+ try {
1016
+ // Check if gh CLI is available
1017
+ try {
1018
+ execSync('gh --version', { stdio: 'ignore', timeout: EXEC_TIMEOUTS.GIT_QUICK });
1019
+ } catch {
1020
+ console.error(chalk.red('GitHub CLI (gh) is not installed or not in PATH'));
1021
+ console.error(chalk.gray('Install from: https://cli.github.com/'));
1022
+ process.exit(1);
1023
+ }
1024
+
1025
+ // Build gh command with options
1026
+ const state = options.state || 'open';
1027
+ const limit = options.limit || 100;
1028
+ let ghCommand = `gh issue list --state ${state} --limit ${limit} --json number,title,body,labels,url,state`;
1029
+
1030
+ if (options.labels) {
1031
+ ghCommand += ` --label "${options.labels}"`;
1032
+ }
1033
+
1034
+ console.log(chalk.cyan(`Fetching issues from GitHub (state: ${state}, limit: ${limit})...`));
1035
+
1036
+ // Fetch issues from GitHub
1037
+ const ghOutput = execSync(ghCommand, {
1038
+ encoding: 'utf-8',
1039
+ stdio: ['pipe', 'pipe', 'pipe'],
1040
+ timeout: EXEC_TIMEOUTS.PROCESS_SPAWN,
1041
+ }).trim();
1042
+
1043
+ if (!ghOutput) {
1044
+ console.log(chalk.yellow('No issues found on GitHub'));
1045
+ return;
1046
+ }
1047
+
1048
+ const ghIssues = JSON.parse(ghOutput);
1049
+ const existingIssues = readIssues();
1050
+
1051
+ let imported = 0;
1052
+ let skipped = 0;
1053
+ let updated = 0;
1054
+
1055
+ for (const ghIssue of ghIssues) {
1056
+ const issueId = `GH-${ghIssue.number}`;
1057
+ const existingIssue = existingIssues.find(i => i.id === issueId);
1058
+
1059
+ // Prepare issue data
1060
+ const issueData: Partial<Issue> = {
1061
+ id: issueId,
1062
+ title: ghIssue.title,
1063
+ status: ghIssue.state === 'OPEN' ? 'registered' : 'completed',
1064
+ priority: 3, // Default priority
1065
+ context: ghIssue.body?.substring(0, 500) || ghIssue.title,
1066
+ source: 'github',
1067
+ source_url: ghIssue.url,
1068
+ tags: ghIssue.labels?.map((l: any) => l.name) || [],
1069
+ };
1070
+
1071
+ if (existingIssue) {
1072
+ // Update existing issue if state changed
1073
+ if (existingIssue.source_url === ghIssue.url) {
1074
+ // Check if status needs updating
1075
+ const newStatus = ghIssue.state === 'OPEN' ? 'registered' : 'completed';
1076
+ if (existingIssue.status !== newStatus || existingIssue.title !== ghIssue.title) {
1077
+ existingIssue.title = ghIssue.title;
1078
+ existingIssue.status = newStatus;
1079
+ existingIssue.updated_at = new Date().toISOString();
1080
+ updated++;
1081
+ } else {
1082
+ skipped++;
1083
+ }
1084
+ } else {
1085
+ skipped++;
1086
+ }
1087
+ } else {
1088
+ // Create new issue
1089
+ try {
1090
+ createIssue(issueData);
1091
+ imported++;
1092
+ } catch (err) {
1093
+ console.error(chalk.red(`Failed to import issue #${ghIssue.number}: ${(err as Error).message}`));
1094
+ }
1095
+ }
1096
+ }
1097
+
1098
+ // Save updates if any
1099
+ if (updated > 0) {
1100
+ writeIssues(existingIssues);
1101
+ }
1102
+
1103
+ console.log(chalk.green(`\n✓ GitHub sync complete:`));
1104
+ console.log(chalk.gray(` - Imported: ${imported} new issues`));
1105
+ console.log(chalk.gray(` - Updated: ${updated} existing issues`));
1106
+ console.log(chalk.gray(` - Skipped: ${skipped} unchanged issues`));
1107
+
1108
+ if (options.json) {
1109
+ console.log(JSON.stringify({ imported, updated, skipped, total: ghIssues.length }));
1110
+ }
1111
+ } catch (err) {
1112
+ console.error(chalk.red(`Failed to pull issues from GitHub: ${(err as Error).message}`));
1113
+ process.exit(1);
1114
+ }
1115
+ }
1116
+
1006
1117
  /**
1007
1118
  * solution - Create or read solutions
1008
1119
  * Create: ccw issue solution <issue-id> --data '{"tasks":[...]}'
@@ -2715,6 +2826,9 @@ export async function issueCommand(
2715
2826
  case 'create':
2716
2827
  await createAction(options);
2717
2828
  break;
2829
+ case 'pull':
2830
+ await pullAction(options);
2831
+ break;
2718
2832
  case 'solution':
2719
2833
  await solutionAction(argsArray[0], options);
2720
2834
  break;
@@ -2769,6 +2883,8 @@ export async function issueCommand(
2769
2883
  console.log(chalk.bold.cyan('\nCCW Issue Management (v3.0 - Multi-Queue + Lifecycle)\n'));
2770
2884
  console.log(chalk.bold('Core Commands:'));
2771
2885
  console.log(chalk.gray(' create --data \'{"title":"..."}\' Create issue (auto-generates ID)'));
2886
+ console.log(chalk.gray(' pull [--state open|closed|all] Pull issues from GitHub'));
2887
+ console.log(chalk.gray(' [--limit N] [--labels label1,label2]'));
2772
2888
  console.log(chalk.gray(' init <issue-id> Initialize new issue (manual ID)'));
2773
2889
  console.log(chalk.gray(' list [issue-id] List issues or tasks'));
2774
2890
  console.log(chalk.gray(' history List completed issues (from history)'));
@@ -2809,6 +2925,9 @@ export async function issueCommand(
2809
2925
  console.log(chalk.gray(' --priority <n> Queue priority (lower = higher)'));
2810
2926
  console.log(chalk.gray(' --json JSON output'));
2811
2927
  console.log(chalk.gray(' --force Force operation'));
2928
+ console.log(chalk.gray(' --state <state> GitHub issue state (open/closed/all)'));
2929
+ console.log(chalk.gray(' --limit <n> Max issues to pull from GitHub'));
2930
+ console.log(chalk.gray(' --labels <labels> Filter by GitHub labels (comma-separated)'));
2812
2931
  console.log();
2813
2932
  console.log(chalk.bold('Storage:'));
2814
2933
  console.log(chalk.gray(' .workflow/issues/issues.jsonl Active issues'));
@@ -124,7 +124,7 @@ export async function upgradeCommand(options: UpgradeOptions): Promise<void> {
124
124
  info('All installations are up to date.');
125
125
  console.log('');
126
126
  info('To upgrade ccw itself, run:');
127
- console.log(chalk.cyan(' npm update -g ccw'));
127
+ console.log(chalk.cyan(' npm update -g claude-code-workflow'));
128
128
  console.log('');
129
129
  return;
130
130
  }
@@ -4,9 +4,10 @@
4
4
  */
5
5
 
6
6
  import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'fs';
7
- import { homedir } from 'os';
8
7
  import { join } from 'path';
8
+ import { homedir } from 'os';
9
9
  import { StoragePaths, GlobalPaths, ensureStorageDir } from './storage-paths.js';
10
+ import { getCodexLensDataDir } from '../utils/codexlens-path.js';
10
11
  import type {
11
12
  LiteLLMApiConfig,
12
13
  ProviderCredential,
@@ -798,7 +799,7 @@ export function syncCodexLensConfig(baseDir: string): { success: boolean; messag
798
799
  const rotationConfig = config.codexlensEmbeddingRotation;
799
800
 
800
801
  // Get CodexLens settings path
801
- const codexlensDir = join(homedir(), '.codexlens');
802
+ const codexlensDir = getCodexLensDataDir();
802
803
  const settingsPath = join(codexlensDir, 'settings.json');
803
804
 
804
805
  // Ensure directory exists
@@ -13,20 +13,16 @@
13
13
 
14
14
  import { spawn } from 'child_process';
15
15
  import { join, dirname } from 'path';
16
- import { homedir } from 'os';
17
16
  import { existsSync } from 'fs';
18
17
  import { fileURLToPath } from 'url';
18
+ import { getCodexLensPython } from '../utils/codexlens-path.js';
19
19
 
20
20
  // Get directory of this module
21
21
  const __filename = fileURLToPath(import.meta.url);
22
22
  const __dirname = dirname(__filename);
23
23
 
24
24
  // Venv paths (reuse CodexLens venv)
25
- const CODEXLENS_VENV = join(homedir(), '.codexlens', 'venv');
26
- const VENV_PYTHON =
27
- process.platform === 'win32'
28
- ? join(CODEXLENS_VENV, 'Scripts', 'python.exe')
29
- : join(CODEXLENS_VENV, 'bin', 'python');
25
+ const VENV_PYTHON = getCodexLensPython();
30
26
 
31
27
  // Script path
32
28
  const EMBEDDER_SCRIPT = join(__dirname, '..', '..', 'scripts', 'memory_embedder.py');
@@ -302,7 +302,7 @@ export async function handleCliRoutes(ctx: RouteContext): Promise<boolean> {
302
302
  if (req.method === 'PUT') {
303
303
  handlePostRequest(req, res, async (body: unknown) => {
304
304
  try {
305
- const updates = body as { enabled?: boolean; primaryModel?: string; secondaryModel?: string; tags?: string[] };
305
+ const updates = body as { enabled?: boolean; primaryModel?: string; secondaryModel?: string; tags?: string[]; envFile?: string | null };
306
306
  const updated = updateToolConfig(initialPath, tool, updates);
307
307
 
308
308
  // Broadcast config updated event
@@ -16,6 +16,7 @@ import type { RouteContext } from '../types.js';
16
16
  import { EXEC_TIMEOUTS } from '../../../utils/exec-constants.js';
17
17
  import { extractJSON } from './utils.js';
18
18
  import { stopWatcherForUninstall } from './watcher-handlers.js';
19
+ import { getCodexLensDataDir } from '../../../utils/codexlens-path.js';
19
20
 
20
21
  export async function handleCodexLensConfigRoutes(ctx: RouteContext): Promise<boolean> {
21
22
  const { pathname, url, req, res, initialPath, handlePostRequest, broadcastToClients } = ctx;
@@ -777,7 +778,7 @@ export async function handleCodexLensConfigRoutes(ctx: RouteContext): Promise<bo
777
778
  const { join } = await import('path');
778
779
  const { readFile } = await import('fs/promises');
779
780
 
780
- const envPath = join(homedir(), '.codexlens', '.env');
781
+ const envPath = join(getCodexLensDataDir(), '.env');
781
782
  let content = '';
782
783
  try {
783
784
  content = await readFile(envPath, 'utf-8');
@@ -829,7 +830,7 @@ export async function handleCodexLensConfigRoutes(ctx: RouteContext): Promise<bo
829
830
  }
830
831
 
831
832
  // Also read settings.json for current configuration
832
- const settingsPath = join(homedir(), '.codexlens', 'settings.json');
833
+ const settingsPath = join(getCodexLensDataDir(), 'settings.json');
833
834
  let settings: Record<string, any> = {};
834
835
  try {
835
836
  const settingsContent = await readFile(settingsPath, 'utf-8');
@@ -943,7 +944,7 @@ export async function handleCodexLensConfigRoutes(ctx: RouteContext): Promise<bo
943
944
  const { join, dirname } = await import('path');
944
945
  const { writeFile, mkdir, readFile } = await import('fs/promises');
945
946
 
946
- const envPath = join(homedir(), '.codexlens', '.env');
947
+ const envPath = join(getCodexLensDataDir(), '.env');
947
948
  await mkdir(dirname(envPath), { recursive: true });
948
949
 
949
950
  // Read existing env file to preserve custom variables
@@ -1072,7 +1073,7 @@ export async function handleCodexLensConfigRoutes(ctx: RouteContext): Promise<bo
1072
1073
  await writeFile(envPath, lines.join('\n'), 'utf-8');
1073
1074
 
1074
1075
  // Also update settings.json with mapped values
1075
- const settingsPath = join(homedir(), '.codexlens', 'settings.json');
1076
+ const settingsPath = join(getCodexLensDataDir(), 'settings.json');
1076
1077
  let settings: Record<string, any> = {};
1077
1078
  try {
1078
1079
  const settingsContent = await readFile(settingsPath, 'utf-8');
@@ -1145,7 +1146,7 @@ export async function handleCodexLensConfigRoutes(ctx: RouteContext): Promise<bo
1145
1146
  const { join } = await import('path');
1146
1147
  const { readFile } = await import('fs/promises');
1147
1148
 
1148
- const settingsPath = join(homedir(), '.codexlens', 'settings.json');
1149
+ const settingsPath = join(getCodexLensDataDir(), 'settings.json');
1149
1150
  let settings: Record<string, any> = {};
1150
1151
  try {
1151
1152
  const content = await readFile(settingsPath, 'utf-8');
@@ -1214,7 +1215,7 @@ export async function handleCodexLensConfigRoutes(ctx: RouteContext): Promise<bo
1214
1215
  const { join, dirname } = await import('path');
1215
1216
  const { writeFile, mkdir, readFile } = await import('fs/promises');
1216
1217
 
1217
- const settingsPath = join(homedir(), '.codexlens', 'settings.json');
1218
+ const settingsPath = join(getCodexLensDataDir(), 'settings.json');
1218
1219
  await mkdir(dirname(settingsPath), { recursive: true });
1219
1220
 
1220
1221
  // Read existing settings
@@ -17,6 +17,7 @@ import {
17
17
  import type { RouteContext } from '../types.js';
18
18
  import { extractJSON } from './utils.js';
19
19
  import { getDefaultTool } from '../../../tools/claude-cli-tools.js';
20
+ import { getCodexLensDataDir } from '../../../utils/codexlens-path.js';
20
21
 
21
22
  export async function handleCodexLensSemanticRoutes(ctx: RouteContext): Promise<boolean> {
22
23
  const { pathname, url, req, res, initialPath, handlePostRequest } = ctx;
@@ -445,9 +446,8 @@ export async function handleCodexLensSemanticRoutes(ctx: RouteContext): Promise<
445
446
  // Write to CodexLens .env file for persistence
446
447
  const { writeFileSync, existsSync, readFileSync } = await import('fs');
447
448
  const { join } = await import('path');
448
- const { homedir } = await import('os');
449
449
 
450
- const codexlensDir = join(homedir(), '.codexlens');
450
+ const codexlensDir = getCodexLensDataDir();
451
451
  const envFile = join(codexlensDir, '.env');
452
452
 
453
453
  // Read existing .env content
@@ -2,13 +2,29 @@
2
2
  * Graph Routes Module
3
3
  * Handles graph visualization API endpoints for codex-lens data
4
4
  */
5
- import { homedir } from 'os';
6
5
  import { join, resolve, normalize } from 'path';
7
6
  import { existsSync, readdirSync } from 'fs';
8
7
  import Database from 'better-sqlite3';
9
8
  import { validatePath as validateAllowedPath } from '../../utils/path-validator.js';
10
9
  import type { RouteContext } from './types.js';
11
10
 
11
+ /**
12
+ * Get the index root directory from CodexLens config or default.
13
+ * Matches Python implementation priority:
14
+ * 1. CODEXLENS_INDEX_DIR environment variable
15
+ * 2. index_dir from ~/.codexlens/config.json
16
+ * 3. Default: ~/.codexlens/indexes
17
+ */
18
+ function getIndexRoot(): string {
19
+ const envOverride = process.env.CODEXLENS_INDEX_DIR;
20
+ if (envOverride) {
21
+ return envOverride;
22
+ }
23
+ // Default: use CodexLens data directory + indexes
24
+ const { getCodexLensDataDir } = require('../../utils/codexlens-path.js');
25
+ return join(getCodexLensDataDir(), 'indexes');
26
+ }
27
+
12
28
  /**
13
29
  * PathMapper utility class (simplified from codex-lens Python implementation)
14
30
  * Maps source paths to index database paths
@@ -17,7 +33,7 @@ class PathMapper {
17
33
  private indexRoot: string;
18
34
 
19
35
  constructor(indexRoot?: string) {
20
- this.indexRoot = indexRoot || join(homedir(), '.codexlens', 'indexes');
36
+ this.indexRoot = indexRoot || getIndexRoot();
21
37
  }
22
38
 
23
39
  /**