@lumenflow/cli 3.14.0 → 3.15.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/dist/chunk-2D2VOCA4.js +37 -0
- package/dist/chunk-2D5KFYGX.js +284 -0
- package/dist/chunk-2GXVIN57.js +14072 -0
- package/dist/chunk-2MQ7HZWZ.js +26 -0
- package/dist/chunk-2UFQ3A3C.js +643 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-4N74J3UT.js +15 -0
- package/dist/chunk-5GTOXFYR.js +392 -0
- package/dist/chunk-5VY6MQMC.js +240 -0
- package/dist/chunk-67XVPMRY.js +1297 -0
- package/dist/chunk-6HO4GWJE.js +164 -0
- package/dist/chunk-6W5XHWYV.js +1890 -0
- package/dist/chunk-6X4EMYJQ.js +64 -0
- package/dist/chunk-6XYXI2NQ.js +772 -0
- package/dist/chunk-7ANSOV6Q.js +285 -0
- package/dist/chunk-A624LFLB.js +1380 -0
- package/dist/chunk-ADN5NHG4.js +126 -0
- package/dist/chunk-B7YJYJKG.js +33 -0
- package/dist/chunk-CCLHCPKG.js +210 -0
- package/dist/chunk-CK36VROC.js +1584 -0
- package/dist/chunk-D3UOFRSB.js +81 -0
- package/dist/chunk-DFR4DJBM.js +230 -0
- package/dist/chunk-DSYBDHYH.js +79 -0
- package/dist/chunk-DWMLTXKQ.js +1176 -0
- package/dist/chunk-E3REJTAJ.js +28 -0
- package/dist/chunk-EA3IVO64.js +633 -0
- package/dist/chunk-EK2AKZKD.js +55 -0
- package/dist/chunk-ELD7JTTT.js +343 -0
- package/dist/chunk-EX6TT2XI.js +195 -0
- package/dist/chunk-EXINSFZE.js +82 -0
- package/dist/chunk-EZ6ZBYBM.js +510 -0
- package/dist/chunk-FBKAPTJ2.js +16 -0
- package/dist/chunk-FVLV5RYH.js +1118 -0
- package/dist/chunk-GDNSBQVK.js +2485 -0
- package/dist/chunk-GPQHMBNN.js +278 -0
- package/dist/chunk-GTFJB67L.js +68 -0
- package/dist/chunk-HANJXVKW.js +1127 -0
- package/dist/chunk-HEVS5YLD.js +269 -0
- package/dist/chunk-HMEVZKPQ.js +9 -0
- package/dist/chunk-HRGSYNLM.js +3511 -0
- package/dist/chunk-ISZR5N4K.js +60 -0
- package/dist/chunk-J6SUPR2C.js +226 -0
- package/dist/chunk-JERYVEIZ.js +244 -0
- package/dist/chunk-JHHWGL2N.js +87 -0
- package/dist/chunk-JONWQUB5.js +775 -0
- package/dist/chunk-K2DIWWDM.js +1766 -0
- package/dist/chunk-KY4PGL5V.js +969 -0
- package/dist/chunk-L737LQ4C.js +1285 -0
- package/dist/chunk-LFTWYIB2.js +497 -0
- package/dist/chunk-LV47RFNJ.js +41 -0
- package/dist/chunk-MKSAITI7.js +15 -0
- package/dist/chunk-MZ7RKIX4.js +212 -0
- package/dist/chunk-NAP6CFSO.js +84 -0
- package/dist/chunk-ND6MY37M.js +16 -0
- package/dist/chunk-NMG736UR.js +683 -0
- package/dist/chunk-NRAXROED.js +32 -0
- package/dist/chunk-NRIZR3A7.js +690 -0
- package/dist/chunk-NX43BG3M.js +233 -0
- package/dist/chunk-O645XLSI.js +297 -0
- package/dist/chunk-OMJD6A3S.js +235 -0
- package/dist/chunk-QB6SJD4T.js +430 -0
- package/dist/chunk-QFSTL4J3.js +276 -0
- package/dist/chunk-QLGDFMFX.js +212 -0
- package/dist/chunk-RIAAGL2E.js +13 -0
- package/dist/chunk-RWO5XMZ6.js +86 -0
- package/dist/chunk-RXRKBBSM.js +149 -0
- package/dist/chunk-RZOZMML6.js +363 -0
- package/dist/chunk-U7I7FS7T.js +113 -0
- package/dist/chunk-UI42RODY.js +717 -0
- package/dist/chunk-UTVMVSCO.js +519 -0
- package/dist/chunk-V6OJGLBA.js +1746 -0
- package/dist/chunk-W2JHVH7D.js +152 -0
- package/dist/chunk-WD3Y7VQN.js +280 -0
- package/dist/chunk-WOCTQ5MS.js +303 -0
- package/dist/chunk-WZR3ZUNN.js +696 -0
- package/dist/chunk-XGI665H7.js +150 -0
- package/dist/chunk-XKY65P2T.js +304 -0
- package/dist/chunk-Y4CQZY65.js +57 -0
- package/dist/chunk-YFEXKLVE.js +194 -0
- package/dist/chunk-YHO3HS5X.js +287 -0
- package/dist/chunk-YLS7AZSX.js +738 -0
- package/dist/chunk-ZE473AO6.js +49 -0
- package/dist/chunk-ZF747T3O.js +644 -0
- package/dist/chunk-ZHCZHZH3.js +43 -0
- package/dist/chunk-ZZNZX2XY.js +87 -0
- package/dist/constants-7QAP3VQ4.js +23 -0
- package/dist/dist-IY3UUMWK.js +33 -0
- package/dist/docs-sync.js +60 -25
- package/dist/docs-sync.js.map +1 -1
- package/dist/gates-runners.js +43 -2
- package/dist/gates-runners.js.map +1 -1
- package/dist/init-templates.js +26 -219
- package/dist/init-templates.js.map +1 -1
- package/dist/invariants-runner-W5RGHCSU.js +27 -0
- package/dist/lane-lock-6J36HD5O.js +35 -0
- package/dist/lumenflow-upgrade.js +60 -0
- package/dist/lumenflow-upgrade.js.map +1 -1
- package/dist/mem-checkpoint-core-EANG2GVN.js +14 -0
- package/dist/mem-signal-core-2LZ2WYHW.js +19 -0
- package/dist/memory-store-OLB5FO7K.js +18 -0
- package/dist/plan-edit.js +19 -24
- package/dist/plan-edit.js.map +1 -1
- package/dist/plan-promote.js +15 -23
- package/dist/plan-promote.js.map +1 -1
- package/dist/plan-resolve.js +111 -0
- package/dist/plan-resolve.js.map +1 -0
- package/dist/public-manifest.js +2 -2
- package/dist/public-manifest.js.map +1 -1
- package/dist/service-6BYCOCO5.js +13 -0
- package/dist/spawn-policy-resolver-NTSZYQ6R.js +17 -0
- package/dist/spawn-task-builder-R4E2BHSW.js +22 -0
- package/dist/sync-templates.js +12 -0
- package/dist/sync-templates.js.map +1 -1
- package/dist/wu-claim-validation.js +9 -1
- package/dist/wu-claim-validation.js.map +1 -1
- package/dist/wu-done-pr-WLFFFEPJ.js +25 -0
- package/dist/wu-done-validation-3J5E36FE.js +30 -0
- package/dist/wu-done.js +42 -1
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-duplicate-id-detector-5S7JHELK.js +232 -0
- package/dist/wu-edit-operations.js +7 -6
- package/dist/wu-edit-operations.js.map +1 -1
- package/dist/wu-edit.js +23 -3
- package/dist/wu-edit.js.map +1 -1
- package/dist/wu-spawn-prompt-builders.js +38 -1
- package/dist/wu-spawn-prompt-builders.js.map +1 -1
- package/package.json +8 -8
- package/packs/sidekick/.turbo/turbo-build.log +1 -1
- package/packs/sidekick/.turbo/turbo-typecheck.log +4 -0
- package/packs/sidekick/package.json +1 -1
- package/packs/software-delivery/.turbo/turbo-build.log +1 -1
- package/packs/software-delivery/.turbo/turbo-typecheck.log +4 -0
- package/packs/software-delivery/package.json +1 -1
- package/templates/core/AGENTS.md.template +19 -0
- package/templates/core/LUMENFLOW.md.template +13 -2
- package/templates/core/UPGRADING.md.template +6 -6
- package/templates/core/ai/onboarding/first-15-mins.md.template +1 -1
- package/templates/core/ai/onboarding/first-wu-mistakes.md.template +10 -0
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +11 -8
- package/templates/core/ai/onboarding/starting-prompt.md.template +1 -1
- package/templates/core/ai/onboarding/wu-sizing-guide.md.template +11 -2
- package/templates/vendors/cursor/.cursor/rules/lumenflow.md.template +9 -1
- package/templates/vendors/windsurf/.windsurf/rules/lumenflow.md.template +9 -1
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listTrackedWUStampIds
|
|
3
|
+
} from "./chunk-LV47RFNJ.js";
|
|
4
|
+
import {
|
|
5
|
+
WU_EVENT_TYPE,
|
|
6
|
+
withMicroWorktree
|
|
7
|
+
} from "./chunk-2GXVIN57.js";
|
|
8
|
+
import {
|
|
9
|
+
createGitForPath
|
|
10
|
+
} from "./chunk-2UFQ3A3C.js";
|
|
11
|
+
import {
|
|
12
|
+
normalizeToDateString,
|
|
13
|
+
parseYAML,
|
|
14
|
+
stringifyYAML,
|
|
15
|
+
todayISO
|
|
16
|
+
} from "./chunk-NRIZR3A7.js";
|
|
17
|
+
import {
|
|
18
|
+
createWuPaths
|
|
19
|
+
} from "./chunk-6HO4GWJE.js";
|
|
20
|
+
import {
|
|
21
|
+
CONSISTENCY_MESSAGES,
|
|
22
|
+
CONSISTENCY_TYPES,
|
|
23
|
+
LOG_PREFIX,
|
|
24
|
+
LUMENFLOW_PATHS,
|
|
25
|
+
REMOTES,
|
|
26
|
+
STRING_LITERALS,
|
|
27
|
+
YAML_OPTIONS,
|
|
28
|
+
toKebab
|
|
29
|
+
} from "./chunk-DWMLTXKQ.js";
|
|
30
|
+
import {
|
|
31
|
+
WU_STATUS
|
|
32
|
+
} from "./chunk-V6OJGLBA.js";
|
|
33
|
+
import {
|
|
34
|
+
ErrorCodes,
|
|
35
|
+
createError
|
|
36
|
+
} from "./chunk-RXRKBBSM.js";
|
|
37
|
+
|
|
38
|
+
// ../core/dist/wu-consistency-detector.js
|
|
39
|
+
import { readFile, readdir, access } from "fs/promises";
|
|
40
|
+
import { constants } from "fs";
|
|
41
|
+
import path from "path";
|
|
42
|
+
var UNKNOWN_WU_STATUS = "unknown";
|
|
43
|
+
function readOptionalStringField(value, fieldName) {
|
|
44
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
45
|
+
return void 0;
|
|
46
|
+
}
|
|
47
|
+
const fieldValue = Reflect.get(value, fieldName);
|
|
48
|
+
return typeof fieldValue === "string" ? fieldValue : void 0;
|
|
49
|
+
}
|
|
50
|
+
function parseWUConsistencyDoc(content) {
|
|
51
|
+
const parsed = parseYAML(content);
|
|
52
|
+
return {
|
|
53
|
+
status: readOptionalStringField(parsed, "status"),
|
|
54
|
+
lane: readOptionalStringField(parsed, "lane"),
|
|
55
|
+
title: readOptionalStringField(parsed, "title"),
|
|
56
|
+
worktree_path: readOptionalStringField(parsed, "worktree_path")
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async function checkWUConsistency(id, projectRoot = process.cwd(), options = {}) {
|
|
60
|
+
const errors = [];
|
|
61
|
+
const paths = createWuPaths({ projectRoot });
|
|
62
|
+
const wuPath = path.join(projectRoot, paths.WU(id));
|
|
63
|
+
const stampPath = path.join(projectRoot, paths.STAMP(id));
|
|
64
|
+
const backlogPath = path.join(projectRoot, paths.BACKLOG());
|
|
65
|
+
const statusPath = path.join(projectRoot, paths.STATUS());
|
|
66
|
+
try {
|
|
67
|
+
await access(wuPath, constants.R_OK);
|
|
68
|
+
} catch {
|
|
69
|
+
return { valid: true, errors: [], stats: { wuExists: false } };
|
|
70
|
+
}
|
|
71
|
+
const wuContent = await readFile(wuPath, { encoding: "utf-8" });
|
|
72
|
+
const wuDoc = parseWUConsistencyDoc(wuContent);
|
|
73
|
+
const yamlStatus = wuDoc.status || UNKNOWN_WU_STATUS;
|
|
74
|
+
const lane = wuDoc.lane || "";
|
|
75
|
+
const title = wuDoc.title || "";
|
|
76
|
+
const worktreePathFromYaml = wuDoc.worktree_path || "";
|
|
77
|
+
let hasStampFile;
|
|
78
|
+
try {
|
|
79
|
+
await access(stampPath, constants.R_OK);
|
|
80
|
+
hasStampFile = true;
|
|
81
|
+
} catch {
|
|
82
|
+
hasStampFile = false;
|
|
83
|
+
}
|
|
84
|
+
const trackedStampIds = options.trackedStampIds ?? await listTrackedWUStampIds({ projectRoot, stampsDir: paths.STAMPS_DIR() });
|
|
85
|
+
const hasStamp = hasStampFile && (trackedStampIds === null || trackedStampIds.has(id));
|
|
86
|
+
let backlogContent;
|
|
87
|
+
try {
|
|
88
|
+
backlogContent = await readFile(backlogPath, { encoding: "utf-8" });
|
|
89
|
+
} catch {
|
|
90
|
+
backlogContent = "";
|
|
91
|
+
}
|
|
92
|
+
const { inDone: backlogInDone, inProgress: backlogInProgress } = parseBacklogSections(backlogContent, id);
|
|
93
|
+
let statusContent;
|
|
94
|
+
try {
|
|
95
|
+
statusContent = await readFile(statusPath, { encoding: "utf-8" });
|
|
96
|
+
} catch {
|
|
97
|
+
statusContent = "";
|
|
98
|
+
}
|
|
99
|
+
const { inProgress: statusInProgress } = parseStatusSections(statusContent, id);
|
|
100
|
+
const normalizedId = id.toUpperCase();
|
|
101
|
+
const hasWorktree = options.activeWorktreeIds !== void 0 ? options.activeWorktreeIds !== null && options.activeWorktreeIds.has(normalizedId) : await checkWorktreeExists(id, projectRoot);
|
|
102
|
+
const worktreePathExists = await checkWorktreePathExists(worktreePathFromYaml);
|
|
103
|
+
if (yamlStatus === WU_STATUS.DONE && statusInProgress) {
|
|
104
|
+
errors.push({
|
|
105
|
+
type: CONSISTENCY_TYPES.YAML_DONE_STATUS_IN_PROGRESS,
|
|
106
|
+
wuId: id,
|
|
107
|
+
description: `WU ${id} has status '${WU_STATUS.DONE}' in YAML but still appears in status.md In Progress section`,
|
|
108
|
+
repairAction: "Remove from status.md In Progress section",
|
|
109
|
+
canAutoRepair: true
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (backlogInDone && backlogInProgress) {
|
|
113
|
+
errors.push({
|
|
114
|
+
type: CONSISTENCY_TYPES.BACKLOG_DUAL_SECTION,
|
|
115
|
+
wuId: id,
|
|
116
|
+
description: `WU ${id} appears in both Done and In Progress sections of backlog.md`,
|
|
117
|
+
repairAction: "Remove from In Progress section (Done wins)",
|
|
118
|
+
canAutoRepair: true
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (yamlStatus === WU_STATUS.DONE && !hasStamp) {
|
|
122
|
+
errors.push({
|
|
123
|
+
type: CONSISTENCY_TYPES.YAML_DONE_NO_STAMP,
|
|
124
|
+
wuId: id,
|
|
125
|
+
title,
|
|
126
|
+
description: `WU ${id} has status '${WU_STATUS.DONE}' but no stamp file exists`,
|
|
127
|
+
repairAction: "Create stamp file",
|
|
128
|
+
canAutoRepair: true
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (yamlStatus === WU_STATUS.DONE && hasWorktree) {
|
|
132
|
+
errors.push({
|
|
133
|
+
type: CONSISTENCY_TYPES.ORPHAN_WORKTREE_DONE,
|
|
134
|
+
wuId: id,
|
|
135
|
+
lane,
|
|
136
|
+
description: `WU ${id} has status '${WU_STATUS.DONE}' but still has an associated worktree`,
|
|
137
|
+
repairAction: "Remove orphan worktree and lane branch",
|
|
138
|
+
canAutoRepair: true
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (hasStamp && yamlStatus !== WU_STATUS.DONE) {
|
|
142
|
+
errors.push({
|
|
143
|
+
type: CONSISTENCY_TYPES.STAMP_EXISTS_YAML_NOT_DONE,
|
|
144
|
+
wuId: id,
|
|
145
|
+
title,
|
|
146
|
+
description: `WU ${id} has stamp file but YAML status is '${yamlStatus}' (not done)`,
|
|
147
|
+
repairAction: "Update YAML to done+locked+completed",
|
|
148
|
+
canAutoRepair: true
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (worktreePathFromYaml && !worktreePathExists && (yamlStatus === WU_STATUS.IN_PROGRESS || yamlStatus === WU_STATUS.BLOCKED)) {
|
|
152
|
+
errors.push({
|
|
153
|
+
type: CONSISTENCY_TYPES.MISSING_WORKTREE_CLAIMED,
|
|
154
|
+
wuId: id,
|
|
155
|
+
title,
|
|
156
|
+
description: CONSISTENCY_MESSAGES.MISSING_WORKTREE_CLAIMED(id, yamlStatus, worktreePathFromYaml),
|
|
157
|
+
repairAction: CONSISTENCY_MESSAGES.MISSING_WORKTREE_CLAIMED_REPAIR,
|
|
158
|
+
canAutoRepair: false
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
valid: errors.length === 0,
|
|
163
|
+
errors,
|
|
164
|
+
stats: {
|
|
165
|
+
yamlStatus,
|
|
166
|
+
hasStamp,
|
|
167
|
+
backlogInDone,
|
|
168
|
+
backlogInProgress,
|
|
169
|
+
statusInProgress,
|
|
170
|
+
hasWorktree,
|
|
171
|
+
worktreePathExists
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async function checkAllWUConsistency(projectRoot = process.cwd()) {
|
|
176
|
+
const paths = createWuPaths({ projectRoot });
|
|
177
|
+
const wuDir = path.join(projectRoot, paths.WU_DIR());
|
|
178
|
+
try {
|
|
179
|
+
await access(wuDir, constants.R_OK);
|
|
180
|
+
} catch {
|
|
181
|
+
return { valid: true, errors: [], checked: 0 };
|
|
182
|
+
}
|
|
183
|
+
const allErrors = [];
|
|
184
|
+
const wuFiles = (await readdir(wuDir)).filter((f) => /^WU-\d+\.yaml$/.test(f));
|
|
185
|
+
const trackedStampIds = await listTrackedWUStampIds({
|
|
186
|
+
projectRoot,
|
|
187
|
+
stampsDir: paths.STAMPS_DIR()
|
|
188
|
+
});
|
|
189
|
+
const activeWorktreeIds = await listActiveWorktreeIds(projectRoot);
|
|
190
|
+
for (const file of wuFiles) {
|
|
191
|
+
const id = file.replace(".yaml", "");
|
|
192
|
+
const report = await checkWUConsistency(id, projectRoot, {
|
|
193
|
+
trackedStampIds,
|
|
194
|
+
activeWorktreeIds
|
|
195
|
+
});
|
|
196
|
+
allErrors.push(...report.errors);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
valid: allErrors.length === 0,
|
|
200
|
+
errors: allErrors,
|
|
201
|
+
checked: wuFiles.length
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
async function checkLaneForOrphanDoneWU(lane, excludeId, projectRoot = process.cwd()) {
|
|
205
|
+
const paths = createWuPaths({ projectRoot });
|
|
206
|
+
const wuDir = path.join(projectRoot, paths.WU_DIR());
|
|
207
|
+
const trackedStampIds = await listTrackedWUStampIds({
|
|
208
|
+
projectRoot,
|
|
209
|
+
stampsDir: paths.STAMPS_DIR()
|
|
210
|
+
});
|
|
211
|
+
try {
|
|
212
|
+
await access(wuDir, constants.R_OK);
|
|
213
|
+
} catch {
|
|
214
|
+
return { valid: true, orphans: [] };
|
|
215
|
+
}
|
|
216
|
+
const orphans = [];
|
|
217
|
+
const wuFiles = (await readdir(wuDir)).filter((f) => /^WU-\d+\.yaml$/.test(f));
|
|
218
|
+
for (const file of wuFiles) {
|
|
219
|
+
const id = file.replace(".yaml", "");
|
|
220
|
+
if (id === excludeId)
|
|
221
|
+
continue;
|
|
222
|
+
const wuPath = path.join(wuDir, file);
|
|
223
|
+
let wuContent;
|
|
224
|
+
try {
|
|
225
|
+
wuContent = await readFile(wuPath, { encoding: "utf-8" });
|
|
226
|
+
} catch {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
let wuDoc;
|
|
230
|
+
try {
|
|
231
|
+
wuDoc = parseYAML(wuContent);
|
|
232
|
+
} catch {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (wuDoc?.lane === lane && wuDoc?.status === WU_STATUS.DONE) {
|
|
236
|
+
const report = await checkWUConsistency(id, projectRoot, { trackedStampIds });
|
|
237
|
+
if (!report.valid) {
|
|
238
|
+
orphans.push({ id, errors: report.errors });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
valid: orphans.length === 0,
|
|
244
|
+
orphans: orphans.map((o) => o.id),
|
|
245
|
+
reports: orphans
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function parseBacklogSections(content, id) {
|
|
249
|
+
const lines = content.split(/\r?\n/);
|
|
250
|
+
let inDone = false;
|
|
251
|
+
let inProgress = false;
|
|
252
|
+
let currentSection = null;
|
|
253
|
+
const exactPattern = `(wu/${id}.yaml)`;
|
|
254
|
+
for (const line of lines) {
|
|
255
|
+
if (line.trim() === "## \u2705 Done") {
|
|
256
|
+
currentSection = WU_STATUS.DONE;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (line.trim() === "## \u{1F527} In progress") {
|
|
260
|
+
currentSection = WU_STATUS.IN_PROGRESS;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (line.trim().startsWith("## ")) {
|
|
264
|
+
currentSection = null;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (line.includes(exactPattern)) {
|
|
268
|
+
if (currentSection === WU_STATUS.DONE)
|
|
269
|
+
inDone = true;
|
|
270
|
+
if (currentSection === WU_STATUS.IN_PROGRESS)
|
|
271
|
+
inProgress = true;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return { inDone, inProgress };
|
|
275
|
+
}
|
|
276
|
+
function parseStatusSections(content, id) {
|
|
277
|
+
const lines = content.split(/\r?\n/);
|
|
278
|
+
let inProgress = false;
|
|
279
|
+
let currentSection = null;
|
|
280
|
+
const exactPattern = `(wu/${id}.yaml)`;
|
|
281
|
+
for (const line of lines) {
|
|
282
|
+
if (line.trim() === "## In Progress") {
|
|
283
|
+
currentSection = WU_STATUS.IN_PROGRESS;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (line.trim().startsWith("## ")) {
|
|
287
|
+
currentSection = null;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (currentSection === WU_STATUS.IN_PROGRESS && line.includes(exactPattern)) {
|
|
291
|
+
inProgress = true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return { inProgress };
|
|
295
|
+
}
|
|
296
|
+
async function checkWorktreeExists(id, projectRoot) {
|
|
297
|
+
try {
|
|
298
|
+
const git = createGitForPath(projectRoot);
|
|
299
|
+
const output = await git.worktreeList();
|
|
300
|
+
const pattern = new RegExp(`${id.toLowerCase()}(?![0-9])`, "i");
|
|
301
|
+
return pattern.test(output);
|
|
302
|
+
} catch {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async function listActiveWorktreeIds(projectRoot) {
|
|
307
|
+
try {
|
|
308
|
+
const git = createGitForPath(projectRoot);
|
|
309
|
+
const output = await git.worktreeList();
|
|
310
|
+
const matches = output.match(/\bwu-\d+\b/gi) ?? [];
|
|
311
|
+
const ids = /* @__PURE__ */ new Set();
|
|
312
|
+
for (const match of matches) {
|
|
313
|
+
ids.add(match.toUpperCase());
|
|
314
|
+
}
|
|
315
|
+
return ids;
|
|
316
|
+
} catch {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async function checkWorktreePathExists(worktreePath) {
|
|
321
|
+
if (!worktreePath) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
await access(worktreePath, constants.R_OK);
|
|
326
|
+
return true;
|
|
327
|
+
} catch {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ../core/dist/wu-consistency-file-repairs.js
|
|
333
|
+
import path2 from "path";
|
|
334
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, constants as constants2 } from "fs";
|
|
335
|
+
import { access as access2 } from "fs/promises";
|
|
336
|
+
async function createStampInWorktree(id, title, worktreePath, projectRoot) {
|
|
337
|
+
const paths = createWuPaths({ projectRoot });
|
|
338
|
+
const stampsDir = path2.join(worktreePath, paths.STAMPS_DIR());
|
|
339
|
+
const stampRelPath = paths.STAMP(id);
|
|
340
|
+
const stampAbsPath = path2.join(worktreePath, stampRelPath);
|
|
341
|
+
if (!existsSync(stampsDir)) {
|
|
342
|
+
mkdirSync(stampsDir, { recursive: true });
|
|
343
|
+
}
|
|
344
|
+
if (existsSync(stampAbsPath)) {
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
const body = `WU ${id} \u2014 ${title}
|
|
348
|
+
Completed: ${todayISO()}
|
|
349
|
+
`;
|
|
350
|
+
writeFileSync(stampAbsPath, body, { encoding: "utf-8" });
|
|
351
|
+
return [stampRelPath];
|
|
352
|
+
}
|
|
353
|
+
async function updateYamlToDoneInWorktree(id, worktreePath, projectRoot) {
|
|
354
|
+
const paths = createWuPaths({ projectRoot });
|
|
355
|
+
const wuRelPath = paths.WU(id);
|
|
356
|
+
const wuSrcPath = path2.join(projectRoot, wuRelPath);
|
|
357
|
+
const wuDestPath = path2.join(worktreePath, wuRelPath);
|
|
358
|
+
const wuReadPath = existsSync(wuDestPath) ? wuDestPath : wuSrcPath;
|
|
359
|
+
const content = readFileSync(wuReadPath, { encoding: "utf-8" });
|
|
360
|
+
const wuDoc = parseYAML(content);
|
|
361
|
+
if (!wuDoc) {
|
|
362
|
+
throw createError(ErrorCodes.YAML_PARSE_ERROR, `Failed to parse WU YAML: ${wuReadPath}`);
|
|
363
|
+
}
|
|
364
|
+
wuDoc.status = WU_STATUS.DONE;
|
|
365
|
+
wuDoc.locked = true;
|
|
366
|
+
const existingCompletedAt = wuDoc.completed_at;
|
|
367
|
+
const completionTimestamp = typeof existingCompletedAt === "string" ? existingCompletedAt : existingCompletedAt instanceof Date ? existingCompletedAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
368
|
+
wuDoc.completed_at = completionTimestamp;
|
|
369
|
+
wuDoc.completed = normalizeToDateString(wuDoc.completed ?? completionTimestamp) ?? completionTimestamp.slice(0, 10);
|
|
370
|
+
const wuDir = path2.dirname(wuDestPath);
|
|
371
|
+
if (!existsSync(wuDir)) {
|
|
372
|
+
mkdirSync(wuDir, { recursive: true });
|
|
373
|
+
}
|
|
374
|
+
const updatedContent = stringifyYAML(wuDoc, { lineWidth: YAML_OPTIONS.LINE_WIDTH });
|
|
375
|
+
writeFileSync(wuDestPath, updatedContent, { encoding: "utf-8" });
|
|
376
|
+
const eventFiles = appendReconciliationEventsInWorktree({
|
|
377
|
+
id,
|
|
378
|
+
lane: wuDoc.lane,
|
|
379
|
+
title: wuDoc.title,
|
|
380
|
+
projectRoot,
|
|
381
|
+
worktreePath
|
|
382
|
+
});
|
|
383
|
+
return [wuRelPath, ...eventFiles];
|
|
384
|
+
}
|
|
385
|
+
function deriveStatusFromEvents(eventsContent, wuId) {
|
|
386
|
+
let status;
|
|
387
|
+
const lines = eventsContent.split(/\r?\n/);
|
|
388
|
+
for (const line of lines) {
|
|
389
|
+
if (!line.trim())
|
|
390
|
+
continue;
|
|
391
|
+
try {
|
|
392
|
+
const event = JSON.parse(line);
|
|
393
|
+
if (event.wuId !== wuId || !event.type)
|
|
394
|
+
continue;
|
|
395
|
+
switch (event.type) {
|
|
396
|
+
case WU_EVENT_TYPE.CLAIM:
|
|
397
|
+
case WU_EVENT_TYPE.CREATE:
|
|
398
|
+
status = WU_STATUS.IN_PROGRESS;
|
|
399
|
+
break;
|
|
400
|
+
case WU_EVENT_TYPE.RELEASE:
|
|
401
|
+
status = WU_STATUS.READY;
|
|
402
|
+
break;
|
|
403
|
+
case WU_EVENT_TYPE.COMPLETE:
|
|
404
|
+
status = WU_STATUS.DONE;
|
|
405
|
+
break;
|
|
406
|
+
case WU_EVENT_TYPE.BLOCK:
|
|
407
|
+
status = WU_STATUS.BLOCKED;
|
|
408
|
+
break;
|
|
409
|
+
case WU_EVENT_TYPE.UNBLOCK:
|
|
410
|
+
status = WU_STATUS.IN_PROGRESS;
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
} catch {
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return status;
|
|
417
|
+
}
|
|
418
|
+
function appendReconciliationEventsInWorktree({ id, lane, title, projectRoot, worktreePath }) {
|
|
419
|
+
const eventsRelPath = LUMENFLOW_PATHS.WU_EVENTS;
|
|
420
|
+
const eventsSrcPath = path2.join(projectRoot, eventsRelPath);
|
|
421
|
+
const eventsDestPath = path2.join(worktreePath, eventsRelPath);
|
|
422
|
+
const eventsReadPath = existsSync(eventsDestPath) ? eventsDestPath : eventsSrcPath;
|
|
423
|
+
const existingContent = existsSync(eventsReadPath) ? readFileSync(eventsReadPath, { encoding: "utf-8" }) : "";
|
|
424
|
+
const derivedStatus = deriveStatusFromEvents(existingContent, id);
|
|
425
|
+
if (derivedStatus === WU_STATUS.DONE) {
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
428
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
429
|
+
const appendEvents = [];
|
|
430
|
+
if (derivedStatus !== WU_STATUS.IN_PROGRESS) {
|
|
431
|
+
appendEvents.push({
|
|
432
|
+
type: WU_EVENT_TYPE.CLAIM,
|
|
433
|
+
wuId: id,
|
|
434
|
+
lane: typeof lane === "string" && lane.trim().length > 0 ? lane : "Operations: Tooling",
|
|
435
|
+
title: typeof title === "string" && title.trim().length > 0 ? title : `WU ${id}`,
|
|
436
|
+
timestamp: now
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
appendEvents.push({
|
|
440
|
+
type: WU_EVENT_TYPE.COMPLETE,
|
|
441
|
+
wuId: id,
|
|
442
|
+
reason: "wu:repair consistency reconciliation for stamp/yaml mismatch",
|
|
443
|
+
timestamp: now
|
|
444
|
+
});
|
|
445
|
+
const destDir = path2.dirname(eventsDestPath);
|
|
446
|
+
if (!existsSync(destDir)) {
|
|
447
|
+
mkdirSync(destDir, { recursive: true });
|
|
448
|
+
}
|
|
449
|
+
const suffix = appendEvents.map((event) => JSON.stringify(event)).join(STRING_LITERALS.NEWLINE);
|
|
450
|
+
const normalizedExisting = existingContent ? existingContent.endsWith(STRING_LITERALS.NEWLINE) ? existingContent : `${existingContent}${STRING_LITERALS.NEWLINE}` : "";
|
|
451
|
+
writeFileSync(eventsDestPath, `${normalizedExisting}${suffix}${STRING_LITERALS.NEWLINE}`, {
|
|
452
|
+
encoding: "utf-8"
|
|
453
|
+
});
|
|
454
|
+
return [eventsRelPath];
|
|
455
|
+
}
|
|
456
|
+
async function removeWUFromSectionInWorktree(relFilePath, id, sectionHeading, worktreePath, projectRoot) {
|
|
457
|
+
const srcPath = path2.join(projectRoot, relFilePath);
|
|
458
|
+
const destPath = path2.join(worktreePath, relFilePath);
|
|
459
|
+
if (!existsSync(srcPath)) {
|
|
460
|
+
return [];
|
|
461
|
+
}
|
|
462
|
+
const content = readFileSync(srcPath, { encoding: "utf-8" });
|
|
463
|
+
const lines = content.split(/\r?\n/);
|
|
464
|
+
let inTargetSection = false;
|
|
465
|
+
let nextSectionIdx = -1;
|
|
466
|
+
let sectionStartIdx = -1;
|
|
467
|
+
const normalizedHeading = sectionHeading.toLowerCase().trim();
|
|
468
|
+
for (let i = 0; i < lines.length; i++) {
|
|
469
|
+
const currentLine = lines[i] ?? "";
|
|
470
|
+
const normalizedLine = currentLine.toLowerCase().trim();
|
|
471
|
+
if (normalizedLine === normalizedHeading || normalizedLine.startsWith(normalizedHeading)) {
|
|
472
|
+
inTargetSection = true;
|
|
473
|
+
sectionStartIdx = i;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (inTargetSection && currentLine.trim().startsWith("## ")) {
|
|
477
|
+
nextSectionIdx = i;
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if (sectionStartIdx === -1)
|
|
482
|
+
return [];
|
|
483
|
+
const endIdx = nextSectionIdx === -1 ? lines.length : nextSectionIdx;
|
|
484
|
+
const newLines = [];
|
|
485
|
+
let modified = false;
|
|
486
|
+
for (let i = 0; i < lines.length; i++) {
|
|
487
|
+
const currentLine = lines[i] ?? "";
|
|
488
|
+
if (i > sectionStartIdx && i < endIdx && currentLine.includes(id)) {
|
|
489
|
+
modified = true;
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
newLines.push(currentLine);
|
|
493
|
+
}
|
|
494
|
+
if (!modified)
|
|
495
|
+
return [];
|
|
496
|
+
const destDir = path2.dirname(destPath);
|
|
497
|
+
if (!existsSync(destDir)) {
|
|
498
|
+
mkdirSync(destDir, { recursive: true });
|
|
499
|
+
}
|
|
500
|
+
writeFileSync(destPath, newLines.join(STRING_LITERALS.NEWLINE), { encoding: "utf-8" });
|
|
501
|
+
return [relFilePath];
|
|
502
|
+
}
|
|
503
|
+
async function removeOrphanWorktree(id, lane, projectRoot) {
|
|
504
|
+
const laneKebab = toKebab(lane);
|
|
505
|
+
const worktreeName = `${laneKebab}-${id.toLowerCase()}`;
|
|
506
|
+
const worktreePath = path2.join(projectRoot, "worktrees", worktreeName);
|
|
507
|
+
const cwd = process.cwd();
|
|
508
|
+
if (cwd.startsWith(worktreePath)) {
|
|
509
|
+
return { skipped: true, reason: "Cannot delete worktree while inside it" };
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
await access2(worktreePath, constants2.R_OK);
|
|
513
|
+
try {
|
|
514
|
+
const gitWorktree = createGitForPath(worktreePath);
|
|
515
|
+
const status = await gitWorktree.getStatus();
|
|
516
|
+
if (status.trim().length > 0) {
|
|
517
|
+
return { skipped: true, reason: "Worktree has uncommitted changes" };
|
|
518
|
+
}
|
|
519
|
+
} catch {
|
|
520
|
+
}
|
|
521
|
+
} catch {
|
|
522
|
+
}
|
|
523
|
+
const paths = createWuPaths({ projectRoot });
|
|
524
|
+
const stampPath = path2.join(projectRoot, paths.STAMP(id));
|
|
525
|
+
try {
|
|
526
|
+
await access2(stampPath, constants2.R_OK);
|
|
527
|
+
} catch {
|
|
528
|
+
return { skipped: true, reason: "WU marked done but no stamp - possible rollback state" };
|
|
529
|
+
}
|
|
530
|
+
const git = createGitForPath(projectRoot);
|
|
531
|
+
try {
|
|
532
|
+
await access2(worktreePath, constants2.R_OK);
|
|
533
|
+
await git.worktreeRemove(worktreePath, { force: true });
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
const branchName = `lane/${laneKebab}/${id.toLowerCase()}`;
|
|
537
|
+
try {
|
|
538
|
+
await git.deleteBranch(branchName, { force: true });
|
|
539
|
+
} catch {
|
|
540
|
+
}
|
|
541
|
+
try {
|
|
542
|
+
await git.raw(["push", REMOTES.ORIGIN, "--delete", branchName]);
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
545
|
+
return { success: true };
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// ../core/dist/wu-inconsistency-repairer.js
|
|
549
|
+
var FILE_REPAIR_STRATEGIES = {
|
|
550
|
+
[CONSISTENCY_TYPES.YAML_DONE_NO_STAMP]: async (error, worktreePath, projectRoot) => {
|
|
551
|
+
const files = await createStampInWorktree(error.wuId, error.title || `WU ${error.wuId}`, worktreePath, projectRoot);
|
|
552
|
+
return { success: true, files };
|
|
553
|
+
},
|
|
554
|
+
[CONSISTENCY_TYPES.YAML_DONE_STATUS_IN_PROGRESS]: async (error, worktreePath, projectRoot) => {
|
|
555
|
+
const paths = createWuPaths({ projectRoot });
|
|
556
|
+
const files = await removeWUFromSectionInWorktree(paths.STATUS(), error.wuId, "## In Progress", worktreePath, projectRoot);
|
|
557
|
+
return { success: true, files };
|
|
558
|
+
},
|
|
559
|
+
[CONSISTENCY_TYPES.BACKLOG_DUAL_SECTION]: async (error, worktreePath, projectRoot) => {
|
|
560
|
+
const paths = createWuPaths({ projectRoot });
|
|
561
|
+
const files = await removeWUFromSectionInWorktree(paths.BACKLOG(), error.wuId, "## \u{1F527} In progress", worktreePath, projectRoot);
|
|
562
|
+
return { success: true, files };
|
|
563
|
+
},
|
|
564
|
+
[CONSISTENCY_TYPES.STAMP_EXISTS_YAML_NOT_DONE]: async (error, worktreePath, projectRoot) => {
|
|
565
|
+
const files = await updateYamlToDoneInWorktree(error.wuId, worktreePath, projectRoot);
|
|
566
|
+
return { success: true, files };
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
var GIT_REPAIR_STRATEGIES = {
|
|
570
|
+
[CONSISTENCY_TYPES.ORPHAN_WORKTREE_DONE]: async (error, projectRoot) => {
|
|
571
|
+
if (!error.lane) {
|
|
572
|
+
return { skipped: true, reason: "Missing lane metadata for orphan worktree cleanup" };
|
|
573
|
+
}
|
|
574
|
+
return await removeOrphanWorktree(error.wuId, error.lane, projectRoot);
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
function categorizeErrors(errors) {
|
|
578
|
+
const fileRepairs = [];
|
|
579
|
+
const gitOnlyRepairs = [];
|
|
580
|
+
const nonRepairable = [];
|
|
581
|
+
for (const error of errors) {
|
|
582
|
+
if (!error.canAutoRepair) {
|
|
583
|
+
nonRepairable.push(error);
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
if (error.type in GIT_REPAIR_STRATEGIES) {
|
|
587
|
+
gitOnlyRepairs.push(error);
|
|
588
|
+
} else {
|
|
589
|
+
fileRepairs.push(error);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return { fileRepairs, gitOnlyRepairs, nonRepairable };
|
|
593
|
+
}
|
|
594
|
+
async function repairSingleErrorInWorktree(error, worktreePath, projectRoot) {
|
|
595
|
+
const strategy = FILE_REPAIR_STRATEGIES[error.type];
|
|
596
|
+
if (!strategy) {
|
|
597
|
+
return { skipped: true, reason: `Unknown error type: ${error.type}` };
|
|
598
|
+
}
|
|
599
|
+
return strategy(error, worktreePath, projectRoot);
|
|
600
|
+
}
|
|
601
|
+
async function repairGitOnlyError(error, projectRoot) {
|
|
602
|
+
const strategy = GIT_REPAIR_STRATEGIES[error.type];
|
|
603
|
+
if (!strategy) {
|
|
604
|
+
return { skipped: true, reason: `Unknown git-only error type: ${error.type}` };
|
|
605
|
+
}
|
|
606
|
+
return strategy(error, projectRoot);
|
|
607
|
+
}
|
|
608
|
+
async function repairWUInconsistency(report, options = {}) {
|
|
609
|
+
const { dryRun = false, projectRoot } = options;
|
|
610
|
+
const isInsideMicroWorktree = projectRoot !== void 0;
|
|
611
|
+
const effectiveProjectRoot = projectRoot ?? process.cwd();
|
|
612
|
+
if (report.valid) {
|
|
613
|
+
return { repaired: 0, skipped: 0, failed: 0 };
|
|
614
|
+
}
|
|
615
|
+
const { fileRepairs, gitOnlyRepairs, nonRepairable } = categorizeErrors(report.errors);
|
|
616
|
+
let repaired = 0;
|
|
617
|
+
let skipped = nonRepairable.length;
|
|
618
|
+
let failed = 0;
|
|
619
|
+
if (dryRun) {
|
|
620
|
+
return {
|
|
621
|
+
repaired: fileRepairs.length + gitOnlyRepairs.length,
|
|
622
|
+
skipped,
|
|
623
|
+
failed: 0
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
if (fileRepairs.length > 0) {
|
|
627
|
+
if (isInsideMicroWorktree) {
|
|
628
|
+
for (const error of fileRepairs) {
|
|
629
|
+
try {
|
|
630
|
+
const result = await repairSingleErrorInWorktree(error, effectiveProjectRoot, effectiveProjectRoot);
|
|
631
|
+
if (result.success && result.files) {
|
|
632
|
+
repaired++;
|
|
633
|
+
} else if (result.skipped) {
|
|
634
|
+
skipped++;
|
|
635
|
+
if (result.reason) {
|
|
636
|
+
console.warn(`${LOG_PREFIX.REPAIR} Skipped ${error.type}: ${result.reason}`);
|
|
637
|
+
}
|
|
638
|
+
} else {
|
|
639
|
+
failed++;
|
|
640
|
+
}
|
|
641
|
+
} catch (err) {
|
|
642
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
643
|
+
console.error(`${LOG_PREFIX.REPAIR} Failed to repair ${error.type}: ${errMessage}`);
|
|
644
|
+
failed++;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
} else {
|
|
648
|
+
try {
|
|
649
|
+
const batchId = `batch-${fileRepairs.map((e) => e.wuId).join("-")}`.slice(0, 50);
|
|
650
|
+
await withMicroWorktree({
|
|
651
|
+
operation: "wu-repair",
|
|
652
|
+
id: batchId,
|
|
653
|
+
logPrefix: LOG_PREFIX.REPAIR,
|
|
654
|
+
execute: async ({ worktreePath }) => {
|
|
655
|
+
const filesModified = [];
|
|
656
|
+
for (const error of fileRepairs) {
|
|
657
|
+
try {
|
|
658
|
+
const result = await repairSingleErrorInWorktree(error, worktreePath, worktreePath);
|
|
659
|
+
if (result.success && result.files) {
|
|
660
|
+
filesModified.push(...result.files);
|
|
661
|
+
repaired++;
|
|
662
|
+
} else if (result.skipped) {
|
|
663
|
+
skipped++;
|
|
664
|
+
if (result.reason) {
|
|
665
|
+
console.warn(`${LOG_PREFIX.REPAIR} Skipped ${error.type}: ${result.reason}`);
|
|
666
|
+
}
|
|
667
|
+
} else {
|
|
668
|
+
failed++;
|
|
669
|
+
}
|
|
670
|
+
} catch (err) {
|
|
671
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
672
|
+
console.error(`${LOG_PREFIX.REPAIR} Failed to repair ${error.type}: ${errMessage}`);
|
|
673
|
+
failed++;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const uniqueFiles = [...new Set(filesModified)];
|
|
677
|
+
return {
|
|
678
|
+
commitMessage: `fix: repair ${repaired} WU inconsistencies`,
|
|
679
|
+
files: uniqueFiles
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
} catch (err) {
|
|
684
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
685
|
+
console.error(`${LOG_PREFIX.REPAIR} Micro-worktree operation failed: ${errMessage}`);
|
|
686
|
+
failed += fileRepairs.length - repaired;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
for (const error of gitOnlyRepairs) {
|
|
691
|
+
try {
|
|
692
|
+
const result = await repairGitOnlyError(error, effectiveProjectRoot);
|
|
693
|
+
if (result.success) {
|
|
694
|
+
repaired++;
|
|
695
|
+
} else if (result.skipped) {
|
|
696
|
+
skipped++;
|
|
697
|
+
if (result.reason) {
|
|
698
|
+
console.warn(`${LOG_PREFIX.REPAIR} Skipped ${error.type}: ${result.reason}`);
|
|
699
|
+
}
|
|
700
|
+
} else {
|
|
701
|
+
failed++;
|
|
702
|
+
}
|
|
703
|
+
} catch (err) {
|
|
704
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
705
|
+
console.error(`${LOG_PREFIX.REPAIR} Failed to repair ${error.type}: ${errMessage}`);
|
|
706
|
+
failed++;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return { repaired, skipped, failed };
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
export {
|
|
713
|
+
checkWUConsistency,
|
|
714
|
+
checkAllWUConsistency,
|
|
715
|
+
checkLaneForOrphanDoneWU,
|
|
716
|
+
repairWUInconsistency
|
|
717
|
+
};
|