@dexto/tools-plan 1.5.8 → 1.6.1

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 (62) hide show
  1. package/dist/errors.cjs +126 -0
  2. package/dist/errors.js +99 -64
  3. package/dist/index.cjs +36 -0
  4. package/dist/index.d.cts +224 -0
  5. package/dist/index.d.ts +1 -25
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +9 -39
  8. package/dist/plan-service-getter.cjs +16 -0
  9. package/dist/plan-service-getter.d.ts +4 -0
  10. package/dist/plan-service-getter.d.ts.map +1 -0
  11. package/dist/plan-service-getter.js +0 -0
  12. package/dist/plan-service.cjs +247 -0
  13. package/dist/plan-service.d.ts +2 -2
  14. package/dist/plan-service.d.ts.map +1 -1
  15. package/dist/plan-service.js +201 -215
  16. package/dist/plan-service.test.cjs +227 -0
  17. package/dist/plan-service.test.js +200 -216
  18. package/dist/tool-factory-config.cjs +38 -0
  19. package/dist/tool-factory-config.d.ts +32 -0
  20. package/dist/tool-factory-config.d.ts.map +1 -0
  21. package/dist/tool-factory-config.js +13 -0
  22. package/dist/tool-factory.cjs +71 -0
  23. package/dist/tool-factory.d.ts +4 -0
  24. package/dist/tool-factory.d.ts.map +1 -0
  25. package/dist/tool-factory.js +40 -0
  26. package/dist/tool-factory.test.cjs +96 -0
  27. package/dist/tool-factory.test.d.ts +7 -0
  28. package/dist/tool-factory.test.d.ts.map +1 -0
  29. package/dist/tool-factory.test.js +95 -0
  30. package/dist/tools/plan-create-tool.cjs +102 -0
  31. package/dist/tools/plan-create-tool.d.ts +15 -3
  32. package/dist/tools/plan-create-tool.d.ts.map +1 -1
  33. package/dist/tools/plan-create-tool.js +77 -71
  34. package/dist/tools/plan-create-tool.test.cjs +174 -0
  35. package/dist/tools/plan-create-tool.test.js +142 -109
  36. package/dist/tools/plan-read-tool.cjs +65 -0
  37. package/dist/tools/plan-read-tool.d.ts +6 -3
  38. package/dist/tools/plan-read-tool.d.ts.map +1 -1
  39. package/dist/tools/plan-read-tool.js +39 -38
  40. package/dist/tools/plan-read-tool.test.cjs +109 -0
  41. package/dist/tools/plan-read-tool.test.js +78 -75
  42. package/dist/tools/plan-review-tool.cjs +98 -0
  43. package/dist/tools/plan-review-tool.d.ts +14 -5
  44. package/dist/tools/plan-review-tool.d.ts.map +1 -1
  45. package/dist/tools/plan-review-tool.js +73 -83
  46. package/dist/tools/plan-update-tool.cjs +92 -0
  47. package/dist/tools/plan-update-tool.d.ts +12 -3
  48. package/dist/tools/plan-update-tool.d.ts.map +1 -1
  49. package/dist/tools/plan-update-tool.js +65 -69
  50. package/dist/tools/plan-update-tool.test.cjs +203 -0
  51. package/dist/tools/plan-update-tool.test.js +171 -142
  52. package/dist/types.cjs +44 -0
  53. package/dist/types.js +17 -24
  54. package/package.json +8 -8
  55. package/.dexto-plugin/plugin.json +0 -7
  56. package/dist/tool-provider.d.ts +0 -44
  57. package/dist/tool-provider.d.ts.map +0 -1
  58. package/dist/tool-provider.js +0 -81
  59. package/dist/tool-provider.test.d.ts +0 -7
  60. package/dist/tool-provider.test.d.ts.map +0 -1
  61. package/dist/tool-provider.test.js +0 -185
  62. package/skills/plan/SKILL.md +0 -102
