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/{chunk-S3DXLZQ6.js → chunk-2RSTDBLD.js} +39 -9
- package/dist/chunk-2RSTDBLD.js.map +1 -0
- package/dist/cli/index.js +382 -265
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +14 -14
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -4
- package/dist/chunk-S3DXLZQ6.js.map +0 -1
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
|
-
|
|
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/
|
|
17
|
-
function
|
|
18
|
-
program2.command("
|
|
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.
|
|
56
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
console.log(
|
|
79
|
-
|
|
80
|
-
|
|
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(
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
681
466
|
}
|
|
682
|
-
function
|
|
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
|
|
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.
|
|
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 =
|
|
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
|
-
${
|
|
525
|
+
${rawData?.reasoning ? `<div class="decision-reasoning">${escapeHtml(String(rawData.reasoning))}</div>` : ""}
|
|
740
526
|
`;
|
|
741
527
|
break;
|
|
742
|
-
case "
|
|
743
|
-
content = `<strong>
|
|
528
|
+
case "thinking":
|
|
529
|
+
content = `<strong>Thinking:</strong> ${escapeHtml(event.content)}`;
|
|
744
530
|
break;
|
|
745
|
-
case "
|
|
746
|
-
content = `<strong>
|
|
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
|
|
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
|
|
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
|
-
${
|
|
602
|
+
${approach}
|
|
603
|
+
${learnings}
|
|
808
604
|
${challenges}
|
|
809
|
-
${
|
|
605
|
+
${suggestions}
|
|
810
606
|
</div>
|
|
811
607
|
`;
|
|
812
608
|
}
|
|
813
609
|
function generateTrajectoryHtml(trajectory) {
|
|
814
610
|
const statusClass = getStatusClass(trajectory.status);
|
|
815
|
-
const duration =
|
|
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
|
-
|
|
819
|
-
|
|
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">${
|
|
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(
|
|
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(
|
|
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
|