@travetto/llm-support 8.0.0-alpha.20

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 (84) hide show
  1. package/README.md +101 -0
  2. package/__index__.ts +11 -0
  3. package/llm/consumer/INSTRUCTIONS.md +44 -0
  4. package/llm/maintainer/INSTRUCTIONS.md +37 -0
  5. package/package.json +56 -0
  6. package/resources/snippets/autograph-cli-orchestration.md +21 -0
  7. package/resources/snippets/code/aws-lambda-package-and-deploy.yml.tpl +36 -0
  8. package/resources/snippets/code/cache-enhancements.config.ts.tpl +6 -0
  9. package/resources/snippets/code/cache-enhancements.service.ts.tpl +16 -0
  10. package/resources/snippets/code/create-web-interceptor.ts.tpl +15 -0
  11. package/resources/snippets/code/create-web-route.controller.ts.tpl +15 -0
  12. package/resources/snippets/code/create-web-route.service.ts.tpl +8 -0
  13. package/resources/snippets/code/email-config.ts.tpl +6 -0
  14. package/resources/snippets/code/email-context-schema.ts.tpl +7 -0
  15. package/resources/snippets/code/email-create-template.mustache.tpl +2 -0
  16. package/resources/snippets/code/email-fixture.json.tpl +6 -0
  17. package/resources/snippets/code/email-preview-test.ts.tpl +14 -0
  18. package/resources/snippets/code/email-render-pipeline.ts.tpl +12 -0
  19. package/resources/snippets/code/email-send-controller.ts.tpl +18 -0
  20. package/resources/snippets/code/email-transport-provider.ts.tpl +8 -0
  21. package/resources/snippets/code/enable-auth-session.config.ts.tpl +34 -0
  22. package/resources/snippets/code/enable-auth-session.controller.ts.tpl +30 -0
  23. package/resources/snippets/code/enable-file-upload.config.ts.tpl +6 -0
  24. package/resources/snippets/code/enable-file-upload.controller.ts.tpl +16 -0
  25. package/resources/snippets/code/enable-linting.package.json.tpl +13 -0
  26. package/resources/snippets/code/generate-config.app-config.ts.tpl +7 -0
  27. package/resources/snippets/code/generate-config.application.yml.tpl +3 -0
  28. package/resources/snippets/code/generate-config.local.yml.tpl +2 -0
  29. package/resources/snippets/code/generate-test-suite.fixture.json.tpl +4 -0
  30. package/resources/snippets/code/generate-test-suite.unit.ts.tpl +11 -0
  31. package/resources/snippets/code/model-indexed.indexes.ts.tpl +14 -0
  32. package/resources/snippets/code/model-indexed.model.ts.tpl +8 -0
  33. package/resources/snippets/code/model-indexed.service.ts.tpl +15 -0
  34. package/resources/snippets/code/model-query.service.ts.tpl +18 -0
  35. package/resources/snippets/code/openapi-client-generation.readme.tpl +13 -0
  36. package/resources/snippets/code/openapi-client-generation.yml.tpl +20 -0
  37. package/resources/snippets/code/openapi-spec-pipeline.yml.tpl +21 -0
  38. package/resources/snippets/code/pack-docker-release.yml.tpl +21 -0
  39. package/resources/snippets/code/project-bootstrap.application.yml.tpl +2 -0
  40. package/resources/snippets/code/project-bootstrap.home-controller.ts.tpl +15 -0
  41. package/resources/snippets/code/project-bootstrap.home-service.ts.tpl +8 -0
  42. package/resources/snippets/code/project-bootstrap.monorepo.package.json.tpl +12 -0
  43. package/resources/snippets/code/project-bootstrap.package.json.tpl +18 -0
  44. package/resources/snippets/code/repo-version-release.yml.tpl +25 -0
  45. package/resources/snippets/code/rest-rpc-client.client.ts.tpl +8 -0
  46. package/resources/snippets/code/rest-rpc-client.index.ts.tpl +1 -0
  47. package/resources/snippets/code/workflow-cloudfront-deploy.yml.tpl +18 -0
  48. package/resources/snippets/code/workflow-gcp-deploy.yml.tpl +18 -0
  49. package/resources/snippets/core-aws-lambda-package-and-deploy.md +21 -0
  50. package/resources/snippets/core-email-compiler-pattern.md +21 -0
  51. package/resources/snippets/core-email-module-contract.md +21 -0
  52. package/resources/snippets/core-email-nodemailer-provider.md +21 -0
  53. package/resources/snippets/core-eslint-ruleset.md +21 -0
  54. package/resources/snippets/core-openapi-client-generation.md +21 -0
  55. package/resources/snippets/core-openapi-spec-pipeline.md +21 -0
  56. package/resources/snippets/core-pack-docker-release.md +21 -0
  57. package/resources/snippets/core-repo-version-release.md +21 -0
  58. package/resources/snippets/core-upload-pattern.md +21 -0
  59. package/resources/snippets/core-web-controller-pattern.md +23 -0
  60. package/resources/snippets/core-work-pool-pattern.md +23 -0
  61. package/resources/snippets/embracinglife-cloudfront-deploy.md +21 -0
  62. package/resources/snippets/embracinglife-gcp-deploy.md +21 -0
  63. package/resources/snippets/recipe-auth-google-oauth.md +21 -0
  64. package/resources/snippets/recipe-indexed-model-pattern.md +21 -0
  65. package/resources/snippets/recipe-upload-presigned.md +21 -0
  66. package/resources/snippets/todo-app-test-pattern.md +21 -0
  67. package/src/consumer-docs.ts +23 -0
  68. package/src/execute.ts +624 -0
  69. package/src/install-guidance.ts +187 -0
  70. package/src/mcp.ts +170 -0
  71. package/src/plan.ts +110 -0
  72. package/src/recommendation.ts +306 -0
  73. package/src/snippet-catalog.ts +80 -0
  74. package/src/snippet-shapes.ts +14 -0
  75. package/src/template-shapes.ts +13 -0
  76. package/src/tooling.ts +197 -0
  77. package/src/types.ts +215 -0
  78. package/src/workflow-guidance.ts +220 -0
  79. package/support/base-command.ts +57 -0
  80. package/support/cli.llm_support_execute.ts +80 -0
  81. package/support/cli.llm_support_mcp.ts +62 -0
  82. package/support/cli.llm_support_plan.ts +30 -0
  83. package/support/cli.llm_support_recommend.ts +34 -0
  84. package/support/cli.llm_support_status.ts +30 -0
