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.5" : "0.0.0-dev";
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",
@@ -16,7 +16,7 @@ import {
16
16
  stopCollectorOnly,
17
17
  stopSharedStack,
18
18
  stopStack
19
- } from "./chunk-ZFM7IU3A.js";
19
+ } from "./chunk-MODOFVMH.js";
20
20
  export {
21
21
  checkRemoteEndpoint,
22
22
  cleanupOrphanedContainers,
package/dist/index.js CHANGED
@@ -51,7 +51,7 @@ import {
51
51
  validateDockerfile,
52
52
  warn,
53
53
  writeState
54
- } from "./chunk-ZFM7IU3A.js";
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 existsSync2, readFileSync, writeFileSync } from "fs";
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 (!existsSync2(filePath)) {
443
+ if (!existsSync3(filePath)) {
212
444
  return null;
213
445
  }
214
- const content = readFileSync(filePath, "utf-8");
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 = readFileSync(filePath, "utf-8");
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 existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
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 = join(root, SPRINT_STATUS_PATH);
247
- if (!existsSync3(filePath)) {
478
+ const filePath = join2(root, SPRINT_STATUS_PATH);
479
+ if (!existsSync4(filePath)) {
248
480
  return {};
249
481
  }
250
482
  try {
251
- const content = readFileSync2(filePath, "utf-8");
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 = join(root, SPRINT_STATUS_PATH);
268
- if (!existsSync3(filePath)) {
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 = readFileSync2(filePath, "utf-8");
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 existsSync4 } from "fs";
286
- import { join as join2 } from "path";
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 = join2(root, storyFilePath);
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 = join2(root, storyFilePath);
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 = join2(root, storyFilePath);
679
+ const fullPath = join3(root, storyFilePath);
448
680
  const previousStatus = readStoryFileStatus(fullPath);
449
681
  if (previousStatus === null) {
450
- if (!existsSync4(fullPath)) {
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
- if (Object.keys(statuses).length > 0) return statuses;
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-XPXVRUOR.js");
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-XPXVRUOR.js");
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.5" : "0.0.0-dev";
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");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeharness",
3
- "version": "0.25.5",
3
+ "version": "0.25.7",
4
4
  "type": "module",
5
5
  "description": "CLI for codeharness — makes autonomous coding agents produce software that actually works",
6
6
  "bin": {