forge-cc 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/package.json +3 -2
- package/dist/gates/codex-gate.d.ts +0 -51
- package/dist/gates/codex-gate.js +0 -121
- package/dist/gates/codex-gate.js.map +0 -1
- package/dist/gates/prd-gate.d.ts +0 -7
- package/dist/gates/prd-gate.js +0 -193
- package/dist/gates/prd-gate.js.map +0 -1
- package/dist/gates/remediation.d.ts +0 -46
- package/dist/gates/remediation.js +0 -423
- package/dist/gates/remediation.js.map +0 -1
- package/dist/gates/review-gate.d.ts +0 -16
- package/dist/gates/review-gate.js +0 -479
- package/dist/gates/review-gate.js.map +0 -1
- package/dist/gates/runtime-gate.d.ts +0 -5
- package/dist/gates/runtime-gate.js +0 -99
- package/dist/gates/runtime-gate.js.map +0 -1
- package/dist/gates/test-analysis.d.ts +0 -21
- package/dist/gates/test-analysis.js +0 -394
- package/dist/gates/test-analysis.js.map +0 -1
- package/dist/gates/visual-capture.d.ts +0 -24
- package/dist/gates/visual-capture.js +0 -144
- package/dist/gates/visual-capture.js.map +0 -1
- package/dist/gates/visual-gate.d.ts +0 -18
- package/dist/gates/visual-gate.js +0 -234
- package/dist/gates/visual-gate.js.map +0 -1
- package/dist/gates/visual-reviewer.d.ts +0 -11
- package/dist/gates/visual-reviewer.js +0 -211
- package/dist/gates/visual-reviewer.js.map +0 -1
- package/dist/go/auto-chain.d.ts +0 -136
- package/dist/go/auto-chain.js +0 -389
- package/dist/go/auto-chain.js.map +0 -1
- package/dist/go/executor.d.ts +0 -137
- package/dist/go/executor.js +0 -447
- package/dist/go/executor.js.map +0 -1
- package/dist/go/finalize.d.ts +0 -108
- package/dist/go/finalize.js +0 -331
- package/dist/go/finalize.js.map +0 -1
- package/dist/go/linear-sync-cli.d.ts +0 -55
- package/dist/go/linear-sync-cli.js +0 -192
- package/dist/go/linear-sync-cli.js.map +0 -1
- package/dist/go/linear-sync.d.ts +0 -112
- package/dist/go/linear-sync.js +0 -375
- package/dist/go/linear-sync.js.map +0 -1
- package/dist/go/prd-queue.d.ts +0 -43
- package/dist/go/prd-queue.js +0 -67
- package/dist/go/prd-queue.js.map +0 -1
- package/dist/go/prd-selector.d.ts +0 -57
- package/dist/go/prd-selector.js +0 -101
- package/dist/go/prd-selector.js.map +0 -1
- package/dist/go/verify-loop.d.ts +0 -64
- package/dist/go/verify-loop.js +0 -327
- package/dist/go/verify-loop.js.map +0 -1
- package/dist/hooks/pre-commit.d.ts +0 -5
- package/dist/hooks/pre-commit.js +0 -75
- package/dist/hooks/pre-commit.js.map +0 -1
- package/dist/linear/issues.d.ts +0 -22
- package/dist/linear/issues.js +0 -51
- package/dist/linear/issues.js.map +0 -1
- package/dist/linear/milestones.d.ts +0 -11
- package/dist/linear/milestones.js +0 -32
- package/dist/linear/milestones.js.map +0 -1
- package/dist/linear/projects.d.ts +0 -16
- package/dist/linear/projects.js +0 -51
- package/dist/linear/projects.js.map +0 -1
- package/dist/reporter/human.d.ts +0 -7
- package/dist/reporter/human.js +0 -93
- package/dist/reporter/human.js.map +0 -1
- package/dist/reporter/json.d.ts +0 -2
- package/dist/reporter/json.js +0 -4
- package/dist/reporter/json.js.map +0 -1
- package/dist/setup/structural-templates.d.ts +0 -12
- package/dist/setup/structural-templates.js +0 -288
- package/dist/setup/structural-templates.js.map +0 -1
- package/dist/setup/templates.d.ts +0 -17
- package/dist/setup/templates.js +0 -109
- package/dist/setup/templates.js.map +0 -1
- package/dist/setup/test-planner.d.ts +0 -38
- package/dist/setup/test-planner.js +0 -91
- package/dist/setup/test-planner.js.map +0 -1
- package/dist/setup/test-scaffold.d.ts +0 -31
- package/dist/setup/test-scaffold.js +0 -209
- package/dist/setup/test-scaffold.js.map +0 -1
- package/dist/setup/test-templates.d.ts +0 -37
- package/dist/setup/test-templates.js +0 -313
- package/dist/setup/test-templates.js.map +0 -1
- package/dist/spec/generator.d.ts +0 -34
- package/dist/spec/generator.js +0 -227
- package/dist/spec/generator.js.map +0 -1
- package/dist/spec/interview.d.ts +0 -142
- package/dist/spec/interview.js +0 -287
- package/dist/spec/interview.js.map +0 -1
- package/dist/spec/linear-sync.d.ts +0 -48
- package/dist/spec/linear-sync.js +0 -125
- package/dist/spec/linear-sync.js.map +0 -1
- package/dist/spec/scanner.d.ts +0 -79
- package/dist/spec/scanner.js +0 -566
- package/dist/spec/scanner.js.map +0 -1
- package/dist/spec/templates.d.ts +0 -375
- package/dist/spec/templates.js +0 -95
- package/dist/spec/templates.js.map +0 -1
- package/dist/state/prd-status.d.ts +0 -62
- package/dist/state/prd-status.js +0 -122
- package/dist/state/prd-status.js.map +0 -1
- package/dist/state/reader.d.ts +0 -7
- package/dist/state/reader.js +0 -43
- package/dist/state/reader.js.map +0 -1
- package/dist/state/writer.d.ts +0 -21
- package/dist/state/writer.js +0 -106
- package/dist/state/writer.js.map +0 -1
- package/dist/team/consensus.d.ts +0 -28
- package/dist/team/consensus.js +0 -130
- package/dist/team/consensus.js.map +0 -1
- package/dist/team/index.d.ts +0 -4
- package/dist/team/index.js +0 -5
- package/dist/team/index.js.map +0 -1
- package/dist/team/lifecycle.d.ts +0 -37
- package/dist/team/lifecycle.js +0 -92
- package/dist/team/lifecycle.js.map +0 -1
- package/dist/team/reviewer.d.ts +0 -10
- package/dist/team/reviewer.js +0 -345
- package/dist/team/reviewer.js.map +0 -1
- package/dist/team/types.d.ts +0 -269
- package/dist/team/types.js +0 -70
- package/dist/team/types.js.map +0 -1
- package/dist/utils/browser.d.ts +0 -10
- package/dist/utils/browser.js +0 -96
- package/dist/utils/browser.js.map +0 -1
- package/dist/utils/platform.d.ts +0 -29
- package/dist/utils/platform.js +0 -90
- package/dist/utils/platform.js.map +0 -1
- package/dist/worktree/identity.d.ts +0 -9
- package/dist/worktree/identity.js +0 -32
- package/dist/worktree/identity.js.map +0 -1
- package/dist/worktree/parallel.d.ts +0 -87
- package/dist/worktree/parallel.js +0 -328
- package/dist/worktree/parallel.js.map +0 -1
- package/dist/worktree/session.d.ts +0 -67
- package/dist/worktree/session.js +0 -194
- package/dist/worktree/session.js.map +0 -1
- package/dist/worktree/state-merge.d.ts +0 -43
- package/dist/worktree/state-merge.js +0 -162
- package/dist/worktree/state-merge.js.map +0 -1
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parallel Scheduler — Milestone Dependency Analyzer & Execution Planner
|
|
3
|
-
*
|
|
4
|
-
* Parses `dependsOn` from PRD milestones, builds a DAG, determines which
|
|
5
|
-
* milestones can run simultaneously in parallel waves, and provides
|
|
6
|
-
* functions to query ready milestones given completed set.
|
|
7
|
-
*
|
|
8
|
-
* Backward compatible: milestones without `dependsOn` are treated as
|
|
9
|
-
* having no dependencies (roots).
|
|
10
|
-
*/
|
|
11
|
-
import { readFile } from "node:fs/promises";
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
// buildDAG
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
|
-
/**
|
|
16
|
-
* Build a directed acyclic graph from milestone dependencies.
|
|
17
|
-
*
|
|
18
|
-
* Validates:
|
|
19
|
-
* - All referenced dependencies exist in the milestone set
|
|
20
|
-
* - No cycles exist in the dependency graph
|
|
21
|
-
*
|
|
22
|
-
* Throws descriptive errors on validation failure.
|
|
23
|
-
*/
|
|
24
|
-
export function buildDAG(milestones) {
|
|
25
|
-
const dag = new Map();
|
|
26
|
-
const milestoneNumbers = new Set(milestones.map((m) => m.number));
|
|
27
|
-
// Initialize all nodes
|
|
28
|
-
for (const milestone of milestones) {
|
|
29
|
-
dag.set(milestone.number, {
|
|
30
|
-
milestone,
|
|
31
|
-
children: [],
|
|
32
|
-
parents: [...milestone.dependsOn],
|
|
33
|
-
depth: -1, // computed later
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
// Validate dependencies exist and build children links
|
|
37
|
-
for (const milestone of milestones) {
|
|
38
|
-
for (const dep of milestone.dependsOn) {
|
|
39
|
-
if (!milestoneNumbers.has(dep)) {
|
|
40
|
-
throw new Error(`Milestone ${milestone.number} ("${milestone.name}") depends on milestone ${dep}, which does not exist`);
|
|
41
|
-
}
|
|
42
|
-
const parentNode = dag.get(dep);
|
|
43
|
-
parentNode.children.push(milestone.number);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
// Detect cycles using DFS with coloring (white/gray/black)
|
|
47
|
-
detectCycles(dag);
|
|
48
|
-
// Compute depths via BFS from roots
|
|
49
|
-
computeDepths(dag);
|
|
50
|
-
return dag;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Detect cycles in the DAG using DFS with three-color marking.
|
|
54
|
-
* Throws an error with a descriptive message if a cycle is found.
|
|
55
|
-
*/
|
|
56
|
-
function detectCycles(dag) {
|
|
57
|
-
const WHITE = 0; // unvisited
|
|
58
|
-
const GRAY = 1; // in current DFS path
|
|
59
|
-
const BLACK = 2; // fully processed
|
|
60
|
-
const color = new Map();
|
|
61
|
-
for (const num of dag.keys()) {
|
|
62
|
-
color.set(num, WHITE);
|
|
63
|
-
}
|
|
64
|
-
const path = [];
|
|
65
|
-
function dfs(nodeNum) {
|
|
66
|
-
color.set(nodeNum, GRAY);
|
|
67
|
-
path.push(nodeNum);
|
|
68
|
-
const node = dag.get(nodeNum);
|
|
69
|
-
for (const childNum of node.children) {
|
|
70
|
-
const childColor = color.get(childNum);
|
|
71
|
-
if (childColor === GRAY) {
|
|
72
|
-
// Found a cycle — extract the cycle path for the error message
|
|
73
|
-
const cycleStart = path.indexOf(childNum);
|
|
74
|
-
const cyclePath = path.slice(cycleStart);
|
|
75
|
-
cyclePath.push(childNum); // close the cycle
|
|
76
|
-
const cycleStr = cyclePath
|
|
77
|
-
.map((n) => {
|
|
78
|
-
const m = dag.get(n).milestone;
|
|
79
|
-
return `M${m.number}("${m.name}")`;
|
|
80
|
-
})
|
|
81
|
-
.join(" -> ");
|
|
82
|
-
throw new Error(`Dependency cycle detected: ${cycleStr}`);
|
|
83
|
-
}
|
|
84
|
-
if (childColor === WHITE) {
|
|
85
|
-
dfs(childNum);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
color.set(nodeNum, BLACK);
|
|
89
|
-
path.pop();
|
|
90
|
-
}
|
|
91
|
-
for (const num of dag.keys()) {
|
|
92
|
-
if (color.get(num) === WHITE) {
|
|
93
|
-
dfs(num);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Compute depth for each node via BFS from roots (nodes with no parents).
|
|
99
|
-
* Depth = longest path from any root to this node.
|
|
100
|
-
*/
|
|
101
|
-
function computeDepths(dag) {
|
|
102
|
-
// Find roots (no parents)
|
|
103
|
-
const roots = [];
|
|
104
|
-
for (const [num, node] of dag) {
|
|
105
|
-
if (node.parents.length === 0) {
|
|
106
|
-
roots.push(num);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
// BFS-like traversal computing max depth
|
|
110
|
-
// Use Kahn's algorithm approach: process nodes whose all parents are resolved
|
|
111
|
-
const depth = new Map();
|
|
112
|
-
const inDegree = new Map();
|
|
113
|
-
for (const [num, node] of dag) {
|
|
114
|
-
inDegree.set(num, node.parents.length);
|
|
115
|
-
}
|
|
116
|
-
const queue = [];
|
|
117
|
-
for (const root of roots) {
|
|
118
|
-
depth.set(root, 0);
|
|
119
|
-
queue.push(root);
|
|
120
|
-
}
|
|
121
|
-
while (queue.length > 0) {
|
|
122
|
-
const current = queue.shift();
|
|
123
|
-
const currentDepth = depth.get(current);
|
|
124
|
-
const node = dag.get(current);
|
|
125
|
-
node.depth = currentDepth;
|
|
126
|
-
for (const childNum of node.children) {
|
|
127
|
-
const childDepth = depth.get(childNum);
|
|
128
|
-
// Set child depth to max of current paths
|
|
129
|
-
if (childDepth === undefined || currentDepth + 1 > childDepth) {
|
|
130
|
-
depth.set(childNum, currentDepth + 1);
|
|
131
|
-
}
|
|
132
|
-
// Decrement in-degree; enqueue when all parents processed
|
|
133
|
-
const remaining = inDegree.get(childNum) - 1;
|
|
134
|
-
inDegree.set(childNum, remaining);
|
|
135
|
-
if (remaining === 0) {
|
|
136
|
-
queue.push(childNum);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// ---------------------------------------------------------------------------
|
|
142
|
-
// computeExecutionWaves
|
|
143
|
-
// ---------------------------------------------------------------------------
|
|
144
|
-
/**
|
|
145
|
-
* Topological sort milestones into parallel execution waves.
|
|
146
|
-
*
|
|
147
|
-
* - Wave 1: all milestones with no dependencies (roots)
|
|
148
|
-
* - Wave 2: milestones whose dependencies are all in Wave 1
|
|
149
|
-
* - Wave N: milestones whose dependencies are all in waves < N
|
|
150
|
-
*/
|
|
151
|
-
export function computeExecutionWaves(dag) {
|
|
152
|
-
if (dag.size === 0) {
|
|
153
|
-
return {
|
|
154
|
-
waves: [],
|
|
155
|
-
totalMilestones: 0,
|
|
156
|
-
maxParallelism: 0,
|
|
157
|
-
isSequential: true,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
// Group milestones by depth — depth corresponds to wave number
|
|
161
|
-
const waveMap = new Map();
|
|
162
|
-
for (const [num, node] of dag) {
|
|
163
|
-
const d = node.depth;
|
|
164
|
-
if (!waveMap.has(d)) {
|
|
165
|
-
waveMap.set(d, []);
|
|
166
|
-
}
|
|
167
|
-
waveMap.get(d).push(num);
|
|
168
|
-
}
|
|
169
|
-
// Sort wave keys and build ExecutionWave array
|
|
170
|
-
const sortedDepths = [...waveMap.keys()].sort((a, b) => a - b);
|
|
171
|
-
const waves = sortedDepths.map((depth, index) => ({
|
|
172
|
-
waveNumber: index + 1,
|
|
173
|
-
milestones: waveMap.get(depth).sort((a, b) => a - b),
|
|
174
|
-
}));
|
|
175
|
-
const maxParallelism = Math.max(...waves.map((w) => w.milestones.length));
|
|
176
|
-
const isSequential = maxParallelism <= 1;
|
|
177
|
-
return {
|
|
178
|
-
waves,
|
|
179
|
-
totalMilestones: dag.size,
|
|
180
|
-
maxParallelism,
|
|
181
|
-
isSequential,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
// ---------------------------------------------------------------------------
|
|
185
|
-
// parseMilestoneDependencies
|
|
186
|
-
// ---------------------------------------------------------------------------
|
|
187
|
-
/**
|
|
188
|
-
* Parse a PRD markdown document to extract milestone definitions and
|
|
189
|
-
* their `dependsOn` fields.
|
|
190
|
-
*
|
|
191
|
-
* Looks for milestone headers like:
|
|
192
|
-
* ### Milestone 1: Name Here
|
|
193
|
-
* ### Milestone 2 — Name Here
|
|
194
|
-
*
|
|
195
|
-
* And within each milestone section, looks for:
|
|
196
|
-
* **dependsOn:** 1, 3
|
|
197
|
-
* dependsOn: [1, 3]
|
|
198
|
-
* **dependsOn:** [1]
|
|
199
|
-
*
|
|
200
|
-
* If no `dependsOn` field is found, treats the milestone as having no
|
|
201
|
-
* dependencies (backward compatible).
|
|
202
|
-
*/
|
|
203
|
-
export function parseMilestoneDependencies(prdContent) {
|
|
204
|
-
const milestones = [];
|
|
205
|
-
// Split PRD into milestone sections
|
|
206
|
-
// Match headers like: ### Milestone 1: Name or ### Milestone 1 — Name
|
|
207
|
-
const milestoneHeaderRe = /###\s*Milestone\s+(\d+)\s*[:\—–-]\s*(.+)/g;
|
|
208
|
-
const headers = [];
|
|
209
|
-
let headerMatch;
|
|
210
|
-
while ((headerMatch = milestoneHeaderRe.exec(prdContent)) !== null) {
|
|
211
|
-
headers.push({
|
|
212
|
-
number: parseInt(headerMatch[1], 10),
|
|
213
|
-
name: headerMatch[2].trim(),
|
|
214
|
-
index: headerMatch.index,
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
// Extract section content for each milestone
|
|
218
|
-
for (let i = 0; i < headers.length; i++) {
|
|
219
|
-
const header = headers[i];
|
|
220
|
-
const sectionStart = header.index;
|
|
221
|
-
const sectionEnd = i + 1 < headers.length ? headers[i + 1].index : prdContent.length;
|
|
222
|
-
const sectionContent = prdContent.slice(sectionStart, sectionEnd);
|
|
223
|
-
// Look for dependsOn field in the section
|
|
224
|
-
const dependsOn = parseDependsOnField(sectionContent);
|
|
225
|
-
milestones.push({
|
|
226
|
-
number: header.number,
|
|
227
|
-
name: header.name,
|
|
228
|
-
dependsOn,
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
return milestones;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Parse the dependsOn field from a milestone section.
|
|
235
|
-
* Supports formats:
|
|
236
|
-
* **dependsOn:** 1, 3
|
|
237
|
-
* dependsOn: [1, 3]
|
|
238
|
-
* **dependsOn:** [1]
|
|
239
|
-
* **dependsOn:** none
|
|
240
|
-
*
|
|
241
|
-
* Returns empty array if not found or explicitly "none".
|
|
242
|
-
*/
|
|
243
|
-
function parseDependsOnField(sectionContent) {
|
|
244
|
-
// Match patterns like:
|
|
245
|
-
// **dependsOn:** 1, 3 (bold markdown: ** before key, :** after)
|
|
246
|
-
// dependsOn: [1, 3] (plain text)
|
|
247
|
-
// **dependsOn:** [1] (bold with brackets)
|
|
248
|
-
const dependsOnRe = /\*{0,2}dependsOn:\*{0,2}\s*(.+)/i;
|
|
249
|
-
const match = sectionContent.match(dependsOnRe);
|
|
250
|
-
if (!match) {
|
|
251
|
-
return [];
|
|
252
|
-
}
|
|
253
|
-
const value = match[1].trim();
|
|
254
|
-
// Handle "none" or empty
|
|
255
|
-
if (value.toLowerCase() === "none" ||
|
|
256
|
-
value === "[]" ||
|
|
257
|
-
value === "" ||
|
|
258
|
-
value === "-") {
|
|
259
|
-
return [];
|
|
260
|
-
}
|
|
261
|
-
// Strip brackets if present: [1, 3] -> 1, 3
|
|
262
|
-
const stripped = value.replace(/^\[/, "").replace(/\].*$/, "");
|
|
263
|
-
// Parse comma-separated numbers
|
|
264
|
-
const numbers = [];
|
|
265
|
-
for (const part of stripped.split(",")) {
|
|
266
|
-
const trimmed = part.trim();
|
|
267
|
-
const num = parseInt(trimmed, 10);
|
|
268
|
-
if (!isNaN(num)) {
|
|
269
|
-
numbers.push(num);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return numbers;
|
|
273
|
-
}
|
|
274
|
-
// ---------------------------------------------------------------------------
|
|
275
|
-
// getReadyMilestones
|
|
276
|
-
// ---------------------------------------------------------------------------
|
|
277
|
-
/**
|
|
278
|
-
* Given execution waves and a set of completed milestone numbers,
|
|
279
|
-
* return which milestones are ready to start.
|
|
280
|
-
*
|
|
281
|
-
* A milestone is ready if:
|
|
282
|
-
* 1. It has not been completed yet
|
|
283
|
-
* 2. All of its dependencies (from the wave schedule) are in the completed set
|
|
284
|
-
*
|
|
285
|
-
* This requires the original DAG to check dependencies, so we accept
|
|
286
|
-
* the waves plus the DAG.
|
|
287
|
-
*/
|
|
288
|
-
export function getReadyMilestones(dag, completed) {
|
|
289
|
-
const ready = [];
|
|
290
|
-
for (const [num, node] of dag) {
|
|
291
|
-
// Skip already completed
|
|
292
|
-
if (completed.has(num)) {
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
// Check if all parents are completed
|
|
296
|
-
const allParentsCompleted = node.parents.every((p) => completed.has(p));
|
|
297
|
-
if (allParentsCompleted) {
|
|
298
|
-
ready.push(num);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return ready.sort((a, b) => a - b);
|
|
302
|
-
}
|
|
303
|
-
// ---------------------------------------------------------------------------
|
|
304
|
-
// buildScheduleFromPRD — convenience function
|
|
305
|
-
// ---------------------------------------------------------------------------
|
|
306
|
-
/**
|
|
307
|
-
* Parse a PRD file and build the full execution schedule.
|
|
308
|
-
* Combines parseMilestoneDependencies + buildDAG + computeExecutionWaves.
|
|
309
|
-
*
|
|
310
|
-
* If the PRD has no `dependsOn` fields, all milestones will be in wave 1
|
|
311
|
-
* (all roots), which is backward compatible with sequential execution
|
|
312
|
-
* when the caller processes them in milestone-number order.
|
|
313
|
-
*/
|
|
314
|
-
export async function buildScheduleFromPRD(prdPath) {
|
|
315
|
-
const prdContent = await readFile(prdPath, "utf-8");
|
|
316
|
-
const milestones = parseMilestoneDependencies(prdContent);
|
|
317
|
-
if (milestones.length === 0) {
|
|
318
|
-
return {
|
|
319
|
-
waves: [],
|
|
320
|
-
totalMilestones: 0,
|
|
321
|
-
maxParallelism: 0,
|
|
322
|
-
isSequential: true,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
const dag = buildDAG(milestones);
|
|
326
|
-
return computeExecutionWaves(dag);
|
|
327
|
-
}
|
|
328
|
-
//# sourceMappingURL=parallel.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parallel.js","sourceRoot":"","sources":["../../src/worktree/parallel.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { UserIdentity } from "./identity.js";
|
|
2
|
-
export interface Session {
|
|
3
|
-
id: string;
|
|
4
|
-
user: string;
|
|
5
|
-
email: string;
|
|
6
|
-
skill: "go" | "spec";
|
|
7
|
-
milestone?: string;
|
|
8
|
-
/** PRD slug this session is executing (for multi-PRD queue tracking) */
|
|
9
|
-
prdSlug?: string;
|
|
10
|
-
branch: string;
|
|
11
|
-
worktreePath: string;
|
|
12
|
-
startedAt: string;
|
|
13
|
-
pid: number;
|
|
14
|
-
status: "active" | "stale" | "completing";
|
|
15
|
-
}
|
|
16
|
-
export interface SessionRegistry {
|
|
17
|
-
sessions: Session[];
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Get the path to the session registry file.
|
|
21
|
-
* Located at <repoRoot>/.forge/sessions.json
|
|
22
|
-
*/
|
|
23
|
-
export declare function getRegistryPath(repoRoot: string): string;
|
|
24
|
-
/**
|
|
25
|
-
* Load the session registry. Returns empty registry if file doesn't exist.
|
|
26
|
-
*/
|
|
27
|
-
export declare function loadRegistry(repoRoot: string): SessionRegistry;
|
|
28
|
-
/**
|
|
29
|
-
* Save the session registry atomically.
|
|
30
|
-
*/
|
|
31
|
-
export declare function saveRegistry(repoRoot: string, registry: SessionRegistry): void;
|
|
32
|
-
/**
|
|
33
|
-
* Register a new session. Returns the created session.
|
|
34
|
-
* Uses file-based locking to prevent lost updates from concurrent writes.
|
|
35
|
-
*/
|
|
36
|
-
export declare function registerSession(repoRoot: string, params: {
|
|
37
|
-
user: UserIdentity;
|
|
38
|
-
skill: "go" | "spec";
|
|
39
|
-
milestone?: string;
|
|
40
|
-
prdSlug?: string;
|
|
41
|
-
branch: string;
|
|
42
|
-
worktreePath: string;
|
|
43
|
-
}): Session;
|
|
44
|
-
/**
|
|
45
|
-
* Deregister (remove) a session by ID.
|
|
46
|
-
* Uses file-based locking to prevent lost updates.
|
|
47
|
-
*/
|
|
48
|
-
export declare function deregisterSession(repoRoot: string, sessionId: string): void;
|
|
49
|
-
/**
|
|
50
|
-
* Update a session's status.
|
|
51
|
-
* Uses file-based locking to prevent lost updates.
|
|
52
|
-
*/
|
|
53
|
-
export declare function updateSessionStatus(repoRoot: string, sessionId: string, status: Session["status"]): void;
|
|
54
|
-
/**
|
|
55
|
-
* Find and mark stale sessions.
|
|
56
|
-
* A session is stale if its PID is no longer running.
|
|
57
|
-
* Uses file-based locking to prevent lost updates.
|
|
58
|
-
*/
|
|
59
|
-
export declare function detectStaleSessions(repoRoot: string): Session[];
|
|
60
|
-
/**
|
|
61
|
-
* Get all active sessions.
|
|
62
|
-
*/
|
|
63
|
-
export declare function getActiveSessions(repoRoot: string): Session[];
|
|
64
|
-
/**
|
|
65
|
-
* Get a session by ID.
|
|
66
|
-
*/
|
|
67
|
-
export declare function getSession(repoRoot: string, sessionId: string): Session | null;
|
package/dist/worktree/session.js
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
import { openSync, closeSync, unlinkSync, mkdirSync, existsSync, } from "node:fs";
|
|
3
|
-
import { readJsonFileSync, writeJsonFileSync, generateSessionId, } from "../utils/platform.js";
|
|
4
|
-
/**
|
|
5
|
-
* Get the path to the session registry file.
|
|
6
|
-
* Located at <repoRoot>/.forge/sessions.json
|
|
7
|
-
*/
|
|
8
|
-
export function getRegistryPath(repoRoot) {
|
|
9
|
-
return join(repoRoot, ".forge", "sessions.json");
|
|
10
|
-
}
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// File-based lock for registry writes
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
const LOCK_RETRIES = 10;
|
|
15
|
-
const LOCK_RETRY_MS = 100;
|
|
16
|
-
function getLockPath(repoRoot) {
|
|
17
|
-
return join(repoRoot, ".forge", "sessions.lock");
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Acquire an exclusive lock file. Retries with backoff on contention.
|
|
21
|
-
* Uses O_CREAT|O_EXCL which atomically fails if the file exists.
|
|
22
|
-
*/
|
|
23
|
-
function acquireLock(repoRoot) {
|
|
24
|
-
const lockPath = getLockPath(repoRoot);
|
|
25
|
-
const dir = join(repoRoot, ".forge");
|
|
26
|
-
if (!existsSync(dir)) {
|
|
27
|
-
mkdirSync(dir, { recursive: true });
|
|
28
|
-
}
|
|
29
|
-
for (let attempt = 0; attempt < LOCK_RETRIES; attempt++) {
|
|
30
|
-
try {
|
|
31
|
-
// O_CREAT | O_EXCL | O_WRONLY — fails if file already exists
|
|
32
|
-
const fd = openSync(lockPath, "wx");
|
|
33
|
-
closeSync(fd);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
if (attempt === LOCK_RETRIES - 1) {
|
|
38
|
-
// Last attempt — force-remove stale lock and try once more
|
|
39
|
-
try {
|
|
40
|
-
unlinkSync(lockPath);
|
|
41
|
-
const fd = openSync(lockPath, "wx");
|
|
42
|
-
closeSync(fd);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
throw new Error(`Failed to acquire session registry lock at ${lockPath} after ${LOCK_RETRIES} attempts`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
// Busy-wait (sync context)
|
|
50
|
-
const waitUntil = Date.now() + LOCK_RETRY_MS;
|
|
51
|
-
while (Date.now() < waitUntil) {
|
|
52
|
-
// spin
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Release the lock file.
|
|
59
|
-
*/
|
|
60
|
-
function releaseLock(repoRoot) {
|
|
61
|
-
try {
|
|
62
|
-
unlinkSync(getLockPath(repoRoot));
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// Lock already removed — non-fatal
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Execute a callback while holding the registry lock.
|
|
70
|
-
* Ensures read-modify-write operations are serialized.
|
|
71
|
-
*/
|
|
72
|
-
function withRegistryLock(repoRoot, fn) {
|
|
73
|
-
acquireLock(repoRoot);
|
|
74
|
-
try {
|
|
75
|
-
return fn();
|
|
76
|
-
}
|
|
77
|
-
finally {
|
|
78
|
-
releaseLock(repoRoot);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Load the session registry. Returns empty registry if file doesn't exist.
|
|
83
|
-
*/
|
|
84
|
-
export function loadRegistry(repoRoot) {
|
|
85
|
-
const data = readJsonFileSync(getRegistryPath(repoRoot));
|
|
86
|
-
if (data === null) {
|
|
87
|
-
return { sessions: [] };
|
|
88
|
-
}
|
|
89
|
-
return data;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Save the session registry atomically.
|
|
93
|
-
*/
|
|
94
|
-
export function saveRegistry(repoRoot, registry) {
|
|
95
|
-
writeJsonFileSync(getRegistryPath(repoRoot), registry);
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Register a new session. Returns the created session.
|
|
99
|
-
* Uses file-based locking to prevent lost updates from concurrent writes.
|
|
100
|
-
*/
|
|
101
|
-
export function registerSession(repoRoot, params) {
|
|
102
|
-
return withRegistryLock(repoRoot, () => {
|
|
103
|
-
const registry = loadRegistry(repoRoot);
|
|
104
|
-
const session = {
|
|
105
|
-
id: generateSessionId(),
|
|
106
|
-
user: params.user.name,
|
|
107
|
-
email: params.user.email,
|
|
108
|
-
skill: params.skill,
|
|
109
|
-
milestone: params.milestone,
|
|
110
|
-
prdSlug: params.prdSlug,
|
|
111
|
-
branch: params.branch,
|
|
112
|
-
worktreePath: params.worktreePath,
|
|
113
|
-
startedAt: new Date().toISOString(),
|
|
114
|
-
pid: process.pid,
|
|
115
|
-
status: "active",
|
|
116
|
-
};
|
|
117
|
-
registry.sessions.push(session);
|
|
118
|
-
saveRegistry(repoRoot, registry);
|
|
119
|
-
return session;
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Deregister (remove) a session by ID.
|
|
124
|
-
* Uses file-based locking to prevent lost updates.
|
|
125
|
-
*/
|
|
126
|
-
export function deregisterSession(repoRoot, sessionId) {
|
|
127
|
-
withRegistryLock(repoRoot, () => {
|
|
128
|
-
const registry = loadRegistry(repoRoot);
|
|
129
|
-
registry.sessions = registry.sessions.filter((s) => s.id !== sessionId);
|
|
130
|
-
saveRegistry(repoRoot, registry);
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Update a session's status.
|
|
135
|
-
* Uses file-based locking to prevent lost updates.
|
|
136
|
-
*/
|
|
137
|
-
export function updateSessionStatus(repoRoot, sessionId, status) {
|
|
138
|
-
withRegistryLock(repoRoot, () => {
|
|
139
|
-
const registry = loadRegistry(repoRoot);
|
|
140
|
-
const session = registry.sessions.find((s) => s.id === sessionId);
|
|
141
|
-
if (session) {
|
|
142
|
-
session.status = status;
|
|
143
|
-
saveRegistry(repoRoot, registry);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Check if a process with the given PID is still running.
|
|
149
|
-
*/
|
|
150
|
-
function isPidAlive(pid) {
|
|
151
|
-
try {
|
|
152
|
-
process.kill(pid, 0);
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
catch {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Find and mark stale sessions.
|
|
161
|
-
* A session is stale if its PID is no longer running.
|
|
162
|
-
* Uses file-based locking to prevent lost updates.
|
|
163
|
-
*/
|
|
164
|
-
export function detectStaleSessions(repoRoot) {
|
|
165
|
-
return withRegistryLock(repoRoot, () => {
|
|
166
|
-
const registry = loadRegistry(repoRoot);
|
|
167
|
-
const newlyStale = [];
|
|
168
|
-
for (const session of registry.sessions) {
|
|
169
|
-
if (session.status === "active" && !isPidAlive(session.pid)) {
|
|
170
|
-
session.status = "stale";
|
|
171
|
-
newlyStale.push(session);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
if (newlyStale.length > 0) {
|
|
175
|
-
saveRegistry(repoRoot, registry);
|
|
176
|
-
}
|
|
177
|
-
return newlyStale;
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Get all active sessions.
|
|
182
|
-
*/
|
|
183
|
-
export function getActiveSessions(repoRoot) {
|
|
184
|
-
const registry = loadRegistry(repoRoot);
|
|
185
|
-
return registry.sessions.filter((s) => s.status === "active");
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Get a session by ID.
|
|
189
|
-
*/
|
|
190
|
-
export function getSession(repoRoot, sessionId) {
|
|
191
|
-
const registry = loadRegistry(repoRoot);
|
|
192
|
-
return registry.sessions.find((s) => s.id === sessionId) ?? null;
|
|
193
|
-
}
|
|
194
|
-
//# sourceMappingURL=session.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/worktree/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export interface MergeResult {
|
|
2
|
-
stateUpdated: boolean;
|
|
3
|
-
roadmapUpdated: boolean;
|
|
4
|
-
warnings: string[];
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Merge state from a completed worktree session back to the main repo.
|
|
8
|
-
*
|
|
9
|
-
* - STATE.md: updates the milestone row with completion status and the
|
|
10
|
-
* Last Session date.
|
|
11
|
-
* - ROADMAP.md: marks the milestone row as complete with the given date.
|
|
12
|
-
*
|
|
13
|
-
* Uses synchronous I/O throughout — consistent with all other worktree modules.
|
|
14
|
-
*
|
|
15
|
-
* @param mainRepoDir - Absolute path to the main repository
|
|
16
|
-
* @param worktreeDir - Absolute path to the completed worktree
|
|
17
|
-
* @param milestoneNumber - The milestone that was completed
|
|
18
|
-
* @param completionDate - Date string (YYYY-MM-DD) for the completion
|
|
19
|
-
*/
|
|
20
|
-
export declare function mergeSessionState(mainRepoDir: string, worktreeDir: string, milestoneNumber: number, completionDate: string): MergeResult;
|
|
21
|
-
/**
|
|
22
|
-
* Update a specific milestone row in a ROADMAP.md file.
|
|
23
|
-
* Uses structured line-by-line parsing (not regex replace) to safely
|
|
24
|
-
* update the status column of the milestone's table row.
|
|
25
|
-
*
|
|
26
|
-
* @param roadmapPath - Absolute path to ROADMAP.md
|
|
27
|
-
* @param milestoneNumber - Which milestone to update
|
|
28
|
-
* @param newStatus - New status string (e.g., "Complete (2026-02-15)")
|
|
29
|
-
* @returns true if the milestone was found and updated
|
|
30
|
-
*/
|
|
31
|
-
export declare function updateRoadmapMilestoneStatus(roadmapPath: string, milestoneNumber: number, newStatus: string): boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Update the milestone progress table in STATE.md.
|
|
34
|
-
* Reads the current STATE.md, finds the milestone row, updates its status.
|
|
35
|
-
* Also updates the `**Last Session:**` date if present.
|
|
36
|
-
* Preserves all other content.
|
|
37
|
-
*
|
|
38
|
-
* @param statePath - Absolute path to STATE.md
|
|
39
|
-
* @param milestoneNumber - Which milestone to update
|
|
40
|
-
* @param newStatus - New status string
|
|
41
|
-
* @returns true if the milestone was found and updated
|
|
42
|
-
*/
|
|
43
|
-
export declare function updateStateMilestoneRow(statePath: string, milestoneNumber: number, newStatus: string): boolean;
|