@@ -1,83 +1,86 @@
1
- /**
2
- * Plan Read Tool Tests
3
- *
4
- * Tests for the plan_read tool.
5
- */
6
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
- import * as path from 'node:path';
8
- import * as fs from 'node:fs/promises';
9
- import * as os from 'node:os';
10
- import { createPlanReadTool } from './plan-read-tool.js';
11
- import { PlanService } from '../plan-service.js';
12
- import { PlanErrorCode } from '../errors.js';
13
- import { DextoRuntimeError } from '@dexto/core';
14
- // Create mock logger
15
- const createMockLogger = () => ({
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import * as path from "node:path";
3
+ import * as fs from "node:fs/promises";
4
+ import * as os from "node:os";
5
+ import { createPlanReadTool } from "./plan-read-tool.js";
6
+ import { PlanService } from "../plan-service.js";
7
+ import { PlanErrorCode } from "../errors.js";
8
+ import { DextoRuntimeError } from "@dexto/core";
9
+ const createMockLogger = () => {
10
+ const logger = {
16
11
  debug: vi.fn(),
12
+ silly: vi.fn(),
17
13
  info: vi.fn(),
18
14
  warn: vi.fn(),
19
15
  error: vi.fn(),
20
- createChild: vi.fn().mockReturnThis(),
21
- });
22
- describe('plan_read tool', () => {
23
- let mockLogger;
24
- let tempDir;
25
- let planService;
26
- beforeEach(async () => {
27
- mockLogger = createMockLogger();
28
- const rawTempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dexto-plan-read-test-'));
29
- tempDir = await fs.realpath(rawTempDir);
30
- planService = new PlanService({ basePath: tempDir }, mockLogger);
31
- vi.clearAllMocks();
16
+ trackException: vi.fn(),
17
+ createChild: vi.fn(() => logger),
18
+ createFileOnlyChild: vi.fn(() => logger),
19
+ setLevel: vi.fn(),
20
+ getLevel: vi.fn(() => "debug"),
21
+ getLogFilePath: vi.fn(() => null),
22
+ destroy: vi.fn(async () => void 0)
23
+ };
24
+ return logger;
25
+ };
26
+ function createToolContext(logger, overrides = {}) {
27
+ return { logger, ...overrides };
28
+ }
29
+ describe("plan_read tool", () => {
30
+ let logger;
31
+ let tempDir;
32
+ let planService;
33
+ beforeEach(async () => {
34
+ logger = createMockLogger();
35
+ const rawTempDir = await fs.mkdtemp(path.join(os.tmpdir(), "dexto-plan-read-test-"));
36
+ tempDir = await fs.realpath(rawTempDir);
37
+ planService = new PlanService({ basePath: tempDir }, logger);
38
+ vi.clearAllMocks();
39
+ });
40
+ afterEach(async () => {
41
+ try {
42
+ await fs.rm(tempDir, { recursive: true, force: true });
43
+ } catch {
44
+ }
45
+ });
46
+ describe("execute", () => {
47
+ it("should return exists: false when no plan exists", async () => {
48
+ const tool = createPlanReadTool(async () => planService);
49
+ const sessionId = "test-session";
50
+ const result = await tool.execute({}, createToolContext(logger, { sessionId }));
51
+ expect(result.exists).toBe(false);
52
+ expect(result.message).toContain("No plan found");
53
+ });
54
+ it("should return plan content and metadata when plan exists", async () => {
55
+ const tool = createPlanReadTool(async () => planService);
56
+ const sessionId = "test-session";
57
+ const content = "# My Plan\n\nSome content";
58
+ const title = "My Plan Title";
59
+ await planService.create(sessionId, content, { title });
60
+ const result = await tool.execute({}, createToolContext(logger, { sessionId }));
61
+ expect(result.exists).toBe(true);
62
+ expect(result.content).toBe(content);
63
+ expect(result.status).toBe("draft");
64
+ expect(result.title).toBe(title);
65
+ expect(result.path).toBe(path.join(tempDir, sessionId, "plan.md"));
32
66
  });
33
- afterEach(async () => {
34
- try {
35
- await fs.rm(tempDir, { recursive: true, force: true });
36
- }
37
- catch {
38
- // Ignore cleanup errors
39
- }
67
+ it("should return ISO timestamps", async () => {
68
+ const tool = createPlanReadTool(async () => planService);
69
+ const sessionId = "test-session";
70
+ await planService.create(sessionId, "# Plan");
71
+ const result = await tool.execute({}, createToolContext(logger, { sessionId }));
72
+ expect(result.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
73
+ expect(result.updatedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
40
74
  });
41
- describe('execute', () => {
42
- it('should return exists: false when no plan exists', async () => {
43
- const tool = createPlanReadTool(planService);
44
- const sessionId = 'test-session';
45
- const result = (await tool.execute({}, { sessionId }));
46
- expect(result.exists).toBe(false);
47
- expect(result.message).toContain('No plan found');
48
- });
49
- it('should return plan content and metadata when plan exists', async () => {
50
- const tool = createPlanReadTool(planService);
51
- const sessionId = 'test-session';
52
- const content = '# My Plan\n\nSome content';
53
- const title = 'My Plan Title';
54
- await planService.create(sessionId, content, { title });
55
- const result = (await tool.execute({}, { sessionId }));
56
- expect(result.exists).toBe(true);
57
- expect(result.content).toBe(content);
58
- expect(result.status).toBe('draft');
59
- expect(result.title).toBe(title);
60
- expect(result.path).toBe(`.dexto/plans/${sessionId}/plan.md`);
61
- });
62
- it('should return ISO timestamps', async () => {
63
- const tool = createPlanReadTool(planService);
64
- const sessionId = 'test-session';
65
- await planService.create(sessionId, '# Plan');
66
- const result = (await tool.execute({}, { sessionId }));
67
- // Should be ISO format
68
- expect(result.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
69
- expect(result.updatedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
70
- });
71
- it('should throw error when sessionId is missing', async () => {
72
- const tool = createPlanReadTool(planService);
73
- try {
74
- await tool.execute({}, {});
75
- expect.fail('Should have thrown an error');
76
- }
77
- catch (error) {
78
- expect(error).toBeInstanceOf(DextoRuntimeError);
79
- expect(error.code).toBe(PlanErrorCode.SESSION_ID_REQUIRED);
80
- }
81
- });
75
+ it("should throw error when sessionId is missing", async () => {
76
+ const tool = createPlanReadTool(async () => planService);
77
+ try {
78
+ await tool.execute({}, createToolContext(logger));
79
+ expect.fail("Should have thrown an error");
80
+ } catch (error) {
81
+ expect(error).toBeInstanceOf(DextoRuntimeError);
82
+ expect(error.code).toBe(PlanErrorCode.SESSION_ID_REQUIRED);
83
+ }
82
84
  });
85
+ });
83
86
  });
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var plan_review_tool_exports = {};
20
+ __export(plan_review_tool_exports, {
21
+ createPlanReviewTool: () => createPlanReviewTool
22
+ });
23
+ module.exports = __toCommonJS(plan_review_tool_exports);
24
+ var import_zod = require("zod");
25
+ var import_core = require("@dexto/core");
26
+ var import_errors = require("../errors.js");
27
+ const PlanReviewInputSchema = import_zod.z.object({
28
+ summary: import_zod.z.string().optional().describe("Brief summary of the plan for context (shown above the plan content)")
29
+ }).strict();
30
+ function createPlanReviewTool(getPlanService) {
31
+ return (0, import_core.defineTool)({
32
+ id: "plan_review",
33
+ description: "Request user review of the current plan. Shows the full plan content for review with options to approve, request changes, or reject. Use after creating or updating a plan to get user approval before implementation.",
34
+ inputSchema: PlanReviewInputSchema,
35
+ presentation: {
36
+ describeHeader: (input) => (0, import_core.createLocalToolCallHeader)({
37
+ title: "Review Plan",
38
+ ...input.summary ? { argsText: (0, import_core.truncateForHeader)(input.summary, 140) } : {}
39
+ }),
40
+ /**
41
+ * Generate preview showing the plan content for review.
42
+ * The ApprovalPrompt component detects plan_review and shows custom options.
43
+ */
44
+ preview: async (input, context) => {
45
+ const resolvedPlanService = await getPlanService(context);
46
+ const { summary } = input;
47
+ if (!context.sessionId) {
48
+ throw import_errors.PlanError.sessionIdRequired();
49
+ }
50
+ const plan = await resolvedPlanService.read(context.sessionId);
51
+ if (!plan) {
52
+ throw import_errors.PlanError.planNotFound(context.sessionId);
53
+ }
54
+ let displayContent = plan.content;
55
+ if (summary) {
56
+ displayContent = `## Summary
57
+ ${summary}
58
+
59
+ ---
60
+
61
+ ${plan.content}`;
62
+ }
63
+ const lineCount = displayContent.split("\n").length;
64
+ const planPath = resolvedPlanService.getPlanPath(context.sessionId);
65
+ return {
66
+ type: "file",
67
+ title: "Review Plan",
68
+ path: planPath,
69
+ operation: "read",
70
+ // 'read' indicates this is for viewing, not creating/modifying
71
+ content: displayContent,
72
+ size: Buffer.byteLength(displayContent, "utf8"),
73
+ lineCount
74
+ };
75
+ }
76
+ },
77
+ async execute(_input, context) {
78
+ const resolvedPlanService = await getPlanService(context);
79
+ if (!context.sessionId) {
80
+ throw import_errors.PlanError.sessionIdRequired();
81
+ }
82
+ const plan = await resolvedPlanService.read(context.sessionId);
83
+ if (!plan) {
84
+ throw import_errors.PlanError.planNotFound(context.sessionId);
85
+ }
86
+ await resolvedPlanService.updateMeta(context.sessionId, { status: "approved" });
87
+ return {
88
+ approved: true,
89
+ message: "Plan approved. You may now proceed with implementation.",
90
+ planStatus: "approved"
91
+ };
92
+ }
93
+ });
94
+ }
95
+ // Annotate the CommonJS export names for ESM import in node:
96
+ 0 && (module.exports = {
97
+ createPlanReviewTool
98
+ });
@@ -8,15 +8,24 @@
8
8
  * - Request Changes: Provide feedback for iteration
9
9
  * - Reject: Reject the plan entirely
10
10
  *
11
- * Uses the tool confirmation pattern (not elicitation) so the user
11
+ * Uses the tool approval pattern (not elicitation) so the user
12
12
  * can see the full plan content before deciding.
13
13
  */
14
- import type { InternalTool } from '@dexto/core';
15
- import type { PlanService } from '../plan-service.js';
14
+ import { z } from 'zod';
15
+ import type { Tool } from '@dexto/core';
16
+ import type { PlanServiceGetter } from '../plan-service-getter.js';
17
+ declare const PlanReviewInputSchema: z.ZodObject<{
18
+ summary: z.ZodOptional<z.ZodString>;
19
+ }, "strict", z.ZodTypeAny, {
20
+ summary?: string | undefined;
21
+ }, {
22
+ summary?: string | undefined;
23
+ }>;
16
24
  /**
17
25
  * Creates the plan_review tool
18
26
  *
19
- * @param planService - Service for plan operations
27
+ * @param getPlanService - Getter for the plan service
20
28
  */
21
- export declare function createPlanReviewTool(planService: PlanService): InternalTool;
29
+ export declare function createPlanReviewTool(getPlanService: PlanServiceGetter): Tool<typeof PlanReviewInputSchema>;
30
+ export {};
22
31
  //# sourceMappingURL=plan-review-tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan-review-tool.d.ts","sourceRoot":"","sources":["../../src/tools/plan-review-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAyC,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AActD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,WAAW,GAAG,YAAY,CAoE3E"}
1
+ {"version":3,"file":"plan-review-tool.d.ts","sourceRoot":"","sources":["../../src/tools/plan-review-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,IAAI,EAAyC,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAGnE,QAAA,MAAM,qBAAqB;;;;;;EAOd,CAAC;AAEd;;;;GAIG;AACH,wBAAgB,oBAAoB,CAChC,cAAc,EAAE,iBAAiB,GAClC,IAAI,CAAC,OAAO,qBAAqB,CAAC,CA2EpC"}
@@ -1,84 +1,74 @@
1
- /**
2
- * Plan Review Tool
3
- *
4
- * Requests user review of the current plan.
5
- * Shows the plan content for review with approval options:
6
- * - Approve: Proceed with implementation
7
- * - Approve + Accept Edits: Proceed and auto-approve file edits
8
- * - Request Changes: Provide feedback for iteration
9
- * - Reject: Reject the plan entirely
10
- *
11
- * Uses the tool confirmation pattern (not elicitation) so the user
12
- * can see the full plan content before deciding.
13
- */
14
- import { z } from 'zod';
15
- import { PlanError } from '../errors.js';
16
- const PlanReviewInputSchema = z
17
- .object({
18
- summary: z
19
- .string()
20
- .optional()
21
- .describe('Brief summary of the plan for context (shown above the plan content)'),
22
- })
23
- .strict();
24
- /**
25
- * Creates the plan_review tool
26
- *
27
- * @param planService - Service for plan operations
28
- */
29
- export function createPlanReviewTool(planService) {
30
- return {
31
- id: 'plan_review',
32
- description: 'Request user review of the current plan. Shows the full plan content for review with options to approve, request changes, or reject. Use after creating or updating a plan to get user approval before implementation.',
33
- inputSchema: PlanReviewInputSchema,
34
- /**
35
- * Generate preview showing the plan content for review.
36
- * The ApprovalPrompt component detects plan_review and shows custom options.
37
- */
38
- generatePreview: async (input, context) => {
39
- const { summary } = input;
40
- if (!context?.sessionId) {
41
- throw PlanError.sessionIdRequired();
42
- }
43
- // Read the current plan
44
- const plan = await planService.read(context.sessionId);
45
- if (!plan) {
46
- throw PlanError.planNotFound(context.sessionId);
47
- }
48
- // Build content with optional summary header
49
- let displayContent = plan.content;
50
- if (summary) {
51
- displayContent = `## Summary\n${summary}\n\n---\n\n${plan.content}`;
52
- }
53
- const lineCount = displayContent.split('\n').length;
54
- const planPath = planService.getPlanPath(context.sessionId);
55
- return {
56
- type: 'file',
57
- path: planPath,
58
- operation: 'read', // 'read' indicates this is for viewing, not creating/modifying
59
- content: displayContent,
60
- size: Buffer.byteLength(displayContent, 'utf8'),
61
- lineCount,
62
- };
63
- },
64
- execute: async (_input, context) => {
65
- // Tool execution means user approved the plan (selected Approve or Approve + Accept Edits)
66
- // Request Changes and Reject are handled as denials in the approval flow
67
- if (!context?.sessionId) {
68
- throw PlanError.sessionIdRequired();
69
- }
70
- // Read plan to verify it still exists
71
- const plan = await planService.read(context.sessionId);
72
- if (!plan) {
73
- throw PlanError.planNotFound(context.sessionId);
74
- }
75
- // Update plan status to approved
76
- await planService.updateMeta(context.sessionId, { status: 'approved' });
77
- return {
78
- approved: true,
79
- message: 'Plan approved. You may now proceed with implementation.',
80
- planStatus: 'approved',
81
- };
82
- },
83
- };
1
+ import { z } from "zod";
2
+ import { createLocalToolCallHeader, defineTool, truncateForHeader } from "@dexto/core";
3
+ import { PlanError } from "../errors.js";
4
+ const PlanReviewInputSchema = z.object({
5
+ summary: z.string().optional().describe("Brief summary of the plan for context (shown above the plan content)")
6
+ }).strict();
7
+ function createPlanReviewTool(getPlanService) {
8
+ return defineTool({
9
+ id: "plan_review",
10
+ description: "Request user review of the current plan. Shows the full plan content for review with options to approve, request changes, or reject. Use after creating or updating a plan to get user approval before implementation.",
11
+ inputSchema: PlanReviewInputSchema,
12
+ presentation: {
13
+ describeHeader: (input) => createLocalToolCallHeader({
14
+ title: "Review Plan",
15
+ ...input.summary ? { argsText: truncateForHeader(input.summary, 140) } : {}
16
+ }),
17
+ /**
18
+ * Generate preview showing the plan content for review.
19
+ * The ApprovalPrompt component detects plan_review and shows custom options.
20
+ */
21
+ preview: async (input, context) => {
22
+ const resolvedPlanService = await getPlanService(context);
23
+ const { summary } = input;
24
+ if (!context.sessionId) {
25
+ throw PlanError.sessionIdRequired();
26
+ }
27
+ const plan = await resolvedPlanService.read(context.sessionId);
28
+ if (!plan) {
29
+ throw PlanError.planNotFound(context.sessionId);
30
+ }
31
+ let displayContent = plan.content;
32
+ if (summary) {
33
+ displayContent = `## Summary
34
+ ${summary}
35
+
36
+ ---
37
+
38
+ ${plan.content}`;
39
+ }
40
+ const lineCount = displayContent.split("\n").length;
41
+ const planPath = resolvedPlanService.getPlanPath(context.sessionId);
42
+ return {
43
+ type: "file",
44
+ title: "Review Plan",
45
+ path: planPath,
46
+ operation: "read",
47
+ // 'read' indicates this is for viewing, not creating/modifying
48
+ content: displayContent,
49
+ size: Buffer.byteLength(displayContent, "utf8"),
50
+ lineCount
51
+ };
52
+ }
53
+ },
54
+ async execute(_input, context) {
55
+ const resolvedPlanService = await getPlanService(context);
56
+ if (!context.sessionId) {
57
+ throw PlanError.sessionIdRequired();
58
+ }
59
+ const plan = await resolvedPlanService.read(context.sessionId);
60
+ if (!plan) {
61
+ throw PlanError.planNotFound(context.sessionId);
62
+ }
63
+ await resolvedPlanService.updateMeta(context.sessionId, { status: "approved" });
64
+ return {
65
+ approved: true,
66
+ message: "Plan approved. You may now proceed with implementation.",
67
+ planStatus: "approved"
68
+ };
69
+ }
70
+ });
84
71
  }
72
+ export {
73
+ createPlanReviewTool
74
+ };
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var plan_update_tool_exports = {};
20
+ __export(plan_update_tool_exports, {
21
+ createPlanUpdateTool: () => createPlanUpdateTool
22
+ });
23
+ module.exports = __toCommonJS(plan_update_tool_exports);
24
+ var import_zod = require("zod");
25
+ var import_diff = require("diff");
26
+ var import_core = require("@dexto/core");
27
+ var import_errors = require("../errors.js");
28
+ const PlanUpdateInputSchema = import_zod.z.object({
29
+ content: import_zod.z.string().describe("Updated plan content in markdown format")
30
+ }).strict();
31
+ function generateDiffPreview(filePath, originalContent, newContent) {
32
+ const unified = (0, import_diff.createPatch)(filePath, originalContent, newContent, "before", "after", {
33
+ context: 3
34
+ });
35
+ const additions = (unified.match(/^\+[^+]/gm) || []).length;
36
+ const deletions = (unified.match(/^-[^-]/gm) || []).length;
37
+ return {
38
+ type: "diff",
39
+ title: "Update Plan",
40
+ unified,
41
+ filename: filePath,
42
+ additions,
43
+ deletions
44
+ };
45
+ }
46
+ function createPlanUpdateTool(getPlanService) {
47
+ return (0, import_core.defineTool)({
48
+ id: "plan_update",
49
+ description: "Update the existing implementation plan for this session. Shows a diff preview for approval before saving. The plan must already exist (use plan_create first).",
50
+ inputSchema: PlanUpdateInputSchema,
51
+ presentation: {
52
+ describeHeader: () => (0, import_core.createLocalToolCallHeader)({
53
+ title: "Update Plan"
54
+ }),
55
+ /**
56
+ * Generate diff preview for approval UI
57
+ */
58
+ preview: async (input, context) => {
59
+ const resolvedPlanService = await getPlanService(context);
60
+ const { content: newContent } = input;
61
+ if (!context.sessionId) {
62
+ throw import_errors.PlanError.sessionIdRequired();
63
+ }
64
+ const existing = await resolvedPlanService.read(context.sessionId);
65
+ if (!existing) {
66
+ throw import_errors.PlanError.planNotFound(context.sessionId);
67
+ }
68
+ const planPath = resolvedPlanService.getPlanPath(context.sessionId);
69
+ return generateDiffPreview(planPath, existing.content, newContent);
70
+ }
71
+ },
72
+ async execute(input, context) {
73
+ const resolvedPlanService = await getPlanService(context);
74
+ const { content } = input;
75
+ if (!context.sessionId) {
76
+ throw import_errors.PlanError.sessionIdRequired();
77
+ }
78
+ const result = await resolvedPlanService.update(context.sessionId, content);
79
+ const planPath = resolvedPlanService.getPlanPath(context.sessionId);
80
+ return {
81
+ success: true,
82
+ path: planPath,
83
+ status: result.meta.status,
84
+ _display: generateDiffPreview(planPath, result.oldContent, result.newContent)
85
+ };
86
+ }
87
+ });
88
+ }
89
+ // Annotate the CommonJS export names for ESM import in node:
90
+ 0 && (module.exports = {
91
+ createPlanUpdateTool
92
+ });
@@ -4,10 +4,19 @@
4
4
  * Updates the implementation plan for the current session.
5
5
  * Shows a diff preview for approval before saving.
6
6
  */
7
- import type { InternalTool } from '@dexto/core';
8
- import type { PlanService } from '../plan-service.js';
7
+ import { z } from 'zod';
8
+ import type { Tool } from '@dexto/core';
9
+ import type { PlanServiceGetter } from '../plan-service-getter.js';
10
+ declare const PlanUpdateInputSchema: z.ZodObject<{
11
+ content: z.ZodString;
12
+ }, "strict", z.ZodTypeAny, {
13
+ content: string;
14
+ }, {
15
+ content: string;
16
+ }>;
9
17
  /**
10
18
  * Creates the plan_update tool
11
19
  */
12
- export declare function createPlanUpdateTool(planService: PlanService): InternalTool;
20
+ export declare function createPlanUpdateTool(getPlanService: PlanServiceGetter): Tool<typeof PlanUpdateInputSchema>;
21
+ export {};
13
22
  //# sourceMappingURL=plan-update-tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan-update-tool.d.ts","sourceRoot":"","sources":["../../src/tools/plan-update-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAyC,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAkCtD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,WAAW,GAAG,YAAY,CAiD3E"}
1
+ {"version":3,"file":"plan-update-tool.d.ts","sourceRoot":"","sources":["../../src/tools/plan-update-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,IAAI,EAAyC,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAGnE,QAAA,MAAM,qBAAqB;;;;;;EAId,CAAC;AA0Bd;;GAEG;AACH,wBAAgB,oBAAoB,CAChC,cAAc,EAAE,iBAAiB,GAClC,IAAI,CAAC,OAAO,qBAAqB,CAAC,CAsDpC"}