rrce-workflow 0.3.12 → 0.3.14

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 (42) hide show
  1. package/README.md +12 -0
  2. package/agent-core/prompts/_base.md +8 -1
  3. package/agent-core/prompts/doctor.md +2 -2
  4. package/agent-core/prompts/documentation.md +1 -1
  5. package/agent-core/prompts/executor.md +1 -1
  6. package/agent-core/prompts/init.md +1 -1
  7. package/agent-core/prompts/orchestrator.md +13 -3
  8. package/agent-core/prompts/planning_discussion.md +1 -1
  9. package/agent-core/prompts/research_discussion.md +2 -2
  10. package/agent-core/prompts/sync.md +1 -1
  11. package/dist/index.js +469 -519
  12. package/docs/AI_AGENT_GUIDE.md +65 -0
  13. package/package.json +1 -1
  14. package/dist/commands/selector.d.ts +0 -1
  15. package/dist/commands/selector.js +0 -29
  16. package/dist/commands/wizard/index.d.ts +0 -1
  17. package/dist/commands/wizard/index.js +0 -86
  18. package/dist/commands/wizard/link-flow.d.ts +0 -5
  19. package/dist/commands/wizard/link-flow.js +0 -97
  20. package/dist/commands/wizard/setup-flow.d.ts +0 -4
  21. package/dist/commands/wizard/setup-flow.js +0 -262
  22. package/dist/commands/wizard/sync-flow.d.ts +0 -4
  23. package/dist/commands/wizard/sync-flow.js +0 -67
  24. package/dist/commands/wizard/update-flow.d.ts +0 -4
  25. package/dist/commands/wizard/update-flow.js +0 -85
  26. package/dist/commands/wizard/utils.d.ts +0 -9
  27. package/dist/commands/wizard/utils.js +0 -33
  28. package/dist/commands/wizard/vscode.d.ts +0 -15
  29. package/dist/commands/wizard/vscode.js +0 -148
  30. package/dist/index.d.ts +0 -1
  31. package/dist/lib/autocomplete-prompt.d.ts +0 -14
  32. package/dist/lib/autocomplete-prompt.js +0 -167
  33. package/dist/lib/detection.d.ts +0 -44
  34. package/dist/lib/detection.js +0 -185
  35. package/dist/lib/git.d.ts +0 -12
  36. package/dist/lib/git.js +0 -37
  37. package/dist/lib/paths.d.ts +0 -108
  38. package/dist/lib/paths.js +0 -296
  39. package/dist/lib/prompts.d.ts +0 -18
  40. package/dist/lib/prompts.js +0 -62
  41. package/dist/types/prompt.d.ts +0 -54
  42. package/dist/types/prompt.js +0 -20
