@constructive-io/graphql-codegen 4.7.3 → 4.8.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.
- package/core/codegen/cli/command-map-generator.js +36 -11
- package/core/codegen/cli/custom-command-generator.js +59 -13
- package/core/codegen/cli/docs-generator.d.ts +1 -1
- package/core/codegen/cli/docs-generator.js +137 -54
- package/core/codegen/cli/executor-generator.js +20 -6
- package/core/codegen/cli/infra-generator.js +17 -14
- package/core/codegen/cli/table-command-generator.js +209 -53
- package/core/codegen/docs-utils.d.ts +12 -1
- package/core/codegen/docs-utils.js +49 -1
- package/core/codegen/hooks-docs-generator.d.ts +1 -1
- package/core/codegen/hooks-docs-generator.js +47 -7
- package/core/codegen/orm/docs-generator.d.ts +1 -1
- package/core/codegen/orm/docs-generator.js +57 -20
- package/core/generate.d.ts +5 -0
- package/core/generate.js +48 -12
- package/core/workspace.d.ts +13 -0
- package/core/workspace.js +92 -0
- package/esm/core/codegen/cli/command-map-generator.js +36 -11
- package/esm/core/codegen/cli/custom-command-generator.js +60 -14
- package/esm/core/codegen/cli/docs-generator.d.ts +1 -1
- package/esm/core/codegen/cli/docs-generator.js +138 -55
- package/esm/core/codegen/cli/executor-generator.js +20 -6
- package/esm/core/codegen/cli/infra-generator.js +17 -14
- package/esm/core/codegen/cli/table-command-generator.js +210 -54
- package/esm/core/codegen/docs-utils.d.ts +12 -1
- package/esm/core/codegen/docs-utils.js +48 -1
- package/esm/core/codegen/hooks-docs-generator.d.ts +1 -1
- package/esm/core/codegen/hooks-docs-generator.js +48 -8
- package/esm/core/codegen/orm/docs-generator.d.ts +1 -1
- package/esm/core/codegen/orm/docs-generator.js +58 -21
- package/esm/core/generate.d.ts +5 -0
- package/esm/core/generate.js +48 -12
- package/esm/core/workspace.d.ts +13 -0
- package/esm/core/workspace.js +89 -0
- package/esm/types/config.d.ts +12 -4
- package/package.json +22 -22
- package/types/config.d.ts +12 -4
|
@@ -48,6 +48,25 @@ function createNamedImportDeclaration(moduleSpecifier, namedImports, typeOnly =
|
|
|
48
48
|
decl.importKind = typeOnly ? 'type' : 'value';
|
|
49
49
|
return decl;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Build the command handler function type:
|
|
53
|
+
* (argv: Partial<Record<string, unknown>>, prompter: Inquirerer, options: CLIOptions) => Promise<void>
|
|
54
|
+
* This matches the actual exported handler signatures from table/custom command files.
|
|
55
|
+
*/
|
|
56
|
+
function buildCommandHandlerType() {
|
|
57
|
+
const argvParam = t.identifier('argv');
|
|
58
|
+
argvParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Partial'), t.tsTypeParameterInstantiation([
|
|
59
|
+
t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
60
|
+
t.tsStringKeyword(),
|
|
61
|
+
t.tsUnknownKeyword(),
|
|
62
|
+
])),
|
|
63
|
+
])));
|
|
64
|
+
const prompterParam = t.identifier('prompter');
|
|
65
|
+
prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
|
|
66
|
+
const optionsParam = t.identifier('options');
|
|
67
|
+
optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('CLIOptions')));
|
|
68
|
+
return t.tsFunctionType(null, [argvParam, prompterParam, optionsParam], t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Promise'), t.tsTypeParameterInstantiation([t.tsVoidKeyword()]))));
|
|
69
|
+
}
|
|
51
70
|
function generateCommandMap(tables, customOperations, toolName) {
|
|
52
71
|
const statements = [];
|
|
53
72
|
statements.push(createNamedImportDeclaration('inquirerer', [
|
|
@@ -74,18 +93,17 @@ function generateCommandMap(tables, customOperations, toolName) {
|
|
|
74
93
|
statements.push(createImportDeclaration(`./commands/${kebab}`, importName));
|
|
75
94
|
}
|
|
76
95
|
const mapProperties = commandEntries.map((entry) => t.objectProperty(t.stringLiteral(entry.kebab), t.identifier(entry.importName)));
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const createCommandMapAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
81
|
-
t.tsStringKeyword(),
|
|
82
|
-
t.tsFunctionType(null, [], t.tsTypeAnnotation(t.tsAnyKeyword())),
|
|
83
|
-
])));
|
|
96
|
+
// Build command handler type matching actual handler signature:
|
|
97
|
+
// (argv: Partial<Record<string, unknown>>, prompter: Inquirerer, options: CLIOptions) => Promise<void>
|
|
98
|
+
const commandHandlerType = buildCommandHandlerType();
|
|
84
99
|
const createCommandMapId = t.identifier('createCommandMap');
|
|
85
100
|
createCommandMapId.typeAnnotation = t.tsTypeAnnotation(t.tsParenthesizedType(t.tsFunctionType(null, [], t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
86
101
|
t.tsStringKeyword(),
|
|
87
|
-
|
|
102
|
+
commandHandlerType,
|
|
88
103
|
]))))));
|
|
104
|
+
const createCommandMapFunc = t.variableDeclaration('const', [
|
|
105
|
+
t.variableDeclarator(createCommandMapId, t.arrowFunctionExpression([], t.objectExpression(mapProperties))),
|
|
106
|
+
]);
|
|
89
107
|
statements.push(createCommandMapFunc);
|
|
90
108
|
const usageLines = [
|
|
91
109
|
'',
|
|
@@ -152,7 +170,7 @@ function generateCommandMap(tables, customOperations, toolName) {
|
|
|
152
170
|
]),
|
|
153
171
|
]))),
|
|
154
172
|
]),
|
|
155
|
-
t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.memberExpression(t.identifier('answer'), t.identifier('command')))),
|
|
173
|
+
t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('command')), t.tsStringKeyword()))),
|
|
156
174
|
])),
|
|
157
175
|
t.variableDeclaration('const', [
|
|
158
176
|
t.variableDeclarator(t.identifier('commandFn'), t.memberExpression(t.identifier('commandMap'), t.identifier('command'), true)),
|
|
@@ -223,8 +241,15 @@ function generateMultiTargetCommandMap(input) {
|
|
|
223
241
|
}
|
|
224
242
|
}
|
|
225
243
|
const mapProperties = commandEntries.map((entry) => t.objectProperty(t.stringLiteral(entry.kebab), t.identifier(entry.importName)));
|
|
244
|
+
// Build command handler type matching actual handler signature
|
|
245
|
+
const multiTargetCommandHandlerType = buildCommandHandlerType();
|
|
246
|
+
const multiTargetCreateCommandMapId = t.identifier('createCommandMap');
|
|
247
|
+
multiTargetCreateCommandMapId.typeAnnotation = t.tsTypeAnnotation(t.tsParenthesizedType(t.tsFunctionType(null, [], t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
248
|
+
t.tsStringKeyword(),
|
|
249
|
+
multiTargetCommandHandlerType,
|
|
250
|
+
]))))));
|
|
226
251
|
const createCommandMapFunc = t.variableDeclaration('const', [
|
|
227
|
-
t.variableDeclarator(
|
|
252
|
+
t.variableDeclarator(multiTargetCreateCommandMapId, t.arrowFunctionExpression([], t.objectExpression(mapProperties))),
|
|
228
253
|
]);
|
|
229
254
|
statements.push(createCommandMapFunc);
|
|
230
255
|
const usageLines = [
|
|
@@ -298,7 +323,7 @@ function generateMultiTargetCommandMap(input) {
|
|
|
298
323
|
]),
|
|
299
324
|
]))),
|
|
300
325
|
]),
|
|
301
|
-
t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.memberExpression(t.identifier('answer'), t.identifier('command')))),
|
|
326
|
+
t.expressionStatement(t.assignmentExpression('=', t.identifier('command'), t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('command')), t.tsStringKeyword()))),
|
|
302
327
|
])),
|
|
303
328
|
t.variableDeclaration('const', [
|
|
304
329
|
t.variableDeclarator(t.identifier('commandFn'), t.memberExpression(t.identifier('commandMap'), t.identifier('command'), true)),
|
|
@@ -101,22 +101,33 @@ function buildDefaultSelectString(returnType, isMutation) {
|
|
|
101
101
|
}
|
|
102
102
|
return '';
|
|
103
103
|
}
|
|
104
|
-
function buildOrmCustomCall(opKind, opName, argsExpr, selectExpr, hasArgs = true) {
|
|
104
|
+
function buildOrmCustomCall(opKind, opName, argsExpr, selectExpr, hasArgs = true, selectTypeName) {
|
|
105
105
|
const callArgs = [];
|
|
106
|
+
// Helper: wrap { select } and cast to `{ select: XxxSelect }` via `unknown`.
|
|
107
|
+
// The ORM method's second parameter is `{ select: S } & StrictSelect<S, XxxSelect>`.
|
|
108
|
+
// We import the concrete Select type (e.g. CheckPasswordPayloadSelect) and cast
|
|
109
|
+
// `{ select: selectFields } as unknown as { select: XxxSelect }` so TS infers
|
|
110
|
+
// `S = XxxSelect` and StrictSelect is satisfied.
|
|
111
|
+
const castSelectWrapper = (sel) => {
|
|
112
|
+
const selectObj = t.objectExpression([
|
|
113
|
+
t.objectProperty(t.identifier('select'), sel),
|
|
114
|
+
]);
|
|
115
|
+
if (!selectTypeName)
|
|
116
|
+
return selectObj;
|
|
117
|
+
return t.tsAsExpression(t.tsAsExpression(selectObj, t.tsUnknownKeyword()), t.tsTypeLiteral([
|
|
118
|
+
t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName)))),
|
|
119
|
+
]));
|
|
120
|
+
};
|
|
106
121
|
if (hasArgs) {
|
|
107
|
-
// Operation has arguments: pass args as first param, select as second
|
|
122
|
+
// Operation has arguments: pass args as first param, select as second.
|
|
108
123
|
callArgs.push(argsExpr);
|
|
109
124
|
if (selectExpr) {
|
|
110
|
-
callArgs.push(
|
|
111
|
-
t.objectProperty(t.identifier('select'), selectExpr),
|
|
112
|
-
]));
|
|
125
|
+
callArgs.push(castSelectWrapper(selectExpr));
|
|
113
126
|
}
|
|
114
127
|
}
|
|
115
128
|
else if (selectExpr) {
|
|
116
|
-
// No arguments: pass { select } as the only param (ORM signature)
|
|
117
|
-
callArgs.push(
|
|
118
|
-
t.objectProperty(t.identifier('select'), selectExpr),
|
|
119
|
-
]));
|
|
129
|
+
// No arguments: pass { select } as the only param (ORM signature).
|
|
130
|
+
callArgs.push(castSelectWrapper(selectExpr));
|
|
120
131
|
}
|
|
121
132
|
return t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.memberExpression(t.identifier('client'), t.identifier(opKind)), t.identifier(opName)), callArgs), t.identifier('execute')), []);
|
|
122
133
|
}
|
|
@@ -152,6 +163,18 @@ function generateCustomCommand(op, options) {
|
|
|
152
163
|
if (utilsImports.length > 0) {
|
|
153
164
|
statements.push(createImportDeclaration(utilsPath, utilsImports));
|
|
154
165
|
}
|
|
166
|
+
// Import the Variables type for this operation from the ORM query/mutation module.
|
|
167
|
+
// Custom operations define their own Variables types (e.g. CheckPasswordVariables)
|
|
168
|
+
// in the ORM layer. We import and cast CLI answers to this type for proper typing.
|
|
169
|
+
if (op.args.length > 0) {
|
|
170
|
+
const variablesTypeName = `${(0, utils_1.ucFirst)(op.name)}Variables`;
|
|
171
|
+
// Commands are at cli/commands/xxx.ts (no target) or cli/commands/{target}/xxx.ts (with target).
|
|
172
|
+
// ORM query/mutation is at orm/{opKind}/ — two or three levels up from commands.
|
|
173
|
+
const ormOpPath = options?.targetName
|
|
174
|
+
? `../../../orm/${opKind}`
|
|
175
|
+
: `../../orm/${opKind}`;
|
|
176
|
+
statements.push(createImportDeclaration(ormOpPath, [variablesTypeName], true));
|
|
177
|
+
}
|
|
155
178
|
const questionsArray = op.args.length > 0
|
|
156
179
|
? (0, arg_mapper_1.buildQuestionsArray)(op.args)
|
|
157
180
|
: t.arrayExpression([]);
|
|
@@ -189,10 +212,16 @@ function generateCustomCommand(op, options) {
|
|
|
189
212
|
])),
|
|
190
213
|
]));
|
|
191
214
|
}
|
|
215
|
+
// Cast args to the specific Variables type for this operation.
|
|
216
|
+
// The ORM expects typed variables (e.g. CheckPasswordVariables), and CLI
|
|
217
|
+
// prompt answers are Record<string, unknown>. We cast through `unknown`
|
|
218
|
+
// first because Record<string, unknown> doesn't directly overlap with
|
|
219
|
+
// Variables types that have specific property types (like `input: SomeInput`).
|
|
220
|
+
const variablesTypeName = `${(0, utils_1.ucFirst)(op.name)}Variables`;
|
|
192
221
|
const argsExpr = op.args.length > 0
|
|
193
|
-
? (hasInputObjectArg
|
|
222
|
+
? t.tsAsExpression(t.tsAsExpression(hasInputObjectArg
|
|
194
223
|
? t.identifier('parsedAnswers')
|
|
195
|
-
: t.identifier('answers'))
|
|
224
|
+
: t.identifier('answers'), t.tsUnknownKeyword()), t.tsTypeReference(t.identifier(variablesTypeName)))
|
|
196
225
|
: t.objectExpression([]);
|
|
197
226
|
// For OBJECT return types, generate runtime select from --select flag
|
|
198
227
|
// For scalar return types, no select is needed
|
|
@@ -202,14 +231,31 @@ function generateCustomCommand(op, options) {
|
|
|
202
231
|
// Generate: const selectFields = buildSelectFromPaths(argv.select ?? 'defaultFields')
|
|
203
232
|
bodyStatements.push(t.variableDeclaration('const', [
|
|
204
233
|
t.variableDeclarator(t.identifier('selectFields'), t.callExpression(t.identifier('buildSelectFromPaths'), [
|
|
205
|
-
t.logicalExpression('??', t.memberExpression(t.identifier('argv'), t.identifier('select')), t.stringLiteral(defaultSelect)),
|
|
234
|
+
t.logicalExpression('??', t.tsAsExpression(t.memberExpression(t.identifier('argv'), t.identifier('select')), t.tsStringKeyword()), t.stringLiteral(defaultSelect)),
|
|
206
235
|
])),
|
|
207
236
|
]));
|
|
208
237
|
selectExpr = t.identifier('selectFields');
|
|
209
238
|
}
|
|
239
|
+
// Derive the Select type name from the operation's return type.
|
|
240
|
+
// e.g. CheckPasswordPayload → CheckPasswordPayloadSelect
|
|
241
|
+
// This is used to cast { select } to the proper type for StrictSelect.
|
|
242
|
+
let selectTypeName;
|
|
243
|
+
if (isObjectReturn) {
|
|
244
|
+
const baseReturnType = unwrapType(op.returnType);
|
|
245
|
+
if (baseReturnType.name) {
|
|
246
|
+
selectTypeName = `${baseReturnType.name}Select`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// Import the Select type from orm/input-types if we have one
|
|
250
|
+
if (selectTypeName) {
|
|
251
|
+
const inputTypesPath = options?.targetName
|
|
252
|
+
? `../../../orm/input-types`
|
|
253
|
+
: `../../orm/input-types`;
|
|
254
|
+
statements.push(createImportDeclaration(inputTypesPath, [selectTypeName], true));
|
|
255
|
+
}
|
|
210
256
|
const hasArgs = op.args.length > 0;
|
|
211
257
|
bodyStatements.push(t.variableDeclaration('const', [
|
|
212
|
-
t.variableDeclarator(t.identifier('result'), t.awaitExpression(buildOrmCustomCall(opKind, op.name, argsExpr, selectExpr, hasArgs))),
|
|
258
|
+
t.variableDeclarator(t.identifier('result'), t.awaitExpression(buildOrmCustomCall(opKind, op.name, argsExpr, selectExpr, hasArgs, selectTypeName))),
|
|
213
259
|
]));
|
|
214
260
|
if (options?.saveToken) {
|
|
215
261
|
bodyStatements.push(t.ifStatement(t.logicalExpression('&&', t.memberExpression(t.identifier('argv'), t.identifier('saveToken')), t.identifier('result')), t.blockStatement([
|
|
@@ -5,7 +5,7 @@ export type { GeneratedDocFile, McpTool } from '../docs-utils';
|
|
|
5
5
|
export declare function generateReadme(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): GeneratedDocFile;
|
|
6
6
|
export declare function generateAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): GeneratedDocFile;
|
|
7
7
|
export declare function getCliMcpTools(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): McpTool[];
|
|
8
|
-
export declare function generateSkills(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): GeneratedDocFile[];
|
|
8
|
+
export declare function generateSkills(tables: CleanTable[], customOperations: CleanOperation[], toolName: string, targetName: string): GeneratedDocFile[];
|
|
9
9
|
export interface MultiTargetDocsInput {
|
|
10
10
|
toolName: string;
|
|
11
11
|
builtinNames: {
|
|
@@ -525,12 +525,16 @@ function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
525
525
|
}
|
|
526
526
|
return tools;
|
|
527
527
|
}
|
|
528
|
-
function generateSkills(tables, customOperations, toolName) {
|
|
528
|
+
function generateSkills(tables, customOperations, toolName, targetName) {
|
|
529
529
|
const files = [];
|
|
530
|
+
const skillName = `cli-${targetName}`;
|
|
531
|
+
const referenceNames = [];
|
|
532
|
+
// Context reference
|
|
533
|
+
referenceNames.push('context');
|
|
530
534
|
files.push({
|
|
531
|
-
fileName:
|
|
532
|
-
content: (0, docs_utils_1.
|
|
533
|
-
|
|
535
|
+
fileName: `${skillName}/references/context.md`,
|
|
536
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
537
|
+
title: 'Context Management',
|
|
534
538
|
description: `Manage API endpoint contexts for ${toolName}`,
|
|
535
539
|
usage: [
|
|
536
540
|
`${toolName} context create <name> --endpoint <url>`,
|
|
@@ -554,10 +558,12 @@ function generateSkills(tables, customOperations, toolName) {
|
|
|
554
558
|
],
|
|
555
559
|
}),
|
|
556
560
|
});
|
|
561
|
+
// Auth reference
|
|
562
|
+
referenceNames.push('auth');
|
|
557
563
|
files.push({
|
|
558
|
-
fileName:
|
|
559
|
-
content: (0, docs_utils_1.
|
|
560
|
-
|
|
564
|
+
fileName: `${skillName}/references/auth.md`,
|
|
565
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
566
|
+
title: 'Authentication',
|
|
561
567
|
description: `Manage authentication tokens for ${toolName}`,
|
|
562
568
|
usage: [
|
|
563
569
|
`${toolName} auth set-token <token>`,
|
|
@@ -576,15 +582,17 @@ function generateSkills(tables, customOperations, toolName) {
|
|
|
576
582
|
],
|
|
577
583
|
}),
|
|
578
584
|
});
|
|
585
|
+
// Table references
|
|
579
586
|
for (const table of tables) {
|
|
580
587
|
const { singularName } = (0, utils_1.getTableNames)(table);
|
|
581
588
|
const kebab = (0, komoji_1.toKebabCase)(singularName);
|
|
582
589
|
const pk = (0, utils_1.getPrimaryKeyInfo)(table)[0];
|
|
583
590
|
const editableFields = (0, docs_utils_1.getEditableFields)(table);
|
|
591
|
+
referenceNames.push(kebab);
|
|
584
592
|
files.push({
|
|
585
|
-
fileName:
|
|
586
|
-
content: (0, docs_utils_1.
|
|
587
|
-
|
|
593
|
+
fileName: `${skillName}/references/${kebab}.md`,
|
|
594
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
595
|
+
title: singularName,
|
|
588
596
|
description: `CRUD operations for ${table.name} records via ${toolName} CLI`,
|
|
589
597
|
usage: [
|
|
590
598
|
`${toolName} ${kebab} list`,
|
|
@@ -608,29 +616,21 @@ function generateSkills(tables, customOperations, toolName) {
|
|
|
608
616
|
description: `Get a ${singularName} by ${pk.name}`,
|
|
609
617
|
code: [`${toolName} ${kebab} get --${pk.name} <value>`],
|
|
610
618
|
},
|
|
611
|
-
{
|
|
612
|
-
description: `Update a ${singularName}`,
|
|
613
|
-
code: [
|
|
614
|
-
`${toolName} ${kebab} update --${pk.name} <value> --${editableFields[0]?.name || 'field'} "new-value"`,
|
|
615
|
-
],
|
|
616
|
-
},
|
|
617
|
-
{
|
|
618
|
-
description: `Delete a ${singularName}`,
|
|
619
|
-
code: [`${toolName} ${kebab} delete --${pk.name} <value>`],
|
|
620
|
-
},
|
|
621
619
|
],
|
|
622
620
|
}),
|
|
623
621
|
});
|
|
624
622
|
}
|
|
623
|
+
// Custom operation references
|
|
625
624
|
for (const op of customOperations) {
|
|
626
625
|
const kebab = (0, komoji_1.toKebabCase)(op.name);
|
|
627
626
|
const usage = op.args.length > 0
|
|
628
627
|
? `${toolName} ${kebab} ${op.args.map((a) => `--${a.name} <value>`).join(' ')}`
|
|
629
628
|
: `${toolName} ${kebab}`;
|
|
629
|
+
referenceNames.push(kebab);
|
|
630
630
|
files.push({
|
|
631
|
-
fileName:
|
|
632
|
-
content: (0, docs_utils_1.
|
|
633
|
-
|
|
631
|
+
fileName: `${skillName}/references/${kebab}.md`,
|
|
632
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
633
|
+
title: op.name,
|
|
634
634
|
description: op.description || `Execute the ${op.name} ${op.kind}`,
|
|
635
635
|
usage: [usage],
|
|
636
636
|
examples: [
|
|
@@ -642,6 +642,39 @@ function generateSkills(tables, customOperations, toolName) {
|
|
|
642
642
|
}),
|
|
643
643
|
});
|
|
644
644
|
}
|
|
645
|
+
// Overview SKILL.md
|
|
646
|
+
const tableKebabs = tables.slice(0, 5).map((t) => (0, komoji_1.toKebabCase)((0, utils_1.getTableNames)(t).singularName));
|
|
647
|
+
files.push({
|
|
648
|
+
fileName: `${skillName}/SKILL.md`,
|
|
649
|
+
content: (0, docs_utils_1.buildSkillFile)({
|
|
650
|
+
name: skillName,
|
|
651
|
+
description: `CLI tool (${toolName}) for the ${targetName} API — provides CRUD commands for ${tables.length} tables and ${customOperations.length} custom operations`,
|
|
652
|
+
usage: [
|
|
653
|
+
`# Context management`,
|
|
654
|
+
`${toolName} context create <name> --endpoint <url>`,
|
|
655
|
+
`${toolName} context use <name>`,
|
|
656
|
+
'',
|
|
657
|
+
`# Authentication`,
|
|
658
|
+
`${toolName} auth set-token <token>`,
|
|
659
|
+
'',
|
|
660
|
+
`# CRUD for any table (e.g. ${tableKebabs[0] || 'model'})`,
|
|
661
|
+
`${toolName} ${tableKebabs[0] || 'model'} list`,
|
|
662
|
+
`${toolName} ${tableKebabs[0] || 'model'} get --id <value>`,
|
|
663
|
+
`${toolName} ${tableKebabs[0] || 'model'} create --<field> <value>`,
|
|
664
|
+
],
|
|
665
|
+
examples: [
|
|
666
|
+
{
|
|
667
|
+
description: 'Set up and query',
|
|
668
|
+
code: [
|
|
669
|
+
`${toolName} context create local --endpoint http://localhost:5000/graphql`,
|
|
670
|
+
`${toolName} context use local`,
|
|
671
|
+
`${toolName} auth set-token <token>`,
|
|
672
|
+
`${toolName} ${tableKebabs[0] || 'model'} list`,
|
|
673
|
+
],
|
|
674
|
+
},
|
|
675
|
+
],
|
|
676
|
+
}, referenceNames),
|
|
677
|
+
});
|
|
645
678
|
return files;
|
|
646
679
|
}
|
|
647
680
|
function generateMultiTargetReadme(input) {
|
|
@@ -1268,22 +1301,26 @@ function getMultiTargetCliMcpTools(input) {
|
|
|
1268
1301
|
function generateMultiTargetSkills(input) {
|
|
1269
1302
|
const { toolName, builtinNames, targets } = input;
|
|
1270
1303
|
const files = [];
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
`${toolName} ${builtinNames.context} use <name>`,
|
|
1275
|
-
`${toolName} ${builtinNames.context} current`,
|
|
1276
|
-
`${toolName} ${builtinNames.context} delete <name>`,
|
|
1277
|
-
];
|
|
1304
|
+
// Generate one skill per target, plus a shared cli-common skill for context/auth
|
|
1305
|
+
const commonSkillName = 'cli-common';
|
|
1306
|
+
const commonReferenceNames = [];
|
|
1278
1307
|
const contextCreateFlags = targets
|
|
1279
1308
|
.map((t) => `--${t.name}-endpoint <url>`)
|
|
1280
1309
|
.join(' ');
|
|
1310
|
+
// Context reference
|
|
1311
|
+
commonReferenceNames.push('context');
|
|
1281
1312
|
files.push({
|
|
1282
|
-
fileName:
|
|
1283
|
-
content: (0, docs_utils_1.
|
|
1284
|
-
|
|
1313
|
+
fileName: `${commonSkillName}/references/context.md`,
|
|
1314
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
1315
|
+
title: 'Context Management',
|
|
1285
1316
|
description: `Manage API endpoint contexts for ${toolName} (multi-target: ${targets.map((t) => t.name).join(', ')})`,
|
|
1286
|
-
usage:
|
|
1317
|
+
usage: [
|
|
1318
|
+
`${toolName} ${builtinNames.context} create <name>`,
|
|
1319
|
+
`${toolName} ${builtinNames.context} list`,
|
|
1320
|
+
`${toolName} ${builtinNames.context} use <name>`,
|
|
1321
|
+
`${toolName} ${builtinNames.context} current`,
|
|
1322
|
+
`${toolName} ${builtinNames.context} delete <name>`,
|
|
1323
|
+
],
|
|
1287
1324
|
examples: [
|
|
1288
1325
|
{
|
|
1289
1326
|
description: 'Create a context for local development (accept all defaults)',
|
|
@@ -1299,20 +1336,15 @@ function generateMultiTargetSkills(input) {
|
|
|
1299
1336
|
`${toolName} ${builtinNames.context} use production`,
|
|
1300
1337
|
],
|
|
1301
1338
|
},
|
|
1302
|
-
{
|
|
1303
|
-
description: 'List and switch contexts',
|
|
1304
|
-
code: [
|
|
1305
|
-
`${toolName} ${builtinNames.context} list`,
|
|
1306
|
-
`${toolName} ${builtinNames.context} use staging`,
|
|
1307
|
-
],
|
|
1308
|
-
},
|
|
1309
1339
|
],
|
|
1310
1340
|
}),
|
|
1311
1341
|
});
|
|
1342
|
+
// Auth reference
|
|
1343
|
+
commonReferenceNames.push('auth');
|
|
1312
1344
|
files.push({
|
|
1313
|
-
fileName:
|
|
1314
|
-
content: (0, docs_utils_1.
|
|
1315
|
-
|
|
1345
|
+
fileName: `${commonSkillName}/references/auth.md`,
|
|
1346
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
1347
|
+
title: 'Authentication',
|
|
1316
1348
|
description: `Manage authentication tokens for ${toolName} (shared across all targets)`,
|
|
1317
1349
|
usage: [
|
|
1318
1350
|
`${toolName} ${builtinNames.auth} set-token <token>`,
|
|
@@ -1331,17 +1363,48 @@ function generateMultiTargetSkills(input) {
|
|
|
1331
1363
|
],
|
|
1332
1364
|
}),
|
|
1333
1365
|
});
|
|
1366
|
+
// Common SKILL.md
|
|
1367
|
+
files.push({
|
|
1368
|
+
fileName: `${commonSkillName}/SKILL.md`,
|
|
1369
|
+
content: (0, docs_utils_1.buildSkillFile)({
|
|
1370
|
+
name: commonSkillName,
|
|
1371
|
+
description: `Shared CLI utilities for ${toolName} — context management and authentication across targets: ${targets.map((t) => t.name).join(', ')}`,
|
|
1372
|
+
usage: [
|
|
1373
|
+
`# Context management`,
|
|
1374
|
+
`${toolName} ${builtinNames.context} create <name>`,
|
|
1375
|
+
`${toolName} ${builtinNames.context} use <name>`,
|
|
1376
|
+
'',
|
|
1377
|
+
`# Authentication`,
|
|
1378
|
+
`${toolName} ${builtinNames.auth} set-token <token>`,
|
|
1379
|
+
`${toolName} ${builtinNames.auth} status`,
|
|
1380
|
+
],
|
|
1381
|
+
examples: [
|
|
1382
|
+
{
|
|
1383
|
+
description: 'Set up and authenticate',
|
|
1384
|
+
code: [
|
|
1385
|
+
`${toolName} ${builtinNames.context} create local`,
|
|
1386
|
+
`${toolName} ${builtinNames.context} use local`,
|
|
1387
|
+
`${toolName} ${builtinNames.auth} set-token <token>`,
|
|
1388
|
+
],
|
|
1389
|
+
},
|
|
1390
|
+
],
|
|
1391
|
+
}, commonReferenceNames),
|
|
1392
|
+
});
|
|
1393
|
+
// Generate one skill per target with table/op references
|
|
1334
1394
|
for (const tgt of targets) {
|
|
1395
|
+
const tgtSkillName = `cli-${tgt.name}`;
|
|
1396
|
+
const tgtReferenceNames = [];
|
|
1335
1397
|
for (const table of tgt.tables) {
|
|
1336
1398
|
const { singularName } = (0, utils_1.getTableNames)(table);
|
|
1337
1399
|
const kebab = (0, komoji_1.toKebabCase)(singularName);
|
|
1338
1400
|
const pk = (0, utils_1.getPrimaryKeyInfo)(table)[0];
|
|
1339
1401
|
const editableFields = (0, docs_utils_1.getEditableFields)(table);
|
|
1340
1402
|
const cmd = `${tgt.name}:${kebab}`;
|
|
1403
|
+
tgtReferenceNames.push(kebab);
|
|
1341
1404
|
files.push({
|
|
1342
|
-
fileName:
|
|
1343
|
-
content: (0, docs_utils_1.
|
|
1344
|
-
|
|
1405
|
+
fileName: `${tgtSkillName}/references/${kebab}.md`,
|
|
1406
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
1407
|
+
title: singularName,
|
|
1345
1408
|
description: `CRUD operations for ${table.name} records via ${toolName} CLI (${tgt.name} target)`,
|
|
1346
1409
|
usage: [
|
|
1347
1410
|
`${toolName} ${cmd} list`,
|
|
@@ -1361,10 +1424,6 @@ function generateMultiTargetSkills(input) {
|
|
|
1361
1424
|
`${toolName} ${cmd} create ${editableFields.map((f) => `--${f.name} "value"`).join(' ')}`,
|
|
1362
1425
|
],
|
|
1363
1426
|
},
|
|
1364
|
-
{
|
|
1365
|
-
description: `Get a ${singularName} by ${pk.name}`,
|
|
1366
|
-
code: [`${toolName} ${cmd} get --${pk.name} <value>`],
|
|
1367
|
-
},
|
|
1368
1427
|
],
|
|
1369
1428
|
}),
|
|
1370
1429
|
});
|
|
@@ -1379,10 +1438,11 @@ function generateMultiTargetSkills(input) {
|
|
|
1379
1438
|
if (tgt.isAuthTarget && op.kind === 'mutation') {
|
|
1380
1439
|
usageLines.push(`${baseUsage} --save-token`);
|
|
1381
1440
|
}
|
|
1441
|
+
tgtReferenceNames.push(kebab);
|
|
1382
1442
|
files.push({
|
|
1383
|
-
fileName:
|
|
1384
|
-
content: (0, docs_utils_1.
|
|
1385
|
-
|
|
1443
|
+
fileName: `${tgtSkillName}/references/${kebab}.md`,
|
|
1444
|
+
content: (0, docs_utils_1.buildSkillReference)({
|
|
1445
|
+
title: op.name,
|
|
1386
1446
|
description: `${op.description || `Execute the ${op.name} ${op.kind}`} (${tgt.name} target)`,
|
|
1387
1447
|
usage: usageLines,
|
|
1388
1448
|
examples: [
|
|
@@ -1394,6 +1454,29 @@ function generateMultiTargetSkills(input) {
|
|
|
1394
1454
|
}),
|
|
1395
1455
|
});
|
|
1396
1456
|
}
|
|
1457
|
+
// Target SKILL.md
|
|
1458
|
+
const firstKebab = tgt.tables.length > 0
|
|
1459
|
+
? (0, komoji_1.toKebabCase)((0, utils_1.getTableNames)(tgt.tables[0]).singularName)
|
|
1460
|
+
: 'model';
|
|
1461
|
+
files.push({
|
|
1462
|
+
fileName: `${tgtSkillName}/SKILL.md`,
|
|
1463
|
+
content: (0, docs_utils_1.buildSkillFile)({
|
|
1464
|
+
name: tgtSkillName,
|
|
1465
|
+
description: `CLI commands for the ${tgt.name} API target — ${tgt.tables.length} tables and ${tgt.customOperations.length} custom operations via ${toolName}`,
|
|
1466
|
+
usage: [
|
|
1467
|
+
`# CRUD for ${tgt.name} tables (e.g. ${firstKebab})`,
|
|
1468
|
+
`${toolName} ${tgt.name}:${firstKebab} list`,
|
|
1469
|
+
`${toolName} ${tgt.name}:${firstKebab} get --id <value>`,
|
|
1470
|
+
`${toolName} ${tgt.name}:${firstKebab} create --<field> <value>`,
|
|
1471
|
+
],
|
|
1472
|
+
examples: [
|
|
1473
|
+
{
|
|
1474
|
+
description: `Query ${tgt.name} records`,
|
|
1475
|
+
code: [`${toolName} ${tgt.name}:${firstKebab} list`],
|
|
1476
|
+
},
|
|
1477
|
+
],
|
|
1478
|
+
}, tgtReferenceNames),
|
|
1479
|
+
});
|
|
1397
1480
|
}
|
|
1398
1481
|
return files;
|
|
1399
1482
|
}
|
|
@@ -86,9 +86,16 @@ function generateExecutorFile(toolName, options) {
|
|
|
86
86
|
])),
|
|
87
87
|
])),
|
|
88
88
|
])),
|
|
89
|
-
|
|
90
|
-
t.
|
|
91
|
-
|
|
89
|
+
(() => {
|
|
90
|
+
const headersId = t.identifier('headers');
|
|
91
|
+
headersId.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
92
|
+
t.tsStringKeyword(),
|
|
93
|
+
t.tsStringKeyword(),
|
|
94
|
+
])));
|
|
95
|
+
return t.variableDeclaration('const', [
|
|
96
|
+
t.variableDeclarator(headersId, t.objectExpression([])),
|
|
97
|
+
]);
|
|
98
|
+
})(),
|
|
92
99
|
t.ifStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('hasValidCredentials')), [t.memberExpression(t.identifier('ctx'), t.identifier('name'))]), t.blockStatement([
|
|
93
100
|
t.variableDeclaration('const', [
|
|
94
101
|
t.variableDeclarator(t.identifier('creds'), t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('getCredentials')), [t.memberExpression(t.identifier('ctx'), t.identifier('name'))])),
|
|
@@ -193,9 +200,16 @@ function generateMultiTargetExecutorFile(toolName, targets, options) {
|
|
|
193
200
|
], [t.identifier('targetName')]),
|
|
194
201
|
])),
|
|
195
202
|
])),
|
|
196
|
-
|
|
197
|
-
t.
|
|
198
|
-
|
|
203
|
+
(() => {
|
|
204
|
+
const headersId = t.identifier('headers');
|
|
205
|
+
headersId.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
206
|
+
t.tsStringKeyword(),
|
|
207
|
+
t.tsStringKeyword(),
|
|
208
|
+
])));
|
|
209
|
+
return t.variableDeclaration('const', [
|
|
210
|
+
t.variableDeclarator(headersId, t.objectExpression([])),
|
|
211
|
+
]);
|
|
212
|
+
})(),
|
|
199
213
|
t.variableDeclaration('let', [
|
|
200
214
|
t.variableDeclarator(t.identifier('endpoint'), t.stringLiteral('')),
|
|
201
215
|
]),
|