agentrace 0.0.4 → 0.0.6
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/commands/init.js +1 -1
- package/dist/commands/mcp-server.js +29 -18
- package/dist/commands/off.js +1 -1
- package/dist/commands/on.js +1 -1
- package/dist/commands/send.d.ts +11 -0
- package/dist/commands/send.js +106 -41
- package/dist/commands/uninstall.js +1 -1
- package/dist/hooks/installer.js +4 -3
- package/dist/index.js +11 -5
- package/dist/mcp/plan-document-client.d.ts +7 -2
- package/dist/mcp/plan-document-client.js +3 -3
- package/dist/utils/session-finder.d.ts +10 -0
- package/dist/utils/session-finder.js +66 -0
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -25,7 +25,7 @@ export async function initCommand(options = {}) {
|
|
|
25
25
|
console.error("Error: Invalid URL format");
|
|
26
26
|
process.exit(1);
|
|
27
27
|
}
|
|
28
|
-
console.log("
|
|
28
|
+
console.log("AgenTrace Setup\n");
|
|
29
29
|
if (options.dev) {
|
|
30
30
|
console.log("[Dev Mode] Using local CLI for hooks\n");
|
|
31
31
|
}
|
|
@@ -6,20 +6,23 @@ import * as fs from "node:fs";
|
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import { PlanDocumentClient } from "../mcp/plan-document-client.js";
|
|
9
|
-
// Read session_id from file written by PreToolUse hook
|
|
10
|
-
function
|
|
9
|
+
// Read session_id and tool_use_id from file written by PreToolUse hook
|
|
10
|
+
function getSessionInfoFromFile() {
|
|
11
11
|
try {
|
|
12
12
|
const sessionFile = path.join(os.homedir(), ".agentrace", "current-session.json");
|
|
13
13
|
if (fs.existsSync(sessionFile)) {
|
|
14
14
|
const content = fs.readFileSync(sessionFile, "utf-8");
|
|
15
15
|
const data = JSON.parse(content);
|
|
16
|
-
return
|
|
16
|
+
return {
|
|
17
|
+
session_id: data.session_id,
|
|
18
|
+
tool_use_id: data.tool_use_id,
|
|
19
|
+
};
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
catch {
|
|
20
|
-
// Ignore errors, return
|
|
23
|
+
// Ignore errors, return empty object
|
|
21
24
|
}
|
|
22
|
-
return
|
|
25
|
+
return {};
|
|
23
26
|
}
|
|
24
27
|
// Tool schemas
|
|
25
28
|
const SearchPlansSchema = z.object({
|
|
@@ -37,10 +40,12 @@ const CreatePlanSchema = z.object({
|
|
|
37
40
|
const UpdatePlanSchema = z.object({
|
|
38
41
|
id: z.string().describe("Plan document ID"),
|
|
39
42
|
body: z.string().describe("Updated plan content in Markdown format"),
|
|
43
|
+
message: z.string().optional().describe("One-line description of what was changed"),
|
|
40
44
|
});
|
|
41
45
|
const SetPlanStatusSchema = z.object({
|
|
42
46
|
id: z.string().describe("Plan document ID"),
|
|
43
|
-
status: z.enum(["scratch", "draft", "planning", "pending", "implementation", "complete"]).describe("New status for the plan"),
|
|
47
|
+
status: z.enum(["scratch", "draft", "planning", "pending", "ready", "implementation", "complete"]).describe("New status for the plan"),
|
|
48
|
+
message: z.string().optional().describe("One-line description of why the status is being changed"),
|
|
44
49
|
});
|
|
45
50
|
// Tool descriptions with usage guidance
|
|
46
51
|
const TOOL_DESCRIPTIONS = {
|
|
@@ -66,7 +71,7 @@ WHEN TO USE:
|
|
|
66
71
|
- When the user asks you to save or persist a plan
|
|
67
72
|
- When planning significant features, refactoring, or architectural changes
|
|
68
73
|
|
|
69
|
-
The plan will be saved to
|
|
74
|
+
The plan will be saved to AgenTrace server and can be reviewed by the team.
|
|
70
75
|
The project is automatically determined from the session's git repository.`,
|
|
71
76
|
update_plan: `Update an existing plan document.
|
|
72
77
|
|
|
@@ -88,15 +93,17 @@ Available statuses:
|
|
|
88
93
|
- draft: Plan not yet fully considered (optional intermediate status)
|
|
89
94
|
- planning: Plan is being designed/refined through discussion
|
|
90
95
|
- pending: Waiting for approval or blocked
|
|
96
|
+
- ready: Plan is finalized and ready for implementation (awaiting review/approval to start)
|
|
91
97
|
- implementation: Active development is in progress
|
|
92
98
|
- complete: The work described in the plan is finished
|
|
93
99
|
|
|
94
|
-
BASIC FLOW: scratch → planning → implementation → complete
|
|
100
|
+
BASIC FLOW: scratch → planning → ready → implementation → complete
|
|
95
101
|
(draft and pending are optional auxiliary statuses)
|
|
96
102
|
|
|
97
103
|
STATUS TRANSITION GUIDELINES:
|
|
98
104
|
- scratch → planning: When you read a scratch plan (usually written by human), review its content and rewrite it into a more concrete plan, then change status to planning
|
|
99
|
-
- planning →
|
|
105
|
+
- planning → ready: When the plan is finalized after discussion, change status to ready
|
|
106
|
+
- ready → implementation: When you start working on the plan, change status to implementation
|
|
100
107
|
- implementation → complete: When all work described in the plan is finished, change status to complete
|
|
101
108
|
|
|
102
109
|
CAUTION:
|
|
@@ -106,10 +113,10 @@ export async function mcpServerCommand() {
|
|
|
106
113
|
const server = new McpServer({
|
|
107
114
|
name: "agentrace",
|
|
108
115
|
version: "1.0.0",
|
|
109
|
-
description: `
|
|
116
|
+
description: `AgenTrace Plan Document Management Server.
|
|
110
117
|
|
|
111
118
|
This server provides tools to manage implementation and design plans.
|
|
112
|
-
Plans are stored on the
|
|
119
|
+
Plans are stored on the AgenTrace server and can be reviewed by the team.
|
|
113
120
|
|
|
114
121
|
IMPORTANT GUIDELINES:
|
|
115
122
|
- When you create a design or implementation plan, ALWAYS save it using create_plan
|
|
@@ -145,6 +152,7 @@ IMPORTANT GUIDELINES:
|
|
|
145
152
|
id: plan.id,
|
|
146
153
|
description: plan.description,
|
|
147
154
|
status: plan.status,
|
|
155
|
+
git_remote_url: plan.project?.canonical_git_repository || null,
|
|
148
156
|
updated_at: plan.updated_at,
|
|
149
157
|
collaborators: plan.collaborators.map((c) => c.display_name).join(", "),
|
|
150
158
|
}));
|
|
@@ -197,12 +205,13 @@ IMPORTANT GUIDELINES:
|
|
|
197
205
|
// create_plan tool
|
|
198
206
|
server.tool("create_plan", TOOL_DESCRIPTIONS.create_plan, CreatePlanSchema.shape, async (args) => {
|
|
199
207
|
try {
|
|
200
|
-
// Read session_id from file written by PreToolUse hook
|
|
201
|
-
const
|
|
208
|
+
// Read session_id and tool_use_id from file written by PreToolUse hook
|
|
209
|
+
const sessionInfo = getSessionInfoFromFile();
|
|
202
210
|
const plan = await getClient().createPlan({
|
|
203
211
|
description: args.description,
|
|
204
212
|
body: args.body,
|
|
205
|
-
claude_session_id:
|
|
213
|
+
claude_session_id: sessionInfo.session_id,
|
|
214
|
+
tool_use_id: sessionInfo.tool_use_id,
|
|
206
215
|
});
|
|
207
216
|
return {
|
|
208
217
|
content: [
|
|
@@ -228,8 +237,8 @@ IMPORTANT GUIDELINES:
|
|
|
228
237
|
// update_plan tool
|
|
229
238
|
server.tool("update_plan", TOOL_DESCRIPTIONS.update_plan, UpdatePlanSchema.shape, async (args) => {
|
|
230
239
|
try {
|
|
231
|
-
// Read session_id from file written by PreToolUse hook
|
|
232
|
-
const
|
|
240
|
+
// Read session_id and tool_use_id from file written by PreToolUse hook
|
|
241
|
+
const sessionInfo = getSessionInfoFromFile();
|
|
233
242
|
// Get current plan to compute patch
|
|
234
243
|
const currentPlan = await getClient().getPlan(args.id);
|
|
235
244
|
// Compute patch using diff-match-patch
|
|
@@ -238,7 +247,9 @@ IMPORTANT GUIDELINES:
|
|
|
238
247
|
const plan = await getClient().updatePlan(args.id, {
|
|
239
248
|
body: args.body,
|
|
240
249
|
patch: patchText,
|
|
241
|
-
|
|
250
|
+
message: args.message,
|
|
251
|
+
claude_session_id: sessionInfo.session_id,
|
|
252
|
+
tool_use_id: sessionInfo.tool_use_id,
|
|
242
253
|
});
|
|
243
254
|
return {
|
|
244
255
|
content: [
|
|
@@ -264,7 +275,7 @@ IMPORTANT GUIDELINES:
|
|
|
264
275
|
// set_plan_status tool
|
|
265
276
|
server.tool("set_plan_status", TOOL_DESCRIPTIONS.set_plan_status, SetPlanStatusSchema.shape, async (args) => {
|
|
266
277
|
try {
|
|
267
|
-
const plan = await getClient().setStatus(args.id, args.status);
|
|
278
|
+
const plan = await getClient().setStatus(args.id, args.status, args.message);
|
|
268
279
|
return {
|
|
269
280
|
content: [
|
|
270
281
|
{
|
package/dist/commands/off.js
CHANGED
|
@@ -4,7 +4,7 @@ export async function offCommand() {
|
|
|
4
4
|
// Check if config exists
|
|
5
5
|
const config = loadConfig();
|
|
6
6
|
if (!config) {
|
|
7
|
-
console.log("
|
|
7
|
+
console.log("AgenTrace is not configured. Run 'npx agentrace init' first.");
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
10
10
|
const result = uninstallHooks();
|
package/dist/commands/on.js
CHANGED
|
@@ -8,7 +8,7 @@ export async function onCommand(options = {}) {
|
|
|
8
8
|
// Check if config exists
|
|
9
9
|
const config = loadConfig();
|
|
10
10
|
if (!config) {
|
|
11
|
-
console.log("
|
|
11
|
+
console.log("AgenTrace is not configured. Run 'npx agentrace init' first.");
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
// Determine hook command
|
package/dist/commands/send.d.ts
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook-based send command.
|
|
3
|
+
* Reads session info from stdin (provided by Claude Code hooks).
|
|
4
|
+
*/
|
|
1
5
|
export declare function sendCommand(): Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Manual send command.
|
|
8
|
+
* Finds session file by ID and sends to server.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sendManualCommand(options: {
|
|
11
|
+
sessionId: string;
|
|
12
|
+
}): Promise<void>;
|
package/dist/commands/send.js
CHANGED
|
@@ -2,6 +2,7 @@ import { execSync } from "child_process";
|
|
|
2
2
|
import { loadConfig } from "../config/manager.js";
|
|
3
3
|
import { getNewLines, saveCursor, hasCursor } from "../config/cursor.js";
|
|
4
4
|
import { sendIngest } from "../utils/http.js";
|
|
5
|
+
import { findSessionFile, extractCwdFromTranscript, } from "../utils/session-finder.js";
|
|
5
6
|
function getGitRemoteUrl(cwd) {
|
|
6
7
|
try {
|
|
7
8
|
const url = execSync("git remote get-url origin", {
|
|
@@ -28,6 +29,78 @@ function getGitBranch(cwd) {
|
|
|
28
29
|
return null;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Core logic for sending transcript data to the server.
|
|
34
|
+
* Shared between hook-based and manual invocations.
|
|
35
|
+
*/
|
|
36
|
+
async function sendTranscript(params) {
|
|
37
|
+
const { sessionId, transcriptPath, cwd, isHook } = params;
|
|
38
|
+
const exitWithError = (message) => {
|
|
39
|
+
console.error(message);
|
|
40
|
+
process.exit(isHook ? 0 : 1);
|
|
41
|
+
};
|
|
42
|
+
// Check if config exists
|
|
43
|
+
const config = loadConfig();
|
|
44
|
+
if (!config) {
|
|
45
|
+
exitWithError("[agentrace] Warning: Config not found. Run 'npx agentrace init' first.");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// Get new lines from transcript
|
|
49
|
+
const { lines, totalLineCount } = getNewLines(transcriptPath, sessionId);
|
|
50
|
+
if (lines.length === 0) {
|
|
51
|
+
if (!isHook) {
|
|
52
|
+
console.log("[agentrace] No new lines to send.");
|
|
53
|
+
}
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
// Parse JSONL lines
|
|
57
|
+
const transcriptLines = [];
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
try {
|
|
60
|
+
transcriptLines.push(JSON.parse(line));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Skip invalid JSON lines
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (transcriptLines.length === 0) {
|
|
67
|
+
if (!isHook) {
|
|
68
|
+
console.log("[agentrace] No valid transcript lines to send.");
|
|
69
|
+
}
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
// Extract git info only on first send (when cursor doesn't exist yet)
|
|
73
|
+
let gitRemoteUrl;
|
|
74
|
+
let gitBranch;
|
|
75
|
+
if (cwd && !hasCursor(sessionId)) {
|
|
76
|
+
gitRemoteUrl = getGitRemoteUrl(cwd) ?? undefined;
|
|
77
|
+
gitBranch = getGitBranch(cwd) ?? undefined;
|
|
78
|
+
}
|
|
79
|
+
// Send to server
|
|
80
|
+
const result = await sendIngest({
|
|
81
|
+
session_id: sessionId,
|
|
82
|
+
transcript_lines: transcriptLines,
|
|
83
|
+
cwd: cwd,
|
|
84
|
+
git_remote_url: gitRemoteUrl,
|
|
85
|
+
git_branch: gitBranch,
|
|
86
|
+
});
|
|
87
|
+
if (result.ok) {
|
|
88
|
+
// Update cursor on success
|
|
89
|
+
saveCursor(sessionId, totalLineCount);
|
|
90
|
+
if (!isHook) {
|
|
91
|
+
console.log(`[agentrace] Sent ${transcriptLines.length} lines for session ${sessionId}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
exitWithError(`[agentrace] Warning: ${result.error}`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
process.exit(0);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Hook-based send command.
|
|
102
|
+
* Reads session info from stdin (provided by Claude Code hooks).
|
|
103
|
+
*/
|
|
31
104
|
export async function sendCommand() {
|
|
32
105
|
// Check if config exists
|
|
33
106
|
const config = loadConfig();
|
|
@@ -68,51 +141,43 @@ export async function sendCommand() {
|
|
|
68
141
|
if (data.hook_event_name === "UserPromptSubmit") {
|
|
69
142
|
await sleep(10000);
|
|
70
143
|
}
|
|
71
|
-
// Get new lines from transcript
|
|
72
|
-
const { lines, totalLineCount } = getNewLines(transcriptPath, sessionId);
|
|
73
|
-
if (lines.length === 0) {
|
|
74
|
-
// No new lines to send
|
|
75
|
-
process.exit(0);
|
|
76
|
-
}
|
|
77
|
-
// Parse JSONL lines
|
|
78
|
-
const transcriptLines = [];
|
|
79
|
-
for (const line of lines) {
|
|
80
|
-
try {
|
|
81
|
-
transcriptLines.push(JSON.parse(line));
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
// Skip invalid JSON lines
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (transcriptLines.length === 0) {
|
|
88
|
-
process.exit(0);
|
|
89
|
-
}
|
|
90
144
|
// Use CLAUDE_PROJECT_DIR (stable project root) instead of cwd (can change during builds)
|
|
91
145
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || data.cwd;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (projectDir && !hasCursor(sessionId)) {
|
|
96
|
-
gitRemoteUrl = getGitRemoteUrl(projectDir) ?? undefined;
|
|
97
|
-
gitBranch = getGitBranch(projectDir) ?? undefined;
|
|
98
|
-
}
|
|
99
|
-
// Send to server
|
|
100
|
-
const result = await sendIngest({
|
|
101
|
-
session_id: sessionId,
|
|
102
|
-
transcript_lines: transcriptLines,
|
|
146
|
+
await sendTranscript({
|
|
147
|
+
sessionId,
|
|
148
|
+
transcriptPath,
|
|
103
149
|
cwd: projectDir,
|
|
104
|
-
|
|
105
|
-
|
|
150
|
+
isHook: true,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Manual send command.
|
|
155
|
+
* Finds session file by ID and sends to server.
|
|
156
|
+
*/
|
|
157
|
+
export async function sendManualCommand(options) {
|
|
158
|
+
const { sessionId } = options;
|
|
159
|
+
// Check if config exists
|
|
160
|
+
const config = loadConfig();
|
|
161
|
+
if (!config) {
|
|
162
|
+
console.error("[agentrace] Error: Config not found. Run 'npx agentrace init' first.");
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
// Find session file
|
|
166
|
+
const transcriptPath = findSessionFile(sessionId);
|
|
167
|
+
if (!transcriptPath) {
|
|
168
|
+
console.error(`[agentrace] Error: Session file not found for ID: ${sessionId}`);
|
|
169
|
+
console.error(" Searched in: ~/.claude/projects/");
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
// Extract cwd from transcript
|
|
173
|
+
const cwd = extractCwdFromTranscript(transcriptPath) ?? undefined;
|
|
174
|
+
console.log(`[agentrace] Found session file: ${transcriptPath}`);
|
|
175
|
+
await sendTranscript({
|
|
176
|
+
sessionId,
|
|
177
|
+
transcriptPath,
|
|
178
|
+
cwd,
|
|
179
|
+
isHook: false,
|
|
106
180
|
});
|
|
107
|
-
if (result.ok) {
|
|
108
|
-
// Update cursor on success
|
|
109
|
-
saveCursor(sessionId, totalLineCount);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
console.error(`[agentrace] Warning: ${result.error}`);
|
|
113
|
-
}
|
|
114
|
-
// Always exit 0 to not block hooks
|
|
115
|
-
process.exit(0);
|
|
116
181
|
}
|
|
117
182
|
function sleep(ms) {
|
|
118
183
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { deleteConfig } from "../config/manager.js";
|
|
2
2
|
import { uninstallHooks, uninstallMcpServer, uninstallPreToolUseHook } from "../hooks/installer.js";
|
|
3
3
|
export async function uninstallCommand() {
|
|
4
|
-
console.log("Uninstalling
|
|
4
|
+
console.log("Uninstalling AgenTrace...\n");
|
|
5
5
|
// Remove hooks
|
|
6
6
|
const hookResult = uninstallHooks();
|
|
7
7
|
if (hookResult.success) {
|
package/dist/hooks/installer.js
CHANGED
|
@@ -236,7 +236,7 @@ export function checkMcpServerInstalled() {
|
|
|
236
236
|
}
|
|
237
237
|
// PreToolUse hook for injecting session_id into agentrace MCP tools
|
|
238
238
|
const SESSION_ID_HOOK_SCRIPT = `#!/usr/bin/env node
|
|
239
|
-
// Agentrace PreToolUse hook: Writes session_id to file for MCP tools
|
|
239
|
+
// Agentrace PreToolUse hook: Writes session_id and tool_use_id to file for MCP tools
|
|
240
240
|
// This hook is called before agentrace MCP tools (create_plan, update_plan)
|
|
241
241
|
|
|
242
242
|
const fs = require('fs');
|
|
@@ -252,9 +252,10 @@ process.stdin.on('end', () => {
|
|
|
252
252
|
try {
|
|
253
253
|
const data = JSON.parse(input);
|
|
254
254
|
const sessionId = data.session_id;
|
|
255
|
+
const toolUseId = data.tool_use_id;
|
|
255
256
|
|
|
256
|
-
// Write session_id to file for MCP server to read
|
|
257
|
-
fs.writeFileSync(sessionFile, JSON.stringify({ session_id: sessionId }));
|
|
257
|
+
// Write session_id and tool_use_id to file for MCP server to read
|
|
258
|
+
fs.writeFileSync(sessionFile, JSON.stringify({ session_id: sessionId, tool_use_id: toolUseId }));
|
|
258
259
|
|
|
259
260
|
// Allow the tool to proceed
|
|
260
261
|
const output = {
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { initCommand } from "./commands/init.js";
|
|
4
4
|
import { loginCommand } from "./commands/login.js";
|
|
5
|
-
import { sendCommand } from "./commands/send.js";
|
|
5
|
+
import { sendCommand, sendManualCommand } from "./commands/send.js";
|
|
6
6
|
import { uninstallCommand } from "./commands/uninstall.js";
|
|
7
7
|
import { onCommand } from "./commands/on.js";
|
|
8
8
|
import { offCommand } from "./commands/off.js";
|
|
9
9
|
import { mcpServerCommand } from "./commands/mcp-server.js";
|
|
10
10
|
const program = new Command();
|
|
11
|
-
program.name("agentrace").description("CLI for
|
|
11
|
+
program.name("agentrace").description("CLI for AgenTrace").version("0.1.0");
|
|
12
12
|
program
|
|
13
13
|
.command("init")
|
|
14
14
|
.description("Initialize agentrace configuration and hooks")
|
|
@@ -25,9 +25,15 @@ program
|
|
|
25
25
|
});
|
|
26
26
|
program
|
|
27
27
|
.command("send")
|
|
28
|
-
.description("Send event to server (used by hooks)")
|
|
29
|
-
.
|
|
30
|
-
|
|
28
|
+
.description("Send event to server (used by hooks, or manually with --claude-session-id)")
|
|
29
|
+
.option("--claude-session-id <id>", "Send existing Claude session by ID")
|
|
30
|
+
.action(async (options) => {
|
|
31
|
+
if (options.claudeSessionId) {
|
|
32
|
+
await sendManualCommand({ sessionId: options.claudeSessionId });
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
await sendCommand();
|
|
36
|
+
}
|
|
31
37
|
});
|
|
32
38
|
program
|
|
33
39
|
.command("uninstall")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type PlanDocumentStatus = "scratch" | "draft" | "planning" | "pending" | "implementation" | "complete";
|
|
1
|
+
export type PlanDocumentStatus = "scratch" | "draft" | "planning" | "pending" | "ready" | "implementation" | "complete";
|
|
2
2
|
export interface Project {
|
|
3
3
|
id: string;
|
|
4
4
|
canonical_git_repository: string;
|
|
@@ -23,10 +23,12 @@ export interface PlanDocumentEvent {
|
|
|
23
23
|
user_id: string | null;
|
|
24
24
|
user_name: string | null;
|
|
25
25
|
patch: string;
|
|
26
|
+
message: string;
|
|
26
27
|
created_at: string;
|
|
27
28
|
}
|
|
28
29
|
export interface ListPlansResponse {
|
|
29
30
|
plans: PlanDocument[];
|
|
31
|
+
next_cursor?: string;
|
|
30
32
|
}
|
|
31
33
|
export interface SearchPlansParams {
|
|
32
34
|
gitRemoteUrl?: string;
|
|
@@ -40,12 +42,15 @@ export interface CreatePlanRequest {
|
|
|
40
42
|
description: string;
|
|
41
43
|
body: string;
|
|
42
44
|
claude_session_id?: string;
|
|
45
|
+
tool_use_id?: string;
|
|
43
46
|
}
|
|
44
47
|
export interface UpdatePlanRequest {
|
|
45
48
|
description?: string;
|
|
46
49
|
body?: string;
|
|
47
50
|
patch?: string;
|
|
51
|
+
message?: string;
|
|
48
52
|
claude_session_id?: string;
|
|
53
|
+
tool_use_id?: string;
|
|
49
54
|
}
|
|
50
55
|
export declare class PlanDocumentClient {
|
|
51
56
|
private serverUrl;
|
|
@@ -58,5 +63,5 @@ export declare class PlanDocumentClient {
|
|
|
58
63
|
createPlan(req: CreatePlanRequest): Promise<PlanDocument>;
|
|
59
64
|
updatePlan(id: string, req: UpdatePlanRequest): Promise<PlanDocument>;
|
|
60
65
|
deletePlan(id: string): Promise<void>;
|
|
61
|
-
setStatus(id: string, status: PlanDocumentStatus): Promise<PlanDocument>;
|
|
66
|
+
setStatus(id: string, status: PlanDocumentStatus, message?: string): Promise<PlanDocument>;
|
|
62
67
|
}
|
|
@@ -5,7 +5,7 @@ export class PlanDocumentClient {
|
|
|
5
5
|
constructor() {
|
|
6
6
|
const config = loadConfig();
|
|
7
7
|
if (!config) {
|
|
8
|
-
throw new Error("
|
|
8
|
+
throw new Error("AgenTrace is not configured. Run 'npx agentrace init' first.");
|
|
9
9
|
}
|
|
10
10
|
this.serverUrl = config.server_url;
|
|
11
11
|
this.apiKey = config.api_key;
|
|
@@ -63,7 +63,7 @@ export class PlanDocumentClient {
|
|
|
63
63
|
async deletePlan(id) {
|
|
64
64
|
await this.request("DELETE", `/api/plans/${id}`);
|
|
65
65
|
}
|
|
66
|
-
async setStatus(id, status) {
|
|
67
|
-
return this.request("PATCH", `/api/plans/${id}/status`, { status });
|
|
66
|
+
async setStatus(id, status, message) {
|
|
67
|
+
return this.request("PATCH", `/api/plans/${id}/status`, { status, message });
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find a Claude session JSONL file by session ID.
|
|
3
|
+
* Searches recursively in ~/.claude/projects/
|
|
4
|
+
*/
|
|
5
|
+
export declare function findSessionFile(sessionId: string): string | null;
|
|
6
|
+
/**
|
|
7
|
+
* Extract the cwd from a transcript JSONL file.
|
|
8
|
+
* Looks for the first entry with type="user" that has a cwd field.
|
|
9
|
+
*/
|
|
10
|
+
export declare function extractCwdFromTranscript(transcriptPath: string): string | null;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
const CLAUDE_PROJECTS_DIR = path.join(os.homedir(), ".claude", "projects");
|
|
5
|
+
/**
|
|
6
|
+
* Find a Claude session JSONL file by session ID.
|
|
7
|
+
* Searches recursively in ~/.claude/projects/
|
|
8
|
+
*/
|
|
9
|
+
export function findSessionFile(sessionId) {
|
|
10
|
+
const targetFileName = `${sessionId}.jsonl`;
|
|
11
|
+
if (!fs.existsSync(CLAUDE_PROJECTS_DIR)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
// Recursively search for the session file
|
|
15
|
+
const result = searchDirectory(CLAUDE_PROJECTS_DIR, targetFileName);
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
function searchDirectory(dir, targetFileName) {
|
|
19
|
+
try {
|
|
20
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
const fullPath = path.join(dir, entry.name);
|
|
23
|
+
if (entry.isDirectory()) {
|
|
24
|
+
const result = searchDirectory(fullPath, targetFileName);
|
|
25
|
+
if (result) {
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else if (entry.isFile() && entry.name === targetFileName) {
|
|
30
|
+
return fullPath;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Skip directories we can't read
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Extract the cwd from a transcript JSONL file.
|
|
41
|
+
* Looks for the first entry with type="user" that has a cwd field.
|
|
42
|
+
*/
|
|
43
|
+
export function extractCwdFromTranscript(transcriptPath) {
|
|
44
|
+
try {
|
|
45
|
+
if (!fs.existsSync(transcriptPath)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const content = fs.readFileSync(transcriptPath, "utf-8");
|
|
49
|
+
const lines = content.split("\n").filter((line) => line.trim() !== "");
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
try {
|
|
52
|
+
const entry = JSON.parse(line);
|
|
53
|
+
if (entry.type === "user" && entry.cwd) {
|
|
54
|
+
return entry.cwd;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Skip invalid JSON lines
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// File read error
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|