package/dist/index.js CHANGED
@@ -857,207 +857,74 @@ var init_tui_utils = __esm({
857
857
  import * as fs6 from "fs";
858
858
  import * as path7 from "path";
859
859
  import * as os from "os";
860
- function checkInstallStatus(workspacePath) {
861
- return {
862
- antigravity: checkAntigravityConfig(),
863
- claude: checkClaudeConfig(),
864
- vscodeGlobal: checkVSCodeGlobalConfig(),
865
- vscodeWorkspace: workspacePath ? checkVSCodeWorkspaceConfig(workspacePath) : false,
866
- opencode: checkOpenCodeConfig()
867
- };
868
- }
869
- function checkAntigravityConfig() {
870
- if (!fs6.existsSync(ANTIGRAVITY_CONFIG)) return false;
871
- try {
872
- const content = JSON.parse(fs6.readFileSync(ANTIGRAVITY_CONFIG, "utf-8"));
873
- return !!content.mcpServers?.["rrce"];
874
- } catch {
875
- return false;
876
- }
877
- }
878
- function checkClaudeConfig() {
879
- if (!fs6.existsSync(CLAUDE_CONFIG)) return false;
880
- try {
881
- const content = JSON.parse(fs6.readFileSync(CLAUDE_CONFIG, "utf-8"));
882
- return !!content.mcpServers?.["rrce"];
883
- } catch {
884
- return false;
885
- }
886
- }
887
- function checkVSCodeGlobalConfig() {
888
- if (!fs6.existsSync(VSCODE_GLOBAL_CONFIG)) return false;
889
- try {
890
- const content = JSON.parse(fs6.readFileSync(VSCODE_GLOBAL_CONFIG, "utf-8"));
891
- return !!content["mcp.servers"]?.["rrce"];
892
- } catch {
893
- return false;
894
- }
895
- }
896
- function checkVSCodeWorkspaceConfig(workspacePath) {
897
- const configPath = path7.join(workspacePath, ".vscode", "mcp.json");
860
+ function checkConfig(target, workspacePath) {
861
+ const config = TARGET_CONFIGS[target];
862
+ const configPath = config.requiresWorkspacePath && workspacePath ? path7.join(workspacePath, ".vscode", "mcp.json") : config.path;
898
863
  if (!fs6.existsSync(configPath)) return false;
899
864
  try {
900
865
  const content = JSON.parse(fs6.readFileSync(configPath, "utf-8"));
901
- return !!content.servers?.["rrce"];
866
+ const targetContainer = config.configKey === "mcp" && target === "opencode" ? content.mcp : config.configKey === "mcp.servers" ? content.mcpServers : config.configKey === "servers" ? content.servers : config.configKey === "mcp.servers" ? content["mcp.servers"] : content[config.configKey];
867
+ return !!(targetContainer && targetContainer[config.mcpKey]);
902
868
  } catch {
903
869
  return false;
904
870
  }
905
871
  }
906
- function checkOpenCodeConfig() {
907
- if (!fs6.existsSync(OPENCODE_CONFIG)) return false;
908
- try {
909
- const content = JSON.parse(fs6.readFileSync(OPENCODE_CONFIG, "utf-8"));
910
- return !!content.mcp?.["rrce"];
911
- } catch {
912
- return false;
913
- }
872
+ function checkInstallStatus(workspacePath) {
873
+ return {
874
+ antigravity: checkConfig("antigravity"),
875
+ claude: checkConfig("claude"),
876
+ vscodeGlobal: checkConfig("vscode-global"),
877
+ vscodeWorkspace: workspacePath ? checkConfig("vscode-workspace", workspacePath) : false,
878
+ opencode: checkConfig("opencode")
879
+ };
914
880
  }
915
881
  function isInstalledAnywhere(workspacePath) {
916
882
  const status = checkInstallStatus(workspacePath);
917
883
  return status.antigravity || status.claude || status.vscodeGlobal || status.vscodeWorkspace || status.opencode;
918
884
  }
919
- function installToConfig(target, workspacePath) {
920
- switch (target) {
921
- case "antigravity":
922
- return installToAntigravity();
923
- case "claude":
924
- return installToClaude();
925
- case "vscode-global":
926
- return installToVSCodeGlobal();
927
- case "vscode-workspace":
928
- return workspacePath ? installToVSCodeWorkspace(workspacePath) : false;
929
- case "opencode":
930
- return installToOpenCode();
931
- default:
932
- return false;
933
- }
934
- }
935
- function installToAntigravity() {
936
- const dir = path7.dirname(ANTIGRAVITY_CONFIG);
937
- if (!fs6.existsSync(dir)) {
938
- fs6.mkdirSync(dir, { recursive: true });
939
- }
940
- let config = { mcpServers: {} };
941
- if (fs6.existsSync(ANTIGRAVITY_CONFIG)) {
942
- try {
943
- config = JSON.parse(fs6.readFileSync(ANTIGRAVITY_CONFIG, "utf-8"));
944
- } catch {
945
- }
946
- }
947
- if (!config.mcpServers) config.mcpServers = {};
948
- config.mcpServers["rrce"] = {
949
- command: "npx",
950
- args: ["-y", "rrce-workflow", "mcp", "start"]
951
- };
952
- try {
953
- fs6.writeFileSync(ANTIGRAVITY_CONFIG, JSON.stringify(config, null, 2));
954
- return true;
955
- } catch {
956
- return false;
957
- }
958
- }
959
- function installToClaude() {
960
- const dir = path7.dirname(CLAUDE_CONFIG);
961
- if (!fs6.existsSync(dir)) {
962
- fs6.mkdirSync(dir, { recursive: true });
963
- }
964
- let config = { mcpServers: {} };
965
- if (fs6.existsSync(CLAUDE_CONFIG)) {
966
- try {
967
- config = JSON.parse(fs6.readFileSync(CLAUDE_CONFIG, "utf-8"));
968
- } catch {
969
- }
970
- }
971
- if (!config.mcpServers) config.mcpServers = {};
972
- config.mcpServers["rrce"] = {
973
- command: "npx",
974
- args: ["-y", "rrce-workflow", "mcp", "start"]
975
- };
976
- try {
977
- fs6.writeFileSync(CLAUDE_CONFIG, JSON.stringify(config, null, 2));
978
- return true;
979
- } catch {
980
- return false;
981
- }
982
- }
983
- function installToVSCodeGlobal() {
984
- const dir = path7.dirname(VSCODE_GLOBAL_CONFIG);
885
+ function installToTarget(target, workspacePath) {
886
+ const config = TARGET_CONFIGS[target];
887
+ const configPath = config.requiresWorkspacePath && workspacePath ? path7.join(workspacePath, ".vscode", "mcp.json") : config.path;
888
+ const dir = path7.dirname(configPath);
985
889
  if (!fs6.existsSync(dir)) {
986
890
  fs6.mkdirSync(dir, { recursive: true });
987
891
  }
988
- let settings = {};
989
- if (fs6.existsSync(VSCODE_GLOBAL_CONFIG)) {
990
- try {
991
- settings = JSON.parse(fs6.readFileSync(VSCODE_GLOBAL_CONFIG, "utf-8"));
992
- } catch {
993
- }
994
- }
995
- if (!settings["mcp.servers"]) settings["mcp.servers"] = {};
996
- settings["mcp.servers"]["rrce"] = {
997
- command: "npx",
998
- args: ["-y", "rrce-workflow", "mcp", "start"]
999
- };
1000
- try {
1001
- fs6.writeFileSync(VSCODE_GLOBAL_CONFIG, JSON.stringify(settings, null, 2));
1002
- return true;
1003
- } catch {
1004
- return false;
892
+ let existingConfig = {};
893
+ if (target === "opencode") {
894
+ existingConfig = { $schema: "https://opencode.ai/config.json" };
895
+ } else if (config.configKey === "servers") {
896
+ existingConfig = { servers: {} };
897
+ } else if (config.configKey === "mcp.servers") {
898
+ existingConfig = { mcpServers: {} };
899
+ } else if (config.configKey === "mcp") {
900
+ existingConfig = {};
1005
901
  }
1006
- }
1007
- function installToVSCodeWorkspace(workspacePath) {
1008
- const vscodeDir = path7.join(workspacePath, ".vscode");
1009
- const configPath = path7.join(vscodeDir, "mcp.json");
1010
- if (!fs6.existsSync(vscodeDir)) {
1011
- fs6.mkdirSync(vscodeDir, { recursive: true });
1012
- }
1013
- let config = { servers: {} };
1014
902
  if (fs6.existsSync(configPath)) {
1015
903
  try {
1016
- config = JSON.parse(fs6.readFileSync(configPath, "utf-8"));
1017
- } catch {
1018
- }
1019
- }
1020
- if (!config.servers) config.servers = {};
1021
- config.servers["rrce"] = {
1022
- command: "npx",
1023
- args: ["-y", "rrce-workflow", "mcp", "start"]
1024
- };
1025
- try {
1026
- fs6.writeFileSync(configPath, JSON.stringify(config, null, 2));
1027
- return true;
1028
- } catch {
1029
- return false;
1030
- }
1031
- }
1032
- function installToOpenCode() {
1033
- const dir = path7.dirname(OPENCODE_CONFIG);
1034
- if (!fs6.existsSync(dir)) {
1035
- fs6.mkdirSync(dir, { recursive: true });
1036
- }
1037
- let config = {
1038
- $schema: "https://opencode.ai/config.json"
1039
- };
1040
- if (fs6.existsSync(OPENCODE_CONFIG)) {
1041
- try {
1042
- config = JSON.parse(fs6.readFileSync(OPENCODE_CONFIG, "utf-8"));
904
+ existingConfig = JSON.parse(fs6.readFileSync(configPath, "utf-8"));
1043
905
  } catch (error) {
1044
- console.error("Warning: Could not parse existing OpenCode config, creating fresh config");
906
+ if (target === "opencode") {
907
+ console.error("Warning: Could not parse existing OpenCode config, creating fresh config");
908
+ }
1045
909
  }
1046
910
  }
1047
- if (!config.mcp) config.mcp = {};
1048
- config.mcp["rrce"] = {
1049
- type: "local",
1050
- command: ["npx", "-y", "rrce-workflow", "mcp", "start"],
1051
- enabled: true
1052
- };
911
+ const container = config.configKey === "mcp" && target === "opencode" ? existingConfig.mcp || (existingConfig.mcp = {}) : config.configKey === "mcp.servers" ? existingConfig.mcpServers || (existingConfig.mcpServers = {}) : config.configKey === "servers" ? existingConfig.servers || (existingConfig.servers = {}) : config.configKey === "mcp.servers" ? existingConfig["mcp.servers"] || (existingConfig["mcp.servers"] = {}) : existingConfig[config.configKey] || (existingConfig[config.configKey] = {});
912
+ container[config.mcpKey] = config.serverConfig;
1053
913
  try {
1054
- fs6.writeFileSync(OPENCODE_CONFIG, JSON.stringify(config, null, 2) + "\n");
914
+ const json = JSON.stringify(existingConfig, null, 2);
915
+ fs6.writeFileSync(configPath, json + (target === "opencode" ? "\n" : ""));
1055
916
  return true;
1056
917
  } catch (error) {
1057
- console.error("Failed to write OpenCode config:", error instanceof Error ? error.message : String(error));
918
+ if (target === "opencode") {
919
+ console.error("Failed to write OpenCode config:", error instanceof Error ? error.message : String(error));
920
+ }
1058
921
  return false;
1059
922
  }
1060
923
  }
924
+ function installToConfig(target, workspacePath) {
925
+ if (target === "vscode-workspace" && !workspacePath) return false;
926
+ return installToTarget(target, workspacePath);
927
+ }
1061
928
  function getTargetLabel(target) {
1062
929
  switch (target) {
1063
930
  case "antigravity":
@@ -1087,7 +954,7 @@ function isVSCodeInstalled() {
1087
954
  const configDir = path7.join(os.homedir(), ".config/Code/User");
1088
955
  return fs6.existsSync(configDir);
1089
956
  }
1090
- var ANTIGRAVITY_CONFIG, CLAUDE_CONFIG, VSCODE_GLOBAL_CONFIG, OPENCODE_CONFIG_DIR, OPENCODE_CONFIG;
957
+ var ANTIGRAVITY_CONFIG, CLAUDE_CONFIG, VSCODE_GLOBAL_CONFIG, OPENCODE_CONFIG_DIR, OPENCODE_CONFIG, TARGET_CONFIGS;
1091
958
  var init_install = __esm({
1092
959
  "src/mcp/install.ts"() {
1093
960
  "use strict";
@@ -1096,6 +963,56 @@ var init_install = __esm({
1096
963
  VSCODE_GLOBAL_CONFIG = path7.join(os.homedir(), ".config/Code/User/settings.json");
1097
964
  OPENCODE_CONFIG_DIR = path7.join(os.homedir(), ".config/opencode");
1098
965
  OPENCODE_CONFIG = path7.join(OPENCODE_CONFIG_DIR, "opencode.json");
966
+ TARGET_CONFIGS = {
967
+ antigravity: {
968
+ path: ANTIGRAVITY_CONFIG,
969
+ configKey: "mcpServers",
970
+ mcpKey: "rrce",
971
+ serverConfig: {
972
+ command: "npx",
973
+ args: ["-y", "rrce-workflow", "mcp", "start"]
974
+ }
975
+ },
976
+ claude: {
977
+ path: CLAUDE_CONFIG,
978
+ configKey: "mcpServers",
979
+ mcpKey: "rrce",
980
+ serverConfig: {
981
+ command: "npx",
982
+ args: ["-y", "rrce-workflow", "mcp", "start"]
983
+ }
984
+ },
985
+ "vscode-global": {
986
+ path: VSCODE_GLOBAL_CONFIG,
987
+ configKey: "mcp.servers",
988
+ mcpKey: "rrce",
989
+ serverConfig: {
990
+ command: "npx",
991
+ args: ["-y", "rrce-workflow", "mcp", "start"]
992
+ }
993
+ },
994
+ "vscode-workspace": {
995
+ path: "",
996
+ // Resolved dynamically
997
+ configKey: "servers",
998
+ mcpKey: "rrce",
999
+ serverConfig: {
1000
+ command: "npx",
1001
+ args: ["-y", "rrce-workflow", "mcp", "start"]
1002
+ },
1003
+ requiresWorkspacePath: true
1004
+ },
1005
+ opencode: {
1006
+ path: OPENCODE_CONFIG,
1007
+ configKey: "mcp",
1008
+ mcpKey: "rrce",
1009
+ serverConfig: {
1010
+ type: "local",
1011
+ command: ["npx", "-y", "rrce-workflow", "mcp", "start"],
1012
+ enabled: true
1013
+ }
1014
+ }
1015
+ };
1099
1016
  }
1100
1017
  });
1101
1018
 
@@ -1239,11 +1156,7 @@ function convertToOpenCodeAgent(prompt, useFileReference = false, promptFilePath
1239
1156
  const hostTools = ["read", "write", "edit", "bash", "grep", "glob", "webfetch", "terminalLastCommand", "task"];
1240
1157
  if (frontmatter.tools) {
1241
1158
  for (const tool of frontmatter.tools) {
1242
- if (hostTools.includes(tool)) {
1243
- tools[tool] = true;
1244
- } else {
1245
- tools[`rrce_${tool}`] = true;
1246
- }
1159
+ tools[tool] = true;
1247
1160
  }
1248
1161
  }
1249
1162
  tools["webfetch"] = true;
@@ -1397,10 +1310,10 @@ function findProjectConfig(config, identifier) {
1397
1310
  if (targetPath && configPath) {
1398
1311
  return configPath === targetPath;
1399
1312
  }
1400
- if (!targetPath && !configPath) {
1313
+ if (!targetPath && !configPath && identifier.name) {
1401
1314
  return p.name === identifier.name;
1402
1315
  }
1403
- if (targetPath && !configPath) {
1316
+ if (targetPath && !configPath && identifier.name) {
1404
1317
  return p.name === identifier.name;
1405
1318
  }
1406
1319
  return false;
@@ -2622,13 +2535,13 @@ function resolveProjectPaths(project, pathInput) {
2622
2535
  let workspaceRoot = pathInput;
2623
2536
  let workspaceName = project;
2624
2537
  if (!workspaceRoot && project) {
2625
- const projConfig = config.projects.find((p) => p.name === project);
2538
+ const projConfig = findProjectConfig(config, { name: project });
2626
2539
  if (projConfig?.path) {
2627
2540
  workspaceRoot = projConfig.path;
2628
2541
  }
2629
2542
  }
2630
2543
  if (!workspaceName && workspaceRoot) {
2631
- const projConfig = config.projects.find((p) => p.path && normalizeProjectPath(p.path) === normalizeProjectPath(workspaceRoot));
2544
+ const projConfig = findProjectConfig(config, { path: workspaceRoot });
2632
2545
  workspaceName = projConfig?.name || getWorkspaceName(workspaceRoot);
2633
2546
  }
2634
2547
  if (!workspaceName) {
@@ -2766,11 +2679,13 @@ function getProjectTasks(projectName) {
2766
2679
  try {
2767
2680
  const meta = JSON.parse(fs15.readFileSync(metaPath, "utf-8"));
2768
2681
  tasks.push(meta);
2769
- } catch {
2682
+ } catch (err) {
2683
+ logger.error(`[getProjectTasks] Failed to parse meta.json in ${dir.name}`, err);
2770
2684
  }
2771
2685
  }
2772
2686
  }
2773
- } catch {
2687
+ } catch (err) {
2688
+ logger.error(`[getProjectTasks] Failed to read tasks directory ${project.tasksPath}`, err);
2774
2689
  }
2775
2690
  return tasks;
2776
2691
  }
@@ -2784,9 +2699,7 @@ async function searchCode(query, projectFilter, limit = 10) {
2784
2699
  if (!permissions.knowledge || !project.knowledgePath) continue;
2785
2700
  const indexingInProgress = indexingJobs.isRunning(project.name);
2786
2701
  const advisoryMessage = indexingInProgress ? "Indexing in progress; results may be stale/incomplete." : void 0;
2787
- const projConfig = config.projects.find(
2788
- (p) => p.path && normalizeProjectPath(p.path) === normalizeProjectPath(project.sourcePath || project.path) || !p.path && p.name === project.name
2789
- );
2702
+ const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path });
2790
2703
  const useRAG = projConfig?.semanticSearch?.enabled;
2791
2704
  if (!useRAG) {
2792
2705
  logger.debug(`[searchCode] Semantic search not enabled for project '${project.name}'`);
@@ -2833,9 +2746,7 @@ async function searchKnowledge(query, projectFilter) {
2833
2746
  if (!permissions.knowledge || !project.knowledgePath) continue;
2834
2747
  const indexingInProgress = indexingJobs.isRunning(project.name);
2835
2748
  const advisoryMessage = indexingInProgress ? "Indexing in progress; results may be stale/incomplete." : void 0;
2836
- const projConfig = config.projects.find(
2837
- (p) => p.path && normalizeProjectPath(p.path) === normalizeProjectPath(project.sourcePath || project.path) || !p.path && p.name === project.name
2838
- );
2749
+ const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path });
2839
2750
  const useRAG = projConfig?.semanticSearch?.enabled;
2840
2751
  if (useRAG) {
2841
2752
  logger.info(`[RAG] Using semantic search for project '${project.name}'`);
@@ -2883,11 +2794,42 @@ async function searchKnowledge(query, projectFilter) {
2883
2794
  });
2884
2795
  }
2885
2796
  }
2886
- } catch {
2797
+ } catch (err) {
2798
+ logger.error(`[searchKnowledge] Failed to read knowledge directory ${project.knowledgePath}`, err);
2887
2799
  }
2888
2800
  }
2889
2801
  return results;
2890
2802
  }
2803
+ function getScanContext(project, scanRoot) {
2804
+ const gitignorePath = path17.join(scanRoot, ".gitignore");
2805
+ const ig = fs15.existsSync(gitignorePath) ? ignore().add(fs15.readFileSync(gitignorePath, "utf-8")) : null;
2806
+ const toPosixRelativePath = (absolutePath) => {
2807
+ const rel = path17.relative(scanRoot, absolutePath);
2808
+ return rel.split(path17.sep).join("/");
2809
+ };
2810
+ const isUnderGitDir = (absolutePath) => {
2811
+ const rel = toPosixRelativePath(absolutePath);
2812
+ return rel === ".git" || rel.startsWith(".git/");
2813
+ };
2814
+ const isIgnoredByGitignore = (absolutePath, isDir) => {
2815
+ if (!ig) return false;
2816
+ const rel = toPosixRelativePath(absolutePath);
2817
+ return ig.ignores(isDir ? `${rel}/` : rel);
2818
+ };
2819
+ const shouldSkipEntryDir = (absolutePath) => {
2820
+ const dirName = path17.basename(absolutePath);
2821
+ if (dirName === ".git") return true;
2822
+ if (SKIP_DIRS.includes(dirName)) return true;
2823
+ if (isIgnoredByGitignore(absolutePath, true)) return true;
2824
+ return false;
2825
+ };
2826
+ const shouldSkipEntryFile = (absolutePath) => {
2827
+ if (isUnderGitDir(absolutePath)) return true;
2828
+ if (isIgnoredByGitignore(absolutePath, false)) return true;
2829
+ return false;
2830
+ };
2831
+ return { shouldSkipEntryDir, shouldSkipEntryFile };
2832
+ }
2891
2833
  async function indexKnowledge(projectName, force = false) {
2892
2834
  const config = loadMCPConfig();
2893
2835
  const projects = getExposedProjects();
@@ -2903,9 +2845,7 @@ async function indexKnowledge(projectName, force = false) {
2903
2845
  progress: { itemsDone: 0 }
2904
2846
  };
2905
2847
  }
2906
- const projConfig = config.projects.find(
2907
- (p2) => p2.path && normalizeProjectPath(p2.path) === normalizeProjectPath(project.sourcePath || project.path) || !p2.path && p2.name === project.name
2908
- ) || (project.source === "global" ? { semanticSearch: { enabled: true, model: "Xenova/all-MiniLM-L6-v2" } } : void 0);
2848
+ const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path }) || (project.source === "global" ? { semanticSearch: { enabled: true, model: "Xenova/all-MiniLM-L6-v2" } } : void 0);
2909
2849
  const isEnabled = projConfig?.semanticSearch?.enabled || project.semanticSearchEnabled;
2910
2850
  if (!isEnabled) {
2911
2851
  return {
@@ -2930,100 +2870,8 @@ async function indexKnowledge(projectName, force = false) {
2930
2870
  progress: { itemsDone: 0 }
2931
2871
  };
2932
2872
  }
2933
- const INDEXABLE_EXTENSIONS = [
2934
- ".ts",
2935
- ".tsx",
2936
- ".js",
2937
- ".jsx",
2938
- ".mjs",
2939
- ".cjs",
2940
- ".py",
2941
- ".pyw",
2942
- ".go",
2943
- ".rs",
2944
- ".java",
2945
- ".kt",
2946
- ".kts",
2947
- ".c",
2948
- ".cpp",
2949
- ".h",
2950
- ".hpp",
2951
- ".cs",
2952
- ".rb",
2953
- ".php",
2954
- ".swift",
2955
- ".md",
2956
- ".mdx",
2957
- ".json",
2958
- ".yaml",
2959
- ".yml",
2960
- ".toml",
2961
- ".sh",
2962
- ".bash",
2963
- ".zsh",
2964
- ".sql",
2965
- ".html",
2966
- ".css",
2967
- ".scss",
2968
- ".sass",
2969
- ".less"
2970
- ];
2971
- const CODE_EXTENSIONS = [
2972
- ".ts",
2973
- ".tsx",
2974
- ".js",
2975
- ".jsx",
2976
- ".mjs",
2977
- ".cjs",
2978
- ".py",
2979
- ".pyw",
2980
- ".go",
2981
- ".rs",
2982
- ".java",
2983
- ".kt",
2984
- ".kts",
2985
- ".c",
2986
- ".cpp",
2987
- ".h",
2988
- ".hpp",
2989
- ".cs",
2990
- ".rb",
2991
- ".php",
2992
- ".swift",
2993
- ".sh",
2994
- ".bash",
2995
- ".zsh",
2996
- ".sql"
2997
- ];
2998
- const SKIP_DIRS = ["node_modules", ".git", "dist", "build", ".next", "__pycache__", "venv", ".venv", "target", "vendor"];
2999
- const gitignorePath = path17.join(scanRoot, ".gitignore");
3000
- const ig = fs15.existsSync(gitignorePath) ? ignore().add(fs15.readFileSync(gitignorePath, "utf-8")) : null;
3001
- const toPosixRelativePath = (absolutePath) => {
3002
- const rel = path17.relative(scanRoot, absolutePath);
3003
- return rel.split(path17.sep).join("/");
3004
- };
3005
- const isUnderGitDir = (absolutePath) => {
3006
- const rel = toPosixRelativePath(absolutePath);
3007
- return rel === ".git" || rel.startsWith(".git/");
3008
- };
3009
- const isIgnoredByGitignore = (absolutePath, isDir) => {
3010
- if (!ig) return false;
3011
- const rel = toPosixRelativePath(absolutePath);
3012
- return ig.ignores(isDir ? `${rel}/` : rel);
3013
- };
3014
- const shouldSkipEntryDir = (absolutePath) => {
3015
- const dirName = path17.basename(absolutePath);
3016
- if (dirName === ".git") return true;
3017
- if (SKIP_DIRS.includes(dirName)) return true;
3018
- if (isIgnoredByGitignore(absolutePath, true)) return true;
3019
- return false;
3020
- };
3021
- const shouldSkipEntryFile = (absolutePath) => {
3022
- if (isUnderGitDir(absolutePath)) return true;
3023
- if (isIgnoredByGitignore(absolutePath, false)) return true;
3024
- return false;
3025
- };
3026
2873
  const runIndexing = async () => {
2874
+ const { shouldSkipEntryDir, shouldSkipEntryFile } = getScanContext(project, scanRoot);
3027
2875
  const indexPath = path17.join(project.knowledgePath || path17.join(scanRoot, ".rrce-workflow", "knowledge"), "embeddings.json");
3028
2876
  const codeIndexPath = path17.join(project.knowledgePath || path17.join(scanRoot, ".rrce-workflow", "knowledge"), "code-embeddings.json");
3029
2877
  const model = projConfig?.semanticSearch?.model || "Xenova/all-MiniLM-L6-v2";
@@ -3072,18 +2920,12 @@ async function indexKnowledge(projectName, force = false) {
3072
2920
  for (const entry of entries) {
3073
2921
  const fullPath = path17.join(dir, entry.name);
3074
2922
  if (entry.isDirectory()) {
3075
- if (shouldSkipEntryDir(fullPath)) {
3076
- continue;
3077
- }
2923
+ if (shouldSkipEntryDir(fullPath)) continue;
3078
2924
  await scanDir(fullPath);
3079
2925
  } else if (entry.isFile()) {
3080
2926
  const ext = path17.extname(entry.name).toLowerCase();
3081
- if (!INDEXABLE_EXTENSIONS.includes(ext)) {
3082
- continue;
3083
- }
3084
- if (shouldSkipEntryFile(fullPath)) {
3085
- continue;
3086
- }
2927
+ if (!INDEXABLE_EXTENSIONS.includes(ext)) continue;
2928
+ if (shouldSkipEntryFile(fullPath)) continue;
3087
2929
  try {
3088
2930
  indexingJobs.update(project.name, { currentItem: fullPath, itemsDone });
3089
2931
  const stat = fs15.statSync(fullPath);
@@ -3109,6 +2951,7 @@ async function indexKnowledge(projectName, force = false) {
3109
2951
  }
3110
2952
  }
3111
2953
  } catch (err) {
2954
+ logger.error(`[indexKnowledge] Failed to index ${fullPath}`, err);
3112
2955
  } finally {
3113
2956
  itemsDone++;
3114
2957
  indexingJobs.update(project.name, { itemsDone });
@@ -3178,7 +3021,8 @@ function getTask(projectName, taskSlug) {
3178
3021
  if (!fs15.existsSync(metaPath)) return null;
3179
3022
  try {
3180
3023
  return JSON.parse(fs15.readFileSync(metaPath, "utf-8"));
3181
- } catch {
3024
+ } catch (err) {
3025
+ logger.error(`[getTask] Failed to parse meta.json for task ${taskSlug}`, err);
3182
3026
  return null;
3183
3027
  }
3184
3028
  }
@@ -3316,6 +3160,7 @@ async function findRelatedFiles2(filePath, projectName, options = {}) {
3316
3160
  };
3317
3161
  }
3318
3162
  }
3163
+ var INDEXABLE_EXTENSIONS, CODE_EXTENSIONS, SKIP_DIRS;
3319
3164
  var init_resources = __esm({
3320
3165
  "src/mcp/resources.ts"() {
3321
3166
  "use strict";
@@ -3329,6 +3174,72 @@ var init_resources = __esm({
3329
3174
  init_context_extractor();
3330
3175
  init_dependency_graph();
3331
3176
  init_paths();
3177
+ INDEXABLE_EXTENSIONS = [
3178
+ ".ts",
3179
+ ".tsx",
3180
+ ".js",
3181
+ ".jsx",
3182
+ ".mjs",
3183
+ ".cjs",
3184
+ ".py",
3185
+ ".pyw",
3186
+ ".go",
3187
+ ".rs",
3188
+ ".java",
3189
+ ".kt",
3190
+ ".kts",
3191
+ ".c",
3192
+ ".cpp",
3193
+ ".h",
3194
+ ".hpp",
3195
+ ".cs",
3196
+ ".rb",
3197
+ ".php",
3198
+ ".swift",
3199
+ ".md",
3200
+ ".mdx",
3201
+ ".json",
3202
+ ".yaml",
3203
+ ".yml",
3204
+ ".toml",
3205
+ ".sh",
3206
+ ".bash",
3207
+ ".zsh",
3208
+ ".sql",
3209
+ ".html",
3210
+ ".css",
3211
+ ".scss",
3212
+ ".sass",
3213
+ ".less"
3214
+ ];
3215
+ CODE_EXTENSIONS = [
3216
+ ".ts",
3217
+ ".tsx",
3218
+ ".js",
3219
+ ".jsx",
3220
+ ".mjs",
3221
+ ".cjs",
3222
+ ".py",
3223
+ ".pyw",
3224
+ ".go",
3225
+ ".rs",
3226
+ ".java",
3227
+ ".kt",
3228
+ ".kts",
3229
+ ".c",
3230
+ ".cpp",
3231
+ ".h",
3232
+ ".hpp",
3233
+ ".cs",
3234
+ ".rb",
3235
+ ".php",
3236
+ ".swift",
3237
+ ".sh",
3238
+ ".bash",
3239
+ ".zsh",
3240
+ ".sql"
3241
+ ];
3242
+ SKIP_DIRS = ["node_modules", ".git", "dist", "build", ".next", "__pycache__", "venv", ".venv", "target", "vendor"];
3332
3243
  }
3333
3244
  });
3334
3245
 
@@ -4666,17 +4577,37 @@ var init_SimpleSelect = __esm({
4666
4577
  }
4667
4578
  });
4668
4579
 
4580
+ // src/lib/project-utils.ts
4581
+ function sortProjects(projects, workspacePath) {
4582
+ return [...projects].sort((a, b) => {
4583
+ const aIsCurrent = workspacePath ? a.path === workspacePath : false;
4584
+ const bIsCurrent = workspacePath ? b.path === workspacePath : false;
4585
+ if (aIsCurrent && !bIsCurrent) return -1;
4586
+ if (!aIsCurrent && bIsCurrent) return 1;
4587
+ const byName = a.name.localeCompare(b.name);
4588
+ if (byName !== 0) return byName;
4589
+ const aKey = a.sourcePath ?? a.path;
4590
+ const bKey = b.sourcePath ?? b.path;
4591
+ return aKey.localeCompare(bKey);
4592
+ });
4593
+ }
4594
+ function projectKey(project) {
4595
+ return project.sourcePath ?? project.path;
4596
+ }
4597
+ function formatProjectLabel(project) {
4598
+ const root = project.sourcePath ?? project.path;
4599
+ return `${project.name} (${project.source})${root ? ` - ${root}` : ""}`;
4600
+ }
4601
+ var init_project_utils = __esm({
4602
+ "src/lib/project-utils.ts"() {
4603
+ "use strict";
4604
+ }
4605
+ });
4606
+
4669
4607
  // src/mcp/ui/ProjectsView.tsx
4670
4608
  import { useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
4671
4609
  import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
4672
4610
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
4673
- function projectKey(p) {
4674
- return p.sourcePath ?? p.path;
4675
- }
4676
- function formatProjectLabel(p) {
4677
- const root = p.sourcePath ?? p.path;
4678
- return `${p.name} (${p.source})${root ? ` - ${root}` : ""}`;
4679
- }
4680
4611
  var ProjectsView;
4681
4612
  var init_ProjectsView = __esm({
4682
4613
  "src/mcp/ui/ProjectsView.tsx"() {
@@ -4686,29 +4617,19 @@ var init_ProjectsView = __esm({
4686
4617
  init_ConfigContext();
4687
4618
  init_indexing_jobs();
4688
4619
  init_config_utils();
4620
+ init_project_utils();
4689
4621
  ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange, workspacePath }) => {
4690
4622
  const { driftReports, checkAllDrift } = useConfig();
4691
4623
  const [config, setConfig] = useState3(initialConfig);
4692
4624
  const [indexingStats, setIndexingStats] = useState3({});
4693
4625
  const sortedProjects = useMemo3(() => {
4694
- return [...allProjects].sort((a, b) => {
4695
- const aIsCurrent = a.path === workspacePath;
4696
- const bIsCurrent = b.path === workspacePath;
4697
- if (aIsCurrent && !bIsCurrent) return -1;
4698
- if (!aIsCurrent && bIsCurrent) return 1;
4699
- const byName = a.name.localeCompare(b.name);
4700
- if (byName !== 0) return byName;
4701
- return projectKey(a).localeCompare(projectKey(b));
4702
- });
4626
+ return sortProjects(allProjects, workspacePath);
4703
4627
  }, [allProjects, workspacePath]);
4704
4628
  useEffect3(() => {
4705
4629
  const updateStats = () => {
4706
4630
  const next = {};
4707
4631
  for (const p of allProjects) {
4708
- let projConfig = findProjectConfig(config, { name: p.name, path: p.path });
4709
- if (!projConfig && p.source === "global") {
4710
- projConfig = config.projects.find((c) => c.name === p.name);
4711
- }
4632
+ const projConfig = findProjectConfig(config, { name: p.name, path: p.path });
4712
4633
  const enabled = projConfig?.semanticSearch?.enabled || p.semanticSearchEnabled || false;
4713
4634
  const prog = indexingJobs.getProgress(p.name);
4714
4635
  next[p.name] = { enabled, ...prog };
@@ -4739,9 +4660,7 @@ var init_ProjectsView = __esm({
4739
4660
  });
4740
4661
  const projectItems = useMemo3(() => {
4741
4662
  return sortedProjects.map((p) => {
4742
- const projectConfig = config.projects.find(
4743
- (c) => c.path && c.path === p.path || p.source === "global" && c.name === p.name || !c.path && c.name === p.name
4744
- );
4663
+ const projectConfig = findProjectConfig(config, { name: p.name, path: p.path });
4745
4664
  const isExposed = projectConfig ? projectConfig.expose : config.defaults.includeNew;
4746
4665
  const drift = driftReports[p.path];
4747
4666
  const idx = indexingStats[p.name];
@@ -4777,7 +4696,7 @@ var init_ProjectsView = __esm({
4777
4696
  const isSelected = selectedIds.includes(item.value);
4778
4697
  const project = allProjects.find((p) => p.path === item.value);
4779
4698
  if (project) {
4780
- const existingConfig = newConfig.projects.find((p) => p.name === project.name);
4699
+ const existingConfig = findProjectConfig(newConfig, { name: project.name, path: project.path });
4781
4700
  const projectPath = project.source === "global" && existingConfig?.path ? existingConfig.path : project.path;
4782
4701
  newConfig = setProjectConfig(
4783
4702
  newConfig,
@@ -4874,28 +4793,168 @@ var init_ui_helpers = __esm({
4874
4793
  }
4875
4794
  });
4876
4795
 
4796
+ // src/mcp/ui/components/TaskRow.tsx
4797
+ import "react";
4798
+ import { Box as Box5, Text as Text5 } from "ink";
4799
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
4800
+ var TaskRow;
4801
+ var init_TaskRow = __esm({
4802
+ "src/mcp/ui/components/TaskRow.tsx"() {
4803
+ "use strict";
4804
+ init_ui_helpers();
4805
+ init_project_utils();
4806
+ TaskRow = ({ row, isSelected, isExpanded, taskCount, hasDrift }) => {
4807
+ if (row.kind === "project") {
4808
+ return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsxs4(Box5, { children: [
4809
+ /* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "white", children: isSelected ? "> " : " " }),
4810
+ /* @__PURE__ */ jsxs4(Text5, { color: isSelected ? "cyan" : "white", children: [
4811
+ getFolderIcon(isExpanded),
4812
+ " ",
4813
+ formatProjectLabel(row.project)
4814
+ ] }),
4815
+ hasDrift && /* @__PURE__ */ jsx6(Text5, { color: "magenta", children: " \u26A0" }),
4816
+ /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
4817
+ " ",
4818
+ taskCount > 0 ? `(${taskCount})` : ""
4819
+ ] })
4820
+ ] }) });
4821
+ }
4822
+ const taskLabel = row.task.title || row.task.task_slug;
4823
+ const status = row.task.status || "";
4824
+ return /* @__PURE__ */ jsxs4(Box5, { children: [
4825
+ /* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "white", children: isSelected ? "> " : " " }),
4826
+ /* @__PURE__ */ jsx6(Text5, { color: "dim", children: " - " }),
4827
+ /* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "white", children: taskLabel }),
4828
+ row.task.task_slug !== "__none__" && /* @__PURE__ */ jsx6(Text5, { backgroundColor: getStatusColor(status), color: "black", children: ` ${getStatusIcon(status)} ${status.toUpperCase().replace("_", " ")} ` })
4829
+ ] });
4830
+ };
4831
+ }
4832
+ });
4833
+
4834
+ // src/mcp/ui/components/TaskDetails.tsx
4835
+ import "react";
4836
+ import { Box as Box6, Text as Text6 } from "ink";
4837
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
4838
+ var TaskDetails;
4839
+ var init_TaskDetails = __esm({
4840
+ "src/mcp/ui/components/TaskDetails.tsx"() {
4841
+ "use strict";
4842
+ init_ui_helpers();
4843
+ TaskDetails = ({ task }) => {
4844
+ if (!task) {
4845
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", justifyContent: "center", alignItems: "center", gap: 1, flexGrow: 1, children: [
4846
+ /* @__PURE__ */ jsx7(Text6, { bold: true, color: "dim", children: "\u2500 No Task Selected \u2500" }),
4847
+ /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Use \u2191/\u2193 to navigate, Enter to expand projects" }),
4848
+ /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Press 's' to cycle task status" })
4849
+ ] });
4850
+ }
4851
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
4852
+ /* @__PURE__ */ jsxs5(Box6, { marginBottom: 1, flexDirection: "column", children: [
4853
+ /* @__PURE__ */ jsx7(Text6, { bold: true, color: "cyan", children: task.title || task.task_slug }),
4854
+ task.summary && /* @__PURE__ */ jsx7(Text6, { color: "white", children: task.summary })
4855
+ ] }),
4856
+ /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
4857
+ /* @__PURE__ */ jsxs5(Box6, { marginTop: 1, paddingX: 1, flexDirection: "column", children: [
4858
+ /* @__PURE__ */ jsx7(Text6, { bold: true, color: "white", children: "\u{1F4CB} STATUS" }),
4859
+ /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginTop: 1, children: [
4860
+ /* @__PURE__ */ jsxs5(Text6, { children: [
4861
+ /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Status: " }),
4862
+ " ",
4863
+ /* @__PURE__ */ jsx7(Text6, { color: getStatusColor(task.status || ""), children: task.status || "unknown" })
4864
+ ] }),
4865
+ /* @__PURE__ */ jsxs5(Text6, { children: [
4866
+ /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Updated:" }),
4867
+ " ",
4868
+ /* @__PURE__ */ jsx7(Text6, { children: task.updated_at || "\u2014" })
4869
+ ] }),
4870
+ /* @__PURE__ */ jsxs5(Text6, { children: [
4871
+ /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Tags: " }),
4872
+ " ",
4873
+ " ",
4874
+ (() => {
4875
+ const tags = task.tags || [];
4876
+ return tags.length > 0 ? tags.map((tag, i) => /* @__PURE__ */ jsxs5(Text6, { children: [
4877
+ /* @__PURE__ */ jsx7(Text6, { color: "cyan", children: tag }),
4878
+ i < tags.length - 1 && /* @__PURE__ */ jsx7(Text6, { color: "dim", children: ", " })
4879
+ ] }, tag)) : /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "\u2014" });
4880
+ })()
4881
+ ] })
4882
+ ] })
4883
+ ] }),
4884
+ /* @__PURE__ */ jsx7(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }),
4885
+ /* @__PURE__ */ jsxs5(Box6, { marginTop: 1, paddingX: 1, flexDirection: "column", children: [
4886
+ /* @__PURE__ */ jsx7(Text6, { bold: true, color: "white", children: "\u{1F4CB} CHECKLIST" }),
4887
+ task.checklist && task.checklist.length > 0 && /* @__PURE__ */ jsx7(Box6, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsxs5(Box6, { marginBottom: 1, children: [
4888
+ /* @__PURE__ */ jsx7(Text6, { backgroundColor: "white", children: getProgressBar(getChecklistProgress(task.checklist).percentage) }),
4889
+ /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
4890
+ " ",
4891
+ " ",
4892
+ getChecklistProgress(task.checklist).completed,
4893
+ "/",
4894
+ getChecklistProgress(task.checklist).total,
4895
+ " (",
4896
+ getChecklistProgress(task.checklist).percentage,
4897
+ "%)"
4898
+ ] })
4899
+ ] }) }),
4900
+ (task.checklist || []).length === 0 ? /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "\u2014" }) : (task.checklist || []).slice(0, 12).map((c, i) => {
4901
+ const isDone = c.status === "done";
4902
+ return /* @__PURE__ */ jsxs5(Text6, { color: isDone ? "dim" : "white", children: [
4903
+ /* @__PURE__ */ jsxs5(Text6, { color: isDone ? "green" : "dim", children: [
4904
+ getCheckbox(c.status || "pending"),
4905
+ " "
4906
+ ] }),
4907
+ c.label || c.id || "item"
4908
+ ] }, c.id || i);
4909
+ })
4910
+ ] }),
4911
+ /* @__PURE__ */ jsx7(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }),
4912
+ /* @__PURE__ */ jsxs5(Box6, { marginTop: 1, paddingX: 1, flexDirection: "column", children: [
4913
+ /* @__PURE__ */ jsx7(Text6, { bold: true, color: "white", children: "\u{1F916} AGENTS" }),
4914
+ /* @__PURE__ */ jsx7(Box6, { marginTop: 1, flexDirection: "column", children: !task.agents ? /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "\u2014" }) : Object.entries(task.agents).map(([agent, info]) => /* @__PURE__ */ jsxs5(Text6, { children: [
4915
+ /* @__PURE__ */ jsxs5(Text6, { color: "dim", children: [
4916
+ "- ",
4917
+ agent,
4918
+ ": "
4919
+ ] }),
4920
+ info?.status === "complete" && /* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u2713" }),
4921
+ info?.status === "in_progress" && /* @__PURE__ */ jsx7(Text6, { color: "yellow", children: "\u27F3" }),
4922
+ info?.status === "pending" && /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "\u25CB" }),
4923
+ info?.blocked && /* @__PURE__ */ jsx7(Text6, { color: "red", children: "\u2715" }),
4924
+ /* @__PURE__ */ jsxs5(Text6, { color: info?.status === "complete" ? "dim" : "white", children: [
4925
+ " ",
4926
+ info?.status || "\u2014"
4927
+ ] }),
4928
+ info?.artifact && /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
4929
+ " (",
4930
+ info.artifact,
4931
+ ")"
4932
+ ] })
4933
+ ] }, agent)) })
4934
+ ] })
4935
+ ] });
4936
+ };
4937
+ }
4938
+ });
4939
+
4877
4940
  // src/mcp/ui/TasksView.tsx
