@saga-ai/dashboard 4.0.0 → 4.2.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.
Files changed (2) hide show
  1. package/dist/cli.cjs +172 -141
  2. package/package.json +2 -2
package/dist/cli.cjs CHANGED
@@ -28,8 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  ));
29
29
 
30
30
  // src/cli.ts
31
- var import_node_fs7 = require("node:fs");
32
- var import_node_path8 = require("node:path");
31
+ var import_node_fs6 = require("node:fs");
32
+ var import_node_path7 = require("node:path");
33
33
  var import_node_process6 = __toESM(require("node:process"), 1);
34
34
  var import_commander = require("commander");
35
35
 
@@ -38,16 +38,50 @@ var import_node_process4 = __toESM(require("node:process"), 1);
38
38
 
39
39
  // src/server/index.ts
40
40
  var import_node_http = require("node:http");
41
- var import_node_path6 = require("node:path");
41
+ var import_node_path5 = require("node:path");
42
42
  var import_express3 = __toESM(require("express"), 1);
43
43
 
44
44
  // src/server/routes.ts
45
45
  var import_express2 = require("express");
46
46
 
47
47
  // src/server/parser.ts
48
- var import_node_fs3 = require("node:fs");
48
+ var import_node_fs2 = require("node:fs");
49
49
  var import_promises = require("node:fs/promises");
50
50
 
51
+ // ../saga-utils/src/directory.ts
52
+ function normalizeRoot(projectRoot) {
53
+ return projectRoot.endsWith("/") ? projectRoot.slice(0, -1) : projectRoot;
54
+ }
55
+ function createSagaPaths(projectRoot) {
56
+ const root = normalizeRoot(projectRoot);
57
+ const saga = `${root}/.saga`;
58
+ return {
59
+ root,
60
+ saga,
61
+ epics: `${saga}/epics`,
62
+ stories: `${saga}/stories`,
63
+ worktrees: `${saga}/worktrees`,
64
+ archive: `${saga}/archive`
65
+ };
66
+ }
67
+ function createStoryPaths(projectRoot, storyId) {
68
+ const { stories } = createSagaPaths(projectRoot);
69
+ const storyDir = `${stories}/${storyId}`;
70
+ return {
71
+ storyId,
72
+ storyDir,
73
+ storyJson: `${storyDir}/story.json`,
74
+ journalMd: `${storyDir}/journal.md`
75
+ };
76
+ }
77
+ function createWorktreePaths(projectRoot, storyId) {
78
+ const { worktrees } = createSagaPaths(projectRoot);
79
+ return {
80
+ storyId,
81
+ worktreeDir: `${worktrees}/${storyId}`
82
+ };
83
+ }
84
+
51
85
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
52
86
  var external_exports = {};
