@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
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_tool_factory = require("./tool-factory.js");
4
+ (0, import_vitest.describe)("planToolsFactory", () => {
5
+ (0, import_vitest.describe)("factory metadata", () => {
6
+ (0, import_vitest.it)("should have metadata", () => {
7
+ (0, import_vitest.expect)(import_tool_factory.planToolsFactory.metadata).toBeDefined();
8
+ (0, import_vitest.expect)(import_tool_factory.planToolsFactory.metadata?.displayName).toBe("Plan Tools");
9
+ (0, import_vitest.expect)(import_tool_factory.planToolsFactory.metadata?.category).toBe("planning");
10
+ });
11
+ });
12
+ (0, import_vitest.describe)("config schema", () => {
13
+ (0, import_vitest.it)("should validate minimal config", () => {
14
+ const result = import_tool_factory.planToolsFactory.configSchema.safeParse({
15
+ type: "plan-tools"
16
+ });
17
+ (0, import_vitest.expect)(result.success).toBe(true);
18
+ if (result.success) {
19
+ (0, import_vitest.expect)(result.data.basePath).toBe(".dexto/plans");
20
+ }
21
+ });
22
+ (0, import_vitest.it)("should validate config with custom basePath", () => {
23
+ const result = import_tool_factory.planToolsFactory.configSchema.safeParse({
24
+ type: "plan-tools",
25
+ basePath: "/custom/path"
26
+ });
27
+ (0, import_vitest.expect)(result.success).toBe(true);
28
+ if (result.success) {
29
+ (0, import_vitest.expect)(result.data.basePath).toBe("/custom/path");
30
+ }
31
+ });
32
+ (0, import_vitest.it)("should validate config with enabledTools", () => {
33
+ const result = import_tool_factory.planToolsFactory.configSchema.safeParse({
34
+ type: "plan-tools",
35
+ enabledTools: ["plan_create", "plan_read"]
36
+ });
37
+ (0, import_vitest.expect)(result.success).toBe(true);
38
+ if (result.success) {
39
+ (0, import_vitest.expect)(result.data.enabledTools).toEqual(["plan_create", "plan_read"]);
40
+ }
41
+ });
42
+ (0, import_vitest.it)("should reject invalid tool names", () => {
43
+ const result = import_tool_factory.planToolsFactory.configSchema.safeParse({
44
+ type: "plan-tools",
45
+ enabledTools: ["invalid_tool"]
46
+ });
47
+ (0, import_vitest.expect)(result.success).toBe(false);
48
+ });
49
+ (0, import_vitest.it)("should reject unknown properties", () => {
50
+ const result = import_tool_factory.planToolsFactory.configSchema.safeParse({
51
+ type: "plan-tools",
52
+ unknownProp: "value"
53
+ });
54
+ (0, import_vitest.expect)(result.success).toBe(false);
55
+ });
56
+ });
57
+ (0, import_vitest.describe)("create", () => {
58
+ (0, import_vitest.it)("should create all tools by default", () => {
59
+ const config = import_tool_factory.planToolsFactory.configSchema.parse({
60
+ type: "plan-tools"
61
+ });
62
+ const tools = import_tool_factory.planToolsFactory.create(config);
63
+ (0, import_vitest.expect)(tools).toHaveLength(4);
64
+ const toolIds = tools.map((t) => t.id);
65
+ (0, import_vitest.expect)(toolIds).toContain("plan_create");
66
+ (0, import_vitest.expect)(toolIds).toContain("plan_read");
67
+ (0, import_vitest.expect)(toolIds).toContain("plan_update");
68
+ (0, import_vitest.expect)(toolIds).toContain("plan_review");
69
+ });
70
+ (0, import_vitest.it)("should create only enabled tools", () => {
71
+ const config = import_tool_factory.planToolsFactory.configSchema.parse({
72
+ type: "plan-tools",
73
+ enabledTools: ["plan_create", "plan_read"]
74
+ });
75
+ const tools = import_tool_factory.planToolsFactory.create(config);
76
+ (0, import_vitest.expect)(tools).toHaveLength(2);
77
+ const toolIds = tools.map((t) => t.id);
78
+ (0, import_vitest.expect)(toolIds).toContain("plan_create");
79
+ (0, import_vitest.expect)(toolIds).toContain("plan_read");
80
+ (0, import_vitest.expect)(toolIds).not.toContain("plan_update");
81
+ });
82
+ });
83
+ (0, import_vitest.describe)("tool definitions", () => {
84
+ (0, import_vitest.it)("should have descriptions and input schemas for all tools", () => {
85
+ const config = import_tool_factory.planToolsFactory.configSchema.parse({
86
+ type: "plan-tools"
87
+ });
88
+ const tools = import_tool_factory.planToolsFactory.create(config);
89
+ for (const tool of tools) {
90
+ (0, import_vitest.expect)(tool.description).toBeDefined();
91
+ (0, import_vitest.expect)(tool.description.length).toBeGreaterThan(0);
92
+ (0, import_vitest.expect)(tool.inputSchema).toBeDefined();
93
+ }
94
+ });
95
+ });
96
+ });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Plan Tools Factory Tests
3
+ *
4
+ * Validates the plan tools factory schema and tool creation.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=tool-factory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-factory.test.d.ts","sourceRoot":"","sources":["../src/tool-factory.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,95 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { planToolsFactory } from "./tool-factory.js";
3
+ describe("planToolsFactory", () => {
4
+ describe("factory metadata", () => {
5
+ it("should have metadata", () => {
6
+ expect(planToolsFactory.metadata).toBeDefined();
7
+ expect(planToolsFactory.metadata?.displayName).toBe("Plan Tools");
8
+ expect(planToolsFactory.metadata?.category).toBe("planning");
9
+ });
10
+ });
11
+ describe("config schema", () => {
12
+ it("should validate minimal config", () => {
13
+ const result = planToolsFactory.configSchema.safeParse({
14
+ type: "plan-tools"
15
+ });
16
+ expect(result.success).toBe(true);
17
+ if (result.success) {
18
+ expect(result.data.basePath).toBe(".dexto/plans");
19
+ }
20
+ });
21
+ it("should validate config with custom basePath", () => {
22
+ const result = planToolsFactory.configSchema.safeParse({
23
+ type: "plan-tools",
24
+ basePath: "/custom/path"
25
+ });
26
+ expect(result.success).toBe(true);
27
+ if (result.success) {
28
+ expect(result.data.basePath).toBe("/custom/path");
29
+ }
30
+ });
31
+ it("should validate config with enabledTools", () => {
32
+ const result = planToolsFactory.configSchema.safeParse({
33
+ type: "plan-tools",
34
+ enabledTools: ["plan_create", "plan_read"]
35
+ });
36
+ expect(result.success).toBe(true);
37
+ if (result.success) {
38
+ expect(result.data.enabledTools).toEqual(["plan_create", "plan_read"]);
39
+ }
40
+ });
41
+ it("should reject invalid tool names", () => {
42
+ const result = planToolsFactory.configSchema.safeParse({
43
+ type: "plan-tools",
44
+ enabledTools: ["invalid_tool"]
45
+ });
46
+ expect(result.success).toBe(false);
47
+ });
48
+ it("should reject unknown properties", () => {
49
+ const result = planToolsFactory.configSchema.safeParse({
50
+ type: "plan-tools",
51
+ unknownProp: "value"
52
+ });
53
+ expect(result.success).toBe(false);
54
+ });
55
+ });
56
+ describe("create", () => {
57
+ it("should create all tools by default", () => {
58
+ const config = planToolsFactory.configSchema.parse({
59
+ type: "plan-tools"
60
+ });
61
+ const tools = planToolsFactory.create(config);
62
+ expect(tools).toHaveLength(4);
63
+ const toolIds = tools.map((t) => t.id);
64
+ expect(toolIds).toContain("plan_create");
65
+ expect(toolIds).toContain("plan_read");
66
+ expect(toolIds).toContain("plan_update");
67
+ expect(toolIds).toContain("plan_review");
68
+ });
69
+ it("should create only enabled tools", () => {
70
+ const config = planToolsFactory.configSchema.parse({
71
+ type: "plan-tools",
72
+ enabledTools: ["plan_create", "plan_read"]
73
+ });
74
+ const tools = planToolsFactory.create(config);
75
+ expect(tools).toHaveLength(2);
76
+ const toolIds = tools.map((t) => t.id);
77
+ expect(toolIds).toContain("plan_create");
78
+ expect(toolIds).toContain("plan_read");
79
+ expect(toolIds).not.toContain("plan_update");
80
+ });
81
+ });
82
+ describe("tool definitions", () => {
83
+ it("should have descriptions and input schemas for all tools", () => {
84
+ const config = planToolsFactory.configSchema.parse({
85
+ type: "plan-tools"
86
+ });
87
+ const tools = planToolsFactory.create(config);
88
+ for (const tool of tools) {
89
+ expect(tool.description).toBeDefined();
90
+ expect(tool.description.length).toBeGreaterThan(0);
91
+ expect(tool.inputSchema).toBeDefined();
92
+ }
93
+ });
94
+ });
95
+ });
@@ -0,0 +1,102 @@
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_create_tool_exports = {};
20
+ __export(plan_create_tool_exports, {
21
+ createPlanCreateTool: () => createPlanCreateTool
22
+ });
23
+ module.exports = __toCommonJS(plan_create_tool_exports);
24
+ var import_zod = require("zod");
25
+ var import_core = require("@dexto/core");
26
+ var import_errors = require("../errors.js");
27
+ const PlanCreateInputSchema = import_zod.z.object({
28
+ title: import_zod.z.string().describe('Plan title (e.g., "Add User Authentication")'),
29
+ content: import_zod.z.string().describe(
30
+ "Plan content in markdown format. Use - [ ] and - [x] for checkboxes to track progress."
31
+ )
32
+ }).strict();
33
+ function createPlanCreateTool(getPlanService) {
34
+ return (0, import_core.defineTool)({
35
+ id: "plan_create",
36
+ description: "Create a new implementation plan for the current session. Shows the plan for approval before saving. Use markdown format for the plan content with clear steps and file references.",
37
+ inputSchema: PlanCreateInputSchema,
38
+ presentation: {
39
+ describeHeader: (input) => (0, import_core.createLocalToolCallHeader)({
40
+ title: "Create Plan",
41
+ argsText: (0, import_core.truncateForHeader)(input.title, 140)
42
+ }),
43
+ /**
44
+ * Generate preview for approval UI
45
+ */
46
+ preview: async (input, context) => {
47
+ const { content } = input;
48
+ if (!context.sessionId) {
49
+ throw import_errors.PlanError.sessionIdRequired();
50
+ }
51
+ const resolvedPlanService = await getPlanService(context);
52
+ const exists = await resolvedPlanService.exists(context.sessionId);
53
+ if (exists) {
54
+ throw import_errors.PlanError.planAlreadyExists(context.sessionId);
55
+ }
56
+ const lineCount = content.split("\n").length;
57
+ const planPath = resolvedPlanService.getPlanPath(context.sessionId);
58
+ return {
59
+ type: "file",
60
+ title: "Create Plan",
61
+ path: planPath,
62
+ operation: "create",
63
+ content,
64
+ size: Buffer.byteLength(content, "utf8"),
65
+ lineCount
66
+ };
67
+ }
68
+ },
69
+ async execute(input, context) {
70
+ const { title, content } = input;
71
+ if (!context.sessionId) {
72
+ throw import_errors.PlanError.sessionIdRequired();
73
+ }
74
+ const resolvedPlanService = await getPlanService(context);
75
+ const exists = await resolvedPlanService.exists(context.sessionId);
76
+ if (exists) {
77
+ throw import_errors.PlanError.planAlreadyExists(context.sessionId);
78
+ }
79
+ const plan = await resolvedPlanService.create(context.sessionId, content, { title });
80
+ const planPath = resolvedPlanService.getPlanPath(context.sessionId);
81
+ const _display = {
82
+ type: "file",
83
+ title: "Create Plan",
84
+ path: planPath,
85
+ operation: "create",
86
+ size: Buffer.byteLength(content, "utf8"),
87
+ lineCount: content.split("\n").length
88
+ };
89
+ return {
90
+ success: true,
91
+ path: planPath,
92
+ status: plan.meta.status,
93
+ title: plan.meta.title,
94
+ _display
95
+ };
96
+ }
97
+ });
98
+ }
99
+ // Annotate the CommonJS export names for ESM import in node:
100
+ 0 && (module.exports = {
101
+ createPlanCreateTool
102
+ });
@@ -4,10 +4,22 @@
4
4
  * Creates a new implementation plan for the current session.
