@dexto/tools-plan 1.5.8 → 1.6.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.
Files changed (43) hide show
  1. package/dist/index.d.ts +1 -25
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -29
  4. package/dist/plan-service-getter.d.ts +4 -0
  5. package/dist/plan-service-getter.d.ts.map +1 -0
  6. package/dist/plan-service-getter.js +1 -0
  7. package/dist/plan-service.d.ts +2 -2
  8. package/dist/plan-service.d.ts.map +1 -1
  9. package/dist/plan-service.js +7 -7
  10. package/dist/plan-service.test.js +6 -0
  11. package/dist/tool-factory-config.d.ts +32 -0
  12. package/dist/tool-factory-config.d.ts.map +1 -0
  13. package/dist/tool-factory-config.js +30 -0
  14. package/dist/tool-factory.d.ts +4 -0
  15. package/dist/tool-factory.d.ts.map +1 -0
  16. package/dist/tool-factory.js +36 -0
  17. package/dist/tool-factory.test.d.ts +7 -0
  18. package/dist/tool-factory.test.d.ts.map +1 -0
  19. package/dist/tool-factory.test.js +100 -0
  20. package/dist/tools/plan-create-tool.d.ts +15 -3
  21. package/dist/tools/plan-create-tool.d.ts.map +1 -1
  22. package/dist/tools/plan-create-tool.js +29 -18
  23. package/dist/tools/plan-create-tool.test.js +47 -22
  24. package/dist/tools/plan-read-tool.d.ts +6 -3
  25. package/dist/tools/plan-read-tool.d.ts.map +1 -1
  26. package/dist/tools/plan-read-tool.js +10 -7
  27. package/dist/tools/plan-read-tool.test.js +31 -19
  28. package/dist/tools/plan-review-tool.d.ts +14 -5
  29. package/dist/tools/plan-review-tool.d.ts.map +1 -1
  30. package/dist/tools/plan-review-tool.js +16 -12
  31. package/dist/tools/plan-update-tool.d.ts +12 -3
  32. package/dist/tools/plan-update-tool.d.ts.map +1 -1
  33. package/dist/tools/plan-update-tool.js +14 -10
  34. package/dist/tools/plan-update-tool.test.js +40 -28
  35. package/package.json +4 -5
  36. package/.dexto-plugin/plugin.json +0 -7
  37. package/dist/tool-provider.d.ts +0 -44
  38. package/dist/tool-provider.d.ts.map +0 -1
  39. package/dist/tool-provider.js +0 -81
  40. package/dist/tool-provider.test.d.ts +0 -7
  41. package/dist/tool-provider.test.d.ts.map +0 -1
  42. package/dist/tool-provider.test.js +0 -185
  43. package/skills/plan/SKILL.md +0 -102
@@ -12,22 +12,34 @@ import { PlanService } from '../plan-service.js';
12
12
  import { PlanErrorCode } from '../errors.js';
13
13
  import { DextoRuntimeError } from '@dexto/core';
14
14
  // Create mock logger