53
87
  __export(external_exports, {
@@ -4089,7 +4123,7 @@ var coerce = {
4089
4123
  };
4090
4124
  var NEVER = INVALID;
4091
4125
 
4092
- // ../saga-types/src/task.ts
4126
+ // ../saga-utils/src/schemas/task.ts
4093
4127
  var TaskStatusSchema = external_exports.enum(["pending", "in_progress", "completed"]);
4094
4128
  var TaskSchema = external_exports.object({
4095
4129
  id: external_exports.string(),
@@ -4103,7 +4137,7 @@ var TaskSchema = external_exports.object({
4103
4137
  });
4104
4138
  var StoryIdSchema = external_exports.string().regex(/^[a-z0-9-]+$/);
4105
4139
 
4106
- // ../saga-types/src/claude-code-task.ts
4140
+ // ../saga-utils/src/schemas/claude-code-task.ts
4107
4141
  var ClaudeCodeTaskSchema = external_exports.object({
4108
4142
  id: external_exports.string(),
4109
4143
  subject: external_exports.string(),
@@ -4116,34 +4150,7 @@ var ClaudeCodeTaskSchema = external_exports.object({
4116
4150
  metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
4117
4151
  });
4118
4152
 
4119
- // ../saga-types/src/directory.ts
4120
- function normalizeRoot(projectRoot) {
4121
- return projectRoot.endsWith("/") ? projectRoot.slice(0, -1) : projectRoot;
4122
- }
4123
- function createSagaPaths(projectRoot) {
4124
- const root = normalizeRoot(projectRoot);
4125
- const saga = `${root}/.saga`;
4126
- return {
4127
- root,
4128
- saga,
4129
- epics: `${saga}/epics`,
4130
- stories: `${saga}/stories`,
4131
- worktrees: `${saga}/worktrees`,
4132
- archive: `${saga}/archive`
4133
- };
4134
- }
4135
- function createStoryPaths(projectRoot, storyId) {
4136
- const { stories } = createSagaPaths(projectRoot);
4137
- const storyDir = `${stories}/${storyId}`;
4138
- return {
4139
- storyId,
4140
- storyDir,
4141
- storyJson: `${storyDir}/story.json`,
4142
- journalMd: `${storyDir}/journal.md`
4143
- };
4144
- }
4145
-
4146
- // ../saga-types/src/epic.ts
4153
+ // ../saga-utils/src/schemas/epic.ts
4147
4154
  var EpicChildSchema = external_exports.object({
4148
4155
  id: external_exports.string(),
4149
4156
  blockedBy: external_exports.array(external_exports.string())
@@ -4155,7 +4162,7 @@ var EpicSchema = external_exports.object({
4155
4162
  children: external_exports.array(EpicChildSchema)
4156
4163
  }).strict();
4157
4164
 
4158
- // ../saga-types/src/session.ts
4165
+ // ../saga-utils/src/schemas/session.ts
4159
4166
  var SessionStatusSchema = external_exports.enum(["running", "completed"]);
4160
4167
  var SessionSchema = external_exports.object({
4161
4168
  /** Unique session name (saga__<epic>__<story>__<pid>) */
@@ -4178,11 +4185,7 @@ var SessionSchema = external_exports.object({
4178
4185
  outputPreview: external_exports.string().optional()
4179
4186
  });
4180
4187
 
4181
- // ../saga-types/src/storage.ts
4182
- var import_node_fs = require("node:fs");
4183
- var import_node_path = require("node:path");
4184
-
4185
- // ../saga-types/src/story.ts
4188
+ // ../saga-utils/src/schemas/story.ts
4186
4189
  var StorySchema = external_exports.object({
4187
4190
  id: external_exports.string(),
4188
4191
  title: external_exports.string(),
@@ -4196,7 +4199,82 @@ var StorySchema = external_exports.object({
4196
4199
  worktree: external_exports.string().optional()
4197
4200
  }).strict();
4198
4201
 
4199
- // ../saga-types/src/storage.ts
4202
+ // ../saga-utils/src/storage.ts
4203
+ var import_node_fs = require("node:fs");
4204
+ var import_node_path = require("node:path");
4205
+ function buildScannedStory(storyDir, storyJsonPath, worktreePath) {
4206
+ if (!(0, import_node_fs.existsSync)(storyJsonPath)) {
4207
+ return null;
4208
+ }
4209
+ let storyData;
4210
+ try {
4211
+ const raw = (0, import_node_fs.readFileSync)(storyJsonPath, "utf-8");
4212
+ storyData = StorySchema.parse(JSON.parse(raw));
4213
+ } catch {
4214
+ return null;
4215
+ }
4216
+ let tasks = [];
4217
+ try {
4218
+ const files = (0, import_node_fs.readdirSync)(storyDir);
4219
+ tasks = files.filter((f) => f.endsWith(".json") && f !== "story.json").map((f) => {
4220
+ const raw = (0, import_node_fs.readFileSync)((0, import_node_path.join)(storyDir, f), "utf-8");
4221
+ return TaskSchema.parse(JSON.parse(raw));
4222
+ });
4223
+ } catch {
4224
+ }
4225
+ const status = deriveStoryStatus(tasks);
4226
+ const journalMdPath = (0, import_node_path.join)(storyDir, "journal.md");
4227
+ return {
4228
+ ...storyData,
4229
+ status,
4230
+ storyPath: storyJsonPath,
4231
+ worktreePath,
4232
+ journalPath: (0, import_node_fs.existsSync)(journalMdPath) ? journalMdPath : void 0,
4233
+ tasks
4234
+ };
4235
+ }
4236
+ function scanMasterStories(projectRoot, storyMap) {
4237
+ const { stories } = createSagaPaths(projectRoot);
4238
+ if (!(0, import_node_fs.existsSync)(stories)) {
4239
+ return;
4240
+ }
4241
+ const entries = (0, import_node_fs.readdirSync)(stories, { withFileTypes: true });
4242
+ for (const entry of entries) {
4243
+ if (!entry.isDirectory()) {
4244
+ continue;
4245
+ }
4246
+ const storyPaths = createStoryPaths(projectRoot, entry.name);
4247
+ const scanned = buildScannedStory(storyPaths.storyDir, storyPaths.storyJson);
4248
+ if (!scanned) {
4249
+ continue;
4250
+ }
4251
+ const wtPaths = createWorktreePaths(projectRoot, entry.name);
4252
+ if ((0, import_node_fs.existsSync)(wtPaths.worktreeDir)) {
4253
+ scanned.worktreePath = wtPaths.worktreeDir;
4254
+ }
4255
+ storyMap.set(scanned.id, scanned);
4256
+ }
4257
+ }
4258
+ function scanWorktreeStories(projectRoot, storyMap) {
4259
+ const { worktrees } = createSagaPaths(projectRoot);
4260
+ if (!(0, import_node_fs.existsSync)(worktrees)) {
4261
+ return;
4262
+ }
4263
+ const entries = (0, import_node_fs.readdirSync)(worktrees, { withFileTypes: true });
4264
+ for (const wtEntry of entries) {
4265
+ if (!wtEntry.isDirectory()) {
4266
+ continue;
4267
+ }
4268
+ const storyId = wtEntry.name;
4269
+ const wtStoryDir = (0, import_node_path.join)(worktrees, storyId, ".saga", "stories", storyId);
4270
+ const wtStoryJson = (0, import_node_path.join)(wtStoryDir, "story.json");
4271
+ const wtPath = (0, import_node_path.join)(worktrees, storyId);
4272
+ const scanned = buildScannedStory(wtStoryDir, wtStoryJson, wtPath);
4273
+ if (scanned) {
4274
+ storyMap.set(scanned.id, scanned);
4275
+ }
4276
+ }
4277
+ }
4200
4278
  function readStory(projectRoot, storyId) {
4201
4279
  const { storyJson } = createStoryPaths(projectRoot, storyId);
4202
4280
  if (!(0, import_node_fs.existsSync)(storyJson)) {
@@ -4247,27 +4325,6 @@ function listEpics(projectRoot) {
4247
4325
  return EpicSchema.parse(parsed);
4248
4326
  });
4249
4327
  }
4250
- function listStories(projectRoot) {
4251
- const { stories } = createSagaPaths(projectRoot);
4252
- if (!(0, import_node_fs.existsSync)(stories)) {
4253
- throw new Error(`Stories directory not found: ${stories}`);
4254
- }
4255
- const entries = (0, import_node_fs.readdirSync)(stories, { withFileTypes: true });
4256
- return entries.filter((entry) => entry.isDirectory()).map((entry) => {
4257
- const storyJsonPath = (0, import_node_path.join)(stories, entry.name, "story.json");
4258
- if (!(0, import_node_fs.existsSync)(storyJsonPath)) {
4259
- return null;
4260
- }
4261
- const raw = (0, import_node_fs.readFileSync)(storyJsonPath, "utf-8");
4262
- let parsed;
4263
- try {
4264
- parsed = JSON.parse(raw);
4265
- } catch {
4266
- throw new Error(`Malformed JSON in story file: ${storyJsonPath}`);
4267
- }
4268
- return StorySchema.parse(parsed);
4269
- }).filter((story) => story !== null);
4270
- }
4271
4328
  function deriveStoryStatus(tasks) {
4272
4329
  if (tasks.length === 0) {
4273
4330
  return "pending";
@@ -4292,46 +4349,11 @@ function deriveEpicStatus(storyStatuses) {
4292
4349
  }
4293
4350
  return "pending";
4294
4351
  }
4295
-
4296
- // src/utils/saga-scanner.ts
4297
- var import_node_fs2 = require("node:fs");
4298
- var import_node_path2 = require("node:path");
4299
- function scanStories(sagaRoot) {
4300
- const storiesDir = (0, import_node_path2.join)(sagaRoot, ".saga", "stories");
4301
- if (!(0, import_node_fs2.existsSync)(storiesDir)) {
4302
- return [];
4303
- }
4304
- const stories = listStories(sagaRoot);
4305
- return stories.map((story) => {
4306
- let tasks = [];
4307
- try {
4308
- tasks = listTasks(sagaRoot, story.id);
4309
- } catch {
4310
- }
4311
- const { journalMd } = createStoryPaths(sagaRoot, story.id);
4312
- const hasJournal = (0, import_node_fs2.existsSync)(journalMd);
4313
- return {
4314
- id: story.id,
4315
- title: story.title,
4316
- description: story.description,
4317
- epicId: story.epic,
4318
- guidance: story.guidance,
4319
- doneWhen: story.doneWhen,
4320
- avoid: story.avoid,
4321
- branch: story.branch,
4322
- pr: story.pr,
4323
- worktree: story.worktree,
4324
- journalPath: hasJournal ? journalMd : void 0,
4325
- tasks
4326
- };
4327
- });
4328
- }
4329
- function scanEpics(sagaRoot) {
4330
- const epicsDir = (0, import_node_path2.join)(sagaRoot, ".saga", "epics");
4331
- if (!(0, import_node_fs2.existsSync)(epicsDir)) {
4332
- return [];
4333
- }
4334
- return listEpics(sagaRoot);
4352
+ function scanStories(projectRoot) {
4353
+ const storyMap = /* @__PURE__ */ new Map();
4354
+ scanMasterStories(projectRoot, storyMap);
4355
+ scanWorktreeStories(projectRoot, storyMap);
4356
+ return Array.from(storyMap.values());
4335
4357
  }
4336
4358
 
4337
4359
  // src/server/parser.ts
@@ -4355,7 +4377,7 @@ function toStoryDetail(story) {
4355
4377
  id: story.id,
4356
4378
  title: story.title,
4357
4379
  description: story.description,
4358
- epic: story.epicId,
4380
+ epic: story.epic,
4359
4381
  status: toApiStatus(derivedStatus),
4360
4382
  tasks,
4361
4383
  guidance: story.guidance,
@@ -4393,14 +4415,19 @@ function buildEpic(scannedEpic, epicStories) {
4393
4415
  }
4394
4416
  function parseStory(sagaRoot, storyId) {
4395
4417
  try {
4396
- const story = readStory(sagaRoot, storyId);
4418
+ const wtPaths = createWorktreePaths(sagaRoot, storyId);
4419
+ const wtStoryDir = `${wtPaths.worktreeDir}/.saga/stories/${storyId}`;
4420
+ const wtStoryJson = `${wtStoryDir}/story.json`;
4421
+ const useWorktree = (0, import_node_fs2.existsSync)(wtStoryJson);
4422
+ const storyRoot = useWorktree ? wtPaths.worktreeDir : sagaRoot;
4423
+ const story = readStory(storyRoot, storyId);
4397
4424
  let tasks = [];
4398
4425
  try {
4399
- tasks = listTasks(sagaRoot, storyId);
4426
+ tasks = listTasks(storyRoot, storyId);
4400
4427
  } catch {
4401
4428
  }
4402
- const { journalMd } = createStoryPaths(sagaRoot, storyId);
4403
- const hasJournal = (0, import_node_fs3.existsSync)(journalMd);
4429
+ const { journalMd } = createStoryPaths(storyRoot, storyId);
4430
+ const hasJournal = (0, import_node_fs2.existsSync)(journalMd);
4404
4431
  const derivedStatus = deriveStoryStatus(tasks);
4405
4432
  return {
4406
4433
  id: story.id,
@@ -4474,7 +4501,11 @@ async function parseJournal(journalPath) {
4474
4501
  }
4475
4502
  function scanSagaDirectory(sagaRoot) {
4476
4503
  const scannedStories = scanStories(sagaRoot);
4477
- const scannedEpics = scanEpics(sagaRoot);
4504
+ let scannedEpics = [];
4505
+ try {
4506
+ scannedEpics = listEpics(sagaRoot);
4507
+ } catch {
4508
+ }
4478
4509
  const allStories = scannedStories.map(toStoryDetail);
4479
4510
  const storiesByEpic = /* @__PURE__ */ new Map();
4480
4511
  const standaloneStories = [];
@@ -4499,9 +4530,9 @@ var import_express = require("express");
4499
4530
 
4500
4531
  // src/lib/sessions.ts
4501
4532
  var import_node_child_process = require("node:child_process");
4502
- var import_node_fs4 = require("node:fs");
4533
+ var import_node_fs3 = require("node:fs");
4503
4534
  var import_promises2 = require("node:fs/promises");
4504
- var import_node_path3 = require("node:path");
4535
+ var import_node_path2 = require("node:path");
4505
4536
  var import_node_process = __toESM(require("node:process"), 1);
4506
4537
  var PREVIEW_LINES_COUNT = 5;
4507
4538
  var PREVIEW_MAX_LENGTH = 500;
@@ -4571,7 +4602,7 @@ function listSessions() {
4571
4602
  name,
4572
4603
  status: "running",
4573
4604
  // If it shows up in tmux ls, it's running
4574
- outputFile: (0, import_node_path3.join)(OUTPUT_DIR, `${name}.jsonl`)
4605
+ outputFile: (0, import_node_path2.join)(OUTPUT_DIR, `${name}.jsonl`)
4575
4606
  });
4576
4607
  }
4577
4608
  }
@@ -4586,8 +4617,8 @@ function getSessionStatus(sessionName) {
4586
4617
  };
4587
4618
  }
4588
4619
  function streamLogs(sessionName) {
4589
- const outputFile = (0, import_node_path3.join)(OUTPUT_DIR, `${sessionName}.jsonl`);
4590
- if (!(0, import_node_fs4.existsSync)(outputFile)) {
4620
+ const outputFile = (0, import_node_path2.join)(OUTPUT_DIR, `${sessionName}.jsonl`);
4621
+ if (!(0, import_node_fs3.existsSync)(outputFile)) {
4591
4622
  throw new Error(`Output file not found: ${outputFile}`);
4592
4623
  }
4593
4624
  return new Promise((resolve, reject) => {
@@ -4640,8 +4671,8 @@ async function buildSessionInfo(name, status) {
4640
4671
  if (!parsed) {
4641
4672
  return null;
4642
4673
  }
4643
- const outputFile = (0, import_node_path3.join)(OUTPUT_DIR, `${name}.jsonl`);
4644
- const outputAvailable = (0, import_node_fs4.existsSync)(outputFile);
4674
+ const outputFile = (0, import_node_path2.join)(OUTPUT_DIR, `${name}.jsonl`);
4675
+ const outputAvailable = (0, import_node_fs3.existsSync)(outputFile);
4645
4676
  let startTime = /* @__PURE__ */ new Date();
4646
4677
  let endTime;
4647
4678
  let outputPreview;
@@ -4887,9 +4918,9 @@ function createApiRouter(sagaRoot) {
4887
4918
  var import_ws = require("ws");
4888
4919
 
4889
4920
  // src/lib/log-stream-manager.ts
4890
- var import_node_fs5 = require("node:fs");
4921
+ var import_node_fs4 = require("node:fs");
4891
4922
  var import_promises3 = require("node:fs/promises");
4892
- var import_node_path4 = require("node:path");
4923
+ var import_node_path3 = require("node:path");
4893
4924
  var import_chokidar = __toESM(require("chokidar"), 1);
4894
4925
  function parseJsonlLines(content) {
4895
4926
  const messages = [];
@@ -4971,8 +5002,8 @@ var LogStreamManager = class {
4971
5002
  * Creates a file watcher if this is the first subscriber.
4972
5003
  */
4973
5004
  async subscribe(sessionName, ws) {
4974
- const outputFile = (0, import_node_path4.join)(OUTPUT_DIR, `${sessionName}.jsonl`);
4975
- if (!(0, import_node_fs5.existsSync)(outputFile)) {
5005
+ const outputFile = (0, import_node_path3.join)(OUTPUT_DIR, `${sessionName}.jsonl`);
5006
+ if (!(0, import_node_fs4.existsSync)(outputFile)) {
4976
5007
  this.sendToClient(ws, {
4977
5008
  type: "logs:error",
4978
5009
  sessionName,
@@ -5101,10 +5132,10 @@ var LogStreamManager = class {
5101
5132
  if (!subs || subs.size === 0) {
5102
5133
  return;
5103
5134
  }
5104
- const outputFile = (0, import_node_path4.join)(OUTPUT_DIR, `${sessionName}.jsonl`);
5135
+ const outputFile = (0, import_node_path3.join)(OUTPUT_DIR, `${sessionName}.jsonl`);
5105
5136
  const finalMessages = [];
5106
5137
  try {
5107
- if ((0, import_node_fs5.existsSync)(outputFile)) {
5138
+ if ((0, import_node_fs4.existsSync)(outputFile)) {
5108
5139
  const content = await (0, import_promises3.readFile)(outputFile, "utf-8");
5109
5140
  const lastLineCount = this.lineCounts.get(sessionName) ?? 0;
5110
5141
  const nonEmptyLines = content.split("\n").filter((l) => l.trim());
@@ -5150,7 +5181,7 @@ var LogStreamManager = class {
5150
5181
 
5151
5182
  // src/server/watcher.ts
5152
5183
  var import_node_events = require("node:events");
5153
- var import_node_path5 = require("node:path");
5184
+ var import_node_path4 = require("node:path");
5154
5185
  var import_node_process2 = __toESM(require("node:process"), 1);
5155
5186
  var import_chokidar2 = __toESM(require("chokidar"), 1);
5156
5187
  var EPIC_PATH_PARTS = 3;
@@ -5163,8 +5194,8 @@ function shouldUsePolling() {
5163
5194
  function parseEpicsPath(parts) {
5164
5195
  if (parts.length === EPIC_PATH_PARTS) {
5165
5196
  const fileName = parts[2];
5166
- if ((0, import_node_path5.extname)(fileName) === ".json") {
5167
- const epicId = (0, import_node_path5.basename)(fileName, ".json");
5197
+ if ((0, import_node_path4.extname)(fileName) === ".json") {
5198
+ const epicId = (0, import_node_path4.basename)(fileName, ".json");
5168
5199
  return {
5169
5200
  epicId,
5170
5201
  isEpicFile: true,
@@ -5195,7 +5226,7 @@ function parseStoriesPath(parts) {
5195
5226
  isMainStoryFile: false
5196
5227
  };
5197
5228
  }
5198
- if ((0, import_node_path5.extname)(fileName) === ".json") {
5229
+ if ((0, import_node_path4.extname)(fileName) === ".json") {
5199
5230
  return {
5200
5231
  storyId,
5201
5232
  isEpicFile: false,
@@ -5207,8 +5238,8 @@ function parseStoriesPath(parts) {
5207
5238
  return null;
5208
5239
  }
5209
5240
  function parseFilePath(filePath, sagaRoot) {
5210
- const relativePath = (0, import_node_path5.relative)(sagaRoot, filePath);
5211
- const parts = relativePath.split(import_node_path5.sep);
5241
+ const relativePath = (0, import_node_path4.relative)(sagaRoot, filePath);
5242
+ const parts = relativePath.split(import_node_path4.sep);
5212
5243
  if (parts[0] !== ".saga" || parts.length < MIN_PATH_PARTS) {
5213
5244
  return null;
5214
5245
  }
@@ -5280,8 +5311,8 @@ function createDebounceKey(parsed) {
5280
5311
  return `epic:${parsed.epicId}`;
5281
5312
  }
5282
5313
  function createChokidarWatcher(sagaRoot) {
5283
- const storiesDir = (0, import_node_path5.join)(sagaRoot, ".saga", "stories");
5284
- const epicsDir = (0, import_node_path5.join)(sagaRoot, ".saga", "epics");
5314
+ const storiesDir = (0, import_node_path4.join)(sagaRoot, ".saga", "stories");
5315
+ const epicsDir = (0, import_node_path4.join)(sagaRoot, ".saga", "epics");
5285
5316
  const usePolling = shouldUsePolling();
5286
5317
  return import_chokidar2.default.watch([storiesDir, epicsDir], {
5287
5318
  persistent: true,
@@ -5313,7 +5344,7 @@ function createFileEventHandler(sagaRoot, debouncer, emitter, state) {
5313
5344
  type: watcherEventType,
5314
5345
  epicId: parsed.epicId,
5315
5346
  storyId: parsed.storyId,
5316
- path: (0, import_node_path5.relative)(sagaRoot, filePath)
5347
+ path: (0, import_node_path4.relative)(sagaRoot, filePath)
5317
5348
  };
5318
5349
  debouncer.schedule(createDebounceKey(parsed), event, (e) => {
5319
5350
  if (!state.closed) {
@@ -5635,8 +5666,8 @@ function createApp(sagaRoot) {
5635
5666
  res.json({ status: "ok" });
5636
5667
  });
5637
5668
  app.use("/api", createApiRouter(sagaRoot));
5638
- const clientDistPath = (0, import_node_path6.join)(__dirname, "client");
5639
- const _indexHtmlPath = (0, import_node_path6.join)(clientDistPath, "index.html");
5669
+ const clientDistPath = (0, import_node_path5.join)(__dirname, "client");
5670
+ const _indexHtmlPath = (0, import_node_path5.join)(clientDistPath, "index.html");
5640
5671
  app.use(import_express3.default.static(clientDistPath));
5641
5672
  app.get("/{*splat}", (_req, res) => {
5642
5673
  res.sendFile("index.html", { root: clientDistPath });
@@ -5674,17 +5705,17 @@ async function startServer(config) {
5674
5705
  }
5675
5706
 
5676
5707
  // src/utils/project-discovery.ts
5677
- var import_node_fs6 = require("node:fs");
5678
- var import_node_path7 = require("node:path");
5708
+ var import_node_fs5 = require("node:fs");
5709
+ var import_node_path6 = require("node:path");
5679
5710
  var import_node_process3 = __toESM(require("node:process"), 1);
5680
5711
  function findProjectRoot(startDir) {
5681
5712
  let currentDir = startDir ?? import_node_process3.default.cwd();
5682
5713
  while (true) {
5683
- const sagaDir = (0, import_node_path7.join)(currentDir, ".saga");
5684
- if ((0, import_node_fs6.existsSync)(sagaDir)) {
5714
+ const sagaDir = (0, import_node_path6.join)(currentDir, ".saga");
5715
+ if ((0, import_node_fs5.existsSync)(sagaDir)) {
5685
5716
  return currentDir;
5686
5717
  }
5687
- const parentDir = (0, import_node_path7.dirname)(currentDir);
5718
+ const parentDir = (0, import_node_path6.dirname)(currentDir);
5688
5719
  if (parentDir === currentDir) {
5689
5720
  return null;
5690
5721
  }
@@ -5693,8 +5724,8 @@ function findProjectRoot(startDir) {
5693
5724
  }
5694
5725
  function resolveProjectPath(explicitPath) {
5695
5726
  if (explicitPath) {
5696
- const sagaDir = (0, import_node_path7.join)(explicitPath, ".saga");
5697
- if (!(0, import_node_fs6.existsSync)(sagaDir)) {
5727
+ const sagaDir = (0, import_node_path6.join)(explicitPath, ".saga");
5728
+ if (!(0, import_node_fs5.existsSync)(sagaDir)) {
5698
5729
  throw new Error(
5699
5730
  `No .saga/ directory found at specified path: ${explicitPath}
5700
5731
  Make sure the path points to a SAGA project root.`
@@ -5760,8 +5791,8 @@ async function sessionsLogsCommand(sessionName) {
5760
5791
  }
5761
5792
 
5762
5793
  // src/cli.ts
5763
- var packageJsonPath = (0, import_node_path8.join)(__dirname, "..", "package.json");
5764
- var packageJson = JSON.parse((0, import_node_fs7.readFileSync)(packageJsonPath, "utf-8"));
5794
+ var packageJsonPath = (0, import_node_path7.join)(__dirname, "..", "package.json");
5795
+ var packageJson = JSON.parse((0, import_node_fs6.readFileSync)(packageJsonPath, "utf-8"));
5765
5796
  var program = new import_commander.Command();
5766
5797
  program.name("saga").description("Dashboard and session monitoring for SAGA - Structured Autonomous Goal Achievement").version(packageJson.version).addHelpCommand("help [command]", "Display help for a command");
5767
5798
  program.option("-p, --path <dir>", "Path to SAGA project directory (overrides auto-discovery)");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saga-ai/dashboard",
3
- "version": "4.0.0",
3
+ "version": "4.2.1",
4
4
  "description": "Dashboard and session monitoring for SAGA - Structured Autonomous Goal Achievement",
5
5
  "type": "module",
6
6
  "bin": {
@@ -80,7 +80,7 @@
80
80
  "vite": "^7.3.1",
81
81
  "vitest": "^4.0.18",
82
82
  "xstate": "^5.26.0",
83
- "@saga-ai/types": "0.0.0"
83
+ "@saga-ai/utils": "0.0.0"
84
84
  },
85
85
  "engines": {
86
86
  "node": ">=18.0.0"