@saga-ai/cli 2.17.0 → 2.17.1

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.cjs CHANGED
@@ -26,11 +26,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  // src/cli.ts
27
27
  var import_node_fs9 = require("node:fs");
28
28
  var import_node_path15 = require("node:path");
29
- var import_node_process10 = __toESM(require("node:process"), 1);
29
+ var import_node_process11 = __toESM(require("node:process"), 1);
30
30
  var import_commander = require("commander");
31
31
 
32
32
  // src/commands/dashboard.ts
33
- var import_node_process3 = __toESM(require("node:process"), 1);
33
+ var import_node_process4 = __toESM(require("node:process"), 1);
34
34
 
35
35
  // src/server/index.ts
36
36
  var import_node_http = require("node:http");
@@ -385,6 +385,7 @@ async function parseJournal(journalPath) {
385
385
  entries.push({
386
386
  timestamp,
387
387
  type: "session",
388
+ title: `Session ${timestamp}`,
388
389
  content: sectionContent
389
390
  });
390
391
  } else if (headerLine.toLowerCase().startsWith("blocker:")) {
@@ -393,9 +394,8 @@ async function parseJournal(journalPath) {
393
394
  timestamp: "",
394
395
  // Blockers may not have timestamps
395
396
  type: "blocker",
396
- content: `${title}
397
-
398
- ${sectionContent}`.trim()
397
+ title,
398
+ content: sectionContent
399
399
  });
