@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 +2 -2
- package/packages/cli/dist/{dist-QQCAQVXJ.js → dist-WVNXA2PS.js} +183 -19
- package/packages/cli/dist/index.js +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/docs/package.json +1 -1
- package/packages/mcp-server/dist/index.d.ts +17 -0
- package/packages/mcp-server/dist/index.js +187 -19
- package/packages/mcp-server/package.json +1 -1
- package/packages/opencode-plugin/dist/index.js +244 -68
- package/packages/opencode-plugin/package.json +1 -1
- package/packages/opencode-tui-plugin/package.json +1 -1
- package/packages/visualizer/package.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemcp/workflows",
|
|
3
|
-
"version": "6.
|
|
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.
|
|
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
|
|
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
|
|
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:
|
|
10994
|
-
const content = await
|
|
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:
|
|
11353
|
+
const { readFile: readFile6, writeFile: writeFile6 } = await import("fs/promises");
|
|
11190
11354
|
let content;
|
|
11191
11355
|
try {
|
|
11192
|
-
content = await
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
13214
|
-
|
|
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
|
|
13828
|
+
var defaultLogger9 = createLogger("DevelopmentPlanResourceHandler");
|
|
13665
13829
|
var DevelopmentPlanResourceHandler = class {
|
|
13666
13830
|
logger;
|
|
13667
13831
|
constructor(logger24) {
|
|
13668
|
-
this.logger = logger24 ??
|
|
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
|
|
13854
|
+
var defaultLogger10 = createLogger("ConversationStateResourceHandler");
|
|
13691
13855
|
var ConversationStateResourceHandler = class {
|
|
13692
13856
|
logger;
|
|
13693
13857
|
constructor(logger24) {
|
|
13694
|
-
this.logger = logger24 ??
|
|
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
|
|
13886
|
+
var defaultLogger11 = createLogger("WorkflowResourceHandler");
|
|
13723
13887
|
var WorkflowResourceHandler = class {
|
|
13724
13888
|
logger;
|
|
13725
13889
|
constructor(logger24) {
|
|
13726
|
-
this.logger = logger24 ??
|
|
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
|
|
13976
|
+
var defaultLogger12 = createLogger("SystemPromptResourceHandler");
|
|
13813
13977
|
var SystemPromptResourceHandler = class {
|
|
13814
13978
|
logger;
|
|
13815
13979
|
constructor(logger24) {
|
|
13816
|
-
this.logger = logger24 ??
|
|
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-
|
|
13
|
+
const { startMcpServer } = await import("./dist-WVNXA2PS.js");
|
|
14
14
|
await startMcpServer();
|
|
15
15
|
} else {
|
|
16
16
|
const mcpServerModule = "@codemcp/workflows-server";
|
|
@@ -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
|