agentrace 0.0.11 → 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.
@@ -47,6 +47,19 @@ const SetPlanStatusSchema = z.object({
47
47
  status: z.enum(["scratch", "draft", "planning", "pending", "ready", "implementation", "complete"]).describe("New status for the plan"),
48
48
  message: z.string().optional().describe("One-line description of why the status is being changed"),
49
49
  });
50
+ const GetPlanCommentsSchema = z.object({
51
+ plan_id: z.string().describe("Plan document ID"),
52
+ status: z.enum(["active", "resolved", "outdated", "all"]).optional().describe("Filter comments by status. Defaults to showing all comments."),
53
+ });
54
+ const AddPlanCommentSchema = z.object({
55
+ plan_id: z.string().describe("Plan document ID"),
56
+ target_text: z.string().describe("The text in the plan body that this comment refers to"),
57
+ content: z.string().describe("The comment content"),
58
+ });
59
+ const ResolvePlanCommentSchema = z.object({
60
+ plan_id: z.string().describe("Plan document ID"),
61
+ thread_id: z.string().describe("Comment thread ID to resolve"),
62
+ });
50
63
  // Tool descriptions with usage guidance
51
64
  const TOOL_DESCRIPTIONS = {
52
65
  search_plans: `Search plan documents with filtering options.
@@ -62,7 +75,14 @@ WHEN TO USE:
62
75
  WHEN TO USE:
63
76
  - When the user asks you to check or review a specific plan by ID
64
77
  - When you need to understand an existing plan before making changes
65
- - When the user references a plan ID in their request`,
78
+ - When the user references a plan ID in their request
79
+
80
+ IMPORTANT - HANDLING COMMENTS:
81
+ - The response includes 'active_comments_count' showing how many unresolved comments exist
82
+ - If the plan is in 'planning' status AND has active comments (active_comments_count > 0):
83
+ - You SHOULD use get_plan_comments to read the comments
84
+ - Consider the feedback in the comments and reflect it in your plan updates
85
+ - Comments represent team feedback that should be addressed before moving to ready`,
66
86
  create_plan: `Create a new plan document to record implementation or design plans.
67
87
 
68
88
  WHEN TO USE:
@@ -108,6 +128,30 @@ STATUS TRANSITION GUIDELINES:
108
128
 
109
129
  CAUTION:
110
130
  - When a plan is in "implementation" status, someone else might already be working on it. Check with the team before starting work on such plans.`,
131
+ get_plan_comments: `Get comments for a plan document.
132
+
133
+ WHEN TO USE:
134
+ - When you need to see feedback or discussions on a specific plan
135
+ - When reviewing a plan that others have commented on
136
+ - Before making changes to understand any concerns or suggestions
137
+
138
+ Returns comments with their status (active, resolved, or outdated).`,
139
+ add_plan_comment: `Add a comment to a specific location in a plan.
140
+
141
+ WHEN TO USE:
142
+ - When you want to provide feedback on a specific part of a plan
143
+ - When you have a question or suggestion about the plan content
144
+ - When reviewing a plan and want to leave notes for the team
145
+
146
+ The comment is anchored to specific text in the plan body.`,
147
+ resolve_plan_comment: `Resolve a comment thread on a plan.
148
+
149
+ WHEN TO USE:
150
+ - When you have addressed the feedback in a comment
151
+ - When the issue raised in the comment has been fixed
152
+ - When the comment is no longer relevant after plan updates
153
+
154
+ Resolving a comment marks it as handled, keeping it visible but indicating it's been addressed.`,
111
155
  };
