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/dist/cli/index.js CHANGED
@@ -248,7 +248,7 @@ function createTrajectory(input) {
248
248
  chapters: [],
249
249
  commits: [],
250
250
  filesChanged: [],
251
- projectId: input.projectId ?? process.cwd(),
251
+ projectId: input.projectId,
252
252
  tags: input.tags ?? []
253
253
  };
254
254
  }
@@ -587,6 +587,14 @@ var TRAJECTORY_FILE = "trajectory.json";
587
587
  var SUMMARY_FILE = "summary.md";
588
588
  var COMPACTION_FILE = "compaction.json";
589
589
  var LEGACY_COMPACTION_SUFFIX = ".compaction.json";
590
+ var DEFAULT_TRAJECTORY_DATA_DIR = join(
591
+ ".agentworkforce",
592
+ "trajectories"
593
+ );
594
+ var LEGACY_TRAJECTORY_DATA_DIR = ".trajectories";
595
+ function getDefaultTrajectoryDataDir(baseDir = process.cwd()) {
596
+ return join(baseDir, DEFAULT_TRAJECTORY_DATA_DIR);
597
+ }
590
598
  function expandPath(path2) {
591
599
  if (path2.startsWith("~")) {
592
600
  return join(process.env.HOME ?? "", path2.slice(1));
@@ -602,7 +610,7 @@ function getSearchPaths() {
602
610
  if (dataDir) {
603
611
  return [expandPath(dataDir)];
604
612
  }
605
- return [join(process.cwd(), ".trajectories")];
613
+ return [getDefaultTrajectoryDataDir()];
606
614
  }
607
615
  function describeReadFailure(reason, error) {
608
616
  if (reason === "schema_violation" && error && typeof error === "object" && "issues" in error) {
@@ -624,13 +632,15 @@ var FileStorage = class {
624
632
  activeDir;
625
633
  completedDir;
626
634
  lastReconcileSummary;
635
+ shouldMigrateLegacyDefault = false;
627
636
  constructor(baseDir) {
628
637
  this.baseDir = baseDir ?? process.cwd();
629
638
  const dataDir = process.env.TRAJECTORIES_DATA_DIR;
630
639
  if (dataDir) {
631
640
  this.trajectoriesDir = expandPath(dataDir);
632
641
  } else {
633
- this.trajectoriesDir = join(this.baseDir, ".trajectories");
642
+ this.trajectoriesDir = getDefaultTrajectoryDataDir(this.baseDir);
643
+ this.shouldMigrateLegacyDefault = true;
634
644
  }
635
645
  this.activeDir = join(this.trajectoriesDir, "active");
636
646
  this.completedDir = join(this.trajectoriesDir, "completed");
@@ -639,6 +649,7 @@ var FileStorage = class {
639
649
  * Initialize storage directories
640
650
  */
641
651
  async initialize() {
652
+ await this.migrateLegacyDefaultDir();
642
653
  await mkdir(this.trajectoriesDir, { recursive: true });
643
654
  await mkdir(this.activeDir, { recursive: true });
644
655
  await mkdir(this.completedDir, { recursive: true });
@@ -646,6 +657,17 @@ var FileStorage = class {
646
657
  await rm(join(this.trajectoriesDir, "index.json"), { force: true });
647
658
  await this.reconcileIndex();
648
659
  }
660
+ async migrateLegacyDefaultDir() {
661
+ if (!this.shouldMigrateLegacyDefault) {
662
+ return;
663
+ }
664
+ const legacyDir = join(this.baseDir, LEGACY_TRAJECTORY_DATA_DIR);
665
+ if (!existsSync(legacyDir) || existsSync(this.trajectoriesDir)) {
666
+ return;
667
+ }
668
+ await mkdir(dirname(this.trajectoriesDir), { recursive: true });
669
+ await rename(legacyDir, this.trajectoriesDir);
670
+ }
649
671
  /**
650
672
  * Scan active/ and completed/ recursively and report trajectory files
651
673
  * that can be loaded plus files that should be surfaced by doctor.
@@ -716,7 +738,7 @@ var FileStorage = class {
716
738
  return this.lastReconcileSummary;
717
739
  }
718
740
  /**
719
- * Move trajectory files that fail to load into `.trajectories/invalid/`
741
+ * Move trajectory files that fail to load into `.agentworkforce/trajectories/invalid/`
720
742
  * so reconcile no longer scans them. Only quarantines parse and schema
721
743
  * failures — transient io_error failures are left in place because the
722
744
  * file may load fine on the next attempt.
@@ -1471,7 +1493,7 @@ function loadFileConfig() {
1471
1493
  }
1472
1494
  function getPrimaryConfigDir() {
1473
1495
  const searchPaths = getSearchPaths();
1474
- return searchPaths[0] ?? join2(process.cwd(), ".trajectories");
1496
+ return searchPaths[0] ?? getDefaultTrajectoryDataDir();
1475
1497
  }
1476
1498
  function readStringEnv(name) {
1477
1499
  return readString(process.env[name]);
@@ -2118,7 +2140,7 @@ function buildCliArgs(cli) {
2118
2140
  }
2119
2141
  }
2120
2142
  function spawnWithStdin(command, args, input) {
2121
- return new Promise((resolve2, reject) => {
2143
+ return new Promise((resolve3, reject) => {
2122
2144
  const child = spawn(command, args, {
2123
2145
  timeout: 3e5,
2124
2146
  stdio: ["pipe", "pipe", "pipe"]
@@ -2136,7 +2158,7 @@ function spawnWithStdin(command, args, input) {
2136
2158
  new Error(`CLI exited with code ${code}: ${stderr.slice(0, 200)}`)
2137
2159
  );
2138
2160
  } else {
2139
- resolve2(Buffer.concat(chunks).toString().trim());
2161
+ resolve3(Buffer.concat(chunks).toString().trim());
2140
2162
  }
2141
2163
  });
2142
2164
  child.stdin.write(input);
@@ -3061,7 +3083,7 @@ function getProviderLabel(provider) {
3061
3083
  return "LLM";
3062
3084
  }
3063
3085
  function getDefaultOutputPath(compacted, workflowId) {
3064
- const trajDir = process.env.TRAJECTORIES_DATA_DIR || ".trajectories";
3086
+ const trajDir = process.env.TRAJECTORIES_DATA_DIR || getDefaultTrajectoryDataDir();
3065
3087
  const compactedDir = join4(trajDir, "compacted");
3066
3088
  if (!existsSync3(compactedDir)) {
3067
3089
  mkdirSync(compactedDir, { recursive: true });
@@ -3477,7 +3499,7 @@ if [ "$COMMIT_SOURCE" = "merge" ] || [ "$COMMIT_SOURCE" = "squash" ] || [ "$COMM
3477
3499
  fi
3478
3500
 
3479
3501
  # Find the trajectories data directory
3480
- TRAJ_DIR="\${TRAJECTORIES_DATA_DIR:-$(git rev-parse --show-toplevel)/.trajectories}"
3502
+ TRAJ_DIR="\${TRAJECTORIES_DATA_DIR:-$(git rev-parse --show-toplevel)/.agentworkforce/trajectories}"
3481
3503
  ACTIVE_DIR="$TRAJ_DIR/active"
3482
3504
 
3483
3505
  # Check if there's an active trajectory
@@ -3537,7 +3559,7 @@ function detectExistingHook() {
3537
3559
  // src/cli/commands/complete.ts
3538
3560
  async function saveTraceFile(trajectory, trace) {
3539
3561
  const dataDir = process.env.TRAJECTORIES_DATA_DIR;
3540
- const baseDir = dataDir ? dataDir : join5(process.cwd(), ".trajectories");
3562
+ const baseDir = dataDir ? dataDir : getDefaultTrajectoryDataDir();
3541
3563
  const completedDir = join5(baseDir, "completed");
3542
3564
  const date = new Date(trajectory.completedAt ?? trajectory.startedAt);
3543
3565
  const monthDir = join5(
@@ -3665,7 +3687,7 @@ function registerDoctorCommand(program2) {
3665
3687
  "List trajectory files that fail to load; optionally quarantine them"
3666
3688
  ).option(
3667
3689
  "--quarantine",
3668
- "Move invalid files to .trajectories/invalid/ so reconcile stops scanning them"
3690
+ "Move invalid files to .agentworkforce/trajectories/invalid/ so reconcile stops scanning them"
3669
3691
  ).action(async (opts) => {
3670
3692
  const storage = new FileStorage();
3671
3693
  await storage.initialize();
@@ -3683,7 +3705,7 @@ function registerDoctorCommand(program2) {
3683
3705
  }
3684
3706
  if (!opts.quarantine) {
3685
3707
  console.log(
3686
- "\nRun `trail doctor --quarantine` to move these files into .trajectories/invalid/."
3708
+ "\nRun `trail doctor --quarantine` to move these files into .agentworkforce/trajectories/invalid/."
3687
3709
  );
3688
3710
  return;
3689
3711
  }
@@ -4487,7 +4509,10 @@ function registerExportCommand(program2) {
4487
4509
  openInBrowser(options.output);
4488
4510
  }
4489
4511
  } else if (options.open && options.format === "html") {
4490
- const outputDir = join7(process.cwd(), ".trajectories", "html");
4512
+ const outputDir = join7(
4513
+ process.env.TRAJECTORIES_DATA_DIR ?? getDefaultTrajectoryDataDir(),
4514
+ "html"
4515
+ );
4491
4516
  await mkdir4(outputDir, { recursive: true });
4492
4517
  const filePath = join7(outputDir, `${trajectory.id}.html`);
4493
4518
  await writeFile4(filePath, output, "utf-8");
@@ -4864,6 +4889,155 @@ function extractDecisions2(trajectory) {
4864
4889
  return decisions;
4865
4890
  }
4866
4891
 
4892
+ // src/core/project-id.ts
4893
+ import { execFileSync as execFileSync2 } from "child_process";
4894
+ import { existsSync as existsSync8, readFileSync as readFileSync3 } from "fs";
4895
+ import { dirname as dirname3, join as join9, resolve as resolve2 } from "path";
4896
+ function resolveProjectId(explicitProjectId, options = {}) {
4897
+ return readString3(explicitProjectId) ?? readString3((options.env ?? process.env).TRAJECTORIES_PROJECT) ?? resolveDefaultProjectId(options.cwd);
4898
+ }
4899
+ function resolveDefaultProjectId(cwd = process.cwd()) {
4900
+ const packageJson = readNearestPackageJson(cwd);
4901
+ return resolvePackageRepositoryId(packageJson) ?? resolveGitRemoteProjectId(cwd) ?? resolvePackageName(packageJson);
4902
+ }
4903
+ function normalizeRepositoryId(value) {
4904
+ const raw = readString3(value);
4905
+ if (!raw) {
4906
+ return void 0;
4907
+ }
4908
+ const withoutGitPrefix = raw.replace(/^git\+/, "");
4909
+ const shorthand = withoutGitPrefix.match(
4910
+ /^(?:github|gitlab|bitbucket):([^/]+\/[^/]+(?:\/[^/]+)*)$/
4911
+ );
4912
+ if (shorthand) {
4913
+ return cleanRepositoryPath(shorthand[1]);
4914
+ }
4915
+ const ownerRepo = withoutGitPrefix.match(
4916
+ /^([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)$/
4917
+ );
4918
+ if (ownerRepo) {
4919
+ return cleanRepositoryPath(ownerRepo[1]);
4920
+ }
4921
+ const sshUrlScpLike = withoutGitPrefix.match(
4922
+ /^ssh:\/\/[^@/\s]+@[^:/\s]+:(.+)$/i
4923
+ );
4924
+ if (sshUrlScpLike) {
4925
+ return cleanRepositoryPath(sshUrlScpLike[1]);
4926
+ }
4927
+ const scpLike = withoutGitPrefix.match(/^[^@/\s]+@[^:/\s]+:(.+)$/);
4928
+ if (scpLike) {
4929
+ return cleanRepositoryPath(scpLike[1]);
4930
+ }
4931
+ let parsed;
4932
+ try {
4933
+ parsed = new URL(withoutGitPrefix);
4934
+ } catch {
4935
+ return void 0;
4936
+ }
4937
+ if (!["https:", "http:", "ssh:", "git:"].includes(parsed.protocol)) {
4938
+ return void 0;
4939
+ }
4940
+ return cleanRepositoryPath(parsed.pathname);
4941
+ }
4942
+ function resolvePackageRepositoryId(packageJson) {
4943
+ if (!packageJson) {
4944
+ return void 0;
4945
+ }
4946
+ const repository = packageJson.repository;
4947
+ if (typeof repository === "string") {
4948
+ return normalizeRepositoryId(repository);
4949
+ }
4950
+ if (isRepositoryObject(repository) && typeof repository.url === "string") {
4951
+ const projectId = normalizeRepositoryId(repository.url);
4952
+ const directory = cleanRepositoryDirectory(repository.directory);
4953
+ return directory && projectId ? `${projectId}//${directory}` : projectId;
4954
+ }
4955
+ return void 0;
4956
+ }
4957
+ function resolvePackageName(packageJson) {
4958
+ return readString3(packageJson?.name);
4959
+ }
4960
+ function resolveGitRemoteProjectId(cwd) {
4961
+ for (const remote of ["upstream", "origin"]) {
4962
+ const remoteUrl = getGitRemoteUrl(cwd, remote);
4963
+ if (!remoteUrl) {
4964
+ continue;
4965
+ }
4966
+ const projectId = normalizeRepositoryId(remoteUrl);
4967
+ if (projectId) {
4968
+ return projectId;
4969
+ }
4970
+ }
4971
+ return void 0;
4972
+ }
4973
+ function getGitRemoteUrl(cwd, remote) {
4974
+ try {
4975
+ return readString3(
4976
+ execFileSync2("git", ["config", "--get", `remote.${remote}.url`], {
4977
+ cwd,
4978
+ encoding: "utf-8",
4979
+ stdio: ["ignore", "pipe", "ignore"]
4980
+ })
4981
+ );
4982
+ } catch {
4983
+ return void 0;
4984
+ }
4985
+ }
4986
+ function readNearestPackageJson(cwd) {
4987
+ let current = resolve2(cwd);
4988
+ while (true) {
4989
+ const candidate = join9(current, "package.json");
4990
+ if (existsSync8(candidate)) {
4991
+ try {
4992
+ const parsed = JSON.parse(readFileSync3(candidate, "utf-8"));
4993
+ if (isPackageJson(parsed)) {
4994
+ return parsed;
4995
+ }
4996
+ } catch {
4997
+ }
4998
+ }
4999
+ const parent = dirname3(current);
5000
+ if (parent === current) {
5001
+ return void 0;
5002
+ }
5003
+ current = parent;
5004
+ }
5005
+ }
5006
+ function cleanRepositoryPath(path2) {
5007
+ const withoutQuery = path2.split(/[?#]/, 1)[0] ?? "";
5008
+ const withoutSlashes = withoutQuery.replace(/^\/+|\/+$/g, "");
5009
+ const withoutGitSuffix = withoutSlashes.replace(/\.git$/i, "");
5010
+ const parts = withoutGitSuffix.split("/").map((part) => part.trim()).filter(Boolean);
5011
+ if (parts.length < 2) {
5012
+ return void 0;
5013
+ }
5014
+ return parts.join("/");
5015
+ }
5016
+ function cleanRepositoryDirectory(directory) {
5017
+ const raw = readString3(directory);
5018
+ if (!raw || raw.startsWith("/") || raw.startsWith("\\") || /^[A-Za-z]:[\\/]/.test(raw)) {
5019
+ return void 0;
5020
+ }
5021
+ const parts = raw.split(/[\\/]/).map((part) => part.trim()).filter(Boolean);
5022
+ if (parts.length === 0 || parts.some((part) => part === "." || part === "..")) {
5023
+ return void 0;
5024
+ }
5025
+ return parts.join("/");
5026
+ }
5027
+ function readString3(value) {
5028
+ if (typeof value !== "string") {
5029
+ return void 0;
5030
+ }
5031
+ const trimmed = value.trim();
5032
+ return trimmed.length > 0 ? trimmed : void 0;
5033
+ }
5034
+ function isPackageJson(value) {
5035
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5036
+ }
5037
+ function isRepositoryObject(value) {
5038
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5039
+ }
5040
+
4867
5041
  // src/cli/commands/start.ts
4868
5042
  function registerStartCommand(program2) {
4869
5043
  program2.command("start <title>").description("Start a new trajectory").option("-t, --task <id>", "External task ID").option(
@@ -4894,7 +5068,7 @@ function registerStartCommand(program2) {
4894
5068
  };
4895
5069
  }
4896
5070
  const agentName = options.agent ?? process.env.TRAJECTORIES_AGENT ?? void 0;
4897
- const projectId = options.project ?? process.env.TRAJECTORIES_PROJECT ?? void 0;
5071
+ const projectId = resolveProjectId(options.project);
4898
5072
  const workflowId = typeof options.workflow === "string" && options.workflow.trim() || typeof process.env.TRAJECTORIES_WORKFLOW_ID === "string" && process.env.TRAJECTORIES_WORKFLOW_ID.trim() || void 0;
4899
5073
  const startRef = captureGitState();
4900
5074
  let trajectory = createTrajectory({