@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as t from '@babel/types';
|
|
2
2
|
import { toKebabCase } from 'komoji';
|
|
3
3
|
import { generateCode } from '../babel-ast';
|
|
4
|
-
import { getGeneratedFileHeader } from '../utils';
|
|
4
|
+
import { getGeneratedFileHeader, ucFirst } from '../utils';
|
|
5
5
|
import { buildQuestionsArray } from './arg-mapper';
|
|
6
6
|
function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false) {
|
|
7
7
|
const specifiers = namedImports.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
|
|
@@ -65,22 +65,33 @@ function buildDefaultSelectString(returnType, isMutation) {
|
|
|
65
65
|
}
|
|
66
66
|
return '';
|
|
67
67
|
}
|
|
68
|
-
function buildOrmCustomCall(opKind, opName, argsExpr, selectExpr, hasArgs = true) {
|
|
68
|
+
function buildOrmCustomCall(opKind, opName, argsExpr, selectExpr, hasArgs = true, selectTypeName) {
|
|
69
69
|
const callArgs = [];
|
|
70
|
+
// Helper: wrap { select } and cast to `{ select: XxxSelect }` via `unknown`.
|
|
71
|
+
// The ORM method's second parameter is `{ select: S } & StrictSelect<S, XxxSelect>`.
|
|
72
|
+
// We import the concrete Select type (e.g. CheckPasswordPayloadSelect) and cast
|
|
73
|
+
// `{ select: selectFields } as unknown as { select: XxxSelect }` so TS infers
|
|
74
|
+
// `S = XxxSelect` and StrictSelect is satisfied.
|
|
75
|
+
const castSelectWrapper = (sel) => {
|
|
76
|
+
const selectObj = t.objectExpression([
|
|
77
|
+
t.objectProperty(t.identifier('select'), sel),
|
|
78
|
+
]);
|
|
79
|
+
if (!selectTypeName)
|
|
80
|
+
return selectObj;
|
|
81
|
+
return t.tsAsExpression(t.tsAsExpression(selectObj, t.tsUnknownKeyword()), t.tsTypeLiteral([
|
|
82
|
+
t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName)))),
|
|
83
|
+
]));
|
|
84
|
+
};
|
|
70
85
|
if (hasArgs) {
|
|
71
|
-
// Operation has arguments: pass args as first param, select as second
|
|
86
|
+
// Operation has arguments: pass args as first param, select as second.
|
|
72
87
|
callArgs.push(argsExpr);
|
|
73
88
|
if (selectExpr) {
|
|
74
|
-
callArgs.push(
|
|
75
|
-
t.objectProperty(t.identifier('select'), selectExpr),
|
|
76
|
-
]));
|
|
89
|
+
callArgs.push(castSelectWrapper(selectExpr));
|
|
77
90
|
}
|
|
78
91
|
}
|
|
79
92
|
else if (selectExpr) {
|
|
80
|
-
// No arguments: pass { select } as the only param (ORM signature)
|
|
81
|
-
callArgs.push(
|
|
82
|
-
t.objectProperty(t.identifier('select'), selectExpr),
|
|
83
|
-
]));
|
|
93
|
+
// No arguments: pass { select } as the only param (ORM signature).
|
|
94
|
+
callArgs.push(castSelectWrapper(selectExpr));
|
|
84
95
|
}
|
|
85
96
|
return t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.memberExpression(t.identifier('client'), t.identifier(opKind)), t.identifier(opName)), callArgs), t.identifier('execute')), []);
|
|
86
97
|
}
|
|
@@ -116,6 +127,18 @@ export function generateCustomCommand(op, options) {
|
|
|
116
127
|
if (utilsImports.length > 0) {
|
|
117
128
|
statements.push(createImportDeclaration(utilsPath, utilsImports));
|
|
118
129
|
}
|
|
130
|
+
// Import the Variables type for this operation from the ORM query/mutation module.
|
|
131
|
+
// Custom operations define their own Variables types (e.g. CheckPasswordVariables)
|
|
132
|
+
// in the ORM layer. We import and cast CLI answers to this type for proper typing.
|
|
133
|
+
if (op.args.length > 0) {
|
|
134
|
+
const variablesTypeName = `${ucFirst(op.name)}Variables`;
|
|
135
|
+
// Commands are at cli/commands/xxx.ts (no target) or cli/commands/{target}/xxx.ts (with target).
|
|
136
|
+
// ORM query/mutation is at orm/{opKind}/ — two or three levels up from commands.
|
|
137
|
+
const ormOpPath = options?.targetName
|
|
138
|
+
? `../../../orm/${opKind}`
|
|
139
|
+
: `../../orm/${opKind}`;
|
|
140
|
+
statements.push(createImportDeclaration(ormOpPath, [variablesTypeName], true));
|
|
141
|
+
}
|
|
119
142
|
const questionsArray = op.args.length > 0
|
|
120
143
|
? buildQuestionsArray(op.args)
|
|
121
144
|
: t.arrayExpression([]);
|
|
@@ -153,10 +176,16 @@ export function generateCustomCommand(op, options) {
|
|
|
153
176
|
])),
|
|
154
177
|
]));
|
|
155
178
|
}
|
|
179
|
+
// Cast args to the specific Variables type for this operation.
|
|
180
|
+
// The ORM expects typed variables (e.g. CheckPasswordVariables), and CLI
|
|
181
|
+
// prompt answers are Record<string, unknown>. We cast through `unknown`
|
|
182
|
+
// first because Record<string, unknown> doesn't directly overlap with
|
|
183
|
+
// Variables types that have specific property types (like `input: SomeInput`).
|
|
184
|
+
const variablesTypeName = `${ucFirst(op.name)}Variables`;
|
|
156
185
|
const argsExpr = op.args.length > 0
|
|
157
|
-
? (hasInputObjectArg
|
|
186
|
+
? t.tsAsExpression(t.tsAsExpression(hasInputObjectArg
|
|
158
187
|
? t.identifier('parsedAnswers')
|
|
159
|
-
: t.identifier('answers'))
|
|
188
|
+
: t.identifier('answers'), t.tsUnknownKeyword()), t.tsTypeReference(t.identifier(variablesTypeName)))
|
|
160
189
|
: t.objectExpression([]);
|
|
161
190
|
// For OBJECT return types, generate runtime select from --select flag
|
|
162
191
|
// For scalar return types, no select is needed
|
|
@@ -166,14 +195,31 @@ export function generateCustomCommand(op, options) {
|
|
|
166
195
|
// Generate: const selectFields = buildSelectFromPaths(argv.select ?? 'defaultFields')
|
|
167
196
|
bodyStatements.push(t.variableDeclaration('const', [
|
|
168
197
|
t.variableDeclarator(t.identifier('selectFields'), t.callExpression(t.identifier('buildSelectFromPaths'), [
|
|
169
|
-
t.logicalExpression('??', t.memberExpression(t.identifier('argv'), t.identifier('select')), t.stringLiteral(defaultSelect)),
|
|
198
|
+
t.logicalExpression('??', t.tsAsExpression(t.memberExpression(t.identifier('argv'), t.identifier('select')), t.tsStringKeyword()), t.stringLiteral(defaultSelect)),
|
|
170
199
|
])),
|
|
171
200
|
]));
|
|
172
201
|
selectExpr = t.identifier('selectFields');
|
|
173
202
|
}
|
|
203
|
+
// Derive the Select type name from the operation's return type.
|
|
204
|
+
// e.g. CheckPasswordPayload → CheckPasswordPayloadSelect
|
|
205
|
+
// This is used to cast { select } to the proper type for StrictSelect.
|
|
206
|
+
let selectTypeName;
|
|
207
|
+
if (isObjectReturn) {
|
|
208
|
+
const baseReturnType = unwrapType(op.returnType);
|
|
209
|
+
if (baseReturnType.name) {
|
|
210
|
+
selectTypeName = `${baseReturnType.name}Select`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Import the Select type from orm/input-types if we have one
|
|
214
|
+
if (selectTypeName) {
|
|
215
|
+
const inputTypesPath = options?.targetName
|
|
216
|
+
? `../../../orm/input-types`
|
|
217
|
+
: `../../orm/input-types`;
|
|
218
|
+
statements.push(createImportDeclaration(inputTypesPath, [selectTypeName], true));
|
|
219
|
+
}
|
|
174
220
|
const hasArgs = op.args.length > 0;
|
|
175
221
|
bodyStatements.push(t.variableDeclaration('const', [
|
|
176
|
-
t.variableDeclarator(t.identifier('result'), t.awaitExpression(buildOrmCustomCall(opKind, op.name, argsExpr, selectExpr, hasArgs))),
|
|
222
|
+
t.variableDeclarator(t.identifier('result'), t.awaitExpression(buildOrmCustomCall(opKind, op.name, argsExpr, selectExpr, hasArgs, selectTypeName))),
|
|
177
223
|
]));
|
|
178
224
|
if (options?.saveToken) {
|
|
179
225
|
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: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { toKebabCase } from 'komoji';
|
|
2
|
-
import { formatArgType, getEditableFields, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, buildSkillFile, } from '../docs-utils';
|
|
2
|
+
import { formatArgType, getEditableFields, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, buildSkillFile, buildSkillReference, } from '../docs-utils';
|
|
3
3
|
import { getScalarFields, getTableNames, getPrimaryKeyInfo, } from '../utils';
|
|
4
4
|
export { resolveDocsConfig } from '../docs-utils';
|
|
5
5
|
export function generateReadme(tables, customOperations, toolName) {
|
|
@@ -513,12 +513,16 @@ export function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
513
513
|
}
|
|
514
514
|
return tools;
|
|
515
515
|
}
|
|
516
|
-
export function generateSkills(tables, customOperations, toolName) {
|
|
516
|
+
export function generateSkills(tables, customOperations, toolName, targetName) {
|
|
517
517
|
const files = [];
|
|
518
|
+
const skillName = `cli-${targetName}`;
|
|
519
|
+
const referenceNames = [];
|
|
520
|
+
// Context reference
|
|
521
|
+
referenceNames.push('context');
|
|
518
522
|
files.push({
|
|
519
|
-
fileName:
|
|
520
|
-
content:
|
|
521
|
-
|
|
523
|
+
fileName: `${skillName}/references/context.md`,
|
|
524
|
+
content: buildSkillReference({
|
|
525
|
+
title: 'Context Management',
|
|
522
526
|
description: `Manage API endpoint contexts for ${toolName}`,
|
|
523
527
|
usage: [
|
|
524
528
|
`${toolName} context create <name> --endpoint <url>`,
|
|
@@ -542,10 +546,12 @@ export function generateSkills(tables, customOperations, toolName) {
|
|
|
542
546
|
],
|
|
543
547
|
}),
|
|
544
548
|
});
|
|
549
|
+
// Auth reference
|
|
550
|
+
referenceNames.push('auth');
|
|
545
551
|
files.push({
|
|
546
|
-
fileName:
|
|
547
|
-
content:
|
|
548
|
-
|
|
552
|
+
fileName: `${skillName}/references/auth.md`,
|
|
553
|
+
content: buildSkillReference({
|
|
554
|
+
title: 'Authentication',
|
|
549
555
|
description: `Manage authentication tokens for ${toolName}`,
|
|
550
556
|
usage: [
|
|
551
557
|
`${toolName} auth set-token <token>`,
|
|
@@ -564,15 +570,17 @@ export function generateSkills(tables, customOperations, toolName) {
|
|
|
564
570
|
],
|
|
565
571
|
}),
|
|
566
572
|
});
|
|
573
|
+
// Table references
|
|
567
574
|
for (const table of tables) {
|
|
568
575
|
const { singularName } = getTableNames(table);
|
|
569
576
|
const kebab = toKebabCase(singularName);
|
|
570
577
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
571
578
|
const editableFields = getEditableFields(table);
|
|
579
|
+
referenceNames.push(kebab);
|
|
572
580
|
files.push({
|
|
573
|
-
fileName:
|
|
574
|
-
content:
|
|
575
|
-
|
|
581
|
+
fileName: `${skillName}/references/${kebab}.md`,
|
|
582
|
+
content: buildSkillReference({
|
|
583
|
+
title: singularName,
|
|
576
584
|
description: `CRUD operations for ${table.name} records via ${toolName} CLI`,
|
|
577
585
|
usage: [
|
|
578
586
|
`${toolName} ${kebab} list`,
|
|
@@ -596,29 +604,21 @@ export function generateSkills(tables, customOperations, toolName) {
|
|
|
596
604
|
description: `Get a ${singularName} by ${pk.name}`,
|
|
597
605
|
code: [`${toolName} ${kebab} get --${pk.name} <value>`],
|
|
598
606
|
},
|
|
599
|
-
{
|
|
600
|
-
description: `Update a ${singularName}`,
|
|
601
|
-
code: [
|
|
602
|
-
`${toolName} ${kebab} update --${pk.name} <value> --${editableFields[0]?.name || 'field'} "new-value"`,
|
|
603
|
-
],
|
|
604
|
-
},
|
|
605
|
-
{
|
|
606
|
-
description: `Delete a ${singularName}`,
|
|
607
|
-
code: [`${toolName} ${kebab} delete --${pk.name} <value>`],
|
|
608
|
-
},
|
|
609
607
|
],
|
|
610
608
|
}),
|
|
611
609
|
});
|
|
612
610
|
}
|
|
611
|
+
// Custom operation references
|
|
613
612
|
for (const op of customOperations) {
|
|
614
613
|
const kebab = toKebabCase(op.name);
|
|
615
614
|
const usage = op.args.length > 0
|
|
616
615
|
? `${toolName} ${kebab} ${op.args.map((a) => `--${a.name} <value>`).join(' ')}`
|
|
617
616
|
: `${toolName} ${kebab}`;
|
|
617
|
+
referenceNames.push(kebab);
|
|
618
618
|
files.push({
|
|
619
|
-
fileName:
|
|
620
|
-
content:
|
|
621
|
-
|
|
619
|
+
fileName: `${skillName}/references/${kebab}.md`,
|
|
620
|
+
content: buildSkillReference({
|
|
621
|
+
title: op.name,
|
|
622
622
|
description: op.description || `Execute the ${op.name} ${op.kind}`,
|
|
623
623
|
usage: [usage],
|
|
624
624
|
examples: [
|
|
@@ -630,6 +630,39 @@ export function generateSkills(tables, customOperations, toolName) {
|
|
|
630
630
|
}),
|
|
631
631
|
});
|
|
632
632
|
}
|
|
633
|
+
// Overview SKILL.md
|
|
634
|
+
const tableKebabs = tables.slice(0, 5).map((t) => toKebabCase(getTableNames(t).singularName));
|
|
635
|
+
files.push({
|
|
636
|
+
fileName: `${skillName}/SKILL.md`,
|
|
637
|
+
content: buildSkillFile({
|
|
638
|
+
name: skillName,
|
|
639
|
+
description: `CLI tool (${toolName}) for the ${targetName} API — provides CRUD commands for ${tables.length} tables and ${customOperations.length} custom operations`,
|
|
640
|
+
usage: [
|
|
641
|
+
`# Context management`,
|
|
642
|
+
`${toolName} context create <name> --endpoint <url>`,
|
|
643
|
+
`${toolName} context use <name>`,
|
|
644
|
+
'',
|
|
645
|
+
`# Authentication`,
|
|
646
|
+
`${toolName} auth set-token <token>`,
|
|
647
|
+
'',
|
|
648
|
+
`# CRUD for any table (e.g. ${tableKebabs[0] || 'model'})`,
|
|
649
|
+
`${toolName} ${tableKebabs[0] || 'model'} list`,
|
|
650
|
+
`${toolName} ${tableKebabs[0] || 'model'} get --id <value>`,
|
|
651
|
+
`${toolName} ${tableKebabs[0] || 'model'} create --<field> <value>`,
|
|
652
|
+
],
|
|
653
|
+
examples: [
|
|
654
|
+
{
|
|
655
|
+
description: 'Set up and query',
|
|
656
|
+
code: [
|
|
657
|
+
`${toolName} context create local --endpoint http://localhost:5000/graphql`,
|
|
658
|
+
`${toolName} context use local`,
|
|
659
|
+
`${toolName} auth set-token <token>`,
|
|
660
|
+
`${toolName} ${tableKebabs[0] || 'model'} list`,
|
|
661
|
+
],
|
|
662
|
+
},
|
|
663
|
+
],
|
|
664
|
+
}, referenceNames),
|
|
665
|
+
});
|
|
633
666
|
return files;
|
|
634
667
|
}
|
|
635
668
|
export function generateMultiTargetReadme(input) {
|
|
@@ -1256,22 +1289,26 @@ export function getMultiTargetCliMcpTools(input) {
|
|
|
1256
1289
|
export function generateMultiTargetSkills(input) {
|
|
1257
1290
|
const { toolName, builtinNames, targets } = input;
|
|
1258
1291
|
const files = [];
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
`${toolName} ${builtinNames.context} use <name>`,
|
|
1263
|
-
`${toolName} ${builtinNames.context} current`,
|
|
1264
|
-
`${toolName} ${builtinNames.context} delete <name>`,
|
|
1265
|
-
];
|
|
1292
|
+
// Generate one skill per target, plus a shared cli-common skill for context/auth
|
|
1293
|
+
const commonSkillName = 'cli-common';
|
|
1294
|
+
const commonReferenceNames = [];
|
|
1266
1295
|
const contextCreateFlags = targets
|
|
1267
1296
|
.map((t) => `--${t.name}-endpoint <url>`)
|
|
1268
1297
|
.join(' ');
|
|
1298
|
+
// Context reference
|
|
1299
|
+
commonReferenceNames.push('context');
|
|
1269
1300
|
files.push({
|
|
1270
|
-
fileName:
|
|
1271
|
-
content:
|
|
1272
|
-
|
|
1301
|
+
fileName: `${commonSkillName}/references/context.md`,
|
|
1302
|
+
content: buildSkillReference({
|
|
1303
|
+
title: 'Context Management',
|
|
1273
1304
|
description: `Manage API endpoint contexts for ${toolName} (multi-target: ${targets.map((t) => t.name).join(', ')})`,
|
|
1274
|
-
usage:
|
|
1305
|
+
usage: [
|
|
1306
|
+
`${toolName} ${builtinNames.context} create <name>`,
|
|
1307
|
+
`${toolName} ${builtinNames.context} list`,
|
|
1308
|
+
`${toolName} ${builtinNames.context} use <name>`,
|
|
1309
|
+
`${toolName} ${builtinNames.context} current`,
|
|
1310
|
+
`${toolName} ${builtinNames.context} delete <name>`,
|
|
1311
|
+
],
|
|
1275
1312
|
examples: [
|
|
1276
1313
|
{
|
|
1277
1314
|
description: 'Create a context for local development (accept all defaults)',
|
|
@@ -1287,20 +1324,15 @@ export function generateMultiTargetSkills(input) {
|
|
|
1287
1324
|
`${toolName} ${builtinNames.context} use production`,
|
|
1288
1325
|
],
|
|
1289
1326
|
},
|
|
1290
|
-
{
|
|
1291
|
-
description: 'List and switch contexts',
|
|
1292
|
-
code: [
|
|
1293
|
-
`${toolName} ${builtinNames.context} list`,
|
|
1294
|
-
`${toolName} ${builtinNames.context} use staging`,
|
|
1295
|
-
],
|
|
1296
|
-
},
|
|
1297
1327
|
],
|
|
1298
1328
|
}),
|
|
1299
1329
|
});
|
|
1330
|
+
// Auth reference
|
|
1331
|
+
commonReferenceNames.push('auth');
|
|
1300
1332
|
files.push({
|
|
1301
|
-
fileName:
|
|
1302
|
-
content:
|
|
1303
|
-
|
|
1333
|
+
fileName: `${commonSkillName}/references/auth.md`,
|
|
1334
|
+
content: buildSkillReference({
|
|
1335
|
+
title: 'Authentication',
|
|
1304
1336
|
description: `Manage authentication tokens for ${toolName} (shared across all targets)`,
|
|
1305
1337
|
usage: [
|
|
1306
1338
|
`${toolName} ${builtinNames.auth} set-token <token>`,
|
|
@@ -1319,17 +1351,48 @@ export function generateMultiTargetSkills(input) {
|
|
|
1319
1351
|
],
|
|
1320
1352
|
}),
|
|
1321
1353
|
});
|
|
1354
|
+
// Common SKILL.md
|
|
1355
|
+
files.push({
|
|
1356
|
+
fileName: `${commonSkillName}/SKILL.md`,
|
|
1357
|
+
content: buildSkillFile({
|
|
1358
|
+
name: commonSkillName,
|
|
1359
|
+
description: `Shared CLI utilities for ${toolName} — context management and authentication across targets: ${targets.map((t) => t.name).join(', ')}`,
|
|
1360
|
+
usage: [
|
|
1361
|
+
`# Context management`,
|
|
1362
|
+
`${toolName} ${builtinNames.context} create <name>`,
|
|
1363
|
+
`${toolName} ${builtinNames.context} use <name>`,
|
|
1364
|
+
'',
|
|
1365
|
+
`# Authentication`,
|
|
1366
|
+
`${toolName} ${builtinNames.auth} set-token <token>`,
|
|
1367
|
+
`${toolName} ${builtinNames.auth} status`,
|
|
1368
|
+
],
|
|
1369
|
+
examples: [
|
|
1370
|
+
{
|
|
1371
|
+
description: 'Set up and authenticate',
|
|
1372
|
+
code: [
|
|
1373
|
+
`${toolName} ${builtinNames.context} create local`,
|
|
1374
|
+
`${toolName} ${builtinNames.context} use local`,
|
|
1375
|
+
`${toolName} ${builtinNames.auth} set-token <token>`,
|
|
1376
|
+
],
|
|
1377
|
+
},
|
|
1378
|
+
],
|
|
1379
|
+
}, commonReferenceNames),
|
|
1380
|
+
});
|
|
1381
|
+
// Generate one skill per target with table/op references
|
|
1322
1382
|
for (const tgt of targets) {
|
|
1383
|
+
const tgtSkillName = `cli-${tgt.name}`;
|
|
1384
|
+
const tgtReferenceNames = [];
|
|
1323
1385
|
for (const table of tgt.tables) {
|
|
1324
1386
|
const { singularName } = getTableNames(table);
|
|
1325
1387
|
const kebab = toKebabCase(singularName);
|
|
1326
1388
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
1327
1389
|
const editableFields = getEditableFields(table);
|
|
1328
1390
|
const cmd = `${tgt.name}:${kebab}`;
|
|
1391
|
+
tgtReferenceNames.push(kebab);
|
|
1329
1392
|
files.push({
|
|
1330
|
-
fileName:
|
|
1331
|
-
content:
|
|
1332
|
-
|
|
1393
|
+
fileName: `${tgtSkillName}/references/${kebab}.md`,
|
|
1394
|
+
content: buildSkillReference({
|
|
1395
|
+
title: singularName,
|
|
1333
1396
|
description: `CRUD operations for ${table.name} records via ${toolName} CLI (${tgt.name} target)`,
|
|
1334
1397
|
usage: [
|
|
1335
1398
|
`${toolName} ${cmd} list`,
|
|
@@ -1349,10 +1412,6 @@ export function generateMultiTargetSkills(input) {
|
|
|
1349
1412
|
`${toolName} ${cmd} create ${editableFields.map((f) => `--${f.name} "value"`).join(' ')}`,
|
|
1350
1413
|
],
|
|
1351
1414
|
},
|
|
1352
|
-
{
|
|
1353
|
-
description: `Get a ${singularName} by ${pk.name}`,
|
|
1354
|
-
code: [`${toolName} ${cmd} get --${pk.name} <value>`],
|
|
1355
|
-
},
|
|
1356
1415
|
],
|
|
1357
1416
|
}),
|
|
1358
1417
|
});
|
|
@@ -1367,10 +1426,11 @@ export function generateMultiTargetSkills(input) {
|
|
|
1367
1426
|
if (tgt.isAuthTarget && op.kind === 'mutation') {
|
|
1368
1427
|
usageLines.push(`${baseUsage} --save-token`);
|
|
1369
1428
|
}
|
|
1429
|
+
tgtReferenceNames.push(kebab);
|
|
1370
1430
|
files.push({
|
|
1371
|
-
fileName:
|
|
1372
|
-
content:
|
|
1373
|
-
|
|
1431
|
+
fileName: `${tgtSkillName}/references/${kebab}.md`,
|
|
1432
|
+
content: buildSkillReference({
|
|
1433
|
+
title: op.name,
|
|
1374
1434
|
description: `${op.description || `Execute the ${op.name} ${op.kind}`} (${tgt.name} target)`,
|
|
1375
1435
|
usage: usageLines,
|
|
1376
1436
|
examples: [
|
|
@@ -1382,6 +1442,29 @@ export function generateMultiTargetSkills(input) {
|
|
|
1382
1442
|
}),
|
|
1383
1443
|
});
|
|
1384
1444
|
}
|
|
1445
|
+
// Target SKILL.md
|
|
1446
|
+
const firstKebab = tgt.tables.length > 0
|
|
1447
|
+
? toKebabCase(getTableNames(tgt.tables[0]).singularName)
|
|
1448
|
+
: 'model';
|
|
1449
|
+
files.push({
|
|
1450
|
+
fileName: `${tgtSkillName}/SKILL.md`,
|
|
1451
|
+
content: buildSkillFile({
|
|
1452
|
+
name: tgtSkillName,
|
|
1453
|
+
description: `CLI commands for the ${tgt.name} API target — ${tgt.tables.length} tables and ${tgt.customOperations.length} custom operations via ${toolName}`,
|
|
1454
|
+
usage: [
|
|
1455
|
+
`# CRUD for ${tgt.name} tables (e.g. ${firstKebab})`,
|
|
1456
|
+
`${toolName} ${tgt.name}:${firstKebab} list`,
|
|
1457
|
+
`${toolName} ${tgt.name}:${firstKebab} get --id <value>`,
|
|
1458
|
+
`${toolName} ${tgt.name}:${firstKebab} create --<field> <value>`,
|
|
1459
|
+
],
|
|
1460
|
+
examples: [
|
|
1461
|
+
{
|
|
1462
|
+
description: `Query ${tgt.name} records`,
|
|
1463
|
+
code: [`${toolName} ${tgt.name}:${firstKebab} list`],
|
|
1464
|
+
},
|
|
1465
|
+
],
|
|
1466
|
+
}, tgtReferenceNames),
|
|
1467
|
+
});
|
|
1385
1468
|
}
|
|
1386
1469
|
return files;
|
|
1387
1470
|
}
|
|
@@ -49,9 +49,16 @@ export function generateExecutorFile(toolName, options) {
|
|
|
49
49
|
])),
|
|
50
50
|
])),
|
|
51
51
|
])),
|
|
52
|
-
|
|
53
|
-
t.
|
|
54
|
-
|
|
52
|
+
(() => {
|
|
53
|
+
const headersId = t.identifier('headers');
|
|
54
|
+
headersId.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
55
|
+
t.tsStringKeyword(),
|
|
56
|
+
t.tsStringKeyword(),
|
|
57
|
+
])));
|
|
58
|
+
return t.variableDeclaration('const', [
|
|
59
|
+
t.variableDeclarator(headersId, t.objectExpression([])),
|
|
60
|
+
]);
|
|
61
|
+
})(),
|
|
55
62
|
t.ifStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('hasValidCredentials')), [t.memberExpression(t.identifier('ctx'), t.identifier('name'))]), t.blockStatement([
|
|
56
63
|
t.variableDeclaration('const', [
|
|
57
64
|
t.variableDeclarator(t.identifier('creds'), t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('getCredentials')), [t.memberExpression(t.identifier('ctx'), t.identifier('name'))])),
|
|
@@ -156,9 +163,16 @@ export function generateMultiTargetExecutorFile(toolName, targets, options) {
|
|
|
156
163
|
], [t.identifier('targetName')]),
|
|
157
164
|
])),
|
|
158
165
|
])),
|
|
159
|
-
|
|
160
|
-
t.
|
|
161
|
-
|
|
166
|
+
(() => {
|
|
167
|
+
const headersId = t.identifier('headers');
|
|
168
|
+
headersId.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
169
|
+
t.tsStringKeyword(),
|
|
170
|
+
t.tsStringKeyword(),
|
|
171
|
+
])));
|
|
172
|
+
return t.variableDeclaration('const', [
|
|
173
|
+
t.variableDeclarator(headersId, t.objectExpression([])),
|
|
174
|
+
]);
|
|
175
|
+
})(),
|
|
162
176
|
t.variableDeclaration('let', [
|
|
163
177
|
t.variableDeclarator(t.identifier('endpoint'), t.stringLiteral('')),
|
|
164
178
|
]),
|
|
@@ -96,7 +96,7 @@ Create Options:
|
|
|
96
96
|
]))),
|
|
97
97
|
]),
|
|
98
98
|
t.returnStatement(t.callExpression(t.identifier('handleSubcommand'), [
|
|
99
|
-
t.memberExpression(t.identifier('answer'), t.identifier('subcommand')),
|
|
99
|
+
t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('subcommand')), t.tsStringKeyword()),
|
|
100
100
|
t.identifier('newArgv'),
|
|
101
101
|
t.identifier('prompter'),
|
|
102
102
|
t.identifier('store'),
|
|
@@ -185,7 +185,7 @@ function buildCreateHandler() {
|
|
|
185
185
|
])),
|
|
186
186
|
]),
|
|
187
187
|
t.variableDeclaration('const', [
|
|
188
|
-
t.variableDeclarator(t.identifier('answers'), t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('prompt')), [
|
|
188
|
+
t.variableDeclarator(t.identifier('answers'), t.tsAsExpression(t.tsAsExpression(t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('prompt')), [
|
|
189
189
|
t.objectExpression([
|
|
190
190
|
t.objectProperty(t.identifier('name'), t.identifier('name'), false, true),
|
|
191
191
|
t.spreadElement(t.identifier('restArgv')),
|
|
@@ -204,7 +204,10 @@ function buildCreateHandler() {
|
|
|
204
204
|
t.objectProperty(t.identifier('required'), t.booleanLiteral(true)),
|
|
205
205
|
]),
|
|
206
206
|
]),
|
|
207
|
-
]))),
|
|
207
|
+
])), t.tsUnknownKeyword()), t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
|
|
208
|
+
t.tsStringKeyword(),
|
|
209
|
+
t.tsStringKeyword(),
|
|
210
|
+
])))),
|
|
208
211
|
]),
|
|
209
212
|
t.variableDeclaration('const', [
|
|
210
213
|
t.variableDeclarator(t.identifier('contextName'), t.memberExpression(t.identifier('answers'), t.identifier('name'))),
|
|
@@ -341,7 +344,7 @@ function buildUseHandler() {
|
|
|
341
344
|
]),
|
|
342
345
|
]))),
|
|
343
346
|
]),
|
|
344
|
-
t.expressionStatement(t.assignmentExpression('=', t.identifier('contextName'), t.memberExpression(t.identifier('answer'), t.identifier('name')))),
|
|
347
|
+
t.expressionStatement(t.assignmentExpression('=', t.identifier('contextName'), t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('name')), t.tsStringKeyword()))),
|
|
345
348
|
])),
|
|
346
349
|
t.ifStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('setCurrentContext')), [t.identifier('contextName')]), t.blockStatement([
|
|
347
350
|
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [
|
|
@@ -457,7 +460,7 @@ function buildDeleteHandler() {
|
|
|
457
460
|
]),
|
|
458
461
|
]))),
|
|
459
462
|
]),
|
|
460
|
-
t.expressionStatement(t.assignmentExpression('=', t.identifier('contextName'), t.memberExpression(t.identifier('answer'), t.identifier('name')))),
|
|
463
|
+
t.expressionStatement(t.assignmentExpression('=', t.identifier('contextName'), t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('name')), t.tsStringKeyword()))),
|
|
461
464
|
])),
|
|
462
465
|
t.ifStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('deleteContext')), [t.identifier('contextName')]), t.blockStatement([
|
|
463
466
|
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [
|
|
@@ -549,7 +552,7 @@ Options:
|
|
|
549
552
|
]))),
|
|
550
553
|
]),
|
|
551
554
|
t.returnStatement(t.callExpression(t.identifier('handleAuthSubcommand'), [
|
|
552
|
-
t.memberExpression(t.identifier('answer'), t.identifier('subcommand')),
|
|
555
|
+
t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('subcommand')), t.tsStringKeyword()),
|
|
553
556
|
t.identifier('newArgv'),
|
|
554
557
|
t.identifier('prompter'),
|
|
555
558
|
t.identifier('store'),
|
|
@@ -651,7 +654,7 @@ function buildSetTokenHandler() {
|
|
|
651
654
|
]),
|
|
652
655
|
]))),
|
|
653
656
|
]),
|
|
654
|
-
t.expressionStatement(t.assignmentExpression('=', t.identifier('tokenValue'), t.memberExpression(t.identifier('answer'), t.identifier('token')))),
|
|
657
|
+
t.expressionStatement(t.assignmentExpression('=', t.identifier('tokenValue'), t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('token')), t.tsStringKeyword()))),
|
|
655
658
|
])),
|
|
656
659
|
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('setCredentials')), [
|
|
657
660
|
t.memberExpression(t.identifier('current'), t.identifier('name')),
|
|
@@ -770,7 +773,7 @@ function buildLogoutHandler() {
|
|
|
770
773
|
]),
|
|
771
774
|
]))),
|
|
772
775
|
]),
|
|
773
|
-
t.ifStatement(t.unaryExpression('!', t.memberExpression(t.identifier('confirm'), t.identifier('confirm'))), t.blockStatement([t.returnStatement()])),
|
|
776
|
+
t.ifStatement(t.unaryExpression('!', t.tsAsExpression(t.memberExpression(t.identifier('confirm'), t.identifier('confirm')), t.tsBooleanKeyword())), t.blockStatement([t.returnStatement()])),
|
|
774
777
|
t.ifStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('removeCredentials')), [
|
|
775
778
|
t.memberExpression(t.identifier('current'), t.identifier('name')),
|
|
776
779
|
]), t.blockStatement([
|
|
@@ -870,7 +873,7 @@ ${targets.map((tgt) => ` --${tgt.name}-endpoint <url> ${tgt.name} endpoint (de
|
|
|
870
873
|
]))),
|
|
871
874
|
]),
|
|
872
875
|
t.returnStatement(t.callExpression(t.identifier('handleSubcommand'), [
|
|
873
|
-
t.memberExpression(t.identifier('answer'), t.identifier('subcommand')),
|
|
876
|
+
t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('subcommand')), t.tsStringKeyword()),
|
|
874
877
|
t.identifier('newArgv'),
|
|
875
878
|
t.identifier('prompter'),
|
|
876
879
|
t.identifier('store'),
|
|
@@ -969,7 +972,7 @@ function buildMultiTargetCreateHandler(targets) {
|
|
|
969
972
|
const targetsObjProps = targets.map((target) => {
|
|
970
973
|
const fieldName = `${target.name}Endpoint`;
|
|
971
974
|
return t.objectProperty(t.stringLiteral(target.name), t.objectExpression([
|
|
972
|
-
t.objectProperty(t.identifier('endpoint'), t.memberExpression(t.identifier('answers'), t.identifier(fieldName))),
|
|
975
|
+
t.objectProperty(t.identifier('endpoint'), t.tsAsExpression(t.memberExpression(t.identifier('answers'), t.identifier(fieldName)), t.tsStringKeyword())),
|
|
973
976
|
]));
|
|
974
977
|
});
|
|
975
978
|
const body = [
|
|
@@ -991,7 +994,7 @@ function buildMultiTargetCreateHandler(targets) {
|
|
|
991
994
|
]))),
|
|
992
995
|
]),
|
|
993
996
|
t.variableDeclaration('const', [
|
|
994
|
-
t.variableDeclarator(t.identifier('contextName'), t.memberExpression(t.identifier('answers'), t.identifier('name'))),
|
|
997
|
+
t.variableDeclarator(t.identifier('contextName'), t.tsAsExpression(t.memberExpression(t.identifier('answers'), t.identifier('name')), t.tsStringKeyword())),
|
|
995
998
|
]),
|
|
996
999
|
t.variableDeclaration('const', [
|
|
997
1000
|
t.variableDeclarator(t.identifier('targets'), t.objectExpression(targetsObjProps)),
|
|
@@ -999,7 +1002,7 @@ function buildMultiTargetCreateHandler(targets) {
|
|
|
999
1002
|
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('createContext')), [
|
|
1000
1003
|
t.identifier('contextName'),
|
|
1001
1004
|
t.objectExpression([
|
|
1002
|
-
t.objectProperty(t.identifier('endpoint'), t.memberExpression(t.identifier('answers'), t.identifier(`${targets[0].name}Endpoint`))),
|
|
1005
|
+
t.objectProperty(t.identifier('endpoint'), t.tsAsExpression(t.memberExpression(t.identifier('answers'), t.identifier(`${targets[0].name}Endpoint`)), t.tsStringKeyword())),
|
|
1003
1006
|
t.objectProperty(t.identifier('targets'), t.identifier('targets')),
|
|
1004
1007
|
]),
|
|
1005
1008
|
])),
|
|
@@ -1022,7 +1025,7 @@ function buildMultiTargetCreateHandler(targets) {
|
|
|
1022
1025
|
t.templateLiteral([
|
|
1023
1026
|
t.templateElement({ raw: ` ${target.name}: `, cooked: ` ${target.name}: ` }),
|
|
1024
1027
|
t.templateElement({ raw: '', cooked: '' }, true),
|
|
1025
|
-
], [t.memberExpression(t.identifier('answers'), t.identifier(fieldName))]),
|
|
1028
|
+
], [t.tsAsExpression(t.memberExpression(t.identifier('answers'), t.identifier(fieldName)), t.tsStringKeyword())]),
|
|
1026
1029
|
])));
|
|
1027
1030
|
}
|
|
1028
1031
|
const func = t.functionDeclaration(t.identifier('handleCreate'), [argvParam, prompterParam, storeParam], t.blockStatement(body), false, true);
|
|
@@ -1098,7 +1101,7 @@ Options:
|
|
|
1098
1101
|
]))),
|
|
1099
1102
|
]),
|
|
1100
1103
|
t.returnStatement(t.callExpression(t.identifier('handleAuthSubcommand'), [
|
|
1101
|
-
t.memberExpression(t.identifier('answer'), t.identifier('subcommand')),
|
|
1104
|
+
t.tsAsExpression(t.memberExpression(t.identifier('answer'), t.identifier('subcommand')), t.tsStringKeyword()),
|
|
1102
1105
|
t.identifier('newArgv'),
|
|
1103
1106
|
t.identifier('prompter'),
|
|
1104
1107
|
t.identifier('store'),
|