@codemcp/workflows 6.8.0 → 6.10.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemcp/workflows",
3
- "version": "6.8.0",
3
+ "version": "6.10.0",
4
4
  "description": "A Model Context Protocol server that acts as an intelligent conversation state manager and development guide for LLMs, featuring comprehensive long-term memory with persistent project artifacts",
5
5
  "type": "module",
6
6
  "main": "packages/cli/dist/index.js",
@@ -51,7 +51,7 @@
51
51
  "typescript": "^5.9.3",
52
52
  "vitepress": "^1.6.4",
53
53
  "vitest": "4.0.18",
54
- "@codemcp/workflows-core": "6.8.0"
54
+ "@codemcp/workflows-core": "6.10.0"
55
55
  },
56
56
  "lint-staged": {
57
57
  "*.{ts,js,mts,cts,tsx,jsx}": [
@@ -4086,14 +4086,18 @@ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
4086
4086
  import { SetLevelRequestSchema } from "@modelcontextprotocol/sdk/types.js";
4087
4087
  import * as path4 from "path";
4088
4088
  import { homedir } from "os";
4089
+ import { watch } from "fs";
4090
+ import { join as join8 } from "path";
4089
4091
  import { execSync as execSync5 } from "child_process";
4092
+ import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
4093
+ import { join as join7 } from "path";
4090
4094
  import { basename as basename4 } from "path";
4091
4095
  import { readFileSync, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
4092
- import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
4096
+ import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
4093
4097
  import { resolve as resolve3 } from "path";
4094
4098
  import { execSync as execSync6 } from "child_process";
4095
4099
  import { readFileSync as readFileSync2 } from "fs";
4096
- import { join as join8, dirname as dirname6 } from "path";
4100
+ import { join as join10, dirname as dirname6 } from "path";
4097
4101
  import { fileURLToPath as fileURLToPath4 } from "url";
4098
4102
  import fs5 from "fs";
4099
4103
  import path5 from "path";
@@ -10659,6 +10663,112 @@ var BeadsTaskBackendClient = class {
10659
10663
  }
10660
10664
  }
10661
10665
  };
10666
+ var defaultLogger8 = createLogger("BeadsPlanSyncer");
10667
+ var BeadsPlanSyncer = class {
10668
+ logger;
10669
+ constructor(logger24) {
10670
+ this.logger = logger24 ?? defaultLogger8;
10671
+ }
10672
+ /**
10673
+ * Sync the given plan file with the latest beads tasks.
10674
+ *
10675
+ * No-ops when the plan file doesn't exist yet, when no phase IDs are
10676
+ * resolved, or when .beads/issues.jsonl is absent. Never throws.
10677
+ */
10678
+ async sync(planFilePath, projectPath) {
10679
+ try {
10680
+ const issues = await this.readIssues(projectPath);
10681
+ if (issues === null) {
10682
+ return;
10683
+ }
10684
+ let planContent;
10685
+ try {
10686
+ planContent = await readFile4(planFilePath, "utf-8");
10687
+ } catch (err) {
10688
+ if (err.code === "ENOENT") return;
10689
+ throw err;
10690
+ }
10691
+ const updated = this.updatePlanContent(planContent, issues);
10692
+ if (updated !== planContent) {
10693
+ await writeFile4(planFilePath, updated, "utf-8");
10694
+ this.logger.debug("Plan file synced with beads tasks", {
10695
+ planFilePath
10696
+ });
10697
+ }
10698
+ } catch (error) {
10699
+ this.logger.warn("BeadsPlanSyncer: sync failed", {
10700
+ error: error instanceof Error ? error.message : String(error),
10701
+ planFilePath,
10702
+ projectPath
10703
+ });
10704
+ }
10705
+ }
10706
+ /**
10707
+ * Read and parse .beads/issues.jsonl.
10708
+ * Returns null if the file doesn't exist.
10709
+ */
10710
+ async readIssues(projectPath) {
10711
+ const jsonlPath = join7(projectPath, ".beads", "issues.jsonl");
10712
+ let raw;
10713
+ try {
10714
+ raw = await readFile4(jsonlPath, "utf-8");
10715
+ } catch (err) {
10716
+ if (err.code === "ENOENT") return null;
10717
+ throw err;
10718
+ }
10719
+ const issues = [];
10720
+ for (const line of raw.split("\n")) {
10721
+ const trimmed = line.trim();
10722
+ if (!trimmed) continue;
10723
+ try {
10724
+ issues.push(JSON.parse(trimmed));
10725
+ } catch {
10726
+ }
10727
+ }
10728
+ return issues;
10729
+ }
10730
+ /**
10731
+ * Find direct children of a given phase task ID.
10732
+ * A child issue has a parent-child dependency pointing to phaseId.
10733
+ */
10734
+ getChildTasks(issues, phaseId) {
10735
+ return issues.filter(
10736
+ (issue) => issue.dependencies?.some(
10737
+ (dep) => dep.depends_on_id === phaseId && dep.type === "parent-child"
10738
+ )
10739
+ );
10740
+ }
10741
+ /**
10742
+ * Rewrite all synced task sections in the plan content.
10743
+ */
10744
+ updatePlanContent(content, issues) {
10745
+ const phaseSectionRe = /(## [^\n]+\n<!-- beads-phase-id: (?!TBD)([^\s>]+) -->)([\s\S]*?)(### Tasks\n)([\s\S]*?)(?=\n## |\n### |$)/g;
10746
+ return content.replace(
10747
+ phaseSectionRe,
10748
+ (_match, phaseHeaderAndId, phaseId, betweenIdAndTasks, tasksHeader, _existingBody) => {
10749
+ const children = this.getChildTasks(issues, phaseId);
10750
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
10751
+ const header = `<!-- beads-synced: ${today} -->
10752
+ *Auto-synced \u2014 do not edit here, use \`bd\` CLI instead.*
10753
+ `;
10754
+ let tasksBody;
10755
+ if (children.length === 0) {
10756
+ tasksBody = `${header}
10757
+ `;
10758
+ } else {
10759
+ const taskLines = children.map((task) => {
10760
+ const checkbox = task.status === "closed" ? "[x]" : "[ ]";
10761
+ return `- ${checkbox} \`${task.id}\` ${task.title}`;
10762
+ }).join("\n");
10763
+ tasksBody = `${header}
10764
+ ${taskLines}
10765
+ `;
10766
+ }
10767
+ return `${phaseHeaderAndId}${betweenIdAndTasks}${tasksHeader}${tasksBody}`;
10768
+ }
10769
+ );
10770
+ }
10771
+ };
10662
10772
  var BeadsPlugin = class {
10663
10773
  projectPath;
10664
10774
  beadsStateManager;
@@ -10666,6 +10776,17 @@ var BeadsPlugin = class {
10666
10776
  planManager;
10667
10777
  logger;
10668
10778
  loggerFactory;
10779
+ planSyncer;
10780
+ /**
10781
+ * Plan file path captured from the most recent hook context.
10782
+ * Set by afterStartDevelopment and beforePhaseTransition so the watcher
10783
+ * always has the correct path without touching GitManager.
10784
+ */
10785
+ activePlanFilePath = null;
10786
+ /** Debounce timer for the JSONL file watcher */
10787
+ syncDebounceTimer = null;
10788
+ /** Active fs.watch watcher (closed on process exit) */
10789
+ jsonlWatcher = null;
10669
10790
  constructor(options) {
10670
10791
  this.projectPath = options.projectPath;
10671
10792
  this.loggerFactory = options.loggerFactory;
@@ -10679,6 +10800,13 @@ var BeadsPlugin = class {
10679
10800
  options.loggerFactory ? options.loggerFactory("BeadsTaskBackendClient") : void 0
10680
10801
  );
10681
10802
  this.planManager = new PlanManager();
10803
+ this.planSyncer = new BeadsPlanSyncer(
10804
+ options.loggerFactory ? options.loggerFactory("BeadsPlanSyncer") : void 0
10805
+ );
10806
+ process.once("exit", () => {
10807
+ this.jsonlWatcher?.close();
10808
+ });
10809
+ this.startJsonlWatcher();
10682
10810
  this.logger.debug("BeadsPlugin initialized", {
10683
10811
  projectPath: this.projectPath
10684
10812
  });
@@ -10713,6 +10841,7 @@ var BeadsPlugin = class {
10713
10841
  * Replaces validateBeadsTaskCompletion() method from proceed-to-phase.ts
10714
10842
  */
10715
10843
  async handleBeforePhaseTransition(context, currentPhase, targetPhase) {
10844
+ this.activePlanFilePath = context.planFilePath;
10716
10845
  this.logger.info(
10717
10846
  "BeadsPlugin: Validating task completion before phase transition",
10718
10847
  {
@@ -10755,6 +10884,7 @@ var BeadsPlugin = class {
10755
10884
  * Implements graceful degradation: continues app operation even if beads operations fail
10756
10885
  */
10757
10886
  async handleAfterStartDevelopment(context, args, _result) {
10887
+ this.activePlanFilePath = context.planFilePath;
10758
10888
  this.logger.info("BeadsPlugin: Setting up beads integration", {
10759
10889
  conversationId: context.conversationId,
10760
10890
  workflow: args.workflow,
@@ -10990,8 +11120,8 @@ Complete tasks: \`bd close <id>\``;
10990
11120
  */
10991
11121
  async extractPhaseTaskIdFromPlanFile(planFilePath, phase) {
10992
11122
  try {
10993
- const { readFile: readFile5 } = await import("fs/promises");
10994
- const content = await readFile5(planFilePath, "utf-8");
11123
+ const { readFile: readFile6 } = await import("fs/promises");
11124
+ const content = await readFile6(planFilePath, "utf-8");
10995
11125
  const phaseName = this.capitalizePhase(phase);
10996
11126
  const phaseHeader = `## ${phaseName}`;
10997
11127
  const lines = content.split("\n");
@@ -11145,6 +11275,40 @@ Complete tasks: \`bd close <id>\``;
11145
11275
  );
11146
11276
  }
11147
11277
  }
11278
+ /**
11279
+ * Start watching the .beads/ directory for changes to issues.jsonl.
11280
+ * Watching the directory (not the file) means the watcher works even when
11281
+ * issues.jsonl doesn't exist yet. On change, debounces 300ms then syncs.
11282
+ */
11283
+ startJsonlWatcher() {
11284
+ const beadsDir = join8(this.projectPath, ".beads");
11285
+ try {
11286
+ this.jsonlWatcher = watch(beadsDir, (_event, filename) => {
11287
+ if (filename !== "issues.jsonl") return;
11288
+ if (this.syncDebounceTimer) {
11289
+ clearTimeout(this.syncDebounceTimer);
11290
+ }
11291
+ this.syncDebounceTimer = setTimeout(() => {
11292
+ this.syncDebounceTimer = null;
11293
+ if (!this.activePlanFilePath) return;
11294
+ this.planSyncer.sync(this.activePlanFilePath, this.projectPath).catch((err) => {
11295
+ this.logger.warn(
11296
+ "BeadsPlugin: Error during watcher-triggered plan sync",
11297
+ {
11298
+ error: err instanceof Error ? err.message : String(err)
11299
+ }
11300
+ );
11301
+ });
11302
+ }, 300);
11303
+ });
11304
+ this.logger.info("BeadsPlugin: Started JSONL file watcher", { beadsDir });
11305
+ } catch (error) {
11306
+ this.logger.warn("BeadsPlugin: Could not start JSONL file watcher", {
11307
+ error: error instanceof Error ? error.message : String(error),
11308
+ beadsDir
11309
+ });
11310
+ }
11311
+ }
11148
11312
  /**
11149
11313
  * Extract Goal section content from plan file
11150
11314
  * Returns the goal content if it exists and is meaningful, otherwise undefined
@@ -11186,10 +11350,10 @@ Complete tasks: \`bd close <id>\``;
11186
11350
  */
11187
11351
  async updatePlanFileWithPhaseTaskIds(planFilePath, phaseTasks) {
11188
11352
  try {
11189
- const { readFile: readFile5, writeFile: writeFile5 } = await import("fs/promises");
11353
+ const { readFile: readFile6, writeFile: writeFile6 } = await import("fs/promises");
11190
11354
  let content;
11191
11355
  try {
11192
- content = await readFile5(planFilePath, "utf-8");
11356
+ content = await readFile6(planFilePath, "utf-8");
11193
11357
  } catch (error) {
11194
11358
  const errorMsg = error instanceof Error ? error.message : String(error);
11195
11359
  this.logger.warn("BeadsPlugin: Failed to read plan file for update", {
@@ -11222,7 +11386,7 @@ Complete tasks: \`bd close <id>\``;
11222
11386
  );
11223
11387
  }
11224
11388
  try {
11225
- await writeFile5(planFilePath, content, "utf-8");
11389
+ await writeFile6(planFilePath, content, "utf-8");
11226
11390
  } catch (error) {
11227
11391
  const errorMsg = error instanceof Error ? error.message : String(error);
11228
11392
  this.logger.warn("BeadsPlugin: Failed to write updated plan file", {
@@ -12601,7 +12765,7 @@ var StartDevelopmentHandler = class extends BaseToolHandler {
12601
12765
  };
12602
12766
  if (context.pluginRegistry) {
12603
12767
  try {
12604
- const originalContent = await readFile4(
12768
+ const originalContent = await readFile5(
12605
12769
  conversationContext.planFilePath,
12606
12770
  "utf-8"
12607
12771
  );
@@ -12612,7 +12776,7 @@ var StartDevelopmentHandler = class extends BaseToolHandler {
12612
12776
  originalContent
12613
12777
  );
12614
12778
  if (modifiedContent && modifiedContent !== originalContent) {
12615
- await writeFile4(
12779
+ await writeFile5(
12616
12780
  conversationContext.planFilePath,
12617
12781
  modifiedContent,
12618
12782
  "utf-8"
@@ -13210,8 +13374,8 @@ function getVersionFromPackageJson() {
13210
13374
  try {
13211
13375
  const currentDir = dirname6(fileURLToPath4(import.meta.url));
13212
13376
  const packageJsonPaths = [
13213
- join8(currentDir, "..", "package.json"),
13214
- join8(currentDir, "..", "..", "..", "package.json")
13377
+ join10(currentDir, "..", "package.json"),
13378
+ join10(currentDir, "..", "..", "..", "package.json")
13215
13379
  ];
13216
13380
  for (const packageJsonPath of packageJsonPaths) {
13217
13381
  try {
@@ -13661,11 +13825,11 @@ function createToolRegistry() {
13661
13825
  });
13662
13826
  return registry;
13663
13827
  }
13664
- var defaultLogger8 = createLogger("DevelopmentPlanResourceHandler");
13828
+ var defaultLogger9 = createLogger("DevelopmentPlanResourceHandler");
13665
13829
  var DevelopmentPlanResourceHandler = class {
13666
13830
  logger;
13667
13831
  constructor(logger24) {
13668
- this.logger = logger24 ?? defaultLogger8;
13832
+ this.logger = logger24 ?? defaultLogger9;
13669
13833
  }
13670
13834
  async handle(uri, context) {
13671
13835
  if (context.loggerFactory) {
@@ -13687,11 +13851,11 @@ var DevelopmentPlanResourceHandler = class {
13687
13851
  }, "Failed to retrieve development plan resource");
13688
13852
  }
13689
13853
  };
13690
- var defaultLogger9 = createLogger("ConversationStateResourceHandler");
13854
+ var defaultLogger10 = createLogger("ConversationStateResourceHandler");
13691
13855
  var ConversationStateResourceHandler = class {
13692
13856
  logger;
13693
13857
  constructor(logger24) {
13694
- this.logger = logger24 ?? defaultLogger9;
13858
+ this.logger = logger24 ?? defaultLogger10;
13695
13859
  }
13696
13860
  async handle(uri, context) {
13697
13861
  if (context.loggerFactory) {
@@ -13719,11 +13883,11 @@ var ConversationStateResourceHandler = class {
13719
13883
  }, "Failed to retrieve conversation state resource");
13720
13884
  }
13721
13885
  };
13722
- var defaultLogger10 = createLogger("WorkflowResourceHandler");
13886
+ var defaultLogger11 = createLogger("WorkflowResourceHandler");
13723
13887
  var WorkflowResourceHandler = class {
13724
13888
  logger;
13725
13889
  constructor(logger24) {
13726
- this.logger = logger24 ?? defaultLogger10;
13890
+ this.logger = logger24 ?? defaultLogger11;
13727
13891
  }
13728
13892
  async handle(uri, context) {
13729
13893
  if (context.loggerFactory) {
@@ -13809,11 +13973,11 @@ var WorkflowResourceHandler = class {
13809
13973
  }, `Failed to load workflow resource: ${uri.href}`);
13810
13974
  }
13811
13975
  };
13812
- var defaultLogger11 = createLogger("SystemPromptResourceHandler");
13976
+ var defaultLogger12 = createLogger("SystemPromptResourceHandler");
13813
13977
  var SystemPromptResourceHandler = class {
13814
13978
  logger;
13815
13979
  constructor(logger24) {
13816
- this.logger = logger24 ?? defaultLogger11;
13980
+ this.logger = logger24 ?? defaultLogger12;
13817
13981
  }
13818
13982
  async handle(uri, context) {
13819
13983
  if (context.loggerFactory) {
@@ -10,7 +10,7 @@ var args = process.argv.slice(2);
10
10
  if (args.length === 0) {
11
11
  const isLocal = existsSync(join(__dirname, "../../mcp-server/dist/index.js"));
12
12
  if (isLocal) {
13
- const { startMcpServer } = await import("./dist-QQCAQVXJ.js");
13
+ const { startMcpServer } = await import("./dist-WVNXA2PS.js");
14
14
  await startMcpServer();
15
15
  } else {
16
16
  const mcpServerModule = "@codemcp/workflows-server";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemcp/workflows-cli",
3
- "version": "6.8.0",
3
+ "version": "6.10.0",
4
4
  "description": "CLI tools for responsible-vibe development workflows",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemcp/workflows-core",
3
- "version": "6.8.0",
3
+ "version": "6.10.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemcp/workflows-docs",
3
- "version": "6.8.0",
3
+ "version": "6.10.0",
4
4
  "description": "Documentation site for Responsible Vibe MCP",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -956,6 +956,17 @@ declare class BeadsPlugin implements IPlugin {
956
956
  private planManager;
957
957
  private logger;
958
958
  private loggerFactory?;
959
+ private planSyncer;
960
+ /**
961
+ * Plan file path captured from the most recent hook context.
962
+ * Set by afterStartDevelopment and beforePhaseTransition so the watcher
963
+ * always has the correct path without touching GitManager.
964
+ */
965
+ private activePlanFilePath;
966
+ /** Debounce timer for the JSONL file watcher */
967
+ private syncDebounceTimer;
968
+ /** Active fs.watch watcher (closed on process exit) */
969
+ private jsonlWatcher;
959
970
  constructor(options: {
960
971
  projectPath: string;
961
972
  loggerFactory?: LoggerFactory;
@@ -1009,6 +1020,12 @@ declare class BeadsPlugin implements IPlugin {
1009
1020
  * Implements graceful error handling: logs errors but continues on non-validation failures
1010
1021
  */
1011
1022
  private validateBeadsTaskCompletion;
1023
+ /**
1024
+ * Start watching the .beads/ directory for changes to issues.jsonl.
1025
+ * Watching the directory (not the file) means the watcher works even when
1026
+ * issues.jsonl doesn't exist yet. On change, debounces 300ms then syncs.
1027
+ */
1028
+ private startJsonlWatcher;
1012
1029
  /**
1013
1030
  * Extract Goal section content from plan file
1014
1031
  * Returns the goal content if it exists and is meaningful, otherwise undefined