4878
4941
  import { useEffect as useEffect4, useMemo as useMemo4, useState as useState4 } from "react";
4879
- import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
4880
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
4942
+ import { Box as Box7, Text as Text7, useInput as useInput3 } from "ink";
4943
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
4881
4944
  function nextStatus(current) {
4882
4945
  const idx = STATUS_CYCLE.indexOf(current || "");
4883
4946
  if (idx === -1) return STATUS_CYCLE[0];
4884
4947
  return STATUS_CYCLE[(idx + 1) % STATUS_CYCLE.length];
4885
4948
  }
4886
- function projectKey2(p) {
4887
- return p.sourcePath ?? p.path;
4888
- }
4889
- function formatProjectLabel2(p) {
4890
- return `${p.name} (${p.source})`;
4891
- }
4892
4949
  var STATUS_CYCLE, TasksView;
4893
4950
  var init_TasksView = __esm({
4894
4951
  "src/mcp/ui/TasksView.tsx"() {
4895
4952
  "use strict";
4896
4953
  init_tasks_fs();
4897
4954
  init_ConfigContext();
4898
- init_ui_helpers();
4955
+ init_project_utils();
4956
+ init_TaskRow();
4957
+ init_TaskDetails();
4899
4958
  STATUS_CYCLE = ["pending", "in_progress", "blocked", "complete"];
4900
4959
  TasksView = ({ projects: allProjects, workspacePath }) => {
4901
4960
  const { driftReports } = useConfig();
@@ -4904,20 +4963,12 @@ var init_TasksView = __esm({
4904
4963
  const [taskCache, setTaskCache] = useState4({});
4905
4964
  const [errorLine, setErrorLine] = useState4(null);
4906
4965
  const sortedProjects = useMemo4(() => {
4907
- return [...allProjects].sort((a, b) => {
4908
- const aIsCurrent = a.path === workspacePath;
4909
- const bIsCurrent = b.path === workspacePath;
4910
- if (aIsCurrent && !bIsCurrent) return -1;
4911
- if (!aIsCurrent && bIsCurrent) return 1;
4912
- const byName = a.name.localeCompare(b.name);
4913
- if (byName !== 0) return byName;
4914
- return projectKey2(a).localeCompare(projectKey2(b));
4915
- });
4966
+ return sortProjects(allProjects, workspacePath);
4916
4967
  }, [allProjects, workspacePath]);
4917
4968
  useEffect4(() => {
4918
4969
  const current = sortedProjects.find((p) => p.path === workspacePath);
4919
4970
  if (current) {
4920
- const k = projectKey2(current);
4971
+ const k = projectKey(current);
4921
4972
  setExpanded((prev) => {
4922
4973
  const next = new Set(prev);
4923
4974
  if (!next.has(k)) {
@@ -4930,12 +4981,12 @@ var init_TasksView = __esm({
4930
4981
  }, [sortedProjects, workspacePath]);
4931
4982
  const refreshTasksForProject = (project) => {
4932
4983
  const res = listProjectTasks(project);
4933
- setTaskCache((prev) => ({ ...prev, [projectKey2(project)]: res.tasks }));
4984
+ setTaskCache((prev) => ({ ...prev, [projectKey(project)]: res.tasks }));
4934
4985
  };
4935
4986
  const refreshAllTasks = () => {
4936
4987
  const next = {};
4937
4988
  for (const p of sortedProjects) {
4938
- next[projectKey2(p)] = listProjectTasks(p).tasks;
4989
+ next[projectKey(p)] = listProjectTasks(p).tasks;
4939
4990
  }
4940
4991
  setTaskCache(next);
4941
4992
  };
@@ -4943,7 +4994,7 @@ var init_TasksView = __esm({
4943
4994
  const rows = [];
4944
4995
  for (const p of sortedProjects) {
4945
4996
  rows.push({ kind: "project", project: p });
4946
- const k = projectKey2(p);
4997
+ const k = projectKey(p);
4947
4998
  if (!expanded.has(k)) continue;
4948
4999
  const tasks = taskCache[k] || [];
4949
5000
  for (const t of tasks) {
@@ -4972,7 +5023,7 @@ var init_TasksView = __esm({
4972
5023
  if (key.return) {
4973
5024
  const row = flattenedRows[selectedIndex];
4974
5025
  if (row?.kind === "project") {
4975
- const k = projectKey2(row.project);
5026
+ const k = projectKey(row.project);
4976
5027
  const next = new Set(expanded);
4977
5028
  if (next.has(k)) {
4978
5029
  next.delete(k);
@@ -4995,7 +5046,7 @@ var init_TasksView = __esm({
4995
5046
  return;
4996
5047
  }
4997
5048
  setTaskCache((prev) => {
4998
- const k = projectKey2(row.project);
5049
+ const k = projectKey(row.project);
4999
5050
  const tasks = prev[k] || [];
5000
5051
  const updated = tasks.map((t) => t.task_slug === row.task.task_slug ? result.meta : t);
5001
5052
  return { ...prev, [k]: updated };
@@ -5012,147 +5063,43 @@ var init_TasksView = __esm({
5012
5063
  }, [flattenedRows]);
5013
5064
  const selectedRow = flattenedRows[selectedIndex];
5014
5065
  const selectedTask = selectedRow?.kind === "task" && selectedRow.task.task_slug !== "__none__" ? selectedRow.task : null;
5015
- return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "white", flexGrow: 1, children: [
5016
- /* @__PURE__ */ jsxs4(Box5, { justifyContent: "space-between", children: [
5017
- /* @__PURE__ */ jsxs4(Box5, { children: [
5018
- /* @__PURE__ */ jsx6(Text5, { bold: true, color: "cyan", children: "\u2699 Tasks" }),
5019
- /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: " \u2022 " }),
5020
- /* @__PURE__ */ jsxs4(Text5, { children: [
5066
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "white", flexGrow: 1, children: [
5067
+ /* @__PURE__ */ jsxs6(Box7, { justifyContent: "space-between", children: [
5068
+ /* @__PURE__ */ jsxs6(Box7, { children: [
5069
+ /* @__PURE__ */ jsx8(Text7, { bold: true, color: "cyan", children: "\u2699 Tasks" }),
5070
+ /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: " \u2022 " }),
5071
+ /* @__PURE__ */ jsxs6(Text7, { children: [
5021
5072
  sortedProjects.length,
5022
5073
  " projects"
5023
5074
  ] }),
5024
- /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: " \u2022 " }),
5025
- /* @__PURE__ */ jsxs4(Text5, { children: [
5075
+ /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: " \u2022 " }),
5076
+ /* @__PURE__ */ jsxs6(Text7, { children: [
5026
5077
  Object.values(taskCache).flat().length,
5027
5078
  " tasks"
5028
5079
  ] })
5029
5080
  ] }),
5030
- /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "\u2191/\u2193:Nav Enter:Expand s:Status R:Refresh" })
5081
+ /* @__PURE__ */ jsx8(Text7, { color: "dim", children: "\u2191/\u2193:Nav Enter:Expand s:Status R:Refresh" })
5031
5082
  ] }),
5032
- errorLine && /* @__PURE__ */ jsx6(Box5, { marginTop: 0, children: /* @__PURE__ */ jsx6(Text5, { color: "red", children: errorLine }) }),
5033
- /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "row", flexGrow: 1, children: [
5034
- /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", width: "55%", children: [
5035
- flattenedRows.length === 0 ? /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "No projects detected." }) : flattenedRows.map((row, idx) => {
5036
- const isSel = idx === selectedIndex;
5037
- if (row.kind === "project") {
5038
- const k = projectKey2(row.project);
5039
- const isOpen = expanded.has(k);
5040
- const count = (taskCache[k] || []).length;
5041
- const drift = driftReports[row.project.path];
5042
- return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsxs4(Box5, { children: [
5043
- /* @__PURE__ */ jsx6(Text5, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
5044
- /* @__PURE__ */ jsxs4(Text5, { color: isSel ? "cyan" : "white", children: [
5045
- getFolderIcon(isOpen),
5046
- " ",
5047
- formatProjectLabel2(row.project)
5048
- ] }),
5049
- drift?.hasDrift && /* @__PURE__ */ jsx6(Text5, { color: "magenta", children: " \u26A0" }),
5050
- /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
5051
- " ",
5052
- count > 0 ? `(${count})` : ""
5053
- ] })
5054
- ] }) }, `p:${k}`);
5055
- }
5056
- const taskLabel = row.task.title || row.task.task_slug;
5057
- const status = row.task.status || "";
5058
- return /* @__PURE__ */ jsxs4(Box5, { children: [
5059
- /* @__PURE__ */ jsx6(Text5, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
5060
- /* @__PURE__ */ jsx6(Text5, { color: "dim", children: " - " }),
5061
- /* @__PURE__ */ jsx6(Text5, { color: isSel ? "cyan" : "white", children: taskLabel }),
5062
- row.task.task_slug !== "__none__" && /* @__PURE__ */ jsx6(Text5, { backgroundColor: getStatusColor(status), color: "black", children: ` ${getStatusIcon(status)} ${status.toUpperCase().replace("_", " ")} ` })
5063
- ] }, `t:${projectKey2(row.project)}:${row.task.task_slug}`);
5083
+ errorLine && /* @__PURE__ */ jsx8(Box7, { marginTop: 0, children: /* @__PURE__ */ jsx8(Text7, { color: "red", children: errorLine }) }),
5084
+ /* @__PURE__ */ jsxs6(Box7, { marginTop: 1, flexDirection: "row", flexGrow: 1, children: [
5085
+ /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", width: "55%", children: [
5086
+ flattenedRows.length === 0 ? /* @__PURE__ */ jsx8(Text7, { color: "dim", children: "No projects detected." }) : flattenedRows.map((row, idx) => {
5087
+ const k = projectKey(row.project);
5088
+ return /* @__PURE__ */ jsx8(
5089
+ TaskRow,
5090
+ {
5091
+ row,
5092
+ isSelected: idx === selectedIndex,
5093
+ isExpanded: expanded.has(k),
5094
+ taskCount: (taskCache[k] || []).length,
5095
+ hasDrift: !!driftReports[row.project.path]?.hasDrift
5096
+ },
5097
+ row.kind === "project" ? `p:${k}` : `t:${k}:${row.task.task_slug}`
5098
+ );
5064
5099
  }),
5065
- /* @__PURE__ */ jsx6(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text5, { color: "gray", children: "\u25B2/\u25BC navigate \u2022 Enter expand/collapse \u2022 s cycle status \u2022 R refresh" }) })
5100
+ /* @__PURE__ */ jsx8(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { color: "gray", children: "\u25B2/\u25BC navigate \u2022 Enter expand/collapse \u2022 s cycle status \u2022 R refresh" }) })
5066
5101
  ] }),
5067
- /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", width: "45%", paddingLeft: 2, children: !selectedTask ? /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", justifyContent: "center", alignItems: "center", gap: 1, flexGrow: 1, children: [
5068
- /* @__PURE__ */ jsx6(Text5, { bold: true, color: "dim", children: "\u2500 No Task Selected \u2500" }),
5069
- /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "Use \u2191/\u2193 to navigate, Enter to expand projects" }),
5070
- /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "Press 's' to cycle task status" })
5071
- ] }) : /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
5072
- /* @__PURE__ */ jsxs4(Box5, { marginBottom: 1, flexDirection: "column", children: [
5073
- /* @__PURE__ */ jsx6(Text5, { bold: true, color: "cyan", children: selectedTask.title || selectedTask.task_slug }),
5074
- selectedTask.summary && /* @__PURE__ */ jsx6(Text5, { color: "white", children: selectedTask.summary })
5075
- ] }),
5076
- /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
5077
- /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, paddingX: 1, flexDirection: "column", children: [
5078
- /* @__PURE__ */ jsx6(Text5, { bold: true, color: "white", children: "\u{1F4CB} STATUS" }),
5079
- /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, children: [
5080
- /* @__PURE__ */ jsxs4(Text5, { children: [
5081
- /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "Status: " }),
5082
- " ",
5083
- /* @__PURE__ */ jsx6(Text5, { color: getStatusColor(selectedTask.status || ""), children: selectedTask.status || "unknown" })
5084
- ] }),
5085
- /* @__PURE__ */ jsxs4(Text5, { children: [
5086
- /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "Updated:" }),
5087
- " ",
5088
- /* @__PURE__ */ jsx6(Text5, { children: selectedTask.updated_at || "\u2014" })
5089
- ] }),
5090
- /* @__PURE__ */ jsxs4(Text5, { children: [
5091
- /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "Tags: " }),
5092
- " ",
5093
- " ",
5094
- (() => {
5095
- const tags = selectedTask.tags || [];
5096
- return tags.length > 0 ? tags.map((tag, i) => /* @__PURE__ */ jsxs4(Text5, { children: [
5097
- /* @__PURE__ */ jsx6(Text5, { color: "cyan", children: tag }),
5098
- i < tags.length - 1 && /* @__PURE__ */ jsx6(Text5, { color: "dim", children: ", " })
5099
- ] }, tag)) : /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "\u2014" });
5100
- })()
5101
- ] })
5102
- ] })
5103
- ] }),
5104
- /* @__PURE__ */ jsx6(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }),
5105
- /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, paddingX: 1, flexDirection: "column", children: [
5106
- /* @__PURE__ */ jsx6(Text5, { bold: true, color: "white", children: "\u{1F4CB} CHECKLIST" }),
5107
- selectedTask.checklist && selectedTask.checklist.length > 0 && /* @__PURE__ */ jsx6(Box5, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsxs4(Box5, { marginBottom: 1, children: [
5108
- /* @__PURE__ */ jsx6(Text5, { backgroundColor: "white", children: getProgressBar(getChecklistProgress(selectedTask.checklist).percentage) }),
5109
- /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
5110
- " ",
5111
- " ",
5112
- getChecklistProgress(selectedTask.checklist).completed,
5113
- "/",
5114
- getChecklistProgress(selectedTask.checklist).total,
5115
- " (",
5116
- getChecklistProgress(selectedTask.checklist).percentage,
5117
- "%)"
5118
- ] })
5119
- ] }) }),
5120
- (selectedTask.checklist || []).length === 0 ? /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "\u2014" }) : (selectedTask.checklist || []).slice(0, 12).map((c, i) => {
5121
- const isDone = c.status === "done";
5122
- return /* @__PURE__ */ jsxs4(Text5, { color: isDone ? "dim" : "white", children: [
5123
- /* @__PURE__ */ jsxs4(Text5, { color: isDone ? "green" : "dim", children: [
5124
- getCheckbox(c.status || "pending"),
5125
- " "
5126
- ] }),
5127
- c.label || c.id || "item"
5128
- ] }, c.id || i);
5129
- })
5130
- ] }),
5131
- /* @__PURE__ */ jsx6(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }),
5132
- /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, paddingX: 1, flexDirection: "column", children: [
5133
- /* @__PURE__ */ jsx6(Text5, { bold: true, color: "white", children: "\u{1F916} AGENTS" }),
5134
- /* @__PURE__ */ jsx6(Box5, { marginTop: 1, flexDirection: "column", children: !selectedTask.agents ? /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "\u2014" }) : Object.entries(selectedTask.agents).map(([agent, info]) => /* @__PURE__ */ jsxs4(Text5, { children: [
5135
- /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
5136
- "- ",
5137
- agent,
5138
- ": "
5139
- ] }),
5140
- info?.status === "complete" && /* @__PURE__ */ jsx6(Text5, { color: "green", children: "\u2713" }),
5141
- info?.status === "in_progress" && /* @__PURE__ */ jsx6(Text5, { color: "yellow", children: "\u27F3" }),
5142
- info?.status === "pending" && /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "\u25CB" }),
5143
- info?.blocked && /* @__PURE__ */ jsx6(Text5, { color: "red", children: "\u2715" }),
5144
- /* @__PURE__ */ jsxs4(Text5, { color: info?.status === "complete" ? "dim" : "white", children: [
5145
- " ",
5146
- info?.status || "\u2014"
5147
- ] }),
5148
- info?.artifact && /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
5149
- " (",
5150
- info.artifact,
5151
- ")"
5152
- ] })
5153
- ] }, agent)) })
5154
- ] })
5155
- ] }) })
5102
+ /* @__PURE__ */ jsx8(Box7, { flexDirection: "column", width: "45%", paddingLeft: 2, children: /* @__PURE__ */ jsx8(TaskDetails, { task: selectedTask }) })
5156
5103
  ] })