112
156
  export async function mcpServerCommand() {
113
157
  const server = new McpServer({
@@ -154,6 +198,7 @@ IMPORTANT GUIDELINES:
154
198
  id: plan.id,
155
199
  description: plan.description,
156
200
  status: plan.status,
201
+ active_comments_count: plan.active_comments_count,
157
202
  git_remote_url: plan.project?.canonical_git_repository || null,
158
203
  url: plan.url || null,
159
204
  updated_at: plan.updated_at,
@@ -184,10 +229,14 @@ IMPORTANT GUIDELINES:
184
229
  server.tool("read_plan", TOOL_DESCRIPTIONS.read_plan, ReadPlanSchema.shape, async (args) => {
185
230
  try {
186
231
  const plan = await getClient().getPlan(args.id);
187
- let text = `# ${plan.description}\n\nStatus: ${plan.status}\n\n${plan.body}`;
232
+ let text = `# ${plan.description}\n\nStatus: ${plan.status}\nActive Comments: ${plan.active_comments_count}\n\n${plan.body}`;
188
233
  if (plan.url) {
189
234
  text += `\n\n---\nURL: ${plan.url}`;
190
235
  }
236
+ // Add reminder if there are active comments on a planning status plan
237
+ if (plan.status === "planning" && plan.active_comments_count > 0) {
238
+ text += `\n\n---\n⚠️ This plan has ${plan.active_comments_count} active comment(s). Use get_plan_comments to review them and consider the feedback.`;
239
+ }
191
240
  return {
192
241
  content: [
193
242
  {
@@ -316,6 +365,115 @@ IMPORTANT GUIDELINES:
316
365
  };
317
366
  }
318
367
  });
368
+ // get_plan_comments tool
369
+ server.tool("get_plan_comments", TOOL_DESCRIPTIONS.get_plan_comments, GetPlanCommentsSchema.shape, async (args) => {
370
+ try {
371
+ const threads = await getClient().getThreads(args.plan_id, args.status);
372
+ if (threads.length === 0) {
373
+ return {
374
+ content: [
375
+ {
376
+ type: "text",
377
+ text: "No comments found for this plan.",
378
+ },
379
+ ],
380
+ };
381
+ }
382
+ const threadList = threads.map((thread) => ({
383
+ id: thread.id,
384
+ target_text: thread.target_text,
385
+ status: thread.status,
386
+ position: thread.position ? {
387
+ start_line: thread.position.start_line,
388
+ start_column: thread.position.start_column,
389
+ end_line: thread.position.end_line,
390
+ end_column: thread.position.end_column,
391
+ found: thread.position.found,
392
+ } : null,
393
+ messages: thread.messages.map((msg) => ({
394
+ id: msg.id,
395
+ user_name: msg.user_name,
396
+ content: msg.content,
397
+ created_at: msg.created_at,
398
+ })),
399
+ created_at: thread.created_at,
400
+ }));
401
+ return {
402
+ content: [
403
+ {
404
+ type: "text",
405
+ text: JSON.stringify(threadList, null, 2),
406
+ },
407
+ ],
408
+ };
409
+ }
410
+ catch (error) {
411
+ return {
412
+ content: [
413
+ {
414
+ type: "text",
415
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
416
+ },
417
+ ],
418
+ isError: true,
419
+ };
420
+ }
421
+ });
422
+ // add_plan_comment tool
423
+ server.tool("add_plan_comment", TOOL_DESCRIPTIONS.add_plan_comment, AddPlanCommentSchema.shape, async (args) => {
424
+ try {
425
+ const thread = await getClient().createThread(args.plan_id, {
426
+ target_text: args.target_text,
427
+ context_before: "",
428
+ context_after: "",
429
+ content: args.content,
430
+ });
431
+ return {
432
+ content: [
433
+ {
434
+ type: "text",
435
+ text: `Comment added successfully.\n\nThread ID: ${thread.id}\nStatus: ${thread.status}\nTarget: "${thread.target_text}"`,
436
+ },
437
+ ],
438
+ };
439
+ }
440
+ catch (error) {
441
+ return {
442
+ content: [
443
+ {
444
+ type: "text",
445
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
446
+ },
447
+ ],
448
+ isError: true,
449
+ };
450
+ }
451
+ });
452
+ // resolve_plan_comment tool
453
+ server.tool("resolve_plan_comment", TOOL_DESCRIPTIONS.resolve_plan_comment, ResolvePlanCommentSchema.shape, async (args) => {
454
+ try {
455
+ const thread = await getClient().resolveThread(args.plan_id, args.thread_id);
456
+ return {
457
+ content: [
458
+ {
459
+ type: "text",
460
+ text: `Comment resolved successfully.\n\nThread ID: ${thread.id}\nStatus: ${thread.status}\nTarget: "${thread.target_text}"`,
461
+ },
462
+ ],
463
+ };
464
+ }
465
+ catch (error) {
466
+ return {
467
+ content: [
468
+ {
469
+ type: "text",
470
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
471
+ },
472
+ ],
473
+ isError: true,
474
+ };
475
+ }
476
+ });
319
477
  // Start the server with stdio transport
320
478
  const transport = new StdioServerTransport();
321
479
  await server.connect(transport);
@@ -16,6 +16,7 @@ export interface PlanDocument {
16
16
  url?: string;
17
17
  created_at: string;
18
18
  updated_at: string;
19
+ active_comments_count: number;
19
20
  }
20
21
  export interface PlanDocumentEvent {
21
22
  id: string;
@@ -53,6 +54,47 @@ export interface UpdatePlanRequest {
53
54
  claude_session_id?: string;
54
55
  tool_use_id?: string;
55
56
  }
57
+ export type PlanCommentThreadStatus = "active" | "resolved" | "outdated";
58
+ export interface CommentPosition {
59
+ start_offset: number;
60
+ end_offset: number;
61
+ start_line: number;
62
+ start_column: number;
63
+ end_line: number;
64
+ end_column: number;
65
+ found: boolean;
66
+ }
67
+ export interface PlanCommentMessage {
68
+ id: string;
69
+ thread_id: string;
70
+ user_id: string;
71
+ user_name: string;
72
+ content: string;
73
+ created_at: string;
74
+ updated_at: string;
75
+ }
76
+ export interface PlanCommentThread {
77
+ id: string;
78
+ plan_document_id: string;
79
+ target_text: string;
80
+ status: PlanCommentThreadStatus;
81
+ position: CommentPosition | null;
82
+ messages: PlanCommentMessage[];
83
+ created_at: string;
84
+ updated_at: string;
85
+ }
86
+ export interface ListThreadsResponse {
87
+ threads: PlanCommentThread[];
88
+ }
89
+ export interface CreateThreadRequest {
90
+ target_text: string;
91
+ context_before: string;
92
+ context_after: string;
93
+ content: string;
94
+ }
95
+ export interface AddMessageRequest {
96
+ content: string;
97
+ }
56
98
  export declare class PlanDocumentClient {
57
99
  private serverUrl;
58
100
  private apiKey;
@@ -65,4 +107,8 @@ export declare class PlanDocumentClient {
65
107
  updatePlan(id: string, req: UpdatePlanRequest): Promise<PlanDocument>;
66
108
  deletePlan(id: string): Promise<void>;
67
109
  setStatus(id: string, status: PlanDocumentStatus, message?: string): Promise<PlanDocument>;
110
+ getThreads(planId: string, status?: PlanCommentThreadStatus | "all"): Promise<PlanCommentThread[]>;
111
+ createThread(planId: string, req: CreateThreadRequest): Promise<PlanCommentThread>;
112
+ resolveThread(planId: string, threadId: string): Promise<PlanCommentThread>;
113
+ addMessage(planId: string, threadId: string, req: AddMessageRequest): Promise<PlanCommentMessage>;
68
114
  }
@@ -69,4 +69,24 @@ export class PlanDocumentClient {
69
69
  async setStatus(id, status, message) {
70
70
  return this.request("PATCH", `/api/plans/${id}/status`, { status, message });
71
71
  }
72
+ // Comment Thread methods
73
+ async getThreads(planId, status) {
74
+ const params = new URLSearchParams();
75
+ if (status) {
76
+ params.set("status", status);
77
+ }
78
+ const query = params.toString();
79
+ const path = `/api/plans/${planId}/comments${query ? `?${query}` : ""}`;
80
+ const response = await this.request("GET", path);
81
+ return response.threads;
82
+ }
83
+ async createThread(planId, req) {
84
+ return this.request("POST", `/api/plans/${planId}/comments`, req);
85
+ }
86
+ async resolveThread(planId, threadId) {
87
+ return this.request("POST", `/api/plans/${planId}/comments/${threadId}/resolve`);
88
+ }
89
+ async addMessage(planId, threadId, req) {
90
+ return this.request("POST", `/api/plans/${planId}/comments/${threadId}/messages`, req);
91
+ }
72
92
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentrace",
3
- "version": "0.0.11",
3
+ "version": "0.1.0",
4
4
  "description": "CLI for AgenTrace - Claude Code session tracker",
5
5
  "type": "module",
6
6
  "bin": {