400
400
  } else if (headerLine.toLowerCase().startsWith("resolution:")) {
401
401
  const title = headerLine.substring("resolution:".length).trim();
@@ -403,9 +403,8 @@ ${sectionContent}`.trim()
403
403
  timestamp: "",
404
404
  // Resolutions may not have timestamps
405
405
  type: "resolution",
406
- content: `${title}
407
-
408
- ${sectionContent}`.trim()
406
+ title,
407
+ content: sectionContent
409
408
  });
410
409
  }
411
410
  }
@@ -1199,12 +1198,16 @@ var LogStreamManager = class {
1199
1198
  // src/server/watcher.ts
1200
1199
  var import_node_events = require("node:events");
1201
1200
  var import_node_path6 = require("node:path");
1201
+ var import_node_process2 = __toESM(require("node:process"), 1);
1202
1202
  var import_chokidar2 = __toESM(require("chokidar"), 1);
1203
1203
  var MIN_PATH_PARTS = 4;
1204
1204
  var ARCHIVE_STORY_PARTS = 5;
1205
1205
  var EPIC_FILE_PARTS = 4;
1206
1206
  var STORY_FILE_PARTS = 6;
1207
1207
  var DEBOUNCE_DELAY_MS = 100;
1208
+ function shouldUsePolling() {
1209
+ return import_node_process2.default.env.SAGA_USE_POLLING === "1";
1210
+ }
1208
1211
  function isStoryMarkdownFile(fileName) {
1209
1212
  return fileName === "story.md" || fileName === "journal.md";
1210
1213
  }
@@ -1327,17 +1330,18 @@ function createDebounceKey(parsed) {
1327
1330
  function createChokidarWatcher(sagaRoot) {
1328
1331
  const epicsDir = (0, import_node_path6.join)(sagaRoot, ".saga", "epics");
1329
1332
  const archiveDir = (0, import_node_path6.join)(sagaRoot, ".saga", "archive");
1333
+ const usePolling = shouldUsePolling();
1330
1334
  return import_chokidar2.default.watch([epicsDir, archiveDir], {
1331
1335
  persistent: true,
1332
1336
  ignoreInitial: true,
1333
- usePolling: true,
1334
- interval: DEBOUNCE_DELAY_MS,
1335
- // Wait for writes to finish before emitting events - prevents missing
1336
- // rapid file changes when polling
1337
- awaitWriteFinish: {
1337
+ // Use polling for tests (reliable) or native watching for production (fast)
1338
+ usePolling,
1339
+ interval: usePolling ? DEBOUNCE_DELAY_MS : void 0,
1340
+ // Wait for writes to finish when polling
1341
+ awaitWriteFinish: usePolling ? {
1338
1342
  stabilityThreshold: 50,
1339
1343
  pollInterval: 50
1340
- }
1344
+ } : false
1341
1345
  });
1342
1346
  }
1343
1347
  async function createSagaWatcher(sagaRoot) {
@@ -1734,9 +1738,9 @@ async function startServer(config) {
1734
1738
  // src/utils/project-discovery.ts
1735
1739
  var import_node_fs4 = require("node:fs");
1736
1740
  var import_node_path9 = require("node:path");
1737
- var import_node_process2 = __toESM(require("node:process"), 1);
1741
+ var import_node_process3 = __toESM(require("node:process"), 1);
1738
1742
  function findProjectRoot(startDir) {
1739
- let currentDir = startDir ?? import_node_process2.default.cwd();
1743
+ let currentDir = startDir ?? import_node_process3.default.cwd();
1740
1744
  while (true) {
1741
1745
  const sagaDir = (0, import_node_path9.join)(currentDir, ".saga");
1742
1746
  if ((0, import_node_fs4.existsSync)(sagaDir)) {
@@ -1776,7 +1780,7 @@ async function dashboardCommand(options) {
1776
1780
  projectPath = resolveProjectPath(options.path);
1777
1781
  } catch (error) {
1778
1782
  console.error(error instanceof Error ? error.message : "Failed to resolve SAGA project path");
1779
- import_node_process3.default.exit(1);
1783
+ import_node_process4.default.exit(1);
1780
1784
  }
1781
1785
  try {
1782
1786
  const server = await startServer({
@@ -1785,21 +1789,21 @@ async function dashboardCommand(options) {
1785
1789
  });
1786
1790
  console.log(`SAGA Dashboard server running on http://localhost:${server.port}`);
1787
1791
  console.log(`Project: ${projectPath}`);
1788
- import_node_process3.default.on("SIGINT", async () => {
1792
+ import_node_process4.default.on("SIGINT", async () => {
1789
1793
  await server.close();
1790
- import_node_process3.default.exit(0);
1794
+ import_node_process4.default.exit(0);
1791
1795
  });
1792
- import_node_process3.default.on("SIGTERM", async () => {
1796
+ import_node_process4.default.on("SIGTERM", async () => {
1793
1797
  await server.close();
1794
- import_node_process3.default.exit(0);
1798
+ import_node_process4.default.exit(0);
1795
1799
  });
1796
1800
  } catch (_error) {
1797
- import_node_process3.default.exit(1);
1801
+ import_node_process4.default.exit(1);
1798
1802
  }
1799
1803
  }
1800
1804
 
1801
1805
  // src/commands/find.ts
1802
- var import_node_process4 = __toESM(require("node:process"), 1);
1806
+ var import_node_process5 = __toESM(require("node:process"), 1);
1803
1807
 
1804
1808
  // src/utils/finder.ts
1805
1809
  var import_node_fs5 = require("node:fs");
@@ -1959,7 +1963,7 @@ async function findCommand(query, options) {
1959
1963
  try {
1960
1964
  projectPath = resolveProjectPath(options.path);
1961
1965
  } catch (_error) {
1962
- import_node_process4.default.exit(1);
1966
+ import_node_process5.default.exit(1);
1963
1967
  }
1964
1968
  const type = options.type ?? "story";
1965
1969
  let result;
@@ -1970,7 +1974,7 @@ async function findCommand(query, options) {
1970
1974
  }
1971
1975
  console.log(JSON.stringify(result, null, 2));
1972
1976
  if (!result.found) {
1973
- import_node_process4.default.exit(1);
1977
+ import_node_process5.default.exit(1);
1974
1978
  }
1975
1979
  }
1976
1980
 
@@ -1978,7 +1982,7 @@ async function findCommand(query, options) {
1978
1982
  var import_node_child_process2 = require("node:child_process");
1979
1983
  var import_node_fs6 = require("node:fs");
1980
1984
  var import_node_path11 = require("node:path");
1981
- var import_node_process5 = __toESM(require("node:process"), 1);
1985
+ var import_node_process6 = __toESM(require("node:process"), 1);
1982
1986
  var DEFAULT_MAX_CYCLES = 10;
1983
1987
  var DEFAULT_MAX_TIME = 60;
1984
1988
  var DEFAULT_MODEL = "opus";
@@ -2436,19 +2440,19 @@ function spawnWorkerAsync(prompt, model, settings, workingDir) {
2436
2440
  if (line.trim()) {
2437
2441
  const formatted = formatStreamLine(line);
2438
2442
  if (formatted) {
2439
- import_node_process5.default.stdout.write(formatted);
2443
+ import_node_process6.default.stdout.write(formatted);
2440
2444
  }
2441
2445
  }
2442
2446
  }
2443
2447
  });
2444
2448
  child.stderr.on("data", (chunk) => {
2445
- import_node_process5.default.stderr.write(chunk);
2449
+ import_node_process6.default.stderr.write(chunk);
2446
2450
  });
2447
2451
  child.on("error", (err) => {
2448
2452
  reject(new Error(`Failed to spawn worker: ${err.message}`));
2449
2453
  });
2450
2454
  child.on("close", (_code) => {
2451
- import_node_process5.default.stdout.write("\n");
2455
+ import_node_process6.default.stdout.write("\n");
2452
2456
  try {
2453
2457
  const result = parseStreamingResult(buffer);
2454
2458
  resolve2(result);
@@ -2612,7 +2616,7 @@ function buildDetachedCommand(storySlug, projectPath, options) {
2612
2616
  function handleDryRun(storyInfo, projectPath, pluginRoot) {
2613
2617
  const dryRunResult = runDryRun(storyInfo, projectPath, pluginRoot);
2614
2618
  printDryRunResults(dryRunResult);
2615
- import_node_process5.default.exit(dryRunResult.success ? 0 : 1);
2619
+ import_node_process6.default.exit(dryRunResult.success ? 0 : 1);
2616
2620
  }
2617
2621
  async function handleDetachedMode(storySlug, storyInfo, projectPath, options) {
2618
2622
  const detachedCommand = buildDetachedCommand(storySlug, projectPath, {
@@ -2631,7 +2635,7 @@ async function handleDetachedMode(storySlug, storyInfo, projectPath, options) {
2631
2635
  console.error(
2632
2636
  `Error creating session: ${error instanceof Error ? error.message : String(error)}`
2633
2637
  );
2634
- import_node_process5.default.exit(1);
2638
+ import_node_process6.default.exit(1);
2635
2639
  }
2636
2640
  }
2637
2641
  async function handleInternalSession(storyInfo, projectPath, pluginRoot, options) {
@@ -2653,7 +2657,7 @@ async function handleInternalSession(storyInfo, projectPath, pluginRoot, options
2653
2657
  );
2654
2658
  if (result.status === "ERROR") {
2655
2659
  console.error(`Error: ${result.summary}`);
2656
- import_node_process5.default.exit(1);
2660
+ import_node_process6.default.exit(1);
2657
2661
  }
2658
2662
  console.log(`
2659
2663
  Implementation ${result.status}: ${result.summary}`);
@@ -2664,28 +2668,28 @@ async function implementCommand(storySlug, options) {
2664
2668
  projectPath = resolveProjectPath(options.path);
2665
2669
  } catch (_error) {
2666
2670
  console.error("Error: SAGA project not found. Run saga init first or use --path option.");
2667
- import_node_process5.default.exit(1);
2671
+ import_node_process6.default.exit(1);
2668
2672
  }
2669
2673
  const storyInfo = await findStory2(projectPath, storySlug);
2670
2674
  if (!storyInfo) {
2671
2675
  console.error(`Error: Story '${storySlug}' not found in project.`);
2672
2676
  console.error("Use /generate-stories to create stories for an epic first.");
2673
- import_node_process5.default.exit(1);
2677
+ import_node_process6.default.exit(1);
2674
2678
  }
2675
- const pluginRoot = import_node_process5.default.env.SAGA_PLUGIN_ROOT;
2679
+ const pluginRoot = import_node_process6.default.env.SAGA_PLUGIN_ROOT;
2676
2680
  if (options.dryRun) {
2677
2681
  handleDryRun(storyInfo, projectPath, pluginRoot);
2678
2682
  }
2679
2683
  if (!pluginRoot) {
2680
2684
  console.error("Error: SAGA_PLUGIN_ROOT environment variable is not set.");
2681
2685
  console.error("This is required to find the worker prompt template.");
2682
- import_node_process5.default.exit(1);
2686
+ import_node_process6.default.exit(1);
2683
2687
  }
2684
2688
  if (!(0, import_node_fs6.existsSync)(storyInfo.worktreePath)) {
2685
2689
  console.error(`Error: Worktree not found at ${storyInfo.worktreePath}`);
2686
- import_node_process5.default.exit(1);
2690
+ import_node_process6.default.exit(1);
2687
2691
  }
2688
- const isInternalSession = import_node_process5.default.env.SAGA_INTERNAL_SESSION === "1";
2692
+ const isInternalSession = import_node_process6.default.env.SAGA_INTERNAL_SESSION === "1";
2689
2693
  if (isInternalSession) {
2690
2694
  await handleInternalSession(storyInfo, projectPath, pluginRoot, options);
2691
2695
  } else {
@@ -2696,7 +2700,7 @@ async function implementCommand(storySlug, options) {
2696
2700
  // src/commands/init.ts
2697
2701
  var import_node_fs7 = require("node:fs");
2698
2702
  var import_node_path12 = require("node:path");
2699
- var import_node_process6 = __toESM(require("node:process"), 1);
2703
+ var import_node_process7 = __toESM(require("node:process"), 1);
2700
2704
  var WORKTREES_PATTERN = ".saga/worktrees/";
2701
2705
  function runInitDryRun(targetPath) {
2702
2706
  const sagaDir = (0, import_node_path12.join)(targetPath, ".saga");
@@ -2760,7 +2764,7 @@ function resolveTargetPath(explicitPath) {
2760
2764
  if (existingRoot) {
2761
2765
  return existingRoot;
2762
2766
  }
2763
- return import_node_process6.default.cwd();
2767
+ return import_node_process7.default.cwd();
2764
2768
  }
2765
2769
  function createDirectoryStructure(projectRoot) {
2766
2770
  const sagaDir = (0, import_node_path12.join)(projectRoot, ".saga");
@@ -2795,11 +2799,11 @@ function initCommand(options) {
2795
2799
  if (options.path) {
2796
2800
  if (!(0, import_node_fs7.existsSync)(options.path)) {
2797
2801
  console.error(`Error: Path does not exist: ${options.path}`);
2798
- import_node_process6.default.exit(1);
2802
+ import_node_process7.default.exit(1);
2799
2803
  }
2800
2804
  if (!(0, import_node_fs7.statSync)(options.path).isDirectory()) {
2801
2805
  console.error(`Error: Path is not a directory: ${options.path}`);
2802
- import_node_process6.default.exit(1);
2806
+ import_node_process7.default.exit(1);
2803
2807
  }
2804
2808
  }
2805
2809
  const targetPath = resolveTargetPath(options.path);
@@ -2815,7 +2819,7 @@ function initCommand(options) {
2815
2819
 
2816
2820
  // src/commands/scope-validator.ts
2817
2821
  var import_node_path13 = require("node:path");
2818
- var import_node_process7 = __toESM(require("node:process"), 1);
2822
+ var import_node_process8 = __toESM(require("node:process"), 1);
2819
2823
  var FILE_PATH_WIDTH = 50;
2820
2824
  var EPIC_STORY_WIDTH = 43;
2821
2825
  var REASON_WIDTH = 56;
@@ -2884,19 +2888,19 @@ function printScopeViolation(filePath, epicSlug, storySlug, worktreePath, reason
2884
2888
  "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F",
2885
2889
  ""
2886
2890
  ].join("\n");
2887
- import_node_process7.default.stderr.write(message);
2891
+ import_node_process8.default.stderr.write(message);
2888
2892
  }
2889
2893
  async function readStdinInput() {
2890
2894
  const chunks = [];
2891
- for await (const chunk of import_node_process7.default.stdin) {
2895
+ for await (const chunk of import_node_process8.default.stdin) {
2892
2896
  chunks.push(chunk);
2893
2897
  }
2894
2898
  return Buffer.concat(chunks).toString("utf-8");
2895
2899
  }
2896
2900
  function getScopeEnvironment() {
2897
- const worktreePath = import_node_process7.default.env.SAGA_PROJECT_DIR || "";
2898
- const epicSlug = import_node_process7.default.env.SAGA_EPIC_SLUG || "";
2899
- const storySlug = import_node_process7.default.env.SAGA_STORY_SLUG || "";
2901
+ const worktreePath = import_node_process8.default.env.SAGA_PROJECT_DIR || "";
2902
+ const epicSlug = import_node_process8.default.env.SAGA_EPIC_SLUG || "";
2903
+ const storySlug = import_node_process8.default.env.SAGA_STORY_SLUG || "";
2900
2904
  if (!(worktreePath && epicSlug && storySlug)) {
2901
2905
  return null;
2902
2906
  }
@@ -2918,23 +2922,23 @@ function validatePath(filePath, worktreePath, epicSlug, storySlug) {
2918
2922
  async function scopeValidatorCommand() {
2919
2923
  const env = getScopeEnvironment();
2920
2924
  if (!env) {
2921
- import_node_process7.default.exit(2);
2925
+ import_node_process8.default.exit(2);
2922
2926
  }
2923
2927
  const toolInput = await readStdinInput();
2924
2928
  const filePath = getFilePathFromInput(toolInput);
2925
2929
  if (!filePath) {
2926
- import_node_process7.default.exit(0);
2930
+ import_node_process8.default.exit(0);
2927
2931
  }
2928
2932
  const violation = validatePath(filePath, env.worktreePath, env.epicSlug, env.storySlug);
2929
2933
  if (violation) {
2930
2934
  printScopeViolation(filePath, env.epicSlug, env.storySlug, env.worktreePath, violation);
2931
- import_node_process7.default.exit(2);
2935
+ import_node_process8.default.exit(2);
2932
2936
  }
2933
- import_node_process7.default.exit(0);
2937
+ import_node_process8.default.exit(0);
2934
2938
  }
2935
2939
 
2936
2940
  // src/commands/sessions/index.ts
2937
- var import_node_process8 = __toESM(require("node:process"), 1);
2941
+ var import_node_process9 = __toESM(require("node:process"), 1);
2938
2942
  async function sessionsListCommand() {
2939
2943
  const sessions = await listSessions();
2940
2944
  console.log(JSON.stringify(sessions, null, 2));
@@ -2948,7 +2952,7 @@ async function sessionsLogsCommand(sessionName) {
2948
2952
  await streamLogs(sessionName);
2949
2953
  } catch (error) {
2950
2954
  console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
2951
- import_node_process8.default.exit(1);
2955
+ import_node_process9.default.exit(1);
2952
2956
  }
2953
2957
  }
2954
2958
  async function sessionsKillCommand(sessionName) {
@@ -2960,7 +2964,7 @@ async function sessionsKillCommand(sessionName) {
2960
2964
  var import_node_child_process3 = require("node:child_process");
2961
2965
  var import_node_fs8 = require("node:fs");
2962
2966
  var import_node_path14 = require("node:path");
2963
- var import_node_process9 = __toESM(require("node:process"), 1);
2967
+ var import_node_process10 = __toESM(require("node:process"), 1);
2964
2968
  function runGitCommand(args, cwd) {
2965
2969
  try {
2966
2970
  const output = (0, import_node_child_process3.execSync)(`git ${args.join(" ")}`, {
@@ -3041,12 +3045,12 @@ function worktreeCommand(epicSlug, storySlug, options) {
3041
3045
  error: error instanceof Error ? error.message : String(error)
3042
3046
  };
3043
3047
  console.log(JSON.stringify(result2, null, 2));
3044
- import_node_process9.default.exit(1);
3048
+ import_node_process10.default.exit(1);
3045
3049
  }
3046
3050
  const result = createWorktree(projectPath, epicSlug, storySlug);
3047
3051
  console.log(JSON.stringify(result, null, 2));
3048
3052
  if (!result.success) {
3049
- import_node_process9.default.exit(1);
3053
+ import_node_process10.default.exit(1);
3050
3054
  }
3051
3055
  }
3052
3056
 
@@ -3109,6 +3113,6 @@ sessionsCommand.command("kill <name>").description("Terminate a session").action
3109
3113
  });
3110
3114
  program.on("command:*", (operands) => {
3111
3115
  console.error(`error: unknown command '${operands[0]}'`);
3112
- import_node_process10.default.exit(1);
3116
+ import_node_process11.default.exit(1);
3113
3117
  });
3114
3118
  program.parse();
@@ -11,5 +11,5 @@ Error generating stack: `+a.message+`
11
11
  `);return c.length>Xm?c.slice(-Xm):c}function Kb({truncatedOutput:u,outputAvailable:r}){const c=_.useRef(null);return _.useLayoutEffect(()=>{u&&c.current&&(c.current.scrollTop=c.current.scrollHeight)},[u]),r?u?f.jsx("pre",{ref:c,className:"font-mono bg-bg-dark text-xs p-2 rounded max-h-24 overflow-y-auto whitespace-pre-wrap",role:"presentation",children:u}):null:f.jsx("div",{className:"text-text-muted text-sm italic",children:"Output unavailable"})}function Jb({session:u}){const[r,c]=_.useState(()=>{const m=new Date(u.startTime).getTime(),g=Date.now();return Math.floor((g-m)/Lm)}),o=kb(u.outputPreview);return _.useEffect(()=>{const m=setInterval(()=>{const g=new Date(u.startTime).getTime(),v=Date.now();c(Math.floor((v-g)/Lm))},Zb);return()=>clearInterval(m)},[u.startTime]),f.jsx(il,{to:`/epic/${u.epicSlug}/story/${u.storySlug}?tab=sessions`,className:"block",children:f.jsxs(ne,{className:"hover:border-primary/50 transition-colors cursor-pointer",children:[f.jsxs(ye,{className:"pb-2",children:[f.jsx(sa,{className:"text-base",children:u.storySlug}),f.jsx("div",{className:"text-sm text-text-muted",children:u.epicSlug})]}),f.jsxs(Ae,{className:"space-y-2",children:[f.jsx("div",{className:"text-sm text-text-muted",children:eo(r)}),f.jsx(Kb,{truncatedOutput:o,outputAvailable:u.outputAvailable})]})]})})}function kc(){return f.jsxs(ne,{className:"animate-pulse min-w-[300px] flex-shrink-0","data-testid":"session-card-skeleton",children:[f.jsxs(ye,{className:"pb-2",children:[f.jsx("div",{className:"h-5 w-32 bg-bg-light rounded"}),f.jsx("div",{className:"h-4 w-20 bg-bg-light rounded mt-1"})]}),f.jsx(Ae,{children:f.jsx("div",{className:"h-16 w-full bg-bg-light rounded"})})]})}function Wb(){const{sessions:u,setSessions:r}=ua(),[c,o]=_.useState(!0);_.useEffect(()=>{(async()=>{try{const v=await fetch("/api/sessions?status=running");if(v.ok){const E=await v.json();r(E)}}catch{r([])}finally{o(!1)}})()},[r]);const m=_.useMemo(()=>u.filter(g=>g.status==="running"),[u]);return c?f.jsx($b,{}):m.length===0?null:f.jsxs("section",{"data-testid":"active-sessions",className:"space-y-4",children:[f.jsx("h2",{className:"text-xl font-semibold text-text",children:"Active Sessions"}),f.jsx("div",{className:"flex gap-4 overflow-x-auto pb-2",children:m.map(g=>f.jsx("div",{className:"min-w-[300px] flex-shrink-0",children:f.jsx(Jb,{session:g})},g.name))})]})}function $b(){return f.jsxs("section",{"data-testid":"active-sessions-skeleton",className:"space-y-4",children:[f.jsx("h2",{className:"text-xl font-semibold text-text",children:"Active Sessions"}),f.jsxs("div",{className:"flex gap-4 overflow-x-auto pb-2",children:[f.jsx(kc,{}),f.jsx(kc,{}),f.jsx(kc,{})]})]})}const Fb=100;function Kc(){return f.jsxs(ne,{className:"animate-pulse","data-testid":"epic-card-skeleton",children:[f.jsx(ye,{children:f.jsx("div",{className:"h-6 w-48 bg-bg-light rounded"})}),f.jsxs(Ae,{className:"space-y-4",children:[f.jsx("div",{className:"h-4 w-full bg-bg-light rounded"}),f.jsxs("div",{className:"flex gap-2",children:[f.jsx("div",{className:"h-6 w-16 bg-bg-light rounded-full"}),f.jsx("div",{className:"h-6 w-16 bg-bg-light rounded-full"}),f.jsx("div",{className:"h-6 w-16 bg-bg-light rounded-full"})]})]})]})}function hu({status:u,count:r}){const c={ready:"bg-text-muted/20 text-text-muted",inProgress:"bg-primary/20 text-primary",blocked:"bg-danger/20 text-danger",completed:"bg-success/20 text-success"},o={ready:"Ready",inProgress:"In Progress",blocked:"Blocked",completed:"Completed"};return f.jsxs(wl,{className:c[u],children:[o[u],": ",r]})}function Ib({epic:u}){const{storyCounts:r}=u,c=r.total>0?Math.round(r.completed/r.total*Fb):0;return f.jsx(il,{to:`/epic/${u.slug}`,className:"block",children:f.jsxs(ne,{className:"hover:border-primary/50 transition-colors cursor-pointer",children:[f.jsx(ye,{children:f.jsx(sa,{className:"text-lg",children:u.title})}),f.jsxs(Ae,{className:"space-y-4",children:[f.jsxs("div",{className:"space-y-2",children:[f.jsxs("div",{className:"flex justify-between text-sm",children:[f.jsx("span",{className:"text-text-muted",children:"Progress"}),f.jsxs("span",{className:"text-text-muted",children:[r.completed,"/",r.total," stories"]})]}),f.jsx(go,{value:c})]}),f.jsxs("div",{className:"flex flex-wrap gap-2",children:[r.ready>0&&f.jsx(hu,{status:"ready",count:r.ready}),r.inProgress>0&&f.jsx(hu,{status:"inProgress",count:r.inProgress}),r.blocked>0&&f.jsx(hu,{status:"blocked",count:r.blocked}),r.completed>0&&f.jsx(hu,{status:"completed",count:r.completed})]})]})]})})}function Pb(){const{epics:u,setEpics:r}=ua(),[c,o]=_.useState(!1),[m,g]=_.useState(!0);_.useEffect(()=>{(async()=>{try{const R=await fetch("/api/epics");if(R.ok){const C=await R.json();r(C)}}catch(R){const C=R instanceof Error?R.message:"Unknown error";ro("/api/epics",C)}finally{g(!1)}})()},[r]);const v=m,E=u.filter(M=>c?!0:!M.isArchived),j=u.some(M=>M.isArchived);return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Wb,{}),f.jsxs("div",{className:"flex items-center justify-between",children:[f.jsx("h1",{className:"text-2xl font-bold text-text",children:"Epics"}),j&&f.jsxs("label",{className:"flex items-center gap-2 text-sm text-text-muted cursor-pointer",children:[f.jsx("input",{type:"checkbox",checked:c,onChange:M=>o(M.target.checked),className:"rounded border-border"}),"Show archived"]})]}),v&&f.jsxs("div",{className:"grid gap-4 md:grid-cols-2 lg:grid-cols-3",children:[f.jsx(Kc,{}),f.jsx(Kc,{}),f.jsx(Kc,{})]}),!v&&E.length===0&&f.jsxs("div",{className:"text-center py-12",children:[f.jsx("p",{className:"text-text-muted text-lg",children:"No epics found."}),f.jsxs("p",{className:"text-text-muted",children:["Run ",f.jsx("code",{className:"text-primary",children:"/create-epic"})," to get started."]})]}),!v&&E.length>0&&f.jsx("div",{className:"grid gap-4 md:grid-cols-2 lg:grid-cols-3",children:E.map(M=>f.jsx(Ib,{epic:M},M.slug))})]})}const t0=uo("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground hover:bg-destructive/90",outline:"border border-input bg-background hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3",lg:"h-11 rounded-md px-8",icon:"h-10 w-10"}},defaultVariants:{variant:"default",size:"default"}}),Eh=_.forwardRef(({className:u,variant:r,size:c,asChild:o=!1,...m},g)=>{const v=o?fp:"button";return f.jsx(v,{className:Ht(t0({variant:r,size:c,className:u})),ref:g,...m})});Eh.displayName="Button";function Ka(u,r,c){let o=c.initialDeps??[],m,g=!0;function v(){var E,j,M;let R;c.key&&((E=c.debug)!=null&&E.call(c))&&(R=Date.now());const C=u();if(!(C.length!==o.length||C.some((Z,G)=>o[G]!==Z)))return m;o=C;let k;if(c.key&&((j=c.debug)!=null&&j.call(c))&&(k=Date.now()),m=r(...C),c.key&&((M=c.debug)!=null&&M.call(c))){const Z=Math.round((Date.now()-R)*100)/100,G=Math.round((Date.now()-k)*100)/100,J=G/16,gt=(St,Et)=>{for(St=String(St);St.length<Et;)St=" "+St;return St};console.info(`%c⏱ ${gt(G,5)} /${gt(Z,5)} ms`,`
12
12
  font-size: .6rem;
13
13
  font-weight: bold;
14
- color: hsl(${Math.max(0,Math.min(120-120*J,120))}deg 100% 31%);`,c?.key)}return c?.onChange&&!(g&&c.skipInitialOnChange)&&c.onChange(m),g=!1,m}return v.updateDeps=E=>{o=E},v}function Vm(u,r){if(u===void 0)throw new Error("Unexpected undefined");return u}const e0=(u,r)=>Math.abs(u-r)<1.01,l0=(u,r,c)=>{let o;return function(...m){u.clearTimeout(o),o=u.setTimeout(()=>r.apply(this,m),c)}},Qm=u=>{const{offsetWidth:r,offsetHeight:c}=u;return{width:r,height:c}},a0=u=>u,n0=u=>{const r=Math.max(u.startIndex-u.overscan,0),c=Math.min(u.endIndex+u.overscan,u.count-1),o=[];for(let m=r;m<=c;m++)o.push(m);return o},i0=(u,r)=>{const c=u.scrollElement;if(!c)return;const o=u.targetWindow;if(!o)return;const m=v=>{const{width:E,height:j}=v;r({width:Math.round(E),height:Math.round(j)})};if(m(Qm(c)),!o.ResizeObserver)return()=>{};const g=new o.ResizeObserver(v=>{const E=()=>{const j=v[0];if(j?.borderBoxSize){const M=j.borderBoxSize[0];if(M){m({width:M.inlineSize,height:M.blockSize});return}}m(Qm(c))};u.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(E):E()});return g.observe(c,{box:"border-box"}),()=>{g.unobserve(c)}},Zm={passive:!0},km=typeof window>"u"?!0:"onscrollend"in window,u0=(u,r)=>{const c=u.scrollElement;if(!c)return;const o=u.targetWindow;if(!o)return;let m=0;const g=u.options.useScrollendEvent&&km?()=>{}:l0(o,()=>{r(m,!1)},u.options.isScrollingResetDelay),v=R=>()=>{const{horizontal:C,isRtl:U}=u.options;m=C?c.scrollLeft*(U&&-1||1):c.scrollTop,g(),r(m,R)},E=v(!0),j=v(!1);c.addEventListener("scroll",E,Zm);const M=u.options.useScrollendEvent&&km;return M&&c.addEventListener("scrollend",j,Zm),()=>{c.removeEventListener("scroll",E),M&&c.removeEventListener("scrollend",j)}},s0=(u,r,c)=>{if(r?.borderBoxSize){const o=r.borderBoxSize[0];if(o)return Math.round(o[c.options.horizontal?"inlineSize":"blockSize"])}return u[c.options.horizontal?"offsetWidth":"offsetHeight"]},c0=(u,{adjustments:r=0,behavior:c},o)=>{var m,g;const v=u+r;(g=(m=o.scrollElement)==null?void 0:m.scrollTo)==null||g.call(m,{[o.options.horizontal?"left":"top"]:v,behavior:c})};class o0{constructor(r){this.unsubs=[],this.scrollElement=null,this.targetWindow=null,this.isScrolling=!1,this.currentScrollToIndex=null,this.measurementsCache=[],this.itemSizeCache=new Map,this.laneAssignments=new Map,this.pendingMeasuredCacheIndexes=[],this.prevLanes=void 0,this.lanesChangedFlag=!1,this.lanesSettling=!1,this.scrollRect=null,this.scrollOffset=null,this.scrollDirection=null,this.scrollAdjustments=0,this.elementsCache=new Map,this.observer=(()=>{let c=null;const o=()=>c||(!this.targetWindow||!this.targetWindow.ResizeObserver?null:c=new this.targetWindow.ResizeObserver(m=>{m.forEach(g=>{const v=()=>{this._measureElement(g.target,g)};this.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(v):v()})}));return{disconnect:()=>{var m;(m=o())==null||m.disconnect(),c=null},observe:m=>{var g;return(g=o())==null?void 0:g.observe(m,{box:"border-box"})},unobserve:m=>{var g;return(g=o())==null?void 0:g.unobserve(m)}}})(),this.range=null,this.setOptions=c=>{Object.entries(c).forEach(([o,m])=>{typeof m>"u"&&delete c[o]}),this.options={debug:!1,initialOffset:0,overscan:1,paddingStart:0,paddingEnd:0,scrollPaddingStart:0,scrollPaddingEnd:0,horizontal:!1,getItemKey:a0,rangeExtractor:n0,onChange:()=>{},measureElement:s0,initialRect:{width:0,height:0},scrollMargin:0,gap:0,indexAttribute:"data-index",initialMeasurementsCache:[],lanes:1,isScrollingResetDelay:150,enabled:!0,isRtl:!1,useScrollendEvent:!1,useAnimationFrameWithResizeObserver:!1,...c}},this.notify=c=>{var o,m;(m=(o=this.options).onChange)==null||m.call(o,this,c)},this.maybeNotify=Ka(()=>(this.calculateRange(),[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]),c=>{this.notify(c)},{key:!1,debug:()=>this.options.debug,initialDeps:[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]}),this.cleanup=()=>{this.unsubs.filter(Boolean).forEach(c=>c()),this.unsubs=[],this.observer.disconnect(),this.scrollElement=null,this.targetWindow=null},this._didMount=()=>()=>{this.cleanup()},this._willUpdate=()=>{var c;const o=this.options.enabled?this.options.getScrollElement():null;if(this.scrollElement!==o){if(this.cleanup(),!o){this.maybeNotify();return}this.scrollElement=o,this.scrollElement&&"ownerDocument"in this.scrollElement?this.targetWindow=this.scrollElement.ownerDocument.defaultView:this.targetWindow=((c=this.scrollElement)==null?void 0:c.window)??null,this.elementsCache.forEach(m=>{this.observer.observe(m)}),this.unsubs.push(this.options.observeElementRect(this,m=>{this.scrollRect=m,this.maybeNotify()})),this.unsubs.push(this.options.observeElementOffset(this,(m,g)=>{this.scrollAdjustments=0,this.scrollDirection=g?this.getScrollOffset()<m?"forward":"backward":null,this.scrollOffset=m,this.isScrolling=g,this.maybeNotify()})),this._scrollToOffset(this.getScrollOffset(),{adjustments:void 0,behavior:void 0})}},this.getSize=()=>this.options.enabled?(this.scrollRect=this.scrollRect??this.options.initialRect,this.scrollRect[this.options.horizontal?"width":"height"]):(this.scrollRect=null,0),this.getScrollOffset=()=>this.options.enabled?(this.scrollOffset=this.scrollOffset??(typeof this.options.initialOffset=="function"?this.options.initialOffset():this.options.initialOffset),this.scrollOffset):(this.scrollOffset=null,0),this.getFurthestMeasurement=(c,o)=>{const m=new Map,g=new Map;for(let v=o-1;v>=0;v--){const E=c[v];if(m.has(E.lane))continue;const j=g.get(E.lane);if(j==null||E.end>j.end?g.set(E.lane,E):E.end<j.end&&m.set(E.lane,!0),m.size===this.options.lanes)break}return g.size===this.options.lanes?Array.from(g.values()).sort((v,E)=>v.end===E.end?v.index-E.index:v.end-E.end)[0]:void 0},this.getMeasurementOptions=Ka(()=>[this.options.count,this.options.paddingStart,this.options.scrollMargin,this.options.getItemKey,this.options.enabled,this.options.lanes],(c,o,m,g,v,E)=>(this.prevLanes!==void 0&&this.prevLanes!==E&&(this.lanesChangedFlag=!0),this.prevLanes=E,this.pendingMeasuredCacheIndexes=[],{count:c,paddingStart:o,scrollMargin:m,getItemKey:g,enabled:v,lanes:E}),{key:!1}),this.getMeasurements=Ka(()=>[this.getMeasurementOptions(),this.itemSizeCache],({count:c,paddingStart:o,scrollMargin:m,getItemKey:g,enabled:v,lanes:E},j)=>{if(!v)return this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),[];if(this.laneAssignments.size>c)for(const U of this.laneAssignments.keys())U>=c&&this.laneAssignments.delete(U);this.lanesChangedFlag&&(this.lanesChangedFlag=!1,this.lanesSettling=!0,this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),this.pendingMeasuredCacheIndexes=[]),this.measurementsCache.length===0&&!this.lanesSettling&&(this.measurementsCache=this.options.initialMeasurementsCache,this.measurementsCache.forEach(U=>{this.itemSizeCache.set(U.key,U.size)}));const M=this.lanesSettling?0:this.pendingMeasuredCacheIndexes.length>0?Math.min(...this.pendingMeasuredCacheIndexes):0;this.pendingMeasuredCacheIndexes=[],this.lanesSettling&&this.measurementsCache.length===c&&(this.lanesSettling=!1);const R=this.measurementsCache.slice(0,M),C=new Array(E).fill(void 0);for(let U=0;U<M;U++){const k=R[U];k&&(C[k.lane]=U)}for(let U=M;U<c;U++){const k=g(U),Z=this.laneAssignments.get(U);let G,J;if(Z!==void 0&&this.options.lanes>1){G=Z;const bt=C[G],qt=bt!==void 0?R[bt]:void 0;J=qt?qt.end+this.options.gap:o+m}else{const bt=this.options.lanes===1?R[U-1]:this.getFurthestMeasurement(R,U);J=bt?bt.end+this.options.gap:o+m,G=bt?bt.lane:U%this.options.lanes,this.options.lanes>1&&this.laneAssignments.set(U,G)}const gt=j.get(k),St=typeof gt=="number"?gt:this.options.estimateSize(U),Et=J+St;R[U]={index:U,start:J,size:St,end:Et,key:k,lane:G},C[G]=U}return this.measurementsCache=R,R},{key:!1,debug:()=>this.options.debug}),this.calculateRange=Ka(()=>[this.getMeasurements(),this.getSize(),this.getScrollOffset(),this.options.lanes],(c,o,m,g)=>this.range=c.length>0&&o>0?r0({measurements:c,outerSize:o,scrollOffset:m,lanes:g}):null,{key:!1,debug:()=>this.options.debug}),this.getVirtualIndexes=Ka(()=>{let c=null,o=null;const m=this.calculateRange();return m&&(c=m.startIndex,o=m.endIndex),this.maybeNotify.updateDeps([this.isScrolling,c,o]),[this.options.rangeExtractor,this.options.overscan,this.options.count,c,o]},(c,o,m,g,v)=>g===null||v===null?[]:c({startIndex:g,endIndex:v,overscan:o,count:m}),{key:!1,debug:()=>this.options.debug}),this.indexFromElement=c=>{const o=this.options.indexAttribute,m=c.getAttribute(o);return m?parseInt(m,10):(console.warn(`Missing attribute name '${o}={index}' on measured element.`),-1)},this._measureElement=(c,o)=>{const m=this.indexFromElement(c),g=this.measurementsCache[m];if(!g)return;const v=g.key,E=this.elementsCache.get(v);E!==c&&(E&&this.observer.unobserve(E),this.observer.observe(c),this.elementsCache.set(v,c)),c.isConnected&&this.resizeItem(m,this.options.measureElement(c,o,this))},this.resizeItem=(c,o)=>{const m=this.measurementsCache[c];if(!m)return;const g=this.itemSizeCache.get(m.key)??m.size,v=o-g;v!==0&&((this.shouldAdjustScrollPositionOnItemSizeChange!==void 0?this.shouldAdjustScrollPositionOnItemSizeChange(m,v,this):m.start<this.getScrollOffset()+this.scrollAdjustments)&&this._scrollToOffset(this.getScrollOffset(),{adjustments:this.scrollAdjustments+=v,behavior:void 0}),this.pendingMeasuredCacheIndexes.push(m.index),this.itemSizeCache=new Map(this.itemSizeCache.set(m.key,o)),this.notify(!1))},this.measureElement=c=>{if(!c){this.elementsCache.forEach((o,m)=>{o.isConnected||(this.observer.unobserve(o),this.elementsCache.delete(m))});return}this._measureElement(c,void 0)},this.getVirtualItems=Ka(()=>[this.getVirtualIndexes(),this.getMeasurements()],(c,o)=>{const m=[];for(let g=0,v=c.length;g<v;g++){const E=c[g],j=o[E];m.push(j)}return m},{key:!1,debug:()=>this.options.debug}),this.getVirtualItemForOffset=c=>{const o=this.getMeasurements();if(o.length!==0)return Vm(o[Nh(0,o.length-1,m=>Vm(o[m]).start,c)])},this.getMaxScrollOffset=()=>{if(!this.scrollElement)return 0;if("scrollHeight"in this.scrollElement)return this.options.horizontal?this.scrollElement.scrollWidth-this.scrollElement.clientWidth:this.scrollElement.scrollHeight-this.scrollElement.clientHeight;{const c=this.scrollElement.document.documentElement;return this.options.horizontal?c.scrollWidth-this.scrollElement.innerWidth:c.scrollHeight-this.scrollElement.innerHeight}},this.getOffsetForAlignment=(c,o,m=0)=>{if(!this.scrollElement)return 0;const g=this.getSize(),v=this.getScrollOffset();o==="auto"&&(o=c>=v+g?"end":"start"),o==="center"?c+=(m-g)/2:o==="end"&&(c-=g);const E=this.getMaxScrollOffset();return Math.max(Math.min(E,c),0)},this.getOffsetForIndex=(c,o="auto")=>{c=Math.max(0,Math.min(c,this.options.count-1));const m=this.measurementsCache[c];if(!m)return;const g=this.getSize(),v=this.getScrollOffset();if(o==="auto")if(m.end>=v+g-this.options.scrollPaddingEnd)o="end";else if(m.start<=v+this.options.scrollPaddingStart)o="start";else return[v,o];if(o==="end"&&c===this.options.count-1)return[this.getMaxScrollOffset(),o];const E=o==="end"?m.end+this.options.scrollPaddingEnd:m.start-this.options.scrollPaddingStart;return[this.getOffsetForAlignment(E,o,m.size),o]},this.isDynamicMode=()=>this.elementsCache.size>0,this.scrollToOffset=(c,{align:o="start",behavior:m}={})=>{m==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),this._scrollToOffset(this.getOffsetForAlignment(c,o),{adjustments:void 0,behavior:m})},this.scrollToIndex=(c,{align:o="auto",behavior:m}={})=>{m==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),c=Math.max(0,Math.min(c,this.options.count-1)),this.currentScrollToIndex=c;let g=0;const v=10,E=M=>{if(!this.targetWindow)return;const R=this.getOffsetForIndex(c,M);if(!R){console.warn("Failed to get offset for index:",c);return}const[C,U]=R;this._scrollToOffset(C,{adjustments:void 0,behavior:m}),this.targetWindow.requestAnimationFrame(()=>{const k=()=>{if(this.currentScrollToIndex!==c)return;const Z=this.getScrollOffset(),G=this.getOffsetForIndex(c,U);if(!G){console.warn("Failed to get offset for index:",c);return}e0(G[0],Z)||j(U)};this.isDynamicMode()?this.targetWindow.requestAnimationFrame(k):k()})},j=M=>{this.targetWindow&&this.currentScrollToIndex===c&&(g++,g<v?this.targetWindow.requestAnimationFrame(()=>E(M)):console.warn(`Failed to scroll to index ${c} after ${v} attempts.`))};E(o)},this.scrollBy=(c,{behavior:o}={})=>{o==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),this._scrollToOffset(this.getScrollOffset()+c,{adjustments:void 0,behavior:o})},this.getTotalSize=()=>{var c;const o=this.getMeasurements();let m;if(o.length===0)m=this.options.paddingStart;else if(this.options.lanes===1)m=((c=o[o.length-1])==null?void 0:c.end)??0;else{const g=Array(this.options.lanes).fill(null);let v=o.length-1;for(;v>=0&&g.some(E=>E===null);){const E=o[v];g[E.lane]===null&&(g[E.lane]=E.end),v--}m=Math.max(...g.filter(E=>E!==null))}return Math.max(m-this.options.scrollMargin+this.options.paddingEnd,0)},this._scrollToOffset=(c,{adjustments:o,behavior:m})=>{this.options.scrollToFn(c,{behavior:m,adjustments:o},this)},this.measure=()=>{this.itemSizeCache=new Map,this.laneAssignments=new Map,this.notify(!1)},this.setOptions(r)}}const Nh=(u,r,c,o)=>{for(;u<=r;){const m=(u+r)/2|0,g=c(m);if(g<o)u=m+1;else if(g>o)r=m-1;else return m}return u>0?u-1:0};function r0({measurements:u,outerSize:r,scrollOffset:c,lanes:o}){const m=u.length-1,g=j=>u[j].start;if(u.length<=o)return{startIndex:0,endIndex:m};let v=Nh(0,m,g,c),E=v;if(o===1)for(;E<m&&u[E].end<c+r;)E++;else if(o>1){const j=Array(o).fill(0);for(;E<m&&j.some(R=>R<c+r);){const R=u[E];j[R.lane]=R.end,E++}const M=Array(o).fill(c+r);for(;v>=0&&M.some(R=>R>=c);){const R=u[v];M[R.lane]=R.start,v--}v=Math.max(0,v-v%o),E=Math.min(m,E+(o-1-E%o))}return{startIndex:v,endIndex:E}}const Km=typeof document<"u"?_.useLayoutEffect:_.useEffect;function f0({useFlushSync:u=!0,...r}){const c=_.useReducer(()=>({}),{})[1],o={...r,onChange:(g,v)=>{var E;u&&v?dp.flushSync(c):c(),(E=r.onChange)==null||E.call(r,g,v)}},[m]=_.useState(()=>new o0(o));return m.setOptions(o),Km(()=>m._didMount(),[]),Km(()=>m._willUpdate()),m}function d0(u){return f0({observeElementRect:i0,observeElementOffset:u0,scrollToFn:c0,...u})}const zh=24,m0=5,h0=50;function g0({status:u}){return u==="running"?f.jsxs("div",{"data-testid":"status-indicator-streaming",className:"flex items-center gap-1.5 text-primary text-sm",children:[f.jsx(rv,{className:"h-3.5 w-3.5 animate-spin","data-testid":"status-icon-loader"}),f.jsx("span",{children:"Streaming"})]}):f.jsxs("div",{"data-testid":"status-indicator-complete",className:"flex items-center gap-1.5 text-success text-sm",children:[f.jsx(bu,{className:"h-3.5 w-3.5","data-testid":"status-icon-complete"}),f.jsx("span",{children:"Complete"})]})}function y0(){return f.jsxs("div",{"data-testid":"log-viewer-skeleton",className:"p-4 space-y-2",children:[f.jsx("div",{className:"h-4 bg-bg-light rounded animate-pulse w-3/4"}),f.jsx("div",{className:"h-4 bg-bg-light rounded animate-pulse w-1/2"}),f.jsx("div",{className:"h-4 bg-bg-light rounded animate-pulse w-5/6"}),f.jsx("div",{className:"h-4 bg-bg-light rounded animate-pulse w-2/3"})]})}function p0({status:u,autoScroll:r,onToggleAutoScroll:c}){return f.jsxs("div",{className:"flex items-center justify-between px-4 py-2 border-b border-bg-light",children:[f.jsx(g0,{status:u}),f.jsxs("button",{type:"button","data-testid":"auto-scroll-toggle",onClick:c,"aria-pressed":r,title:r?"Autoscroll locked to bottom":"Autoscroll unlocked",className:`flex items-center gap-1.5 px-2 py-1 text-xs rounded-md transition-colors ${r?"bg-success/20 text-success hover:bg-success/30":"bg-bg-light text-text-muted hover:bg-bg-lighter"}`,children:[r?f.jsx(hv,{className:"h-3.5 w-3.5","data-testid":"autoscroll-icon-enabled"}):f.jsx(dv,{className:"h-3.5 w-3.5","data-testid":"autoscroll-icon-disabled"}),f.jsx("span",{children:"Autoscroll"})]})]})}function v0({virtualizer:u,virtualItems:r,lines:c}){return f.jsx("div",{className:"relative w-full",style:{height:`${u.getTotalSize()}px`},"data-testid":"log-height-container",children:f.jsx("div",{className:"absolute top-0 left-0 w-full px-4",style:{transform:`translateY(${r[0]?.start??0}px)`},children:r.map(o=>f.jsx("div",{"data-testid":"log-line","data-index":o.index,className:"text-text leading-relaxed",style:{height:`${zh}px`},children:c[o.index]||" "},o.key))})})}function b0(){return f.jsx("div",{"data-testid":"log-viewer",className:"h-full bg-bg-dark rounded-md font-mono flex items-center justify-center",children:f.jsx("span",{className:"text-text-muted",children:"Output unavailable"})})}function x0({error:u}){return f.jsxs("div",{"data-testid":"log-viewer-error",className:"h-full bg-bg-dark rounded-md font-mono flex flex-col items-center justify-center gap-2",children:[f.jsx(vu,{className:"h-6 w-6 text-danger"}),f.jsx("span",{className:"text-danger text-sm",children:"Failed to load logs"}),f.jsx("span",{className:"text-text-muted text-xs max-w-xs text-center",children:u})]})}function S0(u,r){const[c,o]=_.useState(""),[m,g]=_.useState(!0),[v,E]=_.useState(!1),[j,M]=_.useState(null);return _.useEffect(()=>{if(!r){g(!1);return}const R=jm();if(!R){g(!1);return}return wp(u,(C,U,k)=>{U?(o(C),g(!1)):o(Z=>Z+C),k&&E(!0)},C=>{M(C),g(!1)}),R({event:"subscribe:logs",data:{sessionName:u}}),()=>{Bp(u);const C=jm();C&&C({event:"unsubscribe:logs",data:{sessionName:u}})}},[u,r]),{content:c,isLoading:m,streamComplete:v,error:j}}function E0(u,r,c,o){const[m,g]=_.useState(c==="running"),v=_.useRef(0);_.useEffect(()=>{m&&o>v.current&&requestAnimationFrame(()=>{if(r.current&&u.current){const M=r.current.getTotalSize();u.current.scrollTop=M}}),v.current=o},[o,m,r,u]);const E=_.useCallback(()=>{const M=u.current;if(!M)return;!(M.scrollHeight-M.scrollTop-M.clientHeight<h0)&&m&&g(!1)},[m,u]),j=_.useCallback(()=>{g(M=>(M||requestAnimationFrame(()=>{if(r.current&&u.current){const R=r.current.getTotalSize();u.current.scrollTop=R}}),!M))},[r,u]);return{autoScroll:m,handleScroll:E,toggleAutoScroll:j}}function N0(u){return _.useMemo(()=>u?u.split(`
15
- `):[],[u])}function z0({sessionName:u,status:r,outputAvailable:c,initialContent:o=""}){const m=_.useRef(null),g=_.useRef(null),{content:v,isLoading:E,streamComplete:j,error:M}=S0(u,c),C=N0(o||v),U=j?"completed":r,k=d0({count:C.length,getScrollElement:()=>m.current,estimateSize:()=>zh,overscan:m0});g.current=k;const{autoScroll:Z,handleScroll:G,toggleAutoScroll:J}=E0(m,g,U,C.length);if(!c)return f.jsx(b0,{});if(M)return f.jsx(x0,{error:M});const gt=E&&!o,St=k.getVirtualItems();return f.jsxs("div",{"data-testid":"log-viewer",className:"flex flex-col h-full bg-bg-dark rounded-md",children:[f.jsx(p0,{status:U,autoScroll:Z,onToggleAutoScroll:J}),f.jsx("div",{"data-testid":"log-content",ref:m,onScroll:G,className:"flex-1 min-h-0 font-mono overflow-auto",children:gt?f.jsx(y0,{}):f.jsx(v0,{virtualizer:k,virtualItems:St,lines:C})})]})}const T0=1e3,j0=1e3;function Jc(){return f.jsx(ne,{className:"animate-pulse","data-testid":"session-card-skeleton",children:f.jsxs(ye,{className:"pb-2",children:[f.jsxs("div",{className:"flex items-center gap-3",children:[f.jsx("div",{className:"h-4 w-4 bg-bg-light rounded"}),f.jsx("div",{className:"h-5 w-48 bg-bg-light rounded"}),f.jsx("div",{className:"h-5 w-16 bg-bg-light rounded-full ml-auto"})]}),f.jsxs("div",{className:"flex items-center gap-4 mt-2",children:[f.jsx("div",{className:"h-4 w-32 bg-bg-light rounded"}),f.jsx("div",{className:"h-4 w-20 bg-bg-light rounded"})]})]})})}function A0(){return f.jsxs("div",{"data-testid":"sessions-panel-skeleton",className:"space-y-4",children:[f.jsx(Jc,{}),f.jsx(Jc,{}),f.jsx(Jc,{})]})}function Jm({status:u}){return u==="running"?f.jsxs(wl,{className:"bg-primary/20 text-primary flex items-center gap-1",children:[f.jsx(yv,{className:"w-3 h-3"}),"Running"]}):f.jsxs(wl,{className:"bg-success/20 text-success flex items-center gap-1",children:[f.jsx(bu,{className:"w-3 h-3"}),"Completed"]})}function Wm(u){return new Date(u).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function Wc(u,r){const c=new Date(u).getTime(),o=r?new Date(r).getTime():Date.now();return Math.floor((o-c)/T0)}function O0(u,r,c){const[o,m]=_.useState(()=>Wc(u,r));return _.useEffect(()=>{if(!c){m(Wc(u,r));return}const g=setInterval(()=>{m(Wc(u,r))},j0);return()=>clearInterval(g)},[u,r,c]),o}function M0({session:u,defaultExpanded:r=!1}){const[c,o]=_.useState(r),m=u.status==="running",g=O0(u.startTime,u.endTime,m);return u.outputAvailable?f.jsx(ho,{open:c,onOpenChange:o,children:f.jsxs(ne,{"data-testid":"session-detail-card",children:[f.jsx(mo,{asChild:!0,children:f.jsxs(ye,{className:"cursor-pointer hover:bg-bg-light/50 transition-colors py-3",children:[f.jsxs("div",{className:"flex items-center gap-3",children:[c?f.jsx(so,{className:"w-4 h-4 text-text-muted"}):f.jsx(co,{className:"w-4 h-4 text-text-muted"}),f.jsx(Pc,{className:"w-4 h-4 text-text-muted"}),f.jsx("span",{className:"font-medium text-text truncate",children:u.name}),f.jsx(Jm,{status:u.status})]}),f.jsxs("div",{className:"flex items-center gap-4 mt-2 text-sm text-text-muted pl-8",children:[f.jsxs("span",{children:["Started at ",Wm(u.startTime)]}),f.jsx("span",{children:eo(g)})]})]})}),f.jsx(fo,{children:f.jsx(Ae,{className:"pt-0 pb-4",children:f.jsx("div",{className:"h-64",children:f.jsx(z0,{sessionName:u.name,status:u.status,outputAvailable:u.outputAvailable})})})})]})}):f.jsx(ne,{"data-testid":"session-detail-card",className:"opacity-60",children:f.jsxs(ye,{className:"py-3",children:[f.jsxs("div",{className:"flex items-center gap-3",children:[f.jsx(Pc,{className:"w-4 h-4 text-text-muted"}),f.jsx("span",{className:"font-medium text-text truncate",children:u.name}),f.jsx(Jm,{status:u.status})]}),f.jsxs("div",{className:"flex items-center gap-4 mt-2 text-sm text-text-muted",children:[f.jsxs("span",{children:["Started at ",Wm(u.startTime)]}),f.jsx("span",{children:eo(g)})]}),f.jsx("div",{className:"mt-2 text-sm text-text-muted italic",children:"Output unavailable"})]})})}function C0(){return f.jsxs("div",{"data-testid":"sessions-panel-empty",className:"text-center py-8",children:[f.jsx(Pc,{className:"w-8 h-8 text-text-muted mx-auto mb-2"}),f.jsx("p",{className:"text-text-muted",children:"No sessions found for this story"})]})}function D0({onRetry:u}){return f.jsxs("div",{"data-testid":"sessions-panel-error",className:"text-center py-8",children:[f.jsx(vu,{className:"w-8 h-8 text-danger mx-auto mb-2"}),f.jsx("p",{className:"text-danger font-medium mb-1",children:"Failed to load sessions"}),f.jsx("p",{className:"text-text-muted text-sm mb-4",children:"Something went wrong while fetching sessions."}),f.jsxs(Eh,{variant:"outline",size:"sm",onClick:u,children:[f.jsx(vv,{className:"w-4 h-4 mr-2"}),"Retry"]})]})}function _0(u){if(u.length===0)return null;const r=u.filter(c=>c.status==="running");return r.length>0?r[0].name:u[0].name}function U0({epicSlug:u,storySlug:r}){const{sessions:c}=ua(),[o,m]=_.useState([]),[g,v]=_.useState(!0),[E,j]=_.useState(!1),M=_.useRef(void 0),R=_.useMemo(()=>c.filter(G=>G.epicSlug===u&&G.storySlug===r),[c,u,r]),C=R.length>0?R:o,U=_.useMemo(()=>[...C].sort((G,J)=>new Date(J.startTime).getTime()-new Date(G.startTime).getTime()),[C]);M.current===void 0&&U.length>0&&(M.current=_0(U));const k=M.current??null,Z=_.useCallback(async()=>{v(!0),j(!1);try{const G=await fetch(`/api/sessions?epicSlug=${u}&storySlug=${r}`);if(G.ok){const J=await G.json();m(J)}else j(!0),m([])}catch{j(!0),m([])}finally{v(!1)}},[u,r]);return _.useEffect(()=>{Z()},[Z]),g?f.jsx(A0,{}):E?f.jsx("div",{"data-testid":"sessions-panel",children:f.jsx(D0,{onRetry:Z})}):U.length===0?f.jsx("div",{"data-testid":"sessions-panel",children:f.jsx(C0,{})}):f.jsx("div",{"data-testid":"sessions-panel",className:"space-y-4",children:U.map(G=>f.jsx(M0,{session:G,defaultExpanded:G.name===k},G.name))})}const Th=_.forwardRef(({...u},r)=>f.jsx(mp,{ref:r,...u}));Th.displayName="Tabs";const jh=_.forwardRef(({className:u,...r},c)=>f.jsx(hp,{ref:c,className:Ht("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",u),...r}));jh.displayName="TabsList";const Zn=_.forwardRef(({className:u,...r},c)=>f.jsx(gp,{ref:c,className:Ht("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs",u),...r}));Zn.displayName="TabsTrigger";const kn=_.forwardRef(({className:u,...r},c)=>f.jsx(yp,{ref:c,className:Ht("mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",u),...r}));kn.displayName="TabsContent";const R0=404,w0=["tasks","content","journal","sessions"];function B0(u){const r=u.get("tab");return r&&w0.includes(r)?r:"tasks"}function H0(){return f.jsxs("div",{className:"animate-pulse space-y-4","data-testid":"story-header-skeleton",children:[f.jsx("div",{className:"h-8 w-64 bg-bg-light rounded"}),f.jsxs("div",{className:"flex items-center gap-4",children:[f.jsx("div",{className:"h-6 w-24 bg-bg-light rounded-full"}),f.jsx("div",{className:"h-4 w-32 bg-bg-light rounded"})]})]})}function q0(){return f.jsxs("div",{className:"animate-pulse space-y-4","data-testid":"story-content-skeleton",children:[f.jsx("div",{className:"h-6 w-48 bg-bg-light rounded"}),f.jsxs("div",{className:"space-y-2",children:[f.jsx("div",{className:"h-4 w-full bg-bg-light rounded"}),f.jsx("div",{className:"h-4 w-3/4 bg-bg-light rounded"}),f.jsx("div",{className:"h-4 w-5/6 bg-bg-light rounded"})]})]})}function Y0({status:u}){const r={ready:"bg-text-muted/20 text-text-muted",inProgress:"bg-primary/20 text-primary",blocked:"bg-danger/20 text-danger",completed:"bg-success/20 text-success"},c={ready:"Ready",inProgress:"In Progress",blocked:"Blocked",completed:"Completed"};return f.jsx(wl,{className:r[u],children:c[u]})}function G0({status:u}){const r={className:"w-5 h-5 pointer-events-none cursor-default"};switch(u){case"completed":return f.jsx(bu,{...r,className:`${r.className} text-success`,"data-testid":"icon-check-circle"});case"inProgress":return f.jsx(Ic,{...r,className:`${r.className} text-primary fill-primary/20`,"data-testid":"icon-circle-in-progress"});default:return f.jsx(Ic,{...r,className:`${r.className} text-text-muted`,"data-testid":"icon-circle-pending"})}}const $m={pending:"bg-text-muted/20 text-text-muted",inProgress:"bg-primary/20 text-primary",completed:"bg-success/20 text-success"},L0={pending:"pending",inProgress:"in progress",completed:"completed"};function X0(u){return $m[u]||$m.pending}function V0({task:u}){const r=X0(u.status);return f.jsxs("div",{className:"flex items-center gap-3 py-2 px-3 rounded-md hover:bg-bg-light/50",children:[f.jsx(G0,{status:u.status}),f.jsx("span",{className:u.status==="completed"?"text-text-muted line-through":"text-text",children:u.title}),f.jsx(wl,{className:`ml-auto text-xs ${r}`,children:L0[u.status]})]})}function Q0(u){switch(u){case"blocker":return{bg:"bg-danger/10",text:"text-danger",border:"border-danger/30"};case"resolution":return{bg:"bg-success/10",text:"text-success",border:"border-success/30"};default:return{bg:"bg-bg-light",text:"text-text",border:"border-border-muted"}}}function Z0({entry:u,defaultOpen:r=!1}){const[c,o]=_.useState(r),m=Q0(u.type);return f.jsx(ho,{open:c,onOpenChange:o,children:f.jsxs(ne,{className:`${m.bg} border ${m.border}`,children:[f.jsx(mo,{asChild:!0,children:f.jsx(ye,{className:"cursor-pointer hover:bg-bg-light/50 transition-colors py-3",children:f.jsxs("div",{className:"flex items-center gap-3",children:[c?f.jsx(so,{className:"w-4 h-4 text-text-muted","data-testid":"icon-chevron-down"}):f.jsx(co,{className:"w-4 h-4 text-text-muted","data-testid":"icon-chevron-right"}),f.jsx(wl,{className:`${m.bg} ${m.text} border ${m.border}`,children:u.type}),f.jsx(sa,{className:`text-sm font-medium ${m.text}`,children:u.title}),u.type==="blocker"&&f.jsx(vu,{className:"w-4 h-4 text-danger ml-auto","data-testid":"icon-alert-circle"}),u.timestamp&&f.jsx("span",{className:"text-xs text-text-muted ml-auto",children:u.timestamp})]})})}),f.jsx(fo,{children:f.jsx(Ae,{className:"pt-0 pb-4",children:f.jsx("div",{className:"prose prose-sm prose-invert max-w-none",children:f.jsx("pre",{className:"whitespace-pre-wrap text-sm text-text-muted font-mono bg-bg-dark p-3 rounded-md overflow-x-auto",children:u.content})})})})]})})}function k0(u){return u?{completed:u.tasks.filter(r=>r.status==="completed").length,total:u.tasks.length}:{completed:0,total:0}}function Ah(u){return{blockers:u.filter(r=>r.type==="blocker"),sessions:u.filter(r=>r.type==="session"),resolutions:u.filter(r=>r.type==="resolution")}}function K0({epicSlug:u,storySlug:r}){return f.jsxs("div",{className:"text-center py-12",children:[f.jsx("h1",{className:"text-2xl font-bold text-text mb-2",children:"Story not found"}),f.jsxs("p",{className:"text-text-muted mb-4",children:['The story "',r,'" does not exist in epic "',u,'".']}),f.jsx(il,{to:`/epic/${u}`,className:"text-primary hover:underline",children:"← Back to epic"})]})}function J0({epicSlug:u,error:r}){return f.jsxs("div",{className:"text-center py-12",children:[f.jsx("h1",{className:"text-2xl font-bold text-danger mb-2",children:"Error"}),f.jsx("p",{className:"text-text-muted mb-4",children:r}),f.jsx(il,{to:`/epic/${u}`,className:"text-primary hover:underline",children:"← Back to epic"})]})}function W0(){return f.jsxs("div",{className:"space-y-6",children:[f.jsx(H0,{}),f.jsx(q0,{})]})}function $0({story:u,epicSlug:r,storySlug:c}){const o=k0(u);return f.jsxs("div",{className:"space-y-4",children:[f.jsxs("div",{className:"flex items-center gap-2 text-sm text-text-muted",children:[f.jsx(il,{to:`/epic/${r}`,className:"hover:text-primary",children:r}),f.jsx("span",{children:"/"}),f.jsx("span",{className:"text-text",children:c})]}),f.jsx("h1",{className:"text-2xl font-bold text-text",children:u.title}),f.jsxs("div",{className:"flex items-center gap-4",children:[f.jsx(Y0,{status:u.status}),f.jsxs("span",{className:"text-sm text-text-muted",children:[o.completed,"/",o.total," tasks completed"]})]})]})}function F0({tasks:u}){return f.jsxs(ne,{children:[f.jsx(ye,{children:f.jsx(sa,{className:"text-lg",children:"Tasks"})}),f.jsx(Ae,{children:u.length===0?f.jsx("p",{className:"text-text-muted text-center py-4",children:"No tasks defined for this story."}):f.jsx("div",{className:"divide-y divide-border-muted",children:u.map(r=>f.jsx(V0,{task:r},r.id))})})]})}function I0({content:u}){return f.jsxs(ne,{children:[f.jsx(ye,{children:f.jsx(sa,{className:"text-lg",children:"Story Content"})}),f.jsx(Ae,{children:u?f.jsx("div",{className:"prose prose-sm prose-invert max-w-none prose-headings:text-text prose-p:text-text-muted prose-strong:text-text prose-code:text-primary prose-code:bg-bg-dark prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-code:before:content-none prose-code:after:content-none prose-pre:bg-bg-dark prose-pre:border prose-pre:border-border-muted prose-a:text-primary prose-a:no-underline prose-a:hover:underline prose-li:text-text-muted prose-table:border prose-table:border-border-muted prose-th:bg-bg-dark prose-th:px-3 prose-th:py-2 prose-th:text-text prose-td:px-3 prose-td:py-2 prose-td:border-t prose-td:border-border-muted",children:f.jsx(Fm,{remarkPlugins:[Im],children:u})}):f.jsx("p",{className:"text-text-muted text-center py-4",children:"No story content available."})})]})}function P0(u){return u==="Blockers"?"text-danger":u==="Resolutions"?"text-success":"text-text"}function $c({entries:u,type:r,icon:c}){if(u.length===0)return null;const o=P0(r);return f.jsxs("div",{className:"space-y-3",children:[f.jsxs("h3",{className:`text-sm font-semibold ${o} flex items-center gap-2`,children:[f.jsx(c,{className:"w-4 h-4"}),r," (",u.length,")"]}),u.map(m=>f.jsx(Z0,{entry:m,defaultOpen:r==="Blockers"},`${r.toLowerCase()}-${m.title}-${m.timestamp??""}`))]})}function tx({journal:u}){if(u.length===0)return f.jsx(ne,{children:f.jsx(Ae,{className:"py-8",children:f.jsx("p",{className:"text-text-muted text-center",children:"No journal entries yet."})})});const{blockers:r,sessions:c,resolutions:o}=Ah(u);return f.jsxs("div",{className:"space-y-4",children:[f.jsx($c,{entries:r,type:"Blockers",icon:vu}),f.jsx($c,{entries:o,type:"Resolutions",icon:bu}),f.jsx($c,{entries:c,type:"Sessions",icon:Ic})]})}async function ex(u){return u.status===R0?{type:"notFound"}:u.ok?{type:"success",data:await u.json()}:{type:"error"}}function lx(u,r,c){c("Failed to load story"),ro(u,r instanceof Error?r.message:"Unknown error")}function ax(u,r){const{currentStory:c,setCurrentStory:o,clearCurrentStory:m,subscribeToStory:g,unsubscribeFromStory:v,isConnected:E}=ua(),[j,M]=_.useState(!0),[R,C]=_.useState(!1),[U,k]=_.useState(null),Z=!!(u&&r);return _.useEffect(()=>(Z&&(async()=>{M(!0),C(!1),k(null);try{const J=await fetch(`/api/stories/${u}/${r}`),gt=await ex(J);gt.type==="notFound"?C(!0):gt.type==="error"?k("Failed to load story"):o(gt.data)}catch(J){lx(`/api/stories/${u}/${r}`,J,k)}finally{M(!1)}})(),m),[u,r,Z,o,m]),_.useEffect(()=>{if(Z&&E)return g(u,r),()=>{v(u,r)}},[u,r,Z,E,g,v]),{currentStory:c,loading:j,notFound:R,error:U}}function nx(){const{epicSlug:u,storySlug:r}=lo(),[c]=Np(),{currentStory:o,loading:m,notFound:g,error:v}=ax(u,r),E=B0(c);if(g)return f.jsx(K0,{epicSlug:u??"",storySlug:r??""});if(v&&!m)return f.jsx(J0,{epicSlug:u??"",error:v});if(m||!o)return f.jsx(W0,{});const{blockers:j}=Ah(o.journal);return f.jsxs("div",{className:"space-y-6",children:[f.jsx($0,{story:o,epicSlug:u??"",storySlug:r??""}),f.jsxs(Th,{defaultValue:E,className:"w-full",children:[f.jsxs(jh,{className:"mb-4",children:[f.jsx(Zn,{value:"tasks",children:"Tasks"}),f.jsx(Zn,{value:"content",children:"Story Content"}),f.jsxs(Zn,{value:"journal",children:["Journal",j.length>0&&f.jsx(wl,{className:"ml-2 bg-danger/20 text-danger text-xs",children:j.length})]}),f.jsx(Zn,{value:"sessions",children:"Sessions"})]}),f.jsx(kn,{value:"tasks",className:"space-y-4",children:f.jsx(F0,{tasks:o.tasks})}),f.jsx(kn,{value:"content",className:"space-y-4",children:f.jsx(I0,{content:o.content})}),f.jsx(kn,{value:"journal",className:"space-y-4",children:f.jsx(tx,{journal:o.journal})}),f.jsx(kn,{value:"sessions",className:"space-y-4",children:f.jsx(U0,{epicSlug:u??"",storySlug:r??""})})]})]})}const ix=[{path:"/",element:f.jsx(Nb,{}),children:[{path:"/",element:f.jsx(Pb,{})},{path:"epic/:slug",element:f.jsx(Qb,{})},{path:"epic/:epicSlug/story/:storySlug",element:f.jsx(nx,{})}]}];function ux(){return f.jsx(zp,{children:f.jsx(Tp,{children:ix.map(u=>f.jsx(bm,{path:u.path,element:u.element,children:u.children?.map(r=>f.jsx(bm,{path:r.path,element:r.element},r.path))},u.path))})})}const Oh=document.getElementById("root");if(!Oh)throw new Error("Root element not found");Cp.createRoot(Oh).render(f.jsx(_.StrictMode,{children:f.jsx(Zp,{children:f.jsx(ux,{})})}));
14
+ color: hsl(${Math.max(0,Math.min(120-120*J,120))}deg 100% 31%);`,c?.key)}return c?.onChange&&!(g&&c.skipInitialOnChange)&&c.onChange(m),g=!1,m}return v.updateDeps=E=>{o=E},v}function Vm(u,r){if(u===void 0)throw new Error("Unexpected undefined");return u}const e0=(u,r)=>Math.abs(u-r)<1.01,l0=(u,r,c)=>{let o;return function(...m){u.clearTimeout(o),o=u.setTimeout(()=>r.apply(this,m),c)}},Qm=u=>{const{offsetWidth:r,offsetHeight:c}=u;return{width:r,height:c}},a0=u=>u,n0=u=>{const r=Math.max(u.startIndex-u.overscan,0),c=Math.min(u.endIndex+u.overscan,u.count-1),o=[];for(let m=r;m<=c;m++)o.push(m);return o},i0=(u,r)=>{const c=u.scrollElement;if(!c)return;const o=u.targetWindow;if(!o)return;const m=v=>{const{width:E,height:j}=v;r({width:Math.round(E),height:Math.round(j)})};if(m(Qm(c)),!o.ResizeObserver)return()=>{};const g=new o.ResizeObserver(v=>{const E=()=>{const j=v[0];if(j?.borderBoxSize){const M=j.borderBoxSize[0];if(M){m({width:M.inlineSize,height:M.blockSize});return}}m(Qm(c))};u.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(E):E()});return g.observe(c,{box:"border-box"}),()=>{g.unobserve(c)}},Zm={passive:!0},km=typeof window>"u"?!0:"onscrollend"in window,u0=(u,r)=>{const c=u.scrollElement;if(!c)return;const o=u.targetWindow;if(!o)return;let m=0;const g=u.options.useScrollendEvent&&km?()=>{}:l0(o,()=>{r(m,!1)},u.options.isScrollingResetDelay),v=R=>()=>{const{horizontal:C,isRtl:U}=u.options;m=C?c.scrollLeft*(U&&-1||1):c.scrollTop,g(),r(m,R)},E=v(!0),j=v(!1);c.addEventListener("scroll",E,Zm);const M=u.options.useScrollendEvent&&km;return M&&c.addEventListener("scrollend",j,Zm),()=>{c.removeEventListener("scroll",E),M&&c.removeEventListener("scrollend",j)}},s0=(u,r,c)=>{if(r?.borderBoxSize){const o=r.borderBoxSize[0];if(o)return Math.round(o[c.options.horizontal?"inlineSize":"blockSize"])}return u[c.options.horizontal?"offsetWidth":"offsetHeight"]},c0=(u,{adjustments:r=0,behavior:c},o)=>{var m,g;const v=u+r;(g=(m=o.scrollElement)==null?void 0:m.scrollTo)==null||g.call(m,{[o.options.horizontal?"left":"top"]:v,behavior:c})};class o0{constructor(r){this.unsubs=[],this.scrollElement=null,this.targetWindow=null,this.isScrolling=!1,this.currentScrollToIndex=null,this.measurementsCache=[],this.itemSizeCache=new Map,this.laneAssignments=new Map,this.pendingMeasuredCacheIndexes=[],this.prevLanes=void 0,this.lanesChangedFlag=!1,this.lanesSettling=!1,this.scrollRect=null,this.scrollOffset=null,this.scrollDirection=null,this.scrollAdjustments=0,this.elementsCache=new Map,this.observer=(()=>{let c=null;const o=()=>c||(!this.targetWindow||!this.targetWindow.ResizeObserver?null:c=new this.targetWindow.ResizeObserver(m=>{m.forEach(g=>{const v=()=>{this._measureElement(g.target,g)};this.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(v):v()})}));return{disconnect:()=>{var m;(m=o())==null||m.disconnect(),c=null},observe:m=>{var g;return(g=o())==null?void 0:g.observe(m,{box:"border-box"})},unobserve:m=>{var g;return(g=o())==null?void 0:g.unobserve(m)}}})(),this.range=null,this.setOptions=c=>{Object.entries(c).forEach(([o,m])=>{typeof m>"u"&&delete c[o]}),this.options={debug:!1,initialOffset:0,overscan:1,paddingStart:0,paddingEnd:0,scrollPaddingStart:0,scrollPaddingEnd:0,horizontal:!1,getItemKey:a0,rangeExtractor:n0,onChange:()=>{},measureElement:s0,initialRect:{width:0,height:0},scrollMargin:0,gap:0,indexAttribute:"data-index",initialMeasurementsCache:[],lanes:1,isScrollingResetDelay:150,enabled:!0,isRtl:!1,useScrollendEvent:!1,useAnimationFrameWithResizeObserver:!1,...c}},this.notify=c=>{var o,m;(m=(o=this.options).onChange)==null||m.call(o,this,c)},this.maybeNotify=Ka(()=>(this.calculateRange(),[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]),c=>{this.notify(c)},{key:!1,debug:()=>this.options.debug,initialDeps:[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]}),this.cleanup=()=>{this.unsubs.filter(Boolean).forEach(c=>c()),this.unsubs=[],this.observer.disconnect(),this.scrollElement=null,this.targetWindow=null},this._didMount=()=>()=>{this.cleanup()},this._willUpdate=()=>{var c;const o=this.options.enabled?this.options.getScrollElement():null;if(this.scrollElement!==o){if(this.cleanup(),!o){this.maybeNotify();return}this.scrollElement=o,this.scrollElement&&"ownerDocument"in this.scrollElement?this.targetWindow=this.scrollElement.ownerDocument.defaultView:this.targetWindow=((c=this.scrollElement)==null?void 0:c.window)??null,this.elementsCache.forEach(m=>{this.observer.observe(m)}),this.unsubs.push(this.options.observeElementRect(this,m=>{this.scrollRect=m,this.maybeNotify()})),this.unsubs.push(this.options.observeElementOffset(this,(m,g)=>{this.scrollAdjustments=0,this.scrollDirection=g?this.getScrollOffset()<m?"forward":"backward":null,this.scrollOffset=m,this.isScrolling=g,this.maybeNotify()})),this._scrollToOffset(this.getScrollOffset(),{adjustments:void 0,behavior:void 0})}},this.getSize=()=>this.options.enabled?(this.scrollRect=this.scrollRect??this.options.initialRect,this.scrollRect[this.options.horizontal?"width":"height"]):(this.scrollRect=null,0),this.getScrollOffset=()=>this.options.enabled?(this.scrollOffset=this.scrollOffset??(typeof this.options.initialOffset=="function"?this.options.initialOffset():this.options.initialOffset),this.scrollOffset):(this.scrollOffset=null,0),this.getFurthestMeasurement=(c,o)=>{const m=new Map,g=new Map;for(let v=o-1;v>=0;v--){const E=c[v];if(m.has(E.lane))continue;const j=g.get(E.lane);if(j==null||E.end>j.end?g.set(E.lane,E):E.end<j.end&&m.set(E.lane,!0),m.size===this.options.lanes)break}return g.size===this.options.lanes?Array.from(g.values()).sort((v,E)=>v.end===E.end?v.index-E.index:v.end-E.end)[0]:void 0},this.getMeasurementOptions=Ka(()=>[this.options.count,this.options.paddingStart,this.options.scrollMargin,this.options.getItemKey,this.options.enabled,this.options.lanes],(c,o,m,g,v,E)=>(this.prevLanes!==void 0&&this.prevLanes!==E&&(this.lanesChangedFlag=!0),this.prevLanes=E,this.pendingMeasuredCacheIndexes=[],{count:c,paddingStart:o,scrollMargin:m,getItemKey:g,enabled:v,lanes:E}),{key:!1}),this.getMeasurements=Ka(()=>[this.getMeasurementOptions(),this.itemSizeCache],({count:c,paddingStart:o,scrollMargin:m,getItemKey:g,enabled:v,lanes:E},j)=>{if(!v)return this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),[];if(this.laneAssignments.size>c)for(const U of this.laneAssignments.keys())U>=c&&this.laneAssignments.delete(U);this.lanesChangedFlag&&(this.lanesChangedFlag=!1,this.lanesSettling=!0,this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),this.pendingMeasuredCacheIndexes=[]),this.measurementsCache.length===0&&!this.lanesSettling&&(this.measurementsCache=this.options.initialMeasurementsCache,this.measurementsCache.forEach(U=>{this.itemSizeCache.set(U.key,U.size)}));const M=this.lanesSettling?0:this.pendingMeasuredCacheIndexes.length>0?Math.min(...this.pendingMeasuredCacheIndexes):0;this.pendingMeasuredCacheIndexes=[],this.lanesSettling&&this.measurementsCache.length===c&&(this.lanesSettling=!1);const R=this.measurementsCache.slice(0,M),C=new Array(E).fill(void 0);for(let U=0;U<M;U++){const k=R[U];k&&(C[k.lane]=U)}for(let U=M;U<c;U++){const k=g(U),Z=this.laneAssignments.get(U);let G,J;if(Z!==void 0&&this.options.lanes>1){G=Z;const bt=C[G],qt=bt!==void 0?R[bt]:void 0;J=qt?qt.end+this.options.gap:o+m}else{const bt=this.options.lanes===1?R[U-1]:this.getFurthestMeasurement(R,U);J=bt?bt.end+this.options.gap:o+m,G=bt?bt.lane:U%this.options.lanes,this.options.lanes>1&&this.laneAssignments.set(U,G)}const gt=j.get(k),St=typeof gt=="number"?gt:this.options.estimateSize(U),Et=J+St;R[U]={index:U,start:J,size:St,end:Et,key:k,lane:G},C[G]=U}return this.measurementsCache=R,R},{key:!1,debug:()=>this.options.debug}),this.calculateRange=Ka(()=>[this.getMeasurements(),this.getSize(),this.getScrollOffset(),this.options.lanes],(c,o,m,g)=>this.range=c.length>0&&o>0?r0({measurements:c,outerSize:o,scrollOffset:m,lanes:g}):null,{key:!1,debug:()=>this.options.debug}),this.getVirtualIndexes=Ka(()=>{let c=null,o=null;const m=this.calculateRange();return m&&(c=m.startIndex,o=m.endIndex),this.maybeNotify.updateDeps([this.isScrolling,c,o]),[this.options.rangeExtractor,this.options.overscan,this.options.count,c,o]},(c,o,m,g,v)=>g===null||v===null?[]:c({startIndex:g,endIndex:v,overscan:o,count:m}),{key:!1,debug:()=>this.options.debug}),this.indexFromElement=c=>{const o=this.options.indexAttribute,m=c.getAttribute(o);return m?parseInt(m,10):(console.warn(`Missing attribute name '${o}={index}' on measured element.`),-1)},this._measureElement=(c,o)=>{const m=this.indexFromElement(c),g=this.measurementsCache[m];if(!g)return;const v=g.key,E=this.elementsCache.get(v);E!==c&&(E&&this.observer.unobserve(E),this.observer.observe(c),this.elementsCache.set(v,c)),c.isConnected&&this.resizeItem(m,this.options.measureElement(c,o,this))},this.resizeItem=(c,o)=>{const m=this.measurementsCache[c];if(!m)return;const g=this.itemSizeCache.get(m.key)??m.size,v=o-g;v!==0&&((this.shouldAdjustScrollPositionOnItemSizeChange!==void 0?this.shouldAdjustScrollPositionOnItemSizeChange(m,v,this):m.start<this.getScrollOffset()+this.scrollAdjustments)&&this._scrollToOffset(this.getScrollOffset(),{adjustments:this.scrollAdjustments+=v,behavior:void 0}),this.pendingMeasuredCacheIndexes.push(m.index),this.itemSizeCache=new Map(this.itemSizeCache.set(m.key,o)),this.notify(!1))},this.measureElement=c=>{if(!c){this.elementsCache.forEach((o,m)=>{o.isConnected||(this.observer.unobserve(o),this.elementsCache.delete(m))});return}this._measureElement(c,void 0)},this.getVirtualItems=Ka(()=>[this.getVirtualIndexes(),this.getMeasurements()],(c,o)=>{const m=[];for(let g=0,v=c.length;g<v;g++){const E=c[g],j=o[E];m.push(j)}return m},{key:!1,debug:()=>this.options.debug}),this.getVirtualItemForOffset=c=>{const o=this.getMeasurements();if(o.length!==0)return Vm(o[Nh(0,o.length-1,m=>Vm(o[m]).start,c)])},this.getMaxScrollOffset=()=>{if(!this.scrollElement)return 0;if("scrollHeight"in this.scrollElement)return this.options.horizontal?this.scrollElement.scrollWidth-this.scrollElement.clientWidth:this.scrollElement.scrollHeight-this.scrollElement.clientHeight;{const c=this.scrollElement.document.documentElement;return this.options.horizontal?c.scrollWidth-this.scrollElement.innerWidth:c.scrollHeight-this.scrollElement.innerHeight}},this.getOffsetForAlignment=(c,o,m=0)=>{if(!this.scrollElement)return 0;const g=this.getSize(),v=this.getScrollOffset();o==="auto"&&(o=c>=v+g?"end":"start"),o==="center"?c+=(m-g)/2:o==="end"&&(c-=g);const E=this.getMaxScrollOffset();return Math.max(Math.min(E,c),0)},this.getOffsetForIndex=(c,o="auto")=>{c=Math.max(0,Math.min(c,this.options.count-1));const m=this.measurementsCache[c];if(!m)return;const g=this.getSize(),v=this.getScrollOffset();if(o==="auto")if(m.end>=v+g-this.options.scrollPaddingEnd)o="end";else if(m.start<=v+this.options.scrollPaddingStart)o="start";else return[v,o];if(o==="end"&&c===this.options.count-1)return[this.getMaxScrollOffset(),o];const E=o==="end"?m.end+this.options.scrollPaddingEnd:m.start-this.options.scrollPaddingStart;return[this.getOffsetForAlignment(E,o,m.size),o]},this.isDynamicMode=()=>this.elementsCache.size>0,this.scrollToOffset=(c,{align:o="start",behavior:m}={})=>{m==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),this._scrollToOffset(this.getOffsetForAlignment(c,o),{adjustments:void 0,behavior:m})},this.scrollToIndex=(c,{align:o="auto",behavior:m}={})=>{m==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),c=Math.max(0,Math.min(c,this.options.count-1)),this.currentScrollToIndex=c;let g=0;const v=10,E=M=>{if(!this.targetWindow)return;const R=this.getOffsetForIndex(c,M);if(!R){console.warn("Failed to get offset for index:",c);return}const[C,U]=R;this._scrollToOffset(C,{adjustments:void 0,behavior:m}),this.targetWindow.requestAnimationFrame(()=>{const k=()=>{if(this.currentScrollToIndex!==c)return;const Z=this.getScrollOffset(),G=this.getOffsetForIndex(c,U);if(!G){console.warn("Failed to get offset for index:",c);return}e0(G[0],Z)||j(U)};this.isDynamicMode()?this.targetWindow.requestAnimationFrame(k):k()})},j=M=>{this.targetWindow&&this.currentScrollToIndex===c&&(g++,g<v?this.targetWindow.requestAnimationFrame(()=>E(M)):console.warn(`Failed to scroll to index ${c} after ${v} attempts.`))};E(o)},this.scrollBy=(c,{behavior:o}={})=>{o==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),this._scrollToOffset(this.getScrollOffset()+c,{adjustments:void 0,behavior:o})},this.getTotalSize=()=>{var c;const o=this.getMeasurements();let m;if(o.length===0)m=this.options.paddingStart;else if(this.options.lanes===1)m=((c=o[o.length-1])==null?void 0:c.end)??0;else{const g=Array(this.options.lanes).fill(null);let v=o.length-1;for(;v>=0&&g.some(E=>E===null);){const E=o[v];g[E.lane]===null&&(g[E.lane]=E.end),v--}m=Math.max(...g.filter(E=>E!==null))}return Math.max(m-this.options.scrollMargin+this.options.paddingEnd,0)},this._scrollToOffset=(c,{adjustments:o,behavior:m})=>{this.options.scrollToFn(c,{behavior:m,adjustments:o},this)},this.measure=()=>{this.itemSizeCache=new Map,this.laneAssignments=new Map,this.notify(!1)},this.setOptions(r)}}const Nh=(u,r,c,o)=>{for(;u<=r;){const m=(u+r)/2|0,g=c(m);if(g<o)u=m+1;else if(g>o)r=m-1;else return m}return u>0?u-1:0};function r0({measurements:u,outerSize:r,scrollOffset:c,lanes:o}){const m=u.length-1,g=j=>u[j].start;if(u.length<=o)return{startIndex:0,endIndex:m};let v=Nh(0,m,g,c),E=v;if(o===1)for(;E<m&&u[E].end<c+r;)E++;else if(o>1){const j=Array(o).fill(0);for(;E<m&&j.some(R=>R<c+r);){const R=u[E];j[R.lane]=R.end,E++}const M=Array(o).fill(c+r);for(;v>=0&&M.some(R=>R>=c);){const R=u[v];M[R.lane]=R.start,v--}v=Math.max(0,v-v%o),E=Math.min(m,E+(o-1-E%o))}return{startIndex:v,endIndex:E}}const Km=typeof document<"u"?_.useLayoutEffect:_.useEffect;function f0({useFlushSync:u=!0,...r}){const c=_.useReducer(()=>({}),{})[1],o={...r,onChange:(g,v)=>{var E;u&&v?dp.flushSync(c):c(),(E=r.onChange)==null||E.call(r,g,v)}},[m]=_.useState(()=>new o0(o));return m.setOptions(o),Km(()=>m._didMount(),[]),Km(()=>m._willUpdate()),m}function d0(u){return f0({observeElementRect:i0,observeElementOffset:u0,scrollToFn:c0,...u})}const zh=24,m0=5,h0=50;function g0({status:u}){return u==="running"?f.jsxs("div",{"data-testid":"status-indicator-streaming",className:"flex items-center gap-1.5 text-primary text-sm",children:[f.jsx(rv,{className:"h-3.5 w-3.5 animate-spin","data-testid":"status-icon-loader"}),f.jsx("span",{children:"Streaming"})]}):f.jsxs("div",{"data-testid":"status-indicator-complete",className:"flex items-center gap-1.5 text-success text-sm",children:[f.jsx(bu,{className:"h-3.5 w-3.5","data-testid":"status-icon-complete"}),f.jsx("span",{children:"Complete"})]})}function y0(){return f.jsxs("div",{"data-testid":"log-viewer-skeleton",className:"p-4 space-y-2",children:[f.jsx("div",{className:"h-4 bg-bg-light rounded animate-pulse w-3/4"}),f.jsx("div",{className:"h-4 bg-bg-light rounded animate-pulse w-1/2"}),f.jsx("div",{className:"h-4 bg-bg-light rounded animate-pulse w-5/6"}),f.jsx("div",{className:"h-4 bg-bg-light rounded animate-pulse w-2/3"})]})}function p0({status:u,autoScroll:r,onToggleAutoScroll:c}){return f.jsxs("div",{className:"flex items-center justify-between px-4 py-2 border-b border-bg-light",children:[f.jsx(g0,{status:u}),f.jsxs("button",{type:"button","data-testid":"auto-scroll-toggle",onClick:c,"aria-pressed":r,title:r?"Autoscroll locked to bottom":"Autoscroll unlocked",className:`flex items-center gap-1.5 px-2 py-1 text-xs rounded-md transition-colors ${r?"bg-success/20 text-success hover:bg-success/30":"bg-bg-light text-text-muted hover:bg-bg-lighter"}`,children:[r?f.jsx(hv,{className:"h-3.5 w-3.5","data-testid":"autoscroll-icon-enabled"}):f.jsx(dv,{className:"h-3.5 w-3.5","data-testid":"autoscroll-icon-disabled"}),f.jsx("span",{children:"Autoscroll"})]})]})}function v0({virtualizer:u,virtualItems:r,lines:c}){return f.jsx("div",{className:"relative w-full",style:{height:`${u.getTotalSize()}px`},"data-testid":"log-height-container",children:f.jsx("div",{className:"absolute top-0 left-0 w-full px-4",style:{transform:`translateY(${r[0]?.start??0}px)`},children:r.map(o=>f.jsx("div",{"data-testid":"log-line","data-index":o.index,className:"text-text leading-relaxed",style:{height:`${zh}px`},children:c[o.index]||" "},o.key))})})}function b0(){return f.jsx("div",{"data-testid":"log-viewer",className:"h-full bg-bg-dark rounded-md font-mono flex items-center justify-center",children:f.jsx("span",{className:"text-text-muted",children:"Output unavailable"})})}function x0({error:u}){return f.jsxs("div",{"data-testid":"log-viewer",className:"h-full bg-bg-dark rounded-md font-mono flex flex-col items-center justify-center gap-2",children:[f.jsx(vu,{className:"h-6 w-6 text-danger"}),f.jsx("span",{className:"text-danger text-sm",children:"Failed to load logs"}),f.jsx("span",{className:"text-text-muted text-xs max-w-xs text-center",children:u})]})}function S0(u,r){const[c,o]=_.useState(""),[m,g]=_.useState(!0),[v,E]=_.useState(!1),[j,M]=_.useState(null);return _.useEffect(()=>{if(!r){g(!1);return}const R=jm();if(!R){g(!1);return}return wp(u,(C,U,k)=>{U?(o(C),g(!1)):o(Z=>Z+C),k&&E(!0)},C=>{M(C),g(!1)}),R({event:"subscribe:logs",data:{sessionName:u}}),()=>{Bp(u);const C=jm();C&&C({event:"unsubscribe:logs",data:{sessionName:u}})}},[u,r]),{content:c,isLoading:m,streamComplete:v,error:j}}function E0(u,r,c,o){const[m,g]=_.useState(c==="running"),v=_.useRef(0);_.useEffect(()=>{m&&o>v.current&&requestAnimationFrame(()=>{if(r.current&&u.current){const M=r.current.getTotalSize();u.current.scrollTop=M}}),v.current=o},[o,m,r,u]);const E=_.useCallback(()=>{const M=u.current;if(!M)return;!(M.scrollHeight-M.scrollTop-M.clientHeight<h0)&&m&&g(!1)},[m,u]),j=_.useCallback(()=>{g(M=>(M||requestAnimationFrame(()=>{if(r.current&&u.current){const R=r.current.getTotalSize();u.current.scrollTop=R}}),!M))},[r,u]);return{autoScroll:m,handleScroll:E,toggleAutoScroll:j}}function N0(u){return _.useMemo(()=>u?u.split(`
15
+ `):[],[u])}function z0({sessionName:u,status:r,outputAvailable:c,initialContent:o=""}){const m=_.useRef(null),g=_.useRef(null),{content:v,isLoading:E,streamComplete:j,error:M}=S0(u,c),C=N0(o||v),U=j?"completed":r,k=d0({count:C.length,getScrollElement:()=>m.current,estimateSize:()=>zh,overscan:m0});g.current=k;const{autoScroll:Z,handleScroll:G,toggleAutoScroll:J}=E0(m,g,U,C.length);if(!c)return f.jsx(b0,{});if(M)return f.jsx(x0,{error:M});const gt=E&&!o,St=k.getVirtualItems();return f.jsxs("div",{"data-testid":"log-viewer",className:"flex flex-col h-full bg-bg-dark rounded-md",children:[f.jsx(p0,{status:U,autoScroll:Z,onToggleAutoScroll:J}),f.jsx("div",{"data-testid":"log-content",ref:m,onScroll:G,className:"flex-1 min-h-0 font-mono overflow-auto",children:gt?f.jsx(y0,{}):f.jsx(v0,{virtualizer:k,virtualItems:St,lines:C})})]})}const T0=1e3,j0=1e3;function Jc(){return f.jsx(ne,{className:"animate-pulse","data-testid":"session-card-skeleton",children:f.jsxs(ye,{className:"pb-2",children:[f.jsxs("div",{className:"flex items-center gap-3",children:[f.jsx("div",{className:"h-4 w-4 bg-bg-light rounded"}),f.jsx("div",{className:"h-5 w-48 bg-bg-light rounded"}),f.jsx("div",{className:"h-5 w-16 bg-bg-light rounded-full ml-auto"})]}),f.jsxs("div",{className:"flex items-center gap-4 mt-2",children:[f.jsx("div",{className:"h-4 w-32 bg-bg-light rounded"}),f.jsx("div",{className:"h-4 w-20 bg-bg-light rounded"})]})]})})}function A0(){return f.jsxs("div",{"data-testid":"sessions-panel-skeleton",className:"space-y-4",children:[f.jsx(Jc,{}),f.jsx(Jc,{}),f.jsx(Jc,{})]})}function Jm({status:u}){return u==="running"?f.jsxs(wl,{className:"bg-primary/20 text-primary flex items-center gap-1",children:[f.jsx(yv,{className:"w-3 h-3"}),"Running"]}):f.jsxs(wl,{className:"bg-success/20 text-success flex items-center gap-1",children:[f.jsx(bu,{className:"w-3 h-3"}),"Completed"]})}function Wm(u){return new Date(u).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function Wc(u,r){const c=new Date(u).getTime(),o=r?new Date(r).getTime():Date.now();return Math.floor((o-c)/T0)}function O0(u,r,c){const[o,m]=_.useState(()=>Wc(u,r));return _.useEffect(()=>{if(!c){m(Wc(u,r));return}const g=setInterval(()=>{m(Wc(u,r))},j0);return()=>clearInterval(g)},[u,r,c]),o}function M0({session:u,defaultExpanded:r=!1}){const[c,o]=_.useState(r),m=u.status==="running",g=O0(u.startTime,u.endTime,m);return u.outputAvailable?f.jsx(ho,{open:c,onOpenChange:o,children:f.jsxs(ne,{"data-testid":"session-detail-card",children:[f.jsx(mo,{asChild:!0,children:f.jsxs(ye,{"data-testid":"session-card-trigger",className:"cursor-pointer hover:bg-bg-light/50 transition-colors py-3",children:[f.jsxs("div",{className:"flex items-center gap-3",children:[c?f.jsx(so,{className:"w-4 h-4 text-text-muted"}):f.jsx(co,{className:"w-4 h-4 text-text-muted"}),f.jsx(Pc,{className:"w-4 h-4 text-text-muted"}),f.jsx("span",{className:"font-medium text-text truncate",children:u.name}),f.jsx(Jm,{status:u.status})]}),f.jsxs("div",{className:"flex items-center gap-4 mt-2 text-sm text-text-muted pl-8",children:[f.jsxs("span",{children:["Started at ",Wm(u.startTime)]}),f.jsx("span",{children:eo(g)})]})]})}),f.jsx(fo,{children:f.jsx(Ae,{className:"pt-0 pb-4",children:f.jsx("div",{className:"h-64",children:f.jsx(z0,{sessionName:u.name,status:u.status,outputAvailable:u.outputAvailable})})})})]})}):f.jsx(ne,{"data-testid":"session-detail-card",className:"opacity-60",children:f.jsxs(ye,{className:"py-3",children:[f.jsxs("div",{className:"flex items-center gap-3",children:[f.jsx(Pc,{className:"w-4 h-4 text-text-muted"}),f.jsx("span",{className:"font-medium text-text truncate",children:u.name}),f.jsx(Jm,{status:u.status})]}),f.jsxs("div",{className:"flex items-center gap-4 mt-2 text-sm text-text-muted",children:[f.jsxs("span",{children:["Started at ",Wm(u.startTime)]}),f.jsx("span",{children:eo(g)})]}),f.jsx("div",{className:"mt-2 text-sm text-text-muted italic",children:"Output unavailable"})]})})}function C0(){return f.jsxs("div",{"data-testid":"sessions-panel-empty",className:"text-center py-8",children:[f.jsx(Pc,{className:"w-8 h-8 text-text-muted mx-auto mb-2"}),f.jsx("p",{className:"text-text-muted",children:"No sessions found for this story"})]})}function D0({onRetry:u}){return f.jsxs("div",{"data-testid":"sessions-panel-error",className:"text-center py-8",children:[f.jsx(vu,{className:"w-8 h-8 text-danger mx-auto mb-2"}),f.jsx("p",{className:"text-danger font-medium mb-1",children:"Failed to load sessions"}),f.jsx("p",{className:"text-text-muted text-sm mb-4",children:"Something went wrong while fetching sessions."}),f.jsxs(Eh,{variant:"outline",size:"sm",onClick:u,children:[f.jsx(vv,{className:"w-4 h-4 mr-2"}),"Retry"]})]})}function _0(u){if(u.length===0)return null;const r=u.filter(c=>c.status==="running");return r.length>0?r[0].name:u[0].name}function U0({epicSlug:u,storySlug:r}){const{sessions:c}=ua(),[o,m]=_.useState([]),[g,v]=_.useState(!0),[E,j]=_.useState(!1),M=_.useRef(void 0),R=_.useMemo(()=>c.filter(G=>G.epicSlug===u&&G.storySlug===r),[c,u,r]),C=R.length>0?R:o,U=_.useMemo(()=>[...C].sort((G,J)=>new Date(J.startTime).getTime()-new Date(G.startTime).getTime()),[C]);M.current===void 0&&U.length>0&&(M.current=_0(U));const k=M.current??null,Z=_.useCallback(async()=>{v(!0),j(!1);try{const G=await fetch(`/api/sessions?epicSlug=${u}&storySlug=${r}`);if(G.ok){const J=await G.json();m(J)}else j(!0),m([])}catch{j(!0),m([])}finally{v(!1)}},[u,r]);return _.useEffect(()=>{Z()},[Z]),g?f.jsx(A0,{}):E?f.jsx("div",{"data-testid":"sessions-panel",children:f.jsx(D0,{onRetry:Z})}):U.length===0?f.jsx("div",{"data-testid":"sessions-panel",children:f.jsx(C0,{})}):f.jsx("div",{"data-testid":"sessions-panel",className:"space-y-4",children:U.map(G=>f.jsx(M0,{session:G,defaultExpanded:G.name===k},G.name))})}const Th=_.forwardRef(({...u},r)=>f.jsx(mp,{ref:r,...u}));Th.displayName="Tabs";const jh=_.forwardRef(({className:u,...r},c)=>f.jsx(hp,{ref:c,className:Ht("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",u),...r}));jh.displayName="TabsList";const Zn=_.forwardRef(({className:u,...r},c)=>f.jsx(gp,{ref:c,className:Ht("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs",u),...r}));Zn.displayName="TabsTrigger";const kn=_.forwardRef(({className:u,...r},c)=>f.jsx(yp,{ref:c,className:Ht("mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",u),...r}));kn.displayName="TabsContent";const R0=404,w0=["tasks","content","journal","sessions"];function B0(u){const r=u.get("tab");return r&&w0.includes(r)?r:"tasks"}function H0(){return f.jsxs("div",{className:"animate-pulse space-y-4","data-testid":"story-header-skeleton",children:[f.jsx("div",{className:"h-8 w-64 bg-bg-light rounded"}),f.jsxs("div",{className:"flex items-center gap-4",children:[f.jsx("div",{className:"h-6 w-24 bg-bg-light rounded-full"}),f.jsx("div",{className:"h-4 w-32 bg-bg-light rounded"})]})]})}function q0(){return f.jsxs("div",{className:"animate-pulse space-y-4","data-testid":"story-content-skeleton",children:[f.jsx("div",{className:"h-6 w-48 bg-bg-light rounded"}),f.jsxs("div",{className:"space-y-2",children:[f.jsx("div",{className:"h-4 w-full bg-bg-light rounded"}),f.jsx("div",{className:"h-4 w-3/4 bg-bg-light rounded"}),f.jsx("div",{className:"h-4 w-5/6 bg-bg-light rounded"})]})]})}function Y0({status:u}){const r={ready:"bg-text-muted/20 text-text-muted",inProgress:"bg-primary/20 text-primary",blocked:"bg-danger/20 text-danger",completed:"bg-success/20 text-success"},c={ready:"Ready",inProgress:"In Progress",blocked:"Blocked",completed:"Completed"};return f.jsx(wl,{className:r[u],children:c[u]})}function G0({status:u}){const r={className:"w-5 h-5 pointer-events-none cursor-default"};switch(u){case"completed":return f.jsx(bu,{...r,className:`${r.className} text-success`,"data-testid":"icon-check-circle"});case"inProgress":return f.jsx(Ic,{...r,className:`${r.className} text-primary fill-primary/20`,"data-testid":"icon-circle-in-progress"});default:return f.jsx(Ic,{...r,className:`${r.className} text-text-muted`,"data-testid":"icon-circle-pending"})}}const $m={pending:"bg-text-muted/20 text-text-muted",inProgress:"bg-primary/20 text-primary",completed:"bg-success/20 text-success"},L0={pending:"pending",inProgress:"in progress",completed:"completed"};function X0(u){return $m[u]||$m.pending}function V0({task:u}){const r=X0(u.status);return f.jsxs("div",{className:"flex items-center gap-3 py-2 px-3 rounded-md hover:bg-bg-light/50",children:[f.jsx(G0,{status:u.status}),f.jsx("span",{className:u.status==="completed"?"text-text-muted line-through":"text-text",children:u.title}),f.jsx(wl,{className:`ml-auto text-xs ${r}`,children:L0[u.status]})]})}function Q0(u){switch(u){case"blocker":return{bg:"bg-danger/10",text:"text-danger",border:"border-danger/30"};case"resolution":return{bg:"bg-success/10",text:"text-success",border:"border-success/30"};default:return{bg:"bg-bg-light",text:"text-text",border:"border-border-muted"}}}function Z0({entry:u,defaultOpen:r=!1}){const[c,o]=_.useState(r),m=Q0(u.type);return f.jsx(ho,{open:c,onOpenChange:o,children:f.jsxs(ne,{className:`${m.bg} border ${m.border}`,children:[f.jsx(mo,{asChild:!0,children:f.jsx(ye,{className:"cursor-pointer hover:bg-bg-light/50 transition-colors py-3",children:f.jsxs("div",{className:"flex items-center gap-3",children:[c?f.jsx(so,{className:"w-4 h-4 text-text-muted","data-testid":"icon-chevron-down"}):f.jsx(co,{className:"w-4 h-4 text-text-muted","data-testid":"icon-chevron-right"}),f.jsx(wl,{className:`${m.bg} ${m.text} border ${m.border}`,children:u.type}),f.jsx(sa,{className:`text-sm font-medium ${m.text}`,children:u.title}),u.type==="blocker"&&f.jsx(vu,{className:"w-4 h-4 text-danger ml-auto","data-testid":"icon-alert-circle"}),u.timestamp&&f.jsx("span",{className:"text-xs text-text-muted ml-auto",children:u.timestamp})]})})}),f.jsx(fo,{children:f.jsx(Ae,{className:"pt-0 pb-4",children:f.jsx("div",{className:"prose prose-sm prose-invert max-w-none",children:f.jsx("pre",{className:"whitespace-pre-wrap text-sm text-text-muted font-mono bg-bg-dark p-3 rounded-md overflow-x-auto",children:u.content})})})})]})})}function k0(u){return u?{completed:u.tasks.filter(r=>r.status==="completed").length,total:u.tasks.length}:{completed:0,total:0}}function Ah(u){return{blockers:u.filter(r=>r.type==="blocker"),sessions:u.filter(r=>r.type==="session"),resolutions:u.filter(r=>r.type==="resolution")}}function K0({epicSlug:u,storySlug:r}){return f.jsxs("div",{className:"text-center py-12",children:[f.jsx("h1",{className:"text-2xl font-bold text-text mb-2",children:"Story not found"}),f.jsxs("p",{className:"text-text-muted mb-4",children:['The story "',r,'" does not exist in epic "',u,'".']}),f.jsx(il,{to:`/epic/${u}`,className:"text-primary hover:underline",children:"← Back to epic"})]})}function J0({epicSlug:u,error:r}){return f.jsxs("div",{className:"text-center py-12",children:[f.jsx("h1",{className:"text-2xl font-bold text-danger mb-2",children:"Error"}),f.jsx("p",{className:"text-text-muted mb-4",children:r}),f.jsx(il,{to:`/epic/${u}`,className:"text-primary hover:underline",children:"← Back to epic"})]})}function W0(){return f.jsxs("div",{className:"space-y-6",children:[f.jsx(H0,{}),f.jsx(q0,{})]})}function $0({story:u,epicSlug:r,storySlug:c}){const o=k0(u);return f.jsxs("div",{className:"space-y-4",children:[f.jsxs("div",{className:"flex items-center gap-2 text-sm text-text-muted",children:[f.jsx(il,{to:`/epic/${r}`,className:"hover:text-primary",children:r}),f.jsx("span",{children:"/"}),f.jsx("span",{className:"text-text",children:c})]}),f.jsx("h1",{className:"text-2xl font-bold text-text",children:u.title}),f.jsxs("div",{className:"flex items-center gap-4",children:[f.jsx(Y0,{status:u.status}),f.jsxs("span",{className:"text-sm text-text-muted",children:[o.completed,"/",o.total," tasks completed"]})]})]})}function F0({tasks:u}){return f.jsxs(ne,{children:[f.jsx(ye,{children:f.jsx(sa,{className:"text-lg",children:"Tasks"})}),f.jsx(Ae,{children:u.length===0?f.jsx("p",{className:"text-text-muted text-center py-4",children:"No tasks defined for this story."}):f.jsx("div",{className:"divide-y divide-border-muted",children:u.map(r=>f.jsx(V0,{task:r},r.id))})})]})}function I0({content:u}){return f.jsxs(ne,{children:[f.jsx(ye,{children:f.jsx(sa,{className:"text-lg",children:"Story Content"})}),f.jsx(Ae,{children:u?f.jsx("div",{className:"prose prose-sm prose-invert max-w-none prose-headings:text-text prose-p:text-text-muted prose-strong:text-text prose-code:text-primary prose-code:bg-bg-dark prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-code:before:content-none prose-code:after:content-none prose-pre:bg-bg-dark prose-pre:border prose-pre:border-border-muted prose-a:text-primary prose-a:no-underline prose-a:hover:underline prose-li:text-text-muted prose-table:border prose-table:border-border-muted prose-th:bg-bg-dark prose-th:px-3 prose-th:py-2 prose-th:text-text prose-td:px-3 prose-td:py-2 prose-td:border-t prose-td:border-border-muted",children:f.jsx(Fm,{remarkPlugins:[Im],children:u})}):f.jsx("p",{className:"text-text-muted text-center py-4",children:"No story content available."})})]})}function P0(u){return u==="Blockers"?"text-danger":u==="Resolutions"?"text-success":"text-text"}function $c({entries:u,type:r,icon:c}){if(u.length===0)return null;const o=P0(r);return f.jsxs("div",{className:"space-y-3",children:[f.jsxs("h3",{className:`text-sm font-semibold ${o} flex items-center gap-2`,children:[f.jsx(c,{className:"w-4 h-4"}),r," (",u.length,")"]}),u.map(m=>f.jsx(Z0,{entry:m,defaultOpen:r==="Blockers"},`${r.toLowerCase()}-${m.title}-${m.timestamp??""}`))]})}function tx({journal:u}){if(u.length===0)return f.jsx(ne,{children:f.jsx(Ae,{className:"py-8",children:f.jsx("p",{className:"text-text-muted text-center",children:"No journal entries yet."})})});const{blockers:r,sessions:c,resolutions:o}=Ah(u);return f.jsxs("div",{className:"space-y-4",children:[f.jsx($c,{entries:r,type:"Blockers",icon:vu}),f.jsx($c,{entries:o,type:"Resolutions",icon:bu}),f.jsx($c,{entries:c,type:"Sessions",icon:Ic})]})}async function ex(u){return u.status===R0?{type:"notFound"}:u.ok?{type:"success",data:await u.json()}:{type:"error"}}function lx(u,r,c){c("Failed to load story"),ro(u,r instanceof Error?r.message:"Unknown error")}function ax(u,r){const{currentStory:c,setCurrentStory:o,clearCurrentStory:m,subscribeToStory:g,unsubscribeFromStory:v,isConnected:E}=ua(),[j,M]=_.useState(!0),[R,C]=_.useState(!1),[U,k]=_.useState(null),Z=!!(u&&r);return _.useEffect(()=>(Z&&(async()=>{M(!0),C(!1),k(null);try{const J=await fetch(`/api/stories/${u}/${r}`),gt=await ex(J);gt.type==="notFound"?C(!0):gt.type==="error"?k("Failed to load story"):o(gt.data)}catch(J){lx(`/api/stories/${u}/${r}`,J,k)}finally{M(!1)}})(),m),[u,r,Z,o,m]),_.useEffect(()=>{if(Z&&E)return g(u,r),()=>{v(u,r)}},[u,r,Z,E,g,v]),{currentStory:c,loading:j,notFound:R,error:U}}function nx(){const{epicSlug:u,storySlug:r}=lo(),[c]=Np(),{currentStory:o,loading:m,notFound:g,error:v}=ax(u,r),E=B0(c);if(g)return f.jsx(K0,{epicSlug:u??"",storySlug:r??""});if(v&&!m)return f.jsx(J0,{epicSlug:u??"",error:v});if(m||!o)return f.jsx(W0,{});const{blockers:j}=Ah(o.journal??[]);return f.jsxs("div",{className:"space-y-6",children:[f.jsx($0,{story:o,epicSlug:u??"",storySlug:r??""}),f.jsxs(Th,{defaultValue:E,className:"w-full",children:[f.jsxs(jh,{className:"mb-4",children:[f.jsx(Zn,{value:"tasks",children:"Tasks"}),f.jsx(Zn,{value:"content",children:"Story Content"}),f.jsxs(Zn,{value:"journal",children:["Journal",j.length>0&&f.jsx(wl,{className:"ml-2 bg-danger/20 text-danger text-xs",children:j.length})]}),f.jsx(Zn,{value:"sessions",children:"Sessions"})]}),f.jsx(kn,{value:"tasks",className:"space-y-4",children:f.jsx(F0,{tasks:o.tasks})}),f.jsx(kn,{value:"content",className:"space-y-4",children:f.jsx(I0,{content:o.content})}),f.jsx(kn,{value:"journal",className:"space-y-4",children:f.jsx(tx,{journal:o.journal??[]})}),f.jsx(kn,{value:"sessions",className:"space-y-4",children:f.jsx(U0,{epicSlug:u??"",storySlug:r??""})})]})]})}const ix=[{path:"/",element:f.jsx(Nb,{}),children:[{path:"/",element:f.jsx(Pb,{})},{path:"epic/:slug",element:f.jsx(Qb,{})},{path:"epic/:epicSlug/story/:storySlug",element:f.jsx(nx,{})}]}];function ux(){return f.jsx(zp,{children:f.jsx(Tp,{children:ix.map(u=>f.jsx(bm,{path:u.path,element:u.element,children:u.children?.map(r=>f.jsx(bm,{path:r.path,element:r.element},r.path))},u.path))})})}const Oh=document.getElementById("root");if(!Oh)throw new Error("Root element not found");Cp.createRoot(Oh).render(f.jsx(_.StrictMode,{children:f.jsx(Zp,{children:f.jsx(ux,{})})}));
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>SAGA Dashboard</title>
8
- <script type="module" crossorigin src="/assets/index-6CjUWhlc.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-D3_tHSXd.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/vendor-xstate-Dogcz2PC.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-radix-BuqlnHDi.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BQ8AP4vp.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saga-ai/cli",
3
- "version": "2.17.0",
3
+ "version": "2.17.1",
4
4
  "description": "CLI for SAGA - Structured Autonomous Goal Achievement",
5
5
  "type": "module",
6
6
  "bin": {
@@ -93,7 +93,7 @@
93
93
  "build:cli": "esbuild src/cli.ts --bundle --platform=node --outfile=dist/cli.cjs --format=cjs --banner:js='#!/usr/bin/env node' --external:express --external:ws --external:chokidar --external:gray-matter --external:fuse.js --external:commander",
94
94
  "build:client": "vite build --config src/client/vite.config.ts",
95
95
  "build": "pnpm lint && concurrently -g \"pnpm build:cli\" \"pnpm build:client\"",
96
- "test": "pnpm build && vitest run && pnpm run test:integration && pnpm run test:e2e",
96
+ "test": "pnpm build && vitest run && pnpm test:integration && pnpm test:e2e",
97
97
  "test:watch": "vitest",
98
98
  "test:integration": "playwright test --config src/client/playwright.config.ts",
99
99
  "test:integration:ui": "playwright test --config src/client/playwright.config.ts --ui",