rrce-workflow 0.2.93 → 0.2.94

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 (2) hide show
  1. package/dist/index.js +409 -119
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3081,8 +3081,8 @@ Hidden projects: ${projects.length - exposedCount}`,
3081
3081
  }
3082
3082
  async function handleConfigureGlobalPath() {
3083
3083
  const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
3084
- const fs22 = await import("fs");
3085
- const path20 = await import("path");
3084
+ const fs23 = await import("fs");
3085
+ const path21 = await import("path");
3086
3086
  note3(
3087
3087
  `MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
3088
3088
  and coordinate across projects.
@@ -3096,8 +3096,8 @@ locally in each project. MCP needs a central location.`,
3096
3096
  return false;
3097
3097
  }
3098
3098
  try {
3099
- if (!fs22.existsSync(resolvedPath)) {
3100
- fs22.mkdirSync(resolvedPath, { recursive: true });
3099
+ if (!fs23.existsSync(resolvedPath)) {
3100
+ fs23.mkdirSync(resolvedPath, { recursive: true });
3101
3101
  }
3102
3102
  const config = loadMCPConfig();
3103
3103
  saveMCPConfig(config);
@@ -3105,7 +3105,7 @@ locally in each project. MCP needs a central location.`,
3105
3105
  `${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
3106
3106
 
3107
3107
  MCP config will be stored at:
3108
- ${path20.join(resolvedPath, "mcp.yaml")}`,
3108
+ ${path21.join(resolvedPath, "mcp.yaml")}`,
3109
3109
  "Configuration Saved"
3110
3110
  );
3111
3111
  return true;
@@ -3345,46 +3345,234 @@ var init_SimpleSelect = __esm({
3345
3345
  }
3346
3346
  });
3347
3347
 
3348
+ // src/mcp/ui/lib/tasks-fs.ts
3349
+ import * as fs16 from "fs";
3350
+ import * as path18 from "path";
3351
+ function detectStorageModeFromConfig(workspaceRoot) {
3352
+ const configPath = getConfigPath(workspaceRoot);
3353
+ try {
3354
+ const rrceHome = getEffectiveGlobalBase();
3355
+ if (configPath.startsWith(rrceHome)) {
3356
+ return "global";
3357
+ }
3358
+ if (fs16.existsSync(configPath)) {
3359
+ const content = fs16.readFileSync(configPath, "utf-8");
3360
+ if (content.includes("mode: workspace")) return "workspace";
3361
+ if (content.includes("mode: global")) return "global";
3362
+ }
3363
+ } catch {
3364
+ }
3365
+ return "global";
3366
+ }
3367
+ function getEffectiveGlobalBase() {
3368
+ const dummy = resolveDataPath("global", "__rrce_dummy__", "");
3369
+ return path18.dirname(path18.dirname(dummy));
3370
+ }
3371
+ function getProjectRRCEData(project) {
3372
+ const workspaceRoot = project.sourcePath || project.path;
3373
+ const mode = detectStorageModeFromConfig(workspaceRoot);
3374
+ return resolveDataPath(mode, project.name, workspaceRoot);
3375
+ }
3376
+ function listProjectTasks(project) {
3377
+ const rrceData = getProjectRRCEData(project);
3378
+ const tasksPath = path18.join(rrceData, "tasks");
3379
+ if (!fs16.existsSync(tasksPath)) {
3380
+ return { projectName: project.name, tasksPath, tasks: [] };
3381
+ }
3382
+ const tasks = [];
3383
+ try {
3384
+ const entries = fs16.readdirSync(tasksPath, { withFileTypes: true });
3385
+ for (const entry of entries) {
3386
+ if (!entry.isDirectory()) continue;
3387
+ const metaPath = path18.join(tasksPath, entry.name, "meta.json");
3388
+ if (!fs16.existsSync(metaPath)) continue;
3389
+ try {
3390
+ const raw = fs16.readFileSync(metaPath, "utf-8");
3391
+ const meta = JSON.parse(raw);
3392
+ if (!meta.task_slug) meta.task_slug = entry.name;
3393
+ tasks.push(meta);
3394
+ } catch {
3395
+ }
3396
+ }
3397
+ } catch {
3398
+ }
3399
+ tasks.sort((a, b) => {
3400
+ const aTime = Date.parse(a.updated_at || a.created_at || "") || 0;
3401
+ const bTime = Date.parse(b.updated_at || b.created_at || "") || 0;
3402
+ if (aTime !== bTime) return bTime - aTime;
3403
+ return String(a.task_slug).localeCompare(String(b.task_slug));
3404
+ });
3405
+ return { projectName: project.name, tasksPath, tasks };
3406
+ }
3407
+ function updateTaskStatus(project, taskSlug, status) {
3408
+ const rrceData = getProjectRRCEData(project);
3409
+ const metaPath = path18.join(rrceData, "tasks", taskSlug, "meta.json");
3410
+ if (!fs16.existsSync(metaPath)) {
3411
+ return { ok: false, error: `meta.json not found for task '${taskSlug}'` };
3412
+ }
3413
+ try {
3414
+ const meta = JSON.parse(fs16.readFileSync(metaPath, "utf-8"));
3415
+ const next = {
3416
+ ...meta,
3417
+ status,
3418
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
3419
+ };
3420
+ fs16.writeFileSync(metaPath, JSON.stringify(next, null, 2));
3421
+ return { ok: true, meta: next };
3422
+ } catch (e) {
3423
+ return { ok: false, error: String(e) };
3424
+ }
3425
+ }
3426
+ var init_tasks_fs = __esm({
3427
+ "src/mcp/ui/lib/tasks-fs.ts"() {
3428
+ "use strict";
3429
+ init_paths();
3430
+ }
3431
+ });
3432
+
3348
3433
  // src/mcp/ui/ProjectsView.tsx
3349
- import { useState as useState2 } from "react";
3434
+ import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
3350
3435
  import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
3351
3436
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
3352
- var ProjectsView;
3437
+ function nextStatus(current) {
3438
+ const idx = STATUS_CYCLE.indexOf(current || "");
3439
+ if (idx === -1) return STATUS_CYCLE[0];
3440
+ return STATUS_CYCLE[(idx + 1) % STATUS_CYCLE.length];
3441
+ }
3442
+ function projectKey(p) {
3443
+ return p.sourcePath ?? p.path;
3444
+ }
3445
+ function formatProjectLabel(p) {
3446
+ const root = p.sourcePath ?? p.path;
3447
+ return `${p.name} (${p.source})${root ? ` - ${root}` : ""}`;
3448
+ }
3449
+ var STATUS_CYCLE, ProjectsView;
3353
3450
  var init_ProjectsView = __esm({
3354
3451
  "src/mcp/ui/ProjectsView.tsx"() {
3355
3452
  "use strict";
3356
3453
  init_SimpleSelect();
3357
3454
  init_config();
3455
+ init_tasks_fs();
3456
+ STATUS_CYCLE = ["pending", "in_progress", "blocked", "complete"];
3358
3457
  ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange }) => {
3359
3458
  const [config, setConfig] = useState2(initialConfig);
3360
- useInput2((input) => {
3361
- if (input === "a") {
3362
- const newConfig = {
3363
- ...config,
3364
- defaults: {
3365
- ...config.defaults,
3366
- includeNew: !config.defaults.includeNew
3459
+ const [mode, setMode] = useState2("expose");
3460
+ const [expanded, setExpanded] = useState2(() => /* @__PURE__ */ new Set());
3461
+ const [selectedIndex, setSelectedIndex] = useState2(0);
3462
+ const [taskCache, setTaskCache] = useState2({});
3463
+ const [errorLine, setErrorLine] = useState2(null);
3464
+ const sortedProjects = useMemo2(() => {
3465
+ return [...allProjects].sort((a, b) => {
3466
+ const byName = a.name.localeCompare(b.name);
3467
+ if (byName !== 0) return byName;
3468
+ return projectKey(a).localeCompare(projectKey(b));
3469
+ });
3470
+ }, [allProjects]);
3471
+ const refreshTasksForProject = (project) => {
3472
+ const res = listProjectTasks(project);
3473
+ setTaskCache((prev) => ({ ...prev, [projectKey(project)]: res.tasks }));
3474
+ };
3475
+ const refreshAllTasks = () => {
3476
+ const next = {};
3477
+ for (const p of sortedProjects) {
3478
+ next[projectKey(p)] = listProjectTasks(p).tasks;
3479
+ }
3480
+ setTaskCache(next);
3481
+ };
3482
+ useInput2((input, key) => {
3483
+ if (input === "t") {
3484
+ setErrorLine(null);
3485
+ setMode((prev) => prev === "expose" ? "tasks" : "expose");
3486
+ return;
3487
+ }
3488
+ if (mode === "expose") {
3489
+ if (input === "a") {
3490
+ const newConfig = {
3491
+ ...config,
3492
+ defaults: {
3493
+ ...config.defaults,
3494
+ includeNew: !config.defaults.includeNew
3495
+ }
3496
+ };
3497
+ saveMCPConfig(newConfig);
3498
+ setConfig(newConfig);
3499
+ onConfigChange?.();
3500
+ }
3501
+ return;
3502
+ }
3503
+ if (mode === "tasks") {
3504
+ if (input === "R") {
3505
+ setErrorLine(null);
3506
+ refreshAllTasks();
3507
+ return;
3508
+ }
3509
+ if (key.upArrow) {
3510
+ setSelectedIndex((prev) => prev > 0 ? prev - 1 : Math.max(0, flattenedRows.length - 1));
3511
+ return;
3512
+ }
3513
+ if (key.downArrow) {
3514
+ setSelectedIndex((prev) => prev < flattenedRows.length - 1 ? prev + 1 : 0);
3515
+ return;
3516
+ }
3517
+ if (key.return) {
3518
+ const row = flattenedRows[selectedIndex];
3519
+ if (row?.kind === "project") {
3520
+ const k = projectKey(row.project);
3521
+ const next = new Set(expanded);
3522
+ if (next.has(k)) {
3523
+ next.delete(k);
3524
+ } else {
3525
+ next.add(k);
3526
+ refreshTasksForProject(row.project);
3527
+ }
3528
+ setExpanded(next);
3367
3529
  }
3368
- };
3369
- saveMCPConfig(newConfig);
3370
- setConfig(newConfig);
3371
- if (onConfigChange) onConfigChange();
3530
+ return;
3531
+ }
3532
+ if (input === "s") {
3533
+ const row = flattenedRows[selectedIndex];
3534
+ if (row?.kind === "task") {
3535
+ setErrorLine(null);
3536
+ const desired = nextStatus(row.task.status);
3537
+ const result = updateTaskStatus(row.project, row.task.task_slug, desired);
3538
+ if (!result.ok) {
3539
+ setErrorLine(`Failed to update status: ${result.error}`);
3540
+ return;
3541
+ }
3542
+ setTaskCache((prev) => {
3543
+ const k = projectKey(row.project);
3544
+ const tasks = prev[k] || [];
3545
+ const updated = tasks.map((t) => t.task_slug === row.task.task_slug ? result.meta : t);
3546
+ return { ...prev, [k]: updated };
3547
+ });
3548
+ }
3549
+ return;
3550
+ }
3372
3551
  }
3373
3552
  });
3374
- const projectItems = allProjects.map((p) => {
3375
- const projectConfig = config.projects.find(
3376
- (c) => c.path && c.path === p.path || p.source === "global" && c.name === p.name || !c.path && c.name === p.name
3377
- );
3378
- const isExposed = projectConfig ? projectConfig.expose : config.defaults.includeNew;
3379
- return {
3380
- label: p.name + ` (${p.source})` + (p.path ? ` - ${p.path}` : ""),
3381
- value: p.path,
3382
- // Standardized ID: Use root path
3383
- key: p.path,
3384
- exposed: isExposed
3385
- };
3386
- });
3387
- const initialSelected = projectItems.filter((p) => p.exposed).map((p) => p.value);
3553
+ useEffect2(() => {
3554
+ setSelectedIndex((prev) => {
3555
+ if (flattenedRows.length === 0) return 0;
3556
+ return Math.min(prev, flattenedRows.length - 1);
3557
+ });
3558
+ }, [mode, allProjects, expanded, taskCache]);
3559
+ const projectItems = useMemo2(() => {
3560
+ return allProjects.map((p) => {
3561
+ const projectConfig = config.projects.find(
3562
+ (c) => c.path && c.path === p.path || p.source === "global" && c.name === p.name || !c.path && c.name === p.name
3563
+ );
3564
+ const isExposed = projectConfig ? projectConfig.expose : config.defaults.includeNew;
3565
+ return {
3566
+ label: formatProjectLabel(p),
3567
+ value: p.path,
3568
+ key: p.path,
3569
+ exposed: isExposed
3570
+ };
3571
+ });
3572
+ }, [allProjects, config]);
3573
+ const initialSelected = useMemo2(() => {
3574
+ return projectItems.filter((p) => p.exposed).map((p) => p.value);
3575
+ }, [projectItems]);
3388
3576
  const handleSubmit = (selectedIds) => {
3389
3577
  let newConfig = { ...config };
3390
3578
  projectItems.forEach((item) => {
@@ -3404,33 +3592,135 @@ var init_ProjectsView = __esm({
3404
3592
  });
3405
3593
  saveMCPConfig(newConfig);
3406
3594
  setConfig(newConfig);
3407
- if (onConfigChange) onConfigChange();
3595
+ onConfigChange?.();
3408
3596
  };
3597
+ const flattenedRows = useMemo2(() => {
3598
+ const rows = [];
3599
+ for (const p of sortedProjects) {
3600
+ rows.push({ kind: "project", project: p });
3601
+ const k = projectKey(p);
3602
+ if (!expanded.has(k)) continue;
3603
+ const tasks = taskCache[k] || [];
3604
+ for (const t of tasks) {
3605
+ rows.push({ kind: "task", project: p, task: t });
3606
+ }
3607
+ if ((taskCache[k] || []).length === 0) {
3608
+ rows.push({ kind: "task", project: p, task: { task_slug: "__none__", title: "(no tasks)", status: "" } });
3609
+ }
3610
+ }
3611
+ return rows;
3612
+ }, [sortedProjects, expanded, taskCache]);
3613
+ const selectedRow = flattenedRows[selectedIndex];
3614
+ const selectedTask = selectedRow?.kind === "task" && selectedRow.task.task_slug !== "__none__" ? selectedRow.task : null;
3615
+ if (mode === "expose") {
3616
+ return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan", flexGrow: 1, children: [
3617
+ /* @__PURE__ */ jsxs3(Box4, { justifyContent: "space-between", children: [
3618
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: " Projects (Expose Mode) " }),
3619
+ /* @__PURE__ */ jsxs3(Box4, { children: [
3620
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Auto-expose new: " }),
3621
+ /* @__PURE__ */ jsx4(Text4, { color: config.defaults.includeNew ? "green" : "red", children: config.defaults.includeNew ? "ON" : "OFF" }),
3622
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " (Press 'a' to toggle)" })
3623
+ ] })
3624
+ ] }),
3625
+ /* @__PURE__ */ jsx4(Text4, { color: "dim", children: " Space toggles, Enter saves. Press 't' to switch to Tasks Mode." }),
3626
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx4(
3627
+ SimpleSelect,
3628
+ {
3629
+ message: "",
3630
+ items: projectItems,
3631
+ isMulti: true,
3632
+ initialSelected,
3633
+ onSelect: () => {
3634
+ },
3635
+ onSubmit: handleSubmit,
3636
+ onCancel: () => {
3637
+ }
3638
+ },
3639
+ JSON.stringify(initialSelected) + config.defaults.includeNew
3640
+ ) })
3641
+ ] });
3642
+ }
3409
3643
  return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan", flexGrow: 1, children: [
3410
3644
  /* @__PURE__ */ jsxs3(Box4, { justifyContent: "space-between", children: [
3411
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: " Exposed Projects " }),
3412
- /* @__PURE__ */ jsxs3(Box4, { children: [
3413
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Auto-expose new: " }),
3414
- /* @__PURE__ */ jsx4(Text4, { color: config.defaults.includeNew ? "green" : "red", children: config.defaults.includeNew ? "ON" : "OFF" }),
3415
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " (Press 'a' to toggle)" })
3416
- ] })
3645
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: " Projects (Tasks Mode) " }),
3646
+ /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "t:Expose Enter:Expand s:Status R:Refresh" })
3417
3647
  ] }),
3418
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: " Select projects to expose via the MCP server. Use Space to toggle, Enter to save." }),
3419
- /* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx4(
3420
- SimpleSelect,
3421
- {
3422
- message: "",
3423
- items: projectItems,
3424
- isMulti: true,
3425
- initialSelected,
3426
- onSelect: () => {
3427
- },
3428
- onSubmit: handleSubmit,
3429
- onCancel: () => {
3430
- }
3431
- },
3432
- JSON.stringify(initialSelected) + config.defaults.includeNew
3433
- ) })
3648
+ errorLine && /* @__PURE__ */ jsx4(Box4, { marginTop: 0, children: /* @__PURE__ */ jsx4(Text4, { color: "red", children: errorLine }) }),
3649
+ /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "row", flexGrow: 1, children: [
3650
+ /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", width: "55%", children: [
3651
+ flattenedRows.length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "No projects detected." }) : flattenedRows.map((row, idx) => {
3652
+ const isSel = idx === selectedIndex;
3653
+ if (row.kind === "project") {
3654
+ const k = projectKey(row.project);
3655
+ const isOpen = expanded.has(k);
3656
+ const count = (taskCache[k] || []).length;
3657
+ return /* @__PURE__ */ jsxs3(Box4, { children: [
3658
+ /* @__PURE__ */ jsx4(Text4, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
3659
+ /* @__PURE__ */ jsxs3(Text4, { color: isSel ? "cyan" : "white", children: [
3660
+ isOpen ? "\u25BE " : "\u25B8 ",
3661
+ formatProjectLabel(row.project)
3662
+ ] }),
3663
+ /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
3664
+ " ",
3665
+ count > 0 ? ` (tasks: ${count})` : ""
3666
+ ] })
3667
+ ] }, `p:${k}`);
3668
+ }
3669
+ const taskLabel = row.task.title || row.task.task_slug;
3670
+ const status = row.task.status || "";
3671
+ return /* @__PURE__ */ jsxs3(Box4, { children: [
3672
+ /* @__PURE__ */ jsx4(Text4, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
3673
+ /* @__PURE__ */ jsx4(Text4, { color: "dim", children: " - " }),
3674
+ /* @__PURE__ */ jsx4(Text4, { color: isSel ? "cyan" : "white", children: taskLabel }),
3675
+ row.task.task_slug !== "__none__" && /* @__PURE__ */ jsx4(Text4, { color: status === "complete" ? "green" : status === "blocked" ? "red" : "yellow", children: ` [${status}]` })
3676
+ ] }, `t:${projectKey(row.project)}:${row.task.task_slug}`);
3677
+ }),
3678
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "\u25B2/\u25BC navigate \u2022 Enter expand/collapse \u2022 s cycle status \u2022 R refresh \u2022 t expose mode" }) })
3679
+ ] }),
3680
+ /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", width: "45%", paddingLeft: 2, children: !selectedTask ? /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Select a task to view details." }) : /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", children: [
3681
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: selectedTask.title || selectedTask.task_slug }),
3682
+ selectedTask.summary && /* @__PURE__ */ jsx4(Text4, { children: selectedTask.summary }),
3683
+ /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
3684
+ /* @__PURE__ */ jsxs3(Text4, { children: [
3685
+ /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Status: " }),
3686
+ /* @__PURE__ */ jsx4(Text4, { children: selectedTask.status || "unknown" })
3687
+ ] }),
3688
+ /* @__PURE__ */ jsxs3(Text4, { children: [
3689
+ /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Updated: " }),
3690
+ /* @__PURE__ */ jsx4(Text4, { children: selectedTask.updated_at || "\u2014" })
3691
+ ] }),
3692
+ /* @__PURE__ */ jsxs3(Text4, { children: [
3693
+ /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Tags: " }),
3694
+ /* @__PURE__ */ jsx4(Text4, { children: (selectedTask.tags || []).join(", ") || "\u2014" })
3695
+ ] })
3696
+ ] }),
3697
+ /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
3698
+ /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Checklist" }),
3699
+ (selectedTask.checklist || []).length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "\u2014" }) : (selectedTask.checklist || []).slice(0, 12).map((c, i) => /* @__PURE__ */ jsxs3(Text4, { children: [
3700
+ /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "- " }),
3701
+ c.label || c.id || "item",
3702
+ " ",
3703
+ /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
3704
+ "[",
3705
+ c.status || "pending",
3706
+ "]"
3707
+ ] })
3708
+ ] }, c.id || i))
3709
+ ] }),
3710
+ /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
3711
+ /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Agents" }),
3712
+ !selectedTask.agents ? /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "\u2014" }) : Object.entries(selectedTask.agents).map(([agent, info]) => /* @__PURE__ */ jsxs3(Text4, { children: [
3713
+ /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
3714
+ "- ",
3715
+ agent,
3716
+ ": "
3717
+ ] }),
3718
+ info?.status || "\u2014",
3719
+ info?.artifact ? ` (${info.artifact})` : ""
3720
+ ] }, agent))
3721
+ ] })
3722
+ ] }) })
3723
+ ] })
3434
3724
  ] });
3435
3725
  };
3436
3726
  }
@@ -3609,7 +3899,7 @@ var init_StatusBoard = __esm({
3609
3899
  });
3610
3900
 
3611
3901
  // src/mcp/ui/IndexingStatus.tsx
3612
- import { useState as useState4, useEffect as useEffect3 } from "react";
3902
+ import { useState as useState4, useEffect as useEffect4 } from "react";
3613
3903
  import { Box as Box9, Text as Text9 } from "ink";
3614
3904
  import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
3615
3905
  var IndexingStatus;
@@ -3623,7 +3913,7 @@ var init_IndexingStatus = __esm({
3623
3913
  IndexingStatus = ({ projects, config }) => {
3624
3914
  const [stats, setStats] = useState4([]);
3625
3915
  const [loading, setLoading] = useState4(true);
3626
- useEffect3(() => {
3916
+ useEffect4(() => {
3627
3917
  const fetchStats = async () => {
3628
3918
  const newStats = [];
3629
3919
  for (const project of projects) {
@@ -3768,9 +4058,9 @@ var App_exports = {};
3768
4058
  __export(App_exports, {
3769
4059
  App: () => App
3770
4060
  });
3771
- import { useState as useState5, useEffect as useEffect4, useMemo as useMemo2, useCallback } from "react";
4061
+ import { useState as useState5, useEffect as useEffect5, useMemo as useMemo3, useCallback } from "react";
3772
4062
  import { Box as Box11, useInput as useInput4, useApp } from "ink";
3773
- import fs16 from "fs";
4063
+ import fs17 from "fs";
3774
4064
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
3775
4065
  var App;
3776
4066
  var init_App = __esm({
@@ -3806,17 +4096,17 @@ var init_App = __esm({
3806
4096
  setConfig(loadMCPConfig());
3807
4097
  setProjects(scanForProjects());
3808
4098
  }, []);
3809
- const exposedProjects = useMemo2(
4099
+ const exposedProjects = useMemo3(
3810
4100
  () => projects.filter((p) => isProjectExposed(config, p.name, p.path)),
3811
4101
  [projects, config]
3812
4102
  );
3813
- const isRAGEnabled = useMemo2(() => {
4103
+ const isRAGEnabled = useMemo3(() => {
3814
4104
  return exposedProjects.some((p) => {
3815
4105
  const cfg = findProjectConfig(config, { name: p.name, path: p.path });
3816
4106
  return cfg?.semanticSearch?.enabled || p.semanticSearchEnabled;
3817
4107
  });
3818
4108
  }, [exposedProjects, config]);
3819
- const tabs = useMemo2(() => {
4109
+ const tabs = useMemo3(() => {
3820
4110
  const baseTabs = [
3821
4111
  { id: "overview", label: "Overview" },
3822
4112
  { id: "logs", label: "Logs" },
@@ -3836,7 +4126,7 @@ var init_App = __esm({
3836
4126
  installStatus.vscodeGlobal,
3837
4127
  installStatus.vscodeWorkspace
3838
4128
  ].filter(Boolean).length;
3839
- useEffect4(() => {
4129
+ useEffect5(() => {
3840
4130
  const start = async () => {
3841
4131
  const status = getMCPServerStatus();
3842
4132
  if (!status.running) {
@@ -3852,21 +4142,21 @@ var init_App = __esm({
3852
4142
  };
3853
4143
  start();
3854
4144
  }, []);
3855
- useEffect4(() => {
4145
+ useEffect5(() => {
3856
4146
  const logPath = getLogFilePath();
3857
4147
  let lastSize = 0;
3858
- if (fs16.existsSync(logPath)) {
3859
- const stats = fs16.statSync(logPath);
4148
+ if (fs17.existsSync(logPath)) {
4149
+ const stats = fs17.statSync(logPath);
3860
4150
  lastSize = stats.size;
3861
4151
  }
3862
4152
  const interval = setInterval(() => {
3863
- if (fs16.existsSync(logPath)) {
3864
- const stats = fs16.statSync(logPath);
4153
+ if (fs17.existsSync(logPath)) {
4154
+ const stats = fs17.statSync(logPath);
3865
4155
  if (stats.size > lastSize) {
3866
4156
  const buffer = Buffer.alloc(stats.size - lastSize);
3867
- const fd = fs16.openSync(logPath, "r");
3868
- fs16.readSync(fd, buffer, 0, buffer.length, lastSize);
3869
- fs16.closeSync(fd);
4157
+ const fd = fs17.openSync(logPath, "r");
4158
+ fs17.readSync(fd, buffer, 0, buffer.length, lastSize);
4159
+ fs17.closeSync(fd);
3870
4160
  const newContent = buffer.toString("utf-8");
3871
4161
  const newLines = newContent.split("\n").filter((l) => l.trim());
3872
4162
  setLogs((prev) => {
@@ -4077,7 +4367,7 @@ var init_mcp = __esm({
4077
4367
  // src/commands/wizard/index.ts
4078
4368
  import { intro as intro2, select as select5, spinner as spinner7, note as note11, outro as outro7, isCancel as isCancel12 } from "@clack/prompts";
4079
4369
  import pc13 from "picocolors";
4080
- import * as fs21 from "fs";
4370
+ import * as fs22 from "fs";
4081
4371
 
4082
4372
  // src/lib/git.ts
4083
4373
  import { execSync } from "child_process";
@@ -4751,7 +5041,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
4751
5041
  init_paths();
4752
5042
  import { multiselect as multiselect4, spinner as spinner3, note as note7, outro as outro3, cancel as cancel3, isCancel as isCancel8, confirm as confirm6 } from "@clack/prompts";
4753
5043
  import pc9 from "picocolors";
4754
- import * as fs17 from "fs";
5044
+ import * as fs18 from "fs";
4755
5045
  init_detection();
4756
5046
  async function runLinkProjectsFlow(workspacePath, workspaceName) {
4757
5047
  const projects = scanForProjects({
@@ -4790,7 +5080,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
4790
5080
  const s = spinner3();
4791
5081
  s.start("Linking projects");
4792
5082
  const configFilePath = getConfigPath(workspacePath);
4793
- let configContent = fs17.readFileSync(configFilePath, "utf-8");
5083
+ let configContent = fs18.readFileSync(configFilePath, "utf-8");
4794
5084
  if (configContent.includes("linked_projects:")) {
4795
5085
  const lines = configContent.split("\n");
4796
5086
  const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
@@ -4817,7 +5107,7 @@ linked_projects:
4817
5107
  `;
