@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,118 +1,151 @@
1
- /**
2
- * Plan Create Tool Tests
3
- *
4
- * Tests for the plan_create tool including preview generation.
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 { createPlanCreateTool } from './plan-create-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 { createPlanCreateTool } from "./plan-create-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_create tool', () => {
23
- let mockLogger;
24
- let tempDir;
25
- let planService;
26
- beforeEach(async () => {
27
- mockLogger = createMockLogger();
28
- // Create temp directory for testing
29
- const rawTempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dexto-plan-create-test-'));
30
- tempDir = await fs.realpath(rawTempDir);
31
- planService = new PlanService({ basePath: tempDir }, mockLogger);
32
- 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_create 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-create-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("generatePreview", () => {
47
+ it("should return FileDisplayData for new plan", async () => {
48
+ const tool = createPlanCreateTool(async () => planService);
49
+ const sessionId = "test-session";
50
+ const content = "# Implementation Plan\n\n## Steps\n1. First step";
51
+ const previewFn = tool.presentation?.preview;
52
+ expect(previewFn).toBeDefined();
53
+ const preview = await previewFn(
54
+ { title: "Test Plan", content },
55
+ createToolContext(logger, { sessionId })
56
+ );
57
+ expect(preview.type).toBe("file");
58
+ expect(preview.title).toBe("Create Plan");
59
+ expect(preview.operation).toBe("create");
60
+ expect(preview.path).toContain(sessionId);
61
+ expect(preview.path).toMatch(/plan\.md$/);
62
+ expect(preview.content).toBe(content);
63
+ expect(preview.lineCount).toBe(4);
64
+ });
65
+ it("should throw error when sessionId is missing", async () => {
66
+ const tool = createPlanCreateTool(async () => planService);
67
+ const previewFn = tool.presentation?.preview;
68
+ expect(previewFn).toBeDefined();
69
+ try {
70
+ await previewFn({ title: "Test", content: "# Plan" }, createToolContext(logger));
71
+ expect.fail("Should have thrown an error");
72
+ } catch (error) {
73
+ expect(error).toBeInstanceOf(DextoRuntimeError);
74
+ expect(error.code).toBe(PlanErrorCode.SESSION_ID_REQUIRED);
75
+ }
76
+ });
77
+ it("should throw error when plan already exists", async () => {
78
+ const tool = createPlanCreateTool(async () => planService);
79
+ const sessionId = "test-session";
80
+ const previewFn = tool.presentation?.preview;
81
+ expect(previewFn).toBeDefined();
82
+ await planService.create(sessionId, "# Existing Plan");
83
+ try {
84
+ await previewFn(
85
+ { title: "New Plan", content: "# New Content" },
86
+ createToolContext(logger, { sessionId })
87
+ );
88
+ expect.fail("Should have thrown an error");
89
+ } catch (error) {
90
+ expect(error).toBeInstanceOf(DextoRuntimeError);
91
+ expect(error.code).toBe(PlanErrorCode.PLAN_ALREADY_EXISTS);
92
+ }
93
+ });
94
+ });
95
+ describe("execute", () => {
96
+ it("should create plan and return success", async () => {
97
+ const tool = createPlanCreateTool(async () => planService);
98
+ const sessionId = "test-session";
99
+ const content = "# Implementation Plan";
100
+ const title = "My Plan";
101
+ const result = await tool.execute(
102
+ { title, content },
103
+ createToolContext(logger, { sessionId })
104
+ );
105
+ expect(result.success).toBe(true);
106
+ expect(result.path).toContain(sessionId);
107
+ expect(result.path).toMatch(/plan\.md$/);
108
+ expect(result.status).toBe("draft");
109
+ expect(result.title).toBe(title);
33
110
  });
34
- afterEach(async () => {
35
- try {
36
- await fs.rm(tempDir, { recursive: true, force: true });
37
- }
38
- catch {
39
- // Ignore cleanup errors
40
- }
111
+ it("should throw error when plan already exists", async () => {
112
+ const tool = createPlanCreateTool(async () => planService);
113
+ const sessionId = "test-session";
114
+ await planService.create(sessionId, "# Existing Plan");
115
+ try {
116
+ await tool.execute(
117
+ { title: "New Plan", content: "# New content" },
118
+ createToolContext(logger, { sessionId })
119
+ );
120
+ expect.fail("Should have thrown an error");
121
+ } catch (error) {
122
+ expect(error).toBeInstanceOf(DextoRuntimeError);
123
+ expect(error.code).toBe(PlanErrorCode.PLAN_ALREADY_EXISTS);
124
+ }
41
125
  });
42
- describe('generatePreview', () => {
43
- it('should return FileDisplayData for new plan', async () => {
44
- const tool = createPlanCreateTool(planService);
45
- const sessionId = 'test-session';
46
- const content = '# Implementation Plan\n\n## Steps\n1. First step';
47
- const preview = (await tool.generatePreview({ title: 'Test Plan', content }, { sessionId }));
48
- expect(preview.type).toBe('file');
49
- expect(preview.operation).toBe('create');
50
- // Path is now absolute, check it ends with the expected suffix
51
- expect(preview.path).toContain(sessionId);
52
- expect(preview.path).toMatch(/plan\.md$/);
53
- expect(preview.content).toBe(content);
54
- expect(preview.lineCount).toBe(4);
55
- });
56
- it('should throw error when sessionId is missing', async () => {
57
- const tool = createPlanCreateTool(planService);
58
- try {
59
- await tool.generatePreview({ title: 'Test', content: '# Plan' }, {});
60
- expect.fail('Should have thrown an error');
61
- }
62
- catch (error) {
63
- expect(error).toBeInstanceOf(DextoRuntimeError);
64
- expect(error.code).toBe(PlanErrorCode.SESSION_ID_REQUIRED);
65
- }
66
- });
67
- it('should throw error when plan already exists', async () => {
68
- const tool = createPlanCreateTool(planService);
69
- const sessionId = 'test-session';
70
- // Create existing plan
71
- await planService.create(sessionId, '# Existing Plan');
72
- try {
73
- await tool.generatePreview({ title: 'New Plan', content: '# New Content' }, { sessionId });
74
- expect.fail('Should have thrown an error');
75
- }
76
- catch (error) {
77
- expect(error).toBeInstanceOf(DextoRuntimeError);
78
- expect(error.code).toBe(PlanErrorCode.PLAN_ALREADY_EXISTS);
79
- }
80
- });
126
+ it("should throw error when sessionId is missing", async () => {
127
+ const tool = createPlanCreateTool(async () => planService);
128
+ try {
129
+ await tool.execute({ title: "Test", content: "# Plan" }, createToolContext(logger));
130
+ expect.fail("Should have thrown an error");
131
+ } catch (error) {
132
+ expect(error).toBeInstanceOf(DextoRuntimeError);
133
+ expect(error.code).toBe(PlanErrorCode.SESSION_ID_REQUIRED);
134
+ }
81
135
  });
82
- describe('execute', () => {
83
- it('should create plan and return success', async () => {
84
- const tool = createPlanCreateTool(planService);
85
- const sessionId = 'test-session';
86
- const content = '# Implementation Plan';
87
- const title = 'My Plan';
88
- const result = (await tool.execute({ title, content }, { sessionId }));
89
- expect(result.success).toBe(true);
90
- // Path is now absolute, check it ends with the expected suffix
91
- expect(result.path).toContain(sessionId);
92
- expect(result.path).toMatch(/plan\.md$/);
93
- expect(result.status).toBe('draft');
94
- expect(result.title).toBe(title);
95
- });
96
- it('should throw error when sessionId is missing', async () => {
97
- const tool = createPlanCreateTool(planService);
98
- try {
99
- await tool.execute({ title: 'Test', content: '# Plan' }, {});
100
- expect.fail('Should have thrown an error');
101
- }
102
- catch (error) {
103
- expect(error).toBeInstanceOf(DextoRuntimeError);
104
- expect(error.code).toBe(PlanErrorCode.SESSION_ID_REQUIRED);
105
- }
106
- });
107
- it('should include _display data in result', async () => {
108
- const tool = createPlanCreateTool(planService);
109
- const sessionId = 'test-session';
110
- const content = '# Plan\n## Steps';
111
- const result = (await tool.execute({ title: 'Plan', content }, { sessionId }));
112
- expect(result._display).toBeDefined();
113
- expect(result._display.type).toBe('file');
114
- expect(result._display.operation).toBe('create');
115
- expect(result._display.lineCount).toBe(2);
116
- });
136
+ it("should include _display data in result", async () => {
137
+ const tool = createPlanCreateTool(async () => planService);
138
+ const sessionId = "test-session";
139
+ const content = "# Plan\n## Steps";
140
+ const result = await tool.execute(
141
+ { title: "Plan", content },
142
+ createToolContext(logger, { sessionId })
143
+ );
144
+ expect(result._display).toBeDefined();
145
+ expect(result._display.type).toBe("file");
146
+ expect(result._display.title).toBe("Create Plan");
147
+ expect(result._display.operation).toBe("create");
148
+ expect(result._display.lineCount).toBe(2);
117
149
  });
150
+ });
118
151
  });
@@ -0,0 +1,65 @@
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_read_tool_exports = {};
20
+ __export(plan_read_tool_exports, {
21
+ createPlanReadTool: () => createPlanReadTool
22
+ });
23
+ module.exports = __toCommonJS(plan_read_tool_exports);
24
+ var import_zod = require("zod");
25
+ var import_core = require("@dexto/core");
26
+ var import_errors = require("../errors.js");
27
+ const PlanReadInputSchema = import_zod.z.object({}).strict();
28
+ function createPlanReadTool(getPlanService) {
29
+ return (0, import_core.defineTool)({
30
+ id: "plan_read",
31
+ description: "Read the current implementation plan for this session. Returns the plan content and metadata including status. Use markdown checkboxes (- [ ] and - [x]) in the content to track progress.",
32
+ inputSchema: PlanReadInputSchema,
33
+ presentation: {
34
+ describeHeader: () => (0, import_core.createLocalToolCallHeader)({
35
+ title: "Read Plan"
36
+ })
37
+ },
38
+ async execute(_input, context) {
39
+ const resolvedPlanService = await getPlanService(context);
40
+ if (!context.sessionId) {
41
+ throw import_errors.PlanError.sessionIdRequired();
42
+ }
43
+ const plan = await resolvedPlanService.read(context.sessionId);
44
+ if (!plan) {
45
+ return {
46
+ exists: false,
47
+ message: `No plan found for this session. Use plan_create to create one.`
48
+ };
49
+ }
50
+ return {
51
+ exists: true,
52
+ path: resolvedPlanService.getPlanPath(context.sessionId),
53
+ content: plan.content,
54
+ status: plan.meta.status,
55
+ title: plan.meta.title,
56
+ createdAt: new Date(plan.meta.createdAt).toISOString(),
57
+ updatedAt: new Date(plan.meta.updatedAt).toISOString()
58
+ };
59
+ }
60
+ });
61
+ }
62
+ // Annotate the CommonJS export names for ESM import in node:
63
+ 0 && (module.exports = {
64
+ createPlanReadTool
65
+ });
@@ -4,10 +4,13 @@
4
4
  * Reads the current implementation plan for the session.
5
5
  * No approval needed - read-only operation.
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 PlanReadInputSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
9
11
  /**
10
12
  * Creates the plan_read tool
11
13
  */
