@constructive-io/graphql-codegen 4.8.1 → 4.12.2
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/arg-mapper.d.ts +1 -1
- package/core/codegen/cli/arg-mapper.js +15 -11
- package/core/codegen/cli/custom-command-generator.js +4 -3
- package/core/codegen/cli/docs-generator.d.ts +6 -5
- package/core/codegen/cli/docs-generator.js +167 -64
- package/core/codegen/cli/table-command-generator.d.ts +15 -0
- package/core/codegen/cli/table-command-generator.js +29 -3
- package/core/codegen/docs-utils.d.ts +26 -1
- package/core/codegen/docs-utils.js +105 -0
- package/core/codegen/orm/input-types-generator.js +112 -14
- package/core/codegen/orm/model-generator.js +8 -0
- package/core/codegen/orm/select-types.d.ts +4 -2
- package/core/codegen/scalars.js +8 -0
- package/core/codegen/templates/cli-entry.ts +2 -2
- package/core/codegen/templates/cli-utils.ts +28 -0
- package/core/codegen/templates/query-builder.ts +28 -5
- package/core/codegen/templates/select-types.ts +4 -2
- package/core/generate.js +14 -4
- package/esm/core/codegen/cli/arg-mapper.d.ts +1 -1
- package/esm/core/codegen/cli/arg-mapper.js +15 -11
- package/esm/core/codegen/cli/custom-command-generator.js +4 -3
- package/esm/core/codegen/cli/docs-generator.d.ts +6 -5
- package/esm/core/codegen/cli/docs-generator.js +168 -65
- package/esm/core/codegen/cli/table-command-generator.d.ts +15 -0
- package/esm/core/codegen/cli/table-command-generator.js +29 -5
- package/esm/core/codegen/docs-utils.d.ts +26 -1
- package/esm/core/codegen/docs-utils.js +102 -0
- package/esm/core/codegen/orm/input-types-generator.js +112 -14
- package/esm/core/codegen/orm/model-generator.js +8 -0
- package/esm/core/codegen/orm/select-types.d.ts +4 -2
- package/esm/core/codegen/scalars.js +8 -0
- package/esm/core/generate.js +14 -4
- package/package.json +11 -11
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { toKebabCase } from 'komoji';
|
|
2
|
-
import {
|
|
2
|
+
import { flattenArgs, flattenedArgsToFlags, cleanTypeName, getEditableFields, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, buildSkillFile, buildSkillReference, } from '../docs-utils';
|
|
3
3
|
import { getScalarFields, getTableNames, getPrimaryKeyInfo, } from '../utils';
|
|
4
|
+
import { getFieldsWithDefaults } from './table-command-generator';
|
|
4
5
|
export { resolveDocsConfig } from '../docs-utils';
|
|
5
|
-
export function generateReadme(tables, customOperations, toolName) {
|
|
6
|
+
export function generateReadme(tables, customOperations, toolName, registry) {
|
|
6
7
|
const lines = [];
|
|
7
8
|
lines.push(...getReadmeHeader(`${toolName} CLI`));
|
|
8
9
|
lines.push('## Setup');
|
|
@@ -86,10 +87,21 @@ export function generateReadme(tables, customOperations, toolName) {
|
|
|
86
87
|
lines.push('| Field | Type |');
|
|
87
88
|
lines.push('|-------|------|');
|
|
88
89
|
for (const f of scalarFields) {
|
|
89
|
-
lines.push(`| \`${f.name}\` | ${f.type.gqlType} |`);
|
|
90
|
+
lines.push(`| \`${f.name}\` | ${cleanTypeName(f.type.gqlType)} |`);
|
|
90
91
|
}
|
|
91
92
|
lines.push('');
|
|
92
|
-
|
|
93
|
+
const defaultFields = getFieldsWithDefaults(table, registry);
|
|
94
|
+
const requiredCreate = editableFields.filter((f) => !defaultFields.has(f.name));
|
|
95
|
+
const optionalCreate = editableFields.filter((f) => defaultFields.has(f.name));
|
|
96
|
+
if (requiredCreate.length > 0) {
|
|
97
|
+
lines.push(`**Required create fields:** ${requiredCreate.map((f) => `\`${f.name}\``).join(', ')}`);
|
|
98
|
+
}
|
|
99
|
+
if (optionalCreate.length > 0) {
|
|
100
|
+
lines.push(`**Optional create fields (backend defaults):** ${optionalCreate.map((f) => `\`${f.name}\``).join(', ')}`);
|
|
101
|
+
}
|
|
102
|
+
if (requiredCreate.length === 0 && optionalCreate.length === 0) {
|
|
103
|
+
lines.push(`**Create fields:** ${editableFields.map((f) => `\`${f.name}\``).join(', ')}`);
|
|
104
|
+
}
|
|
93
105
|
lines.push('');
|
|
94
106
|
}
|
|
95
107
|
}
|
|
@@ -98,18 +110,20 @@ export function generateReadme(tables, customOperations, toolName) {
|
|
|
98
110
|
lines.push('');
|
|
99
111
|
for (const op of customOperations) {
|
|
100
112
|
const kebab = toKebabCase(op.name);
|
|
113
|
+
const flat = flattenArgs(op.args, registry);
|
|
101
114
|
lines.push(`### \`${kebab}\``);
|
|
102
115
|
lines.push('');
|
|
103
116
|
lines.push(op.description || op.name);
|
|
104
117
|
lines.push('');
|
|
105
118
|
lines.push(`- **Type:** ${op.kind}`);
|
|
106
|
-
if (
|
|
119
|
+
if (flat.length > 0) {
|
|
107
120
|
lines.push('- **Arguments:**');
|
|
108
121
|
lines.push('');
|
|
109
122
|
lines.push(' | Argument | Type |');
|
|
110
123
|
lines.push(' |----------|------|');
|
|
111
|
-
for (const
|
|
112
|
-
|
|
124
|
+
for (const a of flat) {
|
|
125
|
+
const reqLabel = a.required ? ' (required)' : '';
|
|
126
|
+
lines.push(` | \`--${a.flag}\` | ${a.type}${reqLabel} |`);
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
129
|
else {
|
|
@@ -127,13 +141,21 @@ export function generateReadme(tables, customOperations, toolName) {
|
|
|
127
141
|
lines.push(`${toolName} car get --id <uuid> | jq '.'`);
|
|
128
142
|
lines.push('```');
|
|
129
143
|
lines.push('');
|
|
144
|
+
lines.push('## Non-Interactive Mode');
|
|
145
|
+
lines.push('');
|
|
146
|
+
lines.push('Use `--no-tty` to skip all interactive prompts (useful for scripts and CI):');
|
|
147
|
+
lines.push('');
|
|
148
|
+
lines.push('```bash');
|
|
149
|
+
lines.push(`${toolName} --no-tty car create --name "Sedan" --year 2024`);
|
|
150
|
+
lines.push('```');
|
|
151
|
+
lines.push('');
|
|
130
152
|
lines.push(...getReadmeFooter());
|
|
131
153
|
return {
|
|
132
154
|
fileName: 'README.md',
|
|
133
155
|
content: lines.join('\n'),
|
|
134
156
|
};
|
|
135
157
|
}
|
|
136
|
-
export function generateAgentsDocs(tables, customOperations, toolName) {
|
|
158
|
+
export function generateAgentsDocs(tables, customOperations, toolName, registry) {
|
|
137
159
|
const lines = [];
|
|
138
160
|
lines.push(`# ${toolName} CLI - Agent Reference`);
|
|
139
161
|
lines.push('');
|
|
@@ -205,6 +227,13 @@ export function generateAgentsDocs(tables, customOperations, toolName) {
|
|
|
205
227
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
206
228
|
const scalarFields = getScalarFields(table);
|
|
207
229
|
const editableFields = getEditableFields(table);
|
|
230
|
+
const defaultFields = getFieldsWithDefaults(table, registry);
|
|
231
|
+
const requiredCreateFields = editableFields.filter((f) => !defaultFields.has(f.name));
|
|
232
|
+
const optionalCreateFields = editableFields.filter((f) => defaultFields.has(f.name));
|
|
233
|
+
const createFlags = [
|
|
234
|
+
...requiredCreateFields.map((f) => `--${f.name} <value>`),
|
|
235
|
+
...optionalCreateFields.map((f) => `[--${f.name} <value>]`),
|
|
236
|
+
].join(' ');
|
|
208
237
|
lines.push(`### TOOL: ${kebab}`);
|
|
209
238
|
lines.push('');
|
|
210
239
|
lines.push(`CRUD operations for ${table.name} records.`);
|
|
@@ -213,19 +242,20 @@ export function generateAgentsDocs(tables, customOperations, toolName) {
|
|
|
213
242
|
lines.push('SUBCOMMANDS:');
|
|
214
243
|
lines.push(` ${toolName} ${kebab} list List all records`);
|
|
215
244
|
lines.push(` ${toolName} ${kebab} get --${pk.name} <value> Get one record`);
|
|
216
|
-
lines.push(` ${toolName} ${kebab} create ${
|
|
245
|
+
lines.push(` ${toolName} ${kebab} create ${createFlags}`);
|
|
217
246
|
lines.push(` ${toolName} ${kebab} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`);
|
|
218
247
|
lines.push(` ${toolName} ${kebab} delete --${pk.name} <value> Delete one record`);
|
|
219
248
|
lines.push('');
|
|
220
249
|
lines.push('INPUT FIELDS:');
|
|
221
250
|
for (const f of scalarFields) {
|
|
222
251
|
const isPk = f.name === pk.name;
|
|
223
|
-
lines.push(` ${f.name}: ${f.type.gqlType}${isPk ? ' (primary key)' : ''}`);
|
|
252
|
+
lines.push(` ${f.name}: ${cleanTypeName(f.type.gqlType)}${isPk ? ' (primary key)' : ''}`);
|
|
224
253
|
}
|
|
225
254
|
lines.push('');
|
|
226
255
|
lines.push('EDITABLE FIELDS (for create/update):');
|
|
227
256
|
for (const f of editableFields) {
|
|
228
|
-
|
|
257
|
+
const optLabel = defaultFields.has(f.name) ? ' (optional, has backend default)' : '';
|
|
258
|
+
lines.push(` ${f.name}: ${cleanTypeName(f.type.gqlType)}${optLabel}`);
|
|
229
259
|
}
|
|
230
260
|
lines.push('');
|
|
231
261
|
lines.push('OUTPUT: JSON');
|
|
@@ -239,19 +269,21 @@ export function generateAgentsDocs(tables, customOperations, toolName) {
|
|
|
239
269
|
}
|
|
240
270
|
for (const op of customOperations) {
|
|
241
271
|
const kebab = toKebabCase(op.name);
|
|
272
|
+
const flat = flattenArgs(op.args, registry);
|
|
242
273
|
lines.push(`### TOOL: ${kebab}`);
|
|
243
274
|
lines.push('');
|
|
244
275
|
lines.push(op.description || op.name);
|
|
245
276
|
lines.push('');
|
|
246
277
|
lines.push('```');
|
|
247
278
|
lines.push(`TYPE: ${op.kind}`);
|
|
248
|
-
if (
|
|
249
|
-
const flags =
|
|
279
|
+
if (flat.length > 0) {
|
|
280
|
+
const flags = flattenedArgsToFlags(flat);
|
|
250
281
|
lines.push(`USAGE: ${toolName} ${kebab} ${flags}`);
|
|
251
282
|
lines.push('');
|
|
252
283
|
lines.push('INPUT:');
|
|
253
|
-
for (const
|
|
254
|
-
|
|
284
|
+
for (const a of flat) {
|
|
285
|
+
const reqLabel = a.required ? ' (required)' : '';
|
|
286
|
+
lines.push(` ${a.flag}: ${a.type}${reqLabel}`);
|
|
255
287
|
}
|
|
256
288
|
}
|
|
257
289
|
else {
|
|
@@ -329,7 +361,7 @@ export function generateAgentsDocs(tables, customOperations, toolName) {
|
|
|
329
361
|
content: lines.join('\n'),
|
|
330
362
|
};
|
|
331
363
|
}
|
|
332
|
-
export function getCliMcpTools(tables, customOperations, toolName) {
|
|
364
|
+
export function getCliMcpTools(tables, customOperations, toolName, registry) {
|
|
333
365
|
const tools = [];
|
|
334
366
|
tools.push({
|
|
335
367
|
name: `${toolName}_context_create`,
|
|
@@ -402,6 +434,10 @@ export function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
402
434
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
403
435
|
const scalarFields = getScalarFields(table);
|
|
404
436
|
const editableFields = getEditableFields(table);
|
|
437
|
+
const defaultFields = getFieldsWithDefaults(table, registry);
|
|
438
|
+
const requiredCreateFieldNames = editableFields
|
|
439
|
+
.filter((f) => !defaultFields.has(f.name))
|
|
440
|
+
.map((f) => f.name);
|
|
405
441
|
tools.push({
|
|
406
442
|
name: `${toolName}_${kebab}_list`,
|
|
407
443
|
description: `List all ${table.name} records`,
|
|
@@ -424,7 +460,7 @@ export function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
424
460
|
const createProps = {};
|
|
425
461
|
for (const f of editableFields) {
|
|
426
462
|
createProps[f.name] = {
|
|
427
|
-
type: gqlTypeToJsonSchemaType(f.type.gqlType),
|
|
463
|
+
type: gqlTypeToJsonSchemaType(cleanTypeName(f.type.gqlType)),
|
|
428
464
|
description: `${table.name} ${f.name}`,
|
|
429
465
|
};
|
|
430
466
|
}
|
|
@@ -434,7 +470,7 @@ export function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
434
470
|
inputSchema: {
|
|
435
471
|
type: 'object',
|
|
436
472
|
properties: createProps,
|
|
437
|
-
required:
|
|
473
|
+
...(requiredCreateFieldNames.length > 0 ? { required: requiredCreateFieldNames } : {}),
|
|
438
474
|
},
|
|
439
475
|
});
|
|
440
476
|
const updateProps = {
|
|
@@ -445,7 +481,7 @@ export function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
445
481
|
};
|
|
446
482
|
for (const f of editableFields) {
|
|
447
483
|
updateProps[f.name] = {
|
|
448
|
-
type: gqlTypeToJsonSchemaType(f.type.gqlType),
|
|
484
|
+
type: gqlTypeToJsonSchemaType(cleanTypeName(f.type.gqlType)),
|
|
449
485
|
description: `${table.name} ${f.name}`,
|
|
450
486
|
};
|
|
451
487
|
}
|
|
@@ -479,7 +515,7 @@ export function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
479
515
|
_meta: {
|
|
480
516
|
fields: scalarFields.map((f) => ({
|
|
481
517
|
name: f.name,
|
|
482
|
-
type: f.type.gqlType,
|
|
518
|
+
type: cleanTypeName(f.type.gqlType),
|
|
483
519
|
editable: editableFields.some((ef) => ef.name === f.name),
|
|
484
520
|
primaryKey: f.name === pk.name,
|
|
485
521
|
})),
|
|
@@ -488,17 +524,16 @@ export function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
488
524
|
}
|
|
489
525
|
for (const op of customOperations) {
|
|
490
526
|
const kebab = toKebabCase(op.name);
|
|
527
|
+
const flat = flattenArgs(op.args, registry);
|
|
491
528
|
const props = {};
|
|
492
529
|
const required = [];
|
|
493
|
-
for (const
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
type: gqlTypeToJsonSchemaType(baseType.name ?? 'String'),
|
|
498
|
-
description: arg.description || arg.name,
|
|
530
|
+
for (const a of flat) {
|
|
531
|
+
props[a.flag] = {
|
|
532
|
+
type: gqlTypeToJsonSchemaType(a.type),
|
|
533
|
+
description: a.description || a.flag,
|
|
499
534
|
};
|
|
500
|
-
if (
|
|
501
|
-
required.push(
|
|
535
|
+
if (a.required) {
|
|
536
|
+
required.push(a.flag);
|
|
502
537
|
}
|
|
503
538
|
}
|
|
504
539
|
tools.push({
|
|
@@ -513,7 +548,7 @@ export function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
513
548
|
}
|
|
514
549
|
return tools;
|
|
515
550
|
}
|
|
516
|
-
export function generateSkills(tables, customOperations, toolName, targetName) {
|
|
551
|
+
export function generateSkills(tables, customOperations, toolName, targetName, registry) {
|
|
517
552
|
const files = [];
|
|
518
553
|
const skillName = `cli-${targetName}`;
|
|
519
554
|
const referenceNames = [];
|
|
@@ -576,6 +611,11 @@ export function generateSkills(tables, customOperations, toolName, targetName) {
|
|
|
576
611
|
const kebab = toKebabCase(singularName);
|
|
577
612
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
578
613
|
const editableFields = getEditableFields(table);
|
|
614
|
+
const defaultFields = getFieldsWithDefaults(table, registry);
|
|
615
|
+
const createFlags = [
|
|
616
|
+
...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <value>`),
|
|
617
|
+
...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <value>]`),
|
|
618
|
+
].join(' ');
|
|
579
619
|
referenceNames.push(kebab);
|
|
580
620
|
files.push({
|
|
581
621
|
fileName: `${skillName}/references/${kebab}.md`,
|
|
@@ -585,7 +625,7 @@ export function generateSkills(tables, customOperations, toolName, targetName) {
|
|
|
585
625
|
usage: [
|
|
586
626
|
`${toolName} ${kebab} list`,
|
|
587
627
|
`${toolName} ${kebab} get --${pk.name} <value>`,
|
|
588
|
-
`${toolName} ${kebab} create ${
|
|
628
|
+
`${toolName} ${kebab} create ${createFlags}`,
|
|
589
629
|
`${toolName} ${kebab} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`,
|
|
590
630
|
`${toolName} ${kebab} delete --${pk.name} <value>`,
|
|
591
631
|
],
|
|
@@ -597,7 +637,7 @@ export function generateSkills(tables, customOperations, toolName, targetName) {
|
|
|
597
637
|
{
|
|
598
638
|
description: `Create a ${singularName}`,
|
|
599
639
|
code: [
|
|
600
|
-
`${toolName} ${kebab} create ${
|
|
640
|
+
`${toolName} ${kebab} create ${createFlags}`,
|
|
601
641
|
],
|
|
602
642
|
},
|
|
603
643
|
{
|
|
@@ -611,8 +651,9 @@ export function generateSkills(tables, customOperations, toolName, targetName) {
|
|
|
611
651
|
// Custom operation references
|
|
612
652
|
for (const op of customOperations) {
|
|
613
653
|
const kebab = toKebabCase(op.name);
|
|
614
|
-
const
|
|
615
|
-
|
|
654
|
+
const flat = flattenArgs(op.args, registry);
|
|
655
|
+
const usage = flat.length > 0
|
|
656
|
+
? `${toolName} ${kebab} ${flattenedArgsToFlags(flat)}`
|
|
616
657
|
: `${toolName} ${kebab}`;
|
|
617
658
|
referenceNames.push(kebab);
|
|
618
659
|
files.push({
|
|
@@ -649,6 +690,9 @@ export function generateSkills(tables, customOperations, toolName, targetName) {
|
|
|
649
690
|
`${toolName} ${tableKebabs[0] || 'model'} list`,
|
|
650
691
|
`${toolName} ${tableKebabs[0] || 'model'} get --id <value>`,
|
|
651
692
|
`${toolName} ${tableKebabs[0] || 'model'} create --<field> <value>`,
|
|
693
|
+
'',
|
|
694
|
+
`# Non-interactive mode (skip all prompts, use flags only)`,
|
|
695
|
+
`${toolName} --no-tty ${tableKebabs[0] || 'model'} list`,
|
|
652
696
|
],
|
|
653
697
|
examples: [
|
|
654
698
|
{
|
|
@@ -660,13 +704,19 @@ export function generateSkills(tables, customOperations, toolName, targetName) {
|
|
|
660
704
|
`${toolName} ${tableKebabs[0] || 'model'} list`,
|
|
661
705
|
],
|
|
662
706
|
},
|
|
707
|
+
{
|
|
708
|
+
description: 'Non-interactive mode (for scripts and CI)',
|
|
709
|
+
code: [
|
|
710
|
+
`${toolName} --no-tty ${tableKebabs[0] || 'model'} create --<field> <value>`,
|
|
711
|
+
],
|
|
712
|
+
},
|
|
663
713
|
],
|
|
664
714
|
}, referenceNames),
|
|
665
715
|
});
|
|
666
716
|
return files;
|
|
667
717
|
}
|
|
668
718
|
export function generateMultiTargetReadme(input) {
|
|
669
|
-
const { toolName, builtinNames, targets } = input;
|
|
719
|
+
const { toolName, builtinNames, targets, registry } = input;
|
|
670
720
|
const lines = [];
|
|
671
721
|
lines.push(...getReadmeHeader(`${toolName} CLI`));
|
|
672
722
|
lines.push('## Setup');
|
|
@@ -791,6 +841,7 @@ export function generateMultiTargetReadme(input) {
|
|
|
791
841
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
792
842
|
const scalarFields = getScalarFields(table);
|
|
793
843
|
const editableFields = getEditableFields(table);
|
|
844
|
+
const defaultFields = getFieldsWithDefaults(table, registry);
|
|
794
845
|
lines.push(`### \`${tgt.name}:${kebab}\``);
|
|
795
846
|
lines.push('');
|
|
796
847
|
lines.push(`CRUD operations for ${table.name} records.`);
|
|
@@ -808,26 +859,38 @@ export function generateMultiTargetReadme(input) {
|
|
|
808
859
|
lines.push('| Field | Type |');
|
|
809
860
|
lines.push('|-------|------|');
|
|
810
861
|
for (const f of scalarFields) {
|
|
811
|
-
lines.push(`| \`${f.name}\` | ${f.type.gqlType} |`);
|
|
862
|
+
lines.push(`| \`${f.name}\` | ${cleanTypeName(f.type.gqlType)} |`);
|
|
812
863
|
}
|
|
813
864
|
lines.push('');
|
|
814
|
-
|
|
865
|
+
const requiredCreate = editableFields.filter((f) => !defaultFields.has(f.name));
|
|
866
|
+
const optionalCreate = editableFields.filter((f) => defaultFields.has(f.name));
|
|
867
|
+
if (requiredCreate.length > 0) {
|
|
868
|
+
lines.push(`**Required create fields:** ${requiredCreate.map((f) => `\`${f.name}\``).join(', ')}`);
|
|
869
|
+
}
|
|
870
|
+
if (optionalCreate.length > 0) {
|
|
871
|
+
lines.push(`**Optional create fields (backend defaults):** ${optionalCreate.map((f) => `\`${f.name}\``).join(', ')}`);
|
|
872
|
+
}
|
|
873
|
+
if (requiredCreate.length === 0 && optionalCreate.length === 0) {
|
|
874
|
+
lines.push(`**Create fields:** ${editableFields.map((f) => `\`${f.name}\``).join(', ')}`);
|
|
875
|
+
}
|
|
815
876
|
lines.push('');
|
|
816
877
|
}
|
|
817
878
|
for (const op of tgt.customOperations) {
|
|
818
879
|
const kebab = toKebabCase(op.name);
|
|
880
|
+
const flat = flattenArgs(op.args, registry);
|
|
819
881
|
lines.push(`### \`${tgt.name}:${kebab}\``);
|
|
820
882
|
lines.push('');
|
|
821
883
|
lines.push(op.description || op.name);
|
|
822
884
|
lines.push('');
|
|
823
885
|
lines.push(`- **Type:** ${op.kind}`);
|
|
824
|
-
if (
|
|
886
|
+
if (flat.length > 0) {
|
|
825
887
|
lines.push('- **Arguments:**');
|
|
826
888
|
lines.push('');
|
|
827
889
|
lines.push(' | Argument | Type |');
|
|
828
890
|
lines.push(' |----------|------|');
|
|
829
|
-
for (const
|
|
830
|
-
|
|
891
|
+
for (const a of flat) {
|
|
892
|
+
const reqLabel = a.required ? ' (required)' : '';
|
|
893
|
+
lines.push(` | \`--${a.flag}\` | ${a.type}${reqLabel} |`);
|
|
831
894
|
}
|
|
832
895
|
}
|
|
833
896
|
else {
|
|
@@ -852,6 +915,18 @@ export function generateMultiTargetReadme(input) {
|
|
|
852
915
|
}
|
|
853
916
|
lines.push('```');
|
|
854
917
|
lines.push('');
|
|
918
|
+
lines.push('## Non-Interactive Mode');
|
|
919
|
+
lines.push('');
|
|
920
|
+
lines.push('Use `--no-tty` to skip all interactive prompts (useful for scripts and CI):');
|
|
921
|
+
lines.push('');
|
|
922
|
+
lines.push('```bash');
|
|
923
|
+
if (targets.length > 0 && targets[0].tables.length > 0) {
|
|
924
|
+
const tgt = targets[0];
|
|
925
|
+
const kebab = toKebabCase(getTableNames(tgt.tables[0]).singularName);
|
|
926
|
+
lines.push(`${toolName} --no-tty ${tgt.name}:${kebab} create --name "Example"`);
|
|
927
|
+
}
|
|
928
|
+
lines.push('```');
|
|
929
|
+
lines.push('');
|
|
855
930
|
lines.push(...getReadmeFooter());
|
|
856
931
|
return {
|
|
857
932
|
fileName: 'README.md',
|
|
@@ -859,7 +934,7 @@ export function generateMultiTargetReadme(input) {
|
|
|
859
934
|
};
|
|
860
935
|
}
|
|
861
936
|
export function generateMultiTargetAgentsDocs(input) {
|
|
862
|
-
const { toolName, builtinNames, targets } = input;
|
|
937
|
+
const { toolName, builtinNames, targets, registry } = input;
|
|
863
938
|
const lines = [];
|
|
864
939
|
lines.push(`# ${toolName} CLI - Agent Reference`);
|
|
865
940
|
lines.push('');
|
|
@@ -952,6 +1027,13 @@ export function generateMultiTargetAgentsDocs(input) {
|
|
|
952
1027
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
953
1028
|
const scalarFields = getScalarFields(table);
|
|
954
1029
|
const editableFields = getEditableFields(table);
|
|
1030
|
+
const defaultFields = getFieldsWithDefaults(table, registry);
|
|
1031
|
+
const requiredCreateFields = editableFields.filter((f) => !defaultFields.has(f.name));
|
|
1032
|
+
const optionalCreateFields = editableFields.filter((f) => defaultFields.has(f.name));
|
|
1033
|
+
const createFlags = [
|
|
1034
|
+
...requiredCreateFields.map((f) => `--${f.name} <value>`),
|
|
1035
|
+
...optionalCreateFields.map((f) => `[--${f.name} <value>]`),
|
|
1036
|
+
].join(' ');
|
|
955
1037
|
lines.push(`### TOOL: ${tgt.name}:${kebab}`);
|
|
956
1038
|
lines.push('');
|
|
957
1039
|
lines.push(`CRUD operations for ${table.name} records (${tgt.name} target).`);
|
|
@@ -960,19 +1042,20 @@ export function generateMultiTargetAgentsDocs(input) {
|
|
|
960
1042
|
lines.push('SUBCOMMANDS:');
|
|
961
1043
|
lines.push(` ${toolName} ${tgt.name}:${kebab} list List all records`);
|
|
962
1044
|
lines.push(` ${toolName} ${tgt.name}:${kebab} get --${pk.name} <value> Get one record`);
|
|
963
|
-
lines.push(` ${toolName} ${tgt.name}:${kebab} create ${
|
|
1045
|
+
lines.push(` ${toolName} ${tgt.name}:${kebab} create ${createFlags}`);
|
|
964
1046
|
lines.push(` ${toolName} ${tgt.name}:${kebab} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`);
|
|
965
1047
|
lines.push(` ${toolName} ${tgt.name}:${kebab} delete --${pk.name} <value> Delete one record`);
|
|
966
1048
|
lines.push('');
|
|
967
1049
|
lines.push('INPUT FIELDS:');
|
|
968
1050
|
for (const f of scalarFields) {
|
|
969
1051
|
const isPk = f.name === pk.name;
|
|
970
|
-
lines.push(` ${f.name}: ${f.type.gqlType}${isPk ? ' (primary key)' : ''}`);
|
|
1052
|
+
lines.push(` ${f.name}: ${cleanTypeName(f.type.gqlType)}${isPk ? ' (primary key)' : ''}`);
|
|
971
1053
|
}
|
|
972
1054
|
lines.push('');
|
|
973
1055
|
lines.push('EDITABLE FIELDS (for create/update):');
|
|
974
1056
|
for (const f of editableFields) {
|
|
975
|
-
|
|
1057
|
+
const optLabel = defaultFields.has(f.name) ? ' (optional, has backend default)' : '';
|
|
1058
|
+
lines.push(` ${f.name}: ${cleanTypeName(f.type.gqlType)}${optLabel}`);
|
|
976
1059
|
}
|
|
977
1060
|
lines.push('');
|
|
978
1061
|
lines.push('OUTPUT: JSON');
|
|
@@ -986,19 +1069,21 @@ export function generateMultiTargetAgentsDocs(input) {
|
|
|
986
1069
|
}
|
|
987
1070
|
for (const op of tgt.customOperations) {
|
|
988
1071
|
const kebab = toKebabCase(op.name);
|
|
1072
|
+
const flat = flattenArgs(op.args, registry);
|
|
989
1073
|
lines.push(`### TOOL: ${tgt.name}:${kebab}`);
|
|
990
1074
|
lines.push('');
|
|
991
1075
|
lines.push(op.description || op.name);
|
|
992
1076
|
lines.push('');
|
|
993
1077
|
lines.push('```');
|
|
994
1078
|
lines.push(`TYPE: ${op.kind}`);
|
|
995
|
-
if (
|
|
996
|
-
const flags =
|
|
1079
|
+
if (flat.length > 0) {
|
|
1080
|
+
const flags = flattenedArgsToFlags(flat);
|
|
997
1081
|
lines.push(`USAGE: ${toolName} ${tgt.name}:${kebab} ${flags}`);
|
|
998
1082
|
lines.push('');
|
|
999
1083
|
lines.push('INPUT:');
|
|
1000
|
-
for (const
|
|
1001
|
-
|
|
1084
|
+
for (const a of flat) {
|
|
1085
|
+
const reqLabel = a.required ? ' (required)' : '';
|
|
1086
|
+
lines.push(` ${a.flag}: ${a.type}${reqLabel}`);
|
|
1002
1087
|
}
|
|
1003
1088
|
}
|
|
1004
1089
|
else {
|
|
@@ -1087,7 +1172,7 @@ export function generateMultiTargetAgentsDocs(input) {
|
|
|
1087
1172
|
};
|
|
1088
1173
|
}
|
|
1089
1174
|
export function getMultiTargetCliMcpTools(input) {
|
|
1090
|
-
const { toolName, builtinNames, targets } = input;
|
|
1175
|
+
const { toolName, builtinNames, targets, registry } = input;
|
|
1091
1176
|
const tools = [];
|
|
1092
1177
|
const contextEndpointProps = {
|
|
1093
1178
|
name: { type: 'string', description: 'Context name' },
|
|
@@ -1167,6 +1252,10 @@ export function getMultiTargetCliMcpTools(input) {
|
|
|
1167
1252
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
1168
1253
|
const scalarFields = getScalarFields(table);
|
|
1169
1254
|
const editableFields = getEditableFields(table);
|
|
1255
|
+
const defaultFields = getFieldsWithDefaults(table, registry);
|
|
1256
|
+
const requiredCreateFieldNames = editableFields
|
|
1257
|
+
.filter((f) => !defaultFields.has(f.name))
|
|
1258
|
+
.map((f) => f.name);
|
|
1170
1259
|
const prefix = `${toolName}_${tgt.name}_${kebab}`;
|
|
1171
1260
|
tools.push({
|
|
1172
1261
|
name: `${prefix}_list`,
|
|
@@ -1190,7 +1279,7 @@ export function getMultiTargetCliMcpTools(input) {
|
|
|
1190
1279
|
const createProps = {};
|
|
1191
1280
|
for (const f of editableFields) {
|
|
1192
1281
|
createProps[f.name] = {
|
|
1193
|
-
type: gqlTypeToJsonSchemaType(f.type.gqlType),
|
|
1282
|
+
type: gqlTypeToJsonSchemaType(cleanTypeName(f.type.gqlType)),
|
|
1194
1283
|
description: `${table.name} ${f.name}`,
|
|
1195
1284
|
};
|
|
1196
1285
|
}
|
|
@@ -1200,7 +1289,7 @@ export function getMultiTargetCliMcpTools(input) {
|
|
|
1200
1289
|
inputSchema: {
|
|
1201
1290
|
type: 'object',
|
|
1202
1291
|
properties: createProps,
|
|
1203
|
-
required:
|
|
1292
|
+
...(requiredCreateFieldNames.length > 0 ? { required: requiredCreateFieldNames } : {}),
|
|
1204
1293
|
},
|
|
1205
1294
|
});
|
|
1206
1295
|
const updateProps = {
|
|
@@ -1211,7 +1300,7 @@ export function getMultiTargetCliMcpTools(input) {
|
|
|
1211
1300
|
};
|
|
1212
1301
|
for (const f of editableFields) {
|
|
1213
1302
|
updateProps[f.name] = {
|
|
1214
|
-
type: gqlTypeToJsonSchemaType(f.type.gqlType),
|
|
1303
|
+
type: gqlTypeToJsonSchemaType(cleanTypeName(f.type.gqlType)),
|
|
1215
1304
|
description: `${table.name} ${f.name}`,
|
|
1216
1305
|
};
|
|
1217
1306
|
}
|
|
@@ -1245,7 +1334,7 @@ export function getMultiTargetCliMcpTools(input) {
|
|
|
1245
1334
|
_meta: {
|
|
1246
1335
|
fields: scalarFields.map((f) => ({
|
|
1247
1336
|
name: f.name,
|
|
1248
|
-
type: f.type.gqlType,
|
|
1337
|
+
type: cleanTypeName(f.type.gqlType),
|
|
1249
1338
|
editable: editableFields.some((ef) => ef.name === f.name),
|
|
1250
1339
|
primaryKey: f.name === pk.name,
|
|
1251
1340
|
})),
|
|
@@ -1254,17 +1343,16 @@ export function getMultiTargetCliMcpTools(input) {
|
|
|
1254
1343
|
}
|
|
1255
1344
|
for (const op of tgt.customOperations) {
|
|
1256
1345
|
const kebab = toKebabCase(op.name);
|
|
1346
|
+
const flat = flattenArgs(op.args, registry);
|
|
1257
1347
|
const props = {};
|
|
1258
1348
|
const required = [];
|
|
1259
|
-
for (const
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
type: gqlTypeToJsonSchemaType(baseType.name ?? 'String'),
|
|
1264
|
-
description: arg.description || arg.name,
|
|
1349
|
+
for (const a of flat) {
|
|
1350
|
+
props[a.flag] = {
|
|
1351
|
+
type: gqlTypeToJsonSchemaType(a.type),
|
|
1352
|
+
description: a.description || a.flag,
|
|
1265
1353
|
};
|
|
1266
|
-
if (
|
|
1267
|
-
required.push(
|
|
1354
|
+
if (a.required) {
|
|
1355
|
+
required.push(a.flag);
|
|
1268
1356
|
}
|
|
1269
1357
|
}
|
|
1270
1358
|
if (tgt.isAuthTarget && op.kind === 'mutation') {
|
|
@@ -1287,7 +1375,7 @@ export function getMultiTargetCliMcpTools(input) {
|
|
|
1287
1375
|
return tools;
|
|
1288
1376
|
}
|
|
1289
1377
|
export function generateMultiTargetSkills(input) {
|
|
1290
|
-
const { toolName, builtinNames, targets } = input;
|
|
1378
|
+
const { toolName, builtinNames, targets, registry } = input;
|
|
1291
1379
|
const files = [];
|
|
1292
1380
|
// Generate one skill per target, plus a shared cli-common skill for context/auth
|
|
1293
1381
|
const commonSkillName = 'cli-common';
|
|
@@ -1387,6 +1475,11 @@ export function generateMultiTargetSkills(input) {
|
|
|
1387
1475
|
const kebab = toKebabCase(singularName);
|
|
1388
1476
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
1389
1477
|
const editableFields = getEditableFields(table);
|
|
1478
|
+
const defaultFields = getFieldsWithDefaults(table, registry);
|
|
1479
|
+
const createFlags = [
|
|
1480
|
+
...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <value>`),
|
|
1481
|
+
...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <value>]`),
|
|
1482
|
+
].join(' ');
|
|
1390
1483
|
const cmd = `${tgt.name}:${kebab}`;
|
|
1391
1484
|
tgtReferenceNames.push(kebab);
|
|
1392
1485
|
files.push({
|
|
@@ -1397,7 +1490,7 @@ export function generateMultiTargetSkills(input) {
|
|
|
1397
1490
|
usage: [
|
|
1398
1491
|
`${toolName} ${cmd} list`,
|
|
1399
1492
|
`${toolName} ${cmd} get --${pk.name} <value>`,
|
|
1400
|
-
`${toolName} ${cmd} create ${
|
|
1493
|
+
`${toolName} ${cmd} create ${createFlags}`,
|
|
1401
1494
|
`${toolName} ${cmd} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`,
|
|
1402
1495
|
`${toolName} ${cmd} delete --${pk.name} <value>`,
|
|
1403
1496
|
],
|
|
@@ -1409,7 +1502,7 @@ export function generateMultiTargetSkills(input) {
|
|
|
1409
1502
|
{
|
|
1410
1503
|
description: `Create a ${singularName}`,
|
|
1411
1504
|
code: [
|
|
1412
|
-
`${toolName} ${cmd} create ${
|
|
1505
|
+
`${toolName} ${cmd} create ${createFlags}`,
|
|
1413
1506
|
],
|
|
1414
1507
|
},
|
|
1415
1508
|
],
|
|
@@ -1419,8 +1512,9 @@ export function generateMultiTargetSkills(input) {
|
|
|
1419
1512
|
for (const op of tgt.customOperations) {
|
|
1420
1513
|
const kebab = toKebabCase(op.name);
|
|
1421
1514
|
const cmd = `${tgt.name}:${kebab}`;
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1515
|
+
const flat = flattenArgs(op.args, registry);
|
|
1516
|
+
const baseUsage = flat.length > 0
|
|
1517
|
+
? `${toolName} ${cmd} ${flattenedArgsToFlags(flat)}`
|
|
1424
1518
|
: `${toolName} ${cmd}`;
|
|
1425
1519
|
const usageLines = [baseUsage];
|
|
1426
1520
|
if (tgt.isAuthTarget && op.kind === 'mutation') {
|
|
@@ -1456,12 +1550,21 @@ export function generateMultiTargetSkills(input) {
|
|
|
1456
1550
|
`${toolName} ${tgt.name}:${firstKebab} list`,
|
|
1457
1551
|
`${toolName} ${tgt.name}:${firstKebab} get --id <value>`,
|
|
1458
1552
|
`${toolName} ${tgt.name}:${firstKebab} create --<field> <value>`,
|
|
1553
|
+
'',
|
|
1554
|
+
`# Non-interactive mode (skip all prompts, use flags only)`,
|
|
1555
|
+
`${toolName} --no-tty ${tgt.name}:${firstKebab} list`,
|
|
1459
1556
|
],
|
|
1460
1557
|
examples: [
|
|
1461
1558
|
{
|
|
1462
1559
|
description: `Query ${tgt.name} records`,
|
|
1463
1560
|
code: [`${toolName} ${tgt.name}:${firstKebab} list`],
|
|
1464
1561
|
},
|
|
1562
|
+
{
|
|
1563
|
+
description: 'Non-interactive mode (for scripts and CI)',
|
|
1564
|
+
code: [
|
|
1565
|
+
`${toolName} --no-tty ${tgt.name}:${firstKebab} create --<field> <value>`,
|
|
1566
|
+
],
|
|
1567
|
+
},
|
|
1465
1568
|
],
|
|
1466
1569
|
}, tgtReferenceNames),
|
|
1467
1570
|
});
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import type { CleanTable, TypeRegistry } from '../../../types/schema';
|
|
2
2
|
import type { GeneratedFile } from './executor-generator';
|
|
3
|
+
/**
|
|
4
|
+
* Get the set of field names that have defaults in the create input type.
|
|
5
|
+
* Looks up the CreateXInput -> inner input type (e.g. DatabaseInput) in the
|
|
6
|
+
* TypeRegistry and checks each field's defaultValue from introspection.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the inner input type from a CreateXInput or UpdateXInput type.
|
|
10
|
+
* The CreateXInput has an inner field (e.g. "database" of type DatabaseInput)
|
|
11
|
+
* that contains the actual field definitions.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveInnerInputType(inputTypeName: string, typeRegistry: TypeRegistry): {
|
|
14
|
+
name: string;
|
|
15
|
+
fields: Set<string>;
|
|
16
|
+
} | null;
|
|
17
|
+
export declare function getFieldsWithDefaults(table: CleanTable, typeRegistry?: TypeRegistry): Set<string>;
|
|
3
18
|
export interface TableCommandOptions {
|
|
4
19
|
targetName?: string;
|
|
5
20
|
executorImportPath?: string;
|