rrce-workflow 0.2.98 → 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.
Files changed (2) hide show
  1. package/dist/index.js +551 -231
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1266,8 +1266,10 @@ function parseMCPConfig(content) {
1266
1266
  tasks: p.permissions?.tasks ?? DEFAULT_PERMISSIONS.tasks,
1267
1267
  refs: p.permissions?.refs ?? DEFAULT_PERMISSIONS.refs
1268
1268
  },
1269
- semanticSearch: p.semanticSearch
1270
- })) : []
1269
+ semanticSearch: p.semanticSearch,
1270
+ last_synced_version: p.last_synced_version
1271
+ })) : [],
1272
+ last_synced_version: parsed?.last_synced_version
1271
1273
  };
1272
1274
  return config;
1273
1275
  } catch (err) {
@@ -2361,7 +2363,7 @@ var init_dependency_graph = __esm({
2361
2363
  // src/mcp/resources.ts
2362
2364
  import * as fs15 from "fs";
2363
2365
  import * as path17 from "path";
2364
- import * as os2 from "os";
2366
+ import * as os3 from "os";
2365
2367
  import * as crypto from "crypto";
2366
2368
  import ignore from "ignore";
2367
2369
  function resolveProjectPaths(project, pathInput) {
@@ -2903,7 +2905,7 @@ function getContextPreamble() {
2903
2905
  const activeProject = detectActiveProject();
2904
2906
  let contextPreamble = "";
2905
2907
  if (activeProject) {
2906
- 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");
2907
2909
  const workspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
2908
2910
  const rrceData = activeProject.dataPath;
2909
2911
  contextPreamble += `
@@ -2973,7 +2975,7 @@ async function createTask(projectName, taskSlug, taskData) {
2973
2975
  fs15.mkdirSync(path17.join(taskDir, "planning"), { recursive: true });
2974
2976
  fs15.mkdirSync(path17.join(taskDir, "execution"), { recursive: true });
2975
2977
  fs15.mkdirSync(path17.join(taskDir, "docs"), { recursive: true });
2976
- 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");
2977
2979
  const templatePath = path17.join(rrceHome, "templates", "meta.template.json");
2978
2980
  let meta = {
2979
2981
  task_id: crypto.randomUUID(),
@@ -3869,8 +3871,8 @@ Hidden projects: ${projects.length - exposedCount}`,
3869
3871
  }
3870
3872
  async function handleConfigureGlobalPath() {
3871
3873
  const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
3872
- const fs24 = await import("fs");
3873
- const path22 = await import("path");
3874
+ const fs26 = await import("fs");
3875
+ const path24 = await import("path");
3874
3876
  note3(
3875
3877
  `MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
3876
3878
  and coordinate across projects.
@@ -3884,8 +3886,8 @@ locally in each project. MCP needs a central location.`,
3884
3886
  return false;
3885
3887
  }
3886
3888
  try {
3887
- if (!fs24.existsSync(resolvedPath)) {
3888
- fs24.mkdirSync(resolvedPath, { recursive: true });
3889
+ if (!fs26.existsSync(resolvedPath)) {
3890
+ fs26.mkdirSync(resolvedPath, { recursive: true });
3889
3891
  }
3890
3892
  const config = loadMCPConfig();
3891
3893
  saveMCPConfig(config);
@@ -3893,7 +3895,7 @@ locally in each project. MCP needs a central location.`,
3893
3895
  `${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
3894
3896
 
3895
3897
  MCP config will be stored at:
3896
- ${path22.join(resolvedPath, "mcp.yaml")}`,
3898
+ ${path24.join(resolvedPath, "mcp.yaml")}`,
3897
3899
  "Configuration Saved"
3898
3900
  );
3899
3901
  return true;
@@ -4218,10 +4220,191 @@ var init_tasks_fs = __esm({
4218
4220
  }
4219
4221
  });
4220
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
+
4221
4404
  // src/mcp/ui/ProjectsView.tsx
4222
- 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";
4223
4406
  import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
4224
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
4407
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
4225
4408
  function nextStatus(current) {
4226
4409
  const idx = STATUS_CYCLE.indexOf(current || "");
4227
4410
  if (idx === -1) return STATUS_CYCLE[0];
@@ -4230,9 +4413,14 @@ function nextStatus(current) {
4230
4413
  function projectKey(p) {
4231
4414
  return p.sourcePath ?? p.path;
4232
4415
  }
4233
- function formatProjectLabel(p) {
4416
+ function formatProjectLabel(p, drift) {
4234
4417
  const root = p.sourcePath ?? p.path;
4235
- 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;
4236
4424
  }
4237
4425
  var STATUS_CYCLE, ProjectsView;
4238
4426
  var init_ProjectsView = __esm({
@@ -4241,15 +4429,17 @@ var init_ProjectsView = __esm({
4241
4429
  init_SimpleSelect();
4242
4430
  init_config();
4243
4431
  init_tasks_fs();
4432
+ init_ConfigContext();
4244
4433
  STATUS_CYCLE = ["pending", "in_progress", "blocked", "complete"];
4245
4434
  ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange }) => {
4246
- const [config, setConfig] = useState2(initialConfig);
4247
- const [mode, setMode] = useState2("expose");
4248
- const [expanded, setExpanded] = useState2(() => /* @__PURE__ */ new Set());
4249
- const [selectedIndex, setSelectedIndex] = useState2(0);
4250
- const [taskCache, setTaskCache] = useState2({});
4251
- const [errorLine, setErrorLine] = useState2(null);
4252
- 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(() => {
4253
4443
  return [...allProjects].sort((a, b) => {
4254
4444
  const byName = a.name.localeCompare(b.name);
4255
4445
  if (byName !== 0) return byName;
@@ -4273,6 +4463,10 @@ var init_ProjectsView = __esm({
4273
4463
  setMode((prev) => prev === "expose" ? "tasks" : "expose");
4274
4464
  return;
4275
4465
  }
4466
+ if (input === "u") {
4467
+ checkAllDrift();
4468
+ return;
4469
+ }
4276
4470
  if (mode === "expose") {
4277
4471
  if (input === "a") {
4278
4472
  const newConfig = {
@@ -4338,27 +4532,28 @@ var init_ProjectsView = __esm({
4338
4532
  }
4339
4533
  }
4340
4534
  });
4341
- useEffect2(() => {
4535
+ useEffect3(() => {
4342
4536
  setSelectedIndex((prev) => {
4343
4537
  if (flattenedRows.length === 0) return 0;
4344
4538
  return Math.min(prev, flattenedRows.length - 1);
4345
4539
  });
4346
4540
  }, [mode, allProjects, expanded, taskCache]);
4347
- const projectItems = useMemo2(() => {
4541
+ const projectItems = useMemo3(() => {
4348
4542
  return allProjects.map((p) => {
4349
4543
  const projectConfig = config.projects.find(
4350
4544
  (c) => c.path && c.path === p.path || p.source === "global" && c.name === p.name || !c.path && c.name === p.name
4351
4545
  );
4352
4546
  const isExposed = projectConfig ? projectConfig.expose : config.defaults.includeNew;
4547
+ const drift = driftReports[p.path];
4353
4548
  return {
4354
- label: formatProjectLabel(p),
4549
+ label: formatProjectLabel(p, drift),
4355
4550
  value: p.path,
4356
4551
  key: p.path,
4357
4552
  exposed: isExposed
4358
4553
  };
4359
4554
  });
4360
- }, [allProjects, config]);
4361
- const initialSelected = useMemo2(() => {
4555
+ }, [allProjects, config, driftReports]);
4556
+ const initialSelected = useMemo3(() => {
4362
4557
  return projectItems.filter((p) => p.exposed).map((p) => p.value);
4363
4558
  }, [projectItems]);
4364
4559
  const handleSubmit = (selectedIds) => {
@@ -4382,7 +4577,7 @@ var init_ProjectsView = __esm({
4382
4577
  setConfig(newConfig);
4383
4578
  onConfigChange?.();
4384
4579
  };
4385
- const flattenedRows = useMemo2(() => {
4580
+ const flattenedRows = useMemo3(() => {
4386
4581
  const rows = [];
4387
4582
  for (const p of sortedProjects) {
4388
4583
  rows.push({ kind: "project", project: p });
@@ -4403,15 +4598,15 @@ var init_ProjectsView = __esm({
4403
4598
  if (mode === "expose") {
4404
4599
  return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan", flexGrow: 1, children: [
4405
4600
  /* @__PURE__ */ jsxs3(Box4, { justifyContent: "space-between", children: [
4406
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: " Projects (Expose Mode) " }),
4601
+ /* @__PURE__ */ jsx5(Text4, { bold: true, color: "cyan", children: " Projects (Expose Mode) " }),
4407
4602
  /* @__PURE__ */ jsxs3(Box4, { children: [
4408
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Auto-expose new: " }),
4409
- /* @__PURE__ */ jsx4(Text4, { color: config.defaults.includeNew ? "green" : "red", children: config.defaults.includeNew ? "ON" : "OFF" }),
4410
- /* @__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)" })
4411
4606
  ] })
4412
4607
  ] }),
4413
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: " Space toggles, Enter saves. Press 't' to switch to Tasks Mode." }),
4414
- /* @__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(
4415
4610
  SimpleSelect,
4416
4611
  {
4417
4612
  message: "",
@@ -4430,62 +4625,69 @@ var init_ProjectsView = __esm({
4430
4625
  }
4431
4626
  return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan", flexGrow: 1, children: [
4432
4627
  /* @__PURE__ */ jsxs3(Box4, { justifyContent: "space-between", children: [
4433
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: " Projects (Tasks Mode) " }),
4434
- /* @__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" })
4435
4630
  ] }),
4436
- 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 }) }),
4437
4632
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "row", flexGrow: 1, children: [
4438
4633
  /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", width: "55%", children: [
4439
- 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) => {
4440
4635
  const isSel = idx === selectedIndex;
4441
4636
  if (row.kind === "project") {
4442
4637
  const k = projectKey(row.project);
4443
4638
  const isOpen = expanded.has(k);
4444
4639
  const count = (taskCache[k] || []).length;
4445
- return /* @__PURE__ */ jsxs3(Box4, { children: [
4446
- /* @__PURE__ */ jsx4(Text4, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
4447
- /* @__PURE__ */ jsxs3(Text4, { color: isSel ? "cyan" : "white", children: [
4448
- isOpen ? "\u25BE " : "\u25B8 ",
4449
- 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
+ ] })
4450
4652
  ] }),
4451
- /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
4452
- " ",
4453
- count > 0 ? ` (tasks: ${count})` : ""
4454
- ] })
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
+ ] }) })
4455
4657
  ] }, `p:${k}`);
4456
4658
  }
4457
4659
  const taskLabel = row.task.title || row.task.task_slug;
4458
4660
  const status = row.task.status || "";
4459
4661
  return /* @__PURE__ */ jsxs3(Box4, { children: [
4460
- /* @__PURE__ */ jsx4(Text4, { color: isSel ? "cyan" : "white", children: isSel ? "> " : " " }),
4461
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: " - " }),
4462
- /* @__PURE__ */ jsx4(Text4, { color: isSel ? "cyan" : "white", children: taskLabel }),
4463
- 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}]` })
4464
4666
  ] }, `t:${projectKey(row.project)}:${row.task.task_slug}`);
4465
4667
  }),
4466
- /* @__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" }) })
4467
4669
  ] }),
4468
- /* @__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: [
4469
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: selectedTask.title || selectedTask.task_slug }),
4470
- 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 }),
4471
4673
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
4472
4674
  /* @__PURE__ */ jsxs3(Text4, { children: [
4473
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Status: " }),
4474
- /* @__PURE__ */ jsx4(Text4, { children: selectedTask.status || "unknown" })
4675
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Status: " }),
4676
+ /* @__PURE__ */ jsx5(Text4, { children: selectedTask.status || "unknown" })
4475
4677
  ] }),
4476
4678
  /* @__PURE__ */ jsxs3(Text4, { children: [
4477
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Updated: " }),
4478
- /* @__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" })
4479
4681
  ] }),
4480
4682
  /* @__PURE__ */ jsxs3(Text4, { children: [
4481
- /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "Tags: " }),
4482
- /* @__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" })
4483
4685
  ] })
4484
4686
  ] }),
4485
4687
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
4486
- /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Checklist" }),
4487
- (selectedTask.checklist || []).length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: "dim", children: "\u2014" }) : (selectedTask.checklist || []).slice(0, 12).map((c, i) => /* @__PURE__ */ jsxs3(Text4, { children: [
4488
- /* @__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: "- " }),
4489
4691
  c.label || c.id || "item",
4490
4692
  " ",
4491
4693
  /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
@@ -4496,8 +4698,8 @@ var init_ProjectsView = __esm({
4496
4698
  ] }, c.id || i))
4497
4699
  ] }),
4498
4700
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
4499
- /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Agents" }),
4500
- !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: [
4501
4703
  /* @__PURE__ */ jsxs3(Text4, { color: "dim", children: [
4502
4704
  "- ",
4503
4705
  agent,
@@ -4515,9 +4717,9 @@ var init_ProjectsView = __esm({
4515
4717
  });
4516
4718
 
4517
4719
  // src/mcp/ui/components/InstallWizard.tsx
4518
- import { useState as useState3 } from "react";
4720
+ import { useState as useState4 } from "react";
4519
4721
  import { Box as Box5, Text as Text5 } from "ink";
4520
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
4722
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
4521
4723
  var InstallWizard;
4522
4724
  var init_InstallWizard = __esm({
4523
4725
  "src/mcp/ui/components/InstallWizard.tsx"() {
@@ -4525,8 +4727,8 @@ var init_InstallWizard = __esm({
4525
4727
  init_SimpleSelect();
4526
4728
  init_install();
4527
4729
  InstallWizard = ({ workspacePath, onComplete, onCancel }) => {
4528
- const [status, setStatus] = useState3(checkInstallStatus(workspacePath));
4529
- const [message, setMessage] = useState3("");
4730
+ const [status, setStatus] = useState4(checkInstallStatus(workspacePath));
4731
+ const [message, setMessage] = useState4("");
4530
4732
  const options = [
4531
4733
  {
4532
4734
  value: "opencode",
@@ -4577,8 +4779,8 @@ var init_InstallWizard = __esm({
4577
4779
  }, 2e3);
4578
4780
  };
4579
4781
  return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
4580
- message && /* @__PURE__ */ jsx5(Text5, { color: "green", children: message }),
4581
- /* @__PURE__ */ jsx5(
4782
+ message && /* @__PURE__ */ jsx6(Text5, { color: "green", children: message }),
4783
+ /* @__PURE__ */ jsx6(
4582
4784
  SimpleSelect,
4583
4785
  {
4584
4786
  message: "Select integrations to install:",
@@ -4603,7 +4805,7 @@ var init_InstallWizard = __esm({
4603
4805
  // src/mcp/ui/InstallView.tsx
4604
4806
  import "react";
4605
4807
  import { Box as Box6, Text as Text6 } from "ink";
4606
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4808
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
4607
4809
  var InstallView;
4608
4810
  var init_InstallView = __esm({
4609
4811
  "src/mcp/ui/InstallView.tsx"() {
@@ -4613,9 +4815,9 @@ var init_InstallView = __esm({
4613
4815
  InstallView = () => {
4614
4816
  const workspacePath = detectWorkspaceRoot();
4615
4817
  return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "magenta", flexGrow: 1, children: [
4616
- /* @__PURE__ */ jsx6(Text6, { bold: true, color: "magenta", children: " Installation & Configuration " }),
4617
- /* @__PURE__ */ jsx6(Text6, { color: "dim", children: " Configure IDE integrations for OpenCode, VSCode, Claude, and Antigravity." }),
4618
- /* @__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(
4619
4821
  InstallWizard,
4620
4822
  {
4621
4823
  workspacePath,
@@ -4633,7 +4835,7 @@ var init_InstallView = __esm({
4633
4835
  // src/mcp/ui/LogViewer.tsx
4634
4836
  import "react";
4635
4837
  import { Box as Box7, Text as Text7 } from "ink";
4636
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4838
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
4637
4839
  var LogViewer;
4638
4840
  var init_LogViewer = __esm({
4639
4841
  "src/mcp/ui/LogViewer.tsx"() {
@@ -4643,16 +4845,16 @@ var init_LogViewer = __esm({
4643
4845
  const emptyLines = Math.max(0, height - visibleLogs.length);
4644
4846
  const padding = Array(emptyLines).fill("");
4645
4847
  const formatLog = (log) => {
4646
- if (log.includes("[RAG]")) return /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: log });
4647
- if (log.includes("[ERROR]")) return /* @__PURE__ */ jsx7(Text7, { color: "red", children: log });
4648
- if (log.includes("[WARN]")) return /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: log });
4649
- if (log.includes("[INFO]")) return /* @__PURE__ */ jsx7(Text7, { color: "green", children: log });
4650
- if (log.includes("Success")) return /* @__PURE__ */ jsx7(Text7, { color: "green", children: log });
4651
- 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 });
4652
4854
  };
4653
4855
  return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "dim", paddingX: 1, height: height + 2, flexGrow: 1, children: [
4654
- padding.map((_, i) => /* @__PURE__ */ jsx7(Text7, { children: " " }, `empty-${i}`)),
4655
- 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}`))
4656
4858
  ] });
4657
4859
  };
4658
4860
  }
@@ -4661,35 +4863,40 @@ var init_LogViewer = __esm({
4661
4863
  // src/mcp/ui/StatusBoard.tsx
4662
4864
  import "react";
4663
4865
  import { Box as Box8, Text as Text8 } from "ink";
4664
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4866
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
4665
4867
  var StatusBoard;
4666
4868
  var init_StatusBoard = __esm({
4667
4869
  "src/mcp/ui/StatusBoard.tsx"() {
4668
4870
  "use strict";
4669
- StatusBoard = ({ exposedLabel, port, pid, running }) => {
4670
- return /* @__PURE__ */ jsx8(Box8, { borderStyle: "single", borderColor: "cyan", paddingX: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs7(Text8, { children: [
4671
- 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" }),
4672
4874
  " ",
4673
4875
  "\u2502",
4674
4876
  " \u{1F4CB} ",
4675
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: exposedLabel }),
4877
+ /* @__PURE__ */ jsx9(Text8, { color: "yellow", children: exposedLabel }),
4676
4878
  " ",
4677
4879
  "\u2502",
4678
4880
  " Port: ",
4679
- /* @__PURE__ */ jsx8(Text8, { color: "green", children: port }),
4881
+ /* @__PURE__ */ jsx9(Text8, { color: "green", children: port }),
4680
4882
  " ",
4681
4883
  "\u2502",
4682
4884
  " PID: ",
4683
- /* @__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
+ ] })
4684
4891
  ] }) });
4685
4892
  };
4686
4893
  }
4687
4894
  });
4688
4895
 
4689
4896
  // src/mcp/ui/IndexingStatus.tsx
4690
- import { useState as useState4, useEffect as useEffect4 } from "react";
4897
+ import { useState as useState5, useEffect as useEffect5 } from "react";
4691
4898
  import { Box as Box9, Text as Text9 } from "ink";
4692
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4899
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
4693
4900
  var IndexingStatus;
4694
4901
  var init_IndexingStatus = __esm({
4695
4902
  "src/mcp/ui/IndexingStatus.tsx"() {
@@ -4699,9 +4906,9 @@ var init_IndexingStatus = __esm({
4699
4906
  init_resources();
4700
4907
  init_config_utils();
4701
4908
  IndexingStatus = ({ projects, config }) => {
4702
- const [stats, setStats] = useState4([]);
4703
- const [loading, setLoading] = useState4(true);
4704
- useEffect4(() => {
4909
+ const [stats, setStats] = useState5([]);
4910
+ const [loading, setLoading] = useState5(true);
4911
+ useEffect5(() => {
4705
4912
  const fetchStats = async () => {
4706
4913
  const newStats = [];
4707
4914
  for (const project of projects) {
@@ -4763,27 +4970,27 @@ var init_IndexingStatus = __esm({
4763
4970
  return () => clearInterval(interval);
4764
4971
  }, [projects, config]);
4765
4972
  if (loading && stats.length === 0) {
4766
- return /* @__PURE__ */ jsx9(Text9, { children: "Loading indexing status..." });
4973
+ return /* @__PURE__ */ jsx10(Text9, { children: "Loading indexing status..." });
4767
4974
  }
4768
4975
  return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "blue", flexGrow: 1, children: [
4769
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: "blue", children: " RAG Indexing Status " }),
4976
+ /* @__PURE__ */ jsx10(Text9, { bold: true, color: "blue", children: " RAG Indexing Status " }),
4770
4977
  /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
4771
4978
  /* @__PURE__ */ jsxs8(Box9, { children: [
4772
- /* @__PURE__ */ jsx9(Box9, { width: 25, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Project" }) }),
4773
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Status" }) }),
4774
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "State" }) }),
4775
- /* @__PURE__ */ jsx9(Box9, { width: 18, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Progress" }) }),
4776
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Indexed Files" }) }),
4777
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { underline: true, children: "Total Chunks" }) }),
4778
- /* @__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" }) })
4779
4986
  ] }),
4780
- stats.length === 0 ? /* @__PURE__ */ jsx9(Text9, { color: "dim", children: "No exposed projects found." }) : stats.map((s) => /* @__PURE__ */ jsxs8(Box9, { marginTop: 0, children: [
4781
- /* @__PURE__ */ jsx9(Box9, { width: 25, children: /* @__PURE__ */ jsx9(Text9, { color: "white", children: s.projectName }) }),
4782
- /* @__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" }) }),
4783
- /* @__PURE__ */ jsx9(Box9, { width: 18, children: /* @__PURE__ */ jsx9(Text9, { children: s.state === "running" ? `${s.itemsDone}/${s.itemsTotal ?? "?"}` : "-" }) }),
4784
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { children: s.enabled ? s.totalFiles : "-" }) }),
4785
- /* @__PURE__ */ jsx9(Box9, { width: 15, children: /* @__PURE__ */ jsx9(Text9, { children: s.enabled ? s.totalChunks : "-" }) }),
4786
- /* @__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() : "-" }) })
4787
4994
  ] }, s.projectName))
4788
4995
  ] })
4789
4996
  ] });
@@ -4794,7 +5001,7 @@ var init_IndexingStatus = __esm({
4794
5001
  // src/mcp/ui/components/TabBar.tsx
4795
5002
  import "react";
4796
5003
  import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
4797
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
5004
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
4798
5005
  var TabBar;
4799
5006
  var init_TabBar = __esm({
4800
5007
  "src/mcp/ui/components/TabBar.tsx"() {
@@ -4824,7 +5031,7 @@ var init_TabBar = __esm({
4824
5031
  return /* @__PURE__ */ jsxs9(Box10, { borderStyle: "single", paddingX: 1, borderColor: "gray", children: [
4825
5032
  tabs.map((tab, index) => {
4826
5033
  const isActive = tab.id === activeTab;
4827
- return /* @__PURE__ */ jsx10(Box10, { marginRight: 2, children: /* @__PURE__ */ jsx10(
5034
+ return /* @__PURE__ */ jsx11(Box10, { marginRight: 2, children: /* @__PURE__ */ jsx11(
4828
5035
  Text10,
4829
5036
  {
4830
5037
  color: isActive ? "cyan" : "white",
@@ -4834,8 +5041,8 @@ var init_TabBar = __esm({
4834
5041
  }
4835
5042
  ) }, tab.id);
4836
5043
  }),
4837
- /* @__PURE__ */ jsx10(Box10, { flexGrow: 1 }),
4838
- /* @__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" })
4839
5046
  ] });
4840
5047
  };
4841
5048
  }
@@ -4846,10 +5053,10 @@ var App_exports = {};
4846
5053
  __export(App_exports, {
4847
5054
  App: () => App
4848
5055
  });
4849
- 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";
4850
5057
  import { Box as Box11, useInput as useInput4, useApp } from "ink";
4851
- import fs18 from "fs";
4852
- 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";
4853
5060
  var App;
4854
5061
  var init_App = __esm({
4855
5062
  "src/mcp/ui/App.tsx"() {
@@ -4869,32 +5076,28 @@ var init_App = __esm({
4869
5076
  init_resources();
4870
5077
  init_install();
4871
5078
  init_paths();
5079
+ init_ConfigContext();
4872
5080
  App = ({ onExit, initialPort }) => {
4873
5081
  const { exit } = useApp();
4874
- const [activeTab, setActiveTab] = useState5("overview");
4875
- const [logs, setLogs] = useState5([]);
4876
- 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({
4877
5086
  port: initialPort,
4878
5087
  pid: process.pid,
4879
5088
  running: false
4880
5089
  });
4881
- const [config, setConfig] = useState5(() => loadMCPConfig());
4882
- const [projects, setProjects] = useState5(() => scanForProjects());
4883
- const refreshData = useCallback(() => {
4884
- setConfig(loadMCPConfig());
4885
- setProjects(scanForProjects());
4886
- }, []);
4887
- const exposedProjects = useMemo3(
4888
- () => projects.filter((p) => isProjectExposed(config, p.name, p.path)),
4889
- [projects, config]
4890
- );
4891
- const isRAGEnabled = useMemo3(() => {
5090
+ const isRAGEnabled = useMemo4(() => {
4892
5091
  return exposedProjects.some((p) => {
4893
5092
  const cfg = findProjectConfig(config, { name: p.name, path: p.path });
4894
5093
  return cfg?.semanticSearch?.enabled || p.semanticSearchEnabled;
4895
5094
  });
4896
5095
  }, [exposedProjects, config]);
4897
- const tabs = useMemo3(() => {
5096
+ const hasAnyDrift = useMemo4(
5097
+ () => Object.values(driftReports).some((r) => r.hasDrift),
5098
+ [driftReports]
5099
+ );
5100
+ const tabs = useMemo4(() => {
4898
5101
  const baseTabs = [
4899
5102
  { id: "overview", label: "Overview" },
4900
5103
  { id: "logs", label: "Logs" },
@@ -4914,7 +5117,7 @@ var init_App = __esm({
4914
5117
  installStatus.vscodeGlobal,
4915
5118
  installStatus.vscodeWorkspace
4916
5119
  ].filter(Boolean).length;
4917
- useEffect5(() => {
5120
+ useEffect6(() => {
4918
5121
  const start = async () => {
4919
5122
  const status = getMCPServerStatus();
4920
5123
  if (!status.running) {
@@ -4930,21 +5133,21 @@ var init_App = __esm({
4930
5133
  };
4931
5134
  start();
4932
5135
  }, []);
4933
- useEffect5(() => {
5136
+ useEffect6(() => {
4934
5137
  const logPath = getLogFilePath();
4935
5138
  let lastSize = 0;
4936
- if (fs18.existsSync(logPath)) {
4937
- const stats = fs18.statSync(logPath);
5139
+ if (fs20.existsSync(logPath)) {
5140
+ const stats = fs20.statSync(logPath);
4938
5141
  lastSize = stats.size;
4939
5142
  }
4940
5143
  const interval = setInterval(() => {
4941
- if (fs18.existsSync(logPath)) {
4942
- const stats = fs18.statSync(logPath);
5144
+ if (fs20.existsSync(logPath)) {
5145
+ const stats = fs20.statSync(logPath);
4943
5146
  if (stats.size > lastSize) {
4944
5147
  const buffer = Buffer.alloc(stats.size - lastSize);
4945
- const fd = fs18.openSync(logPath, "r");
4946
- fs18.readSync(fd, buffer, 0, buffer.length, lastSize);
4947
- fs18.closeSync(fd);
5148
+ const fd = fs20.openSync(logPath, "r");
5149
+ fs20.readSync(fd, buffer, 0, buffer.length, lastSize);
5150
+ fs20.closeSync(fd);
4948
5151
  const newContent = buffer.toString("utf-8");
4949
5152
  const newLines = newContent.split("\n").filter((l) => l.trim());
4950
5153
  setLogs((prev) => {
@@ -4978,13 +5181,13 @@ var init_App = __esm({
4978
5181
  });
4979
5182
  const termHeight = process.stdout.rows || 24;
4980
5183
  const contentHeight = termHeight - 8;
4981
- const handleConfigChange = useCallback(() => {
5184
+ const handleConfigChange = useCallback3(() => {
4982
5185
  refreshData();
4983
5186
  }, [refreshData]);
4984
5187
  return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", padding: 0, height: termHeight, children: [
4985
- /* @__PURE__ */ jsx11(TabBar, { tabs, activeTab, onChange: setActiveTab }),
5188
+ /* @__PURE__ */ jsx12(TabBar, { tabs, activeTab, onChange: setActiveTab }),
4986
5189
  /* @__PURE__ */ jsxs10(Box11, { marginTop: 1, flexGrow: 1, children: [
4987
- activeTab === "overview" && /* @__PURE__ */ jsx11(
5190
+ activeTab === "overview" && /* @__PURE__ */ jsx12(
4988
5191
  Overview,
4989
5192
  {
4990
5193
  serverStatus: serverInfo,
@@ -4995,18 +5198,19 @@ var init_App = __esm({
4995
5198
  }
4996
5199
  }
4997
5200
  ),
4998
- activeTab === "projects" && /* @__PURE__ */ jsx11(ProjectsView, { config, projects, onConfigChange: handleConfigChange }),
4999
- activeTab === "indexing" && /* @__PURE__ */ jsx11(IndexingStatus, { config, projects: exposedProjects }),
5000
- activeTab === "install" && /* @__PURE__ */ jsx11(InstallView, {}),
5001
- 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 })
5002
5205
  ] }),
5003
- /* @__PURE__ */ jsx11(Box11, { marginTop: 0, children: /* @__PURE__ */ jsx11(
5206
+ /* @__PURE__ */ jsx12(Box11, { marginTop: 0, children: /* @__PURE__ */ jsx12(
5004
5207
  StatusBoard,
5005
5208
  {
5006
5209
  exposedLabel: `${exposedProjects.length} / ${projects.length} projects`,
5007
5210
  port: serverInfo.port,
5008
5211
  pid: serverInfo.pid,
5009
- running: serverInfo.running
5212
+ running: serverInfo.running,
5213
+ hasDrift: hasAnyDrift
5010
5214
  }
5011
5215
  ) })
5012
5216
  ] });
@@ -5017,9 +5221,10 @@ var init_App = __esm({
5017
5221
  // src/mcp/commands/start.ts
5018
5222
  import { confirm as confirm3, isCancel as isCancel5, text } from "@clack/prompts";
5019
5223
  async function handleStartServer() {
5020
- const React12 = await import("react");
5224
+ const React13 = await import("react");
5021
5225
  const { render } = await import("ink");
5022
5226
  const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
5227
+ const { ConfigProvider: ConfigProvider2 } = await Promise.resolve().then(() => (init_ConfigContext(), ConfigContext_exports));
5023
5228
  const config = loadMCPConfig();
5024
5229
  const projects = scanForProjects();
5025
5230
  const exposedProjects = projects.filter((p) => {
@@ -5058,13 +5263,20 @@ async function handleStartServer() {
5058
5263
  }
5059
5264
  }
5060
5265
  process.stdin.resume();
5061
- const app = render(React12.createElement(App2, {
5062
- initialPort,
5063
- 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
5064
5278
  }
5065
- }), {
5066
- exitOnCtrlC: false
5067
- });
5279
+ );
5068
5280
  await app.waitUntilExit();
5069
5281
  console.clear();
5070
5282
  }
@@ -5155,7 +5367,7 @@ var init_mcp = __esm({
5155
5367
  // src/commands/wizard/index.ts
5156
5368
  import { intro as intro2, select as select5, spinner as spinner7, note as note11, outro as outro7, isCancel as isCancel12 } from "@clack/prompts";
5157
5369
  import pc13 from "picocolors";
5158
- import * as fs23 from "fs";
5370
+ import * as fs25 from "fs";
5159
5371
 
5160
5372
  // src/lib/git.ts
5161
5373
  import { execSync } from "child_process";
@@ -5303,6 +5515,7 @@ import { stringify as stringify2 } from "yaml";
5303
5515
  init_paths();
5304
5516
  import * as fs7 from "fs";
5305
5517
  import * as path8 from "path";
5518
+ import * as os2 from "os";
5306
5519
  import "yaml";
5307
5520
  function copyPromptsToDir(prompts, targetDir, extension) {
5308
5521
  for (const prompt of prompts) {
@@ -5313,6 +5526,33 @@ function copyPromptsToDir(prompts, targetDir, extension) {
5313
5526
  fs7.writeFileSync(targetPath, content);
5314
5527
  }
5315
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
+ }
5316
5556
  function convertToOpenCodeAgent(prompt, useFileReference = false, promptFilePath) {
5317
5557
  const { frontmatter, content } = prompt;
5318
5558
  const tools = {};
@@ -5570,8 +5810,8 @@ linked_projects:
5570
5810
  async function registerWithMCP(config, workspacePath, workspaceName) {
5571
5811
  if (!config.exposeToMCP) return;
5572
5812
  try {
5573
- const { loadMCPConfig: loadMCPConfig2, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5574
- const mcpConfig = loadMCPConfig2();
5813
+ const { loadMCPConfig: loadMCPConfig3, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5814
+ const mcpConfig = loadMCPConfig3();
5575
5815
  setProjectConfig2(
5576
5816
  mcpConfig,
5577
5817
  workspaceName,
@@ -5836,7 +6076,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
5836
6076
  init_paths();
5837
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";
5838
6078
  import pc9 from "picocolors";
5839
- import * as fs19 from "fs";
6079
+ import * as fs21 from "fs";
5840
6080
  init_detection();
5841
6081
  async function runLinkProjectsFlow(workspacePath, workspaceName) {
5842
6082
  const projects = scanForProjects({
@@ -5875,7 +6115,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
5875
6115
  const s = spinner3();
5876
6116
  s.start("Linking projects");
5877
6117
  const configFilePath = getConfigPath(workspacePath);
5878
- let configContent = fs19.readFileSync(configFilePath, "utf-8");
6118
+ let configContent = fs21.readFileSync(configFilePath, "utf-8");
5879
6119
  if (configContent.includes("linked_projects:")) {
5880
6120
  const lines = configContent.split("\n");
5881
6121
  const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
@@ -5902,7 +6142,7 @@ linked_projects:
5902
6142
  `;
5903
6143
  });
5904
6144
  }
5905
- fs19.writeFileSync(configFilePath, configContent);
6145
+ fs21.writeFileSync(configFilePath, configContent);
5906
6146
  generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
5907
6147
  s.stop("Projects linked");
5908
6148
  const workspaceFile = `${workspaceName}.code-workspace`;
@@ -5920,8 +6160,8 @@ linked_projects:
5920
6160
  });
5921
6161
  if (shouldExpose && !isCancel8(shouldExpose)) {
5922
6162
  try {
5923
- const { loadMCPConfig: loadMCPConfig2, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5924
- const mcpConfig = loadMCPConfig2();
6163
+ const { loadMCPConfig: loadMCPConfig3, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6164
+ const mcpConfig = loadMCPConfig3();
5925
6165
  for (const project of selectedProjects) {
5926
6166
  setProjectConfig2(mcpConfig, project.name, true, void 0, project.dataPath);
5927
6167
  }
@@ -5937,15 +6177,15 @@ linked_projects:
5937
6177
  init_paths();
5938
6178
  import { confirm as confirm7, spinner as spinner4, note as note8, outro as outro4, cancel as cancel4, isCancel as isCancel9 } from "@clack/prompts";
5939
6179
  import pc10 from "picocolors";
5940
- import * as fs20 from "fs";
5941
- import * as path20 from "path";
6180
+ import * as fs22 from "fs";
6181
+ import * as path22 from "path";
5942
6182
  async function runSyncToGlobalFlow(workspacePath, workspaceName) {
5943
6183
  const localPath = getLocalWorkspacePath(workspacePath);
5944
6184
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
5945
- const globalPath = path20.join(customGlobalPath, "workspaces", workspaceName);
6185
+ const globalPath = path22.join(customGlobalPath, "workspaces", workspaceName);
5946
6186
  const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
5947
6187
  const existingDirs = subdirs.filter(
5948
- (dir) => fs20.existsSync(path20.join(localPath, dir))
6188
+ (dir) => fs22.existsSync(path22.join(localPath, dir))
5949
6189
  );
5950
6190
  if (existingDirs.length === 0) {
5951
6191
  outro4(pc10.yellow("No data found in workspace storage to sync."));
@@ -5971,8 +6211,8 @@ Destination: ${pc10.cyan(globalPath)}`,
5971
6211
  try {
5972
6212
  ensureDir(globalPath);
5973
6213
  for (const dir of existingDirs) {
5974
- const srcDir = path20.join(localPath, dir);
5975
- const destDir = path20.join(globalPath, dir);
6214
+ const srcDir = path22.join(localPath, dir);
6215
+ const destDir = path22.join(globalPath, dir);
5976
6216
  ensureDir(destDir);
5977
6217
  copyDirRecursive(srcDir, destDir);
5978
6218
  }
@@ -5999,29 +6239,74 @@ init_paths();
5999
6239
  init_prompts();
6000
6240
  import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
6001
6241
  import pc11 from "picocolors";
6002
- import * as fs21 from "fs";
6003
- import * as path21 from "path";
6004
- 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";
6005
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
+ }
6006
6270
  async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
6007
6271
  const s = spinner5();
6008
6272
  s.start("Checking for updates");
6009
6273
  try {
6010
6274
  const agentCoreDir = getAgentCoreDir();
6011
6275
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
6276
+ const runningVersion = getPackageVersion2();
6012
6277
  const mode = currentStorageMode || "global";
6013
6278
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
6014
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);
6015
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
+ }
6016
6302
  const updateTargets = [
6017
6303
  ` \u2022 prompts/ (${prompts.length} agent prompts)`,
6018
6304
  ` \u2022 templates/ (output templates)`,
6019
6305
  ` \u2022 docs/ (documentation)`
6020
6306
  ];
6021
- const configFilePath = getConfigPath(workspacePath);
6022
6307
  const ideTargets = [];
6023
- if (fs21.existsSync(configFilePath)) {
6024
- const configContent = fs21.readFileSync(configFilePath, "utf-8");
6308
+ if (fs23.existsSync(configFilePath)) {
6309
+ const configContent = fs23.readFileSync(configFilePath, "utf-8");
6025
6310
  if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
6026
6311
  if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
6027
6312
  if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
@@ -6047,17 +6332,41 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
6047
6332
  }
6048
6333
  s.start("Updating from package");
6049
6334
  for (const dataPath of dataPaths) {
6050
- copyDirToAllStoragePaths(path21.join(agentCoreDir, "templates"), "templates", [dataPath]);
6051
- copyDirToAllStoragePaths(path21.join(agentCoreDir, "prompts"), "prompts", [dataPath]);
6052
- 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);
6053
6362
  }
6054
6363
  const rrceHome = customGlobalPath || getDefaultRRCEHome2();
6055
- ensureDir(path21.join(rrceHome, "templates"));
6056
- ensureDir(path21.join(rrceHome, "docs"));
6057
- copyDirRecursive(path21.join(agentCoreDir, "templates"), path21.join(rrceHome, "templates"));
6058
- copyDirRecursive(path21.join(agentCoreDir, "docs"), path21.join(rrceHome, "docs"));
6059
- if (fs21.existsSync(configFilePath)) {
6060
- 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");
6061
6370
  if (configContent.includes("copilot: true")) {
6062
6371
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
6063
6372
  ensureDir(copilotPath);
@@ -6076,6 +6385,29 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
6076
6385
  updateOpenCodeAgents(prompts, mode, primaryDataPath);
6077
6386
  }
6078
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
+ }
6079
6411
  }
6080
6412
  s.stop("Update complete");
6081
6413
  const summary = [
@@ -6087,6 +6419,9 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
6087
6419
  if (ideTargets.length > 0) {
6088
6420
  summary.push(` \u2713 IDE integrations: ${ideTargets.join(", ")}`);
6089
6421
  }
6422
+ if (driftReport.modifiedFiles.length > 0) {
6423
+ summary.push(` \u2713 ${driftReport.modifiedFiles.length} modified files backed up`);
6424
+ }
6090
6425
  summary.push(
6091
6426
  ``,
6092
6427
  `Your configuration and knowledge files were preserved.`,
@@ -6104,50 +6439,35 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
6104
6439
  function updateOpenCodeAgents(prompts, mode, primaryDataPath) {
6105
6440
  if (mode === "global") {
6106
6441
  try {
6107
- const promptsDir = path21.join(path21.dirname(OPENCODE_CONFIG), "prompts");
6442
+ const promptsDir = path23.join(path23.dirname(OPENCODE_CONFIG), "prompts");
6108
6443
  ensureDir(promptsDir);
6109
- let opencodeConfig = { $schema: "https://opencode.ai/config.json" };
6110
- if (fs21.existsSync(OPENCODE_CONFIG)) {
6111
- opencodeConfig = JSON.parse(fs21.readFileSync(OPENCODE_CONFIG, "utf-8"));
6112
- }
6113
- if (!opencodeConfig.agent) opencodeConfig.agent = {};
6114
- const currentAgentBaseNames = prompts.map((p) => path21.basename(p.filePath, ".md"));
6115
- const currentAgentIds = new Set(currentAgentBaseNames.map((base) => `rrce_${base}`));
6116
- const existingAgentNames = Object.keys(opencodeConfig.agent);
6117
- for (const existingName of existingAgentNames) {
6118
- const isLegacyBaseName = currentAgentBaseNames.includes(existingName);
6119
- const isRrcePrefixed = existingName.startsWith("rrce_");
6120
- const isStaleRrcePrefixed = isRrcePrefixed && !currentAgentIds.has(existingName);
6121
- if (isLegacyBaseName || isStaleRrcePrefixed) {
6122
- delete opencodeConfig.agent[existingName];
6123
- const legacyBaseName = isLegacyBaseName ? existingName : existingName.replace(/^rrce_/, "");
6124
- const oldPromptFile = path21.join(promptsDir, `rrce-${legacyBaseName}.md`);
6125
- if (fs21.existsSync(oldPromptFile)) {
6126
- fs21.unlinkSync(oldPromptFile);
6127
- }
6128
- }
6129
- }
6444
+ const newAgents = {};
6130
6445
  for (const prompt of prompts) {
6131
- const baseName = path21.basename(prompt.filePath, ".md");
6446
+ const baseName = path23.basename(prompt.filePath, ".md");
6132
6447
  const agentId = `rrce_${baseName}`;
6133
6448
  const promptFileName = `rrce-${baseName}.md`;
6134
- const promptFilePath = path21.join(promptsDir, promptFileName);
6135
- fs21.writeFileSync(promptFilePath, prompt.content);
6449
+ const promptFilePath = path23.join(promptsDir, promptFileName);
6450
+ fs23.writeFileSync(promptFilePath, prompt.content);
6136
6451
  const agentConfig = convertToOpenCodeAgent(prompt, true, `./prompts/${promptFileName}`);
6137
- 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));
6138
6461
  }
6139
- if (!opencodeConfig.agent.plan) opencodeConfig.agent.plan = {};
6140
- opencodeConfig.agent.plan.disable = true;
6141
- fs21.writeFileSync(OPENCODE_CONFIG, JSON.stringify(opencodeConfig, null, 2) + "\n");
6142
6462
  } catch (e) {
6143
6463
  console.error("Failed to update global OpenCode config with agents:", e);
6144
6464
  }
6145
6465
  } else {
6146
- const opencodeBaseDir = path21.join(primaryDataPath, ".opencode", "agent");
6466
+ const opencodeBaseDir = path23.join(primaryDataPath, ".opencode", "agent");
6147
6467
  ensureDir(opencodeBaseDir);
6148
6468
  clearDirectory(opencodeBaseDir);
6149
6469
  for (const prompt of prompts) {
6150
- const baseName = path21.basename(prompt.filePath, ".md");
6470
+ const baseName = path23.basename(prompt.filePath, ".md");
6151
6471
  const agentId = `rrce_${baseName}`;
6152
6472
  const agentConfig = convertToOpenCodeAgent(prompt);
6153
6473
  const content = `---
@@ -6157,22 +6477,22 @@ ${stringify3({
6157
6477
  tools: agentConfig.tools
6158
6478
  })}---
6159
6479
  ${agentConfig.prompt}`;
6160
- fs21.writeFileSync(path21.join(opencodeBaseDir, `${agentId}.md`), content);
6480
+ fs23.writeFileSync(path23.join(opencodeBaseDir, `${agentId}.md`), content);
6161
6481
  }
6162
6482
  }
6163
6483
  }
6164
6484
  function clearDirectory(dirPath) {
6165
- if (!fs21.existsSync(dirPath)) return;
6166
- const entries = fs21.readdirSync(dirPath, { withFileTypes: true });
6485
+ if (!fs23.existsSync(dirPath)) return;
6486
+ const entries = fs23.readdirSync(dirPath, { withFileTypes: true });
6167
6487
  for (const entry of entries) {
6168
6488
  if (entry.isFile()) {
6169
- fs21.unlinkSync(path21.join(dirPath, entry.name));
6489
+ fs23.unlinkSync(path23.join(dirPath, entry.name));
6170
6490
  }
6171
6491
  }
6172
6492
  }
6173
6493
  function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
6174
- const globalPath = path21.join(customGlobalPath, "workspaces", workspaceName);
6175
- const workspacePath = path21.join(workspaceRoot, ".rrce-workflow");
6494
+ const globalPath = path23.join(customGlobalPath, "workspaces", workspaceName);
6495
+ const workspacePath = path23.join(workspaceRoot, ".rrce-workflow");
6176
6496
  switch (mode) {
6177
6497
  case "global":
6178
6498
  return [globalPath];
@@ -6186,7 +6506,7 @@ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot,
6186
6506
  // src/commands/wizard/delete-flow.ts
6187
6507
  import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
6188
6508
  import pc12 from "picocolors";
6189
- import * as fs22 from "fs";
6509
+ import * as fs24 from "fs";
6190
6510
  init_detection();
6191
6511
  init_config();
6192
6512
  async function runDeleteGlobalProjectFlow(availableProjects) {
@@ -6230,8 +6550,8 @@ Are you sure?`,
6230
6550
  for (const projectName of projectsToDelete) {
6231
6551
  const project = globalProjects.find((p) => p.name === projectName);
6232
6552
  if (!project) continue;
6233
- if (fs22.existsSync(project.dataPath)) {
6234
- fs22.rmSync(project.dataPath, { recursive: true, force: true });
6553
+ if (fs24.existsSync(project.dataPath)) {
6554
+ fs24.rmSync(project.dataPath, { recursive: true, force: true });
6235
6555
  }
6236
6556
  const newConfig = removeProjectConfig(mcpConfig, projectName);
6237
6557
  configChanged = true;
@@ -6277,11 +6597,11 @@ Workspace: ${pc13.bold(workspaceName)}`,
6277
6597
  workspacePath
6278
6598
  });
6279
6599
  const configFilePath = getConfigPath(workspacePath);
6280
- let isAlreadyConfigured = fs23.existsSync(configFilePath);
6600
+ let isAlreadyConfigured = fs25.existsSync(configFilePath);
6281
6601
  let currentStorageMode = null;
6282
6602
  if (isAlreadyConfigured) {
6283
6603
  try {
6284
- const configContent = fs23.readFileSync(configFilePath, "utf-8");
6604
+ const configContent = fs25.readFileSync(configFilePath, "utf-8");
6285
6605
  const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
6286
6606
  currentStorageMode = modeMatch?.[1] ?? null;
6287
6607
  } catch {
@@ -6298,7 +6618,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
6298
6618
  }
6299
6619
  }
6300
6620
  const localDataPath = getLocalWorkspacePath(workspacePath);
6301
- const hasLocalData = fs23.existsSync(localDataPath);
6621
+ const hasLocalData = fs25.existsSync(localDataPath);
6302
6622
  if (isAlreadyConfigured) {
6303
6623
  const menuOptions = [];
6304
6624
  menuOptions.push({