@posthog/agent 1.20.0 → 1.22.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/dist/claude-cli/cli.js +2251 -1888
- package/dist/src/adapters/claude/claude-adapter.d.ts +1 -1
- package/dist/src/adapters/claude/claude-adapter.d.ts.map +1 -1
- package/dist/src/adapters/claude/claude-adapter.js +141 -133
- package/dist/src/adapters/claude/claude-adapter.js.map +1 -1
- package/dist/src/adapters/types.d.ts +3 -3
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/agent.d.ts +1 -1
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +9 -8
- package/dist/src/agent.js.map +1 -1
- package/dist/src/file-manager.d.ts +10 -0
- package/dist/src/file-manager.d.ts.map +1 -1
- package/dist/src/file-manager.js +49 -10
- package/dist/src/file-manager.js.map +1 -1
- package/dist/src/git-manager.d.ts +1 -0
- package/dist/src/git-manager.d.ts.map +1 -1
- package/dist/src/git-manager.js +10 -3
- package/dist/src/git-manager.js.map +1 -1
- package/dist/src/posthog-api.d.ts +2 -1
- package/dist/src/posthog-api.d.ts.map +1 -1
- package/dist/src/posthog-api.js +11 -0
- package/dist/src/posthog-api.js.map +1 -1
- package/dist/src/task-progress-reporter.d.ts +12 -4
- package/dist/src/task-progress-reporter.d.ts.map +1 -1
- package/dist/src/task-progress-reporter.js +282 -117
- package/dist/src/task-progress-reporter.js.map +1 -1
- package/dist/src/types.d.ts +17 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/workflow/config.d.ts.map +1 -1
- package/dist/src/workflow/config.js +11 -0
- package/dist/src/workflow/config.js.map +1 -1
- package/dist/src/workflow/steps/build.js +3 -3
- package/dist/src/workflow/steps/build.js.map +1 -1
- package/dist/src/workflow/steps/finalize.d.ts +3 -0
- package/dist/src/workflow/steps/finalize.d.ts.map +1 -0
- package/dist/src/workflow/steps/finalize.js +182 -0
- package/dist/src/workflow/steps/finalize.js.map +1 -0
- package/dist/src/workflow/steps/plan.js +3 -3
- package/dist/src/workflow/steps/plan.js.map +1 -1
- package/dist/src/workflow/steps/research.js +3 -3
- package/dist/src/workflow/steps/research.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters/claude/claude-adapter.ts +56 -46
- package/src/adapters/types.ts +3 -3
- package/src/agent.ts +17 -8
- package/src/file-manager.ts +59 -6
- package/src/git-manager.ts +11 -3
- package/src/posthog-api.ts +33 -1
- package/src/task-progress-reporter.ts +310 -138
- package/src/types.ts +20 -1
- package/src/workflow/config.ts +11 -0
- package/src/workflow/steps/build.ts +3 -3
- package/src/workflow/steps/finalize.ts +216 -0
- package/src/workflow/steps/plan.ts +3 -3
- package/src/workflow/steps/research.ts +3 -3
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { LocalArtifact } from '../../file-manager.js';
|
|
2
|
+
import type { Task, TaskRunArtifact } from '../../types.js';
|
|
3
|
+
import type { WorkflowStepRunner } from '../types.js';
|
|
4
|
+
import { finalizeStepGitActions } from '../utils.js';
|
|
5
|
+
|
|
6
|
+
const MAX_SNIPPET_LENGTH = 1200;
|
|
7
|
+
|
|
8
|
+
export const finalizeStep: WorkflowStepRunner = async ({ step, context }) => {
|
|
9
|
+
const {
|
|
10
|
+
task,
|
|
11
|
+
logger,
|
|
12
|
+
fileManager,
|
|
13
|
+
gitManager,
|
|
14
|
+
posthogAPI,
|
|
15
|
+
progressReporter,
|
|
16
|
+
} = context;
|
|
17
|
+
|
|
18
|
+
const stepLogger = logger.child('FinalizeStep');
|
|
19
|
+
const artifacts = await fileManager.collectTaskArtifacts(task.id);
|
|
20
|
+
let uploadedArtifacts: TaskRunArtifact[] | undefined;
|
|
21
|
+
|
|
22
|
+
if (artifacts.length && posthogAPI && progressReporter.runId) {
|
|
23
|
+
try {
|
|
24
|
+
const payload = artifacts.map((artifact) => ({
|
|
25
|
+
name: artifact.name,
|
|
26
|
+
type: artifact.type,
|
|
27
|
+
content: artifact.content,
|
|
28
|
+
content_type: artifact.contentType,
|
|
29
|
+
}));
|
|
30
|
+
uploadedArtifacts = await posthogAPI.uploadTaskArtifacts(task.id, progressReporter.runId, payload);
|
|
31
|
+
stepLogger.info('Uploaded task artifacts to PostHog', {
|
|
32
|
+
taskId: task.id,
|
|
33
|
+
uploadedCount: uploadedArtifacts.length,
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
stepLogger.warn('Failed to upload task artifacts', {
|
|
37
|
+
taskId: task.id,
|
|
38
|
+
error: error instanceof Error ? error.message : String(error),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
stepLogger.debug('Skipping artifact upload', {
|
|
43
|
+
hasArtifacts: artifacts.length > 0,
|
|
44
|
+
hasPostHogApi: Boolean(posthogAPI),
|
|
45
|
+
runId: progressReporter.runId,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const prBody = buildPullRequestBody(task, artifacts, uploadedArtifacts);
|
|
50
|
+
await fileManager.cleanupTaskDirectory(task.id);
|
|
51
|
+
await gitManager.addAllPostHogFiles();
|
|
52
|
+
|
|
53
|
+
// Commit the deletion of artifacts
|
|
54
|
+
await finalizeStepGitActions(context, step, {
|
|
55
|
+
commitMessage: `Cleanup task artifacts for ${task.title}`,
|
|
56
|
+
allowEmptyCommit: true
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
context.stepResults[step.id] = {
|
|
60
|
+
prBody,
|
|
61
|
+
uploadedArtifacts,
|
|
62
|
+
artifactCount: artifacts.length,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return { status: 'completed' };
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function buildPullRequestBody(task: Task, artifacts: LocalArtifact[], uploaded?: TaskRunArtifact[]): string {
|
|
69
|
+
const lines: string[] = [];
|
|
70
|
+
const taskSlug = (task as any).slug || task.id;
|
|
71
|
+
|
|
72
|
+
lines.push('## Task context');
|
|
73
|
+
lines.push(`- **Task**: ${taskSlug}`);
|
|
74
|
+
lines.push(`- **Title**: ${task.title}`);
|
|
75
|
+
lines.push(`- **Origin**: ${task.origin_product}`);
|
|
76
|
+
|
|
77
|
+
if (task.description) {
|
|
78
|
+
lines.push('');
|
|
79
|
+
lines.push('> ' + task.description.trim().split('\n').join('\n> '));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const usedFiles = new Set<string>();
|
|
83
|
+
|
|
84
|
+
const contextArtifact = artifacts.find((artifact) => artifact.name === 'context.md');
|
|
85
|
+
if (contextArtifact) {
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push('### Task prompt');
|
|
88
|
+
lines.push(contextArtifact.content);
|
|
89
|
+
usedFiles.add(contextArtifact.name);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const researchArtifact = artifacts.find((artifact) => artifact.name === 'research.json');
|
|
93
|
+
if (researchArtifact) {
|
|
94
|
+
usedFiles.add(researchArtifact.name);
|
|
95
|
+
const researchSection = formatResearchSection(researchArtifact.content);
|
|
96
|
+
if (researchSection) {
|
|
97
|
+
lines.push('');
|
|
98
|
+
lines.push(researchSection);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const planArtifact = artifacts.find((artifact) => artifact.name === 'plan.md');
|
|
103
|
+
if (planArtifact) {
|
|
104
|
+
lines.push('');
|
|
105
|
+
lines.push('### Implementation plan');
|
|
106
|
+
lines.push(planArtifact.content);
|
|
107
|
+
usedFiles.add(planArtifact.name);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const todoArtifact = artifacts.find((artifact) => artifact.name === 'todos.json');
|
|
111
|
+
if (todoArtifact) {
|
|
112
|
+
const summary = summarizeTodos(todoArtifact.content);
|
|
113
|
+
if (summary) {
|
|
114
|
+
lines.push('');
|
|
115
|
+
lines.push('### Todo list');
|
|
116
|
+
lines.push(summary);
|
|
117
|
+
}
|
|
118
|
+
usedFiles.add(todoArtifact.name);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const remainingArtifacts = artifacts.filter((artifact) => !usedFiles.has(artifact.name));
|
|
122
|
+
if (remainingArtifacts.length) {
|
|
123
|
+
lines.push('');
|
|
124
|
+
lines.push('### Additional artifacts');
|
|
125
|
+
for (const artifact of remainingArtifacts) {
|
|
126
|
+
lines.push(`#### ${artifact.name}`);
|
|
127
|
+
lines.push(renderCodeFence(artifact.content));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const artifactList = uploaded ?? artifacts.map((artifact) => ({
|
|
132
|
+
name: artifact.name,
|
|
133
|
+
type: artifact.type,
|
|
134
|
+
}));
|
|
135
|
+
|
|
136
|
+
if (artifactList.length) {
|
|
137
|
+
lines.push('');
|
|
138
|
+
lines.push('### Uploaded artifacts');
|
|
139
|
+
for (const artifact of artifactList) {
|
|
140
|
+
const rawStoragePath = 'storage_path' in artifact ? (artifact as any).storage_path : undefined;
|
|
141
|
+
const storagePath = typeof rawStoragePath === 'string' ? rawStoragePath : undefined;
|
|
142
|
+
const storage = storagePath && storagePath.trim().length > 0 ? ` – \`${storagePath.trim()}\`` : '';
|
|
143
|
+
lines.push(`- ${artifact.name} (${artifact.type})${storage}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return lines.join('\n\n');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function renderCodeFence(content: string): string {
|
|
151
|
+
const snippet = truncate(content, MAX_SNIPPET_LENGTH);
|
|
152
|
+
return ['```', snippet, '```'].join('\n');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function truncate(value: string, maxLength: number): string {
|
|
156
|
+
if (value.length <= maxLength) {
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
return `${value.slice(0, maxLength)}\n…`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function formatResearchSection(content: string): string | null {
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(content);
|
|
165
|
+
const sections: string[] = [];
|
|
166
|
+
|
|
167
|
+
if (parsed.context) {
|
|
168
|
+
sections.push('### Research summary');
|
|
169
|
+
sections.push(parsed.context);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (parsed.questions && parsed.questions.length) {
|
|
173
|
+
sections.push('');
|
|
174
|
+
sections.push('### Questions needing answers');
|
|
175
|
+
for (const question of parsed.questions) {
|
|
176
|
+
sections.push(`- ${question.question ?? question}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (parsed.answers && parsed.answers.length) {
|
|
181
|
+
sections.push('');
|
|
182
|
+
sections.push('### Answers provided');
|
|
183
|
+
for (const answer of parsed.answers) {
|
|
184
|
+
const questionId = answer.questionId ? ` (Q: ${answer.questionId})` : '';
|
|
185
|
+
sections.push(`- ${answer.selectedOption || answer.customInput || 'answer'}${questionId}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return sections.length ? sections.join('\n') : null;
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function summarizeTodos(content: string): string | null {
|
|
196
|
+
try {
|
|
197
|
+
const data = JSON.parse(content);
|
|
198
|
+
const total = data?.metadata?.total ?? data?.items?.length;
|
|
199
|
+
const completed = data?.metadata?.completed ?? data?.items?.filter((item: any) => item.status === 'completed').length;
|
|
200
|
+
|
|
201
|
+
const lines = [
|
|
202
|
+
`Progress: ${completed}/${total} completed`,
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
if (data?.items?.length) {
|
|
206
|
+
for (const item of data.items) {
|
|
207
|
+
lines.push(`- [${item.status}] ${item.content}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return lines.join('\n');
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
@@ -102,9 +102,9 @@ export const planStep: WorkflowStepRunner = async ({ step, context }) => {
|
|
|
102
102
|
let planContent = '';
|
|
103
103
|
for await (const message of response) {
|
|
104
104
|
emitEvent(adapter.createRawSDKEvent(message));
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
emitEvent(
|
|
105
|
+
const transformedEvents = adapter.transform(message);
|
|
106
|
+
for (const event of transformedEvents) {
|
|
107
|
+
emitEvent(event);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
const todoList = await todoManager.checkAndPersistFromMessage(message, task.id);
|
|
@@ -83,9 +83,9 @@ export const researchStep: WorkflowStepRunner = async ({ step, context }) => {
|
|
|
83
83
|
let jsonContent = '';
|
|
84
84
|
for await (const message of response) {
|
|
85
85
|
emitEvent(adapter.createRawSDKEvent(message));
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
emitEvent(
|
|
86
|
+
const transformedEvents = adapter.transform(message);
|
|
87
|
+
for (const event of transformedEvents) {
|
|
88
|
+
emitEvent(event);
|
|
89
89
|
}
|
|
90
90
|
if (message.type === 'assistant' && message.message?.content) {
|
|
91
91
|
for (const c of message.message.content) {
|