agent-trajectories 0.5.9 → 0.6.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/README.md CHANGED
@@ -21,7 +21,7 @@ A **trajectory** is the complete story of agent work on a task:
21
21
  Works with any task system: Beads, Linear, Jira, GitHub Issues, or standalone. Trajectories are a universal format—like Markdown for documentation.
22
22
 
23
23
  ### Multiple Storage Backends
24
- - **File system** (default) - `.trajectories/` directory, git-friendly
24
+ - **File system** (default) - `.agentworkforce/trajectories/` directory, git-friendly
25
25
  - **SQLite** - Local indexing and search
26
26
  - **PostgreSQL/S3** - For teams and archival
27
27
 
@@ -95,17 +95,25 @@ Over time, trajectories become a searchable knowledge base:
95
95
  ### CLI
96
96
 
97
97
  ```bash
98
- # Install globally (trail command available directly)
98
+ # Run without installing globally
99
+ npx --yes agent-trajectories start "Implement auth module"
100
+
101
+ # Or install globally if you prefer the short trail command
99
102
  npm install -g agent-trajectories
103
+ trail start "Implement auth module"
100
104
 
101
- # Or install locally (requires npx prefix)
105
+ # Or install locally in a project
102
106
  npm install agent-trajectories
107
+ npx --no-install trail start "Implement auth module"
108
+ # or
109
+ npm exec -- trail start "Implement auth module"
103
110
  ```
104
111
 
105
112
  ```bash
106
113
  # Start tracking a task
107
114
  trail start "Implement auth module"
108
- # (use `npx trail start ...` if installed locally)
115
+ # (for non-global installs, replace `trail` with
116
+ # `npx --yes agent-trajectories`, `npx --no-install trail`, or `npm exec -- trail`)
109
117
 
110
118
  # View current status
111
119
  trail status
@@ -145,7 +153,7 @@ Use `--discard-sources` when the compacted summary should replace the raw source
145
153
  - name: Compact trajectories
146
154
  run: |
147
155
  PR_COMMITS=$(git log ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} --format=%H | paste -sd, -)
148
- OUTPUT=".trajectories/compacted/pr-${{ github.event.pull_request.number }}.json"
156
+ OUTPUT=".agentworkforce/trajectories/compacted/pr-${{ github.event.pull_request.number }}.json"
149
157
  if [ -n "$PR_COMMITS" ]; then
150
158
  npx agent-trajectories compact --commits "$PR_COMMITS" --output "$OUTPUT" --discard-sources
151
159
  else
@@ -153,7 +161,7 @@ Use `--discard-sources` when the compacted summary should replace the raw source
153
161
  fi
154
162
  - name: Commit compacted trajectories
155
163
  run: |
156
- git add .trajectories/ || true
164
+ git add .agentworkforce/trajectories/ || true
157
165
  git diff --cached --quiet || \
158
166
  (git commit -m "chore: compact trajectories for PR #${{ github.event.pull_request.number }}" && git push)
159
167
  ```
