@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.
Files changed (37) hide show
  1. package/core/codegen/cli/command-map-generator.js +36 -11
  2. package/core/codegen/cli/custom-command-generator.js +59 -13
  3. package/core/codegen/cli/docs-generator.d.ts +1 -1
  4. package/core/codegen/cli/docs-generator.js +137 -54
  5. package/core/codegen/cli/executor-generator.js +20 -6
  6. package/core/codegen/cli/infra-generator.js +17 -14
  7. package/core/codegen/cli/table-command-generator.js +209 -53
  8. package/core/codegen/docs-utils.d.ts +12 -1
  9. package/core/codegen/docs-utils.js +49 -1
  10. package/core/codegen/hooks-docs-generator.d.ts +1 -1
  11. package/core/codegen/hooks-docs-generator.js +47 -7
  12. package/core/codegen/orm/docs-generator.d.ts +1 -1
  13. package/core/codegen/orm/docs-generator.js +57 -20
  14. package/core/generate.d.ts +5 -0
  15. package/core/generate.js +48 -12
  16. package/core/workspace.d.ts +13 -0
  17. package/core/workspace.js +92 -0
  18. package/esm/core/codegen/cli/command-map-generator.js +36 -11
  19. package/esm/core/codegen/cli/custom-command-generator.js +60 -14
  20. package/esm/core/codegen/cli/docs-generator.d.ts +1 -1
  21. package/esm/core/codegen/cli/docs-generator.js +138 -55
  22. package/esm/core/codegen/cli/executor-generator.js +20 -6
  23. package/esm/core/codegen/cli/infra-generator.js +17 -14
  24. package/esm/core/codegen/cli/table-command-generator.js +210 -54
  25. package/esm/core/codegen/docs-utils.d.ts +12 -1
  26. package/esm/core/codegen/docs-utils.js +48 -1
  27. package/esm/core/codegen/hooks-docs-generator.d.ts +1 -1
  28. package/esm/core/codegen/hooks-docs-generator.js +48 -8
  29. package/esm/core/codegen/orm/docs-generator.d.ts +1 -1
  30. package/esm/core/codegen/orm/docs-generator.js +58 -21
  31. package/esm/core/generate.d.ts +5 -0
  32. package/esm/core/generate.js +48 -12
  33. package/esm/core/workspace.d.ts +13 -0
  34. package/esm/core/workspace.js +89 -0
  35. package/esm/types/config.d.ts +12 -4
  36. package/package.json +22 -22
  37. 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
- const createCommandMapFunc = t.variableDeclaration('const', [
78
- t.variableDeclarator(t.identifier('createCommandMap'), t.arrowFunctionExpression([], t.objectExpression(mapProperties))),
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
- t.tsFunctionType(null, [], t.tsTypeAnnotation(t.tsUnknownKeyword())),
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(t.identifier('createCommandMap'), t.arrowFunctionExpression([], t.objectExpression(mapProperties))),
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(t.objectExpression([
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(t.objectExpression([
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: 'skills/context.md',
532
- content: (0, docs_utils_1.buildSkillFile)({
533
- name: `${toolName}-context`,
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: 'skills/auth.md',
559
- content: (0, docs_utils_1.buildSkillFile)({
560
- name: `${toolName}-auth`,
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: `skills/${kebab}.md`,
586
- content: (0, docs_utils_1.buildSkillFile)({
587
- name: `${toolName}-${kebab}`,
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: `skills/${kebab}.md`,
632
- content: (0, docs_utils_1.buildSkillFile)({
633
- name: `${toolName}-${kebab}`,
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
- const contextUsage = [
1272
- `${toolName} ${builtinNames.context} create <name>`,
1273
- `${toolName} ${builtinNames.context} list`,
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: `skills/${builtinNames.context}.md`,
1283
- content: (0, docs_utils_1.buildSkillFile)({
1284
- name: `${toolName}-${builtinNames.context}`,
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: contextUsage,
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: `skills/${builtinNames.auth}.md`,
1314
- content: (0, docs_utils_1.buildSkillFile)({
1315
- name: `${toolName}-${builtinNames.auth}`,
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: `skills/${tgt.name}-${kebab}.md`,
1343
- content: (0, docs_utils_1.buildSkillFile)({
1344
- name: `${toolName}-${cmd}`,
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: `skills/${tgt.name}-${kebab}.md`,
1384
- content: (0, docs_utils_1.buildSkillFile)({
1385
- name: `${toolName}-${cmd}`,
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
- t.variableDeclaration('const', [
90
- t.variableDeclarator(t.identifier('headers'), t.objectExpression([])),
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
- t.variableDeclaration('const', [
197
- t.variableDeclarator(t.identifier('headers'), t.objectExpression([])),
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
  ]),