@@ -0,0 +1,306 @@
1
+ import {
2
+ type InstallGuidance,
3
+ type LlmOperation,
4
+ type RecommendationQuery,
5
+ type RecommendationResponse,
6
+ type WorkflowGuidance
7
+ } from './types.ts';
8
+ import { INSTALL_BUNDLES } from './install-guidance.ts';
9
+ import { recommendSnippets } from './snippet-catalog.ts';
10
+ import { WORKFLOWS } from './workflow-guidance.ts';
11
+
12
+ const OPERATIONS: LlmOperation[] = [
13
+ {
14
+ id: 'project-bootstrap',
15
+ category: 'project',
16
+ title: 'Project bootstrap',
17
+ summary: 'Create a project with guided module and backend selection.',
18
+ requiredModules: ['@travetto/runtime', '@travetto/config'],
19
+ optionalModules: ['@travetto/web', '@travetto/model', '@travetto/test', '@travetto/eslint']
20
+ },
21
+ {
22
+ id: 'create-web-route',
23
+ category: 'web',
24
+ title: 'Create route/controller/service',
25
+ summary: 'Generate a web route with controller and service separation.',
26
+ requiredModules: ['@travetto/web', '@travetto/schema', '@travetto/di'],
27
+ optionalModules: ['@travetto/auth-web', '@travetto/model-query']
28
+ },
29
+ {
30
+ id: 'enable-file-upload',
31
+ category: 'upload',
32
+ title: 'Enable file upload',
33
+ summary: 'Support direct upload and presigned URL upload flows.',
34
+ requiredModules: ['@travetto/web-upload'],
35
+ optionalModules: ['@travetto/model-s3', '@travetto/model-firestore']
36
+ },
37
+ {
38
+ id: 'enable-auth-session',
39
+ category: 'auth',
40
+ title: 'Enable auth/session',
41
+ summary: 'Add login, self, and logout flows with session support.',
42
+ requiredModules: ['@travetto/auth', '@travetto/auth-web'],
43
+ optionalModules: ['@travetto/auth-session', '@travetto/auth-web-session', '@travetto/model-memory']
44
+ },
45
+ {
46
+ id: 'rest-rpc-client',
47
+ category: 'web',
48
+ title: 'Create rest-rpc client',
49
+ summary: 'Generate UI-focused rest-rpc client integration.',
50
+ requiredModules: ['@travetto/web-rpc'],
51
+ optionalModules: ['@travetto/openapi']
52
+ },
53
+ {
54
+ id: 'model-indexed-assistant',
55
+ category: 'model',
56
+ title: 'Model indexed support',
57
+ summary: 'Add indexes and index-aware query helpers.',
58
+ requiredModules: ['@travetto/model-indexed'],
59
+ optionalModules: ['@travetto/model-query']
60
+ },
61
+ {
62
+ id: 'model-query-assistant',
63
+ category: 'model',
64
+ title: 'Model query support',
65
+ summary: 'Craft query expressions from natural language intent.',
66
+ requiredModules: ['@travetto/model-query'],
67
+ optionalModules: ['@travetto/model-query-language']
68
+ },
69
+ {
70
+ id: 'workflow-gcp-deploy',
71
+ category: 'workflow',
72
+ title: 'Generate GCP deploy workflow',
73
+ summary: 'Create GitHub workflows for GCP deploy pipelines.',
74
+ requiredModules: [],
75
+ optionalModules: []
76
+ },
77
+ {
78
+ id: 'workflow-cloudfront-deploy',
79
+ category: 'workflow',
80
+ title: 'Generate CloudFront deploy workflow',
81
+ summary: 'Create GitHub workflows for S3 + CloudFront UI deploy pipelines.',
82
+ requiredModules: [],
83
+ optionalModules: []
84
+ },
85
+ {
86
+ id: 'enable-linting',
87
+ category: 'quality',
88
+ title: 'Enable linting and fix',
89
+ summary: 'Configure linting support and fix workflows for generated code.',
90
+ requiredModules: ['@travetto/eslint'],
91
+ optionalModules: ['@travetto/test']
92
+ },
93
+ {
94
+ id: 'generate-config',
95
+ category: 'config',
96
+ title: 'Create configuration classes/files',
97
+ summary: 'Generate config schema classes and profile-aware config files.',
98
+ requiredModules: ['@travetto/config'],
99
+ optionalModules: []
100
+ },
101
+ {
102
+ id: 'generate-test-suite',
103
+ category: 'test',
104
+ title: 'Create test suite',
105
+ summary: 'Generate unit/integration tests and fixture setup.',
106
+ requiredModules: ['@travetto/test'],
107
+ optionalModules: []
108
+ },
109
+ {
110
+ id: 'create-web-interceptor',
111
+ category: 'web',
112
+ title: 'Create web interceptor',
113
+ summary: 'Generate web interceptor for cross-cutting concerns.',
114
+ requiredModules: ['@travetto/web'],
115
+ optionalModules: ['@travetto/auth-web', '@travetto/cache']
116
+ },
117
+ {
118
+ id: 'email-create-template',
119
+ category: 'email',
120
+ title: 'Create email template',
121
+ summary: 'Generate template files for transactional email.',
122
+ requiredModules: ['@travetto/email'],
123
+ optionalModules: ['@travetto/email-compiler', '@travetto/email-inky']
124
+ },
125
+ {
126
+ id: 'email-context-schema',
127
+ category: 'email',
128
+ title: 'Generate email context schema',
129
+ summary: 'Create typed context contracts for template rendering.',
130
+ requiredModules: ['@travetto/email', '@travetto/schema'],
131
+ optionalModules: []
132
+ },
133
+ {
134
+ id: 'email-render-pipeline',
135
+ category: 'email',
136
+ title: 'Add email render pipeline',
137
+ summary: 'Wire template compiler and renderer integration.',
138
+ requiredModules: ['@travetto/email'],
139
+ optionalModules: ['@travetto/email-compiler', '@travetto/email-inky']
140
+ },
141
+ {
142
+ id: 'email-transport-provider',
143
+ category: 'email',
144
+ title: 'Add email transport/provider',
145
+ summary: 'Configure provider wiring for runtime delivery.',
146
+ requiredModules: ['@travetto/email'],
147
+ optionalModules: ['@travetto/email-nodemailer']
148
+ },
149
+ {
150
+ id: 'email-preview-snapshot',
151
+ category: 'email',
152
+ title: 'Add email preview/snapshot',
153
+ summary: 'Generate preview and snapshot validation support.',
154
+ requiredModules: ['@travetto/email', '@travetto/test'],
155
+ optionalModules: []
156
+ },
157
+ {
158
+ id: 'email-send-flow',
159
+ category: 'email',
160
+ title: 'Add email send integration',
161
+ summary: 'Integrate send operations with endpoint or worker execution.',
162
+ requiredModules: ['@travetto/email'],
163
+ optionalModules: ['@travetto/worker', '@travetto/web']
164
+ },
165
+ {
166
+ id: 'email-test-fixtures',
167
+ category: 'email',
168
+ title: 'Add email test fixtures',
169
+ summary: 'Generate fixture data and tests for email generation flows.',
170
+ requiredModules: ['@travetto/email', '@travetto/test'],
171
+ optionalModules: []
172
+ },
173
+ {
174
+ id: 'cache-enhancements',
175
+ category: 'cache',
176
+ title: 'Add cache enhancements',
177
+ summary: 'Generate cache decorators and eviction workflows.',
178
+ requiredModules: ['@travetto/cache'],
179
+ optionalModules: ['@travetto/model']
180
+ },
181
+ {
182
+ id: 'openapi-spec-pipeline',
183
+ category: 'web',
184
+ title: 'Add OpenAPI spec pipeline',
185
+ summary: 'Generate OpenAPI specification workflow and persistence settings.',
186
+ requiredModules: ['@travetto/openapi'],
187
+ optionalModules: ['@travetto/web', '@travetto/schema']
188
+ },
189
+ {
190
+ id: 'openapi-client-generation',
191
+ category: 'web',
192
+ title: 'Add OpenAPI client generation',
193
+ summary: 'Generate client workflow from OpenAPI spec artifacts.',
194
+ requiredModules: ['@travetto/openapi'],
195
+ optionalModules: ['@travetto/web-rpc', '@travetto/web']
196
+ },
197
+ {
198
+ id: 'aws-lambda-package-and-deploy',
199
+ category: 'workflow',
200
+ title: 'Add AWS Lambda package/deploy',
201
+ summary: 'Generate packaging and deployment workflow for web AWS Lambda targets.',
202
+ requiredModules: ['@travetto/web-aws-lambda', '@travetto/pack'],
203
+ optionalModules: ['@travetto/web', '@travetto/config']
204
+ },
205
+ {
206
+ id: 'pack-docker-release',
207
+ category: 'workflow',
208
+ title: 'Add Docker pack release',
209
+ summary: 'Generate workflow for container image build and publish via pack:docker.',
210
+ requiredModules: ['@travetto/pack'],
211
+ optionalModules: ['@travetto/config', '@travetto/runtime']
212
+ },
213
+ {
214
+ id: 'repo-version-release',
215
+ category: 'workflow',
216
+ title: 'Add repo version release flow',
217
+ summary: 'Generate monorepo versioning workflow using repo:version.',
218
+ requiredModules: ['@travetto/repo'],
219
+ optionalModules: ['@travetto/pack', '@travetto/registry']
220
+ },
221
+ {
222
+ id: 'excluded-log-config',
223
+ category: 'quality',
224
+ title: 'Log configuration (excluded)',
225
+ summary: 'Excluded by scope decision.',
226
+ requiredModules: ['@travetto/log'],
227
+ optionalModules: [],
228
+ excluded: true
229
+ },
230
+ {
231
+ id: 'excluded-log-instrumentation',
232
+ category: 'quality',
233
+ title: 'Log instrumentation (excluded)',
234
+ summary: 'Excluded by scope decision.',
235
+ requiredModules: ['@travetto/log'],
236
+ optionalModules: [],
237
+ excluded: true
238
+ },
239
+ {
240
+ id: 'excluded-eslint-profile',
241
+ category: 'quality',
242
+ title: 'ESLint profile generation (excluded)',
243
+ summary: 'Excluded by scope decision.',
244
+ requiredModules: ['@travetto/eslint'],
245
+ optionalModules: [],
246
+ excluded: true
247
+ },
248
+ {
249
+ id: 'excluded-test-mock-service',
250
+ category: 'test',
251
+ title: 'Test mock service generation (excluded)',
252
+ summary: 'Excluded by scope decision.',
253
+ requiredModules: ['@travetto/test'],
254
+ optionalModules: [],
255
+ excluded: true
256
+ }
257
+ ];
258
+
259
+ function matchesIds<T extends { id: string }>(items: T[], ids?: string[]): T[] {
260
+ if (!ids || ids.length === 0) {
261
+ return items;
262
+ }
263
+ const wanted = new Set(ids);
264
+ return items.filter(item => wanted.has(item.id));
265
+ }
266
+
267
+ export function recommendBundles(ids?: string[]): InstallGuidance[] {
268
+ return matchesIds(INSTALL_BUNDLES, ids);
269
+ }
270
+
271
+ export function recommendWorkflows(ids?: string[]): WorkflowGuidance[] {
272
+ return matchesIds(WORKFLOWS, ids);
273
+ }
274
+
275
+ export function recommendOperations(query: RecommendationQuery = {}): LlmOperation[] {
276
+ const { categories, includeExcluded = false } = query;
277
+ const selected = categories && categories.length ?
278
+ OPERATIONS.filter(item => categories.includes(item.category)) :
279
+ OPERATIONS;
280
+ return selected.filter(item => includeExcluded || !item.excluded);
281
+ }
282
+
283
+ export const LLM_OPERATION_CATEGORIES = [...new Set(OPERATIONS.map(item => item.category))].sort();
284
+
285
+ export function getValidOperationIds(includeExcluded = false): string[] {
286
+ return recommendOperations({ includeExcluded }).map(item => item.id);
287
+ }
288
+
289
+ export async function recommend(query: RecommendationQuery = {}): Promise<RecommendationResponse> {
290
+ const operations = recommendOperations(query);
291
+ return {
292
+ bundles: recommendBundles(query.bundles),
293
+ workflows: recommendWorkflows(query.workflows),
294
+ operations,
295
+ snippets: await recommendSnippets({
296
+ ...query,
297
+ operations: operations.map(item => item.id)
298
+ })
299
+ };
300
+ }
301
+
302
+ export const EXCLUDED_OPERATION_IDS = OPERATIONS
303
+ .filter(item => item.excluded)
304
+ .map(item => item.id);
305
+
306
+ export const LLM_OPERATIONS = OPERATIONS;
@@ -0,0 +1,80 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import { SchemaValidator } from '@travetto/schema';
5
+ import { JSONUtil, RuntimeIndex } from '@travetto/runtime';
6
+
7
+ import type { RecommendationQuery, SnippetSource } from './types.ts';
8
+ import { SnippetSourceSchema } from './snippet-shapes.ts';
9
+
10
+ const SNIPPET_RELATIVE_DIR = 'snippets';
11
+
12
+ function snippetDirectory(): string {
13
+ return path.resolve(RuntimeIndex.getModule('@travetto/llm-support')!.sourcePath, 'resources', SNIPPET_RELATIVE_DIR);
14
+ }
15
+
16
+ function isErrnoException(err: unknown): err is NodeJS.ErrnoException {
17
+ return typeof err === 'object' && err !== null && 'code' in err;
18
+ }
19
+
20
+ async function loadSnippet(fullPath: string): Promise<SnippetSource> {
21
+ const content = await fs.readFile(fullPath, 'utf8');
22
+ const match = content.match(/<!--\s*json\s*([\s\S]*?)\s*-->/i);
23
+ if (!match) {
24
+ throw new Error(`Invalid snippet markdown: ${fullPath}`);
25
+ }
26
+ const bound = SnippetSourceSchema.from(JSONUtil.fromUTF8(match[1].trim()));
27
+ await SchemaValidator.validate(SnippetSourceSchema, bound);
28
+ return bound;
29
+ }
30
+
31
+ async function loadSnippets(): Promise<SnippetSource[]> {
32
+ try {
33
+ const dir = snippetDirectory();
34
+ const files = await fs.readdir(dir);
35
+ const filtered = files
36
+ .filter(file => file.endsWith('.md'))
37
+ .sort();
38
+ return Promise.all(filtered.map(file => loadSnippet(path.join(dir, file))));
39
+ } catch (err) {
40
+ if (isErrnoException(err) && (err.code === 'ENOENT' || err.code === 'ERR_MODULE_NOT_FOUND')) {
41
+ return [];
42
+ }
43
+ throw err;
44
+ }
45
+ }
46
+
47
+ let snippetsPromise: Promise<SnippetSource[]> | undefined;
48
+
49
+ async function getSnippets(): Promise<SnippetSource[]> {
50
+ snippetsPromise ??= loadSnippets();
51
+ return snippetsPromise;
52
+ }
53
+
54
+ function matchesAny(value: string[], desired?: string[]): boolean {
55
+ if (!desired || desired.length === 0) {
56
+ return true;
57
+ }
58
+ const wanted = new Set(desired);
59
+ return value.some(item => wanted.has(item));
60
+ }
61
+
62
+ export async function recommendSnippets(query: RecommendationQuery = {}): Promise<SnippetSource[]> {
63
+ const snippets = await getSnippets();
64
+ const byOperation = query.operations && query.operations.length > 0 ?
65
+ snippets.filter(item => matchesAny(item.operationIds ?? [], query.operations)) :
66
+ snippets;
67
+
68
+ const byTag = query.snippetTags && query.snippetTags.length > 0 ?
69
+ byOperation.filter(item => matchesAny(item.capabilityTags, query.snippetTags)) :
70
+ byOperation;
71
+
72
+ return byTag;
73
+ }
74
+
75
+ export async function getValidSnippetTags(): Promise<string[]> {
76
+ const snippets = await getSnippets();
77
+ return [...new Set(snippets.flatMap(item => item.capabilityTags))].sort();
78
+ }
79
+
80
+ export const LLM_SNIPPET_SOURCES = getSnippets();
@@ -0,0 +1,14 @@
1
+ import { Schema } from '@travetto/schema';
2
+
3
+ import type { SnippetSource } from './types.ts';
4
+
5
+ @Schema()
6
+ export class SnippetSourceSchema implements SnippetSource {
7
+ sourceId!: string;
8
+ repositoryId!: string;
9
+ filePath!: string;
10
+ capabilityTags!: string[];
11
+ operationIds?: string[];
12
+ applicability?: string[];
13
+ notes!: string[];
14
+ }
@@ -0,0 +1,13 @@
1
+ import { Schema } from '@travetto/schema';
2
+ import type { Package } from '@travetto/manifest';
3
+
4
+ export type PackageJsonShape = Partial<Pick<Package, 'name' | 'private' | 'type' | 'scripts' | 'devDependencies'>>;
5
+
6
+ @Schema()
7
+ export class PackageJsonSchema implements PackageJsonShape {
8
+ name?: string;
9
+ private?: boolean;
10
+ type?: string;
11
+ scripts?: Record<string, string>;
12
+ devDependencies?: Record<string, string>;
13
+ }
package/src/tooling.ts ADDED
@@ -0,0 +1,197 @@
1
+ import type { Class } from '@travetto/runtime';
2
+ import { SchemaValidator } from '@travetto/schema';
3
+
4
+ import { executeOperations } from './execute.ts';
5
+ import { buildPlans } from './plan.ts';
6
+ import { recommend } from './recommendation.ts';
7
+ import {
8
+ type ExecutionRequest,
9
+ type ExecutionResponse,
10
+ ExecutionResponseSchema,
11
+ LlmSupportExecuteToolInput,
12
+ LlmSupportPlanToolInput,
13
+ LlmSupportRecommendToolInput,
14
+ type PlanResponse,
15
+ PlanResponseSchema,
16
+ type RecommendationQuery,
17
+ type RecommendationResponse,
18
+ RecommendationResponseSchema
19
+ } from './types.ts';
20
+
21
+ export type LlmSupportToolName = 'llm_support_recommend' | 'llm_support_plan' | 'llm_support_execute';
22
+
23
+ export interface LlmSupportToolDefinition {
24
+ name: LlmSupportToolName;
25
+ description: string;
26
+ inputSchema: object;
27
+ }
28
+
29
+ export interface LlmSupportFlowInput {
30
+ query?: RecommendationQuery;
31
+ execute?: Omit<ExecutionRequest, 'operations'> & { operations?: string[] };
32
+ }
33
+
34
+ export interface LlmSupportFlowResult {
35
+ recommendation: RecommendationResponse;
36
+ plan: PlanResponse;
37
+ execution: ExecutionResponse;
38
+ }
39
+
40
+ function isRecord(value: unknown): value is Record<string, unknown> {
41
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
42
+ }
43
+
44
+ function toList(value: unknown): string[] | undefined {
45
+ if (Array.isArray(value)) {
46
+ return value.filter((entry): entry is string => typeof entry === 'string');
47
+ }
48
+ if (typeof value === 'string') {
49
+ return [value];
50
+ }
51
+ return undefined;
52
+ }
53
+
54
+ function normalizeQuery(input: RecommendationQuery): RecommendationQuery {
55
+ return {
56
+ ...input,
57
+ bundles: toList(input.bundles) ?? input.bundles,
58
+ workflows: toList(input.workflows) ?? input.workflows,
59
+ operations: toList(input.operations) ?? input.operations,
60
+ snippetTags: toList(input.snippetTags) ?? input.snippetTags
61
+ };
62
+ }
63
+
64
+ async function validateInput<T extends object>(schema: Class<T>, payload: unknown): Promise<T> {
65
+ const bound = schema.from({});
66
+ Object.assign(bound, isRecord(payload) ? payload : {});
67
+ await SchemaValidator.validate(schema, bound);
68
+ return bound;
69
+ }
70
+
71
+ async function validateOutput<T extends object>(schema: Class<T>, payload: unknown): Promise<T> {
72
+ const bound = schema.from({});
73
+ Object.assign(bound, isRecord(payload) ? payload : {});
74
+ await SchemaValidator.validate(schema, bound);
75
+ return bound;
76
+ }
77
+
78
+ export function getLlmSupportToolDefinitions(): LlmSupportToolDefinition[] {
79
+ return [
80
+ {
81
+ name: 'llm_support_recommend',
82
+ description: 'Recommend bundles, workflows, operations, and snippets for Travetto implementation goals.',
83
+ inputSchema: {
84
+ type: 'object',
85
+ properties: {
86
+ bundles: { type: 'array', items: { type: 'string' } },
87
+ workflows: { type: 'array', items: { type: 'string' } },
88
+ operations: { type: 'array', items: { type: 'string' } },
89
+ categories: { type: 'array', items: { type: 'string' } },
90
+ snippetTags: { type: 'array', items: { type: 'string' } },
91
+ includeExcluded: { type: 'boolean' }
92
+ },
93
+ additionalProperties: false
94
+ }
95
+ },
96
+ {
97
+ name: 'llm_support_plan',
98
+ description: 'Build file-level plans for selected operations with rationale and snippets.',
99
+ inputSchema: {
100
+ type: 'object',
101
+ properties: {
102
+ operations: { type: 'array', items: { type: 'string' } },
103
+ categories: { type: 'array', items: { type: 'string' } },
104
+ snippetTags: { type: 'array', items: { type: 'string' } },
105
+ includeExcluded: { type: 'boolean' }
106
+ },
107
+ additionalProperties: false
108
+ }
109
+ },
110
+ {
111
+ name: 'llm_support_execute',
112
+ description: 'Execute selected operations against a target directory. Dry-run is the default unless apply is true.',
113
+ inputSchema: {
114
+ type: 'object',
115
+ required: ['operations', 'targetDir'],
116
+ properties: {
117
+ operations: { type: 'array', items: { type: 'string' }, minItems: 1 },
118
+ targetDir: { type: 'string', minLength: 1 },
119
+ apply: { type: 'boolean' },
120
+ overwrite: { type: 'boolean' },
121
+ monorepo: { type: 'boolean' },
122
+ workspacePath: { type: 'string' },
123
+ workspaceName: { type: 'string' },
124
+ routePath: { type: 'string' },
125
+ controllerName: { type: 'string' },
126
+ serviceName: { type: 'string' },
127
+ modelName: { type: 'string' },
128
+ projectName: { type: 'string' },
129
+ emailName: { type: 'string' },
130
+ sendRoutePath: { type: 'string' }
131
+ },
132
+ additionalProperties: false
133
+ }
134
+ }
135
+ ];
136
+ }
137
+
138
+ export async function runLlmSupportTool(
139
+ name: LlmSupportToolName,
140
+ payload: unknown
141
+ ): Promise<RecommendationResponse | PlanResponse | ExecutionResponse> {
142
+ switch (name) {
143
+ case 'llm_support_recommend': {
144
+ const input = await validateInput(LlmSupportRecommendToolInput, payload);
145
+ const output = await recommend(normalizeQuery(input));
146
+ return validateOutput(RecommendationResponseSchema, output);
147
+ }
148
+ case 'llm_support_plan': {
149
+ const input = await validateInput(LlmSupportPlanToolInput, payload);
150
+ const output = await buildPlans(normalizeQuery(input));
151
+ return validateOutput(PlanResponseSchema, output);
152
+ }
153
+ case 'llm_support_execute': {
154
+ const input = await validateInput(LlmSupportExecuteToolInput, payload);
155
+ const request: ExecutionRequest = {
156
+ operations: toList(input.operations) ?? [],
157
+ targetDir: input.targetDir,
158
+ dryRun: input.apply !== true,
159
+ overwrite: input.overwrite,
160
+ monorepo: input.monorepo,
161
+ workspacePath: input.workspacePath,
162
+ workspaceName: input.workspaceName,
163
+ routePath: input.routePath,
164
+ controllerName: input.controllerName,
165
+ serviceName: input.serviceName,
166
+ modelName: input.modelName,
167
+ projectName: input.projectName,
168
+ emailName: input.emailName,
169
+ sendRoutePath: input.sendRoutePath
170
+ };
171
+ const output = await executeOperations(request);
172
+ return validateOutput(ExecutionResponseSchema, output);
173
+ }
174
+ }
175
+ }
176
+
177
+ export async function runLlmSupportFlow(input: LlmSupportFlowInput = {}): Promise<LlmSupportFlowResult> {
178
+ const query = normalizeQuery(input.query ?? {});
179
+ const recommendation = await validateOutput(RecommendationResponseSchema, await recommend(query));
180
+ const plan = await validateOutput(PlanResponseSchema, await buildPlans(query));
181
+
182
+ const execute = input.execute ?? { targetDir: '.', dryRun: true };
183
+ const operations = toList(execute.operations) ?? plan.plans.map(item => item.operationId);
184
+
185
+ const executionRequest: ExecutionRequest = {
186
+ ...execute,
187
+ operations,
188
+ targetDir: execute.targetDir
189
+ };
190
+ const execution = await validateOutput(ExecutionResponseSchema, await executeOperations(executionRequest));
191
+
192
+ return {
193
+ recommendation,
194
+ plan,
195
+ execution
196
+ };
197
+ }