5157
5104
  ] });
5158
5105
  };
@@ -5161,8 +5108,8 @@ var init_TasksView = __esm({
5161
5108
 
5162
5109
  // src/mcp/ui/LogViewer.tsx
5163
5110
  import "react";
5164
- import { Box as Box6, Text as Text6 } from "ink";
5165
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
5111
+ import { Box as Box8, Text as Text8 } from "ink";
5112
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
5166
5113
  var LogViewer;
5167
5114
  var init_LogViewer = __esm({
5168
5115
  "src/mcp/ui/LogViewer.tsx"() {
@@ -5172,16 +5119,16 @@ var init_LogViewer = __esm({
5172
5119
  const emptyLines = Math.max(0, height - visibleLogs.length);
5173
5120
  const padding = Array(emptyLines).fill("");
5174
5121
  const formatLog = (log) => {
5175
- if (log.includes("[RAG]")) return /* @__PURE__ */ jsx7(Text6, { color: "cyan", children: log });
5176
- if (log.includes("[ERROR]")) return /* @__PURE__ */ jsx7(Text6, { color: "red", children: log });
5177
- if (log.includes("[WARN]")) return /* @__PURE__ */ jsx7(Text6, { color: "yellow", children: log });
5178
- if (log.includes("[INFO]")) return /* @__PURE__ */ jsx7(Text6, { color: "green", children: log });
5179
- if (log.includes("Success")) return /* @__PURE__ */ jsx7(Text6, { color: "green", children: log });
5180
- return /* @__PURE__ */ jsx7(Text6, { children: log });
5122
+ if (log.includes("[RAG]")) return /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: log });
5123
+ if (log.includes("[ERROR]")) return /* @__PURE__ */ jsx9(Text8, { color: "red", children: log });
5124
+ if (log.includes("[WARN]")) return /* @__PURE__ */ jsx9(Text8, { color: "yellow", children: log });
5125
+ if (log.includes("[INFO]")) return /* @__PURE__ */ jsx9(Text8, { color: "green", children: log });
5126
+ if (log.includes("Success")) return /* @__PURE__ */ jsx9(Text8, { color: "green", children: log });
5127
+ return /* @__PURE__ */ jsx9(Text8, { children: log });
5181
5128
  };
5182
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "white", paddingX: 1, height: height + 2, flexGrow: 1, children: [
5183
- padding.map((_, i) => /* @__PURE__ */ jsx7(Text6, { children: " " }, `empty-${i}`)),
5184
- visibleLogs.map((log, i) => /* @__PURE__ */ jsx7(Box6, { children: formatLog(log) }, `log-${i}`))
5129
+ return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "white", paddingX: 1, height: height + 2, flexGrow: 1, children: [
5130
+ padding.map((_, i) => /* @__PURE__ */ jsx9(Text8, { children: " " }, `empty-${i}`)),
5131
+ visibleLogs.map((log, i) => /* @__PURE__ */ jsx9(Box8, { children: formatLog(log) }, `log-${i}`))
5185
5132
  ] });
5186
5133
  };
5187
5134
  }
@@ -5189,28 +5136,28 @@ var init_LogViewer = __esm({
5189
5136
 
5190
5137
  // src/mcp/ui/StatusBoard.tsx
5191
5138
  import "react";
5192
- import { Box as Box7, Text as Text7 } from "ink";
5193
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
5139
+ import { Box as Box9, Text as Text9 } from "ink";
5140
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
5194
5141
  var StatusBoard;
5195
5142
  var init_StatusBoard = __esm({
5196
5143
  "src/mcp/ui/StatusBoard.tsx"() {
5197
5144
  "use strict";
5198
5145
  StatusBoard = ({ exposedLabel, port, pid, running, hasDrift }) => {
5199
- return /* @__PURE__ */ jsx8(Box7, { borderStyle: "single", borderColor: "white", paddingX: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs6(Text7, { children: [
5200
- running ? /* @__PURE__ */ jsx8(Text7, { color: "green", children: "\u25CF RUNNING" }) : /* @__PURE__ */ jsx8(Text7, { color: "red", children: "\u25CF STOPPED" }),
5146
+ return /* @__PURE__ */ jsx10(Box9, { borderStyle: "single", borderColor: "white", paddingX: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs8(Text9, { children: [
5147
+ running ? /* @__PURE__ */ jsx10(Text9, { color: "green", children: "\u25CF RUNNING" }) : /* @__PURE__ */ jsx10(Text9, { color: "red", children: "\u25CF STOPPED" }),
5201
5148
  " ",
5202
5149
  "\u2502",
5203
5150
  " \u{1F4CB} ",
5204
- /* @__PURE__ */ jsx8(Text7, { color: "yellow", children: exposedLabel }),
5151
+ /* @__PURE__ */ jsx10(Text9, { color: "yellow", children: exposedLabel }),
5205
5152
  " ",
5206
5153
  "\u2502",
5207
5154
  " Port: ",
5208
- /* @__PURE__ */ jsx8(Text7, { color: "green", children: port }),
5155
+ /* @__PURE__ */ jsx10(Text9, { color: "green", children: port }),
5209
5156
  " ",
5210
5157
  "\u2502",
5211
5158
  " PID: ",
5212
- /* @__PURE__ */ jsx8(Text7, { color: "green", children: pid }),
5213
- hasDrift && /* @__PURE__ */ jsxs6(Text7, { color: "magenta", bold: true, children: [
5159
+ /* @__PURE__ */ jsx10(Text9, { color: "green", children: pid }),
5160
+ hasDrift && /* @__PURE__ */ jsxs8(Text9, { color: "magenta", bold: true, children: [
5214
5161
  " ",
5215
5162
  "\u2502",
5216
5163
  " \u2B06 UPDATE AVAILABLE"
@@ -5222,26 +5169,29 @@ var init_StatusBoard = __esm({
5222
5169
 
5223
5170
  // src/mcp/ui/components/TabBar.tsx
5224
5171
  import "react";
5225
- import { Box as Box8, Text as Text8, useInput as useInput4 } from "ink";
5226
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
5172
+ import { Box as Box10, Text as Text10, useInput as useInput4 } from "ink";
5173
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
5227
5174
  var TabBar;
5228
5175
  var init_TabBar = __esm({
5229
5176
  "src/mcp/ui/components/TabBar.tsx"() {
5230
5177
  "use strict";
5231
5178
  TabBar = ({ tabs, activeTab, onChange }) => {
5232
5179
  useInput4((input, key) => {
5180
+ if (tabs.length === 0) return;
5233
5181
  if (key.leftArrow) {
5234
5182
  const index = tabs.findIndex((t) => t.id === activeTab);
5235
5183
  if (index !== -1) {
5236
5184
  const nextIndex = (index - 1 + tabs.length) % tabs.length;
5237
- onChange(tabs[nextIndex]?.id || tabs[0].id);
5185
+ const nextTab = tabs[nextIndex];
5186
+ if (nextTab) onChange(nextTab.id);
5238
5187
  }
5239
5188
  }
5240
5189
  if (key.rightArrow) {
5241
5190
  const index = tabs.findIndex((t) => t.id === activeTab);
5242
5191
  if (index !== -1) {
5243
5192
  const nextIndex = (index + 1) % tabs.length;
5244
- onChange(tabs[nextIndex]?.id || tabs[0].id);
5193
+ const nextTab = tabs[nextIndex];
5194
+ if (nextTab) onChange(nextTab.id);
5245
5195
  }
5246
5196
  }
5247
5197
  const num = parseInt(input);
@@ -5250,11 +5200,11 @@ var init_TabBar = __esm({
5250
5200
  if (tab) onChange(tab.id);
5251
5201
  }
5252
5202
  });
5253
- return /* @__PURE__ */ jsxs7(Box8, { borderStyle: "single", paddingX: 1, borderColor: "gray", children: [
5203
+ return /* @__PURE__ */ jsxs9(Box10, { borderStyle: "single", paddingX: 1, borderColor: "gray", children: [
5254
5204
  tabs.map((tab, index) => {
5255
5205
  const isActive = tab.id === activeTab;
5256
- return /* @__PURE__ */ jsx9(Box8, { marginRight: 2, children: /* @__PURE__ */ jsx9(
5257
- Text8,
5206
+ return /* @__PURE__ */ jsx11(Box10, { marginRight: 2, children: /* @__PURE__ */ jsx11(
5207
+ Text10,
5258
5208
  {
5259
5209
  color: isActive ? "cyan" : "white",
5260
5210
  bold: isActive,
@@ -5263,8 +5213,8 @@ var init_TabBar = __esm({
5263
5213
  }
5264
5214
  ) }, tab.id);
5265
5215
  }),
5266
- /* @__PURE__ */ jsx9(Box8, { flexGrow: 1 }),
5267
- /* @__PURE__ */ jsx9(Text8, { color: "dim", children: "Use \u25C4/\u25BA arrows to navigate" })
5216
+ /* @__PURE__ */ jsx11(Box10, { flexGrow: 1 }),
5217
+ /* @__PURE__ */ jsx11(Text10, { color: "dim", children: "Use \u25C4/\u25BA arrows to navigate" })
5268
5218
  ] });
5269
5219
  };
5270
5220
  }
@@ -5276,9 +5226,9 @@ __export(App_exports, {
5276
5226
  App: () => App
5277
5227
  });
5278
5228
  import { useState as useState5, useEffect as useEffect6, useMemo as useMemo5, useCallback as useCallback3 } from "react";
5279
- import { Box as Box9, useInput as useInput5, useApp } from "ink";
5229
+ import { Box as Box11, useInput as useInput5, useApp } from "ink";
5280
5230
  import fs20 from "fs";
5281
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
5231
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
5282
5232
  var App;
5283
5233
  var init_App = __esm({
5284
5234
  "src/mcp/ui/App.tsx"() {
@@ -5398,10 +5348,10 @@ var init_App = __esm({
5398
5348
  const handleConfigChange = useCallback3(() => {
5399
5349
  refreshData();
5400
5350
  }, [refreshData]);
5401
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", padding: 0, height: termHeight, children: [
5402
- /* @__PURE__ */ jsx10(TabBar, { tabs, activeTab, onChange: setActiveTab }),
5403
- /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexGrow: 1, children: [
5404
- activeTab === "overview" && /* @__PURE__ */ jsx10(
5351
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", padding: 0, height: termHeight, children: [
5352
+ /* @__PURE__ */ jsx12(TabBar, { tabs, activeTab, onChange: setActiveTab }),
5353
+ /* @__PURE__ */ jsxs10(Box11, { marginTop: 1, flexGrow: 1, children: [
5354
+ activeTab === "overview" && /* @__PURE__ */ jsx12(
5405
5355
  Overview,
5406
5356
  {
5407
5357
  serverStatus: serverInfo,
@@ -5413,11 +5363,11 @@ var init_App = __esm({
5413
5363
  logs
5414
5364
  }
5415
5365
  ),
5416
- activeTab === "logs" && /* @__PURE__ */ jsx10(LogViewer, { logs, height: contentHeight }),
5417
- activeTab === "tasks" && /* @__PURE__ */ jsx10(TasksView, { projects, workspacePath }),
5418
- activeTab === "projects" && /* @__PURE__ */ jsx10(ProjectsView, { config, projects, onConfigChange: handleConfigChange, workspacePath })
5366
+ activeTab === "logs" && /* @__PURE__ */ jsx12(LogViewer, { logs, height: contentHeight }),
5367
+ activeTab === "tasks" && /* @__PURE__ */ jsx12(TasksView, { projects, workspacePath }),
5368
+ activeTab === "projects" && /* @__PURE__ */ jsx12(ProjectsView, { config, projects, onConfigChange: handleConfigChange, workspacePath })
5419
5369
  ] }),
5420
- /* @__PURE__ */ jsx10(Box9, { marginTop: 0, children: /* @__PURE__ */ jsx10(
5370
+ /* @__PURE__ */ jsx12(Box11, { marginTop: 0, children: /* @__PURE__ */ jsx12(
5421
5371
  StatusBoard,
5422
5372
  {
5423
5373
  exposedLabel: `${exposedProjects.length} / ${projects.length} projects`,
@@ -5435,7 +5385,7 @@ var init_App = __esm({
5435
5385
  // src/mcp/commands/start.ts
5436
5386
  import { confirm as confirm3, isCancel as isCancel5, text } from "@clack/prompts";
5437
5387
  async function handleStartServer() {
5438
- const React11 = await import("react");
5388
+ const React13 = await import("react");
5439
5389
  const { render } = await import("ink");
5440
5390
  const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
5441
5391
  const { ConfigProvider: ConfigProvider2 } = await Promise.resolve().then(() => (init_ConfigContext(), ConfigContext_exports));
@@ -5478,10 +5428,10 @@ async function handleStartServer() {
5478
5428
  }
5479
5429
  process.stdin.resume();
5480
5430
  const app = render(
5481
- React11.createElement(
5431
+ React13.createElement(
5482
5432
  ConfigProvider2,
5483
5433
  null,
5484
- React11.createElement(App2, {
5434
+ React13.createElement(App2, {
5485
5435
  initialPort,
5486
5436
  onExit: () => {
5487
5437
  }