agent-trajectories 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.beads/.local_version +1 -0
  2. package/.beads/README.md +81 -0
  3. package/.beads/config.yaml +62 -0
  4. package/.beads/issues.jsonl +0 -0
  5. package/.beads/metadata.json +4 -0
  6. package/.gitattributes +3 -0
  7. package/IMPLEMENTATION-PROPOSAL.md +1598 -0
  8. package/PROPOSAL-trajectories.md +1582 -0
  9. package/README.md +275 -0
  10. package/biome.json +20 -0
  11. package/docs/architecture/core.md +477 -0
  12. package/docs/architecture/memory-integration.md +186 -0
  13. package/docs/architecture/web-viewer.md +213 -0
  14. package/package.json +47 -0
  15. package/src/cli/commands/abandon.ts +32 -0
  16. package/src/cli/commands/complete.ts +51 -0
  17. package/src/cli/commands/decision.ts +49 -0
  18. package/src/cli/commands/export.ts +100 -0
  19. package/src/cli/commands/index.ts +39 -0
  20. package/src/cli/commands/list.ts +90 -0
  21. package/src/cli/commands/show.ts +98 -0
  22. package/src/cli/commands/start.ts +53 -0
  23. package/src/cli/commands/status.ts +68 -0
  24. package/src/cli/index.ts +25 -0
  25. package/src/cli/runner.ts +67 -0
  26. package/src/core/id.ts +62 -0
  27. package/src/core/index.ts +54 -0
  28. package/src/core/schema.ts +272 -0
  29. package/src/core/trajectory.ts +283 -0
  30. package/src/core/types.ts +272 -0
  31. package/src/export/index.ts +10 -0
  32. package/src/export/json.ts +26 -0
  33. package/src/export/markdown.ts +216 -0
  34. package/src/export/pr-summary.ts +57 -0
  35. package/src/export/timeline.ts +69 -0
  36. package/src/index.ts +69 -0
  37. package/src/storage/file.ts +394 -0
  38. package/src/storage/index.ts +6 -0
  39. package/src/storage/interface.ts +92 -0
  40. package/src/web/generator.ts +347 -0
  41. package/src/web/index.ts +6 -0
  42. package/src/web/styles.ts +355 -0
  43. package/src/workspace/index.ts +6 -0
  44. package/src/workspace/storage.ts +231 -0
  45. package/src/workspace/types.ts +88 -0
  46. package/tests/cli/commands.test.ts +476 -0
  47. package/tests/core/trajectory.test.ts +426 -0
  48. package/tests/export/export.test.ts +376 -0
  49. package/tests/storage/storage.test.ts +410 -0
  50. package/tests/web/generator.test.ts +197 -0
  51. package/tests/workspace/storage.test.ts +275 -0
  52. package/tsconfig.json +26 -0
  53. package/tsup.config.ts +14 -0
  54. package/vitest.config.ts +15 -0
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Workspace storage - persists decisions, patterns, knowledge
3
+ */
4
+
5
+ import { mkdir, readFile, writeFile, readdir } from "node:fs/promises";
6
+ import { existsSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import type {
9
+ Workspace,
10
+ WorkspaceDecision,
11
+ WorkspacePattern,
12
+ WorkspaceKnowledge,
13
+ WorkspaceQuery,
14
+ WorkspaceQueryResult,
15
+ } from "./types.js";
16
+ import { generateRandomId } from "../core/id.js";
17
+
18
+ export class WorkspaceStorage {
19
+ private basePath: string;
20
+ private decisionsPath: string;
21
+ private patternsPath: string;
22
+ private knowledgePath: string;
23
+
24
+ constructor(basePath?: string) {
25
+ this.basePath = basePath || join(process.cwd(), ".agent-workspace");
26
+ this.decisionsPath = join(this.basePath, "decisions");
27
+ this.patternsPath = join(this.basePath, "patterns");
28
+ this.knowledgePath = join(this.basePath, "knowledge");
29
+ }
30
+
31
+ async initialize(): Promise<void> {
32
+ await mkdir(this.decisionsPath, { recursive: true });
33
+ await mkdir(this.patternsPath, { recursive: true });
34
+ await mkdir(this.knowledgePath, { recursive: true });
35
+ }
36
+
37
+ // Decisions
38
+
39
+ async addDecision(
40
+ decision: Omit<WorkspaceDecision, "id">
41
+ ): Promise<WorkspaceDecision> {
42
+ const id = `dec_${generateRandomId()}`;
43
+ const full: WorkspaceDecision = { ...decision, id };
44
+ await writeFile(
45
+ join(this.decisionsPath, `${id}.json`),
46
+ JSON.stringify(full, null, 2)
47
+ );
48
+ return full;
49
+ }
50
+
51
+ async getDecision(id: string): Promise<WorkspaceDecision | null> {
52
+ const path = join(this.decisionsPath, `${id}.json`);
53
+ if (!existsSync(path)) return null;
54
+ const content = await readFile(path, "utf-8");
55
+ return JSON.parse(content);
56
+ }
57
+
58
+ async listDecisions(): Promise<WorkspaceDecision[]> {
59
+ if (!existsSync(this.decisionsPath)) return [];
60
+ const files = await readdir(this.decisionsPath);
61
+ const decisions: WorkspaceDecision[] = [];
62
+ for (const file of files) {
63
+ if (file.endsWith(".json")) {
64
+ const content = await readFile(join(this.decisionsPath, file), "utf-8");
65
+ decisions.push(JSON.parse(content));
66
+ }
67
+ }
68
+ return decisions.sort(
69
+ (a, b) =>
70
+ new Date(b.sourceDate).getTime() - new Date(a.sourceDate).getTime()
71
+ );
72
+ }
73
+
74
+ async updateDecision(
75
+ id: string,
76
+ updates: Partial<WorkspaceDecision>
77
+ ): Promise<WorkspaceDecision | null> {
78
+ const existing = await this.getDecision(id);
79
+ if (!existing) return null;
80
+ const updated = { ...existing, ...updates, id };
81
+ await writeFile(
82
+ join(this.decisionsPath, `${id}.json`),
83
+ JSON.stringify(updated, null, 2)
84
+ );
85
+ return updated;
86
+ }
87
+
88
+ // Patterns
89
+
90
+ async addPattern(
91
+ pattern: Omit<WorkspacePattern, "id" | "createdAt" | "updatedAt">
92
+ ): Promise<WorkspacePattern> {
93
+ const id = `pat_${generateRandomId()}`;
94
+ const now = new Date().toISOString();
95
+ const full: WorkspacePattern = {
96
+ ...pattern,
97
+ id,
98
+ createdAt: now,
99
+ updatedAt: now,
100
+ };
101
+ await writeFile(
102
+ join(this.patternsPath, `${id}.json`),
103
+ JSON.stringify(full, null, 2)
104
+ );
105
+ return full;
106
+ }
107
+
108
+ async getPattern(id: string): Promise<WorkspacePattern | null> {
109
+ const path = join(this.patternsPath, `${id}.json`);
110
+ if (!existsSync(path)) return null;
111
+ const content = await readFile(path, "utf-8");
112
+ return JSON.parse(content);
113
+ }
114
+
115
+ async listPatterns(): Promise<WorkspacePattern[]> {
116
+ if (!existsSync(this.patternsPath)) return [];
117
+ const files = await readdir(this.patternsPath);
118
+ const patterns: WorkspacePattern[] = [];
119
+ for (const file of files) {
120
+ if (file.endsWith(".json")) {
121
+ const content = await readFile(join(this.patternsPath, file), "utf-8");
122
+ patterns.push(JSON.parse(content));
123
+ }
124
+ }
125
+ return patterns.sort((a, b) => a.title.localeCompare(b.title));
126
+ }
127
+
128
+ // Knowledge
129
+
130
+ async addKnowledge(
131
+ knowledge: Omit<WorkspaceKnowledge, "id" | "createdAt" | "updatedAt">
132
+ ): Promise<WorkspaceKnowledge> {
133
+ const id = `know_${generateRandomId()}`;
134
+ const now = new Date().toISOString();
135
+ const full: WorkspaceKnowledge = {
136
+ ...knowledge,
137
+ id,
138
+ createdAt: now,
139
+ updatedAt: now,
140
+ };
141
+ await writeFile(
142
+ join(this.knowledgePath, `${id}.json`),
143
+ JSON.stringify(full, null, 2)
144
+ );
145
+ return full;
146
+ }
147
+
148
+ async getKnowledge(id: string): Promise<WorkspaceKnowledge | null> {
149
+ const path = join(this.knowledgePath, `${id}.json`);
150
+ if (!existsSync(path)) return null;
151
+ const content = await readFile(path, "utf-8");
152
+ return JSON.parse(content);
153
+ }
154
+
155
+ async listKnowledge(): Promise<WorkspaceKnowledge[]> {
156
+ if (!existsSync(this.knowledgePath)) return [];
157
+ const files = await readdir(this.knowledgePath);
158
+ const knowledge: WorkspaceKnowledge[] = [];
159
+ for (const file of files) {
160
+ if (file.endsWith(".json")) {
161
+ const content = await readFile(join(this.knowledgePath, file), "utf-8");
162
+ knowledge.push(JSON.parse(content));
163
+ }
164
+ }
165
+ return knowledge.sort((a, b) => a.title.localeCompare(b.title));
166
+ }
167
+
168
+ // Query
169
+
170
+ async query(q: WorkspaceQuery): Promise<WorkspaceQueryResult> {
171
+ const result: WorkspaceQueryResult = {
172
+ decisions: [],
173
+ patterns: [],
174
+ knowledge: [],
175
+ trajectories: [],
176
+ };
177
+
178
+ const searchText = q.query.toLowerCase();
179
+ const limit = q.limit || 10;
180
+
181
+ // Search decisions
182
+ if (!q.type || q.type === "decision" || q.type === "any") {
183
+ const decisions = await this.listDecisions();
184
+ result.decisions = decisions
185
+ .filter((d) => {
186
+ const text =
187
+ `${d.title} ${d.decision} ${d.reasoning} ${d.tags.join(" ")}`.toLowerCase();
188
+ return text.includes(searchText);
189
+ })
190
+ .slice(0, limit);
191
+ }
192
+
193
+ // Search patterns
194
+ if (!q.type || q.type === "pattern" || q.type === "any") {
195
+ const patterns = await this.listPatterns();
196
+ result.patterns = patterns
197
+ .filter((p) => {
198
+ const text =
199
+ `${p.title} ${p.description} ${p.when} ${p.tags.join(" ")}`.toLowerCase();
200
+ return text.includes(searchText);
201
+ })
202
+ .slice(0, limit);
203
+ }
204
+
205
+ // Search knowledge
206
+ if (!q.type || q.type === "knowledge" || q.type === "any") {
207
+ const knowledge = await this.listKnowledge();
208
+ result.knowledge = knowledge
209
+ .filter((k) => {
210
+ const text =
211
+ `${k.title} ${k.content} ${k.tags.join(" ")}`.toLowerCase();
212
+ return text.includes(searchText);
213
+ })
214
+ .slice(0, limit);
215
+ }
216
+
217
+ return result;
218
+ }
219
+
220
+ // Export full workspace state
221
+ async export(): Promise<Workspace> {
222
+ return {
223
+ projectId: "default",
224
+ decisions: await this.listDecisions(),
225
+ patterns: await this.listPatterns(),
226
+ knowledge: await this.listKnowledge(),
227
+ version: 1,
228
+ lastUpdated: new Date().toISOString(),
229
+ };
230
+ }
231
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Workspace types - knowledge layer extracted from trajectories
3
+ */
4
+
5
+ /**
6
+ * A decision promoted to the workspace for future reference
7
+ */
8
+ export interface WorkspaceDecision {
9
+ id: string;
10
+ title: string;
11
+ context: string;
12
+ decision: string;
13
+ reasoning: string;
14
+ alternatives?: string[];
15
+ consequences?: string[];
16
+ sourceTrajectory: string;
17
+ sourceDate: string;
18
+ tags: string[];
19
+ status: "active" | "superseded" | "deprecated";
20
+ supersededBy?: string;
21
+ }
22
+
23
+ /**
24
+ * A reusable pattern extracted from trajectories
25
+ */
26
+ export interface WorkspacePattern {
27
+ id: string;
28
+ title: string;
29
+ description: string;
30
+ when: string; // When to use this pattern
31
+ structure: string; // Template or structure
32
+ example?: string; // Example usage
33
+ sourceTrajectories: string[];
34
+ tags: string[];
35
+ createdAt: string;
36
+ updatedAt: string;
37
+ }
38
+
39
+ /**
40
+ * Knowledge document - architecture, conventions, etc
41
+ */
42
+ export interface WorkspaceKnowledge {
43
+ id: string;
44
+ title: string;
45
+ category: "architecture" | "convention" | "guide" | "reference";
46
+ content: string; // Markdown content
47
+ sourceTrajectories: string[];
48
+ tags: string[];
49
+ createdAt: string;
50
+ updatedAt: string;
51
+ }
52
+
53
+ /**
54
+ * Query for searching the workspace
55
+ */
56
+ export interface WorkspaceQuery {
57
+ type?: "decision" | "pattern" | "knowledge" | "trajectory" | "any";
58
+ query: string;
59
+ context?: string; // Current task context
60
+ tags?: string[];
61
+ limit?: number;
62
+ }
63
+
64
+ /**
65
+ * Result from a workspace query
66
+ */
67
+ export interface WorkspaceQueryResult {
68
+ decisions: WorkspaceDecision[];
69
+ patterns: WorkspacePattern[];
70
+ knowledge: WorkspaceKnowledge[];
71
+ trajectories: Array<{
72
+ id: string;
73
+ title: string;
74
+ relevance: string;
75
+ }>;
76
+ }
77
+
78
+ /**
79
+ * The full workspace state
80
+ */
81
+ export interface Workspace {
82
+ projectId: string;
83
+ decisions: WorkspaceDecision[];
84
+ patterns: WorkspacePattern[];
85
+ knowledge: WorkspaceKnowledge[];
86
+ version: number;
87
+ lastUpdated: string;
88
+ }