4818
5108
  });
4819
5109
  }
4820
- fs17.writeFileSync(configFilePath, configContent);
5110
+ fs18.writeFileSync(configFilePath, configContent);
4821
5111
  generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
4822
5112
  s.stop("Projects linked");
4823
5113
  const workspaceFile = `${workspaceName}.code-workspace`;
@@ -4852,15 +5142,15 @@ linked_projects:
4852
5142
  init_paths();
4853
5143
  import { confirm as confirm7, spinner as spinner4, note as note8, outro as outro4, cancel as cancel4, isCancel as isCancel9 } from "@clack/prompts";
4854
5144
  import pc10 from "picocolors";
4855
- import * as fs18 from "fs";
4856
- import * as path18 from "path";
5145
+ import * as fs19 from "fs";
5146
+ import * as path19 from "path";
4857
5147
  async function runSyncToGlobalFlow(workspacePath, workspaceName) {
4858
5148
  const localPath = getLocalWorkspacePath(workspacePath);
4859
5149
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
4860
- const globalPath = path18.join(customGlobalPath, "workspaces", workspaceName);
5150
+ const globalPath = path19.join(customGlobalPath, "workspaces", workspaceName);
4861
5151
  const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
4862
5152
  const existingDirs = subdirs.filter(
4863
- (dir) => fs18.existsSync(path18.join(localPath, dir))
5153
+ (dir) => fs19.existsSync(path19.join(localPath, dir))
4864
5154
  );
4865
5155
  if (existingDirs.length === 0) {
4866
5156
  outro4(pc10.yellow("No data found in workspace storage to sync."));
@@ -4886,8 +5176,8 @@ Destination: ${pc10.cyan(globalPath)}`,
4886
5176
  try {
4887
5177
  ensureDir(globalPath);
4888
5178
  for (const dir of existingDirs) {
4889
- const srcDir = path18.join(localPath, dir);
4890
- const destDir = path18.join(globalPath, dir);
5179
+ const srcDir = path19.join(localPath, dir);
5180
+ const destDir = path19.join(globalPath, dir);
4891
5181
  ensureDir(destDir);
4892
5182
  copyDirRecursive(srcDir, destDir);
4893
5183
  }
@@ -4914,8 +5204,8 @@ init_paths();
4914
5204
  init_prompts();
4915
5205
  import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
4916
5206
  import pc11 from "picocolors";
4917
- import * as fs19 from "fs";
4918
- import * as path19 from "path";
5207
+ import * as fs20 from "fs";
5208
+ import * as path20 from "path";
4919
5209
  import { stringify as stringify3 } from "yaml";
4920
5210
  init_install();
4921
5211
  async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
@@ -4935,8 +5225,8 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
4935
5225
  ];
4936
5226
  const configFilePath = getConfigPath(workspacePath);
4937
5227
  const ideTargets = [];
4938
- if (fs19.existsSync(configFilePath)) {
4939
- const configContent = fs19.readFileSync(configFilePath, "utf-8");
5228
+ if (fs20.existsSync(configFilePath)) {
5229
+ const configContent = fs20.readFileSync(configFilePath, "utf-8");
4940
5230
  if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
4941
5231
  if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
4942
5232
  if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
@@ -4962,17 +5252,17 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
4962
5252
  }
4963
5253
  s.start("Updating from package");
4964
5254
  for (const dataPath of dataPaths) {
4965
- copyDirToAllStoragePaths(path19.join(agentCoreDir, "templates"), "templates", [dataPath]);
4966
- copyDirToAllStoragePaths(path19.join(agentCoreDir, "prompts"), "prompts", [dataPath]);
4967
- copyDirToAllStoragePaths(path19.join(agentCoreDir, "docs"), "docs", [dataPath]);
5255
+ copyDirToAllStoragePaths(path20.join(agentCoreDir, "templates"), "templates", [dataPath]);
5256
+ copyDirToAllStoragePaths(path20.join(agentCoreDir, "prompts"), "prompts", [dataPath]);
5257
+ copyDirToAllStoragePaths(path20.join(agentCoreDir, "docs"), "docs", [dataPath]);
4968
5258
  }
4969
5259
  const rrceHome = customGlobalPath || getDefaultRRCEHome2();
4970
- ensureDir(path19.join(rrceHome, "templates"));
4971
- ensureDir(path19.join(rrceHome, "docs"));
4972
- copyDirRecursive(path19.join(agentCoreDir, "templates"), path19.join(rrceHome, "templates"));
4973
- copyDirRecursive(path19.join(agentCoreDir, "docs"), path19.join(rrceHome, "docs"));
4974
- if (fs19.existsSync(configFilePath)) {
4975
- const configContent = fs19.readFileSync(configFilePath, "utf-8");
5260
+ ensureDir(path20.join(rrceHome, "templates"));
5261
+ ensureDir(path20.join(rrceHome, "docs"));
5262
+ copyDirRecursive(path20.join(agentCoreDir, "templates"), path20.join(rrceHome, "templates"));
5263
+ copyDirRecursive(path20.join(agentCoreDir, "docs"), path20.join(rrceHome, "docs"));
5264
+ if (fs20.existsSync(configFilePath)) {
5265
+ const configContent = fs20.readFileSync(configFilePath, "utf-8");
4976
5266
  if (configContent.includes("copilot: true")) {
4977
5267
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
4978
5268
  ensureDir(copilotPath);
@@ -5019,14 +5309,14 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
5019
5309
  function updateOpenCodeAgents(prompts, mode, primaryDataPath) {
5020
5310
  if (mode === "global") {
5021
5311
  try {
5022
- const promptsDir = path19.join(path19.dirname(OPENCODE_CONFIG), "prompts");
5312
+ const promptsDir = path20.join(path20.dirname(OPENCODE_CONFIG), "prompts");
5023
5313
  ensureDir(promptsDir);
5024
5314
  let opencodeConfig = { $schema: "https://opencode.ai/config.json" };
5025
- if (fs19.existsSync(OPENCODE_CONFIG)) {
5026
- opencodeConfig = JSON.parse(fs19.readFileSync(OPENCODE_CONFIG, "utf-8"));
5315
+ if (fs20.existsSync(OPENCODE_CONFIG)) {
5316
+ opencodeConfig = JSON.parse(fs20.readFileSync(OPENCODE_CONFIG, "utf-8"));
5027
5317
  }
5028
5318
  if (!opencodeConfig.agent) opencodeConfig.agent = {};
5029
- const currentAgentNames = prompts.map((p) => path19.basename(p.filePath, ".md"));
5319
+ const currentAgentNames = prompts.map((p) => path20.basename(p.filePath, ".md"));
5030
5320
  const existingAgentNames = Object.keys(opencodeConfig.agent);
5031
5321
  const rrceAgentPrefixes = ["init", "research", "planning", "executor", "doctor", "documentation", "sync"];
5032
5322
  for (const existingName of existingAgentNames) {
@@ -5034,30 +5324,30 @@ function updateOpenCodeAgents(prompts, mode, primaryDataPath) {
5034
5324
  const stillExists = currentAgentNames.includes(existingName);
5035
5325
  if (isRrceAgent && !stillExists) {
5036
5326
  delete opencodeConfig.agent[existingName];
5037
- const oldPromptFile = path19.join(promptsDir, `rrce-${existingName}.md`);
5038
- if (fs19.existsSync(oldPromptFile)) {
5039
- fs19.unlinkSync(oldPromptFile);
5327
+ const oldPromptFile = path20.join(promptsDir, `rrce-${existingName}.md`);
5328
+ if (fs20.existsSync(oldPromptFile)) {
5329
+ fs20.unlinkSync(oldPromptFile);
5040
5330
  }
5041
5331
  }
5042
5332
  }
5043
5333
  for (const prompt of prompts) {
5044
- const baseName = path19.basename(prompt.filePath, ".md");
5334
+ const baseName = path20.basename(prompt.filePath, ".md");
5045
5335
  const promptFileName = `rrce-${baseName}.md`;
5046
- const promptFilePath = path19.join(promptsDir, promptFileName);
5047
- fs19.writeFileSync(promptFilePath, prompt.content);
5336
+ const promptFilePath = path20.join(promptsDir, promptFileName);
5337
+ fs20.writeFileSync(promptFilePath, prompt.content);
5048
5338
  const agentConfig = convertToOpenCodeAgent(prompt, true, `./prompts/${promptFileName}`);
5049
5339
  opencodeConfig.agent[baseName] = agentConfig;
5050
5340
  }
5051
- fs19.writeFileSync(OPENCODE_CONFIG, JSON.stringify(opencodeConfig, null, 2) + "\n");
5341
+ fs20.writeFileSync(OPENCODE_CONFIG, JSON.stringify(opencodeConfig, null, 2) + "\n");
5052
5342
  } catch (e) {
5053
5343
  console.error("Failed to update global OpenCode config with agents:", e);
5054
5344
  }
5055
5345
  } else {
5056
- const opencodeBaseDir = path19.join(primaryDataPath, ".opencode", "agent");
5346
+ const opencodeBaseDir = path20.join(primaryDataPath, ".opencode", "agent");
5057
5347
  ensureDir(opencodeBaseDir);
5058
5348
  clearDirectory(opencodeBaseDir);
5059
5349
  for (const prompt of prompts) {
5060
- const baseName = path19.basename(prompt.filePath, ".md");
5350
+ const baseName = path20.basename(prompt.filePath, ".md");
5061
5351
  const agentConfig = convertToOpenCodeAgent(prompt);
5062
5352
  const content = `---
5063
5353
  ${stringify3({
@@ -5066,22 +5356,22 @@ ${stringify3({
5066
5356
  tools: agentConfig.tools
5067
5357
  })}---
5068
5358
  ${agentConfig.prompt}`;
5069
- fs19.writeFileSync(path19.join(opencodeBaseDir, `${baseName}.md`), content);
5359
+ fs20.writeFileSync(path20.join(opencodeBaseDir, `${baseName}.md`), content);
5070
5360
  }
5071
5361
  }
5072
5362
  }
5073
5363
  function clearDirectory(dirPath) {
5074
- if (!fs19.existsSync(dirPath)) return;
5075
- const entries = fs19.readdirSync(dirPath, { withFileTypes: true });
5364
+ if (!fs20.existsSync(dirPath)) return;
5365
+ const entries = fs20.readdirSync(dirPath, { withFileTypes: true });
5076
5366
  for (const entry of entries) {
5077
5367
  if (entry.isFile()) {
5078
- fs19.unlinkSync(path19.join(dirPath, entry.name));
5368
+ fs20.unlinkSync(path20.join(dirPath, entry.name));
5079
5369
  }
5080
5370
  }
5081
5371
  }
5082
5372
  function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
5083
- const globalPath = path19.join(customGlobalPath, "workspaces", workspaceName);
5084
- const workspacePath = path19.join(workspaceRoot, ".rrce-workflow");
5373
+ const globalPath = path20.join(customGlobalPath, "workspaces", workspaceName);
5374
+ const workspacePath = path20.join(workspaceRoot, ".rrce-workflow");
5085
5375
  switch (mode) {
5086
5376
  case "global":
5087
5377
  return [globalPath];
@@ -5095,7 +5385,7 @@ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot,
5095
5385
  // src/commands/wizard/delete-flow.ts
5096
5386
  import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
5097
5387
  import pc12 from "picocolors";
5098
- import * as fs20 from "fs";
5388
+ import * as fs21 from "fs";
5099
5389
  init_detection();
5100
5390
  init_config();
5101
5391
  async function runDeleteGlobalProjectFlow(availableProjects) {
@@ -5139,8 +5429,8 @@ Are you sure?`,
5139
5429
  for (const projectName of projectsToDelete) {
5140
5430
  const project = globalProjects.find((p) => p.name === projectName);
5141
5431
  if (!project) continue;
5142
- if (fs20.existsSync(project.dataPath)) {
5143
- fs20.rmSync(project.dataPath, { recursive: true, force: true });
5432
+ if (fs21.existsSync(project.dataPath)) {
5433
+ fs21.rmSync(project.dataPath, { recursive: true, force: true });
5144
5434
  }
5145
5435
  const newConfig = removeProjectConfig(mcpConfig, projectName);
5146
5436
  configChanged = true;
@@ -5186,11 +5476,11 @@ Workspace: ${pc13.bold(workspaceName)}`,
5186
5476
  workspacePath
5187
5477
  });
5188
5478
  const configFilePath = getConfigPath(workspacePath);
5189
- let isAlreadyConfigured = fs21.existsSync(configFilePath);
5479
+ let isAlreadyConfigured = fs22.existsSync(configFilePath);
5190
5480
  let currentStorageMode = null;
5191
5481
  if (isAlreadyConfigured) {
5192
5482
  try {
5193
- const configContent = fs21.readFileSync(configFilePath, "utf-8");
5483
+ const configContent = fs22.readFileSync(configFilePath, "utf-8");
5194
5484
  const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
5195
5485
  currentStorageMode = modeMatch?.[1] ?? null;
5196
5486
  } catch {
@@ -5207,7 +5497,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
5207
5497
  }
5208
5498
  }
5209
5499
  const localDataPath = getLocalWorkspacePath(workspacePath);
5210
- const hasLocalData = fs21.existsSync(localDataPath);
5500
+ const hasLocalData = fs22.existsSync(localDataPath);
5211
5501
  if (isAlreadyConfigured) {
5212
5502
  const menuOptions = [];
5213
5503
  menuOptions.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rrce-workflow",
3
- "version": "0.2.93",
3
+ "version": "0.2.94",
4
4
  "description": "RRCE-Workflow TUI - Agentic code workflow generator for AI-assisted development",
5
5
  "author": "RRCE Team",
6
6
  "license": "MIT",