@constructive-io/graphql-codegen 4.14.4 → 4.15.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.
@@ -1,5 +1,5 @@
1
1
  import { toKebabCase } from 'komoji';
2
- import { flattenArgs, flattenedArgsToFlags, cleanTypeName, getEditableFields, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, buildSkillFile, buildSkillReference, } from '../docs-utils';
2
+ import { flattenArgs, flattenedArgsToFlags, cleanTypeName, getEditableFields, categorizeSpecialFields, buildSpecialFieldsMarkdown, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, buildSkillFile, buildSkillReference, } from '../docs-utils';
3
3
  import { getScalarFields, getTableNames, getPrimaryKeyInfo, } from '../utils';
4
4
  import { getFieldsWithDefaults } from './table-command-generator';
5
5
  export { resolveDocsConfig } from '../docs-utils';
@@ -83,7 +83,7 @@ export function generateReadme(tables, customOperations, toolName, registry) {
83
83
  const kebab = toKebabCase(singularName);
84
84
  const pk = getPrimaryKeyInfo(table)[0];
85
85
  const scalarFields = getScalarFields(table);
86
- const editableFields = getEditableFields(table);
86
+ const editableFields = getEditableFields(table, registry);
87
87
  lines.push(`### \`${kebab}\``);
88
88
  lines.push('');
89
89
  lines.push(`CRUD operations for ${table.name} records.`);
@@ -116,6 +116,8 @@ export function generateReadme(tables, customOperations, toolName, registry) {
116
116
  if (requiredCreate.length === 0 && optionalCreate.length === 0) {
117
117
  lines.push(`**Create fields:** ${editableFields.map((f) => `\`${f.name}\``).join(', ')}`);
118
118
  }
119
+ const specialGroups = categorizeSpecialFields(table, registry);
120
+ lines.push(...buildSpecialFieldsMarkdown(specialGroups));
119
121
  lines.push('');
120
122
  }
121
123
  }
@@ -169,228 +171,42 @@ export function generateReadme(tables, customOperations, toolName, registry) {
169
171
  content: lines.join('\n'),
170
172
  };
171
173
  }
172
- export function generateAgentsDocs(tables, customOperations, toolName, registry) {
174
+ export function generateAgentsDocs(tables, customOperations, toolName, _registry) {
173
175
  const lines = [];
174
- lines.push(`# ${toolName} CLI - Agent Reference`);
176
+ const tableCount = tables.length;
177
+ const customOpCount = customOperations.length;
178
+ lines.push(`# ${toolName} CLI`);
175
179
  lines.push('');
176
180
  lines.push('<!-- @constructive-io/graphql-codegen - DO NOT EDIT -->');
177
- lines.push('> This document is structured for LLM/agent consumption.');
178
181
  lines.push('');
179
- lines.push('## OVERVIEW');
182
+ lines.push('## Stack');
180
183
  lines.push('');
181
- lines.push(`\`${toolName}\` is a CLI tool for interacting with a GraphQL API.`);
182
- lines.push('All commands output JSON to stdout. All commands accept `--help` or `-h` for usage.');
183
- lines.push(`Configuration is stored at \`~/.${toolName}/config/\` via appstash.`);
184
+ lines.push(`- Generated CLI for a GraphQL API (TypeScript)`);
185
+ lines.push(`- ${tableCount} table${tableCount !== 1 ? 's' : ''}${customOpCount > 0 ? `, ${customOpCount} custom operation${customOpCount !== 1 ? 's' : ''}` : ''}`);
186
+ lines.push(`- Config stored at \`~/.${toolName}/config/\` via appstash`);
184
187
  lines.push('');
185
- lines.push('## PREREQUISITES');
186
- lines.push('');
187
- lines.push('Before running any data commands, you must:');
188
- lines.push('');
189
- lines.push(`1. Create a context: \`${toolName} context create <name> --endpoint <url>\``);
190
- lines.push(`2. Activate it: \`${toolName} context use <name>\``);
191
- lines.push(`3. Authenticate: \`${toolName} auth set-token <token>\``);
192
- lines.push('');
193
- lines.push('## TOOLS');
194
- lines.push('');
195
- lines.push('### TOOL: context');
196
- lines.push('');
197
- lines.push('Manage named API endpoint contexts (like kubectl contexts).');
198
- lines.push('');
199
- lines.push('```');
200
- lines.push('SUBCOMMANDS:');
201
- lines.push(` ${toolName} context create <name> --endpoint <url> Create a new context`);
202
- lines.push(` ${toolName} context list List all contexts`);
203
- lines.push(` ${toolName} context use <name> Set active context`);
204
- lines.push(` ${toolName} context current Show active context`);
205
- lines.push(` ${toolName} context delete <name> Delete a context`);
206
- lines.push('');
207
- lines.push('INPUT:');
208
- lines.push(' name: string (required) - Context identifier');
209
- lines.push(' endpoint: string (required for create) - GraphQL endpoint URL');
210
- lines.push('');
211
- lines.push('OUTPUT: JSON');
212
- lines.push(' create: { name, endpoint }');
213
- lines.push(' list: [{ name, endpoint, isCurrent, hasCredentials }]');
214
- lines.push(' use: { name, endpoint }');
215
- lines.push(' current: { name, endpoint }');
216
- lines.push(' delete: { deleted: name }');
217
- lines.push('```');
218
- lines.push('');
219
- lines.push('### TOOL: auth');
220
- lines.push('');
221
- lines.push('Manage authentication tokens per context.');
222
- lines.push('');
223
- lines.push('```');
224
- lines.push('SUBCOMMANDS:');
225
- lines.push(` ${toolName} auth set-token <token> Store bearer token for current context`);
226
- lines.push(` ${toolName} auth status Show auth status for all contexts`);
227
- lines.push(` ${toolName} auth logout Remove credentials for current context`);
228
- lines.push('');
229
- lines.push('INPUT:');
230
- lines.push(' token: string (required for set-token) - Bearer token value');
231
- lines.push('');
232
- lines.push('OUTPUT: JSON');
233
- lines.push(' set-token: { context, status: "authenticated" }');
234
- lines.push(' status: [{ context, authenticated: boolean }]');
235
- lines.push(' logout: { context, status: "logged out" }');
236
- lines.push('```');
237
- lines.push('');
238
- lines.push('### TOOL: config');
239
- lines.push('');
240
- lines.push('Manage per-context key-value configuration variables.');
241
- lines.push('');
242
- lines.push('```');
243
- lines.push('SUBCOMMANDS:');
244
- lines.push(` ${toolName} config get <key> Get a config value`);
245
- lines.push(` ${toolName} config set <key> <value> Set a config value`);
246
- lines.push(` ${toolName} config list List all config values`);
247
- lines.push(` ${toolName} config delete <key> Delete a config value`);
248
- lines.push('');
249
- lines.push('INPUT:');
250
- lines.push(' key: string (required for get/set/delete) - Variable name');
251
- lines.push(' value: string (required for set) - Variable value');
252
- lines.push('');
253
- lines.push('OUTPUT: JSON');
254
- lines.push(' get: { key, value }');
255
- lines.push(' set: { key, value }');
256
- lines.push(' list: { vars: { key: value, ... } }');
257
- lines.push(' delete: { deleted: key }');
258
- lines.push('```');
259
- lines.push('');
260
- for (const table of tables) {
261
- const { singularName } = getTableNames(table);
262
- const kebab = toKebabCase(singularName);
263
- const pk = getPrimaryKeyInfo(table)[0];
264
- const scalarFields = getScalarFields(table);
265
- const editableFields = getEditableFields(table);
266
- const defaultFields = getFieldsWithDefaults(table, registry);
267
- const requiredCreateFields = editableFields.filter((f) => !defaultFields.has(f.name));
268
- const optionalCreateFields = editableFields.filter((f) => defaultFields.has(f.name));
269
- const createFlags = [
270
- ...requiredCreateFields.map((f) => `--${f.name} <value>`),
271
- ...optionalCreateFields.map((f) => `[--${f.name} <value>]`),
272
- ].join(' ');
273
- lines.push(`### TOOL: ${kebab}`);
274
- lines.push('');
275
- lines.push(`CRUD operations for ${table.name} records.`);
276
- lines.push('');
277
- lines.push('```');
278
- lines.push('SUBCOMMANDS:');
279
- lines.push(` ${toolName} ${kebab} list List all records`);
280
- lines.push(` ${toolName} ${kebab} get --${pk.name} <value> Get one record`);
281
- lines.push(` ${toolName} ${kebab} create ${createFlags}`);
282
- lines.push(` ${toolName} ${kebab} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`);
283
- lines.push(` ${toolName} ${kebab} delete --${pk.name} <value> Delete one record`);
284
- lines.push('');
285
- lines.push('INPUT FIELDS:');
286
- for (const f of scalarFields) {
287
- const isPk = f.name === pk.name;
288
- lines.push(` ${f.name}: ${cleanTypeName(f.type.gqlType)}${isPk ? ' (primary key)' : ''}`);
289
- }
290
- lines.push('');
291
- lines.push('EDITABLE FIELDS (for create/update):');
292
- for (const f of editableFields) {
293
- const optLabel = defaultFields.has(f.name) ? ' (optional, has backend default)' : '';
294
- lines.push(` ${f.name}: ${cleanTypeName(f.type.gqlType)}${optLabel}`);
295
- }
296
- lines.push('');
297
- lines.push('OUTPUT: JSON');
298
- lines.push(` list: [{ ${scalarFields.map((f) => f.name).join(', ')} }]`);
299
- lines.push(` get: { ${scalarFields.map((f) => f.name).join(', ')} }`);
300
- lines.push(` create: { ${scalarFields.map((f) => f.name).join(', ')} }`);
301
- lines.push(` update: { ${scalarFields.map((f) => f.name).join(', ')} }`);
302
- lines.push(` delete: { ${pk.name} }`);
303
- lines.push('```');
304
- lines.push('');
305
- }
306
- for (const op of customOperations) {
307
- const kebab = toKebabCase(op.name);
308
- const flat = flattenArgs(op.args, registry);
309
- lines.push(`### TOOL: ${kebab}`);
310
- lines.push('');
311
- lines.push(op.description || op.name);
312
- lines.push('');
313
- lines.push('```');
314
- lines.push(`TYPE: ${op.kind}`);
315
- if (flat.length > 0) {
316
- const flags = flattenedArgsToFlags(flat);
317
- lines.push(`USAGE: ${toolName} ${kebab} ${flags}`);
318
- lines.push('');
319
- lines.push('INPUT:');
320
- for (const a of flat) {
321
- const reqLabel = a.required ? ' (required)' : '';
322
- lines.push(` ${a.flag}: ${a.type}${reqLabel}`);
323
- }
324
- }
325
- else {
326
- lines.push(`USAGE: ${toolName} ${kebab}`);
327
- lines.push('');
328
- lines.push('INPUT: none');
329
- }
330
- lines.push('');
331
- lines.push('OUTPUT: JSON');
332
- lines.push('```');
333
- lines.push('');
334
- }
335
- lines.push('## WORKFLOWS');
336
- lines.push('');
337
- lines.push('### Initial setup');
188
+ lines.push('## Quick Start');
338
189
  lines.push('');
339
190
  lines.push('```bash');
340
- lines.push(`${toolName} context create dev --endpoint http://localhost:5000/graphql`);
191
+ lines.push(`${toolName} context create dev --endpoint <url>`);
341
192
  lines.push(`${toolName} context use dev`);
342
- lines.push(`${toolName} auth set-token eyJhbGciOiJIUzI1NiIs...`);
193
+ lines.push(`${toolName} auth set-token <token>`);
343
194
  lines.push('```');
344
195
  lines.push('');
345
- if (tables.length > 0) {
346
- const firstTable = tables[0];
347
- const { singularName } = getTableNames(firstTable);
348
- const kebab = toKebabCase(singularName);
349
- const editableFields = getEditableFields(firstTable);
350
- const pk = getPrimaryKeyInfo(firstTable)[0];
351
- lines.push(`### CRUD workflow (${kebab})`);
352
- lines.push('');
353
- lines.push('```bash');
354
- lines.push(`# List all`);
355
- lines.push(`${toolName} ${kebab} list`);
356
- lines.push('');
357
- lines.push(`# Create`);
358
- lines.push(`${toolName} ${kebab} create ${editableFields.map((f) => `--${f.name} "value"`).join(' ')}`);
359
- lines.push('');
360
- lines.push(`# Get by ${pk.name}`);
361
- lines.push(`${toolName} ${kebab} get --${pk.name} <value>`);
362
- lines.push('');
363
- lines.push(`# Update`);
364
- lines.push(`${toolName} ${kebab} update --${pk.name} <value> --${editableFields[0]?.name || 'field'} "new-value"`);
365
- lines.push('');
366
- lines.push(`# Delete`);
367
- lines.push(`${toolName} ${kebab} delete --${pk.name} <value>`);
368
- lines.push('```');
369
- lines.push('');
370
- }
371
- lines.push('### Piping output');
196
+ lines.push('## Resources');
372
197
  lines.push('');
373
- lines.push('```bash');
374
- lines.push(`# Pretty print`);
375
- lines.push(`${toolName} car list | jq '.'`);
376
- lines.push('');
377
- lines.push(`# Extract field`);
378
- lines.push(`${toolName} car list | jq '.[].id'`);
198
+ lines.push(`- **Full API reference:** [README.md](./README.md) — CRUD docs for all ${tableCount} tables`);
199
+ lines.push('- **Schema types:** [types.ts](./types.ts)');
379
200
  lines.push('');
380
- lines.push(`# Count results`);
381
- lines.push(`${toolName} car list | jq 'length'`);
382
- lines.push('```');
201
+ lines.push('## Conventions');
383
202
  lines.push('');
384
- lines.push('## ERROR HANDLING');
203
+ lines.push('- All commands output JSON to stdout');
204
+ lines.push('- Use `--help` on any command for usage');
205
+ lines.push('- Exit 0 = success, 1 = error');
385
206
  lines.push('');
386
- lines.push('All errors are written to stderr. Exit codes:');
387
- lines.push('- `0`: Success');
388
- lines.push('- `1`: Error (auth failure, not found, validation error, network error)');
207
+ lines.push('## Boundaries');
389
208
  lines.push('');
390
- lines.push('Common errors:');
391
- lines.push('- "No active context": Run `context use <name>` first');
392
- lines.push('- "Not authenticated": Run `auth set-token <token>` first');
393
- lines.push('- "Record not found": The requested ID does not exist');
209
+ lines.push('All files in this directory are generated. Do not edit manually.');
394
210
  lines.push('');
395
211
  return {
396
212
  fileName: 'AGENTS.md',
@@ -508,7 +324,7 @@ export function getCliMcpTools(tables, customOperations, toolName, registry) {
508
324
  const kebab = toKebabCase(singularName);
509
325
  const pk = getPrimaryKeyInfo(table)[0];
510
326
  const scalarFields = getScalarFields(table);
511
- const editableFields = getEditableFields(table);
327
+ const editableFields = getEditableFields(table, registry);
512
328
  const defaultFields = getFieldsWithDefaults(table, registry);
513
329
  const requiredCreateFieldNames = editableFields
514
330
  .filter((f) => !defaultFields.has(f.name))
@@ -713,18 +529,23 @@ export function generateSkills(tables, customOperations, toolName, targetName, r
713
529
  const { singularName } = getTableNames(table);
714
530
  const kebab = toKebabCase(singularName);
715
531
  const pk = getPrimaryKeyInfo(table)[0];
716
- const editableFields = getEditableFields(table);
532
+ const editableFields = getEditableFields(table, registry);
717
533
  const defaultFields = getFieldsWithDefaults(table, registry);
718
534
  const createFlags = [
719
535
  ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <value>`),
720
536
  ...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <value>]`),
721
537
  ].join(' ');
722
538
  referenceNames.push(kebab);
539
+ const skillSpecialGroups = categorizeSpecialFields(table, registry);
540
+ const skillSpecialDesc = skillSpecialGroups.length > 0
541
+ ? `CRUD operations for ${table.name} records via ${toolName} CLI\n\n` +
542
+ skillSpecialGroups.map((g) => `**${g.label}:** ${g.fields.map((f) => `\`${f.name}\``).join(', ')}\n${g.description}`).join('\n\n')
543
+ : `CRUD operations for ${table.name} records via ${toolName} CLI`;
723
544
  files.push({
724
545
  fileName: `${skillName}/references/${kebab}.md`,
725
546
  content: buildSkillReference({
726
547
  title: singularName,
727
- description: `CRUD operations for ${table.name} records via ${toolName} CLI`,
548
+ description: skillSpecialDesc,
728
549
  usage: [
729
550
  `${toolName} ${kebab} list`,
730
551
  `${toolName} ${kebab} get --${pk.name} <value>`,
@@ -982,7 +803,7 @@ export function generateMultiTargetReadme(input) {
982
803
  const kebab = toKebabCase(singularName);
983
804
  const pk = getPrimaryKeyInfo(table)[0];
984
805
  const scalarFields = getScalarFields(table);
985
- const editableFields = getEditableFields(table);
806
+ const editableFields = getEditableFields(table, registry);
986
807
  const defaultFields = getFieldsWithDefaults(table, registry);
987
808
  lines.push(`### \`${tgt.name}:${kebab}\``);
988
809
  lines.push('');
@@ -1015,6 +836,8 @@ export function generateMultiTargetReadme(input) {
1015
836
  if (requiredCreate.length === 0 && optionalCreate.length === 0) {
1016
837
  lines.push(`**Create fields:** ${editableFields.map((f) => `\`${f.name}\``).join(', ')}`);
1017
838
  }
839
+ const mtSpecialGroups = categorizeSpecialFields(table, registry);
840
+ lines.push(...buildSpecialFieldsMarkdown(mtSpecialGroups));
1018
841
  lines.push('');
1019
842
  }
1020
843
  for (const op of tgt.customOperations) {
@@ -1076,283 +899,50 @@ export function generateMultiTargetReadme(input) {
1076
899
  };
1077
900
  }
1078
901
  export function generateMultiTargetAgentsDocs(input) {
1079
- const { toolName, builtinNames, targets, registry } = input;
902
+ const { toolName, builtinNames, targets } = input;
1080
903
  const lines = [];
1081
- lines.push(`# ${toolName} CLI - Agent Reference`);
904
+ const totalTables = targets.reduce((sum, t) => sum + t.tables.length, 0);
905
+ const totalCustomOps = targets.reduce((sum, t) => sum + t.customOperations.length, 0);
906
+ lines.push(`# ${toolName} CLI`);
1082
907
  lines.push('');
1083
908
  lines.push('<!-- @constructive-io/graphql-codegen - DO NOT EDIT -->');
1084
- lines.push('> This document is structured for LLM/agent consumption.');
1085
909
  lines.push('');
1086
- lines.push('## OVERVIEW');
910
+ lines.push('## Stack');
1087
911
  lines.push('');
1088
- lines.push(`\`${toolName}\` is a unified multi-target CLI for interacting with multiple GraphQL APIs.`);
1089
- lines.push('All commands output JSON to stdout. All commands accept `--help` or `-h` for usage.');
1090
- lines.push(`Configuration is stored at \`~/.${toolName}/config/\` via appstash.`);
912
+ lines.push(`- Unified multi-target CLI for GraphQL APIs (TypeScript)`);
913
+ lines.push(`- ${targets.length} target${targets.length !== 1 ? 's' : ''}: ${targets.map((t) => t.name).join(', ')}`);
914
+ lines.push(`- ${totalTables} table${totalTables !== 1 ? 's' : ''}${totalCustomOps > 0 ? `, ${totalCustomOps} custom operation${totalCustomOps !== 1 ? 's' : ''}` : ''}`);
915
+ lines.push(`- Config stored at \`~/.${toolName}/config/\` via appstash`);
1091
916
  lines.push('');
1092
- lines.push('TARGETS:');
1093
- for (const tgt of targets) {
1094
- lines.push(` ${tgt.name}: ${tgt.endpoint}`);
1095
- }
1096
- lines.push('');
1097
- lines.push('COMMAND FORMAT:');
1098
- lines.push(` ${toolName} <target>:<command> <subcommand> [flags] Target-specific commands`);
1099
- lines.push(` ${toolName} ${builtinNames.context} <subcommand> [flags] Context management`);
1100
- lines.push(` ${toolName} ${builtinNames.auth} <subcommand> [flags] Authentication`);
1101
- lines.push(` ${toolName} ${builtinNames.config} <subcommand> [flags] Config key-value store`);
1102
- lines.push('');
1103
- lines.push('## PREREQUISITES');
1104
- lines.push('');
1105
- lines.push('Before running any data commands, you must:');
1106
- lines.push('');
1107
- lines.push(`1. Create a context: \`${toolName} ${builtinNames.context} create <name>\``);
1108
- lines.push(` (prompts for per-target endpoints, defaults baked from config)`);
1109
- lines.push(`2. Activate it: \`${toolName} ${builtinNames.context} use <name>\``);
1110
- lines.push(`3. Authenticate: \`${toolName} ${builtinNames.auth} set-token <token>\``);
1111
- lines.push('');
1112
- lines.push('For local development, create a context accepting all defaults:');
917
+ lines.push('## Quick Start');
1113
918
  lines.push('');
1114
919
  lines.push('```bash');
1115
- lines.push(`${toolName} ${builtinNames.context} create local`);
1116
- lines.push(`${toolName} ${builtinNames.context} use local`);
920
+ lines.push(`${toolName} ${builtinNames.context} create dev`);
921
+ lines.push(`${toolName} ${builtinNames.context} use dev`);
1117
922
  lines.push(`${toolName} ${builtinNames.auth} set-token <token>`);
1118
923
  lines.push('```');
1119
924
  lines.push('');
1120
- lines.push('## TOOLS');
1121
- lines.push('');
1122
- lines.push(`### TOOL: ${builtinNames.context}`);
1123
- lines.push('');
1124
- lines.push('Manage named API endpoint contexts. Each context stores per-target endpoint overrides.');
1125
- lines.push('');
1126
- lines.push('```');
1127
- lines.push('SUBCOMMANDS:');
1128
- lines.push(` ${toolName} ${builtinNames.context} create <name> Create a new context`);
1129
- lines.push(` ${toolName} ${builtinNames.context} list List all contexts`);
1130
- lines.push(` ${toolName} ${builtinNames.context} use <name> Set active context`);
1131
- lines.push(` ${toolName} ${builtinNames.context} current Show active context`);
1132
- lines.push(` ${toolName} ${builtinNames.context} delete <name> Delete a context`);
1133
- lines.push('');
1134
- lines.push('CREATE OPTIONS:');
1135
- for (const tgt of targets) {
1136
- lines.push(` --${tgt.name}-endpoint: string (default: ${tgt.endpoint})`);
1137
- }
1138
- lines.push('');
1139
- lines.push('OUTPUT: JSON');
1140
- lines.push(' create: { name, endpoint, targets }');
1141
- lines.push(' list: [{ name, endpoint, isCurrent, hasCredentials }]');
1142
- lines.push(' use: { name, endpoint }');
1143
- lines.push(' current: { name, endpoint }');
1144
- lines.push(' delete: { deleted: name }');
1145
- lines.push('```');
1146
- lines.push('');
1147
- lines.push(`### TOOL: ${builtinNames.auth}`);
1148
- lines.push('');
1149
- lines.push('Manage authentication tokens per context. One shared token across all targets.');
1150
- lines.push('');
1151
- lines.push('```');
1152
- lines.push('SUBCOMMANDS:');
1153
- lines.push(` ${toolName} ${builtinNames.auth} set-token <token> Store bearer token for current context`);
1154
- lines.push(` ${toolName} ${builtinNames.auth} status Show auth status for all contexts`);
1155
- lines.push(` ${toolName} ${builtinNames.auth} logout Remove credentials for current context`);
1156
- lines.push('');
1157
- lines.push('INPUT:');
1158
- lines.push(' token: string (required for set-token) - Bearer token value');
1159
- lines.push('');
1160
- lines.push('OUTPUT: JSON');
1161
- lines.push(' set-token: { context, status: "authenticated" }');
1162
- lines.push(' status: [{ context, authenticated: boolean }]');
1163
- lines.push(' logout: { context, status: "logged out" }');
1164
- lines.push('```');
1165
- lines.push('');
1166
- lines.push(`### TOOL: ${builtinNames.config}`);
1167
- lines.push('');
1168
- lines.push('Manage per-context key-value configuration variables.');
1169
- lines.push('');
1170
- lines.push('```');
1171
- lines.push('SUBCOMMANDS:');
1172
- lines.push(` ${toolName} ${builtinNames.config} get <key> Get a config value`);
1173
- lines.push(` ${toolName} ${builtinNames.config} set <key> <value> Set a config value`);
1174
- lines.push(` ${toolName} ${builtinNames.config} list List all config values`);
1175
- lines.push(` ${toolName} ${builtinNames.config} delete <key> Delete a config value`);
1176
- lines.push('');
1177
- lines.push('INPUT:');
1178
- lines.push(' key: string (required for get/set/delete) - Variable name');
1179
- lines.push(' value: string (required for set) - Variable value');
1180
- lines.push('');
1181
- lines.push('OUTPUT: JSON');
1182
- lines.push(' get: { key, value }');
1183
- lines.push(' set: { key, value }');
1184
- lines.push(' list: { vars: { key: value, ... } }');
1185
- lines.push(' delete: { deleted: key }');
1186
- lines.push('```');
1187
- lines.push('');
1188
- lines.push('### TOOL: helpers (SDK)');
1189
- lines.push('');
1190
- lines.push('Typed client factories for use in scripts and services (generated helpers.ts).');
1191
- lines.push('Resolves credentials via: appstash store -> env vars -> throw.');
925
+ lines.push('## Command Format');
1192
926
  lines.push('');
1193
927
  lines.push('```');
1194
- lines.push('FACTORIES:');
1195
- for (const tgt of targets) {
1196
- const pascalName = tgt.name.charAt(0).toUpperCase() + tgt.name.slice(1);
1197
- lines.push(` create${pascalName}Client(contextName?) Create a configured ${tgt.name} ORM client`);
1198
- }
1199
- lines.push('');
1200
- lines.push('USAGE:');
1201
- lines.push(` import { create${targets[0] ? targets[0].name.charAt(0).toUpperCase() + targets[0].name.slice(1) : 'Target'}Client } from './helpers';`);
1202
- lines.push(` const client = create${targets[0] ? targets[0].name.charAt(0).toUpperCase() + targets[0].name.slice(1) : 'Target'}Client();`);
1203
- lines.push('');
1204
- lines.push('CREDENTIAL RESOLUTION:');
1205
- lines.push(` 1. appstash store (~/.${toolName}/config/)`);
1206
- const envPrefix = toolName.toUpperCase().replace(/-/g, '_');
1207
- lines.push(` 2. env vars (${envPrefix}_TOKEN, ${envPrefix}_<TARGET>_ENDPOINT)`);
1208
- lines.push(' 3. throws with actionable error message');
1209
- lines.push('```');
1210
- lines.push('');
1211
- for (const tgt of targets) {
1212
- for (const table of tgt.tables) {
1213
- const { singularName } = getTableNames(table);
1214
- const kebab = toKebabCase(singularName);
1215
- const pk = getPrimaryKeyInfo(table)[0];
1216
- const scalarFields = getScalarFields(table);
1217
- const editableFields = getEditableFields(table);
1218
- const defaultFields = getFieldsWithDefaults(table, registry);
1219
- const requiredCreateFields = editableFields.filter((f) => !defaultFields.has(f.name));
1220
- const optionalCreateFields = editableFields.filter((f) => defaultFields.has(f.name));
1221
- const createFlags = [
1222
- ...requiredCreateFields.map((f) => `--${f.name} <value>`),
1223
- ...optionalCreateFields.map((f) => `[--${f.name} <value>]`),
1224
- ].join(' ');
1225
- lines.push(`### TOOL: ${tgt.name}:${kebab}`);
1226
- lines.push('');
1227
- lines.push(`CRUD operations for ${table.name} records (${tgt.name} target).`);
1228
- lines.push('');
1229
- lines.push('```');
1230
- lines.push('SUBCOMMANDS:');
1231
- lines.push(` ${toolName} ${tgt.name}:${kebab} list List all records`);
1232
- lines.push(` ${toolName} ${tgt.name}:${kebab} get --${pk.name} <value> Get one record`);
1233
- lines.push(` ${toolName} ${tgt.name}:${kebab} create ${createFlags}`);
1234
- lines.push(` ${toolName} ${tgt.name}:${kebab} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`);
1235
- lines.push(` ${toolName} ${tgt.name}:${kebab} delete --${pk.name} <value> Delete one record`);
1236
- lines.push('');
1237
- lines.push('INPUT FIELDS:');
1238
- for (const f of scalarFields) {
1239
- const isPk = f.name === pk.name;
1240
- lines.push(` ${f.name}: ${cleanTypeName(f.type.gqlType)}${isPk ? ' (primary key)' : ''}`);
1241
- }
1242
- lines.push('');
1243
- lines.push('EDITABLE FIELDS (for create/update):');
1244
- for (const f of editableFields) {
1245
- const optLabel = defaultFields.has(f.name) ? ' (optional, has backend default)' : '';
1246
- lines.push(` ${f.name}: ${cleanTypeName(f.type.gqlType)}${optLabel}`);
1247
- }
1248
- lines.push('');
1249
- lines.push('OUTPUT: JSON');
1250
- lines.push(` list: [{ ${scalarFields.map((f) => f.name).join(', ')} }]`);
1251
- lines.push(` get: { ${scalarFields.map((f) => f.name).join(', ')} }`);
1252
- lines.push(` create: { ${scalarFields.map((f) => f.name).join(', ')} }`);
1253
- lines.push(` update: { ${scalarFields.map((f) => f.name).join(', ')} }`);
1254
- lines.push(` delete: { ${pk.name} }`);
1255
- lines.push('```');
1256
- lines.push('');
1257
- }
1258
- for (const op of tgt.customOperations) {
1259
- const kebab = toKebabCase(op.name);
1260
- const flat = flattenArgs(op.args, registry);
1261
- lines.push(`### TOOL: ${tgt.name}:${kebab}`);
1262
- lines.push('');
1263
- lines.push(op.description || op.name);
1264
- lines.push('');
1265
- lines.push('```');
1266
- lines.push(`TYPE: ${op.kind}`);
1267
- if (flat.length > 0) {
1268
- const flags = flattenedArgsToFlags(flat);
1269
- lines.push(`USAGE: ${toolName} ${tgt.name}:${kebab} ${flags}`);
1270
- lines.push('');
1271
- lines.push('INPUT:');
1272
- for (const a of flat) {
1273
- const reqLabel = a.required ? ' (required)' : '';
1274
- lines.push(` ${a.flag}: ${a.type}${reqLabel}`);
1275
- }
1276
- }
1277
- else {
1278
- lines.push(`USAGE: ${toolName} ${tgt.name}:${kebab}`);
1279
- lines.push('');
1280
- lines.push('INPUT: none');
1281
- }
1282
- if (tgt.isAuthTarget && op.kind === 'mutation') {
1283
- lines.push('');
1284
- lines.push('FLAGS:');
1285
- lines.push(' --save-token: boolean - Auto-save returned token to credentials');
1286
- }
1287
- lines.push('');
1288
- lines.push('OUTPUT: JSON');
1289
- lines.push('```');
1290
- lines.push('');
1291
- }
1292
- }
1293
- lines.push('## WORKFLOWS');
1294
- lines.push('');
1295
- lines.push('### Initial setup');
1296
- lines.push('');
1297
- lines.push('```bash');
1298
- lines.push(`${toolName} ${builtinNames.context} create dev`);
1299
- lines.push(`${toolName} ${builtinNames.context} use dev`);
1300
- lines.push(`${toolName} ${builtinNames.auth} set-token eyJhbGciOiJIUzI1NiIs...`);
928
+ lines.push(`${toolName} <target>:<command> <subcommand> [flags]`);
1301
929
  lines.push('```');
1302
930
  lines.push('');
1303
- lines.push('### Switch environment');
931
+ lines.push('## Resources');
1304
932
  lines.push('');
1305
- lines.push('```bash');
1306
- lines.push(`${toolName} ${builtinNames.context} create production \\`);
1307
- for (let i = 0; i < targets.length; i++) {
1308
- const tgt = targets[i];
1309
- const continuation = i < targets.length - 1 ? ' \\' : '';
1310
- lines.push(` --${tgt.name}-endpoint https://${tgt.name}.prod.example.com/graphql${continuation}`);
1311
- }
1312
- lines.push(`${toolName} ${builtinNames.context} use production`);
1313
- lines.push('```');
1314
- lines.push('');
1315
- if (targets.length > 0 && targets[0].tables.length > 0) {
1316
- const tgt = targets[0];
1317
- const table = tgt.tables[0];
1318
- const { singularName } = getTableNames(table);
1319
- const kebab = toKebabCase(singularName);
1320
- const editableFields = getEditableFields(table);
1321
- const pk = getPrimaryKeyInfo(table)[0];
1322
- lines.push(`### CRUD workflow (${tgt.name}:${kebab})`);
1323
- lines.push('');
1324
- lines.push('```bash');
1325
- lines.push(`${toolName} ${tgt.name}:${kebab} list`);
1326
- lines.push(`${toolName} ${tgt.name}:${kebab} create ${editableFields.map((f) => `--${f.name} "value"`).join(' ')}`);
1327
- lines.push(`${toolName} ${tgt.name}:${kebab} get --${pk.name} <value>`);
1328
- lines.push(`${toolName} ${tgt.name}:${kebab} update --${pk.name} <value> --${editableFields[0]?.name || 'field'} "new-value"`);
1329
- lines.push(`${toolName} ${tgt.name}:${kebab} delete --${pk.name} <value>`);
1330
- lines.push('```');
1331
- lines.push('');
1332
- }
1333
- lines.push('### Piping output');
933
+ lines.push(`- **Full API reference:** [README.md](./README.md) — CRUD docs for all ${totalTables} tables across ${targets.length} targets`);
934
+ lines.push('- **Schema types:** [types.ts](./types.ts)');
935
+ lines.push('- **SDK helpers:** [helpers.ts](./helpers.ts) typed client factories');
1334
936
  lines.push('');
1335
- lines.push('```bash');
1336
- if (targets.length > 0 && targets[0].tables.length > 0) {
1337
- const tgt = targets[0];
1338
- const kebab = toKebabCase(getTableNames(tgt.tables[0]).singularName);
1339
- lines.push(`${toolName} ${tgt.name}:${kebab} list | jq '.'`);
1340
- lines.push(`${toolName} ${tgt.name}:${kebab} list | jq '.[].id'`);
1341
- lines.push(`${toolName} ${tgt.name}:${kebab} list | jq 'length'`);
1342
- }
1343
- lines.push('```');
937
+ lines.push('## Conventions');
1344
938
  lines.push('');
1345
- lines.push('## ERROR HANDLING');
939
+ lines.push('- All commands output JSON to stdout');
940
+ lines.push('- Use `--help` on any command for usage');
941
+ lines.push('- Exit 0 = success, 1 = error');
1346
942
  lines.push('');
1347
- lines.push('All errors are written to stderr. Exit codes:');
1348
- lines.push('- `0`: Success');
1349
- lines.push('- `1`: Error (auth failure, not found, validation error, network error)');
943
+ lines.push('## Boundaries');
1350
944
  lines.push('');
1351
- lines.push('Common errors:');
1352
- lines.push(`- "No active context": Run \`${builtinNames.context} use <name>\` first`);
1353
- lines.push(`- "Not authenticated": Run \`${builtinNames.auth} set-token <token>\` first`);
1354
- lines.push('- "Unknown target": The target name is not recognized');
1355
- lines.push('- "Record not found": The requested ID does not exist');
945
+ lines.push('All files in this directory are generated. Do not edit manually.');
1356
946
  lines.push('');
1357
947
  return {
1358
948
  fileName: 'AGENTS.md',
@@ -1478,7 +1068,7 @@ export function getMultiTargetCliMcpTools(input) {
1478
1068
  const kebab = toKebabCase(singularName);
1479
1069
  const pk = getPrimaryKeyInfo(table)[0];
1480
1070
  const scalarFields = getScalarFields(table);
1481
- const editableFields = getEditableFields(table);
1071
+ const editableFields = getEditableFields(table, registry);
1482
1072
  const defaultFields = getFieldsWithDefaults(table, registry);
1483
1073
  const requiredCreateFieldNames = editableFields
1484
1074
  .filter((f) => !defaultFields.has(f.name))
@@ -1740,7 +1330,7 @@ export function generateMultiTargetSkills(input) {
1740
1330
  const { singularName } = getTableNames(table);
1741
1331
  const kebab = toKebabCase(singularName);
1742
1332
  const pk = getPrimaryKeyInfo(table)[0];
1743
- const editableFields = getEditableFields(table);
1333
+ const editableFields = getEditableFields(table, registry);
1744
1334
  const defaultFields = getFieldsWithDefaults(table, registry);
1745
1335
  const createFlags = [
1746
1336
  ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <value>`),
@@ -1748,11 +1338,16 @@ export function generateMultiTargetSkills(input) {
1748
1338
  ].join(' ');
1749
1339
  const cmd = `${tgt.name}:${kebab}`;
1750
1340
  tgtReferenceNames.push(kebab);
1341
+ const mtSkillSpecialGroups = categorizeSpecialFields(table, registry);
1342
+ const mtSkillSpecialDesc = mtSkillSpecialGroups.length > 0
1343
+ ? `CRUD operations for ${table.name} records via ${toolName} CLI (${tgt.name} target)\n\n` +
1344
+ mtSkillSpecialGroups.map((g) => `**${g.label}:** ${g.fields.map((f) => `\`${f.name}\``).join(', ')}\n${g.description}`).join('\n\n')
1345
+ : `CRUD operations for ${table.name} records via ${toolName} CLI (${tgt.name} target)`;
1751
1346
  files.push({
1752
1347
  fileName: `${tgtSkillName}/references/${kebab}.md`,
1753
1348
  content: buildSkillReference({
1754
1349
  title: singularName,
1755
- description: `CRUD operations for ${table.name} records via ${toolName} CLI (${tgt.name} target)`,
1350
+ description: mtSkillSpecialDesc,
1756
1351
  usage: [
1757
1352
  `${toolName} ${cmd} list`,
1758
1353
  `${toolName} ${cmd} get --${pk.name} <value>`,