12
- export declare function createPlanReadTool(planService: PlanService): InternalTool;
14
+ export declare function createPlanReadTool(getPlanService: PlanServiceGetter): Tool<typeof PlanReadInputSchema>;
15
+ export {};
13
16
  //# sourceMappingURL=plan-read-tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan-read-tool.d.ts","sourceRoot":"","sources":["../../src/tools/plan-read-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAwB,MAAM,aAAa,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKtD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,YAAY,CAgCzE"}
1
+ {"version":3,"file":"plan-read-tool.d.ts","sourceRoot":"","sources":["../../src/tools/plan-read-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAGnE,QAAA,MAAM,mBAAmB,iDAAwB,CAAC;AAElD;;GAEG;AACH,wBAAgB,kBAAkB,CAC9B,cAAc,EAAE,iBAAiB,GAClC,IAAI,CAAC,OAAO,mBAAmB,CAAC,CAuClC"}
@@ -1,40 +1,41 @@
1
- /**
2
- * Plan Read Tool
3
- *
4
- * Reads the current implementation plan for the session.
5
- * No approval needed - read-only operation.
6
- */
7
- import { z } from 'zod';
8
- import { PlanError } from '../errors.js';
1
+ import { z } from "zod";
2
+ import { createLocalToolCallHeader, defineTool } from "@dexto/core";
3
+ import { PlanError } from "../errors.js";
9
4
  const PlanReadInputSchema = z.object({}).strict();
10
- /**
11
- * Creates the plan_read tool
12
- */
13
- export function createPlanReadTool(planService) {
14
- return {
15
- id: 'plan_read',
16
- description: 'Read the current implementation plan for this session. Returns the plan content and metadata including status. Use markdown checkboxes (- [ ] and - [x]) in the content to track progress.',
17
- inputSchema: PlanReadInputSchema,
18
- execute: async (_input, context) => {
19
- if (!context?.sessionId) {
20
- throw PlanError.sessionIdRequired();
21
- }
22
- const plan = await planService.read(context.sessionId);
23
- if (!plan) {
24
- return {
25
- exists: false,
26
- message: `No plan found for this session. Use plan_create to create one.`,
27
- };
28
- }
29
- return {
30
- exists: true,
31
- path: `.dexto/plans/${context.sessionId}/plan.md`,
32
- content: plan.content,
33
- status: plan.meta.status,
34
- title: plan.meta.title,
35
- createdAt: new Date(plan.meta.createdAt).toISOString(),
36
- updatedAt: new Date(plan.meta.updatedAt).toISOString(),
37
- };
38
- },
39
- };
5
+ function createPlanReadTool(getPlanService) {
6
+ return defineTool({
7
+ id: "plan_read",
8
+ description: "Read the current implementation plan for this session. Returns the plan content and metadata including status. Use markdown checkboxes (- [ ] and - [x]) in the content to track progress.",
9
+ inputSchema: PlanReadInputSchema,
10
+ presentation: {
11
+ describeHeader: () => createLocalToolCallHeader({
12
+ title: "Read Plan"
13
+ })
14
+ },
15
+ async execute(_input, context) {
16
+ const resolvedPlanService = await getPlanService(context);
17
+ if (!context.sessionId) {
18
+ throw PlanError.sessionIdRequired();
19
+ }
20
+ const plan = await resolvedPlanService.read(context.sessionId);
21
+ if (!plan) {
22
+ return {
23
+ exists: false,
24
+ message: `No plan found for this session. Use plan_create to create one.`
25
+ };
26
+ }
27
+ return {
28
+ exists: true,
29
+ path: resolvedPlanService.getPlanPath(context.sessionId),
30
+ content: plan.content,
31
+ status: plan.meta.status,
32
+ title: plan.meta.title,
33
+ createdAt: new Date(plan.meta.createdAt).toISOString(),
34
+ updatedAt: new Date(plan.meta.updatedAt).toISOString()
35
+ };
36
+ }
37
+ });
40
38
  }
39
+ export {
40
+ createPlanReadTool
41
+ };
@@ -0,0 +1,109 @@
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_read_tool = require("./plan-read-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_read 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-read-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)("execute", () => {
70
+ (0, import_vitest.it)("should return exists: false when no plan exists", async () => {
71
+ const tool = (0, import_plan_read_tool.createPlanReadTool)(async () => planService);
72
+ const sessionId = "test-session";
73
+ const result = await tool.execute({}, createToolContext(logger, { sessionId }));
74
+ (0, import_vitest.expect)(result.exists).toBe(false);
75
+ (0, import_vitest.expect)(result.message).toContain("No plan found");
76
+ });
77
+ (0, import_vitest.it)("should return plan content and metadata when plan exists", async () => {
78
+ const tool = (0, import_plan_read_tool.createPlanReadTool)(async () => planService);
79
+ const sessionId = "test-session";
80
+ const content = "# My Plan\n\nSome content";
81
+ const title = "My Plan Title";
82
+ await planService.create(sessionId, content, { title });
83
+ const result = await tool.execute({}, createToolContext(logger, { sessionId }));
84
+ (0, import_vitest.expect)(result.exists).toBe(true);
85
+ (0, import_vitest.expect)(result.content).toBe(content);
86
+ (0, import_vitest.expect)(result.status).toBe("draft");
87
+ (0, import_vitest.expect)(result.title).toBe(title);
88
+ (0, import_vitest.expect)(result.path).toBe(path.join(tempDir, sessionId, "plan.md"));
89
+ });
90
+ (0, import_vitest.it)("should return ISO timestamps", async () => {
91
+ const tool = (0, import_plan_read_tool.createPlanReadTool)(async () => planService);
92
+ const sessionId = "test-session";
93
+ await planService.create(sessionId, "# Plan");
94
+ const result = await tool.execute({}, createToolContext(logger, { sessionId }));
95
+ (0, import_vitest.expect)(result.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
96
+ (0, import_vitest.expect)(result.updatedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
97
+ });
98
+ (0, import_vitest.it)("should throw error when sessionId is missing", async () => {
99
+ const tool = (0, import_plan_read_tool.createPlanReadTool)(async () => planService);
100
+ try {
101
+ await tool.execute({}, createToolContext(logger));
102
+ import_vitest.expect.fail("Should have thrown an error");
103
+ } catch (error) {
104
+ (0, import_vitest.expect)(error).toBeInstanceOf(import_core.DextoRuntimeError);
105
+ (0, import_vitest.expect)(error.code).toBe(import_errors.PlanErrorCode.SESSION_ID_REQUIRED);
106
+ }
107
+ });
108
+ });
109
+ });