15
- const createMockLogger = () => ({
16
- debug: vi.fn(),
17
- info: vi.fn(),
18
- warn: vi.fn(),
19
- error: vi.fn(),
20
- createChild: vi.fn().mockReturnThis(),
21
- });
15
+ const createMockLogger = () => {
16
+ const logger = {
17
+ debug: vi.fn(),
18
+ silly: vi.fn(),
19
+ info: vi.fn(),
20
+ warn: vi.fn(),
21
+ error: vi.fn(),
22
+ trackException: vi.fn(),
23
+ createChild: vi.fn(() => logger),
24
+ setLevel: vi.fn(),
25
+ getLevel: vi.fn(() => 'debug'),
26
+ getLogFilePath: vi.fn(() => null),
27
+ destroy: vi.fn(async () => undefined),
28
+ };
29
+ return logger;
30
+ };
31
+ function createToolContext(logger, overrides = {}) {
32
+ return { logger, ...overrides };
33
+ }
22
34
  describe('plan_update tool', () => {
23
- let mockLogger;
35
+ let logger;
24
36
  let tempDir;
25
37
  let planService;
26
38
  beforeEach(async () => {
27
- mockLogger = createMockLogger();
39
+ logger = createMockLogger();
28
40
  const rawTempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dexto-plan-update-test-'));
29
41
  tempDir = await fs.realpath(rawTempDir);
30
- planService = new PlanService({ basePath: tempDir }, mockLogger);
42
+ planService = new PlanService({ basePath: tempDir }, logger);
31
43
  vi.clearAllMocks();
32
44
  });
33
45
  afterEach(async () => {
@@ -40,12 +52,12 @@ describe('plan_update tool', () => {
40
52
  });
41
53
  describe('generatePreview', () => {
42
54
  it('should return DiffDisplayData with unified diff', async () => {
43
- const tool = createPlanUpdateTool(planService);
55
+ const tool = createPlanUpdateTool(async () => planService);
44
56
  const sessionId = 'test-session';
45
57
  const originalContent = '# Plan\n\n## Steps\n1. First step';
46
58
  const newContent = '# Plan\n\n## Steps\n1. First step\n2. Second step';
47
59
  await planService.create(sessionId, originalContent);
48
- const preview = (await tool.generatePreview({ content: newContent }, { sessionId }));
60
+ const preview = (await tool.generatePreview({ content: newContent }, createToolContext(logger, { sessionId })));
49
61
  expect(preview.type).toBe('diff');
50
62
  // Path is now absolute, check it ends with the expected suffix
51
63
  expect(preview.filename).toContain(sessionId);
@@ -56,10 +68,10 @@ describe('plan_update tool', () => {
56
68
  expect(preview.additions).toBeGreaterThan(0);
57
69
  });
58
70
  it('should throw error when plan does not exist', async () => {
59
- const tool = createPlanUpdateTool(planService);
71
+ const tool = createPlanUpdateTool(async () => planService);
60
72
  const sessionId = 'test-session';
61
73
  try {
62
- await tool.generatePreview({ content: '# New Content' }, { sessionId });
74
+ await tool.generatePreview({ content: '# New Content' }, createToolContext(logger, { sessionId }));
63
75
  expect.fail('Should have thrown an error');
64
76
  }
65
77
  catch (error) {
@@ -68,9 +80,9 @@ describe('plan_update tool', () => {
68
80
  }
69
81
  });
70
82
  it('should throw error when sessionId is missing', async () => {
71
- const tool = createPlanUpdateTool(planService);
83
+ const tool = createPlanUpdateTool(async () => planService);
72
84
  try {
73
- await tool.generatePreview({ content: '# Content' }, {});
85
+ await tool.generatePreview({ content: '# Content' }, createToolContext(logger));
74
86
  expect.fail('Should have thrown an error');
75
87
  }
76
88
  catch (error) {
@@ -79,24 +91,24 @@ describe('plan_update tool', () => {
79
91
  }
80
92
  });
81
93
  it('should show deletions in diff', async () => {
82
- const tool = createPlanUpdateTool(planService);
94
+ const tool = createPlanUpdateTool(async () => planService);
83
95
  const sessionId = 'test-session';
84
96
  const originalContent = '# Plan\n\nLine to remove\nKeep this';
85
97
  const newContent = '# Plan\n\nKeep this';
86
98
  await planService.create(sessionId, originalContent);
87
- const preview = (await tool.generatePreview({ content: newContent }, { sessionId }));
99
+ const preview = (await tool.generatePreview({ content: newContent }, createToolContext(logger, { sessionId })));
88
100
  expect(preview.deletions).toBeGreaterThan(0);
89
101
  expect(preview.unified).toContain('-Line to remove');
90
102
  });
91
103
  });
92
104
  describe('execute', () => {
93
105
  it('should update plan content and return success', async () => {
94
- const tool = createPlanUpdateTool(planService);
106
+ const tool = createPlanUpdateTool(async () => planService);
95
107
  const sessionId = 'test-session';
96
108
  const originalContent = '# Original Plan';
97
109
  const newContent = '# Updated Plan';
98
110
  await planService.create(sessionId, originalContent);
99
- const result = (await tool.execute({ content: newContent }, { sessionId }));
111
+ const result = (await tool.execute({ content: newContent }, createToolContext(logger, { sessionId })));
100
112
  expect(result.success).toBe(true);
101
113
  // Path is now absolute, check it ends with the expected suffix
102
114
  expect(result.path).toContain(sessionId);
@@ -106,20 +118,20 @@ describe('plan_update tool', () => {
106
118
  expect(plan.content).toBe(newContent);
107
119
  });
108
120
  it('should include _display data with diff', async () => {
109
- const tool = createPlanUpdateTool(planService);
121
+ const tool = createPlanUpdateTool(async () => planService);
110
122
  const sessionId = 'test-session';
111
123
  await planService.create(sessionId, '# Original');
112
- const result = (await tool.execute({ content: '# Updated' }, { sessionId }));
124
+ const result = (await tool.execute({ content: '# Updated' }, createToolContext(logger, { sessionId })));
113
125
  expect(result._display).toBeDefined();
114
126
  expect(result._display.type).toBe('diff');
115
127
  expect(result._display.unified).toContain('-# Original');
116
128
  expect(result._display.unified).toContain('+# Updated');
117
129
  });
118
130
  it('should throw error when plan does not exist', async () => {
119
- const tool = createPlanUpdateTool(planService);
131
+ const tool = createPlanUpdateTool(async () => planService);
120
132
  const sessionId = 'non-existent';
121
133
  try {
122
- await tool.execute({ content: '# Content' }, { sessionId });
134
+ await tool.execute({ content: '# Content' }, createToolContext(logger, { sessionId }));
123
135
  expect.fail('Should have thrown an error');
124
136
  }
125
137
  catch (error) {
@@ -128,9 +140,9 @@ describe('plan_update tool', () => {
128
140
  }
129
141
  });
130
142
  it('should throw error when sessionId is missing', async () => {
131
- const tool = createPlanUpdateTool(planService);
143
+ const tool = createPlanUpdateTool(async () => planService);
132
144
  try {
133
- await tool.execute({ content: '# Content' }, {});
145
+ await tool.execute({ content: '# Content' }, createToolContext(logger));
134
146
  expect.fail('Should have thrown an error');
135
147
  }
136
148
  catch (error) {
@@ -139,11 +151,11 @@ describe('plan_update tool', () => {
139
151
  }
140
152
  });
141
153
  it('should preserve plan status after update', async () => {
142
- const tool = createPlanUpdateTool(planService);
154
+ const tool = createPlanUpdateTool(async () => planService);
143
155
  const sessionId = 'test-session';
144
156
  await planService.create(sessionId, '# Plan');
145
157
  await planService.updateMeta(sessionId, { status: 'approved' });
146
- await tool.execute({ content: '# Updated Plan' }, { sessionId });
158
+ await tool.execute({ content: '# Updated Plan' }, createToolContext(logger, { sessionId }));
147
159
  const plan = await planService.read(sessionId);
148
160
  expect(plan.meta.status).toBe('approved');
149
161
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexto/tools-plan",
3
- "version": "1.5.8",
3
+ "version": "1.6.0",
4
4
  "description": "Implementation planning tools with session-linked plans",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,14 +12,13 @@
12
12
  }
13
13
  },
14
14
  "files": [
15
- "dist",
16
- ".dexto-plugin",
17
- "skills"
15
+ "dist"
18
16
  ],
19
17
  "dependencies": {
20
18
  "diff": "^7.0.0",
21
19
  "zod": "^3.24.1",
22
- "@dexto/core": "1.5.8"
20
+ "@dexto/agent-config": "1.6.0",
21
+ "@dexto/core": "1.6.0"
23
22
  },
24
23
  "devDependencies": {
25
24
  "@types/diff": "^7.0.0",
@@ -1,7 +0,0 @@
1
- {
2
- "name": "plan-tools",
3
- "version": "0.1.0",
4
- "description": "Implementation planning tools with session-linked plans. Create, read, and update plans tied to your session.",
5
- "author": "Dexto",
6
- "customToolProviders": ["plan-tools"]
7
- }
@@ -1,44 +0,0 @@
1
- /**
2
- * Plan Tools Provider
3
- *
4
- * Provides implementation planning tools:
5
- * - plan_create: Create a new plan for the session
6
- * - plan_read: Read the current plan
7
- * - plan_update: Update the existing plan
8
- * - plan_review: Request user review of the plan (shows plan content with approval options)
9
- */
10
- import { z } from 'zod';
11
- import type { CustomToolProvider } from '@dexto/core';
12
- /**
13
- * Configuration schema for Plan tools provider
14
- */
15
- declare const PlanToolsConfigSchema: z.ZodObject<{
16
- type: z.ZodLiteral<"plan-tools">;
17
- basePath: z.ZodDefault<z.ZodString>;
18
- enabledTools: z.ZodOptional<z.ZodArray<z.ZodEnum<["plan_create", "plan_read", "plan_update", "plan_review"]>, "many">>;
19
- }, "strict", z.ZodTypeAny, {
20
- type: "plan-tools";
21
- basePath: string;
22
- enabledTools?: ("plan_create" | "plan_read" | "plan_update" | "plan_review")[] | undefined;
23
- }, {
24
- type: "plan-tools";
25
- basePath?: string | undefined;
26
- enabledTools?: ("plan_create" | "plan_read" | "plan_update" | "plan_review")[] | undefined;
27
- }>;
28
- type PlanToolsConfig = z.output<typeof PlanToolsConfigSchema>;
29
- /**
30
- * Plan tools provider
31
- *
32
- * Provides implementation planning tools:
33
- * - plan_create: Create a new plan with markdown content
34
- * - plan_read: Read the current plan
35
- * - plan_update: Update existing plan (shows diff preview)
36
- * - plan_review: Request user review of the plan (shows plan with approval options)
37
- *
38
- * Plans are stored in .dexto/plans/{sessionId}/ with:
39
- * - plan.md: Markdown content with checkboxes (- [ ] and - [x])
40
- * - plan-meta.json: Metadata (status, title, timestamps)
41
- */
42
- export declare const planToolsProvider: CustomToolProvider<'plan-tools', PlanToolsConfig>;
43
- export {};
44
- //# sourceMappingURL=tool-provider.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-provider.d.ts","sourceRoot":"","sources":["../src/tool-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,kBAAkB,EAAqC,MAAM,aAAa,CAAC;AAazF;;GAEG;AACH,QAAA,MAAM,qBAAqB;;;;;;;;;;;;EAcd,CAAC;AAEd,KAAK,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE9D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,EAAE,kBAAkB,CAAC,YAAY,EAAE,eAAe,CAwC/E,CAAC"}
@@ -1,81 +0,0 @@
1
- /**
2
- * Plan Tools Provider
3
- *
4
- * Provides implementation planning tools:
5
- * - plan_create: Create a new plan for the session
6
- * - plan_read: Read the current plan
7
- * - plan_update: Update the existing plan
8
- * - plan_review: Request user review of the plan (shows plan content with approval options)
9
- */
10
- import * as path from 'node:path';
11
- import { z } from 'zod';
12
- import { PlanService } from './plan-service.js';
13
- import { createPlanCreateTool } from './tools/plan-create-tool.js';
14
- import { createPlanReadTool } from './tools/plan-read-tool.js';
15
- import { createPlanUpdateTool } from './tools/plan-update-tool.js';
16
- import { createPlanReviewTool } from './tools/plan-review-tool.js';
17
- /**
18
- * Available plan tool names for enabledTools configuration
19
- */
20
- const PLAN_TOOL_NAMES = ['plan_create', 'plan_read', 'plan_update', 'plan_review'];
21
- /**
22
- * Configuration schema for Plan tools provider
23
- */
24
- const PlanToolsConfigSchema = z
25
- .object({
26
- type: z.literal('plan-tools'),
27
- basePath: z
28
- .string()
29
- .default('.dexto/plans')
30
- .describe('Base directory for plan storage (relative to working directory)'),
31
- enabledTools: z
32
- .array(z.enum(PLAN_TOOL_NAMES))
33
- .optional()
34
- .describe(`Subset of tools to enable. If not specified, all tools are enabled. Available: ${PLAN_TOOL_NAMES.join(', ')}`),
35
- })
36
- .strict();
37
- /**
38
- * Plan tools provider
39
- *
40
- * Provides implementation planning tools:
41
- * - plan_create: Create a new plan with markdown content
42
- * - plan_read: Read the current plan
43
- * - plan_update: Update existing plan (shows diff preview)
44
- * - plan_review: Request user review of the plan (shows plan with approval options)
45
- *
46
- * Plans are stored in .dexto/plans/{sessionId}/ with:
47
- * - plan.md: Markdown content with checkboxes (- [ ] and - [x])
48
- * - plan-meta.json: Metadata (status, title, timestamps)
49
- */
50
- export const planToolsProvider = {
51
- type: 'plan-tools',
52
- configSchema: PlanToolsConfigSchema,
53
- create: (config, context) => {
54
- const { logger } = context;
55
- // Resolve base path (relative to cwd or absolute)
56
- const basePath = path.isAbsolute(config.basePath)
57
- ? config.basePath
58
- : path.join(process.cwd(), config.basePath);
59
- logger.debug(`Creating PlanService with basePath: ${basePath}`);
60
- const planService = new PlanService({ basePath }, logger);
61
- // Build tool map for selective enabling
62
- const toolCreators = {
63
- plan_create: () => createPlanCreateTool(planService),
64
- plan_read: () => createPlanReadTool(planService),
65
- plan_update: () => createPlanUpdateTool(planService),
66
- plan_review: () => createPlanReviewTool(planService),
67
- };
68
- // Determine which tools to create
69
- const toolsToCreate = config.enabledTools ?? PLAN_TOOL_NAMES;
70
- if (config.enabledTools) {
71
- logger.debug(`Creating subset of plan tools: ${toolsToCreate.join(', ')}`);
72
- }
73
- // Create and return only the enabled tools
74
- return toolsToCreate.map((toolName) => toolCreators[toolName]());
75
- },
76
- metadata: {
77
- displayName: 'Plan Tools',
78
- description: 'Create and manage implementation plans linked to sessions',
79
- category: 'planning',
80
- },
81
- };
@@ -1,7 +0,0 @@
1
- /**
2
- * Plan Tools Provider Tests
3
- *
4
- * Tests for the planToolsProvider configuration and tool creation.
5
- */
6
- export {};
7
- //# sourceMappingURL=tool-provider.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-provider.test.d.ts","sourceRoot":"","sources":["../src/tool-provider.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -1,185 +0,0 @@
1
- /**
2
- * Plan Tools Provider Tests
3
- *
4
- * Tests for the planToolsProvider configuration and tool creation.
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 { planToolsProvider } from './tool-provider.js';
11
- // Create mock logger
12
- const createMockLogger = () => ({
13
- debug: vi.fn(),
14
- info: vi.fn(),
15
- warn: vi.fn(),
16
- error: vi.fn(),
17
- createChild: vi.fn().mockReturnThis(),
18
- });
19
- // Create mock context with logger and minimal agent
20
- const createMockContext = (logger) => ({
21
- logger: logger,
22
- agent: {}, // Minimal mock - provider only uses logger
23
- });
24
- describe('planToolsProvider', () => {
25
- let mockLogger;
26
- let tempDir;
27
- let originalCwd;
28
- beforeEach(async () => {
29
- mockLogger = createMockLogger();
30
- // Create temp directory for testing
31
- const rawTempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dexto-provider-test-'));
32
- tempDir = await fs.realpath(rawTempDir);
33
- // Store original cwd and mock process.cwd to return temp dir
34
- originalCwd = process.cwd();
35
- vi.spyOn(process, 'cwd').mockReturnValue(tempDir);
36
- vi.clearAllMocks();
37
- });
38
- afterEach(async () => {
39
- // Restore mocked process.cwd
40
- vi.mocked(process.cwd).mockRestore();
41
- try {
42
- await fs.rm(tempDir, { recursive: true, force: true });
43
- }
44
- catch {
45
- // Ignore cleanup errors
46
- }
47
- });
48
- describe('provider metadata', () => {
49
- it('should have correct type', () => {
50
- expect(planToolsProvider.type).toBe('plan-tools');
51
- });
52
- it('should have metadata', () => {
53
- expect(planToolsProvider.metadata).toBeDefined();
54
- expect(planToolsProvider.metadata?.displayName).toBe('Plan Tools');
55
- expect(planToolsProvider.metadata?.category).toBe('planning');
56
- });
57
- });
58
- describe('config schema', () => {
59
- it('should validate minimal config', () => {
60
- const result = planToolsProvider.configSchema.safeParse({
61
- type: 'plan-tools',
62
- });
63
- expect(result.success).toBe(true);
64
- if (result.success) {
65
- expect(result.data.basePath).toBe('.dexto/plans');
66
- }
67
- });
68
- it('should validate config with custom basePath', () => {
69
- const result = planToolsProvider.configSchema.safeParse({
70
- type: 'plan-tools',
71
- basePath: '/custom/path',
72
- });
73
- expect(result.success).toBe(true);
74
- if (result.success) {
75
- expect(result.data.basePath).toBe('/custom/path');
76
- }
77
- });
78
- it('should validate config with enabledTools', () => {
79
- const result = planToolsProvider.configSchema.safeParse({
80
- type: 'plan-tools',
81
- enabledTools: ['plan_create', 'plan_read'],
82
- });
83
- expect(result.success).toBe(true);
84
- if (result.success) {
85
- expect(result.data.enabledTools).toEqual(['plan_create', 'plan_read']);
86
- }
87
- });
88
- it('should reject invalid tool names', () => {
89
- const result = planToolsProvider.configSchema.safeParse({
90
- type: 'plan-tools',
91
- enabledTools: ['invalid_tool'],
92
- });
93
- expect(result.success).toBe(false);
94
- });
95
- it('should reject unknown properties', () => {
96
- const result = planToolsProvider.configSchema.safeParse({
97
- type: 'plan-tools',
98
- unknownProp: 'value',
99
- });
100
- expect(result.success).toBe(false);
101
- });
102
- });
103
- describe('create', () => {
104
- it('should create all tools by default', () => {
105
- const config = planToolsProvider.configSchema.parse({
106
- type: 'plan-tools',
107
- });
108
- const tools = planToolsProvider.create(config, createMockContext(mockLogger));
109
- expect(tools).toHaveLength(4);
110
- const toolIds = tools.map((t) => t.id);
111
- expect(toolIds).toContain('plan_create');
112
- expect(toolIds).toContain('plan_read');
113
- expect(toolIds).toContain('plan_update');
114
- expect(toolIds).toContain('plan_review');
115
- });
116
- it('should create only enabled tools', () => {
117
- const config = planToolsProvider.configSchema.parse({
118
- type: 'plan-tools',
119
- enabledTools: ['plan_create', 'plan_read'],
120
- });
121
- const tools = planToolsProvider.create(config, createMockContext(mockLogger));
122
- expect(tools).toHaveLength(2);
123
- const toolIds = tools.map((t) => t.id);
124
- expect(toolIds).toContain('plan_create');
125
- expect(toolIds).toContain('plan_read');
126
- expect(toolIds).not.toContain('plan_update');
127
- });
128
- it('should create single tool', () => {
129
- const config = planToolsProvider.configSchema.parse({
130
- type: 'plan-tools',
131
- enabledTools: ['plan_update'],
132
- });
133
- const tools = planToolsProvider.create(config, createMockContext(mockLogger));
134
- expect(tools).toHaveLength(1);
135
- expect(tools[0].id).toBe('plan_update');
136
- });
137
- it('should use relative basePath from cwd', () => {
138
- const config = planToolsProvider.configSchema.parse({
139
- type: 'plan-tools',
140
- basePath: '.dexto/plans',
141
- });
142
- planToolsProvider.create(config, createMockContext(mockLogger));
143
- // Verify debug log was called with resolved path
144
- expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining(path.join(tempDir, '.dexto/plans')));
145
- });
146
- it('should use absolute basePath as-is', () => {
147
- const absolutePath = '/absolute/path/to/plans';
148
- const config = planToolsProvider.configSchema.parse({
149
- type: 'plan-tools',
150
- basePath: absolutePath,
151
- });
152
- planToolsProvider.create(config, createMockContext(mockLogger));
153
- expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining(absolutePath));
154
- });
155
- it('should log when creating subset of tools', () => {
156
- const config = planToolsProvider.configSchema.parse({
157
- type: 'plan-tools',
158
- enabledTools: ['plan_create'],
159
- });
160
- planToolsProvider.create(config, createMockContext(mockLogger));
161
- expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining('Creating subset of plan tools'));
162
- });
163
- });
164
- describe('tool descriptions', () => {
165
- it('should have descriptions for all tools', () => {
166
- const config = planToolsProvider.configSchema.parse({
167
- type: 'plan-tools',
168
- });
169
- const tools = planToolsProvider.create(config, createMockContext(mockLogger));
170
- for (const tool of tools) {
171
- expect(tool.description).toBeDefined();
172
- expect(tool.description.length).toBeGreaterThan(0);
173
- }
174
- });
175
- it('should have input schemas for all tools', () => {
176
- const config = planToolsProvider.configSchema.parse({
177
- type: 'plan-tools',
178
- });
179
- const tools = planToolsProvider.create(config, createMockContext(mockLogger));
180
- for (const tool of tools) {
181
- expect(tool.inputSchema).toBeDefined();
182
- }
183
- });
184
- });
185
- });
@@ -1,102 +0,0 @@
1
- ---
2
- name: plan
3
- description: Enter planning mode to create and manage implementation plans
4
- user-invocable: true
5
- ---
6
-
7
- # Planning Mode - PLAN FIRST, THEN IMPLEMENT
8
-
9
- **CRITICAL**: You are in planning mode. You MUST create and get approval for a plan BEFORE writing any code or making any changes.
10
-
11
- ## MANDATORY WORKFLOW
12
-
13
- **DO NOT skip these steps. DO NOT start implementing until the plan is approved.**
14
-
15
- 1. **Research first** (if needed): Use the explore agent or read relevant files to understand the codebase
16
- 2. **Check for existing plan**: Use `plan_read` to see if a plan exists
17
- 3. **Create/update plan**: Use `plan_create` or `plan_update` to define your approach
18
- 4. **Request review**: Use `plan_review` to get user approval
19
- 5. **WAIT for approval**: Only proceed to implementation after user approves
20
- 6. **Implement**: Execute the approved plan, updating checkboxes as you go
21
-
22
- ## Research Phase
23
-
24
- Before creating your plan, you should understand the codebase:
25
-
26
- - **Use the explore agent** (spawn_agent with subagent_type="Explore") to search for relevant code, patterns, and existing implementations
27
- - **Read key files** to understand the current architecture
28
- - **Identify dependencies** and files that will need changes
29
-
30
- This research informs your plan and prevents wasted effort from incorrect assumptions.
31
-
32
- ## Available Tools
33
-
34
- - **plan_create**: Create a new plan (REQUIRED before any implementation)
35
- - **plan_read**: Read the current plan
36
- - **plan_update**: Update the existing plan (shows diff preview)
37
- - **plan_review**: Request user review - returns approve/iterate/reject with feedback
38
-
39
- ## WHAT YOU MUST DO NOW
40
-
41
- 1. **Research**: Use the explore agent or read files to understand the relevant parts of the codebase
42
- 2. **Check plan**: Use `plan_read` to check if a plan already exists
43
- 3. **Create plan**: Use `plan_create` to create a comprehensive plan based on your research
44
- 4. **Get approval**: Use `plan_review` to request user approval
45
- 5. **STOP and WAIT** - do not write any code until the user approves via plan_review
46
-
47
- ## Plan Structure
48
-
49
- ```markdown
50
- # {Title}
51
-
52
- ## Objective
53
- {Clear statement of what we're building/fixing}
54
-
55
- ## Steps
56
-
57
- ### 1. {Step Name}
58
- - [ ] {Task description}
59
- - [ ] {Task description}
60
- Files: `path/to/file.ts`, `path/to/other.ts`
61
-
62
- ### 2. {Step Name}
63
- - [ ] {Task description}
64
- Files: `path/to/file.ts`
65
-
66
- ## Considerations
67
- - {Edge cases to handle}
68
- - {Error scenarios}
69
-
70
- ## Success Criteria
71
- - {How we know we're done}
72
- ```
73
-
74
- ## Guidelines
75
-
76
- - **Break down complex tasks** into clear, sequential steps
77
- - **Include specific file paths** that will be created or modified
78
- - **Note dependencies** between steps
79
- - **Keep plans concise** but complete
80
-
81
- ## Handling Review Responses
82
-
83
- After calling `plan_review`, handle the response:
84
-
85
- - **approve**: User approved - proceed with implementation
86
- - **iterate**: User wants changes - update the plan based on feedback, then call `plan_review` again
87
- - **reject**: User rejected - ask what they want instead
88
-
89
- ## DO NOT
90
-
91
- - ❌ Start writing code before creating a plan
92
- - ❌ Skip the plan_review step
93
- - ❌ Assume approval - wait for explicit user response
94
- - ❌ Make changes outside the approved plan without updating it first
95
-
96
- ---
97
-
98
- **START NOW**:
99
- 1. Research the codebase using the explore agent if needed
100
- 2. Use `plan_read` to check for an existing plan
101
- 3. Use `plan_create` to create your plan
102
- 4. Use `plan_review` to get approval before any implementation