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.
- package/.beads/.local_version +1 -0
- package/.beads/README.md +81 -0
- package/.beads/config.yaml +62 -0
- package/.beads/issues.jsonl +0 -0
- package/.beads/metadata.json +4 -0
- package/.gitattributes +3 -0
- package/IMPLEMENTATION-PROPOSAL.md +1598 -0
- package/PROPOSAL-trajectories.md +1582 -0
- package/README.md +275 -0
- package/biome.json +20 -0
- package/docs/architecture/core.md +477 -0
- package/docs/architecture/memory-integration.md +186 -0
- package/docs/architecture/web-viewer.md +213 -0
- package/package.json +47 -0
- package/src/cli/commands/abandon.ts +32 -0
- package/src/cli/commands/complete.ts +51 -0
- package/src/cli/commands/decision.ts +49 -0
- package/src/cli/commands/export.ts +100 -0
- package/src/cli/commands/index.ts +39 -0
- package/src/cli/commands/list.ts +90 -0
- package/src/cli/commands/show.ts +98 -0
- package/src/cli/commands/start.ts +53 -0
- package/src/cli/commands/status.ts +68 -0
- package/src/cli/index.ts +25 -0
- package/src/cli/runner.ts +67 -0
- package/src/core/id.ts +62 -0
- package/src/core/index.ts +54 -0
- package/src/core/schema.ts +272 -0
- package/src/core/trajectory.ts +283 -0
- package/src/core/types.ts +272 -0
- package/src/export/index.ts +10 -0
- package/src/export/json.ts +26 -0
- package/src/export/markdown.ts +216 -0
- package/src/export/pr-summary.ts +57 -0
- package/src/export/timeline.ts +69 -0
- package/src/index.ts +69 -0
- package/src/storage/file.ts +394 -0
- package/src/storage/index.ts +6 -0
- package/src/storage/interface.ts +92 -0
- package/src/web/generator.ts +347 -0
- package/src/web/index.ts +6 -0
- package/src/web/styles.ts +355 -0
- package/src/workspace/index.ts +6 -0
- package/src/workspace/storage.ts +231 -0
- package/src/workspace/types.ts +88 -0
- package/tests/cli/commands.test.ts +476 -0
- package/tests/core/trajectory.test.ts +426 -0
- package/tests/export/export.test.ts +376 -0
- package/tests/storage/storage.test.ts +410 -0
- package/tests/web/generator.test.ts +197 -0
- package/tests/workspace/storage.test.ts +275 -0
- package/tsconfig.json +26 -0
- package/tsup.config.ts +14 -0
- 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
|
+
}
|