agent-trajectories 0.1.1 → 0.2.2

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
@@ -2,104 +2,41 @@
2
2
  import {
3
3
  FileStorage,
4
4
  abandonTrajectory,
5
+ addChapter,
5
6
  addDecision,
6
7
  completeTrajectory,
7
8
  createTrajectory,
8
9
  exportToJSON,
9
10
  exportToMarkdown,
10
- exportToTimeline
11
- } from "../chunk-S3DXLZQ6.js";
11
+ exportToTimeline,
12
+ getSearchPaths
13
+ } from "../chunk-2RSTDBLD.js";
12
14
 
13
15
  // src/cli/index.ts
14
16
  import { program } from "commander";
15
17
 
16
- // src/cli/commands/start.ts
17
- function registerStartCommand(program2) {
18
- program2.command("start <title>").description("Start a new trajectory").option("-t, --task <id>", "External task ID").option("-s, --source <system>", "Task system (github, linear, jira, beads)").option("--url <url>", "URL to external task").action(async (title, options) => {
19
- const storage = new FileStorage();
20
- await storage.initialize();
21
- const active = await storage.getActive();
22
- if (active) {
23
- console.error(`Error: Trajectory already active: ${active.id}`);
24
- console.error(`Complete or abandon it first with: trail complete or trail abandon`);
25
- throw new Error("Trajectory already active");
26
- }
27
- let source;
28
- if (options.task) {
29
- source = {
30
- system: options.source || "plain",
31
- id: options.task,
32
- url: options.url
33
- };
34
- }
35
- const trajectory = createTrajectory({
36
- title,
37
- source
38
- });
39
- await storage.save(trajectory);
40
- console.log(`\u2713 Trajectory started: ${trajectory.id}`);
41
- console.log(` Title: ${title}`);
42
- if (source) {
43
- console.log(` Linked to: ${source.id} (${source.system})`);
44
- }
45
- });
46
- }
47
-
48
- // src/cli/commands/status.ts
49
- function registerStatusCommand(program2) {
50
- program2.command("status").description("Show active trajectory status").action(async () => {
18
+ // src/cli/commands/abandon.ts
19
+ function registerAbandonCommand(program2) {
20
+ program2.command("abandon").description("Abandon the active trajectory").option("-r, --reason <text>", "Reason for abandonment").action(async (options) => {
51
21
  const storage = new FileStorage();
52
22
  await storage.initialize();
53
23
  const active = await storage.getActive();
54
24
  if (!active) {
55
- console.log("No active trajectory");
56
- console.log('Start one with: trail start "Task description"');
57
- return;
58
- }
59
- const duration = formatDuration(
60
- (/* @__PURE__ */ new Date()).getTime() - new Date(active.startedAt).getTime()
61
- );
62
- const eventCount = active.chapters.reduce(
63
- (sum, ch) => sum + ch.events.length,
64
- 0
65
- );
66
- const decisionCount = active.chapters.reduce(
67
- (sum, ch) => sum + ch.events.filter((e) => e.type === "decision").length,
68
- 0
69
- );
70
- console.log(`Active Trajectory: ${active.id}`);
71
- console.log(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`);
72
- console.log(`Task: ${active.task.title}`);
73
- if (active.task.source) {
74
- console.log(`Source: ${active.task.source.system}:${active.task.source.id}`);
25
+ console.error("Error: No active trajectory");
26
+ throw new Error("No active trajectory");
75
27
  }
76
- console.log(`Status: ${active.status}`);
77
- console.log(`Started: ${duration} ago`);
78
- console.log(`Chapters: ${active.chapters.length}`);
79
- console.log(`Events: ${eventCount}`);
80
- console.log(`Decisions: ${decisionCount}`);
81
- if (active.chapters.length > 0) {
82
- const currentChapter = active.chapters[active.chapters.length - 1];
83
- console.log(`
84
- Current Chapter: ${currentChapter.title}`);
85
- console.log(` Agent: ${currentChapter.agentName}`);
28
+ const abandoned = abandonTrajectory(active, options.reason);
29
+ await storage.save(abandoned);
30
+ console.log(`\u2713 Trajectory abandoned: ${abandoned.id}`);
31
+ if (options.reason) {
32
+ console.log(` Reason: ${options.reason}`);
86
33
  }
87
34
  });
88
35
  }
89
- function formatDuration(ms) {
90
- const seconds = Math.floor(ms / 1e3);
91
- const minutes = Math.floor(seconds / 60);
92
- const hours = Math.floor(minutes / 60);
93
- const days = Math.floor(hours / 24);
94
- if (days > 0) return `${days}d ${hours % 24}h`;
95
- if (hours > 0) return `${hours}h ${minutes % 60}m`;
96
- if (minutes > 0) return `${minutes}m`;
97
- return `${seconds}s`;
98
- }
99
36
 
100
37
  // src/cli/commands/complete.ts
101
38
  function registerCompleteCommand(program2) {
102
- program2.command("complete").description("Complete the active trajectory with retrospective").option("--summary <text>", "Summary of what was accomplished").option("--approach <text>", "How the work was approached").option("--confidence <number>", "Confidence level 0-1", parseFloat).action(async (options) => {
39
+ program2.command("complete").description("Complete the active trajectory with retrospective").option("--summary <text>", "Summary of what was accomplished").option("--approach <text>", "How the work was approached").option("--confidence <number>", "Confidence level 0-1", Number.parseFloat).action(async (options) => {
103
40
  const storage = new FileStorage();
104
41
  await storage.initialize();
105
42
  const active = await storage.getActive();
@@ -129,28 +66,15 @@ function registerCompleteCommand(program2) {
129
66
  });
130
67
  }
131
68
 
132
- // src/cli/commands/abandon.ts
133
- function registerAbandonCommand(program2) {
134
- program2.command("abandon").description("Abandon the active trajectory").option("-r, --reason <text>", "Reason for abandonment").action(async (options) => {
135
- const storage = new FileStorage();
136
- await storage.initialize();
137
- const active = await storage.getActive();
138
- if (!active) {
139
- console.error("Error: No active trajectory");
140
- throw new Error("No active trajectory");
141
- }
142
- const abandoned = abandonTrajectory(active, options.reason);
143
- await storage.save(abandoned);
144
- console.log(`\u2713 Trajectory abandoned: ${abandoned.id}`);
145
- if (options.reason) {
146
- console.log(` Reason: ${options.reason}`);
147
- }
148
- });
149
- }
150
-
151
69
  // src/cli/commands/decision.ts
152
70
  function registerDecisionCommand(program2) {
153
- program2.command("decision <choice>").description("Record a decision").option("-r, --reasoning <text>", "Why this choice was made (optional for minor decisions)").option("-a, --alternatives <items>", "Comma-separated alternatives considered").action(async (choice, options) => {
71
+ program2.command("decision <choice>").description("Record a decision").option(
72
+ "-r, --reasoning <text>",
73
+ "Why this choice was made (optional for minor decisions)"
74
+ ).option(
75
+ "-a, --alternatives <items>",
76
+ "Comma-separated alternatives considered"
77
+ ).action(async (choice, options) => {
154
78
  const storage = new FileStorage();
155
79
  await storage.initialize();
156
80
  const active = await storage.getActive();
@@ -178,149 +102,10 @@ function registerDecisionCommand(program2) {
178
102
  });
179
103
  }
180
104
 
181
- // src/cli/commands/list.ts
182
- function registerListCommand(program2) {
183
- program2.command("list").description("List and search trajectories").option("-s, --status <status>", "Filter by status (active, completed, abandoned)").option("-l, --limit <number>", "Limit results", parseInt).option("--search <query>", "Search trajectories by title or content").action(async (options) => {
184
- const storage = new FileStorage();
185
- await storage.initialize();
186
- let trajectories = await storage.list({
187
- status: options.status,
188
- limit: options.search ? void 0 : options.limit
189
- // Apply limit after search
190
- });
191
- if (options.search) {
192
- const query = options.search.toLowerCase();
193
- trajectories = trajectories.filter((traj) => {
194
- if (traj.title.toLowerCase().includes(query)) return true;
195
- if (traj.id.toLowerCase().includes(query)) return true;
196
- return false;
197
- });
198
- if (options.limit) {
199
- trajectories = trajectories.slice(0, options.limit);
200
- }
201
- }
202
- if (trajectories.length === 0) {
203
- if (options.search) {
204
- console.log(`No trajectories found matching "${options.search}"`);
205
- } else {
206
- console.log("No trajectories found");
207
- }
208
- return;
209
- }
210
- const searchNote = options.search ? ` matching "${options.search}"` : "";
211
- console.log(`Found ${trajectories.length} trajectories${searchNote}:
212
- `);
213
- for (const traj of trajectories) {
214
- const statusIcon = getStatusIcon(traj.status);
215
- const confidence = traj.confidence ? ` (${Math.round(traj.confidence * 100)}%)` : "";
216
- console.log(`${statusIcon} ${traj.id}`);
217
- console.log(` ${traj.title}${confidence}`);
218
- console.log(` Started: ${formatDate(traj.startedAt)}`);
219
- if (traj.completedAt) {
220
- console.log(` Completed: ${formatDate(traj.completedAt)}`);
221
- }
222
- console.log("");
223
- }
224
- });
225
- }
226
- function getStatusIcon(status) {
227
- switch (status) {
228
- case "active":
229
- return "\u{1F504}";
230
- case "completed":
231
- return "\u2705";
232
- case "abandoned":
233
- return "\u274C";
234
- default:
235
- return "\u2022";
236
- }
237
- }
238
- function formatDate(isoString) {
239
- return new Date(isoString).toLocaleDateString("en-US", {
240
- month: "short",
241
- day: "numeric",
242
- year: "numeric"
243
- });
244
- }
245
-
246
- // src/cli/commands/show.ts
247
- function registerShowCommand(program2) {
248
- program2.command("show <id>").description("Show trajectory details").option("-d, --decisions", "Show decisions only").action(async (id, options) => {
249
- const storage = new FileStorage();
250
- await storage.initialize();
251
- const trajectory = await storage.get(id);
252
- if (!trajectory) {
253
- console.error(`Error: Trajectory not found: ${id}`);
254
- throw new Error("Trajectory not found");
255
- }
256
- if (options.decisions) {
257
- const decisions = extractDecisions(trajectory);
258
- if (decisions.length === 0) {
259
- console.log("No decisions recorded");
260
- return;
261
- }
262
- console.log(`Decisions for ${trajectory.task.title}:
263
- `);
264
- for (const decision of decisions) {
265
- console.log(`\u2022 ${decision.question}`);
266
- console.log(` Chose: ${decision.chosen}`);
267
- console.log(` Reasoning: ${decision.reasoning}`);
268
- if (decision.alternatives.length > 0) {
269
- console.log(` Alternatives: ${decision.alternatives.join(", ")}`);
270
- }
271
- console.log("");
272
- }
273
- return;
274
- }
275
- console.log(`Trajectory: ${trajectory.id}`);
276
- console.log(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`);
277
- console.log(`Title: ${trajectory.task.title}`);
278
- console.log(`Status: ${trajectory.status}`);
279
- console.log(`Started: ${trajectory.startedAt}`);
280
- if (trajectory.completedAt) {
281
- console.log(`Ended: ${trajectory.completedAt}`);
282
- }
283
- if (trajectory.task.source) {
284
- console.log(`Source: ${trajectory.task.source.system}:${trajectory.task.source.id}`);
285
- }
286
- console.log(`
287
- Chapters: ${trajectory.chapters.length}`);
288
- for (const chapter of trajectory.chapters) {
289
- console.log(` \u2022 ${chapter.title} (${chapter.agentName})`);
290
- console.log(` Events: ${chapter.events.length}`);
291
- }
292
- if (trajectory.retrospective) {
293
- console.log(`
294
- Retrospective:`);
295
- console.log(` Summary: ${trajectory.retrospective.summary}`);
296
- console.log(` Confidence: ${Math.round(trajectory.retrospective.confidence * 100)}%`);
297
- }
298
- });
299
- }
300
- function extractDecisions(trajectory) {
301
- const decisions = [];
302
- if (trajectory.retrospective?.decisions) {
303
- decisions.push(...trajectory.retrospective.decisions);
304
- }
305
- for (const chapter of trajectory.chapters) {
306
- for (const event of chapter.events) {
307
- if (event.type === "decision" && event.raw) {
308
- const raw = event.raw;
309
- if (raw.question && raw.chosen && raw.reasoning) {
310
- if (!decisions.some((d) => d.question === raw.question)) {
311
- decisions.push(raw);
312
- }
313
- }
314
- }
315
- }
316
- }
317
- return decisions;
318
- }
319
-
320
105
  // src/cli/commands/export.ts
321
- import { writeFile, mkdir } from "fs/promises";
322
- import { join } from "path";
323
106
  import { exec } from "child_process";
107
+ import { mkdir, writeFile } from "fs/promises";
108
+ import { join } from "path";
324
109
 
325
110
  // src/web/styles.ts
326
111
  var styles = `
@@ -679,7 +464,7 @@ h2 {
679
464
  function escapeHtml(text) {
680
465
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
681
466
  }
682
- function formatDate2(isoDate) {
467
+ function formatDate(isoDate) {
683
468
  const date = new Date(isoDate);
684
469
  return date.toLocaleDateString("en-US", {
685
470
  month: "short",
@@ -689,7 +474,7 @@ function formatDate2(isoDate) {
689
474
  minute: "2-digit"
690
475
  });
691
476
  }
692
- function formatDuration2(startDate, endDate) {
477
+ function formatDuration(startDate, endDate) {
693
478
  const start = new Date(startDate).getTime();
694
479
  const end = endDate ? new Date(endDate).getTime() : Date.now();
695
480
  const ms = end - start;
@@ -721,33 +506,43 @@ function renderDecision(decision) {
721
506
  </div>` : "";
722
507
  return `
723
508
  <div class="decision">
724
- <div class="decision-title">${escapeHtml(decision.title)}</div>
509
+ <div class="decision-title">${escapeHtml(decision.question)}: ${escapeHtml(decision.chosen)}</div>
725
510
  <div class="decision-reasoning">${escapeHtml(decision.reasoning)}</div>
726
511
  ${alternatives}
727
512
  </div>
728
513
  `;
729
514
  }
730
515
  function renderEvent(event) {
731
- const time = formatDate2(event.timestamp);
516
+ const time = formatDate(new Date(event.ts).toISOString());
732
517
  let content = "";
733
518
  let typeClass = "";
519
+ const rawData = event.raw;
734
520
  switch (event.type) {
735
521
  case "decision":
736
522
  typeClass = "decision";
737
523
  content = `
738
524
  <strong>Decision:</strong> ${escapeHtml(event.content)}
739
- ${event.metadata?.reasoning ? `<div class="decision-reasoning">${escapeHtml(event.metadata.reasoning)}</div>` : ""}
525
+ ${rawData?.reasoning ? `<div class="decision-reasoning">${escapeHtml(String(rawData.reasoning))}</div>` : ""}
740
526
  `;
741
527
  break;
742
- case "observation":
743
- content = `<strong>Observed:</strong> ${escapeHtml(event.content)}`;
528
+ case "thinking":
529
+ content = `<strong>Thinking:</strong> ${escapeHtml(event.content)}`;
744
530
  break;
745
- case "action":
746
- content = `<strong>Action:</strong> ${escapeHtml(event.content)}`;
531
+ case "prompt":
532
+ content = `<strong>Prompt:</strong> ${escapeHtml(event.content)}`;
747
533
  break;
748
534
  case "tool_call":
749
535
  content = `<strong>Tool:</strong> <code>${escapeHtml(event.content)}</code>`;
750
536
  break;
537
+ case "tool_result":
538
+ content = `<strong>Result:</strong> ${escapeHtml(event.content)}`;
539
+ break;
540
+ case "message_sent":
541
+ content = `<strong>Sent:</strong> ${escapeHtml(event.content)}`;
542
+ break;
543
+ case "message_received":
544
+ content = `<strong>Received:</strong> ${escapeHtml(event.content)}`;
545
+ break;
751
546
  case "error":
752
547
  content = `<strong style="color: var(--error)">Error:</strong> ${escapeHtml(event.content)}`;
753
548
  break;
@@ -772,7 +567,6 @@ function renderChapter(chapter, index) {
772
567
  Chapter ${index + 1}: ${escapeHtml(chapter.title)}
773
568
  </div>
774
569
  <div class="chapter-agent">Agent: ${escapeHtml(chapter.agentName)}</div>
775
- ${chapter.summary ? `<p>${escapeHtml(chapter.summary)}</p>` : ""}
776
570
  ${chapter.events.length > 0 ? `
777
571
  <h3 class="collapsible" onclick="this.classList.toggle('open')">Events (${chapter.events.length})</h3>
778
572
  <div class="collapsible-content">
@@ -788,9 +582,10 @@ function renderRetrospective(trajectory) {
788
582
  }
789
583
  const retro = trajectory.retrospective;
790
584
  const confidencePercent = Math.round(retro.confidence * 100);
791
- const wentWell = retro.wentWell?.length ? `<div><strong>What went well:</strong><ul class="list">${retro.wentWell.map((w) => `<li>${escapeHtml(w)}</li>`).join("")}</ul></div>` : "";
585
+ const approach = retro.approach ? `<div><strong>Approach:</strong><p>${escapeHtml(retro.approach)}</p></div>` : "";
586
+ const learnings = retro.learnings?.length ? `<div><strong>Learnings:</strong><ul class="list">${retro.learnings.map((l) => `<li>${escapeHtml(l)}</li>`).join("")}</ul></div>` : "";
792
587
  const challenges = retro.challenges?.length ? `<div><strong>Challenges:</strong><ul class="list">${retro.challenges.map((c) => `<li>${escapeHtml(c)}</li>`).join("")}</ul></div>` : "";
793
- const wouldDoDifferently = retro.wouldDoDifferently?.length ? `<div><strong>Would do differently:</strong><ul class="list">${retro.wouldDoDifferently.map((w) => `<li>${escapeHtml(w)}</li>`).join("")}</ul></div>` : "";
588
+ const suggestions = retro.suggestions?.length ? `<div><strong>Suggestions:</strong><ul class="list">${retro.suggestions.map((s) => `<li>${escapeHtml(s)}</li>`).join("")}</ul></div>` : "";
794
589
  return `
795
590
  <div class="retrospective">
796
591
  <h3>\u{1F4DD} Retrospective</h3>
@@ -804,21 +599,20 @@ function renderRetrospective(trajectory) {
804
599
  <span>${confidencePercent}%</span>
805
600
  </div>
806
601
 
807
- ${wentWell}
602
+ ${approach}
603
+ ${learnings}
808
604
  ${challenges}
809
- ${wouldDoDifferently}
605
+ ${suggestions}
810
606
  </div>
811
607
  `;
812
608
  }
813
609
  function generateTrajectoryHtml(trajectory) {
814
610
  const statusClass = getStatusClass(trajectory.status);
815
- const duration = formatDuration2(trajectory.startedAt, trajectory.completedAt);
611
+ const duration = formatDuration(trajectory.startedAt, trajectory.completedAt);
816
612
  const decisions = trajectory.chapters.flatMap(
817
- (ch) => ch.events.filter((e) => e.type === "decision").map((e) => ({
818
- title: e.content,
819
- reasoning: e.metadata?.reasoning || "",
820
- alternatives: e.metadata?.alternatives
821
- }))
613
+ (ch) => ch.events.filter((e) => e.type === "decision" && e.raw).map((e) => e.raw).filter(
614
+ (d) => d !== void 0 && typeof d.question === "string"
615
+ )
822
616
  );
823
617
  const decisionsHtml = decisions.length ? `
824
618
  <h2 class="collapsible open" onclick="this.classList.toggle('open')">
@@ -874,7 +668,7 @@ function generateTrajectoryHtml(trajectory) {
874
668
  </div>
875
669
  <div class="meta-item">
876
670
  <span class="meta-label">Started</span>
877
- <span class="meta-value">${formatDate2(trajectory.startedAt)}</span>
671
+ <span class="meta-value">${formatDate(trajectory.startedAt)}</span>
878
672
  </div>
879
673
  ${trajectory.task.source ? `
880
674
  <div class="meta-item">
@@ -907,7 +701,11 @@ function generateTrajectoryHtml(trajectory) {
907
701
 
908
702
  // src/cli/commands/export.ts
909
703
  function registerExportCommand(program2) {
910
- program2.command("export [id]").description("Export a trajectory").option("-f, --format <format>", "Export format (md, json, timeline, html)", "md").option("-o, --output <path>", "Output file path").option("--open", "Open in browser (html format only)").action(async (id, options) => {
704
+ program2.command("export [id]").description("Export a trajectory").option(
705
+ "-f, --format <format>",
706
+ "Export format (md, json, timeline, html)",
707
+ "md"
708
+ ).option("-o, --output <path>", "Output file path").option("--open", "Open in browser (html format only)").action(async (id, options) => {
911
709
  const storage = new FileStorage();
912
710
  await storage.initialize();
913
711
  let trajectory;
@@ -921,7 +719,9 @@ function registerExportCommand(program2) {
921
719
  trajectory = await storage.getActive();
922
720
  if (!trajectory) {
923
721
  console.error("Error: No active trajectory and no ID provided");
924
- console.error("Usage: trail export <id> or trail export (with active trajectory)");
722
+ console.error(
723
+ "Usage: trail export <id> or trail export (with active trajectory)"
724
+ );
925
725
  throw new Error("No trajectory specified");
926
726
  }
927
727
  }
@@ -936,8 +736,6 @@ function registerExportCommand(program2) {
936
736
  case "html":
937
737
  output = generateTrajectoryHtml(trajectory);
938
738
  break;
939
- case "md":
940
- case "markdown":
941
739
  default:
942
740
  output = exportToMarkdown(trajectory);
943
741
  break;
@@ -977,6 +775,317 @@ function openInBrowser(path) {
977
775
  });
978
776
  }
979
777
 
778
+ // src/cli/commands/list.ts
779
+ import { existsSync } from "fs";
780
+ function registerListCommand(program2) {
781
+ program2.command("list").description("List and search trajectories").option(
782
+ "-s, --status <status>",
783
+ "Filter by status (active, completed, abandoned)"
784
+ ).option("-l, --limit <number>", "Limit results", Number.parseInt).option("--search <query>", "Search trajectories by title or content").action(async (options) => {
785
+ const searchPaths = getSearchPaths();
786
+ let allTrajectories = [];
787
+ const seenIds = /* @__PURE__ */ new Set();
788
+ for (const searchPath of searchPaths) {
789
+ if (!existsSync(searchPath)) {
790
+ continue;
791
+ }
792
+ const originalDataDir = process.env.TRAJECTORIES_DATA_DIR;
793
+ process.env.TRAJECTORIES_DATA_DIR = searchPath;
794
+ try {
795
+ const storage = new FileStorage();
796
+ await storage.initialize();
797
+ const trajectories = await storage.list({
798
+ status: options.status,
799
+ limit: options.search ? void 0 : void 0
800
+ // Don't limit per-path
801
+ });
802
+ for (const traj of trajectories) {
803
+ if (!seenIds.has(traj.id)) {
804
+ seenIds.add(traj.id);
805
+ allTrajectories.push(traj);
806
+ }
807
+ }
808
+ } finally {
809
+ if (originalDataDir !== void 0) {
810
+ process.env.TRAJECTORIES_DATA_DIR = originalDataDir;
811
+ } else {
812
+ process.env.TRAJECTORIES_DATA_DIR = void 0;
813
+ }
814
+ }
815
+ }
816
+ allTrajectories.sort(
817
+ (a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()
818
+ );
819
+ if (options.search) {
820
+ const query = options.search.toLowerCase();
821
+ allTrajectories = allTrajectories.filter((traj) => {
822
+ if (traj.title.toLowerCase().includes(query)) return true;
823
+ if (traj.id.toLowerCase().includes(query)) return true;
824
+ return false;
825
+ });
826
+ }
827
+ if (options.limit) {
828
+ allTrajectories = allTrajectories.slice(0, options.limit);
829
+ }
830
+ if (allTrajectories.length === 0) {
831
+ if (options.search) {
832
+ console.log(`No trajectories found matching "${options.search}"`);
833
+ } else {
834
+ console.log("No trajectories found");
835
+ }
836
+ return;
837
+ }
838
+ const searchNote = options.search ? ` matching "${options.search}"` : "";
839
+ console.log(
840
+ `Found ${allTrajectories.length} trajectories${searchNote}:
841
+ `
842
+ );
843
+ for (const traj of allTrajectories) {
844
+ const statusIcon = getStatusIcon(traj.status);
845
+ const confidence = traj.confidence ? ` (${Math.round(traj.confidence * 100)}%)` : "";
846
+ console.log(`${statusIcon} ${traj.id}`);
847
+ console.log(` ${traj.title}${confidence}`);
848
+ console.log(` Started: ${formatDate2(traj.startedAt)}`);
849
+ if (traj.completedAt) {
850
+ console.log(` Completed: ${formatDate2(traj.completedAt)}`);
851
+ }
852
+ console.log("");
853
+ }
854
+ });
855
+ }
856
+ function getStatusIcon(status) {
857
+ switch (status) {
858
+ case "active":
859
+ return "\u{1F504}";
860
+ case "completed":
861
+ return "\u2705";
862
+ case "abandoned":
863
+ return "\u274C";
864
+ default:
865
+ return "\u2022";
866
+ }
867
+ }
868
+ function formatDate2(isoString) {
869
+ return new Date(isoString).toLocaleDateString("en-US", {
870
+ month: "short",
871
+ day: "numeric",
872
+ year: "numeric"
873
+ });
874
+ }
875
+
876
+ // src/cli/commands/show.ts
877
+ import { existsSync as existsSync2 } from "fs";
878
+ async function findTrajectory(id) {
879
+ const searchPaths = getSearchPaths();
880
+ for (const searchPath of searchPaths) {
881
+ if (!existsSync2(searchPath)) {
882
+ continue;
883
+ }
884
+ const originalDataDir = process.env.TRAJECTORIES_DATA_DIR;
885
+ process.env.TRAJECTORIES_DATA_DIR = searchPath;
886
+ try {
887
+ const storage = new FileStorage();
888
+ await storage.initialize();
889
+ const trajectory = await storage.get(id);
890
+ if (trajectory) {
891
+ return trajectory;
892
+ }
893
+ } finally {
894
+ if (originalDataDir !== void 0) {
895
+ process.env.TRAJECTORIES_DATA_DIR = originalDataDir;
896
+ } else {
897
+ process.env.TRAJECTORIES_DATA_DIR = void 0;
898
+ }
899
+ }
900
+ }
901
+ return null;
902
+ }
903
+ function registerShowCommand(program2) {
904
+ program2.command("show <id>").description("Show trajectory details").option("-d, --decisions", "Show decisions only").action(async (id, options) => {
905
+ const trajectory = await findTrajectory(id);
906
+ if (!trajectory) {
907
+ console.error(`Error: Trajectory not found: ${id}`);
908
+ throw new Error("Trajectory not found");
909
+ }
910
+ if (options.decisions) {
911
+ const decisions = extractDecisions(trajectory);
912
+ if (decisions.length === 0) {
913
+ console.log("No decisions recorded");
914
+ return;
915
+ }
916
+ console.log(`Decisions for ${trajectory.task.title}:
917
+ `);
918
+ for (const decision of decisions) {
919
+ console.log(`\u2022 ${decision.question}`);
920
+ console.log(` Chose: ${decision.chosen}`);
921
+ console.log(` Reasoning: ${decision.reasoning}`);
922
+ if (decision.alternatives.length > 0) {
923
+ console.log(` Alternatives: ${decision.alternatives.join(", ")}`);
924
+ }
925
+ console.log("");
926
+ }
927
+ return;
928
+ }
929
+ console.log(`Trajectory: ${trajectory.id}`);
930
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
931
+ console.log(`Title: ${trajectory.task.title}`);
932
+ console.log(`Status: ${trajectory.status}`);
933
+ console.log(`Started: ${trajectory.startedAt}`);
934
+ if (trajectory.completedAt) {
935
+ console.log(`Ended: ${trajectory.completedAt}`);
936
+ }
937
+ if (trajectory.task.source) {
938
+ console.log(
939
+ `Source: ${trajectory.task.source.system}:${trajectory.task.source.id}`
940
+ );
941
+ }
942
+ console.log(`
943
+ Chapters: ${trajectory.chapters.length}`);
944
+ for (const chapter of trajectory.chapters) {
945
+ console.log(` \u2022 ${chapter.title} (${chapter.agentName})`);
946
+ console.log(` Events: ${chapter.events.length}`);
947
+ }
948
+ if (trajectory.retrospective) {
949
+ console.log("\nRetrospective:");
950
+ console.log(` Summary: ${trajectory.retrospective.summary}`);
951
+ console.log(
952
+ ` Confidence: ${Math.round(trajectory.retrospective.confidence * 100)}%`
953
+ );
954
+ }
955
+ });
956
+ }
957
+ function extractDecisions(trajectory) {
958
+ const decisions = [];
959
+ if (trajectory.retrospective?.decisions) {
960
+ decisions.push(...trajectory.retrospective.decisions);
961
+ }
962
+ for (const chapter of trajectory.chapters) {
963
+ for (const event of chapter.events) {
964
+ if (event.type === "decision" && event.raw) {
965
+ const raw = event.raw;
966
+ if (raw.question && raw.chosen && raw.reasoning) {
967
+ if (!decisions.some((d) => d.question === raw.question)) {
968
+ decisions.push(raw);
969
+ }
970
+ }
971
+ }
972
+ }
973
+ }
974
+ return decisions;
975
+ }
976
+
977
+ // src/cli/commands/start.ts
978
+ function registerStartCommand(program2) {
979
+ program2.command("start <title>").description("Start a new trajectory").option("-t, --task <id>", "External task ID").option(
980
+ "-s, --source <system>",
981
+ "Task system (github, linear, jira, beads)"
982
+ ).option("--url <url>", "URL to external task").option("-a, --agent <name>", "Agent name (or set TRAJECTORIES_AGENT)").option("-p, --project <id>", "Project ID (or set TRAJECTORIES_PROJECT)").option("-q, --quiet", "Only output trajectory ID (for scripting)").action(async (title, options) => {
983
+ const storage = new FileStorage();
984
+ await storage.initialize();
985
+ const active = await storage.getActive();
986
+ if (active) {
987
+ if (!options.quiet) {
988
+ console.error(`Error: Trajectory already active: ${active.id}`);
989
+ console.error(
990
+ "Complete or abandon it first with: trail complete or trail abandon"
991
+ );
992
+ }
993
+ throw new Error("Trajectory already active");
994
+ }
995
+ let source;
996
+ if (options.task) {
997
+ source = {
998
+ system: options.source || "plain",
999
+ id: options.task,
1000
+ url: options.url
1001
+ };
1002
+ }
1003
+ const agentName = options.agent ?? process.env.TRAJECTORIES_AGENT ?? void 0;
1004
+ const projectId = options.project ?? process.env.TRAJECTORIES_PROJECT ?? void 0;
1005
+ let trajectory = createTrajectory({
1006
+ title,
1007
+ source,
1008
+ projectId
1009
+ });
1010
+ if (agentName) {
1011
+ trajectory = addChapter(trajectory, {
1012
+ title: "Initial work",
1013
+ agentName
1014
+ });
1015
+ }
1016
+ await storage.save(trajectory);
1017
+ if (options.quiet) {
1018
+ console.log(trajectory.id);
1019
+ } else {
1020
+ console.log(`\u2713 Trajectory started: ${trajectory.id}`);
1021
+ console.log(` Title: ${title}`);
1022
+ if (agentName) {
1023
+ console.log(` Agent: ${agentName}`);
1024
+ }
1025
+ if (projectId) {
1026
+ console.log(` Project: ${projectId}`);
1027
+ }
1028
+ if (source) {
1029
+ console.log(` Linked to: ${source.id} (${source.system})`);
1030
+ }
1031
+ }
1032
+ });
1033
+ }
1034
+
1035
+ // src/cli/commands/status.ts
1036
+ function registerStatusCommand(program2) {
1037
+ program2.command("status").description("Show active trajectory status").action(async () => {
1038
+ const storage = new FileStorage();
1039
+ await storage.initialize();
1040
+ const active = await storage.getActive();
1041
+ if (!active) {
1042
+ console.log("No active trajectory");
1043
+ console.log('Start one with: trail start "Task description"');
1044
+ return;
1045
+ }
1046
+ const duration = formatDuration2(
1047
+ (/* @__PURE__ */ new Date()).getTime() - new Date(active.startedAt).getTime()
1048
+ );
1049
+ const eventCount = active.chapters.reduce(
1050
+ (sum, ch) => sum + ch.events.length,
1051
+ 0
1052
+ );
1053
+ const decisionCount = active.chapters.reduce(
1054
+ (sum, ch) => sum + ch.events.filter((e) => e.type === "decision").length,
1055
+ 0
1056
+ );
1057
+ console.log(`Active Trajectory: ${active.id}`);
1058
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
1059
+ console.log(`Task: ${active.task.title}`);
1060
+ if (active.task.source) {
1061
+ console.log(
1062
+ `Source: ${active.task.source.system}:${active.task.source.id}`
1063
+ );
1064
+ }
1065
+ console.log(`Status: ${active.status}`);
1066
+ console.log(`Started: ${duration} ago`);
1067
+ console.log(`Chapters: ${active.chapters.length}`);
1068
+ console.log(`Events: ${eventCount}`);
1069
+ console.log(`Decisions: ${decisionCount}`);
1070
+ if (active.chapters.length > 0) {
1071
+ const currentChapter = active.chapters[active.chapters.length - 1];
1072
+ console.log(`
1073
+ Current Chapter: ${currentChapter.title}`);
1074
+ console.log(` Agent: ${currentChapter.agentName}`);
1075
+ }
1076
+ });
1077
+ }
1078
+ function formatDuration2(ms) {
1079
+ const seconds = Math.floor(ms / 1e3);
1080
+ const minutes = Math.floor(seconds / 60);
1081
+ const hours = Math.floor(minutes / 60);
1082
+ const days = Math.floor(hours / 24);
1083
+ if (days > 0) return `${days}d ${hours % 24}h`;
1084
+ if (hours > 0) return `${hours}h ${minutes % 60}m`;
1085
+ if (minutes > 0) return `${minutes}m`;
1086
+ return `${seconds}s`;
1087
+ }
1088
+
980
1089
  // src/cli/commands/index.ts
981
1090
  function registerCommands(program2) {
982
1091
  registerStartCommand(program2);
@@ -990,7 +1099,15 @@ function registerCommands(program2) {
990
1099
  }
991
1100
 
992
1101
  // src/cli/index.ts
993
- program.name("trail").description("Leave a trail of your work for others to follow").version("0.1.0");
1102
+ program.name("trail").description("Leave a trail of your work for others to follow").version("0.1.0").option(
1103
+ "--data-dir <path>",
1104
+ "Override trajectory storage directory (or set TRAJECTORIES_DATA_DIR)"
1105
+ ).hook("preAction", (thisCommand) => {
1106
+ const opts = thisCommand.opts();
1107
+ if (opts.dataDir) {
1108
+ process.env.TRAJECTORIES_DATA_DIR = opts.dataDir;
1109
+ }
1110
+ });
994
1111
  registerCommands(program);
995
1112
  program.parse();
996
1113
  //# sourceMappingURL=index.js.map