codeharness 0.25.5 → 0.25.7
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.
|
@@ -3207,7 +3207,7 @@ function generateDockerfileTemplate(projectDir, stackOrDetections) {
|
|
|
3207
3207
|
}
|
|
3208
3208
|
|
|
3209
3209
|
// src/modules/infra/init-project.ts
|
|
3210
|
-
var HARNESS_VERSION = true ? "0.25.
|
|
3210
|
+
var HARNESS_VERSION = true ? "0.25.7" : "0.0.0-dev";
|
|
3211
3211
|
function failResult(opts, error) {
|
|
3212
3212
|
return {
|
|
3213
3213
|
status: "fail",
|
package/dist/index.js
CHANGED
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
validateDockerfile,
|
|
52
52
|
warn,
|
|
53
53
|
writeState
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-MODOFVMH.js";
|
|
55
55
|
|
|
56
56
|
// src/index.ts
|
|
57
57
|
import { Command } from "commander";
|
|
@@ -173,8 +173,240 @@ import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as re
|
|
|
173
173
|
import { join as join8, dirname as dirname3 } from "path";
|
|
174
174
|
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
175
175
|
|
|
176
|
+
// src/modules/sprint/state.ts
|
|
177
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync, existsSync as existsSync6, unlinkSync } from "fs";
|
|
178
|
+
import { join as join4, dirname } from "path";
|
|
179
|
+
|
|
180
|
+
// src/modules/sprint/migration.ts
|
|
181
|
+
import { readFileSync, existsSync as existsSync2 } from "fs";
|
|
182
|
+
import { join } from "path";
|
|
183
|
+
var OLD_FILES = {
|
|
184
|
+
storyRetries: "ralph/.story_retries",
|
|
185
|
+
flaggedStories: "ralph/.flagged_stories",
|
|
186
|
+
ralphStatus: "ralph/status.json",
|
|
187
|
+
sprintStatusYaml: "_bmad-output/implementation-artifacts/sprint-status.yaml",
|
|
188
|
+
sessionIssues: "_bmad-output/implementation-artifacts/.session-issues.md"
|
|
189
|
+
};
|
|
190
|
+
function resolve(relative3) {
|
|
191
|
+
return join(process.cwd(), relative3);
|
|
192
|
+
}
|
|
193
|
+
function readIfExists(relative3) {
|
|
194
|
+
const p = resolve(relative3);
|
|
195
|
+
if (!existsSync2(p)) return null;
|
|
196
|
+
try {
|
|
197
|
+
return readFileSync(p, "utf-8");
|
|
198
|
+
} catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function emptyStory() {
|
|
203
|
+
return {
|
|
204
|
+
status: "backlog",
|
|
205
|
+
attempts: 0,
|
|
206
|
+
lastAttempt: null,
|
|
207
|
+
lastError: null,
|
|
208
|
+
proofPath: null,
|
|
209
|
+
acResults: null
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function upsertStory(stories, key, patch) {
|
|
213
|
+
stories[key] = { ...stories[key] ?? emptyStory(), ...patch };
|
|
214
|
+
}
|
|
215
|
+
function parseStoryRetries(content, stories) {
|
|
216
|
+
for (const line of content.split("\n")) {
|
|
217
|
+
const trimmed = line.trim();
|
|
218
|
+
if (!trimmed) continue;
|
|
219
|
+
const parts = trimmed.split(/\s+/);
|
|
220
|
+
if (parts.length < 2) continue;
|
|
221
|
+
const count = parseInt(parts[1], 10);
|
|
222
|
+
if (!isNaN(count)) upsertStory(stories, parts[0], { attempts: count });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function parseStoryRetriesRecord(content) {
|
|
226
|
+
const result = {};
|
|
227
|
+
for (const line of content.split("\n")) {
|
|
228
|
+
const trimmed = line.trim();
|
|
229
|
+
if (!trimmed) continue;
|
|
230
|
+
const parts = trimmed.split(/\s+/);
|
|
231
|
+
if (parts.length < 2) continue;
|
|
232
|
+
const count = parseInt(parts[1], 10);
|
|
233
|
+
if (!isNaN(count) && count >= 0) result[parts[0]] = count;
|
|
234
|
+
}
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
function parseFlaggedStoriesList(content) {
|
|
238
|
+
const seen = /* @__PURE__ */ new Set();
|
|
239
|
+
const result = [];
|
|
240
|
+
for (const line of content.split("\n")) {
|
|
241
|
+
const trimmed = line.trim();
|
|
242
|
+
if (trimmed !== "" && !seen.has(trimmed)) {
|
|
243
|
+
seen.add(trimmed);
|
|
244
|
+
result.push(trimmed);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
function parseFlaggedStories(content, stories) {
|
|
250
|
+
for (const line of content.split("\n")) {
|
|
251
|
+
const key = line.trim();
|
|
252
|
+
if (key) upsertStory(stories, key, { status: "blocked" });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function mapYamlStatus(value) {
|
|
256
|
+
const mapping = {
|
|
257
|
+
done: "done",
|
|
258
|
+
backlog: "backlog",
|
|
259
|
+
verifying: "verifying",
|
|
260
|
+
"in-progress": "in-progress",
|
|
261
|
+
"ready-for-dev": "ready",
|
|
262
|
+
blocked: "blocked",
|
|
263
|
+
failed: "failed",
|
|
264
|
+
review: "review",
|
|
265
|
+
ready: "ready"
|
|
266
|
+
};
|
|
267
|
+
return mapping[value.trim().toLowerCase()] ?? null;
|
|
268
|
+
}
|
|
269
|
+
function parseSprintStatusYaml(content, stories) {
|
|
270
|
+
for (const line of content.split("\n")) {
|
|
271
|
+
const trimmed = line.trim();
|
|
272
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
273
|
+
const match = trimmed.match(/^([a-zA-Z0-9_-]+):\s*(.+)$/);
|
|
274
|
+
if (!match) continue;
|
|
275
|
+
const key = match[1];
|
|
276
|
+
if (key === "development_status" || key.startsWith("epic-")) continue;
|
|
277
|
+
const status = mapYamlStatus(match[2]);
|
|
278
|
+
if (status) upsertStory(stories, key, { status });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function parseRalphStatus(content) {
|
|
282
|
+
try {
|
|
283
|
+
const data = JSON.parse(content);
|
|
284
|
+
return {
|
|
285
|
+
active: data.status === "running",
|
|
286
|
+
startedAt: null,
|
|
287
|
+
iteration: data.loop_count ?? 0,
|
|
288
|
+
cost: 0,
|
|
289
|
+
completed: [],
|
|
290
|
+
failed: [],
|
|
291
|
+
currentStory: null,
|
|
292
|
+
currentPhase: null,
|
|
293
|
+
lastAction: null,
|
|
294
|
+
acProgress: null
|
|
295
|
+
};
|
|
296
|
+
} catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function parseRalphStatusToSession(content) {
|
|
301
|
+
try {
|
|
302
|
+
const data = JSON.parse(content);
|
|
303
|
+
return {
|
|
304
|
+
active: data.status === "running",
|
|
305
|
+
startedAt: null,
|
|
306
|
+
iteration: data.loop_count ?? 0,
|
|
307
|
+
elapsedSeconds: data.elapsed_seconds ?? 0
|
|
308
|
+
};
|
|
309
|
+
} catch {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function parseSessionIssues(content) {
|
|
314
|
+
const items = [];
|
|
315
|
+
let currentStory = "";
|
|
316
|
+
let itemId = 0;
|
|
317
|
+
for (const line of content.split("\n")) {
|
|
318
|
+
const headerMatch = line.match(/^###\s+([a-zA-Z0-9_-]+)\s*[—-]/);
|
|
319
|
+
if (headerMatch) {
|
|
320
|
+
currentStory = headerMatch[1];
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const bulletMatch = line.match(/^-\s+(.+)/);
|
|
324
|
+
if (bulletMatch && currentStory) {
|
|
325
|
+
itemId++;
|
|
326
|
+
items.push({
|
|
327
|
+
id: `migrated-${itemId}`,
|
|
328
|
+
story: currentStory,
|
|
329
|
+
description: bulletMatch[1],
|
|
330
|
+
source: "retro",
|
|
331
|
+
resolved: false
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return items;
|
|
336
|
+
}
|
|
337
|
+
function migrateV1ToV2(v1) {
|
|
338
|
+
const defaults = defaultState();
|
|
339
|
+
const retriesContent = readIfExists(OLD_FILES.storyRetries);
|
|
340
|
+
const retries = retriesContent ? parseStoryRetriesRecord(retriesContent) : {};
|
|
341
|
+
const flaggedContent = readIfExists(OLD_FILES.flaggedStories);
|
|
342
|
+
const flagged = flaggedContent ? parseFlaggedStoriesList(flaggedContent) : [];
|
|
343
|
+
const statusContent = readIfExists(OLD_FILES.ralphStatus);
|
|
344
|
+
const session = statusContent ? parseRalphStatusToSession(statusContent) ?? defaults.session : defaults.session;
|
|
345
|
+
return {
|
|
346
|
+
version: 2,
|
|
347
|
+
sprint: v1.sprint,
|
|
348
|
+
stories: v1.stories,
|
|
349
|
+
retries,
|
|
350
|
+
flagged,
|
|
351
|
+
epics: {},
|
|
352
|
+
session,
|
|
353
|
+
observability: defaults.observability,
|
|
354
|
+
run: {
|
|
355
|
+
...defaults.run,
|
|
356
|
+
...v1.run
|
|
357
|
+
},
|
|
358
|
+
actionItems: v1.actionItems
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function migrateFromOldFormat() {
|
|
362
|
+
const hasAnyOldFile = Object.values(OLD_FILES).some((rel) => existsSync2(resolve(rel)));
|
|
363
|
+
if (!hasAnyOldFile) return fail2("No old format files found for migration");
|
|
364
|
+
try {
|
|
365
|
+
const stories = {};
|
|
366
|
+
let run = defaultState().run;
|
|
367
|
+
let actionItems = [];
|
|
368
|
+
const yamlContent = readIfExists(OLD_FILES.sprintStatusYaml);
|
|
369
|
+
if (yamlContent) parseSprintStatusYaml(yamlContent, stories);
|
|
370
|
+
const retriesContent = readIfExists(OLD_FILES.storyRetries);
|
|
371
|
+
if (retriesContent) parseStoryRetries(retriesContent, stories);
|
|
372
|
+
const flaggedContent = readIfExists(OLD_FILES.flaggedStories);
|
|
373
|
+
if (flaggedContent) parseFlaggedStories(flaggedContent, stories);
|
|
374
|
+
const statusContent = readIfExists(OLD_FILES.ralphStatus);
|
|
375
|
+
let session = defaultState().session;
|
|
376
|
+
if (statusContent) {
|
|
377
|
+
const parsed = parseRalphStatus(statusContent);
|
|
378
|
+
if (parsed) run = parsed;
|
|
379
|
+
const parsedSession = parseRalphStatusToSession(statusContent);
|
|
380
|
+
if (parsedSession) session = parsedSession;
|
|
381
|
+
}
|
|
382
|
+
const issuesContent = readIfExists(OLD_FILES.sessionIssues);
|
|
383
|
+
if (issuesContent) actionItems = parseSessionIssues(issuesContent);
|
|
384
|
+
const sprint = computeSprintCounts(stories);
|
|
385
|
+
const retries = retriesContent ? parseStoryRetriesRecord(retriesContent) : {};
|
|
386
|
+
const flagged = flaggedContent ? parseFlaggedStoriesList(flaggedContent) : [];
|
|
387
|
+
const migrated = {
|
|
388
|
+
version: 2,
|
|
389
|
+
sprint,
|
|
390
|
+
stories,
|
|
391
|
+
retries,
|
|
392
|
+
flagged,
|
|
393
|
+
epics: {},
|
|
394
|
+
session,
|
|
395
|
+
observability: defaultState().observability,
|
|
396
|
+
run,
|
|
397
|
+
actionItems
|
|
398
|
+
};
|
|
399
|
+
const writeResult = writeStateAtomic(migrated);
|
|
400
|
+
if (!writeResult.success) return fail2(writeResult.error);
|
|
401
|
+
return ok2(migrated);
|
|
402
|
+
} catch (err) {
|
|
403
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
404
|
+
return fail2(`Migration failed: ${msg}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
176
408
|
// src/lib/sync/story-files.ts
|
|
177
|
-
import { existsSync as
|
|
409
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
178
410
|
var BEADS_TO_STORY_STATUS = {
|
|
179
411
|
open: "in-progress",
|
|
180
412
|
closed: "done"
|
|
@@ -208,10 +440,10 @@ function resolveStoryFilePath(beadsIssue) {
|
|
|
208
440
|
return trimmed;
|
|
209
441
|
}
|
|
210
442
|
function readStoryFileStatus(filePath) {
|
|
211
|
-
if (!
|
|
443
|
+
if (!existsSync3(filePath)) {
|
|
212
444
|
return null;
|
|
213
445
|
}
|
|
214
|
-
const content =
|
|
446
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
215
447
|
const match = content.match(/^#{0,2}\s*Status:\s*(.+)$/m);
|
|
216
448
|
if (!match) {
|
|
217
449
|
return null;
|
|
@@ -219,7 +451,7 @@ function readStoryFileStatus(filePath) {
|
|
|
219
451
|
return match[1].trim();
|
|
220
452
|
}
|
|
221
453
|
function updateStoryFileStatus(filePath, newStatus) {
|
|
222
|
-
const content =
|
|
454
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
223
455
|
const statusRegex = /^(#{0,2}\s*)Status:\s*.+$/m;
|
|
224
456
|
if (statusRegex.test(content)) {
|
|
225
457
|
const updated = content.replace(statusRegex, `$1Status: ${newStatus}`);
|
|
@@ -237,18 +469,18 @@ function updateStoryFileStatus(filePath, newStatus) {
|
|
|
237
469
|
}
|
|
238
470
|
|
|
239
471
|
// src/lib/sync/sprint-yaml.ts
|
|
240
|
-
import { existsSync as
|
|
241
|
-
import { join } from "path";
|
|
472
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
473
|
+
import { join as join2 } from "path";
|
|
242
474
|
import { parse } from "yaml";
|
|
243
475
|
var SPRINT_STATUS_PATH = "_bmad-output/implementation-artifacts/sprint-status.yaml";
|
|
244
476
|
function readSprintStatus(dir) {
|
|
245
477
|
const root = dir ?? process.cwd();
|
|
246
|
-
const filePath =
|
|
247
|
-
if (!
|
|
478
|
+
const filePath = join2(root, SPRINT_STATUS_PATH);
|
|
479
|
+
if (!existsSync4(filePath)) {
|
|
248
480
|
return {};
|
|
249
481
|
}
|
|
250
482
|
try {
|
|
251
|
-
const content =
|
|
483
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
252
484
|
const parsed = parse(content);
|
|
253
485
|
if (!parsed || typeof parsed !== "object") {
|
|
254
486
|
return {};
|
|
@@ -264,12 +496,12 @@ function readSprintStatus(dir) {
|
|
|
264
496
|
}
|
|
265
497
|
function updateSprintStatus(storyKey, newStatus, dir) {
|
|
266
498
|
const root = dir ?? process.cwd();
|
|
267
|
-
const filePath =
|
|
268
|
-
if (!
|
|
499
|
+
const filePath = join2(root, SPRINT_STATUS_PATH);
|
|
500
|
+
if (!existsSync4(filePath)) {
|
|
269
501
|
warn(`sprint-status.yaml not found at ${filePath}, skipping update`);
|
|
270
502
|
return;
|
|
271
503
|
}
|
|
272
|
-
const content =
|
|
504
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
273
505
|
const keyPattern = new RegExp(`^(\\s*${escapeRegExp(storyKey)}:\\s*)\\S+(.*)$`, "m");
|
|
274
506
|
if (!keyPattern.test(content)) {
|
|
275
507
|
return;
|
|
@@ -282,8 +514,8 @@ function escapeRegExp(s) {
|
|
|
282
514
|
}
|
|
283
515
|
|
|
284
516
|
// src/lib/sync/beads.ts
|
|
285
|
-
import { existsSync as
|
|
286
|
-
import { join as
|
|
517
|
+
import { existsSync as existsSync5 } from "fs";
|
|
518
|
+
import { join as join3 } from "path";
|
|
287
519
|
function syncBeadsToStoryFile(beadsId, beadsFns, dir) {
|
|
288
520
|
const root = dir ?? process.cwd();
|
|
289
521
|
const issues = beadsFns.listIssues();
|
|
@@ -310,7 +542,7 @@ function syncBeadsToStoryFile(beadsId, beadsFns, dir) {
|
|
|
310
542
|
};
|
|
311
543
|
}
|
|
312
544
|
const storyKey = storyKeyFromPath(storyFilePath);
|
|
313
|
-
const fullPath =
|
|
545
|
+
const fullPath = join3(root, storyFilePath);
|
|
314
546
|
const currentStoryStatus = readStoryFileStatus(fullPath);
|
|
315
547
|
if (currentStoryStatus === null) {
|
|
316
548
|
return {
|
|
@@ -355,7 +587,7 @@ function syncBeadsToStoryFile(beadsId, beadsFns, dir) {
|
|
|
355
587
|
function syncStoryFileToBeads(storyKey, beadsFns, dir) {
|
|
356
588
|
const root = dir ?? process.cwd();
|
|
357
589
|
const storyFilePath = `_bmad-output/implementation-artifacts/${storyKey}.md`;
|
|
358
|
-
const fullPath =
|
|
590
|
+
const fullPath = join3(root, storyFilePath);
|
|
359
591
|
const currentStoryStatus = readStoryFileStatus(fullPath);
|
|
360
592
|
if (currentStoryStatus === null) {
|
|
361
593
|
return {
|
|
@@ -444,10 +676,10 @@ function syncClose(beadsId, beadsFns, dir) {
|
|
|
444
676
|
};
|
|
445
677
|
}
|
|
446
678
|
const storyKey = storyKeyFromPath(storyFilePath);
|
|
447
|
-
const fullPath =
|
|
679
|
+
const fullPath = join3(root, storyFilePath);
|
|
448
680
|
const previousStatus = readStoryFileStatus(fullPath);
|
|
449
681
|
if (previousStatus === null) {
|
|
450
|
-
if (!
|
|
682
|
+
if (!existsSync5(fullPath)) {
|
|
451
683
|
return {
|
|
452
684
|
storyKey,
|
|
453
685
|
beadsId,
|
|
@@ -519,238 +751,6 @@ function syncAll(direction, beadsFns, dir) {
|
|
|
519
751
|
return results;
|
|
520
752
|
}
|
|
521
753
|
|
|
522
|
-
// src/modules/sprint/state.ts
|
|
523
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync, existsSync as existsSync6, unlinkSync } from "fs";
|
|
524
|
-
import { join as join4, dirname } from "path";
|
|
525
|
-
|
|
526
|
-
// src/modules/sprint/migration.ts
|
|
527
|
-
import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
|
|
528
|
-
import { join as join3 } from "path";
|
|
529
|
-
var OLD_FILES = {
|
|
530
|
-
storyRetries: "ralph/.story_retries",
|
|
531
|
-
flaggedStories: "ralph/.flagged_stories",
|
|
532
|
-
ralphStatus: "ralph/status.json",
|
|
533
|
-
sprintStatusYaml: "_bmad-output/implementation-artifacts/sprint-status.yaml",
|
|
534
|
-
sessionIssues: "_bmad-output/implementation-artifacts/.session-issues.md"
|
|
535
|
-
};
|
|
536
|
-
function resolve(relative3) {
|
|
537
|
-
return join3(process.cwd(), relative3);
|
|
538
|
-
}
|
|
539
|
-
function readIfExists(relative3) {
|
|
540
|
-
const p = resolve(relative3);
|
|
541
|
-
if (!existsSync5(p)) return null;
|
|
542
|
-
try {
|
|
543
|
-
return readFileSync3(p, "utf-8");
|
|
544
|
-
} catch {
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
function emptyStory() {
|
|
549
|
-
return {
|
|
550
|
-
status: "backlog",
|
|
551
|
-
attempts: 0,
|
|
552
|
-
lastAttempt: null,
|
|
553
|
-
lastError: null,
|
|
554
|
-
proofPath: null,
|
|
555
|
-
acResults: null
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
function upsertStory(stories, key, patch) {
|
|
559
|
-
stories[key] = { ...stories[key] ?? emptyStory(), ...patch };
|
|
560
|
-
}
|
|
561
|
-
function parseStoryRetries(content, stories) {
|
|
562
|
-
for (const line of content.split("\n")) {
|
|
563
|
-
const trimmed = line.trim();
|
|
564
|
-
if (!trimmed) continue;
|
|
565
|
-
const parts = trimmed.split(/\s+/);
|
|
566
|
-
if (parts.length < 2) continue;
|
|
567
|
-
const count = parseInt(parts[1], 10);
|
|
568
|
-
if (!isNaN(count)) upsertStory(stories, parts[0], { attempts: count });
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
function parseStoryRetriesRecord(content) {
|
|
572
|
-
const result = {};
|
|
573
|
-
for (const line of content.split("\n")) {
|
|
574
|
-
const trimmed = line.trim();
|
|
575
|
-
if (!trimmed) continue;
|
|
576
|
-
const parts = trimmed.split(/\s+/);
|
|
577
|
-
if (parts.length < 2) continue;
|
|
578
|
-
const count = parseInt(parts[1], 10);
|
|
579
|
-
if (!isNaN(count) && count >= 0) result[parts[0]] = count;
|
|
580
|
-
}
|
|
581
|
-
return result;
|
|
582
|
-
}
|
|
583
|
-
function parseFlaggedStoriesList(content) {
|
|
584
|
-
const seen = /* @__PURE__ */ new Set();
|
|
585
|
-
const result = [];
|
|
586
|
-
for (const line of content.split("\n")) {
|
|
587
|
-
const trimmed = line.trim();
|
|
588
|
-
if (trimmed !== "" && !seen.has(trimmed)) {
|
|
589
|
-
seen.add(trimmed);
|
|
590
|
-
result.push(trimmed);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
return result;
|
|
594
|
-
}
|
|
595
|
-
function parseFlaggedStories(content, stories) {
|
|
596
|
-
for (const line of content.split("\n")) {
|
|
597
|
-
const key = line.trim();
|
|
598
|
-
if (key) upsertStory(stories, key, { status: "blocked" });
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
function mapYamlStatus(value) {
|
|
602
|
-
const mapping = {
|
|
603
|
-
done: "done",
|
|
604
|
-
backlog: "backlog",
|
|
605
|
-
verifying: "verifying",
|
|
606
|
-
"in-progress": "in-progress",
|
|
607
|
-
"ready-for-dev": "ready",
|
|
608
|
-
blocked: "blocked",
|
|
609
|
-
failed: "failed",
|
|
610
|
-
review: "review",
|
|
611
|
-
ready: "ready"
|
|
612
|
-
};
|
|
613
|
-
return mapping[value.trim().toLowerCase()] ?? null;
|
|
614
|
-
}
|
|
615
|
-
function parseSprintStatusYaml(content, stories) {
|
|
616
|
-
for (const line of content.split("\n")) {
|
|
617
|
-
const trimmed = line.trim();
|
|
618
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
619
|
-
const match = trimmed.match(/^([a-zA-Z0-9_-]+):\s*(.+)$/);
|
|
620
|
-
if (!match) continue;
|
|
621
|
-
const key = match[1];
|
|
622
|
-
if (key === "development_status" || key.startsWith("epic-")) continue;
|
|
623
|
-
const status = mapYamlStatus(match[2]);
|
|
624
|
-
if (status) upsertStory(stories, key, { status });
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
function parseRalphStatus(content) {
|
|
628
|
-
try {
|
|
629
|
-
const data = JSON.parse(content);
|
|
630
|
-
return {
|
|
631
|
-
active: data.status === "running",
|
|
632
|
-
startedAt: null,
|
|
633
|
-
iteration: data.loop_count ?? 0,
|
|
634
|
-
cost: 0,
|
|
635
|
-
completed: [],
|
|
636
|
-
failed: [],
|
|
637
|
-
currentStory: null,
|
|
638
|
-
currentPhase: null,
|
|
639
|
-
lastAction: null,
|
|
640
|
-
acProgress: null
|
|
641
|
-
};
|
|
642
|
-
} catch {
|
|
643
|
-
return null;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
function parseRalphStatusToSession(content) {
|
|
647
|
-
try {
|
|
648
|
-
const data = JSON.parse(content);
|
|
649
|
-
return {
|
|
650
|
-
active: data.status === "running",
|
|
651
|
-
startedAt: null,
|
|
652
|
-
iteration: data.loop_count ?? 0,
|
|
653
|
-
elapsedSeconds: data.elapsed_seconds ?? 0
|
|
654
|
-
};
|
|
655
|
-
} catch {
|
|
656
|
-
return null;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
function parseSessionIssues(content) {
|
|
660
|
-
const items = [];
|
|
661
|
-
let currentStory = "";
|
|
662
|
-
let itemId = 0;
|
|
663
|
-
for (const line of content.split("\n")) {
|
|
664
|
-
const headerMatch = line.match(/^###\s+([a-zA-Z0-9_-]+)\s*[—-]/);
|
|
665
|
-
if (headerMatch) {
|
|
666
|
-
currentStory = headerMatch[1];
|
|
667
|
-
continue;
|
|
668
|
-
}
|
|
669
|
-
const bulletMatch = line.match(/^-\s+(.+)/);
|
|
670
|
-
if (bulletMatch && currentStory) {
|
|
671
|
-
itemId++;
|
|
672
|
-
items.push({
|
|
673
|
-
id: `migrated-${itemId}`,
|
|
674
|
-
story: currentStory,
|
|
675
|
-
description: bulletMatch[1],
|
|
676
|
-
source: "retro",
|
|
677
|
-
resolved: false
|
|
678
|
-
});
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
return items;
|
|
682
|
-
}
|
|
683
|
-
function migrateV1ToV2(v1) {
|
|
684
|
-
const defaults = defaultState();
|
|
685
|
-
const retriesContent = readIfExists(OLD_FILES.storyRetries);
|
|
686
|
-
const retries = retriesContent ? parseStoryRetriesRecord(retriesContent) : {};
|
|
687
|
-
const flaggedContent = readIfExists(OLD_FILES.flaggedStories);
|
|
688
|
-
const flagged = flaggedContent ? parseFlaggedStoriesList(flaggedContent) : [];
|
|
689
|
-
const statusContent = readIfExists(OLD_FILES.ralphStatus);
|
|
690
|
-
const session = statusContent ? parseRalphStatusToSession(statusContent) ?? defaults.session : defaults.session;
|
|
691
|
-
return {
|
|
692
|
-
version: 2,
|
|
693
|
-
sprint: v1.sprint,
|
|
694
|
-
stories: v1.stories,
|
|
695
|
-
retries,
|
|
696
|
-
flagged,
|
|
697
|
-
epics: {},
|
|
698
|
-
session,
|
|
699
|
-
observability: defaults.observability,
|
|
700
|
-
run: {
|
|
701
|
-
...defaults.run,
|
|
702
|
-
...v1.run
|
|
703
|
-
},
|
|
704
|
-
actionItems: v1.actionItems
|
|
705
|
-
};
|
|
706
|
-
}
|
|
707
|
-
function migrateFromOldFormat() {
|
|
708
|
-
const hasAnyOldFile = Object.values(OLD_FILES).some((rel) => existsSync5(resolve(rel)));
|
|
709
|
-
if (!hasAnyOldFile) return fail2("No old format files found for migration");
|
|
710
|
-
try {
|
|
711
|
-
const stories = {};
|
|
712
|
-
let run = defaultState().run;
|
|
713
|
-
let actionItems = [];
|
|
714
|
-
const yamlContent = readIfExists(OLD_FILES.sprintStatusYaml);
|
|
715
|
-
if (yamlContent) parseSprintStatusYaml(yamlContent, stories);
|
|
716
|
-
const retriesContent = readIfExists(OLD_FILES.storyRetries);
|
|
717
|
-
if (retriesContent) parseStoryRetries(retriesContent, stories);
|
|
718
|
-
const flaggedContent = readIfExists(OLD_FILES.flaggedStories);
|
|
719
|
-
if (flaggedContent) parseFlaggedStories(flaggedContent, stories);
|
|
720
|
-
const statusContent = readIfExists(OLD_FILES.ralphStatus);
|
|
721
|
-
let session = defaultState().session;
|
|
722
|
-
if (statusContent) {
|
|
723
|
-
const parsed = parseRalphStatus(statusContent);
|
|
724
|
-
if (parsed) run = parsed;
|
|
725
|
-
const parsedSession = parseRalphStatusToSession(statusContent);
|
|
726
|
-
if (parsedSession) session = parsedSession;
|
|
727
|
-
}
|
|
728
|
-
const issuesContent = readIfExists(OLD_FILES.sessionIssues);
|
|
729
|
-
if (issuesContent) actionItems = parseSessionIssues(issuesContent);
|
|
730
|
-
const sprint = computeSprintCounts(stories);
|
|
731
|
-
const retries = retriesContent ? parseStoryRetriesRecord(retriesContent) : {};
|
|
732
|
-
const flagged = flaggedContent ? parseFlaggedStoriesList(flaggedContent) : [];
|
|
733
|
-
const migrated = {
|
|
734
|
-
version: 2,
|
|
735
|
-
sprint,
|
|
736
|
-
stories,
|
|
737
|
-
retries,
|
|
738
|
-
flagged,
|
|
739
|
-
epics: {},
|
|
740
|
-
session,
|
|
741
|
-
observability: defaultState().observability,
|
|
742
|
-
run,
|
|
743
|
-
actionItems
|
|
744
|
-
};
|
|
745
|
-
const writeResult = writeStateAtomic(migrated);
|
|
746
|
-
if (!writeResult.success) return fail2(writeResult.error);
|
|
747
|
-
return ok2(migrated);
|
|
748
|
-
} catch (err) {
|
|
749
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
750
|
-
return fail2(`Migration failed: ${msg}`);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
754
|
// src/modules/sprint/state.ts
|
|
755
755
|
function projectRoot() {
|
|
756
756
|
return process.cwd();
|
|
@@ -1096,6 +1096,28 @@ function reconcileState() {
|
|
|
1096
1096
|
corrections.push("removed malformed .flagged_stories file");
|
|
1097
1097
|
}
|
|
1098
1098
|
}
|
|
1099
|
+
if (Object.keys(state.stories).length === 0) {
|
|
1100
|
+
try {
|
|
1101
|
+
const yamlStatuses = readSprintStatus(projectRoot());
|
|
1102
|
+
const storyKeyPattern = /^\d+-\d+-/;
|
|
1103
|
+
for (const [key, status] of Object.entries(yamlStatuses)) {
|
|
1104
|
+
if (!storyKeyPattern.test(key)) continue;
|
|
1105
|
+
state.stories[key] = {
|
|
1106
|
+
status,
|
|
1107
|
+
attempts: 0,
|
|
1108
|
+
lastAttempt: null,
|
|
1109
|
+
lastError: null,
|
|
1110
|
+
proofPath: null,
|
|
1111
|
+
acResults: null
|
|
1112
|
+
};
|
|
1113
|
+
changed = true;
|
|
1114
|
+
}
|
|
1115
|
+
if (Object.keys(state.stories).length > 0) {
|
|
1116
|
+
corrections.push(`imported ${Object.keys(state.stories).length} stories from sprint-status.yaml`);
|
|
1117
|
+
}
|
|
1118
|
+
} catch {
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1099
1121
|
const epicStories = /* @__PURE__ */ new Map();
|
|
1100
1122
|
for (const key of Object.keys(state.stories)) {
|
|
1101
1123
|
const [epicNum] = parseStoryKey(key);
|
|
@@ -1770,12 +1792,7 @@ function readSprintStatusFromState() {
|
|
|
1770
1792
|
const stateResult = getSprintState();
|
|
1771
1793
|
if (!stateResult.success) return {};
|
|
1772
1794
|
const statuses = getStoryStatusesFromState(stateResult.data);
|
|
1773
|
-
|
|
1774
|
-
try {
|
|
1775
|
-
return readSprintStatus(process.cwd());
|
|
1776
|
-
} catch {
|
|
1777
|
-
return {};
|
|
1778
|
-
}
|
|
1795
|
+
return statuses;
|
|
1779
1796
|
}
|
|
1780
1797
|
function shouldDeferPhase2(phase, remainingMinutes) {
|
|
1781
1798
|
return shouldDeferPhase(phase, remainingMinutes);
|
|
@@ -7558,7 +7575,7 @@ function registerTeardownCommand(program) {
|
|
|
7558
7575
|
} else if (otlpMode === "remote-routed") {
|
|
7559
7576
|
if (!options.keepDocker) {
|
|
7560
7577
|
try {
|
|
7561
|
-
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-
|
|
7578
|
+
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-YLD7DF2U.js");
|
|
7562
7579
|
stopCollectorOnly2();
|
|
7563
7580
|
result.docker.stopped = true;
|
|
7564
7581
|
if (!isJson) {
|
|
@@ -7590,7 +7607,7 @@ function registerTeardownCommand(program) {
|
|
|
7590
7607
|
info("Shared stack: kept running (other projects may use it)");
|
|
7591
7608
|
}
|
|
7592
7609
|
} else if (isLegacyStack) {
|
|
7593
|
-
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-
|
|
7610
|
+
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-YLD7DF2U.js");
|
|
7594
7611
|
let stackRunning = false;
|
|
7595
7612
|
try {
|
|
7596
7613
|
stackRunning = isStackRunning2(composeFile);
|
|
@@ -9489,7 +9506,7 @@ function registerAuditCommand(program) {
|
|
|
9489
9506
|
}
|
|
9490
9507
|
|
|
9491
9508
|
// src/index.ts
|
|
9492
|
-
var VERSION = true ? "0.25.
|
|
9509
|
+
var VERSION = true ? "0.25.7" : "0.0.0-dev";
|
|
9493
9510
|
function createProgram() {
|
|
9494
9511
|
const program = new Command();
|
|
9495
9512
|
program.name("codeharness").description("Makes autonomous coding agents produce software that actually works").version(VERSION).option("--json", "Output in machine-readable JSON format");
|