5
5
  * Shows a 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 PlanCreateInputSchema: z.ZodObject<{
11
+ title: z.ZodString;
12
+ content: z.ZodString;
13
+ }, "strict", z.ZodTypeAny, {
14
+ title: string;
15
+ content: string;
16
+ }, {
17
+ title: string;
18
+ content: string;
19
+ }>;
9
20
  /**
10
21
  * Creates the plan_create tool
11
22
  */
12
- export declare function createPlanCreateTool(planService: PlanService): InternalTool;
23
+ export declare function createPlanCreateTool(getPlanService: PlanServiceGetter): Tool<typeof PlanCreateInputSchema>;
24
+ export {};
13
25
  //# sourceMappingURL=plan-create-tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan-create-tool.d.ts","sourceRoot":"","sources":["../../src/tools/plan-create-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAyC,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAgBtD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,WAAW,GAAG,YAAY,CAgE3E"}
1
+ {"version":3,"file":"plan-create-tool.d.ts","sourceRoot":"","sources":["../../src/tools/plan-create-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;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;;;;;;;;;EASd,CAAC;AAEd;;GAEG;AACH,wBAAgB,oBAAoB,CAChC,cAAc,EAAE,iBAAiB,GAClC,IAAI,CAAC,OAAO,qBAAqB,CAAC,CAkFpC"}
@@ -1,72 +1,78 @@
1
- /**
2
- * Plan Create Tool
3
- *
4
- * Creates a new implementation plan for the current session.
5
- * Shows a preview for approval before saving.
6
- */
7
- import { z } from 'zod';
8
- import { PlanError } from '../errors.js';
9
- const PlanCreateInputSchema = z
10
- .object({
11
- title: z.string().describe('Plan title (e.g., "Add User Authentication")'),
12
- content: z
13
- .string()
14
- .describe('Plan content in markdown format. Use - [ ] and - [x] for checkboxes to track progress.'),
15
- })
16
- .strict();
17
- /**
18
- * Creates the plan_create tool
19
- */
20
- export function createPlanCreateTool(planService) {
21
- return {
22
- id: 'plan_create',
23
- description: 'Create a new implementation plan for the current session. Shows the plan for approval before saving. Use markdown format for the plan content with clear steps and file references.',
24
- inputSchema: PlanCreateInputSchema,
25
- /**
26
- * Generate preview for approval UI
27
- */
28
- generatePreview: async (input, context) => {
29
- const { content } = input;
30
- if (!context?.sessionId) {
31
- throw PlanError.sessionIdRequired();
32
- }
33
- // Check if plan already exists
34
- const exists = await planService.exists(context.sessionId);
35
- if (exists) {
36
- throw PlanError.planAlreadyExists(context.sessionId);
37
- }
38
- // Return preview for approval UI
39
- const lineCount = content.split('\n').length;
40
- const planPath = planService.getPlanPath(context.sessionId);
41
- return {
42
- type: 'file',
43
- path: planPath,
44
- operation: 'create',
45
- content,
46
- size: content.length,
47
- lineCount,
48
- };
49
- },
50
- execute: async (input, context) => {
51
- const { title, content } = input;
52
- if (!context?.sessionId) {
53
- throw PlanError.sessionIdRequired();
54
- }
55
- const plan = await planService.create(context.sessionId, content, { title });
56
- const planPath = planService.getPlanPath(context.sessionId);
57
- return {
58
- success: true,
59
- path: planPath,
60
- status: plan.meta.status,
61
- title: plan.meta.title,
62
- _display: {
63
- type: 'file',
64
- path: planPath,
65
- operation: 'create',
66
- size: content.length,
67
- lineCount: content.split('\n').length,
68
- },
69
- };
70
- },
71
- };
1
+ import { z } from "zod";
2
+ import { createLocalToolCallHeader, defineTool, truncateForHeader } from "@dexto/core";
3
+ import { PlanError } from "../errors.js";
4
+ const PlanCreateInputSchema = z.object({
5
+ title: z.string().describe('Plan title (e.g., "Add User Authentication")'),
6
+ content: z.string().describe(
7
+ "Plan content in markdown format. Use - [ ] and - [x] for checkboxes to track progress."
8
+ )
9
+ }).strict();
10
+ function createPlanCreateTool(getPlanService) {
11
+ return defineTool({
12
+ id: "plan_create",
13
+ description: "Create a new implementation plan for the current session. Shows the plan for approval before saving. Use markdown format for the plan content with clear steps and file references.",
14
+ inputSchema: PlanCreateInputSchema,
15
+ presentation: {
16
+ describeHeader: (input) => createLocalToolCallHeader({
17
+ title: "Create Plan",
18
+ argsText: truncateForHeader(input.title, 140)
19
+ }),
20
+ /**
21
+ * Generate preview for approval UI
22
+ */
23
+ preview: async (input, context) => {
24
+ const { content } = input;
25
+ if (!context.sessionId) {
26
+ throw PlanError.sessionIdRequired();
27
+ }
28
+ const resolvedPlanService = await getPlanService(context);
29
+ const exists = await resolvedPlanService.exists(context.sessionId);
30
+ if (exists) {
31
+ throw PlanError.planAlreadyExists(context.sessionId);
32
+ }
33
+ const lineCount = content.split("\n").length;
34
+ const planPath = resolvedPlanService.getPlanPath(context.sessionId);
35
+ return {
36
+ type: "file",
37
+ title: "Create Plan",
38
+ path: planPath,
39
+ operation: "create",
40
+ content,
41
+ size: Buffer.byteLength(content, "utf8"),
42
+ lineCount
43
+ };
44
+ }
45
+ },
46
+ async execute(input, context) {
47
+ const { title, content } = input;
48
+ if (!context.sessionId) {
49
+ throw PlanError.sessionIdRequired();
50
+ }
51
+ const resolvedPlanService = await getPlanService(context);
52
+ const exists = await resolvedPlanService.exists(context.sessionId);
53
+ if (exists) {
54
+ throw PlanError.planAlreadyExists(context.sessionId);
55
+ }
56
+ const plan = await resolvedPlanService.create(context.sessionId, content, { title });
57
+ const planPath = resolvedPlanService.getPlanPath(context.sessionId);
58
+ const _display = {
59
+ type: "file",
60
+ title: "Create Plan",
61
+ path: planPath,
62
+ operation: "create",
63
+ size: Buffer.byteLength(content, "utf8"),
64
+ lineCount: content.split("\n").length
65
+ };
66
+ return {
67
+ success: true,
68
+ path: planPath,
69
+ status: plan.meta.status,
70
+ title: plan.meta.title,
71
+ _display
72
+ };
73
+ }
74
+ });
72
75
  }
76
+ export {
77
+ createPlanCreateTool
78
+ };
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var import_vitest = require("vitest");
25
+ var path = __toESM(require("node:path"), 1);
26
+ var fs = __toESM(require("node:fs/promises"), 1);
27
+ var os = __toESM(require("node:os"), 1);
28
+ var import_plan_create_tool = require("./plan-create-tool.js");
29
+ var import_plan_service = require("../plan-service.js");
30
+ var import_errors = require("../errors.js");
31
+ var import_core = require("@dexto/core");
32
+ const createMockLogger = () => {
33
+ const logger = {
34
+ debug: import_vitest.vi.fn(),
35
+ silly: import_vitest.vi.fn(),
36
+ info: import_vitest.vi.fn(),
37
+ warn: import_vitest.vi.fn(),
38
+ error: import_vitest.vi.fn(),
39
+ trackException: import_vitest.vi.fn(),
40
+ createChild: import_vitest.vi.fn(() => logger),
41
+ createFileOnlyChild: import_vitest.vi.fn(() => logger),
42
+ setLevel: import_vitest.vi.fn(),
43
+ getLevel: import_vitest.vi.fn(() => "debug"),
44
+ getLogFilePath: import_vitest.vi.fn(() => null),
45
+ destroy: import_vitest.vi.fn(async () => void 0)
46
+ };
47
+ return logger;
48
+ };
49
+ function createToolContext(logger, overrides = {}) {
50
+ return { logger, ...overrides };
51
+ }
52
+ (0, import_vitest.describe)("plan_create tool", () => {
53
+ let logger;
54
+ let tempDir;
55
+ let planService;
56
+ (0, import_vitest.beforeEach)(async () => {
57
+ logger = createMockLogger();
58
+ const rawTempDir = await fs.mkdtemp(path.join(os.tmpdir(), "dexto-plan-create-test-"));
59
+ tempDir = await fs.realpath(rawTempDir);
60
+ planService = new import_plan_service.PlanService({ basePath: tempDir }, logger);
61
+ import_vitest.vi.clearAllMocks();
62
+ });
63
+ (0, import_vitest.afterEach)(async () => {
64
+ try {
65
+ await fs.rm(tempDir, { recursive: true, force: true });
66
+ } catch {
67
+ }
68
+ });
69
+ (0, import_vitest.describe)("generatePreview", () => {
70
+ (0, import_vitest.it)("should return FileDisplayData for new plan", async () => {
71
+ const tool = (0, import_plan_create_tool.createPlanCreateTool)(async () => planService);
72
+ const sessionId = "test-session";
73
+ const content = "# Implementation Plan\n\n## Steps\n1. First step";
74
+ const previewFn = tool.presentation?.preview;
75
+ (0, import_vitest.expect)(previewFn).toBeDefined();
76
+ const preview = await previewFn(
77
+ { title: "Test Plan", content },
78
+ createToolContext(logger, { sessionId })
79
+ );
80
+ (0, import_vitest.expect)(preview.type).toBe("file");
81
+ (0, import_vitest.expect)(preview.title).toBe("Create Plan");
82
+ (0, import_vitest.expect)(preview.operation).toBe("create");
83
+ (0, import_vitest.expect)(preview.path).toContain(sessionId);
84
+ (0, import_vitest.expect)(preview.path).toMatch(/plan\.md$/);
85
+ (0, import_vitest.expect)(preview.content).toBe(content);
86
+ (0, import_vitest.expect)(preview.lineCount).toBe(4);
87
+ });
88
+ (0, import_vitest.it)("should throw error when sessionId is missing", async () => {
89
+ const tool = (0, import_plan_create_tool.createPlanCreateTool)(async () => planService);
90
+ const previewFn = tool.presentation?.preview;
91
+ (0, import_vitest.expect)(previewFn).toBeDefined();
92
+ try {
93
+ await previewFn({ title: "Test", content: "# Plan" }, createToolContext(logger));
94
+ import_vitest.expect.fail("Should have thrown an error");
95
+ } catch (error) {
96
+ (0, import_vitest.expect)(error).toBeInstanceOf(import_core.DextoRuntimeError);
97
+ (0, import_vitest.expect)(error.code).toBe(import_errors.PlanErrorCode.SESSION_ID_REQUIRED);
98
+ }
99
+ });
100
+ (0, import_vitest.it)("should throw error when plan already exists", async () => {
101
+ const tool = (0, import_plan_create_tool.createPlanCreateTool)(async () => planService);
102
+ const sessionId = "test-session";
103
+ const previewFn = tool.presentation?.preview;
104
+ (0, import_vitest.expect)(previewFn).toBeDefined();
105
+ await planService.create(sessionId, "# Existing Plan");
106
+ try {
107
+ await previewFn(
108
+ { title: "New Plan", content: "# New Content" },
109
+ createToolContext(logger, { sessionId })
110
+ );
111
+ import_vitest.expect.fail("Should have thrown an error");
112
+ } catch (error) {
113
+ (0, import_vitest.expect)(error).toBeInstanceOf(import_core.DextoRuntimeError);
114
+ (0, import_vitest.expect)(error.code).toBe(import_errors.PlanErrorCode.PLAN_ALREADY_EXISTS);
115
+ }
116
+ });
117
+ });
118
+ (0, import_vitest.describe)("execute", () => {
119
+ (0, import_vitest.it)("should create plan and return success", async () => {
120
+ const tool = (0, import_plan_create_tool.createPlanCreateTool)(async () => planService);
121
+ const sessionId = "test-session";
122
+ const content = "# Implementation Plan";
123
+ const title = "My Plan";
124
+ const result = await tool.execute(
125
+ { title, content },
126
+ createToolContext(logger, { sessionId })
127
+ );
128
+ (0, import_vitest.expect)(result.success).toBe(true);
129
+ (0, import_vitest.expect)(result.path).toContain(sessionId);
130
+ (0, import_vitest.expect)(result.path).toMatch(/plan\.md$/);
131
+ (0, import_vitest.expect)(result.status).toBe("draft");
132
+ (0, import_vitest.expect)(result.title).toBe(title);
133
+ });
134
+ (0, import_vitest.it)("should throw error when plan already exists", async () => {
135
+ const tool = (0, import_plan_create_tool.createPlanCreateTool)(async () => planService);
136
+ const sessionId = "test-session";
137
+ await planService.create(sessionId, "# Existing Plan");
138
+ try {
139
+ await tool.execute(
140
+ { title: "New Plan", content: "# New content" },
141
+ createToolContext(logger, { sessionId })
142
+ );
143
+ import_vitest.expect.fail("Should have thrown an error");
144
+ } catch (error) {
145
+ (0, import_vitest.expect)(error).toBeInstanceOf(import_core.DextoRuntimeError);
146
+ (0, import_vitest.expect)(error.code).toBe(import_errors.PlanErrorCode.PLAN_ALREADY_EXISTS);
147
+ }
148
+ });
149
+ (0, import_vitest.it)("should throw error when sessionId is missing", async () => {
150
+ const tool = (0, import_plan_create_tool.createPlanCreateTool)(async () => planService);
151
+ try {
152
+ await tool.execute({ title: "Test", content: "# Plan" }, createToolContext(logger));
153
+ import_vitest.expect.fail("Should have thrown an error");
154
+ } catch (error) {
155
+ (0, import_vitest.expect)(error).toBeInstanceOf(import_core.DextoRuntimeError);
156
+ (0, import_vitest.expect)(error.code).toBe(import_errors.PlanErrorCode.SESSION_ID_REQUIRED);
157
+ }
158
+ });
159
+ (0, import_vitest.it)("should include _display data in result", async () => {
160
+ const tool = (0, import_plan_create_tool.createPlanCreateTool)(async () => planService);
161
+ const sessionId = "test-session";
162
+ const content = "# Plan\n## Steps";
163
+ const result = await tool.execute(
164
+ { title: "Plan", content },
165
+ createToolContext(logger, { sessionId })
166
+ );
167
+ (0, import_vitest.expect)(result._display).toBeDefined();
168
+ (0, import_vitest.expect)(result._display.type).toBe("file");
169
+ (0, import_vitest.expect)(result._display.title).toBe("Create Plan");
170
+ (0, import_vitest.expect)(result._display.operation).toBe("create");
171
+ (0, import_vitest.expect)(result._display.lineCount).toBe(2);
172
+ });
173
+ });
174
+ });