@codemcp/workflows 6.7.0 → 6.9.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.
@@ -6505,6 +6505,10 @@ var PluginRegistry = class {
6505
6505
  }
6506
6506
  };
6507
6507
 
6508
+ // src/plugin-system/beads-plugin.ts
6509
+ import { watch } from "fs";
6510
+ import { join as join8 } from "path";
6511
+
6508
6512
  // src/components/beads/beads-task-backend-client.ts
6509
6513
  import { execSync as execSync5 } from "child_process";
6510
6514
  var defaultLogger7 = createLogger("BeadsTaskBackendClient");
@@ -6665,6 +6669,116 @@ var BeadsTaskBackendClient = class {
6665
6669
  }
6666
6670
  };
6667
6671
 
6672
+ // src/components/beads/beads-plan-syncer.ts
6673
+ import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
6674
+ import { join as join7 } from "path";
6675
+ var defaultLogger8 = createLogger("BeadsPlanSyncer");
6676
+ var BeadsPlanSyncer = class {
6677
+ logger;
6678
+ constructor(logger24) {
6679
+ this.logger = logger24 ?? defaultLogger8;
6680
+ }
6681
+ /**
6682
+ * Sync the given plan file with the latest beads tasks.
6683
+ *
6684
+ * No-ops when the plan file doesn't exist yet, when no phase IDs are
6685
+ * resolved, or when .beads/issues.jsonl is absent. Never throws.
6686
+ */
6687
+ async sync(planFilePath, projectPath) {
6688
+ try {
6689
+ const issues = await this.readIssues(projectPath);
6690
+ if (issues === null) {
6691
+ return;
6692
+ }
6693
+ let planContent;
6694
+ try {
6695
+ planContent = await readFile4(planFilePath, "utf-8");
6696
+ } catch (err) {
6697
+ if (err.code === "ENOENT") return;
6698
+ throw err;
6699
+ }
6700
+ const updated = this.updatePlanContent(planContent, issues);
6701
+ if (updated !== planContent) {
6702
+ await writeFile4(planFilePath, updated, "utf-8");
6703
+ this.logger.debug("Plan file synced with beads tasks", {
6704
+ planFilePath
6705
+ });
6706
+ }
6707
+ } catch (error) {
6708
+ this.logger.warn("BeadsPlanSyncer: sync failed", {
6709
+ error: error instanceof Error ? error.message : String(error),
6710
+ planFilePath,
6711
+ projectPath
6712
+ });
6713
+ }
6714
+ }
6715
+ /**
6716
+ * Read and parse .beads/issues.jsonl.
6717
+ * Returns null if the file doesn't exist.
6718
+ */
6719
+ async readIssues(projectPath) {
6720
+ const jsonlPath = join7(projectPath, ".beads", "issues.jsonl");
6721
+ let raw;
6722
+ try {
6723
+ raw = await readFile4(jsonlPath, "utf-8");
6724
+ } catch (err) {
6725
+ if (err.code === "ENOENT") return null;
6726
+ throw err;
6727
+ }
6728
+ const issues = [];
6729
+ for (const line of raw.split("\n")) {
6730
+ const trimmed = line.trim();
6731
+ if (!trimmed) continue;
6732
+ try {
6733
+ issues.push(JSON.parse(trimmed));
6734
+ } catch {
6735
+ }
6736
+ }
6737
+ return issues;
6738
+ }
6739
+ /**
6740
+ * Find direct children of a given phase task ID.
6741
+ * A child issue has a parent-child dependency pointing to phaseId.
6742
+ */
6743
+ getChildTasks(issues, phaseId) {
6744
+ return issues.filter(
6745
+ (issue) => issue.dependencies?.some(
6746
+ (dep) => dep.depends_on_id === phaseId && dep.type === "parent-child"
6747
+ )
6748
+ );
6749
+ }
6750
+ /**
6751
+ * Rewrite all synced task sections in the plan content.
6752
+ */
6753
+ updatePlanContent(content, issues) {
6754
+ const phaseSectionRe = /(## [^\n]+\n<!-- beads-phase-id: (?!TBD)([^\s>]+) -->)([\s\S]*?)(### Tasks\n)([\s\S]*?)(?=\n## |\n### |$)/g;
6755
+ return content.replace(
6756
+ phaseSectionRe,
6757
+ (_match, phaseHeaderAndId, phaseId, betweenIdAndTasks, tasksHeader, _existingBody) => {
6758
+ const children = this.getChildTasks(issues, phaseId);
6759
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6760
+ const header = `<!-- beads-synced: ${today} -->
6761
+ *Auto-synced \u2014 do not edit here, use \`bd\` CLI instead.*
6762
+ `;
6763
+ let tasksBody;
6764
+ if (children.length === 0) {
6765
+ tasksBody = `${header}
6766
+ `;
6767
+ } else {
6768
+ const taskLines = children.map((task) => {
6769
+ const checkbox = task.status === "closed" ? "[x]" : "[ ]";
6770
+ return `- ${checkbox} \`${task.id}\` ${task.title}`;
6771
+ }).join("\n");
6772
+ tasksBody = `${header}
6773
+ ${taskLines}
6774
+ `;
6775
+ }
6776
+ return `${phaseHeaderAndId}${betweenIdAndTasks}${tasksHeader}${tasksBody}`;
6777
+ }
6778
+ );
6779
+ }
6780
+ };
6781
+
6668
6782
  // src/plugin-system/beads-plugin.ts
6669
6783
  var BeadsPlugin = class {
6670
6784
  projectPath;
@@ -6673,6 +6787,17 @@ var BeadsPlugin = class {
6673
6787
  planManager;
6674
6788
  logger;
6675
6789
  loggerFactory;
6790
+ planSyncer;
6791
+ /**
6792
+ * Plan file path captured from the most recent hook context.
6793
+ * Set by afterStartDevelopment and beforePhaseTransition so the watcher
6794
+ * always has the correct path without touching GitManager.
6795
+ */
6796
+ activePlanFilePath = null;
6797
+ /** Debounce timer for the JSONL file watcher */
6798
+ syncDebounceTimer = null;
6799
+ /** Active fs.watch watcher (closed on process exit) */
6800
+ jsonlWatcher = null;
6676
6801
  constructor(options) {
6677
6802
  this.projectPath = options.projectPath;
6678
6803
  this.loggerFactory = options.loggerFactory;
@@ -6686,6 +6811,13 @@ var BeadsPlugin = class {
6686
6811
  options.loggerFactory ? options.loggerFactory("BeadsTaskBackendClient") : void 0
6687
6812
  );
6688
6813
  this.planManager = new PlanManager();
6814
+ this.planSyncer = new BeadsPlanSyncer(
6815
+ options.loggerFactory ? options.loggerFactory("BeadsPlanSyncer") : void 0
6816
+ );
6817
+ process.once("exit", () => {
6818
+ this.jsonlWatcher?.close();
6819
+ });
6820
+ this.startJsonlWatcher();
6689
6821
  this.logger.debug("BeadsPlugin initialized", {
6690
6822
  projectPath: this.projectPath
6691
6823
  });
@@ -6720,6 +6852,7 @@ var BeadsPlugin = class {
6720
6852
  * Replaces validateBeadsTaskCompletion() method from proceed-to-phase.ts
6721
6853
  */
6722
6854
  async handleBeforePhaseTransition(context, currentPhase, targetPhase) {
6855
+ this.activePlanFilePath = context.planFilePath;
6723
6856
  this.logger.info(
6724
6857
  "BeadsPlugin: Validating task completion before phase transition",
6725
6858
  {
@@ -6762,6 +6895,7 @@ var BeadsPlugin = class {
6762
6895
  * Implements graceful degradation: continues app operation even if beads operations fail
6763
6896
  */
6764
6897
  async handleAfterStartDevelopment(context, args, _result) {
6898
+ this.activePlanFilePath = context.planFilePath;
6765
6899
  this.logger.info("BeadsPlugin: Setting up beads integration", {
6766
6900
  conversationId: context.conversationId,
6767
6901
  workflow: args.workflow,
@@ -6997,8 +7131,8 @@ Complete tasks: \`bd close <id>\``;
6997
7131
  */
6998
7132
  async extractPhaseTaskIdFromPlanFile(planFilePath, phase) {
6999
7133
  try {
7000
- const { readFile: readFile5 } = await import("fs/promises");
7001
- const content = await readFile5(planFilePath, "utf-8");
7134
+ const { readFile: readFile6 } = await import("fs/promises");
7135
+ const content = await readFile6(planFilePath, "utf-8");
7002
7136
  const phaseName = this.capitalizePhase(phase);
7003
7137
  const phaseHeader = `## ${phaseName}`;
7004
7138
  const lines = content.split("\n");
@@ -7152,6 +7286,40 @@ Complete tasks: \`bd close <id>\``;
7152
7286
  );
7153
7287
  }
7154
7288
  }
7289
+ /**
7290
+ * Start watching the .beads/ directory for changes to issues.jsonl.
7291
+ * Watching the directory (not the file) means the watcher works even when
7292
+ * issues.jsonl doesn't exist yet. On change, debounces 300ms then syncs.
7293
+ */
7294
+ startJsonlWatcher() {
7295
+ const beadsDir = join8(this.projectPath, ".beads");
7296
+ try {
7297
+ this.jsonlWatcher = watch(beadsDir, (_event, filename) => {
7298
+ if (filename !== "issues.jsonl") return;
7299
+ if (this.syncDebounceTimer) {
7300
+ clearTimeout(this.syncDebounceTimer);
7301
+ }
7302
+ this.syncDebounceTimer = setTimeout(() => {
7303
+ this.syncDebounceTimer = null;
7304
+ if (!this.activePlanFilePath) return;
7305
+ this.planSyncer.sync(this.activePlanFilePath, this.projectPath).catch((err) => {
7306
+ this.logger.warn(
7307
+ "BeadsPlugin: Error during watcher-triggered plan sync",
7308
+ {
7309
+ error: err instanceof Error ? err.message : String(err)
7310
+ }
7311
+ );
7312
+ });
7313
+ }, 300);
7314
+ });
7315
+ this.logger.info("BeadsPlugin: Started JSONL file watcher", { beadsDir });
7316
+ } catch (error) {
7317
+ this.logger.warn("BeadsPlugin: Could not start JSONL file watcher", {
7318
+ error: error instanceof Error ? error.message : String(error),
7319
+ beadsDir
7320
+ });
7321
+ }
7322
+ }
7155
7323
  /**
7156
7324
  * Extract Goal section content from plan file
7157
7325
  * Returns the goal content if it exists and is meaningful, otherwise undefined
@@ -7193,10 +7361,10 @@ Complete tasks: \`bd close <id>\``;
7193
7361
  */
7194
7362
  async updatePlanFileWithPhaseTaskIds(planFilePath, phaseTasks) {
7195
7363
  try {
7196
- const { readFile: readFile5, writeFile: writeFile5 } = await import("fs/promises");
7364
+ const { readFile: readFile6, writeFile: writeFile6 } = await import("fs/promises");
7197
7365
  let content;
7198
7366
  try {
7199
- content = await readFile5(planFilePath, "utf-8");
7367
+ content = await readFile6(planFilePath, "utf-8");
7200
7368
  } catch (error) {
7201
7369
  const errorMsg = error instanceof Error ? error.message : String(error);
7202
7370
  this.logger.warn("BeadsPlugin: Failed to read plan file for update", {
@@ -7229,7 +7397,7 @@ Complete tasks: \`bd close <id>\``;
7229
7397
  );
7230
7398
  }
7231
7399
  try {
7232
- await writeFile5(planFilePath, content, "utf-8");
7400
+ await writeFile6(planFilePath, content, "utf-8");
7233
7401
  } catch (error) {
7234
7402
  const errorMsg = error instanceof Error ? error.message : String(error);
7235
7403
  this.logger.warn("BeadsPlugin: Failed to write updated plan file", {
@@ -8506,7 +8674,7 @@ Summarize findings and ask user if ready to proceed.`;
8506
8674
  // src/tool-handlers/start-development.ts
8507
8675
  import { basename as basename4 } from "path";
8508
8676
  import { readFileSync, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
8509
- import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
8677
+ import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
8510
8678
  import { resolve as resolve3 } from "path";
8511
8679
  var StartDevelopmentHandler = class extends BaseToolHandler {
8512
8680
  projectDocsManager = null;
@@ -8626,7 +8794,7 @@ var StartDevelopmentHandler = class extends BaseToolHandler {
8626
8794
  };
8627
8795
  if (context.pluginRegistry) {
8628
8796
  try {
8629
- const originalContent = await readFile4(
8797
+ const originalContent = await readFile5(
8630
8798
  conversationContext.planFilePath,
8631
8799
  "utf-8"
8632
8800
  );
@@ -8637,7 +8805,7 @@ var StartDevelopmentHandler = class extends BaseToolHandler {
8637
8805
  originalContent
8638
8806
  );
8639
8807
  if (modifiedContent && modifiedContent !== originalContent) {
8640
- await writeFile4(
8808
+ await writeFile5(
8641
8809
  conversationContext.planFilePath,
8642
8810
  modifiedContent,
8643
8811
  "utf-8"
@@ -9243,7 +9411,7 @@ import { z as z3 } from "zod";
9243
9411
  // src/version-info.ts
9244
9412
  import { execSync as execSync6 } from "child_process";
9245
9413
  import { readFileSync as readFileSync2 } from "fs";
9246
- import { join as join8, dirname as dirname6 } from "path";
9414
+ import { join as join10, dirname as dirname6 } from "path";
9247
9415
  import { fileURLToPath as fileURLToPath4 } from "url";
9248
9416
  var logger17 = createLogger("VersionInfo");
9249
9417
  var BUILD_TIME_VERSION = null;
@@ -9251,8 +9419,8 @@ function getVersionFromPackageJson() {
9251
9419
  try {
9252
9420
  const currentDir = dirname6(fileURLToPath4(import.meta.url));
9253
9421
  const packageJsonPaths = [
9254
- join8(currentDir, "..", "package.json"),
9255
- join8(currentDir, "..", "..", "..", "package.json")
9422
+ join10(currentDir, "..", "package.json"),
9423
+ join10(currentDir, "..", "..", "..", "package.json")
9256
9424
  ];
9257
9425
  for (const packageJsonPath of packageJsonPaths) {
9258
9426
  try {
@@ -9713,11 +9881,11 @@ function createToolRegistry() {
9713
9881
  }
9714
9882
 
9715
9883
  // src/resource-handlers/development-plan.ts
9716
- var defaultLogger8 = createLogger("DevelopmentPlanResourceHandler");
9884
+ var defaultLogger9 = createLogger("DevelopmentPlanResourceHandler");
9717
9885
  var DevelopmentPlanResourceHandler = class {
9718
9886
  logger;
9719
9887
  constructor(logger24) {
9720
- this.logger = logger24 ?? defaultLogger8;
9888
+ this.logger = logger24 ?? defaultLogger9;
9721
9889
  }
9722
9890
  async handle(uri, context) {
9723
9891
  if (context.loggerFactory) {
@@ -9741,11 +9909,11 @@ var DevelopmentPlanResourceHandler = class {
9741
9909
  };
9742
9910
 
9743
9911
  // src/resource-handlers/conversation-state.ts
9744
- var defaultLogger9 = createLogger("ConversationStateResourceHandler");
9912
+ var defaultLogger10 = createLogger("ConversationStateResourceHandler");
9745
9913
  var ConversationStateResourceHandler = class {
9746
9914
  logger;
9747
9915
  constructor(logger24) {
9748
- this.logger = logger24 ?? defaultLogger9;
9916
+ this.logger = logger24 ?? defaultLogger10;
9749
9917
  }
9750
9918
  async handle(uri, context) {
9751
9919
  if (context.loggerFactory) {
@@ -9778,11 +9946,11 @@ var ConversationStateResourceHandler = class {
9778
9946
  import fs5 from "fs";
9779
9947
  import path5 from "path";
9780
9948
  import { fileURLToPath as fileURLToPath5 } from "url";
9781
- var defaultLogger10 = createLogger("WorkflowResourceHandler");
9949
+ var defaultLogger11 = createLogger("WorkflowResourceHandler");
9782
9950
  var WorkflowResourceHandler = class {
9783
9951
  logger;
9784
9952
  constructor(logger24) {
9785
- this.logger = logger24 ?? defaultLogger10;
9953
+ this.logger = logger24 ?? defaultLogger11;
9786
9954
  }
9787
9955
  async handle(uri, context) {
9788
9956
  if (context.loggerFactory) {
@@ -9870,11 +10038,11 @@ var WorkflowResourceHandler = class {
9870
10038
  };
9871
10039
 
9872
10040
  // src/resource-handlers/system-prompt.ts
9873
- var defaultLogger11 = createLogger("SystemPromptResourceHandler");
10041
+ var defaultLogger12 = createLogger("SystemPromptResourceHandler");
9874
10042
  var SystemPromptResourceHandler = class {
9875
10043
  logger;
9876
10044
  constructor(logger24) {
9877
- this.logger = logger24 ?? defaultLogger11;
10045
+ this.logger = logger24 ?? defaultLogger12;
9878
10046
  }
9879
10047
  async handle(uri, context) {
9880
10048
  if (context.loggerFactory) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemcp/workflows-server",
3
- "version": "6.7.0",
3
+ "version": "6.9.0",
4
4
  "description": "MCP server for responsible-vibe development workflows - provides structured workflow guidance",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",