@@ -463,7 +471,7 @@ The client manages trajectories with persistent storage.
463
471
  ```typescript
464
472
  const client = new TrajectoryClient({
465
473
  defaultAgent: 'my-agent', // Default agent name
466
- dataDir: '.trajectories', // Storage directory
474
+ dataDir: '.', // Base directory; stores under .agentworkforce/trajectories
467
475
  autoSave: true, // Auto-save after operations
468
476
  });
469
477
 
@@ -542,7 +550,7 @@ const t = TrajectoryBuilder.create('Task')
542
550
  This project is in early development. See [PROPOSAL-trajectories.md](./PROPOSAL-trajectories.md) for the full design document.
543
551
 
544
552
  **v1.0 (current)**
545
- - [x] File-based storage (`.trajectories/`)
553
+ - [x] File-based storage (`.agentworkforce/trajectories/`)
546
554
  - [x] Core CLI commands (`start`, `decision`, `complete`, `list`, `show`, `export`)
547
555
  - [x] Agent Trace spec compliance (`.trace.json` generation)
548
556
  - [x] Multi-agent participation tracking
@@ -1,8 +1,157 @@
1
1
  // src/sdk/client.ts
2
2
  import { spawn } from "child_process";
3
- import { existsSync as existsSync2, readFileSync } from "fs";
3
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
4
4
  import { createRequire } from "module";
5
- import { dirname as dirname2, resolve as resolvePath } from "path";
5
+ import { dirname as dirname3, resolve as resolvePath } from "path";
6
+
7
+ // src/core/project-id.ts
8
+ import { execFileSync } from "child_process";
9
+ import { existsSync, readFileSync } from "fs";
10
+ import { dirname, join, resolve } from "path";
11
+ function resolveProjectId(explicitProjectId, options = {}) {
12
+ return readString(explicitProjectId) ?? readString((options.env ?? process.env).TRAJECTORIES_PROJECT) ?? resolveDefaultProjectId(options.cwd);
13
+ }
14
+ function resolveDefaultProjectId(cwd = process.cwd()) {
15
+ const packageJson = readNearestPackageJson(cwd);
16
+ return resolvePackageRepositoryId(packageJson) ?? resolveGitRemoteProjectId(cwd) ?? resolvePackageName(packageJson);
17
+ }
18
+ function normalizeRepositoryId(value) {
19
+ const raw = readString(value);
20
+ if (!raw) {
21
+ return void 0;
22
+ }
23
+ const withoutGitPrefix = raw.replace(/^git\+/, "");
24
+ const shorthand = withoutGitPrefix.match(
25
+ /^(?:github|gitlab|bitbucket):([^/]+\/[^/]+(?:\/[^/]+)*)$/
26
+ );
27
+ if (shorthand) {
28
+ return cleanRepositoryPath(shorthand[1]);
29
+ }
30
+ const ownerRepo = withoutGitPrefix.match(
31
+ /^([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)$/
32
+ );
33
+ if (ownerRepo) {
34
+ return cleanRepositoryPath(ownerRepo[1]);
35
+ }
36
+ const sshUrlScpLike = withoutGitPrefix.match(
37
+ /^ssh:\/\/[^@/\s]+@[^:/\s]+:(.+)$/i
38
+ );
39
+ if (sshUrlScpLike) {
40
+ return cleanRepositoryPath(sshUrlScpLike[1]);
41
+ }
42
+ const scpLike = withoutGitPrefix.match(/^[^@/\s]+@[^:/\s]+:(.+)$/);
43
+ if (scpLike) {
44
+ return cleanRepositoryPath(scpLike[1]);
45
+ }
46
+ let parsed;
47
+ try {
48
+ parsed = new URL(withoutGitPrefix);
49
+ } catch {
50
+ return void 0;
51
+ }
52
+ if (!["https:", "http:", "ssh:", "git:"].includes(parsed.protocol)) {
53
+ return void 0;
54
+ }
55
+ return cleanRepositoryPath(parsed.pathname);
56
+ }
57
+ function resolvePackageRepositoryId(packageJson) {
58
+ if (!packageJson) {
59
+ return void 0;
60
+ }
61
+ const repository = packageJson.repository;
62
+ if (typeof repository === "string") {
63
+ return normalizeRepositoryId(repository);
64
+ }
65
+ if (isRepositoryObject(repository) && typeof repository.url === "string") {
66
+ const projectId = normalizeRepositoryId(repository.url);
67
+ const directory = cleanRepositoryDirectory(repository.directory);
68
+ return directory && projectId ? `${projectId}//${directory}` : projectId;
69
+ }
70
+ return void 0;
71
+ }
72
+ function resolvePackageName(packageJson) {
73
+ return readString(packageJson?.name);
74
+ }
75
+ function resolveGitRemoteProjectId(cwd) {
76
+ for (const remote of ["upstream", "origin"]) {
77
+ const remoteUrl = getGitRemoteUrl(cwd, remote);
78
+ if (!remoteUrl) {
79
+ continue;
80
+ }
81
+ const projectId = normalizeRepositoryId(remoteUrl);
82
+ if (projectId) {
83
+ return projectId;
84
+ }
85
+ }
86
+ return void 0;
87
+ }
88
+ function getGitRemoteUrl(cwd, remote) {
89
+ try {
90
+ return readString(
91
+ execFileSync("git", ["config", "--get", `remote.${remote}.url`], {
92
+ cwd,
93
+ encoding: "utf-8",
94
+ stdio: ["ignore", "pipe", "ignore"]
95
+ })
96
+ );
97
+ } catch {
98
+ return void 0;
99
+ }
100
+ }
101
+ function readNearestPackageJson(cwd) {
102
+ let current = resolve(cwd);
103
+ while (true) {
104
+ const candidate = join(current, "package.json");
105
+ if (existsSync(candidate)) {
106
+ try {
107
+ const parsed = JSON.parse(readFileSync(candidate, "utf-8"));
108
+ if (isPackageJson(parsed)) {
109
+ return parsed;
110
+ }
111
+ } catch {
112
+ }
113
+ }
114
+ const parent = dirname(current);
115
+ if (parent === current) {
116
+ return void 0;
117
+ }
118
+ current = parent;
119
+ }
120
+ }
121
+ function cleanRepositoryPath(path) {
122
+ const withoutQuery = path.split(/[?#]/, 1)[0] ?? "";
123
+ const withoutSlashes = withoutQuery.replace(/^\/+|\/+$/g, "");
124
+ const withoutGitSuffix = withoutSlashes.replace(/\.git$/i, "");
125
+ const parts = withoutGitSuffix.split("/").map((part) => part.trim()).filter(Boolean);
126
+ if (parts.length < 2) {
127
+ return void 0;
128
+ }
129
+ return parts.join("/");
130
+ }
131
+ function cleanRepositoryDirectory(directory) {
132
+ const raw = readString(directory);
133
+ if (!raw || raw.startsWith("/") || raw.startsWith("\\") || /^[A-Za-z]:[\\/]/.test(raw)) {
134
+ return void 0;
135
+ }
136
+ const parts = raw.split(/[\\/]/).map((part) => part.trim()).filter(Boolean);
137
+ if (parts.length === 0 || parts.some((part) => part === "." || part === "..")) {
138
+ return void 0;
139
+ }
140
+ return parts.join("/");
141
+ }
142
+ function readString(value) {
143
+ if (typeof value !== "string") {
144
+ return void 0;
145
+ }
146
+ const trimmed = value.trim();
147
+ return trimmed.length > 0 ? trimmed : void 0;
148
+ }
149
+ function isPackageJson(value) {
150
+ return value !== null && typeof value === "object" && !Array.isArray(value);
151
+ }
152
+ function isRepositoryObject(value) {
153
+ return value !== null && typeof value === "object" && !Array.isArray(value);
154
+ }
6
155
 
7
156
  // src/core/id.ts
8
157
  import { webcrypto } from "crypto";
@@ -269,7 +418,7 @@ function createTrajectory(input) {
269
418
  chapters: [],
270
419
  commits: [],
271
420
  filesChanged: [],
272
- projectId: input.projectId ?? process.cwd(),
421
+ projectId: input.projectId,
273
422
  tags: input.tags ?? []
274
423
  };
275
424
  }
@@ -666,7 +815,7 @@ function formatTime(isoString) {
666
815
  }
667
816
 
668
817
  // src/storage/file.ts
669
- import { existsSync } from "fs";
818
+ import { existsSync as existsSync2 } from "fs";
670
819
  import {
671
820
  mkdir,
672
821
  readFile,
@@ -677,19 +826,27 @@ import {
677
826
  } from "fs/promises";
678
827
  import {
679
828
  basename,
680
- dirname,
829
+ dirname as dirname2,
681
830
  isAbsolute,
682
- join,
831
+ join as join2,
683
832
  relative,
684
- resolve
833
+ resolve as resolve2
685
834
  } from "path";
686
835
  var TRAJECTORY_FILE = "trajectory.json";
687
836
  var SUMMARY_FILE = "summary.md";
688
837
  var COMPACTION_FILE = "compaction.json";
689
838
  var LEGACY_COMPACTION_SUFFIX = ".compaction.json";
839
+ var DEFAULT_TRAJECTORY_DATA_DIR = join2(
840
+ ".agentworkforce",
841
+ "trajectories"
842
+ );
843
+ var LEGACY_TRAJECTORY_DATA_DIR = ".trajectories";
844
+ function getDefaultTrajectoryDataDir(baseDir = process.cwd()) {
845
+ return join2(baseDir, DEFAULT_TRAJECTORY_DATA_DIR);
846
+ }
690
847
  function expandPath(path) {
691
848
  if (path.startsWith("~")) {
692
- return join(process.env.HOME ?? "", path.slice(1));
849
+ return join2(process.env.HOME ?? "", path.slice(1));
693
850
  }
694
851
  return path;
695
852
  }
@@ -713,28 +870,42 @@ var FileStorage = class {
713
870
  activeDir;
714
871
  completedDir;
715
872
  lastReconcileSummary;
873
+ shouldMigrateLegacyDefault = false;
716
874
  constructor(baseDir) {
717
875
  this.baseDir = baseDir ?? process.cwd();
718
876
  const dataDir = process.env.TRAJECTORIES_DATA_DIR;
719
877
  if (dataDir) {
720
878
  this.trajectoriesDir = expandPath(dataDir);
721
879
  } else {
722
- this.trajectoriesDir = join(this.baseDir, ".trajectories");
880
+ this.trajectoriesDir = getDefaultTrajectoryDataDir(this.baseDir);
881
+ this.shouldMigrateLegacyDefault = true;
723
882
  }
724
- this.activeDir = join(this.trajectoriesDir, "active");
725
- this.completedDir = join(this.trajectoriesDir, "completed");
883
+ this.activeDir = join2(this.trajectoriesDir, "active");
884
+ this.completedDir = join2(this.trajectoriesDir, "completed");
726
885
  }
727
886
  /**
728
887
  * Initialize storage directories
729
888
  */
730
889
  async initialize() {
890
+ await this.migrateLegacyDefaultDir();
731
891
  await mkdir(this.trajectoriesDir, { recursive: true });
732
892
  await mkdir(this.activeDir, { recursive: true });
733
893
  await mkdir(this.completedDir, { recursive: true });
734
894
  await this.migrateLegacyIndexCompactionMarkers();
735
- await rm(join(this.trajectoriesDir, "index.json"), { force: true });
895
+ await rm(join2(this.trajectoriesDir, "index.json"), { force: true });
736
896
  await this.reconcileIndex();
737
897
  }
898
+ async migrateLegacyDefaultDir() {
899
+ if (!this.shouldMigrateLegacyDefault) {
900
+ return;
901
+ }
902
+ const legacyDir = join2(this.baseDir, LEGACY_TRAJECTORY_DATA_DIR);
903
+ if (!existsSync2(legacyDir) || existsSync2(this.trajectoriesDir)) {
904
+ return;
905
+ }
906
+ await mkdir(dirname2(this.trajectoriesDir), { recursive: true });
907
+ await rename(legacyDir, this.trajectoriesDir);
908
+ }
738
909
  /**
739
910
  * Scan active/ and completed/ recursively and report trajectory files
740
911
  * that can be loaded plus files that should be surfaced by doctor.
@@ -805,7 +976,7 @@ var FileStorage = class {
805
976
  return this.lastReconcileSummary;
806
977
  }
807
978
  /**
808
- * Move trajectory files that fail to load into `.trajectories/invalid/`
979
+ * Move trajectory files that fail to load into `.agentworkforce/trajectories/invalid/`
809
980
  * so reconcile no longer scans them. Only quarantines parse and schema
810
981
  * failures — transient io_error failures are left in place because the
811
982
  * file may load fine on the next attempt.
@@ -815,7 +986,7 @@ var FileStorage = class {
815
986
  */
816
987
  async quarantineInvalid() {
817
988
  const summary = await this.reconcileIndex();
818
- const targetDir = join(this.trajectoriesDir, "invalid");
989
+ const targetDir = join2(this.trajectoriesDir, "invalid");
819
990
  const candidates = summary.failures.filter((f) => f.reason !== "io_error");
820
991
  if (candidates.length === 0) {
821
992
  return { moved: [], targetDir };
@@ -825,7 +996,7 @@ var FileStorage = class {
825
996
  for (const failure of candidates) {
826
997
  const dest = await this.resolveQuarantineDest(failure.path, targetDir);
827
998
  try {
828
- await mkdir(dirname(dest), { recursive: true });
999
+ await mkdir(dirname2(dest), { recursive: true });
829
1000
  await rename(failure.path, dest);
830
1001
  moved.push(failure);
831
1002
  } catch (error) {
@@ -851,13 +1022,13 @@ var FileStorage = class {
851
1022
  async resolveQuarantineDest(sourcePath, targetDir) {
852
1023
  const rel = relative(this.trajectoriesDir, sourcePath);
853
1024
  const safeRel = rel && !rel.startsWith("..") && !isAbsolute(rel) ? rel : basename(sourcePath);
854
- let dest = join(targetDir, safeRel);
855
- if (!existsSync(dest)) return dest;
1025
+ let dest = join2(targetDir, safeRel);
1026
+ if (!existsSync2(dest)) return dest;
856
1027
  const ext = safeRel.endsWith(".json") ? ".json" : "";
857
1028
  const stem = ext ? safeRel.slice(0, -ext.length) : safeRel;
858
1029
  for (let i = 1; i < 1e3; i += 1) {
859
- dest = join(targetDir, `${stem}.${i}${ext}`);
860
- if (!existsSync(dest)) return dest;
1030
+ dest = join2(targetDir, `${stem}.${i}${ext}`);
1031
+ if (!existsSync2(dest)) return dest;
861
1032
  }
862
1033
  return dest;
863
1034
  }
@@ -874,7 +1045,7 @@ var FileStorage = class {
874
1045
  throw error;
875
1046
  }
876
1047
  for (const entry of entries) {
877
- const entryPath = join(dir, entry.name);
1048
+ const entryPath = join2(dir, entry.name);
878
1049
  if (entry.isDirectory()) {
879
1050
  await this.walkJsonFilesInto(entryPath, out);
880
1051
  } else if (entry.isFile() && isTrajectoryJsonFile(entry.name)) {
@@ -905,20 +1076,20 @@ var FileStorage = class {
905
1076
  let trajectoryDir;
906
1077
  if (isCompleted) {
907
1078
  const date = new Date(trajectory2.completedAt ?? trajectory2.startedAt);
908
- const monthDir = join(
1079
+ const monthDir = join2(
909
1080
  this.completedDir,
910
1081
  `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`
911
1082
  );
912
- trajectoryDir = join(monthDir, trajectory2.id);
1083
+ trajectoryDir = join2(monthDir, trajectory2.id);
913
1084
  } else {
914
- trajectoryDir = join(this.activeDir, trajectory2.id);
1085
+ trajectoryDir = join2(this.activeDir, trajectory2.id);
915
1086
  }
916
- const filePath = join(trajectoryDir, TRAJECTORY_FILE);
1087
+ const filePath = join2(trajectoryDir, TRAJECTORY_FILE);
917
1088
  await this.removeTrajectoryFiles(existingPaths, filePath);
918
1089
  await mkdir(trajectoryDir, { recursive: true });
919
1090
  if (isCompleted) {
920
1091
  const markdown = exportToMarkdown(trajectory2);
921
- await writeFile(join(trajectoryDir, SUMMARY_FILE), markdown, "utf-8");
1092
+ await writeFile(join2(trajectoryDir, SUMMARY_FILE), markdown, "utf-8");
922
1093
  }
923
1094
  await writeFile(filePath, JSON.stringify(trajectory2, null, 2), "utf-8");
924
1095
  }
@@ -927,7 +1098,7 @@ var FileStorage = class {
927
1098
  */
928
1099
  async get(id) {
929
1100
  for (const filePath of this.getActiveCandidatePaths(id)) {
930
- if (!existsSync(filePath)) continue;
1101
+ if (!existsSync2(filePath)) continue;
931
1102
  const trajectory2 = await this.readTrajectoryOrNull(filePath);
932
1103
  if (trajectory2?.id === id) {
933
1104
  return trajectory2;
@@ -1132,9 +1303,9 @@ var FileStorage = class {
1132
1303
  return [];
1133
1304
  }
1134
1305
  return [
1135
- join(this.activeDir, id, TRAJECTORY_FILE),
1306
+ join2(this.activeDir, id, TRAJECTORY_FILE),
1136
1307
  // Legacy layout from v0.5.x and earlier.
1137
- join(this.activeDir, `${id}.json`)
1308
+ join2(this.activeDir, `${id}.json`)
1138
1309
  ];
1139
1310
  }
1140
1311
  async loadAllTrajectories() {
@@ -1188,7 +1359,7 @@ var FileStorage = class {
1188
1359
  }
1189
1360
  getTrajectoryIdFromPath(filePath) {
1190
1361
  if (basename(filePath) === TRAJECTORY_FILE) {
1191
- const id = basename(dirname(filePath));
1362
+ const id = basename(dirname2(filePath));
1192
1363
  return isSafeTrajectoryId(id) ? id : void 0;
1193
1364
  }
1194
1365
  const name = basename(filePath);
@@ -1209,10 +1380,10 @@ var FileStorage = class {
1209
1380
  }
1210
1381
  async removeTrajectoryFile(filePath, summary) {
1211
1382
  if (basename(filePath) === TRAJECTORY_FILE) {
1212
- const trajectoryDir = dirname(filePath);
1383
+ const trajectoryDir = dirname2(filePath);
1213
1384
  await this.countDirectoryTrajectoryFiles(trajectoryDir, summary);
1214
1385
  await this.removeFileIfExists(
1215
- join(dirname(trajectoryDir), `${basename(trajectoryDir)}.trace.json`),
1386
+ join2(dirname2(trajectoryDir), `${basename(trajectoryDir)}.trace.json`),
1216
1387
  "trace",
1217
1388
  summary
1218
1389
  );
@@ -1238,40 +1409,40 @@ var FileStorage = class {
1238
1409
  }
1239
1410
  async countDirectoryTrajectoryFiles(trajectoryDir, summary) {
1240
1411
  await this.countFileIfExists(
1241
- join(trajectoryDir, TRAJECTORY_FILE),
1412
+ join2(trajectoryDir, TRAJECTORY_FILE),
1242
1413
  "json",
1243
1414
  summary
1244
1415
  );
1245
1416
  await this.countFileIfExists(
1246
- join(trajectoryDir, SUMMARY_FILE),
1417
+ join2(trajectoryDir, SUMMARY_FILE),
1247
1418
  "markdown",
1248
1419
  summary
1249
1420
  );
1250
1421
  await this.countFileIfExists(
1251
- join(trajectoryDir, `${basename(trajectoryDir)}.trace.json`),
1422
+ join2(trajectoryDir, `${basename(trajectoryDir)}.trace.json`),
1252
1423
  "trace",
1253
1424
  summary
1254
1425
  );
1255
1426
  await this.countFileIfExists(
1256
- join(trajectoryDir, "trace.json"),
1427
+ join2(trajectoryDir, "trace.json"),
1257
1428
  "trace",
1258
1429
  summary
1259
1430
  );
1260
1431
  await this.countFileIfExists(
1261
- join(trajectoryDir, COMPACTION_FILE),
1432
+ join2(trajectoryDir, COMPACTION_FILE),
1262
1433
  "compaction",
1263
1434
  summary
1264
1435
  );
1265
1436
  }
1266
1437
  async removeFileIfExists(path, kind, summary) {
1267
- if (!existsSync(path)) {
1438
+ if (!existsSync2(path)) {
1268
1439
  return;
1269
1440
  }
1270
1441
  await rm(path, { force: true });
1271
1442
  this.incrementDeleteSummary(kind, summary);
1272
1443
  }
1273
1444
  async countFileIfExists(path, kind, summary) {
1274
- if (existsSync(path)) {
1445
+ if (existsSync2(path)) {
1275
1446
  this.incrementDeleteSummary(kind, summary);
1276
1447
  }
1277
1448
  }
@@ -1312,21 +1483,21 @@ var FileStorage = class {
1312
1483
  }
1313
1484
  getCompactionMarkerPath(filePath, id) {
1314
1485
  if (basename(filePath) === TRAJECTORY_FILE) {
1315
- return join(dirname(filePath), COMPACTION_FILE);
1486
+ return join2(dirname2(filePath), COMPACTION_FILE);
1316
1487
  }
1317
- return join(dirname(filePath), `${id}${LEGACY_COMPACTION_SUFFIX}`);
1488
+ return join2(dirname2(filePath), `${id}${LEGACY_COMPACTION_SUFFIX}`);
1318
1489
  }
1319
1490
  getTrajectoryIdFromCompactionMarkerPath(markerPath) {
1320
1491
  if (basename(markerPath) === COMPACTION_FILE) {
1321
- const id = basename(dirname(markerPath));
1492
+ const id = basename(dirname2(markerPath));
1322
1493
  return id.startsWith("traj_") ? id : void 0;
1323
1494
  }
1324
1495
  const markerName = basename(markerPath);
1325
1496
  return markerName.endsWith(LEGACY_COMPACTION_SUFFIX) ? markerName.slice(0, -LEGACY_COMPACTION_SUFFIX.length) : void 0;
1326
1497
  }
1327
1498
  async migrateLegacyIndexCompactionMarkers() {
1328
- const indexPath = join(this.trajectoriesDir, "index.json");
1329
- if (!existsSync(indexPath)) {
1499
+ const indexPath = join2(this.trajectoriesDir, "index.json");
1500
+ if (!existsSync2(indexPath)) {
1330
1501
  return;
1331
1502
  }
1332
1503
  let parsed;
@@ -1352,7 +1523,7 @@ var FileStorage = class {
1352
1523
  if (typeof compactedInto !== "string") {
1353
1524
  return;
1354
1525
  }
1355
- const paths = typeof path === "string" && existsSync(path) && this.isPathInsideTrajectoriesDir(path) ? [path] : await this.findTrajectoryFilePaths(id);
1526
+ const paths = typeof path === "string" && existsSync2(path) && this.isPathInsideTrajectoriesDir(path) ? [path] : await this.findTrajectoryFilePaths(id);
1356
1527
  if (paths.length === 0) return;
1357
1528
  const marker = {
1358
1529
  trajectoryId: id,
@@ -1372,7 +1543,7 @@ var FileStorage = class {
1372
1543
  );
1373
1544
  }
1374
1545
  isPathInsideTrajectoriesDir(path) {
1375
- const rel = relative(resolve(this.trajectoriesDir), resolve(path));
1546
+ const rel = relative(resolve2(this.trajectoriesDir), resolve2(path));
1376
1547
  return Boolean(rel && !rel.startsWith("..") && !isAbsolute(rel));
1377
1548
  }
1378
1549
  async walkFilesInto(dir, out, predicate) {
@@ -1384,7 +1555,7 @@ var FileStorage = class {
1384
1555
  throw error;
1385
1556
  }
1386
1557
  for (const entry of entries) {
1387
- const entryPath = join(dir, entry.name);
1558
+ const entryPath = join2(dir, entry.name);
1388
1559
  if (entry.isDirectory()) {
1389
1560
  await this.walkFilesInto(entryPath, out, predicate);
1390
1561
  } else if (entry.isFile() && predicate(entry.name)) {
@@ -1517,12 +1688,12 @@ function resolveTrajectoryCliInvocation() {
1517
1688
  try {
1518
1689
  const packageJsonPath = require2.resolve("agent-trajectories/package.json");
1519
1690
  const pkg = JSON.parse(
1520
- readFileSync(packageJsonPath, "utf-8")
1691
+ readFileSync2(packageJsonPath, "utf-8")
1521
1692
  );
1522
1693
  const binEntry = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.trail ?? (pkg.name ? pkg.bin?.[pkg.name] : void 0);
1523
1694
  if (binEntry) {
1524
- const cliPath = resolvePath(dirname2(packageJsonPath), binEntry);
1525
- if (existsSync2(cliPath)) {
1695
+ const cliPath = resolvePath(dirname3(packageJsonPath), binEntry);
1696
+ if (existsSync3(cliPath)) {
1526
1697
  return { command: process.execPath, args: [cliPath] };
1527
1698
  }
1528
1699
  }
@@ -1567,7 +1738,7 @@ async function compactWorkflow(workflowId, options) {
1567
1738
  if (options?.discardSources) {
1568
1739
  args.push("--discard-sources");
1569
1740
  }
1570
- return new Promise((resolve2, reject) => {
1741
+ return new Promise((resolve3, reject) => {
1571
1742
  const child = spawn(cli.command, args, {
1572
1743
  cwd: options?.cwd,
1573
1744
  stdio: ["ignore", "pipe", "pipe"]
@@ -1595,7 +1766,7 @@ async function compactWorkflow(workflowId, options) {
1595
1766
  }
1596
1767
  try {
1597
1768
  const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
1598
- resolve2(parseCompactWorkflowOutput(stdout));
1769
+ resolve3(parseCompactWorkflowOutput(stdout));
1599
1770
  } catch (error) {
1600
1771
  reject(
1601
1772
  error instanceof Error ? error : new Error("compactWorkflow failed: unable to parse CLI output")
@@ -1822,7 +1993,7 @@ var TrajectoryClient = class {
1822
1993
  constructor(options = {}) {
1823
1994
  this.storage = options.storage ?? new FileStorage(options.dataDir);
1824
1995
  this.defaultAgent = options.defaultAgent ?? process.env.TRAJECTORIES_AGENT;
1825
- this.projectId = options.projectId ?? process.env.TRAJECTORIES_PROJECT;
1996
+ this.projectId = resolveProjectId(options.projectId);
1826
1997
  this.autoSave = options.autoSave ?? true;
1827
1998
  this.autoCompact = normalizeAutoCompactOptions(options.autoCompact);
1828
1999
  this.autoCompactCwd = options.storage ? void 0 : options.dataDir;
@@ -2264,7 +2435,7 @@ function trajectory(title) {
2264
2435
 
2265
2436
  // src/core/trailers.ts
2266
2437
  import { execSync as execSync2 } from "child_process";
2267
- import { readFileSync as readFileSync2 } from "fs";
2438
+ import { readFileSync as readFileSync3 } from "fs";
2268
2439
 
2269
2440
  // src/core/trace.ts
2270
2441
  import { execSync } from "child_process";
@@ -2407,6 +2578,9 @@ export {
2407
2578
  exportToMarkdown,
2408
2579
  exportToPRSummary,
2409
2580
  exportToTimeline,
2581
+ DEFAULT_TRAJECTORY_DATA_DIR,
2582
+ LEGACY_TRAJECTORY_DATA_DIR,
2583
+ getDefaultTrajectoryDataDir,
2410
2584
  FileStorage,
2411
2585
  compactWorkflow,
2412
2586
  TrajectorySession,
@@ -2420,4 +2594,4 @@ export {
2420
2594
  getCommitsBetween,
2421
2595
  getFilesChangedBetween
2422
2596
  };
2423
- //# sourceMappingURL=chunk-JMH3Z5BB.js.map
2597
+ //# sourceMappingURL=chunk-BVTUGGYW.js.map