@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.
- package/dist/index.d.ts +1 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -29
- package/dist/plan-service-getter.d.ts +4 -0
- package/dist/plan-service-getter.d.ts.map +1 -0
- package/dist/plan-service-getter.js +1 -0
- package/dist/plan-service.d.ts +2 -2
- package/dist/plan-service.d.ts.map +1 -1
- package/dist/plan-service.js +7 -7
- package/dist/plan-service.test.js +6 -0
- package/dist/tool-factory-config.d.ts +32 -0
- package/dist/tool-factory-config.d.ts.map +1 -0
- package/dist/tool-factory-config.js +30 -0
- package/dist/tool-factory.d.ts +4 -0
- package/dist/tool-factory.d.ts.map +1 -0
- package/dist/tool-factory.js +36 -0
- package/dist/tool-factory.test.d.ts +7 -0
- package/dist/tool-factory.test.d.ts.map +1 -0
- package/dist/tool-factory.test.js +100 -0
- package/dist/tools/plan-create-tool.d.ts +15 -3
- package/dist/tools/plan-create-tool.d.ts.map +1 -1
- package/dist/tools/plan-create-tool.js +29 -18
- package/dist/tools/plan-create-tool.test.js +47 -22
- package/dist/tools/plan-read-tool.d.ts +6 -3
- package/dist/tools/plan-read-tool.d.ts.map +1 -1
- package/dist/tools/plan-read-tool.js +10 -7
- package/dist/tools/plan-read-tool.test.js +31 -19
- package/dist/tools/plan-review-tool.d.ts +14 -5
- package/dist/tools/plan-review-tool.d.ts.map +1 -1
- package/dist/tools/plan-review-tool.js +16 -12
- package/dist/tools/plan-update-tool.d.ts +12 -3
- package/dist/tools/plan-update-tool.d.ts.map +1 -1
- package/dist/tools/plan-update-tool.js +14 -10
- package/dist/tools/plan-update-tool.test.js +40 -28
- package/package.json +4 -5
- package/.dexto-plugin/plugin.json +0 -7
- package/dist/tool-provider.d.ts +0 -44
- package/dist/tool-provider.d.ts.map +0 -1
- package/dist/tool-provider.js +0 -81
- package/dist/tool-provider.test.d.ts +0 -7
- package/dist/tool-provider.test.d.ts.map +0 -1
- package/dist/tool-provider.test.js +0 -185
- package/skills/plan/SKILL.md +0 -102
package/dist/index.d.ts
CHANGED
|
@@ -3,32 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Implementation planning tools with session-linked plans.
|
|
5
5
|
* Provides tools for creating, reading, updating, and tracking plans.
|
|
6
|
-
*
|
|
7
|
-
* This package is a Dexto plugin that automatically registers:
|
|
8
|
-
* - Custom tool provider: plan-tools
|
|
9
|
-
* - Skill: plan (planning mode instructions)
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* 1. Install the package
|
|
13
|
-
* 2. The plugin discovery will find .dexto-plugin/plugin.json
|
|
14
|
-
* 3. Tools and skill are automatically registered
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* Path to the plugin directory containing .dexto-plugin manifest.
|
|
18
|
-
* Use this in image definitions to declare bundled plugins.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```typescript
|
|
22
|
-
* import { PLUGIN_PATH } from '@dexto/tools-plan';
|
|
23
|
-
*
|
|
24
|
-
* export default defineImage({
|
|
25
|
-
* bundledPlugins: [PLUGIN_PATH],
|
|
26
|
-
* // ...
|
|
27
|
-
* });
|
|
28
|
-
* ```
|
|
29
6
|
*/
|
|
30
|
-
export
|
|
31
|
-
export { planToolsProvider } from './tool-provider.js';
|
|
7
|
+
export { planToolsFactory } from './tool-factory.js';
|
|
32
8
|
export { PlanService } from './plan-service.js';
|
|
33
9
|
export type { Plan, PlanMeta, PlanStatus, PlanServiceOptions, PlanUpdateResult } from './types.js';
|
|
34
10
|
export { PlanError, PlanErrorCode, type PlanErrorCodeType } from './errors.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnG,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,36 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Implementation planning tools with session-linked plans.
|
|
5
5
|
* Provides tools for creating, reading, updating, and tracking plans.
|
|
6
|
-
*
|
|
7
|
-
* This package is a Dexto plugin that automatically registers:
|
|
8
|
-
* - Custom tool provider: plan-tools
|
|
9
|
-
* - Skill: plan (planning mode instructions)
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* 1. Install the package
|
|
13
|
-
* 2. The plugin discovery will find .dexto-plugin/plugin.json
|
|
14
|
-
* 3. Tools and skill are automatically registered
|
|
15
|
-
*/
|
|
16
|
-
import * as path from 'node:path';
|
|
17
|
-
import { fileURLToPath } from 'node:url';
|
|
18
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
/**
|
|
20
|
-
* Path to the plugin directory containing .dexto-plugin manifest.
|
|
21
|
-
* Use this in image definitions to declare bundled plugins.
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```typescript
|
|
25
|
-
* import { PLUGIN_PATH } from '@dexto/tools-plan';
|
|
26
|
-
*
|
|
27
|
-
* export default defineImage({
|
|
28
|
-
* bundledPlugins: [PLUGIN_PATH],
|
|
29
|
-
* // ...
|
|
30
|
-
* });
|
|
31
|
-
* ```
|
|
32
6
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
export { planToolsProvider } from './tool-provider.js';
|
|
7
|
+
// Tool factory (image-compatible)
|
|
8
|
+
export { planToolsFactory } from './tool-factory.js';
|
|
36
9
|
// Service (for advanced use cases)
|
|
37
10
|
export { PlanService } from './plan-service.js';
|
|
38
11
|
// Error utilities
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-service-getter.d.ts","sourceRoot":"","sources":["../src/plan-service-getter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/plan-service.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - plan.md: The plan content
|
|
7
7
|
* - plan-meta.json: Metadata (status, checkpoints, timestamps)
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
9
|
+
import type { Logger } from '@dexto/core';
|
|
10
10
|
import type { Plan, PlanMeta, PlanServiceOptions, PlanUpdateResult } from './types.js';
|
|
11
11
|
/**
|
|
12
12
|
* Service for managing implementation plans.
|
|
@@ -14,7 +14,7 @@ import type { Plan, PlanMeta, PlanServiceOptions, PlanUpdateResult } from './typ
|
|
|
14
14
|
export declare class PlanService {
|
|
15
15
|
private basePath;
|
|
16
16
|
private logger;
|
|
17
|
-
constructor(options: PlanServiceOptions, logger
|
|
17
|
+
constructor(options: PlanServiceOptions, logger: Logger);
|
|
18
18
|
/**
|
|
19
19
|
* Resolves and validates a session directory path.
|
|
20
20
|
* Prevents path traversal attacks by ensuring the resolved path stays within basePath.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-service.d.ts","sourceRoot":"","sources":["../src/plan-service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"plan-service.d.ts","sourceRoot":"","sources":["../src/plan-service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMvF;;GAEG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;IAKvD;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;OAEG;IACH,OAAO,CAAC,UAAU;IAIlB;;;OAGG;IACI,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAI7C;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKjD;;;;;OAKG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAoC7F;;;;OAIG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAiDnD;;;;;OAKG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqC3E;;;;;OAKG;IACG,UAAU,CACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,GACrD,OAAO,CAAC,QAAQ,CAAC;IA2BpB;;;;;OAKG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAYjD"}
|
package/dist/plan-service.js
CHANGED
|
@@ -92,7 +92,7 @@ export class PlanService {
|
|
|
92
92
|
fs.writeFile(this.getPlanPath(sessionId), content, 'utf-8'),
|
|
93
93
|
fs.writeFile(this.getMetaPath(sessionId), JSON.stringify(meta, null, 2), 'utf-8'),
|
|
94
94
|
]);
|
|
95
|
-
this.logger
|
|
95
|
+
this.logger.debug(`Created plan for session ${sessionId}`);
|
|
96
96
|
return { content, meta };
|
|
97
97
|
}
|
|
98
98
|
catch (error) {
|
|
@@ -116,7 +116,7 @@ export class PlanService {
|
|
|
116
116
|
const metaParsed = JSON.parse(metaContent);
|
|
117
117
|
const metaResult = PlanMetaSchema.safeParse(metaParsed);
|
|
118
118
|
if (!metaResult.success) {
|
|
119
|
-
this.logger
|
|
119
|
+
this.logger.warn(`Invalid plan metadata for session ${sessionId}, using defaults`);
|
|
120
120
|
// Return with minimal metadata if parsing fails
|
|
121
121
|
return {
|
|
122
122
|
content,
|
|
@@ -139,11 +139,11 @@ export class PlanService {
|
|
|
139
139
|
// JSON parse errors (SyntaxError) mean corrupted data - treat as not found
|
|
140
140
|
// but log for debugging
|
|
141
141
|
if (error instanceof SyntaxError) {
|
|
142
|
-
this.logger
|
|
142
|
+
this.logger.error(`Failed to read plan for session ${sessionId}: ${error.message}`);
|
|
143
143
|
return null;
|
|
144
144
|
}
|
|
145
145
|
// For real I/O errors (permission denied, disk issues), throw to surface the issue
|
|
146
|
-
this.logger
|
|
146
|
+
this.logger.error(`Failed to read plan for session ${sessionId}: ${err.message ?? String(err)}`);
|
|
147
147
|
throw PlanError.storageError('read', sessionId, err);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
@@ -170,7 +170,7 @@ export class PlanService {
|
|
|
170
170
|
fs.writeFile(this.getPlanPath(sessionId), content, 'utf-8'),
|
|
171
171
|
fs.writeFile(this.getMetaPath(sessionId), JSON.stringify(updatedMeta, null, 2), 'utf-8'),
|
|
172
172
|
]);
|
|
173
|
-
this.logger
|
|
173
|
+
this.logger.debug(`Updated plan for session ${sessionId}`);
|
|
174
174
|
return {
|
|
175
175
|
oldContent,
|
|
176
176
|
newContent: content,
|
|
@@ -199,7 +199,7 @@ export class PlanService {
|
|
|
199
199
|
};
|
|
200
200
|
try {
|
|
201
201
|
await fs.writeFile(this.getMetaPath(sessionId), JSON.stringify(updatedMeta, null, 2), 'utf-8');
|
|
202
|
-
this.logger
|
|
202
|
+
this.logger.debug(`Updated plan metadata for session ${sessionId}`);
|
|
203
203
|
return updatedMeta;
|
|
204
204
|
}
|
|
205
205
|
catch (error) {
|
|
@@ -218,7 +218,7 @@ export class PlanService {
|
|
|
218
218
|
}
|
|
219
219
|
try {
|
|
220
220
|
await fs.rm(this.getPlanDir(sessionId), { recursive: true, force: true });
|
|
221
|
-
this.logger
|
|
221
|
+
this.logger.debug(`Deleted plan for session ${sessionId}`);
|
|
222
222
|
}
|
|
223
223
|
catch (error) {
|
|
224
224
|
throw PlanError.storageError('delete', sessionId, error);
|
|
@@ -13,10 +13,16 @@ import { DextoRuntimeError } from '@dexto/core';
|
|
|
13
13
|
// Create mock logger
|
|
14
14
|
const createMockLogger = () => ({
|
|
15
15
|
debug: vi.fn(),
|
|
16
|
+
silly: vi.fn(),
|
|
16
17
|
info: vi.fn(),
|
|
17
18
|
warn: vi.fn(),
|
|
18
19
|
error: vi.fn(),
|
|
20
|
+
trackException: vi.fn(),
|
|
19
21
|
createChild: vi.fn().mockReturnThis(),
|
|
22
|
+
setLevel: vi.fn(),
|
|
23
|
+
getLevel: vi.fn(() => 'debug'),
|
|
24
|
+
getLogFilePath: vi.fn(() => null),
|
|
25
|
+
destroy: vi.fn(async () => undefined),
|
|
20
26
|
});
|
|
21
27
|
describe('PlanService', () => {
|
|
22
28
|
let mockLogger;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan Tools Factory
|
|
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
|
+
/**
|
|
12
|
+
* Available plan tool names for enabledTools configuration
|
|
13
|
+
*/
|
|
14
|
+
export declare const PLAN_TOOL_NAMES: readonly ["plan_create", "plan_read", "plan_update", "plan_review"];
|
|
15
|
+
/**
|
|
16
|
+
* Configuration schema for Plan tools factory
|
|
17
|
+
*/
|
|
18
|
+
export declare const PlanToolsConfigSchema: z.ZodObject<{
|
|
19
|
+
type: z.ZodLiteral<"plan-tools">;
|
|
20
|
+
basePath: z.ZodDefault<z.ZodString>;
|
|
21
|
+
enabledTools: z.ZodOptional<z.ZodArray<z.ZodEnum<["plan_create", "plan_read", "plan_update", "plan_review"]>, "many">>;
|
|
22
|
+
}, "strict", z.ZodTypeAny, {
|
|
23
|
+
type: "plan-tools";
|
|
24
|
+
basePath: string;
|
|
25
|
+
enabledTools?: ("plan_create" | "plan_read" | "plan_update" | "plan_review")[] | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
type: "plan-tools";
|
|
28
|
+
basePath?: string | undefined;
|
|
29
|
+
enabledTools?: ("plan_create" | "plan_read" | "plan_update" | "plan_review")[] | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
export type PlanToolsConfig = z.output<typeof PlanToolsConfigSchema>;
|
|
32
|
+
//# sourceMappingURL=tool-factory-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-factory-config.d.ts","sourceRoot":"","sources":["../src/tool-factory-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,eAAe,qEAAsE,CAAC;AAEnG;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;EAcrB,CAAC;AAEd,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan Tools Factory
|
|
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
|
+
/**
|
|
12
|
+
* Available plan tool names for enabledTools configuration
|
|
13
|
+
*/
|
|
14
|
+
export const PLAN_TOOL_NAMES = ['plan_create', 'plan_read', 'plan_update', 'plan_review'];
|
|
15
|
+
/**
|
|
16
|
+
* Configuration schema for Plan tools factory
|
|
17
|
+
*/
|
|
18
|
+
export const PlanToolsConfigSchema = z
|
|
19
|
+
.object({
|
|
20
|
+
type: z.literal('plan-tools'),
|
|
21
|
+
basePath: z
|
|
22
|
+
.string()
|
|
23
|
+
.default('.dexto/plans')
|
|
24
|
+
.describe('Base directory for plan storage (relative to working directory)'),
|
|
25
|
+
enabledTools: z
|
|
26
|
+
.array(z.enum(PLAN_TOOL_NAMES))
|
|
27
|
+
.optional()
|
|
28
|
+
.describe(`Subset of tools to enable. If not specified, all tools are enabled. Available: ${PLAN_TOOL_NAMES.join(', ')}`),
|
|
29
|
+
})
|
|
30
|
+
.strict();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-factory.d.ts","sourceRoot":"","sources":["../src/tool-factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAQvD,OAAO,EAGH,KAAK,eAAe,EACvB,MAAM,0BAA0B,CAAC;AAKlC,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,eAAe,CAiCzD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { PlanService } from './plan-service.js';
|
|
3
|
+
import { createPlanCreateTool } from './tools/plan-create-tool.js';
|
|
4
|
+
import { createPlanReadTool } from './tools/plan-read-tool.js';
|
|
5
|
+
import { createPlanUpdateTool } from './tools/plan-update-tool.js';
|
|
6
|
+
import { createPlanReviewTool } from './tools/plan-review-tool.js';
|
|
7
|
+
import { PLAN_TOOL_NAMES, PlanToolsConfigSchema, } from './tool-factory-config.js';
|
|
8
|
+
export const planToolsFactory = {
|
|
9
|
+
configSchema: PlanToolsConfigSchema,
|
|
10
|
+
metadata: {
|
|
11
|
+
displayName: 'Plan Tools',
|
|
12
|
+
description: 'Create and manage implementation plans linked to sessions',
|
|
13
|
+
category: 'planning',
|
|
14
|
+
},
|
|
15
|
+
create: (config) => {
|
|
16
|
+
const basePath = path.isAbsolute(config.basePath)
|
|
17
|
+
? config.basePath
|
|
18
|
+
: path.join(process.cwd(), config.basePath);
|
|
19
|
+
let planService;
|
|
20
|
+
const getPlanService = async (context) => {
|
|
21
|
+
if (planService) {
|
|
22
|
+
return planService;
|
|
23
|
+
}
|
|
24
|
+
planService = new PlanService({ basePath }, context.logger);
|
|
25
|
+
return planService;
|
|
26
|
+
};
|
|
27
|
+
const toolCreators = {
|
|
28
|
+
plan_create: () => createPlanCreateTool(getPlanService),
|
|
29
|
+
plan_read: () => createPlanReadTool(getPlanService),
|
|
30
|
+
plan_update: () => createPlanUpdateTool(getPlanService),
|
|
31
|
+
plan_review: () => createPlanReviewTool(getPlanService),
|
|
32
|
+
};
|
|
33
|
+
const toolsToCreate = config.enabledTools ?? PLAN_TOOL_NAMES;
|
|
34
|
+
return toolsToCreate.map((toolName) => toolCreators[toolName]());
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -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,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan Tools Factory Tests
|
|
3
|
+
*
|
|
4
|
+
* Validates the plan tools factory schema and tool creation.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import { planToolsFactory } from './tool-factory.js';
|
|
8
|
+
describe('planToolsFactory', () => {
|
|
9
|
+
describe('factory metadata', () => {
|
|
10
|
+
it('should have metadata', () => {
|
|
11
|
+
expect(planToolsFactory.metadata).toBeDefined();
|
|
12
|
+
expect(planToolsFactory.metadata?.displayName).toBe('Plan Tools');
|
|
13
|
+
expect(planToolsFactory.metadata?.category).toBe('planning');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
describe('config schema', () => {
|
|
17
|
+
it('should validate minimal config', () => {
|
|
18
|
+
const result = planToolsFactory.configSchema.safeParse({
|
|
19
|
+
type: 'plan-tools',
|
|
20
|
+
});
|
|
21
|
+
expect(result.success).toBe(true);
|
|
22
|
+
if (result.success) {
|
|
23
|
+
expect(result.data.basePath).toBe('.dexto/plans');
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
it('should validate config with custom basePath', () => {
|
|
27
|
+
const result = planToolsFactory.configSchema.safeParse({
|
|
28
|
+
type: 'plan-tools',
|
|
29
|
+
basePath: '/custom/path',
|
|
30
|
+
});
|
|
31
|
+
expect(result.success).toBe(true);
|
|
32
|
+
if (result.success) {
|
|
33
|
+
expect(result.data.basePath).toBe('/custom/path');
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
it('should validate config with enabledTools', () => {
|
|
37
|
+
const result = planToolsFactory.configSchema.safeParse({
|
|
38
|
+
type: 'plan-tools',
|
|
39
|
+
enabledTools: ['plan_create', 'plan_read'],
|
|
40
|
+
});
|
|
41
|
+
expect(result.success).toBe(true);
|
|
42
|
+
if (result.success) {
|
|
43
|
+
expect(result.data.enabledTools).toEqual(['plan_create', 'plan_read']);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
it('should reject invalid tool names', () => {
|
|
47
|
+
const result = planToolsFactory.configSchema.safeParse({
|
|
48
|
+
type: 'plan-tools',
|
|
49
|
+
enabledTools: ['invalid_tool'],
|
|
50
|
+
});
|
|
51
|
+
expect(result.success).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
it('should reject unknown properties', () => {
|
|
54
|
+
const result = planToolsFactory.configSchema.safeParse({
|
|
55
|
+
type: 'plan-tools',
|
|
56
|
+
unknownProp: 'value',
|
|
57
|
+
});
|
|
58
|
+
expect(result.success).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('create', () => {
|
|
62
|
+
it('should create all tools by default', () => {
|
|
63
|
+
const config = planToolsFactory.configSchema.parse({
|
|
64
|
+
type: 'plan-tools',
|
|
65
|
+
});
|
|
66
|
+
const tools = planToolsFactory.create(config);
|
|
67
|
+
expect(tools).toHaveLength(4);
|
|
68
|
+
const toolIds = tools.map((t) => t.id);
|
|
69
|
+
expect(toolIds).toContain('plan_create');
|
|
70
|
+
expect(toolIds).toContain('plan_read');
|
|
71
|
+
expect(toolIds).toContain('plan_update');
|
|
72
|
+
expect(toolIds).toContain('plan_review');
|
|
73
|
+
});
|
|
74
|
+
it('should create only enabled tools', () => {
|
|
75
|
+
const config = planToolsFactory.configSchema.parse({
|
|
76
|
+
type: 'plan-tools',
|
|
77
|
+
enabledTools: ['plan_create', 'plan_read'],
|
|
78
|
+
});
|
|
79
|
+
const tools = planToolsFactory.create(config);
|
|
80
|
+
expect(tools).toHaveLength(2);
|
|
81
|
+
const toolIds = tools.map((t) => t.id);
|
|
82
|
+
expect(toolIds).toContain('plan_create');
|
|
83
|
+
expect(toolIds).toContain('plan_read');
|
|
84
|
+
expect(toolIds).not.toContain('plan_update');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('tool definitions', () => {
|
|
88
|
+
it('should have descriptions and input schemas for all tools', () => {
|
|
89
|
+
const config = planToolsFactory.configSchema.parse({
|
|
90
|
+
type: 'plan-tools',
|
|
91
|
+
});
|
|
92
|
+
const tools = planToolsFactory.create(config);
|
|
93
|
+
for (const tool of tools) {
|
|
94
|
+
expect(tool.description).toBeDefined();
|
|
95
|
+
expect(tool.description.length).toBeGreaterThan(0);
|
|
96
|
+
expect(tool.inputSchema).toBeDefined();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -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
|
|
8
|
-
import type {
|
|
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(
|
|
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;
|
|
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,CA0EpC"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Shows a preview for approval before saving.
|
|
6
6
|
*/
|
|
7
7
|
import { z } from 'zod';
|
|
8
|
+
import { defineTool } from '@dexto/core';
|
|
8
9
|
import { PlanError } from '../errors.js';
|
|
9
10
|
const PlanCreateInputSchema = z
|
|
10
11
|
.object({
|
|
@@ -17,9 +18,10 @@ const PlanCreateInputSchema = z
|
|
|
17
18
|
/**
|
|
18
19
|
* Creates the plan_create tool
|
|
19
20
|
*/
|
|
20
|
-
export function createPlanCreateTool(
|
|
21
|
-
return {
|
|
21
|
+
export function createPlanCreateTool(getPlanService) {
|
|
22
|
+
return defineTool({
|
|
22
23
|
id: 'plan_create',
|
|
24
|
+
displayName: 'Plan',
|
|
23
25
|
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
26
|
inputSchema: PlanCreateInputSchema,
|
|
25
27
|
/**
|
|
@@ -27,46 +29,55 @@ export function createPlanCreateTool(planService) {
|
|
|
27
29
|
*/
|
|
28
30
|
generatePreview: async (input, context) => {
|
|
29
31
|
const { content } = input;
|
|
30
|
-
if (!context
|
|
32
|
+
if (!context.sessionId) {
|
|
31
33
|
throw PlanError.sessionIdRequired();
|
|
32
34
|
}
|
|
35
|
+
const resolvedPlanService = await getPlanService(context);
|
|
33
36
|
// Check if plan already exists
|
|
34
|
-
const exists = await
|
|
37
|
+
const exists = await resolvedPlanService.exists(context.sessionId);
|
|
35
38
|
if (exists) {
|
|
36
39
|
throw PlanError.planAlreadyExists(context.sessionId);
|
|
37
40
|
}
|
|
38
41
|
// Return preview for approval UI
|
|
39
42
|
const lineCount = content.split('\n').length;
|
|
40
|
-
const planPath =
|
|
43
|
+
const planPath = resolvedPlanService.getPlanPath(context.sessionId);
|
|
41
44
|
return {
|
|
42
45
|
type: 'file',
|
|
43
46
|
path: planPath,
|
|
44
47
|
operation: 'create',
|
|
45
48
|
content,
|
|
46
|
-
size: content
|
|
49
|
+
size: Buffer.byteLength(content, 'utf8'),
|
|
47
50
|
lineCount,
|
|
48
51
|
};
|
|
49
52
|
},
|
|
50
|
-
|
|
53
|
+
async execute(input, context) {
|
|
51
54
|
const { title, content } = input;
|
|
52
|
-
if (!context
|
|
55
|
+
if (!context.sessionId) {
|
|
53
56
|
throw PlanError.sessionIdRequired();
|
|
54
57
|
}
|
|
55
|
-
const
|
|
56
|
-
|
|
58
|
+
const resolvedPlanService = await getPlanService(context);
|
|
59
|
+
// Keep consistent with generatePreview: fail early if plan already exists.
|
|
60
|
+
// (PlanService.create also guards this, but this keeps the control flow obvious.)
|
|
61
|
+
const exists = await resolvedPlanService.exists(context.sessionId);
|
|
62
|
+
if (exists) {
|
|
63
|
+
throw PlanError.planAlreadyExists(context.sessionId);
|
|
64
|
+
}
|
|
65
|
+
const plan = await resolvedPlanService.create(context.sessionId, content, { title });
|
|
66
|
+
const planPath = resolvedPlanService.getPlanPath(context.sessionId);
|
|
67
|
+
const _display = {
|
|
68
|
+
type: 'file',
|
|
69
|
+
path: planPath,
|
|
70
|
+
operation: 'create',
|
|
71
|
+
size: Buffer.byteLength(content, 'utf8'),
|
|
72
|
+
lineCount: content.split('\n').length,
|
|
73
|
+
};
|
|
57
74
|
return {
|
|
58
75
|
success: true,
|
|
59
76
|
path: planPath,
|
|
60
77
|
status: plan.meta.status,
|
|
61
78
|
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
|
-
},
|
|
79
|
+
_display,
|
|
69
80
|
};
|
|
70
81
|
},
|
|
71
|
-
};
|
|
82
|
+
});
|
|
72
83
|
}
|