rrce-workflow 0.2.97 → 0.2.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1031,6 +1031,7 @@ var init_prompt = __esm({
1031
1031
  description: z.string(),
1032
1032
  "argument-hint": z.union([z.string(), z.array(z.string())]).optional(),
1033
1033
  tools: z.array(z.string()).optional(),
1034
+ mode: z.enum(["primary", "subagent"]).optional(),
1034
1035
  "required-args": z.array(PromptArgSchema).optional(),
1035
1036
  "optional-args": z.array(PromptArgSchema).optional(),
1036
1037
  "auto-identity": AutoIdentitySchema.optional()
@@ -1265,8 +1266,10 @@ function parseMCPConfig(content) {
1265
1266
  tasks: p.permissions?.tasks ?? DEFAULT_PERMISSIONS.tasks,
1266
1267
  refs: p.permissions?.refs ?? DEFAULT_PERMISSIONS.refs
1267
1268
  },
1268
- semanticSearch: p.semanticSearch
1269
- })) : []
1269
+ semanticSearch: p.semanticSearch,
1270
+ last_synced_version: p.last_synced_version
1271
+ })) : [],
1272
+ last_synced_version: parsed?.last_synced_version
1270
1273
  };
1271
1274
  return config;
1272
1275
  } catch (err) {
@@ -2360,7 +2363,7 @@ var init_dependency_graph = __esm({
2360
2363
  // src/mcp/resources.ts
2361
2364
  import * as fs15 from "fs";
2362
2365
  import * as path17 from "path";
2363
- import * as os2 from "os";
2366
+ import * as os3 from "os";
2364
2367
  import * as crypto from "crypto";
2365
2368
  import ignore from "ignore";
2366
2369
  function resolveProjectPaths(project, pathInput) {
@@ -2902,7 +2905,7 @@ function getContextPreamble() {
2902
2905
  const activeProject = detectActiveProject();
2903
2906
  let contextPreamble = "";
2904
2907
  if (activeProject) {
2905
- const rrceHome = process.env.RRCE_HOME || path17.join(os2.homedir(), ".rrce-workflow");
2908
+ const rrceHome = process.env.RRCE_HOME || path17.join(os3.homedir(), ".rrce-workflow");
2906
2909
  const workspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
2907
2910
  const rrceData = activeProject.dataPath;
2908
2911
  contextPreamble += `
@@ -2972,7 +2975,7 @@ async function createTask(projectName, taskSlug, taskData) {
2972
2975
  fs15.mkdirSync(path17.join(taskDir, "planning"), { recursive: true });
2973
2976
  fs15.mkdirSync(path17.join(taskDir, "execution"), { recursive: true });
2974
2977
  fs15.mkdirSync(path17.join(taskDir, "docs"), { recursive: true });
2975
- const rrceHome = process.env.RRCE_HOME || path17.join(os2.homedir(), ".rrce-workflow");
2978
+ const rrceHome = process.env.RRCE_HOME || path17.join(os3.homedir(), ".rrce-workflow");
2976
2979
  const templatePath = path17.join(rrceHome, "templates", "meta.template.json");
2977
2980
  let meta = {
2978
2981
  task_id: crypto.randomUUID(),
@@ -3868,8 +3871,8 @@ Hidden projects: ${projects.length - exposedCount}`,
3868
3871
  }
3869
3872
  async function handleConfigureGlobalPath() {
3870
3873
  const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
3871
- const fs24 = await import("fs");
3872
- const path22 = await import("path");
3874
+ const fs26 = await import("fs");
3875
+ const path24 = await import("path");
3873
3876
  note3(
3874
3877
  `MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
3875
3878
  and coordinate across projects.
@@ -3883,8 +3886,8 @@ locally in each project. MCP needs a central location.`,
3883
3886
  return false;
3884
3887
  }
3885
3888
  try {
3886
- if (!fs24.existsSync(resolvedPath)) {
3887
- fs24.mkdirSync(resolvedPath, { recursive: true });
3889
+ if (!fs26.existsSync(resolvedPath)) {
3890
+ fs26.mkdirSync(resolvedPath, { recursive: true });
3888
3891
  }
3889
3892
  const config = loadMCPConfig();
3890
3893
  saveMCPConfig(config);
@@ -3892,7 +3895,7 @@ locally in each project. MCP needs a central location.`,
3892
3895
  `${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
3893
3896
 
3894
3897
  MCP config will be stored at:
3895
- ${path22.join(resolvedPath, "mcp.yaml")}`,
3898
+ ${path24.join(resolvedPath, "mcp.yaml")}`,
3896
3899
  "Configuration Saved"
3897
3900
  );
3898
3901
  return true;
@@ -4217,10 +4220,191 @@ var init_tasks_fs = __esm({
4217
4220
  }
4218
4221
  });
4219
4222
 
4223
+ // src/lib/drift-service.ts
4224
+ import * as fs18 from "fs";
4225
+ import * as path20 from "path";
4226
+ import * as crypto2 from "crypto";
4227
+ var DriftService;
4228
+ var init_drift_service = __esm({
4229
+ "src/lib/drift-service.ts"() {
4230
+ "use strict";
4231
+ DriftService = class {
4232
+ static CHECKSUM_FILENAME = ".rrce-checksums.json";
4233
+ static calculateHash(filePath) {
4234
+ const content = fs18.readFileSync(filePath);
4235
+ return crypto2.createHash("md5").update(content).digest("hex");
4236
+ }
4237
+ static getManifestPath(projectPath) {
4238
+ return path20.join(projectPath, this.CHECKSUM_FILENAME);
4239
+ }
4240
+ static loadManifest(projectPath) {
4241
+ const manifestPath = this.getManifestPath(projectPath);
4242
+ if (!fs18.existsSync(manifestPath)) {
4243
+ return {};
4244
+ }
4245
+ try {
4246
+ return JSON.parse(fs18.readFileSync(manifestPath, "utf8"));
4247
+ } catch (e) {
4248
+ return {};
4249
+ }
4250
+ }
4251
+ static saveManifest(projectPath, manifest) {
4252
+ const manifestPath = this.getManifestPath(projectPath);
4253
+ fs18.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
4254
+ }
4255
+ /**
4256
+ * Generates a manifest for the current state of files in the project
4257
+ */
4258
+ static generateManifest(projectPath, files) {
4259
+ const manifest = {};
4260
+ for (const file of files) {
4261
+ const fullPath = path20.join(projectPath, file);
4262
+ if (fs18.existsSync(fullPath)) {
4263
+ const stats = fs18.statSync(fullPath);
4264
+ manifest[file] = {
4265
+ hash: this.calculateHash(fullPath),
4266
+ mtime: stats.mtimeMs
4267
+ };
4268
+ }
4269
+ }
4270
+ return manifest;
4271
+ }
4272
+ /**
4273
+ * Compares current files against the manifest to detect modifications
4274
+ */
4275
+ static detectModifiedFiles(projectPath) {
4276
+ const manifest = this.loadManifest(projectPath);
4277
+ const modifiedFiles = [];
4278
+ for (const [relPath, entry] of Object.entries(manifest)) {
4279
+ const fullPath = path20.join(projectPath, relPath);
4280
+ if (!fs18.existsSync(fullPath)) {
4281
+ continue;
4282
+ }
4283
+ const stats = fs18.statSync(fullPath);
4284
+ if (stats.mtimeMs === entry.mtime) {
4285
+ continue;
4286
+ }
4287
+ const currentHash = this.calculateHash(fullPath);
4288
+ if (currentHash !== entry.hash) {
4289
+ modifiedFiles.push(relPath);
4290
+ }
4291
+ }
4292
+ return modifiedFiles;
4293
+ }
4294
+ /**
4295
+ * Full drift check: version + modifications
4296
+ */
4297
+ static checkDrift(projectPath, currentVersion, runningVersion) {
4298
+ const modifiedFiles = this.detectModifiedFiles(projectPath);
4299
+ let type = "none";
4300
+ let hasDrift = false;
4301
+ if (currentVersion !== runningVersion) {
4302
+ hasDrift = true;
4303
+ type = "version";
4304
+ } else if (modifiedFiles.length > 0) {
4305
+ hasDrift = true;
4306
+ type = "modified";
4307
+ }
4308
+ return {
4309
+ hasDrift,
4310
+ type,
4311
+ modifiedFiles,
4312
+ version: {
4313
+ current: currentVersion || "0.0.0",
4314
+ running: runningVersion
4315
+ }
4316
+ };
4317
+ }
4318
+ };
4319
+ }
4320
+ });
4321
+
4322
+ // src/mcp/ui/ConfigContext.tsx
4323
+ var ConfigContext_exports = {};
4324
+ __export(ConfigContext_exports, {
4325
+ ConfigContext: () => ConfigContext,
4326
+ ConfigProvider: () => ConfigProvider,
4327
+ useConfig: () => useConfig
4328
+ });
4329
+ import { createContext, useContext, useState as useState2, useCallback, useMemo as useMemo2, useEffect as useEffect2 } from "react";
4330
+ import * as fs19 from "fs";
4331
+ import * as path21 from "path";
4332
+ import { jsx as jsx4 } from "react/jsx-runtime";
4333
+ function getPackageVersion() {
4334
+ try {
4335
+ const agentCoreDir = getAgentCoreDir();
4336
+ const packageJsonPath = path21.join(path21.dirname(agentCoreDir), "package.json");
4337
+ if (fs19.existsSync(packageJsonPath)) {
4338
+ return JSON.parse(fs19.readFileSync(packageJsonPath, "utf8")).version;
4339
+ }
4340
+ } catch (e) {
4341
+ }
4342
+ return "0.0.0";
4343
+ }
4344
+ function useConfig() {
4345
+ const context = useContext(ConfigContext);
4346
+ if (!context) {
4347
+ throw new Error("useConfig must be used within a ConfigProvider");
4348
+ }
4349
+ return context;
4350
+ }
4351
+ var ConfigContext, ConfigProvider;
4352
+ var init_ConfigContext = __esm({
4353
+ "src/mcp/ui/ConfigContext.tsx"() {
4354
+ "use strict";
4355
+ init_config();
4356
+ init_detection();
4357
+ init_config_utils();
4358
+ init_drift_service();
4359
+ init_prompts();
4360
+ ConfigContext = createContext(null);
4361
+ ConfigProvider = ({ children }) => {
4362
+ const [config, setConfig] = useState2(() => loadMCPConfig());
4363
+ const [projects, setProjects] = useState2(() => scanForProjects());
4364
+ const [driftReports, setDriftReports] = useState2({});
4365
+ const refresh = useCallback(() => {
4366
+ const newConfig = loadMCPConfig();
4367
+ const newProjects = scanForProjects();
4368
+ setConfig(newConfig);
4369
+ setProjects(newProjects);
4370
+ }, []);
4371
+ const checkAllDrift = useCallback(() => {
4372
+ const runningVersion = getPackageVersion();
4373
+ const reports = {};
4374
+ for (const project of projects) {
4375
+ const projectConfig = findProjectConfig(config, { name: project.name, path: project.path });
4376
+ const currentVersion = projectConfig?.last_synced_version;
4377
+ reports[project.path] = DriftService.checkDrift(project.dataPath, currentVersion, runningVersion);
4378
+ }
4379
+ setDriftReports(reports);
4380
+ }, [projects, config]);
4381
+ useEffect2(() => {
4382
+ checkAllDrift();
4383
+ }, [checkAllDrift]);
4384
+ const exposedProjects = useMemo2(
4385
+ () => projects.filter((p) => {
4386
+ const cfg = findProjectConfig(config, { name: p.name, path: p.path });
4387
+ return cfg?.expose ?? config.defaults.includeNew;
4388
+ }),
4389
+ [projects, config]
4390
+ );
4391
+ const value = useMemo2(() => ({
4392
+ config,
4393
+ projects,
4394
+ exposedProjects,
4395
+ driftReports,
4396
+ refresh,
4397
+ checkAllDrift
4398
+ }), [config, projects, exposedProjects, driftReports, refresh, checkAllDrift]);
4399
+ return /* @__PURE__ */ jsx4(ConfigContext.Provider, { value, children });
4400
+ };
4401
+ }
4402
+ });
4403
+
4220
4404
  // src/mcp/ui/ProjectsView.tsx
4221
- import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
4405
+ import { useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
4222
4406
  import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
4223
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
4407
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
4224
4408
  function nextStatus(current) {
4225
4409
  const idx = STATUS_CYCLE.indexOf(current || "");
4226
4410
  if (idx === -1) return STATUS_CYCLE[0];
@@ -4229,9 +4413,14 @@ function nextStatus(current) {
4229
4413
  function projectKey(p) {
4230
4414
  return p.sourcePath ?? p.path;
4231
4415
  }
4232
- function formatProjectLabel(p) {
4416
+ function formatProjectLabel(p, drift) {
4233
4417
  const root = p.sourcePath ?? p.path;
4234
- return `${p.name} (${p.source})${root ? ` - ${root}` : ""}`;
4418
+ const driftLabel = drift?.hasDrift ? " [UPDATE AVAILABLE]" : "";
4419
+ const label = `${p.name} (${p.source})${root ? ` - ${root}` : ""}`;
4420
+ if (drift?.hasDrift) {
4421
+ return `${label} ${driftLabel}`;
4422
+ }
4423
+ return label;
4235
4424
  }
4236
4425
  var STATUS_CYCLE, ProjectsView;
4237
4426
  var init_ProjectsView = __esm({
@@ -4240,15 +4429,17 @@ var init_ProjectsView = __esm({
4240
4429
  init_SimpleSelect();
4241
4430
  init_config();
4242
4431
  init_tasks_fs();
4432
+ init_ConfigContext();
4243
4433
  STATUS_CYCLE = ["pending", "in_progress", "blocked", "complete"];
4244
4434
  ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange }) => {
4245
- const [config, setConfig] = useState2(initialConfig);
4246
- const [mode, setMode] = useState2("expose");
4247
- const [expanded, setExpanded] = useState2(() => /* @__PURE__ */ new Set());
4248
- const [selectedIndex, setSelectedIndex] = useState2(0);
4249
- const [taskCache, setTaskCache] = useState2({});
4250
- const [errorLine, setErrorLine] = useState2(null);
4251
- const sortedProjects = useMemo2(() => {
4435
+ const { driftReports, checkAllDrift } = useConfig();
4436
+ const [config, setConfig] = useState3(initialConfig);
4437
+ const [mode, setMode] = useState3("expose");
4438
+ const [expanded, setExpanded] = useState3(() => /* @__PURE__ */ new Set());
4439
+ const [selectedIndex, setSelectedIndex] = useState3(0);
4440
+ const [taskCache, setTaskCache] = useState3({});
4441
+ const [errorLine, setErrorLine] = useState3(null);
4442
+ const sortedProjects = useMemo3(() => {
4252
4443
  return [...allProjects].sort((a, b) => {
4253
4444
  const byName = a.name.localeCompare(b.name);
4254
4445
  if (byName !== 0) return byName;
@@ -4272,6 +4463,10 @@ var init_ProjectsView = __esm({
4272
4463
  setMode((prev) => prev === "expose" ? "tasks" : "expose");
4273
4464
  return;
4274
4465
  }
4466
+ if (input === "u") {
4467
+ checkAllDrift();
4468
+ return;
4469
+ }
4275
4470
  if (mode === "expose") {
4276
4471
  if (input === "a") {
4277
4472
  const newConfig = {
@@ -4337,27 +4532,28 @@ var init_ProjectsView = __esm({
4337
4532
  }
4338
4533
  }
4339
4534
  });
4340
- useEffect2(() => {
4535
+ useEffect3(() => {
4341
4536
  setSelectedIndex((prev) => {
4342
4537
  if (flattenedRows.length === 0) return 0;
4343
4538
  return Math.min(prev, flattenedRows.length - 1);
4344
4539
  });
4345
4540
  }, [mode, allProjects, expanded, taskCache]);
4346
- const projectItems = useMemo2(() => {
4541
+ const projectItems = useMemo3(() => {
4347
4542
  return allProjects.map((p) => {
4348
4543
  const projectConfig = config.projects.find(
4349
4544
  (c) => c.path && c.path === p.path || p.source === "global" && c.name === p.name || !c.path && c.name === p.name
4350
4545
  );
4351
4546
  const isExposed = projectConfig ? projectConfig.expose : config.defaults.includeNew;
4547
+ const drift = driftReports[p.path];
4352
4548
  return {
4353
- label: formatProjectLabel(p),
4549
+ label: formatProjectLabel(p, drift),
4354
4550
  value: p.path,
4355
4551
  key: p.path,
4356
4552
  exposed: isExposed
4357
4553
  };
4358
4554
  });
4359
- }, [allProjects, config]);
4360
- const initialSelected = useMemo2(() => {
4555
+ }, [allProjects, config, driftReports]);
4556
+ const initialSelected = useMemo3(() => {
4361
4557
  return projectItems.filter((p) => p.exposed).map((p) => p.value);
4362
4558
  }, [projectItems]);
4363
4559
  const handleSubmit = (selectedIds) => {
@@ -4381,7 +4577,7 @@ var init_ProjectsView = __esm({
4381
4577
  setConfig(newConfig);
4382
4578
  onConfigChange?.();
4383
4579
  };
4384
- const flattenedRows = useMemo2(() => {
4580
+ const flattenedRows = useMemo3(() => {
4385
4581
  const rows = [];
4386
4582
  for (const p of sortedProjects) {
4387
4583
  rows.push({ kind: "project", project: p });
@@ -4402,15 +4598,15 @@ var init_ProjectsView = __esm({
4402
4598
  if (mode === "expose") {
4403
4599
  return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan", flexGrow: 1, children: [
4404
4600
  /* @__PURE__ */ jsxs3(Box4, { justifyContent: "space-between", children: [
4405
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: " Projects (Expose Mode) " }),
4601
+ /* @__PURE__ */ jsx5(Text4, { bold: true, color: "cyan", children: " Projects (Expose Mode) " }),
4406
4602
  /* @__PURE__ */ jsxs3(Box4, { children: [
4407
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Auto-expose new: " }),
4408
- /* @__PURE__ */ jsx4(Text4, { color: config.defaults.includeNew ? "green" : "red", children: config.defaults.includeNew ? "ON" : "OFF" }),
4409
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " (Press 'a' to toggle)" })
4603
+ /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: "Auto-expose new: " }),
4604
+ /* @__PURE__ */ jsx5(Text4, { color: config.defaults.includeNew ? "green" : "red", children: config.defaults.includeNew ? "ON" : "OFF" }),
4605
+ /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: " (Press 'a' to toggle)" })
4410
4606
  ] })
4411
4607
  ] }),
4412
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: " Space toggles, Enter saves. Press 't' to switch to Tasks Mode." }),
4413
- /* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx4(
4608
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: " Space toggles, Enter saves. Press 't' to switch to Tasks Mode." }),
4609
+ /* @__PURE__ */ jsx5(Box4, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx5(
4414
4610
  SimpleSelect,
4415
4611
  {
4416
4612
  message: "",
@@ -4429,62 +4625,69 @@ var init_ProjectsView = __esm({
4429
4625
  }
4430
4626
  return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan", flexGrow: 1, children: [
4431
4627
  /* @__PURE__ */ jsxs3(Box4, { justifyContent: "space-between", children: [
4432
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: " Projects (Tasks Mode) " }),
4433
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "t:Expose Enter:Expand s:Status R:Refresh" })
4628
+ /* @__PURE__ */ jsx5(Text4, { bold: true, color: "cyan", children: " Projects (Tasks Mode) " }),
4629
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "t:Expose Enter:Expand s:Status u:DriftCheck R:Refresh" })
4434
4630
  ] }),
4435
- errorLine && /* @__PURE__ */ jsx4(Box4, { marginTop: 0, children: /* @__PURE__ */ jsx4(Text4, { color: "red", children: errorLine }) }),
4631
+ errorLine && /* @__PURE__ */ jsx5(Box4, { marginTop: 0, children: /* @__PURE__ */ jsx5(Text4, { color: "red", children: errorLine }) }),
4436
4632
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "row", flexGrow: 1, children: [
4437
4633
  /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", width: "55%", children: [
4438
- flattenedRows.length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "No projects detected." }) : flattenedRows.map((row, idx) => {
4634
+ flattenedRows.length === 0 ? /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "No projects detected." }) : flattenedRows.map((row, idx) => {
4439
4635
  const isSel = idx === selectedIndex;
4440
4636
  if (row.kind === "project") {
4441
4637
  const k = projectKey(row.project);
4442
4638
  const isOpen = expanded.has(k);
4443
4639
  const count = (taskCache[k] || []).length;
4444
- return /* @__PURE__ */ jsxs3(Box4, { children: [
4445
- /* @__PURE__ */ jsx4(Text4, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
4446
- /* @__PURE__ */ jsxs3(Text4, { color: isSel ? "cyan" : "white", children: [
4447
- isOpen ? "\u25BE " : "\u25B8 ",
4448
- formatProjectLabel(row.project)
4640
+ const drift = driftReports[row.project.path];
4641
+ return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", children: [
4642
+ /* @__PURE__ */ jsxs3(Box4, { children: [
4643
+ /* @__PURE__ */ jsx5(Text4, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
4644
+ /* @__PURE__ */ jsxs3(Text4, { color: isSel ? "cyan" : "white", children: [
4645
+ isOpen ? "\u25BE " : "\u25B8 ",
4646
+ formatProjectLabel(row.project, drift)
4647
+ ] }),
4648
+ /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
4649
+ " ",
4650
+ count > 0 ? ` (tasks: ${count})` : ""
4651
+ ] })
4449
4652
  ] }),
4450
- /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
4451
- " ",
4452
- count > 0 ? ` (tasks: ${count})` : ""
4453
- ] })
4653
+ isSel && drift?.hasDrift && /* @__PURE__ */ jsx5(Box4, { marginLeft: 4, children: /* @__PURE__ */ jsxs3(Text4, { color: "magenta", dimColor: true, italic: true, children: [
4654
+ drift.type === "version" ? "New version available. " : "Modifications detected. ",
4655
+ "Run 'rrce-workflow wizard' to update."
4656
+ ] }) })
4454
4657
  ] }, `p:${k}`);
4455
4658
  }
4456
4659
  const taskLabel = row.task.title || row.task.task_slug;
4457
4660
  const status = row.task.status || "";
4458
4661
  return /* @__PURE__ */ jsxs3(Box4, { children: [
4459
- /* @__PURE__ */ jsx4(Text4, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
4460
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: " - " }),
4461
- /* @__PURE__ */ jsx4(Text4, { color: isSel ? "cyan" : "white", children: taskLabel }),
4462
- row.task.task_slug !== "__none__" && /* @__PURE__ */ jsx4(Text4, { color: status === "complete" ? "green" : status === "blocked" ? "red" : "yellow", children: ` [${status}]` })
4662
+ /* @__PURE__ */ jsx5(Text4, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
4663
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: " - " }),
4664
+ /* @__PURE__ */ jsx5(Text4, { color: isSel ? "cyan" : "white", children: taskLabel }),
4665
+ row.task.task_slug !== "__none__" && /* @__PURE__ */ jsx5(Text4, { color: status === "complete" ? "green" : status === "blocked" ? "red" : "yellow", children: ` [${status}]` })
4463
4666
  ] }, `t:${projectKey(row.project)}:${row.task.task_slug}`);
4464
4667
  }),
4465
- /* @__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" }) })
4668
+ /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "gray", children: "\u25B2/\u25BC navigate \u2022 Enter expand/collapse \u2022 s cycle status \u2022 R refresh \u2022 t expose mode" }) })
4466
4669
  ] }),
4467
- /* @__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: [
4468
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: selectedTask.title || selectedTask.task_slug }),
4469
- selectedTask.summary && /* @__PURE__ */ jsx4(Text4, { children: selectedTask.summary }),
4670
+ /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", width: "45%", paddingLeft: 2, children: !selectedTask ? /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Select a task to view details." }) : /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", children: [
4671
+ /* @__PURE__ */ jsx5(Text4, { bold: true, color: "cyan", children: selectedTask.title || selectedTask.task_slug }),
4672
+ selectedTask.summary && /* @__PURE__ */ jsx5(Text4, { children: selectedTask.summary }),
4470
4673
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
4471
4674
  /* @__PURE__ */ jsxs3(Text4, { children: [
4472
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Status: " }),
4473
- /* @__PURE__ */ jsx4(Text4, { children: selectedTask.status || "unknown" })
4675
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Status: " }),
4676
+ /* @__PURE__ */ jsx5(Text4, { children: selectedTask.status || "unknown" })
4474
4677
  ] }),
4475
4678
  /* @__PURE__ */ jsxs3(Text4, { children: [
4476
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Updated: " }),
4477
- /* @__PURE__ */ jsx4(Text4, { children: selectedTask.updated_at || "\u2014" })
4679
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Updated: " }),
4680
+ /* @__PURE__ */ jsx5(Text4, { children: selectedTask.updated_at || "\u2014" })
4478
4681
  ] }),
4479
4682
  /* @__PURE__ */ jsxs3(Text4, { children: [
4480
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Tags: " }),
4481
- /* @__PURE__ */ jsx4(Text4, { children: (selectedTask.tags || []).join(", ") || "\u2014" })
4683
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Tags: " }),
4684
+ /* @__PURE__ */ jsx5(Text4, { children: (selectedTask.tags || []).join(", ") || "\u2014" })
4482
4685
  ] })
4483
4686
  ] }),
4484
4687
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
4485
- /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Checklist" }),
4486
- (selectedTask.checklist || []).length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "\u2014" }) : (selectedTask.checklist || []).slice(0, 12).map((c, i) => /* @__PURE__ */ jsxs3(Text4, { children: [
4487
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "- " }),
4688
+ /* @__PURE__ */ jsx5(Text4, { bold: true, children: "Checklist" }),
4689
+ (selectedTask.checklist || []).length === 0 ? /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "\u2014" }) : (selectedTask.checklist || []).slice(0, 12).map((c, i) => /* @__PURE__ */ jsxs3(Text4, { children: [
4690
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "- " }),
4488
4691
  c.label || c.id || "item",
4489
4692
  " ",
4490
4693
  /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
@@ -4495,8 +4698,8 @@ var init_ProjectsView = __esm({
4495
4698
  ] }, c.id || i))
4496
4699
  ] }),
4497
4700
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
4498
- /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Agents" }),
4499
- !selectedTask.agents ? /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "\u2014" }) : Object.entries(selectedTask.agents).map(([agent, info]) => /* @__PURE__ */ jsxs3(Text4, { children: [
4701
+ /* @__PURE__ */ jsx5(Text4, { bold: true, children: "Agents" }),
4702
+ !selectedTask.agents ? /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "\u2014" }) : Object.entries(selectedTask.agents).map(([agent, info]) => /* @__PURE__ */ jsxs3(Text4, { children: [
4500
4703
  /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
4501
4704
  "- ",
4502
4705
  agent,
@@ -4514,9 +4717,9 @@ var init_ProjectsView = __esm({
4514
4717
  });
4515
4718
 
4516
4719
  // src/mcp/ui/components/InstallWizard.tsx
4517
- import { useState as useState3 } from "react";
4720
+ import { useState as useState4 } from "react";
4518
4721
  import { Box as Box5, Text as Text5 } from "ink";
4519
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
4722
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
4520
4723
  var InstallWizard;
4521
4724
  var init_InstallWizard = __esm({
4522
4725
  "src/mcp/ui/components/InstallWizard.tsx"() {
@@ -4524,8 +4727,8 @@ var init_InstallWizard = __esm({
4524
4727
  init_SimpleSelect();
4525
4728
  init_install();
4526
4729
  InstallWizard = ({ workspacePath, onComplete, onCancel }) => {
4527
- const [status, setStatus] = useState3(checkInstallStatus(workspacePath));
4528
- const [message, setMessage] = useState3("");
4730
+ const [status, setStatus] = useState4(checkInstallStatus(workspacePath));
4731
+ const [message, setMessage] = useState4("");
4529
4732
  const options = [
4530
4733
  {
4531
4734
  value: "opencode",
@@ -4576,8 +4779,8 @@ var init_InstallWizard = __esm({
4576
4779
  }, 2e3);
4577
4780
  };
4578
4781
  return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
4579
- message && /* @__PURE__ */ jsx5(Text5, { color: "green", children: message }),
4580
- /* @__PURE__ */ jsx5(
4782
+ message && /* @__PURE__ */ jsx6(Text5, { color: "green", children: message }),
4783
+ /* @__PURE__ */ jsx6(
4581
4784
  SimpleSelect,
4582
4785
  {
4583
4786
  message: "Select integrations to install:",
@@ -4602,7 +4805,7 @@ var init_InstallWizard = __esm({
4602
4805
  // src/mcp/ui/InstallView.tsx
4603
4806
  import "react";
4604
4807
  import { Box as Box6, Text as Text6 } from "ink";
4605
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4808
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
4606
4809
  var InstallView;
4607
4810
  var init_InstallView = __esm({
4608
4811
  "src/mcp/ui/InstallView.tsx"() {
@@ -4612,9 +4815,9 @@ var init_InstallView = __esm({
4612
4815
  InstallView = () => {
4613
4816
  const workspacePath = detectWorkspaceRoot();
4614
4817
  return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "magenta", flexGrow: 1, children: [
4615
- /* @__PURE__ */ jsx6(Text6, { bold: true, color: "magenta", children: " Installation & Configuration " }),
4616
- /* @__PURE__ */ jsx6(Text6, { color: "dim", children: " Configure IDE integrations for OpenCode, VSCode, Claude, and Antigravity." }),
4617
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx6(
4818
+ /* @__PURE__ */ jsx7(Text6, { bold: true, color: "magenta", children: " Installation & Configuration " }),
4819
+ /* @__PURE__ */ jsx7(Text6, { color: "dim", children: " Configure IDE integrations for OpenCode, VSCode, Claude, and Antigravity." }),
4820
+ /* @__PURE__ */ jsx7(Box6, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx7(
4618
4821
  InstallWizard,
4619
4822
  {
4620
4823
  workspacePath,
@@ -4632,7 +4835,7 @@ var init_InstallView = __esm({
4632
4835
  // src/mcp/ui/LogViewer.tsx
4633
4836
  import "react";
4634
4837
  import { Box as Box7, Text as Text7 } from "ink";
4635
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4838
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
4636
4839
  var LogViewer;
4637
4840
  var init_LogViewer = __esm({
4638
4841
  "src/mcp/ui/LogViewer.tsx"() {
@@ -4642,16 +4845,16 @@ var init_LogViewer = __esm({
4642
4845
  const emptyLines = Math.max(0, height - visibleLogs.length);
4643
4846
  const padding = Array(emptyLines).fill("");
4644
4847
  const formatLog = (log) => {
4645
- if (log.includes("[RAG]")) return /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: log });
4646
- if (log.includes("[ERROR]")) return /* @__PURE__ */ jsx7(Text7, { color: "red", children: log });
4647
- if (log.includes("[WARN]")) return /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: log });
4648
- if (log.includes("[INFO]")) return /* @__PURE__ */ jsx7(Text7, { color: "green", children: log });
4649
- if (log.includes("Success")) return /* @__PURE__ */ jsx7(Text7, { color: "green", children: log });
4650
- return /* @__PURE__ */ jsx7(Text7, { children: log });
4848
+ if (log.includes("[RAG]")) return /* @__PURE__ */ jsx8(Text7, { color: "cyan", children: log });
4849
+ if (log.includes("[ERROR]")) return /* @__PURE__ */ jsx8(Text7, { color: "red", children: log });
4850
+ if (log.includes("[WARN]")) return /* @__PURE__ */ jsx8(Text7, { color: "yellow", children: log });
4851
+ if (log.includes("[INFO]")) return /* @__PURE__ */ jsx8(Text7, { color: "green", children: log });
4852
+ if (log.includes("Success")) return /* @__PURE__ */ jsx8(Text7, { color: "green", children: log });
4853
+ return /* @__PURE__ */ jsx8(Text7, { children: log });
4651
4854
  };
4652
4855
  return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "dim", paddingX: 1, height: height + 2, flexGrow: 1, children: [
4653
- padding.map((_, i) => /* @__PURE__ */ jsx7(Text7, { children: " " }, `empty-${i}`)),
4654
- visibleLogs.map((log, i) => /* @__PURE__ */ jsx7(Box7, { children: formatLog(log) }, `log-${i}`))
4856
+ padding.map((_, i) => /* @__PURE__ */ jsx8(Text7, { children: " " }, `empty-${i}`)),
4857
+ visibleLogs.map((log, i) => /* @__PURE__ */ jsx8(Box7, { children: formatLog(log) }, `log-${i}`))
4655
4858
  ] });
4656
4859
  };
4657
4860
  }
@@ -4660,35 +4863,40 @@ var init_LogViewer = __esm({
4660
4863
  // src/mcp/ui/StatusBoard.tsx
4661
4864
  import "react";
4662
4865
  import { Box as Box8, Text as Text8 } from "ink";
4663
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4866
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
4664
4867
  var StatusBoard;
4665
4868
  var init_StatusBoard = __esm({
4666
4869
  "src/mcp/ui/StatusBoard.tsx"() {
4667
4870
  "use strict";
4668
- StatusBoard = ({ exposedLabel, port, pid, running }) => {
4669
- return /* @__PURE__ */ jsx8(Box8, { borderStyle: "single", borderColor: "cyan", paddingX: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs7(Text8, { children: [
4670
- running ? /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u25CF RUNNING" }) : /* @__PURE__ */ jsx8(Text8, { color: "red", children: "\u25CF STOPPED" }),
4871
+ StatusBoard = ({ exposedLabel, port, pid, running, hasDrift }) => {
4872
+ return /* @__PURE__ */ jsx9(Box8, { borderStyle: "single", borderColor: "cyan", paddingX: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs7(Text8, { children: [
4873
+ running ? /* @__PURE__ */ jsx9(Text8, { color: "green", children: "\u25CF RUNNING" }) : /* @__PURE__ */ jsx9(Text8, { color: "red", children: "\u25CF STOPPED" }),
4671
4874
  " ",
4672
4875
  "\u2502",
4673
4876
  " \u{1F4CB} ",
4674
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: exposedLabel }),
4877
+ /* @__PURE__ */ jsx9(Text8, { color: "yellow", children: exposedLabel }),
4675
4878
  " ",
4676
4879
  "\u2502",
4677
4880
  " Port: ",
4678
- /* @__PURE__ */ jsx8(Text8, { color: "green", children: port }),
4881
+ /* @__PURE__ */ jsx9(Text8, { color: "green", children: port }),
4679
4882
  " ",
4680
4883
  "\u2502",
4681
4884
  " PID: ",
4682
- /* @__PURE__ */ jsx8(Text8, { color: "green", children: pid })
4885
+ /* @__PURE__ */ jsx9(Text8, { color: "green", children: pid }),
4886
+ hasDrift && /* @__PURE__ */ jsxs7(Text8, { color: "magenta", bold: true, children: [
4887
+ " ",
4888
+ "\u2502",
4889
+ " \u2B06 UPDATE AVAILABLE"
4890
+ ] })
4683
4891
  ] }) });
4684
4892
  };
4685
4893
  }
4686
4894
  });
4687
4895
 
4688
4896
  // src/mcp/ui/IndexingStatus.tsx
4689
- import { useState as useState4, useEffect as useEffect4 } from "react";
4897
+ import { useState as useState5, useEffect as useEffect5 } from "react";
4690
4898
  import { Box as Box9, Text as Text9 } from "ink";
4691
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4899
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
4692
4900
  var IndexingStatus;
4693
4901
  var init_IndexingStatus = __esm({
4694
4902
  "src/mcp/ui/IndexingStatus.tsx"() {
@@ -4698,9 +4906,9 @@ var init_IndexingStatus = __esm({
4698
4906
  init_resources();
4699
4907
  init_config_utils();
4700
4908
  IndexingStatus = ({ projects, config }) => {
4701
- const [stats, setStats] = useState4([]);
4702
- const [loading, setLoading] = useState4(true);
4703
- useEffect4(() => {
4909
+ const [stats, setStats] = useState5([]);
4910
+ const [loading, setLoading] = useState5(true);
4911
+ useEffect5(() => {
4704
4912
  const fetchStats = async () => {
4705
4913
  const newStats = [];
4706
4914
  for (const project of projects) {
@@ -4762,27 +4970,27 @@ var init_IndexingStatus = __esm({
4762
4970
  return () => clearInterval(interval);
4763
4971
  }, [projects, config]);
4764
4972
  if (loading && stats.length === 0) {
4765
- return /* @__PURE__ */ jsx9(Text9, { children: "Loading indexing status..." });
4973
+ return /* @__PURE__ */ jsx10(Text9, { children: "Loading indexing status..." });
4766
4974
  }
4767
4975
  return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "blue", flexGrow: 1, children: [
4768
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: "blue", children: " RAG Indexing Status " }),
4976
+ /* @__PURE__ */ jsx10(Text9, { bold: true, color: "blue", children: " RAG Indexing Status " }),
4769
4977
  /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
4770
4978
  /* @__PURE__ */ jsxs8(Box9, { children: [
4771
- /* @__PURE__ */ jsx9(Box9, { width: 25, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Project" }) }),
4772
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Status" }) }),
4773
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "State" }) }),
4774
- /* @__PURE__ */ jsx9(Box9, { width: 18, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Progress" }) }),
4775
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Indexed Files" }) }),
4776
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Total Chunks" }) }),
4777
- /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Last Index" }) })
4979
+ /* @__PURE__ */ jsx10(Box9, { width: 25, children: /* @__PURE__ */ jsx10(Text9, { underline: true, children: "Project" }) }),
4980
+ /* @__PURE__ */ jsx10(Box9, { width: 15, children: /* @__PURE__ */ jsx10(Text9, { underline: true, children: "Status" }) }),
4981
+ /* @__PURE__ */ jsx10(Box9, { width: 15, children: /* @__PURE__ */ jsx10(Text9, { underline: true, children: "State" }) }),
4982
+ /* @__PURE__ */ jsx10(Box9, { width: 18, children: /* @__PURE__ */ jsx10(Text9, { underline: true, children: "Progress" }) }),
4983
+ /* @__PURE__ */ jsx10(Box9, { width: 15, children: /* @__PURE__ */ jsx10(Text9, { underline: true, children: "Indexed Files" }) }),
4984
+ /* @__PURE__ */ jsx10(Box9, { width: 15, children: /* @__PURE__ */ jsx10(Text9, { underline: true, children: "Total Chunks" }) }),
4985
+ /* @__PURE__ */ jsx10(Box9, { children: /* @__PURE__ */ jsx10(Text9, { underline: true, children: "Last Index" }) })
4778
4986
  ] }),
4779
- stats.length === 0 ? /* @__PURE__ */ jsx9(Text9, { color: "dim", children: "No exposed projects found." }) : stats.map((s) => /* @__PURE__ */ jsxs8(Box9, { marginTop: 0, children: [
4780
- /* @__PURE__ */ jsx9(Box9, { width: 25, children: /* @__PURE__ */ jsx9(Text9, { color: "white", children: s.projectName }) }),
4781
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { color: s.state === "running" ? "yellow" : s.state === "failed" ? "red" : s.enabled ? "green" : "dim", children: s.enabled ? s.state : "disabled" }) }),
4782
- /* @__PURE__ */ jsx9(Box9, { width: 18, children: /* @__PURE__ */ jsx9(Text9, { children: s.state === "running" ? `${s.itemsDone}/${s.itemsTotal ?? "?"}` : "-" }) }),
4783
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { children: s.enabled ? s.totalFiles : "-" }) }),
4784
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { children: s.enabled ? s.totalChunks : "-" }) }),
4785
- /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text9, { children: s.lastFullIndex ? new Date(s.lastFullIndex).toLocaleTimeString() : "-" }) })
4987
+ stats.length === 0 ? /* @__PURE__ */ jsx10(Text9, { color: "dim", children: "No exposed projects found." }) : stats.map((s) => /* @__PURE__ */ jsxs8(Box9, { marginTop: 0, children: [
4988
+ /* @__PURE__ */ jsx10(Box9, { width: 25, children: /* @__PURE__ */ jsx10(Text9, { color: "white", children: s.projectName }) }),
4989
+ /* @__PURE__ */ jsx10(Box9, { width: 15, children: /* @__PURE__ */ jsx10(Text9, { color: s.state === "running" ? "yellow" : s.state === "failed" ? "red" : s.enabled ? "green" : "dim", children: s.enabled ? s.state : "disabled" }) }),
4990
+ /* @__PURE__ */ jsx10(Box9, { width: 18, children: /* @__PURE__ */ jsx10(Text9, { children: s.state === "running" ? `${s.itemsDone}/${s.itemsTotal ?? "?"}` : "-" }) }),
4991
+ /* @__PURE__ */ jsx10(Box9, { width: 15, children: /* @__PURE__ */ jsx10(Text9, { children: s.enabled ? s.totalFiles : "-" }) }),
4992
+ /* @__PURE__ */ jsx10(Box9, { width: 15, children: /* @__PURE__ */ jsx10(Text9, { children: s.enabled ? s.totalChunks : "-" }) }),
4993
+ /* @__PURE__ */ jsx10(Box9, { children: /* @__PURE__ */ jsx10(Text9, { children: s.lastFullIndex ? new Date(s.lastFullIndex).toLocaleTimeString() : "-" }) })
4786
4994
  ] }, s.projectName))
4787
4995
  ] })
4788
4996
  ] });
@@ -4793,7 +5001,7 @@ var init_IndexingStatus = __esm({
4793
5001
  // src/mcp/ui/components/TabBar.tsx
4794
5002
  import "react";
4795
5003
  import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
4796
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
5004
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
4797
5005
  var TabBar;
4798
5006
  var init_TabBar = __esm({
4799
5007
  "src/mcp/ui/components/TabBar.tsx"() {
@@ -4823,7 +5031,7 @@ var init_TabBar = __esm({
4823
5031
  return /* @__PURE__ */ jsxs9(Box10, { borderStyle: "single", paddingX: 1, borderColor: "gray", children: [
4824
5032
  tabs.map((tab, index) => {
4825
5033
  const isActive = tab.id === activeTab;
4826
- return /* @__PURE__ */ jsx10(Box10, { marginRight: 2, children: /* @__PURE__ */ jsx10(
5034
+ return /* @__PURE__ */ jsx11(Box10, { marginRight: 2, children: /* @__PURE__ */ jsx11(
4827
5035
  Text10,
4828
5036
  {
4829
5037
  color: isActive ? "cyan" : "white",
@@ -4833,8 +5041,8 @@ var init_TabBar = __esm({
4833
5041
  }
4834
5042
  ) }, tab.id);
4835
5043
  }),
4836
- /* @__PURE__ */ jsx10(Box10, { flexGrow: 1 }),
4837
- /* @__PURE__ */ jsx10(Text10, { color: "dim", children: "Use \u25C4/\u25BA arrows to navigate" })
5044
+ /* @__PURE__ */ jsx11(Box10, { flexGrow: 1 }),
5045
+ /* @__PURE__ */ jsx11(Text10, { color: "dim", children: "Use \u25C4/\u25BA arrows to navigate" })
4838
5046
  ] });
4839
5047
  };
4840
5048
  }
@@ -4845,10 +5053,10 @@ var App_exports = {};
4845
5053
  __export(App_exports, {
4846
5054
  App: () => App
4847
5055
  });
4848
- import { useState as useState5, useEffect as useEffect5, useMemo as useMemo3, useCallback } from "react";
5056
+ import { useState as useState6, useEffect as useEffect6, useMemo as useMemo4, useCallback as useCallback3 } from "react";
4849
5057
  import { Box as Box11, useInput as useInput4, useApp } from "ink";
4850
- import fs18 from "fs";
4851
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
5058
+ import fs20 from "fs";
5059
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
4852
5060
  var App;
4853
5061
  var init_App = __esm({
4854
5062
  "src/mcp/ui/App.tsx"() {
@@ -4868,32 +5076,28 @@ var init_App = __esm({
4868
5076
  init_resources();
4869
5077
  init_install();
4870
5078
  init_paths();
5079
+ init_ConfigContext();
4871
5080
  App = ({ onExit, initialPort }) => {
4872
5081
  const { exit } = useApp();
4873
- const [activeTab, setActiveTab] = useState5("overview");
4874
- const [logs, setLogs] = useState5([]);
4875
- const [serverInfo, setServerInfo] = useState5({
5082
+ const { config, projects, exposedProjects, driftReports, refresh: refreshData } = useConfig();
5083
+ const [activeTab, setActiveTab] = useState6("overview");
5084
+ const [logs, setLogs] = useState6([]);
5085
+ const [serverInfo, setServerInfo] = useState6({
4876
5086
  port: initialPort,
4877
5087
  pid: process.pid,
4878
5088
  running: false
4879
5089
  });
4880
- const [config, setConfig] = useState5(() => loadMCPConfig());
4881
- const [projects, setProjects] = useState5(() => scanForProjects());
4882
- const refreshData = useCallback(() => {
4883
- setConfig(loadMCPConfig());
4884
- setProjects(scanForProjects());
4885
- }, []);
4886
- const exposedProjects = useMemo3(
4887
- () => projects.filter((p) => isProjectExposed(config, p.name, p.path)),
4888
- [projects, config]
4889
- );
4890
- const isRAGEnabled = useMemo3(() => {
5090
+ const isRAGEnabled = useMemo4(() => {
4891
5091
  return exposedProjects.some((p) => {
4892
5092
  const cfg = findProjectConfig(config, { name: p.name, path: p.path });
4893
5093
  return cfg?.semanticSearch?.enabled || p.semanticSearchEnabled;
4894
5094
  });
4895
5095
  }, [exposedProjects, config]);
4896
- const tabs = useMemo3(() => {
5096
+ const hasAnyDrift = useMemo4(
5097
+ () => Object.values(driftReports).some((r) => r.hasDrift),
5098
+ [driftReports]
5099
+ );
5100
+ const tabs = useMemo4(() => {
4897
5101
  const baseTabs = [
4898
5102
  { id: "overview", label: "Overview" },
4899
5103
  { id: "logs", label: "Logs" },
@@ -4913,7 +5117,7 @@ var init_App = __esm({
4913
5117
  installStatus.vscodeGlobal,
4914
5118
  installStatus.vscodeWorkspace
4915
5119
  ].filter(Boolean).length;
4916
- useEffect5(() => {
5120
+ useEffect6(() => {
4917
5121
  const start = async () => {
4918
5122
  const status = getMCPServerStatus();
4919
5123
  if (!status.running) {
@@ -4929,21 +5133,21 @@ var init_App = __esm({
4929
5133
  };
4930
5134
  start();
4931
5135
  }, []);
4932
- useEffect5(() => {
5136
+ useEffect6(() => {
4933
5137
  const logPath = getLogFilePath();
4934
5138
  let lastSize = 0;
4935
- if (fs18.existsSync(logPath)) {
4936
- const stats = fs18.statSync(logPath);
5139
+ if (fs20.existsSync(logPath)) {
5140
+ const stats = fs20.statSync(logPath);
4937
5141
  lastSize = stats.size;
4938
5142
  }
4939
5143
  const interval = setInterval(() => {
4940
- if (fs18.existsSync(logPath)) {
4941
- const stats = fs18.statSync(logPath);
5144
+ if (fs20.existsSync(logPath)) {
5145
+ const stats = fs20.statSync(logPath);
4942
5146
  if (stats.size > lastSize) {
4943
5147
  const buffer = Buffer.alloc(stats.size - lastSize);
4944
- const fd = fs18.openSync(logPath, "r");
4945
- fs18.readSync(fd, buffer, 0, buffer.length, lastSize);
4946
- fs18.closeSync(fd);
5148
+ const fd = fs20.openSync(logPath, "r");
5149
+ fs20.readSync(fd, buffer, 0, buffer.length, lastSize);
5150
+ fs20.closeSync(fd);
4947
5151
  const newContent = buffer.toString("utf-8");
4948
5152
  const newLines = newContent.split("\n").filter((l) => l.trim());
4949
5153
  setLogs((prev) => {
@@ -4977,13 +5181,13 @@ var init_App = __esm({
4977
5181
  });
4978
5182
  const termHeight = process.stdout.rows || 24;
4979
5183
  const contentHeight = termHeight - 8;
4980
- const handleConfigChange = useCallback(() => {
5184
+ const handleConfigChange = useCallback3(() => {
4981
5185
  refreshData();
4982
5186
  }, [refreshData]);
4983
5187
  return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", padding: 0, height: termHeight, children: [
4984
- /* @__PURE__ */ jsx11(TabBar, { tabs, activeTab, onChange: setActiveTab }),
5188
+ /* @__PURE__ */ jsx12(TabBar, { tabs, activeTab, onChange: setActiveTab }),
4985
5189
  /* @__PURE__ */ jsxs10(Box11, { marginTop: 1, flexGrow: 1, children: [
4986
- activeTab === "overview" && /* @__PURE__ */ jsx11(
5190
+ activeTab === "overview" && /* @__PURE__ */ jsx12(
4987
5191
  Overview,
4988
5192
  {
4989
5193
  serverStatus: serverInfo,
@@ -4994,18 +5198,19 @@ var init_App = __esm({
4994
5198
  }
4995
5199
  }
4996
5200
  ),
4997
- activeTab === "projects" && /* @__PURE__ */ jsx11(ProjectsView, { config, projects, onConfigChange: handleConfigChange }),
4998
- activeTab === "indexing" && /* @__PURE__ */ jsx11(IndexingStatus, { config, projects: exposedProjects }),
4999
- activeTab === "install" && /* @__PURE__ */ jsx11(InstallView, {}),
5000
- activeTab === "logs" && /* @__PURE__ */ jsx11(LogViewer, { logs, height: contentHeight })
5201
+ activeTab === "projects" && /* @__PURE__ */ jsx12(ProjectsView, { config, projects, onConfigChange: handleConfigChange }),
5202
+ activeTab === "indexing" && /* @__PURE__ */ jsx12(IndexingStatus, { config, projects: exposedProjects }),
5203
+ activeTab === "install" && /* @__PURE__ */ jsx12(InstallView, {}),
5204
+ activeTab === "logs" && /* @__PURE__ */ jsx12(LogViewer, { logs, height: contentHeight })
5001
5205
  ] }),
5002
- /* @__PURE__ */ jsx11(Box11, { marginTop: 0, children: /* @__PURE__ */ jsx11(
5206
+ /* @__PURE__ */ jsx12(Box11, { marginTop: 0, children: /* @__PURE__ */ jsx12(
5003
5207
  StatusBoard,
5004
5208
  {
5005
5209
  exposedLabel: `${exposedProjects.length} / ${projects.length} projects`,
5006
5210
  port: serverInfo.port,
5007
5211
  pid: serverInfo.pid,
5008
- running: serverInfo.running
5212
+ running: serverInfo.running,
5213
+ hasDrift: hasAnyDrift
5009
5214
  }
5010
5215
  ) })
5011
5216
  ] });
@@ -5016,9 +5221,10 @@ var init_App = __esm({
5016
5221
  // src/mcp/commands/start.ts
5017
5222
  import { confirm as confirm3, isCancel as isCancel5, text } from "@clack/prompts";
5018
5223
  async function handleStartServer() {
5019
- const React12 = await import("react");
5224
+ const React13 = await import("react");
5020
5225
  const { render } = await import("ink");
5021
5226
  const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
5227
+ const { ConfigProvider: ConfigProvider2 } = await Promise.resolve().then(() => (init_ConfigContext(), ConfigContext_exports));
5022
5228
  const config = loadMCPConfig();
5023
5229
  const projects = scanForProjects();
5024
5230
  const exposedProjects = projects.filter((p) => {
@@ -5057,13 +5263,20 @@ async function handleStartServer() {
5057
5263
  }
5058
5264
  }
5059
5265
  process.stdin.resume();
5060
- const app = render(React12.createElement(App2, {
5061
- initialPort,
5062
- onExit: () => {
5266
+ const app = render(
5267
+ React13.createElement(
5268
+ ConfigProvider2,
5269
+ null,
5270
+ React13.createElement(App2, {
5271
+ initialPort,
5272
+ onExit: () => {
5273
+ }
5274
+ })
5275
+ ),
5276
+ {
5277
+ exitOnCtrlC: false
5063
5278
  }
5064
- }), {
5065
- exitOnCtrlC: false
5066
- });
5279
+ );
5067
5280
  await app.waitUntilExit();
5068
5281
  console.clear();
5069
5282
  }
@@ -5154,7 +5367,7 @@ var init_mcp = __esm({
5154
5367
  // src/commands/wizard/index.ts
5155
5368
  import { intro as intro2, select as select5, spinner as spinner7, note as note11, outro as outro7, isCancel as isCancel12 } from "@clack/prompts";
5156
5369
  import pc13 from "picocolors";
5157
- import * as fs23 from "fs";
5370
+ import * as fs25 from "fs";
5158
5371
 
5159
5372
  // src/lib/git.ts
5160
5373
  import { execSync } from "child_process";
@@ -5302,6 +5515,7 @@ import { stringify as stringify2 } from "yaml";
5302
5515
  init_paths();
5303
5516
  import * as fs7 from "fs";
5304
5517
  import * as path8 from "path";
5518
+ import * as os2 from "os";
5305
5519
  import "yaml";
5306
5520
  function copyPromptsToDir(prompts, targetDir, extension) {
5307
5521
  for (const prompt of prompts) {
@@ -5312,10 +5526,37 @@ function copyPromptsToDir(prompts, targetDir, extension) {
5312
5526
  fs7.writeFileSync(targetPath, content);
5313
5527
  }
5314
5528
  }
5529
+ function updateOpenCodeConfig(newAgents) {
5530
+ const opencodePath = path8.join(os2.homedir(), ".config", "opencode", "opencode.json");
5531
+ if (!fs7.existsSync(opencodePath)) {
5532
+ return;
5533
+ }
5534
+ try {
5535
+ const config = JSON.parse(fs7.readFileSync(opencodePath, "utf8"));
5536
+ if (!config.agents) {
5537
+ config.agents = {};
5538
+ }
5539
+ const existingAgentKeys = Object.keys(config.agents);
5540
+ const rrceKeys = existingAgentKeys.filter((key) => key.startsWith("rrce_"));
5541
+ for (const key of rrceKeys) {
5542
+ if (!newAgents[key]) {
5543
+ delete config.agents[key];
5544
+ }
5545
+ }
5546
+ for (const [key, value] of Object.entries(newAgents)) {
5547
+ if (key.startsWith("rrce_")) {
5548
+ config.agents[key] = value;
5549
+ }
5550
+ }
5551
+ fs7.writeFileSync(opencodePath, JSON.stringify(config, null, 2));
5552
+ } catch (e) {
5553
+ console.error("Failed to update OpenCode config:", e);
5554
+ }
5555
+ }
5315
5556
  function convertToOpenCodeAgent(prompt, useFileReference = false, promptFilePath) {
5316
5557
  const { frontmatter, content } = prompt;
5317
5558
  const tools = {};
5318
- const hostTools = ["read", "write", "edit", "bash", "grep", "glob", "webfetch", "terminalLastCommand"];
5559
+ const hostTools = ["read", "write", "edit", "bash", "grep", "glob", "webfetch", "terminalLastCommand", "task"];
5319
5560
  if (frontmatter.tools) {
5320
5561
  for (const tool of frontmatter.tools) {
5321
5562
  if (hostTools.includes(tool)) {
@@ -5326,10 +5567,11 @@ function convertToOpenCodeAgent(prompt, useFileReference = false, promptFilePath
5326
5567
  }
5327
5568
  }
5328
5569
  tools["webfetch"] = true;
5329
- const invocationHint = " (Invoke via @rrce_*)";
5570
+ const mode = frontmatter.mode || "subagent";
5571
+ const invocationHint = mode === "primary" ? "" : " (Invoke via @rrce_*)";
5330
5572
  return {
5331
5573
  description: `${frontmatter.description}${invocationHint}`,
5332
- mode: "subagent",
5574
+ mode,
5333
5575
  prompt: useFileReference && promptFilePath ? `{file:${promptFilePath}}` : content,
5334
5576
  tools
5335
5577
  };
@@ -5491,6 +5733,9 @@ async function installAgentPrompts(config, workspacePath, dataPaths) {
5491
5733
  const agentConfig = convertToOpenCodeAgent(prompt, true, `./prompts/${promptFileName}`);
5492
5734
  opencodeConfig.agent[agentId] = agentConfig;
5493
5735
  }
5736
+ if (!opencodeConfig.agent) opencodeConfig.agent = {};
5737
+ if (!opencodeConfig.agent.plan) opencodeConfig.agent.plan = {};
5738
+ opencodeConfig.agent.plan.disable = true;
5494
5739
  fs10.writeFileSync(OPENCODE_CONFIG, JSON.stringify(opencodeConfig, null, 2) + "\n");
5495
5740
  } catch (e) {
5496
5741
  console.error("Failed to update global OpenCode config with agents:", e);
@@ -5565,8 +5810,8 @@ linked_projects:
5565
5810
  async function registerWithMCP(config, workspacePath, workspaceName) {
5566
5811
  if (!config.exposeToMCP) return;
5567
5812
  try {
5568
- const { loadMCPConfig: loadMCPConfig2, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5569
- const mcpConfig = loadMCPConfig2();
5813
+ const { loadMCPConfig: loadMCPConfig3, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5814
+ const mcpConfig = loadMCPConfig3();
5570
5815
  setProjectConfig2(
5571
5816
  mcpConfig,
5572
5817
  workspaceName,
@@ -5831,7 +6076,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
5831
6076
  init_paths();
5832
6077
  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";
5833
6078
  import pc9 from "picocolors";
5834
- import * as fs19 from "fs";
6079
+ import * as fs21 from "fs";
5835
6080
  init_detection();
5836
6081
  async function runLinkProjectsFlow(workspacePath, workspaceName) {
5837
6082
  const projects = scanForProjects({
@@ -5870,7 +6115,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
5870
6115
  const s = spinner3();
5871
6116
  s.start("Linking projects");
5872
6117
  const configFilePath = getConfigPath(workspacePath);
5873
- let configContent = fs19.readFileSync(configFilePath, "utf-8");
6118
+ let configContent = fs21.readFileSync(configFilePath, "utf-8");
5874
6119
  if (configContent.includes("linked_projects:")) {
5875
6120
  const lines = configContent.split("\n");
5876
6121
  const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
@@ -5897,7 +6142,7 @@ linked_projects:
5897
6142
  `;
5898
6143
  });
5899
6144
  }
5900
- fs19.writeFileSync(configFilePath, configContent);
6145
+ fs21.writeFileSync(configFilePath, configContent);
5901
6146
  generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
5902
6147
  s.stop("Projects linked");
5903
6148
  const workspaceFile = `${workspaceName}.code-workspace`;
@@ -5915,8 +6160,8 @@ linked_projects:
5915
6160
  });
5916
6161
  if (shouldExpose && !isCancel8(shouldExpose)) {
5917
6162
  try {
5918
- const { loadMCPConfig: loadMCPConfig2, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5919
- const mcpConfig = loadMCPConfig2();
6163
+ const { loadMCPConfig: loadMCPConfig3, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6164
+ const mcpConfig = loadMCPConfig3();
5920
6165
  for (const project of selectedProjects) {
5921
6166
  setProjectConfig2(mcpConfig, project.name, true, void 0, project.dataPath);
5922
6167
  }
@@ -5932,15 +6177,15 @@ linked_projects:
5932
6177
  init_paths();
5933
6178
  import { confirm as confirm7, spinner as spinner4, note as note8, outro as outro4, cancel as cancel4, isCancel as isCancel9 } from "@clack/prompts";
5934
6179
  import pc10 from "picocolors";
5935
- import * as fs20 from "fs";
5936
- import * as path20 from "path";
6180
+ import * as fs22 from "fs";
6181
+ import * as path22 from "path";
5937
6182
  async function runSyncToGlobalFlow(workspacePath, workspaceName) {
5938
6183
  const localPath = getLocalWorkspacePath(workspacePath);
5939
6184
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
5940
- const globalPath = path20.join(customGlobalPath, "workspaces", workspaceName);
6185
+ const globalPath = path22.join(customGlobalPath, "workspaces", workspaceName);
5941
6186
  const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
5942
6187
  const existingDirs = subdirs.filter(
5943
- (dir) => fs20.existsSync(path20.join(localPath, dir))
6188
+ (dir) => fs22.existsSync(path22.join(localPath, dir))
5944
6189
  );
5945
6190
  if (existingDirs.length === 0) {
5946
6191
  outro4(pc10.yellow("No data found in workspace storage to sync."));
@@ -5966,8 +6211,8 @@ Destination: ${pc10.cyan(globalPath)}`,
5966
6211
  try {
5967
6212
  ensureDir(globalPath);
5968
6213
  for (const dir of existingDirs) {
5969
- const srcDir = path20.join(localPath, dir);
5970
- const destDir = path20.join(globalPath, dir);
6214
+ const srcDir = path22.join(localPath, dir);
6215
+ const destDir = path22.join(globalPath, dir);
5971
6216
  ensureDir(destDir);
5972
6217
  copyDirRecursive(srcDir, destDir);
5973
6218
  }
@@ -5994,29 +6239,74 @@ init_paths();
5994
6239
  init_prompts();
5995
6240
  import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
5996
6241
  import pc11 from "picocolors";
5997
- import * as fs21 from "fs";
5998
- import * as path21 from "path";
5999
- import { stringify as stringify3 } from "yaml";
6242
+ import * as fs23 from "fs";
6243
+ import * as path23 from "path";
6244
+ import { stringify as stringify3, parse } from "yaml";
6000
6245
  init_install();
6246
+ init_drift_service();
6247
+ function backupFile(filePath) {
6248
+ if (!fs23.existsSync(filePath)) return null;
6249
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T")[0] + "-" + Date.now();
6250
+ const backupPath = `${filePath}.${timestamp}.bak`;
6251
+ try {
6252
+ fs23.copyFileSync(filePath, backupPath);
6253
+ return backupPath;
6254
+ } catch (e) {
6255
+ console.error(`Failed to backup ${filePath}:`, e);
6256
+ return null;
6257
+ }
6258
+ }
6259
+ function getPackageVersion2() {
6260
+ try {
6261
+ const agentCoreDir = getAgentCoreDir();
6262
+ const packageJsonPath = path23.join(path23.dirname(agentCoreDir), "package.json");
6263
+ if (fs23.existsSync(packageJsonPath)) {
6264
+ return JSON.parse(fs23.readFileSync(packageJsonPath, "utf8")).version;
6265
+ }
6266
+ } catch (e) {
6267
+ }
6268
+ return "0.0.0";
6269
+ }
6001
6270
  async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
6002
6271
  const s = spinner5();
6003
6272
  s.start("Checking for updates");
6004
6273
  try {
6005
6274
  const agentCoreDir = getAgentCoreDir();
6006
6275
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
6276
+ const runningVersion = getPackageVersion2();
6007
6277
  const mode = currentStorageMode || "global";
6008
6278
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
6009
6279
  const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
6280
+ const configFilePath = getConfigPath(workspacePath);
6281
+ let currentSyncedVersion;
6282
+ if (fs23.existsSync(configFilePath)) {
6283
+ try {
6284
+ const content = fs23.readFileSync(configFilePath, "utf-8");
6285
+ const config = parse(content);
6286
+ currentSyncedVersion = config.last_synced_version;
6287
+ } catch (e) {
6288
+ }
6289
+ }
6290
+ const driftReport = DriftService.checkDrift(dataPaths[0], currentSyncedVersion, runningVersion);
6010
6291
  s.stop("Updates found");
6292
+ if (driftReport.type === "version") {
6293
+ note9(`New version available: ${pc11.green(runningVersion)} (Current synced: ${pc11.dim(currentSyncedVersion || "None")})`, "Update Available");
6294
+ }
6295
+ if (driftReport.modifiedFiles.length > 0) {
6296
+ note9(
6297
+ pc11.yellow(`The following files have been modified and will be backed up before updating:
6298
+ `) + driftReport.modifiedFiles.map((f) => ` \u2022 ${f}`).join("\n"),
6299
+ "Modifications Detected"
6300
+ );
6301
+ }
6011
6302
  const updateTargets = [
6012
6303
  ` \u2022 prompts/ (${prompts.length} agent prompts)`,
6013
6304
  ` \u2022 templates/ (output templates)`,
6014
6305
  ` \u2022 docs/ (documentation)`
6015
6306
  ];
6016
- const configFilePath = getConfigPath(workspacePath);
6017
6307
  const ideTargets = [];
6018
- if (fs21.existsSync(configFilePath)) {
6019
- const configContent = fs21.readFileSync(configFilePath, "utf-8");
6308
+ if (fs23.existsSync(configFilePath)) {
6309
+ const configContent = fs23.readFileSync(configFilePath, "utf-8");
6020
6310
  if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
6021
6311
  if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
6022
6312
  if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
@@ -6042,17 +6332,41 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
6042
6332
  }
6043
6333
  s.start("Updating from package");
6044
6334
  for (const dataPath of dataPaths) {
6045
- copyDirToAllStoragePaths(path21.join(agentCoreDir, "templates"), "templates", [dataPath]);
6046
- copyDirToAllStoragePaths(path21.join(agentCoreDir, "prompts"), "prompts", [dataPath]);
6047
- copyDirToAllStoragePaths(path21.join(agentCoreDir, "docs"), "docs", [dataPath]);
6335
+ const dirs = ["templates", "prompts", "docs"];
6336
+ const updatedFiles = [];
6337
+ for (const dir of dirs) {
6338
+ const srcDir = path23.join(agentCoreDir, dir);
6339
+ if (!fs23.existsSync(srcDir)) continue;
6340
+ const syncFiles = (src, rel) => {
6341
+ const entries = fs23.readdirSync(src, { withFileTypes: true });
6342
+ for (const entry of entries) {
6343
+ const entrySrc = path23.join(src, entry.name);
6344
+ const entryRel = path23.join(rel, entry.name);
6345
+ const entryDest = path23.join(dataPath, entryRel);
6346
+ if (entry.isDirectory()) {
6347
+ ensureDir(entryDest);
6348
+ syncFiles(entrySrc, entryRel);
6349
+ } else {
6350
+ if (driftReport.modifiedFiles.includes(entryRel)) {
6351
+ backupFile(entryDest);
6352
+ }
6353
+ fs23.copyFileSync(entrySrc, entryDest);
6354
+ updatedFiles.push(entryRel);
6355
+ }
6356
+ }
6357
+ };
6358
+ syncFiles(srcDir, dir);
6359
+ }
6360
+ const manifest = DriftService.generateManifest(dataPath, updatedFiles);
6361
+ DriftService.saveManifest(dataPath, manifest);
6048
6362
  }
6049
6363
  const rrceHome = customGlobalPath || getDefaultRRCEHome2();
6050
- ensureDir(path21.join(rrceHome, "templates"));
6051
- ensureDir(path21.join(rrceHome, "docs"));
6052
- copyDirRecursive(path21.join(agentCoreDir, "templates"), path21.join(rrceHome, "templates"));
6053
- copyDirRecursive(path21.join(agentCoreDir, "docs"), path21.join(rrceHome, "docs"));
6054
- if (fs21.existsSync(configFilePath)) {
6055
- const configContent = fs21.readFileSync(configFilePath, "utf-8");
6364
+ ensureDir(path23.join(rrceHome, "templates"));
6365
+ ensureDir(path23.join(rrceHome, "docs"));
6366
+ copyDirRecursive(path23.join(agentCoreDir, "templates"), path23.join(rrceHome, "templates"));
6367
+ copyDirRecursive(path23.join(agentCoreDir, "docs"), path23.join(rrceHome, "docs"));
6368
+ if (fs23.existsSync(configFilePath)) {
6369
+ const configContent = fs23.readFileSync(configFilePath, "utf-8");
6056
6370
  if (configContent.includes("copilot: true")) {
6057
6371
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
6058
6372
  ensureDir(copilotPath);
@@ -6071,6 +6385,29 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
6071
6385
  updateOpenCodeAgents(prompts, mode, primaryDataPath);
6072
6386
  }
6073
6387
  }
6388
+ try {
6389
+ const yaml = parse(configContent);
6390
+ yaml.last_synced_version = runningVersion;
6391
+ fs23.writeFileSync(configFilePath, stringify3(yaml));
6392
+ } catch (e) {
6393
+ console.error("Failed to update config.yaml version:", e);
6394
+ }
6395
+ }
6396
+ const mcpPath = path23.join(rrceHome, "mcp.yaml");
6397
+ if (fs23.existsSync(mcpPath)) {
6398
+ try {
6399
+ const content = fs23.readFileSync(mcpPath, "utf-8");
6400
+ const yaml = parse(content);
6401
+ if (yaml.projects) {
6402
+ const project = yaml.projects.find((p) => p.name === workspaceName);
6403
+ if (project) {
6404
+ project.last_synced_version = runningVersion;
6405
+ fs23.writeFileSync(mcpPath, stringify3(yaml));
6406
+ }
6407
+ }
6408
+ } catch (e) {
6409
+ console.error("Failed to update mcp.yaml version:", e);
6410
+ }
6074
6411
  }
6075
6412
  s.stop("Update complete");
6076
6413
  const summary = [
@@ -6082,6 +6419,9 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
6082
6419
  if (ideTargets.length > 0) {
6083
6420
  summary.push(` \u2713 IDE integrations: ${ideTargets.join(", ")}`);
6084
6421
  }
6422
+ if (driftReport.modifiedFiles.length > 0) {
6423
+ summary.push(` \u2713 ${driftReport.modifiedFiles.length} modified files backed up`);
6424
+ }
6085
6425
  summary.push(
6086
6426
  ``,
6087
6427
  `Your configuration and knowledge files were preserved.`,
@@ -6099,48 +6439,35 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
6099
6439
  function updateOpenCodeAgents(prompts, mode, primaryDataPath) {
6100
6440
  if (mode === "global") {
6101
6441
  try {
6102
- const promptsDir = path21.join(path21.dirname(OPENCODE_CONFIG), "prompts");
6442
+ const promptsDir = path23.join(path23.dirname(OPENCODE_CONFIG), "prompts");
6103
6443
  ensureDir(promptsDir);
6104
- let opencodeConfig = { $schema: "https://opencode.ai/config.json" };
6105
- if (fs21.existsSync(OPENCODE_CONFIG)) {
6106
- opencodeConfig = JSON.parse(fs21.readFileSync(OPENCODE_CONFIG, "utf-8"));
6107
- }
6108
- if (!opencodeConfig.agent) opencodeConfig.agent = {};
6109
- const currentAgentBaseNames = prompts.map((p) => path21.basename(p.filePath, ".md"));
6110
- const currentAgentIds = new Set(currentAgentBaseNames.map((base) => `rrce_${base}`));
6111
- const existingAgentNames = Object.keys(opencodeConfig.agent);
6112
- for (const existingName of existingAgentNames) {
6113
- const isLegacyBaseName = currentAgentBaseNames.includes(existingName);
6114
- const isRrcePrefixed = existingName.startsWith("rrce_");
6115
- const isStaleRrcePrefixed = isRrcePrefixed && !currentAgentIds.has(existingName);
6116
- if (isLegacyBaseName || isStaleRrcePrefixed) {
6117
- delete opencodeConfig.agent[existingName];
6118
- const legacyBaseName = isLegacyBaseName ? existingName : existingName.replace(/^rrce_/, "");
6119
- const oldPromptFile = path21.join(promptsDir, `rrce-${legacyBaseName}.md`);
6120
- if (fs21.existsSync(oldPromptFile)) {
6121
- fs21.unlinkSync(oldPromptFile);
6122
- }
6123
- }
6124
- }
6444
+ const newAgents = {};
6125
6445
  for (const prompt of prompts) {
6126
- const baseName = path21.basename(prompt.filePath, ".md");
6446
+ const baseName = path23.basename(prompt.filePath, ".md");
6127
6447
  const agentId = `rrce_${baseName}`;
6128
6448
  const promptFileName = `rrce-${baseName}.md`;
6129
- const promptFilePath = path21.join(promptsDir, promptFileName);
6130
- fs21.writeFileSync(promptFilePath, prompt.content);
6449
+ const promptFilePath = path23.join(promptsDir, promptFileName);
6450
+ fs23.writeFileSync(promptFilePath, prompt.content);
6131
6451
  const agentConfig = convertToOpenCodeAgent(prompt, true, `./prompts/${promptFileName}`);
6132
- opencodeConfig.agent[agentId] = agentConfig;
6452
+ newAgents[agentId] = agentConfig;
6453
+ }
6454
+ updateOpenCodeConfig(newAgents);
6455
+ if (fs23.existsSync(OPENCODE_CONFIG)) {
6456
+ const config = JSON.parse(fs23.readFileSync(OPENCODE_CONFIG, "utf8"));
6457
+ if (!config.agents) config.agents = {};
6458
+ if (!config.agents.plan) config.agents.plan = {};
6459
+ config.agents.plan.disable = true;
6460
+ fs23.writeFileSync(OPENCODE_CONFIG, JSON.stringify(config, null, 2));
6133
6461
  }
6134
- fs21.writeFileSync(OPENCODE_CONFIG, JSON.stringify(opencodeConfig, null, 2) + "\n");
6135
6462
  } catch (e) {
6136
6463
  console.error("Failed to update global OpenCode config with agents:", e);
6137
6464
  }
6138
6465
  } else {
6139
- const opencodeBaseDir = path21.join(primaryDataPath, ".opencode", "agent");
6466
+ const opencodeBaseDir = path23.join(primaryDataPath, ".opencode", "agent");
6140
6467
  ensureDir(opencodeBaseDir);
6141
6468
  clearDirectory(opencodeBaseDir);
6142
6469
  for (const prompt of prompts) {
6143
- const baseName = path21.basename(prompt.filePath, ".md");
6470
+ const baseName = path23.basename(prompt.filePath, ".md");
6144
6471
  const agentId = `rrce_${baseName}`;
6145
6472
  const agentConfig = convertToOpenCodeAgent(prompt);
6146
6473
  const content = `---
@@ -6150,22 +6477,22 @@ ${stringify3({
6150
6477
  tools: agentConfig.tools
6151
6478
  })}---
6152
6479
  ${agentConfig.prompt}`;
6153
- fs21.writeFileSync(path21.join(opencodeBaseDir, `${agentId}.md`), content);
6480
+ fs23.writeFileSync(path23.join(opencodeBaseDir, `${agentId}.md`), content);
6154
6481
  }
6155
6482
  }
6156
6483
  }
6157
6484
  function clearDirectory(dirPath) {
6158
- if (!fs21.existsSync(dirPath)) return;
6159
- const entries = fs21.readdirSync(dirPath, { withFileTypes: true });
6485
+ if (!fs23.existsSync(dirPath)) return;
6486
+ const entries = fs23.readdirSync(dirPath, { withFileTypes: true });
6160
6487
  for (const entry of entries) {
6161
6488
  if (entry.isFile()) {
6162
- fs21.unlinkSync(path21.join(dirPath, entry.name));
6489
+ fs23.unlinkSync(path23.join(dirPath, entry.name));
6163
6490
  }
6164
6491
  }
6165
6492
  }
6166
6493
  function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
6167
- const globalPath = path21.join(customGlobalPath, "workspaces", workspaceName);
6168
- const workspacePath = path21.join(workspaceRoot, ".rrce-workflow");
6494
+ const globalPath = path23.join(customGlobalPath, "workspaces", workspaceName);
6495
+ const workspacePath = path23.join(workspaceRoot, ".rrce-workflow");
6169
6496
  switch (mode) {
6170
6497
  case "global":
6171
6498
  return [globalPath];
@@ -6179,7 +6506,7 @@ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot,
6179
6506
  // src/commands/wizard/delete-flow.ts
6180
6507
  import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
6181
6508
  import pc12 from "picocolors";
6182
- import * as fs22 from "fs";
6509
+ import * as fs24 from "fs";
6183
6510
  init_detection();
6184
6511
  init_config();
6185
6512
  async function runDeleteGlobalProjectFlow(availableProjects) {
@@ -6223,8 +6550,8 @@ Are you sure?`,
6223
6550
  for (const projectName of projectsToDelete) {
6224
6551
  const project = globalProjects.find((p) => p.name === projectName);
6225
6552
  if (!project) continue;
6226
- if (fs22.existsSync(project.dataPath)) {
6227
- fs22.rmSync(project.dataPath, { recursive: true, force: true });
6553
+ if (fs24.existsSync(project.dataPath)) {
6554
+ fs24.rmSync(project.dataPath, { recursive: true, force: true });
6228
6555
  }
6229
6556
  const newConfig = removeProjectConfig(mcpConfig, projectName);
6230
6557
  configChanged = true;
@@ -6270,11 +6597,11 @@ Workspace: ${pc13.bold(workspaceName)}`,
6270
6597
  workspacePath
6271
6598
  });
6272
6599
  const configFilePath = getConfigPath(workspacePath);
6273
- let isAlreadyConfigured = fs23.existsSync(configFilePath);
6600
+ let isAlreadyConfigured = fs25.existsSync(configFilePath);
6274
6601
  let currentStorageMode = null;
6275
6602
  if (isAlreadyConfigured) {
6276
6603
  try {
6277
- const configContent = fs23.readFileSync(configFilePath, "utf-8");
6604
+ const configContent = fs25.readFileSync(configFilePath, "utf-8");
6278
6605
  const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
6279
6606
  currentStorageMode = modeMatch?.[1] ?? null;
6280
6607
  } catch {
@@ -6291,7 +6618,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
6291
6618
  }
6292
6619
  }
6293
6620
  const localDataPath = getLocalWorkspacePath(workspacePath);
6294
- const hasLocalData = fs23.existsSync(localDataPath);
6621
+ const hasLocalData = fs25.existsSync(localDataPath);
6295
6622
  if (isAlreadyConfigured) {
6296
6623
  const menuOptions = [];
6297
6624
  menuOptions.push({