@constructive-io/graphql-codegen 4.7.2 → 4.8.0
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/docs-generator.d.ts +1 -1
- package/core/codegen/cli/docs-generator.js +137 -54
- package/core/codegen/docs-utils.d.ts +12 -1
- package/core/codegen/docs-utils.js +49 -1
- package/core/codegen/hooks-docs-generator.d.ts +1 -1
- package/core/codegen/hooks-docs-generator.js +47 -7
- package/core/codegen/orm/docs-generator.d.ts +1 -1
- package/core/codegen/orm/docs-generator.js +57 -20
- package/core/generate.d.ts +5 -0
- package/core/generate.js +48 -12
- package/core/workspace.d.ts +13 -0
- package/core/workspace.js +92 -0
- package/esm/core/codegen/cli/docs-generator.d.ts +1 -1
- package/esm/core/codegen/cli/docs-generator.js +138 -55
- package/esm/core/codegen/docs-utils.d.ts +12 -1
- package/esm/core/codegen/docs-utils.js +48 -1
- package/esm/core/codegen/hooks-docs-generator.d.ts +1 -1
- package/esm/core/codegen/hooks-docs-generator.js +48 -8
- package/esm/core/codegen/orm/docs-generator.d.ts +1 -1
- package/esm/core/codegen/orm/docs-generator.js +58 -21
- package/esm/core/generate.d.ts +5 -0
- package/esm/core/generate.js +48 -12
- package/esm/core/workspace.d.ts +13 -0
- package/esm/core/workspace.js +89 -0
- package/esm/types/config.d.ts +12 -4
- package/package.json +22 -22
- package/types/config.d.ts +12 -4
package/core/generate.js
CHANGED
|
@@ -66,6 +66,18 @@ const target_docs_generator_1 = require("./codegen/target-docs-generator");
|
|
|
66
66
|
const introspect_1 = require("./introspect");
|
|
67
67
|
const output_1 = require("./output");
|
|
68
68
|
const pipeline_1 = require("./pipeline");
|
|
69
|
+
const workspace_1 = require("./workspace");
|
|
70
|
+
function resolveSkillsOutputDir(config, outputRoot) {
|
|
71
|
+
const workspaceRoot = (0, workspace_1.findWorkspaceRoot)(node_path_1.default.resolve(outputRoot)) ??
|
|
72
|
+
(0, workspace_1.findWorkspaceRoot)(process.cwd()) ??
|
|
73
|
+
process.cwd();
|
|
74
|
+
if (config.skillsPath) {
|
|
75
|
+
return node_path_1.default.isAbsolute(config.skillsPath)
|
|
76
|
+
? config.skillsPath
|
|
77
|
+
: node_path_1.default.resolve(workspaceRoot, config.skillsPath);
|
|
78
|
+
}
|
|
79
|
+
return node_path_1.default.resolve(workspaceRoot, 'skills');
|
|
80
|
+
}
|
|
69
81
|
async function generate(options = {}, internalOptions) {
|
|
70
82
|
// Apply defaults to get resolved config
|
|
71
83
|
const config = (0, config_1.getConfigOptions)(options);
|
|
@@ -261,6 +273,8 @@ async function generate(options = {}, internalOptions) {
|
|
|
261
273
|
...(customOperations.mutations ?? []),
|
|
262
274
|
];
|
|
263
275
|
const allMcpTools = [];
|
|
276
|
+
const targetName = internalOptions?.targetName ?? 'default';
|
|
277
|
+
const skillsToWrite = [];
|
|
264
278
|
if (runOrm) {
|
|
265
279
|
if (docsConfig.readme) {
|
|
266
280
|
const readme = (0, docs_generator_2.generateOrmReadme)(tables, allCustomOps);
|
|
@@ -274,8 +288,8 @@ async function generate(options = {}, internalOptions) {
|
|
|
274
288
|
allMcpTools.push(...(0, docs_generator_2.getOrmMcpTools)(tables, allCustomOps));
|
|
275
289
|
}
|
|
276
290
|
if (docsConfig.skills) {
|
|
277
|
-
for (const skill of (0, docs_generator_2.generateOrmSkills)(tables, allCustomOps)) {
|
|
278
|
-
|
|
291
|
+
for (const skill of (0, docs_generator_2.generateOrmSkills)(tables, allCustomOps, targetName)) {
|
|
292
|
+
skillsToWrite.push({ path: skill.fileName, content: skill.content });
|
|
279
293
|
}
|
|
280
294
|
}
|
|
281
295
|
}
|
|
@@ -292,8 +306,8 @@ async function generate(options = {}, internalOptions) {
|
|
|
292
306
|
allMcpTools.push(...(0, hooks_docs_generator_1.getHooksMcpTools)(tables, allCustomOps));
|
|
293
307
|
}
|
|
294
308
|
if (docsConfig.skills) {
|
|
295
|
-
for (const skill of (0, hooks_docs_generator_1.generateHooksSkills)(tables, allCustomOps)) {
|
|
296
|
-
|
|
309
|
+
for (const skill of (0, hooks_docs_generator_1.generateHooksSkills)(tables, allCustomOps, targetName)) {
|
|
310
|
+
skillsToWrite.push({ path: skill.fileName, content: skill.content });
|
|
297
311
|
}
|
|
298
312
|
}
|
|
299
313
|
}
|
|
@@ -313,8 +327,8 @@ async function generate(options = {}, internalOptions) {
|
|
|
313
327
|
allMcpTools.push(...(0, docs_generator_1.getCliMcpTools)(tables, allCustomOps, toolName));
|
|
314
328
|
}
|
|
315
329
|
if (docsConfig.skills) {
|
|
316
|
-
for (const skill of (0, docs_generator_1.generateSkills)(tables, allCustomOps, toolName)) {
|
|
317
|
-
|
|
330
|
+
for (const skill of (0, docs_generator_1.generateSkills)(tables, allCustomOps, toolName, targetName)) {
|
|
331
|
+
skillsToWrite.push({ path: skill.fileName, content: skill.content });
|
|
318
332
|
}
|
|
319
333
|
}
|
|
320
334
|
}
|
|
@@ -352,6 +366,21 @@ async function generate(options = {}, internalOptions) {
|
|
|
352
366
|
};
|
|
353
367
|
}
|
|
354
368
|
allFilesWritten.push(...(writeResult.filesWritten ?? []));
|
|
369
|
+
if (skillsToWrite.length > 0) {
|
|
370
|
+
const skillsOutputDir = resolveSkillsOutputDir(config, outputRoot);
|
|
371
|
+
const skillsWriteResult = await (0, output_1.writeGeneratedFiles)(skillsToWrite, skillsOutputDir, [], {
|
|
372
|
+
pruneStaleFiles: false,
|
|
373
|
+
});
|
|
374
|
+
if (!skillsWriteResult.success) {
|
|
375
|
+
return {
|
|
376
|
+
success: false,
|
|
377
|
+
message: `Failed to write generated skill files: ${skillsWriteResult.errors?.join(', ')}`,
|
|
378
|
+
output: skillsOutputDir,
|
|
379
|
+
errors: skillsWriteResult.errors,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
allFilesWritten.push(...(skillsWriteResult.filesWritten ?? []));
|
|
383
|
+
}
|
|
355
384
|
}
|
|
356
385
|
const generators = [
|
|
357
386
|
runReactQuery && 'React Query',
|
|
@@ -530,7 +559,7 @@ async function generateMulti(options) {
|
|
|
530
559
|
dryRun,
|
|
531
560
|
schemaOnly,
|
|
532
561
|
schemaOnlyFilename: schemaOnly ? `${name}.graphql` : undefined,
|
|
533
|
-
}, useUnifiedCli ? { skipCli: true } :
|
|
562
|
+
}, useUnifiedCli ? { skipCli: true, targetName: name } : { targetName: name });
|
|
534
563
|
results.push({ name, result });
|
|
535
564
|
if (!result.success) {
|
|
536
565
|
hasError = true;
|
|
@@ -613,17 +642,24 @@ async function generateMulti(options) {
|
|
|
613
642
|
if (docsConfig.mcp) {
|
|
614
643
|
allMcpTools.push(...(0, docs_generator_1.getMultiTargetCliMcpTools)(docsInput));
|
|
615
644
|
}
|
|
616
|
-
if (docsConfig.skills) {
|
|
617
|
-
for (const skill of (0, docs_generator_1.generateMultiTargetSkills)(docsInput)) {
|
|
618
|
-
cliFilesToWrite.push({ path: node_path_1.default.posix.join('cli', skill.fileName), content: skill.content });
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
645
|
if (docsConfig.mcp && allMcpTools.length > 0) {
|
|
622
646
|
const mcpFile = (0, target_docs_generator_1.generateCombinedMcpConfig)(allMcpTools, toolName);
|
|
623
647
|
cliFilesToWrite.push({ path: node_path_1.default.posix.join('cli', mcpFile.fileName), content: mcpFile.content });
|
|
624
648
|
}
|
|
625
649
|
const { writeGeneratedFiles: writeFiles } = await Promise.resolve().then(() => __importStar(require('./output')));
|
|
626
650
|
await writeFiles(cliFilesToWrite, '.', [], { pruneStaleFiles: false });
|
|
651
|
+
if (docsConfig.skills) {
|
|
652
|
+
const cliSkillsToWrite = (0, docs_generator_1.generateMultiTargetSkills)(docsInput).map((skill) => ({
|
|
653
|
+
path: skill.fileName,
|
|
654
|
+
content: skill.content,
|
|
655
|
+
}));
|
|
656
|
+
const firstTargetResolved = (0, config_1.getConfigOptions)({
|
|
657
|
+
...(firstTargetConfig ?? {}),
|
|
658
|
+
...(cliOverrides ?? {}),
|
|
659
|
+
});
|
|
660
|
+
const skillsOutputDir = resolveSkillsOutputDir(firstTargetResolved, firstTargetResolved.output);
|
|
661
|
+
await writeFiles(cliSkillsToWrite, skillsOutputDir, [], { pruneStaleFiles: false });
|
|
662
|
+
}
|
|
627
663
|
}
|
|
628
664
|
// Generate root-root README if multi-target
|
|
629
665
|
if (names.length > 1 && targetInfos.length > 0 && !dryRun) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the workspace root directory.
|
|
3
|
+
*
|
|
4
|
+
* Search order:
|
|
5
|
+
* 1. pnpm-workspace.yaml (pnpm workspaces)
|
|
6
|
+
* 2. lerna.json (lerna workspaces)
|
|
7
|
+
* 3. package.json with "workspaces" field (npm/yarn workspaces)
|
|
8
|
+
* 4. Nearest package.json (fallback for non-workspace projects)
|
|
9
|
+
*
|
|
10
|
+
* @param cwd - Starting directory to search from (defaults to process.cwd())
|
|
11
|
+
* @returns The workspace root path, or null if nothing found
|
|
12
|
+
*/
|
|
13
|
+
export declare function findWorkspaceRoot(cwd?: string): string | null;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findWorkspaceRoot = findWorkspaceRoot;
|
|
4
|
+
/**
|
|
5
|
+
* Workspace detection utilities
|
|
6
|
+
*
|
|
7
|
+
* Finds the root of a workspace by walking up directories looking for
|
|
8
|
+
* workspace markers (pnpm-workspace.yaml, lerna.json, package.json with workspaces).
|
|
9
|
+
* Falls back to the nearest package.json directory.
|
|
10
|
+
*
|
|
11
|
+
* Inspired by @pgpmjs/env walkUp / resolvePnpmWorkspace patterns.
|
|
12
|
+
*/
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
14
|
+
const node_path_1 = require("node:path");
|
|
15
|
+
/**
|
|
16
|
+
* Walk up directories from startDir looking for a file.
|
|
17
|
+
* Returns the directory containing the file, or null if not found.
|
|
18
|
+
*/
|
|
19
|
+
function walkUp(startDir, filename) {
|
|
20
|
+
let currentDir = (0, node_path_1.resolve)(startDir);
|
|
21
|
+
while (currentDir) {
|
|
22
|
+
const targetPath = (0, node_path_1.resolve)(currentDir, filename);
|
|
23
|
+
if ((0, node_fs_1.existsSync)(targetPath)) {
|
|
24
|
+
return currentDir;
|
|
25
|
+
}
|
|
26
|
+
const parentDir = (0, node_path_1.dirname)(currentDir);
|
|
27
|
+
if (parentDir === currentDir) {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
currentDir = parentDir;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Find the first pnpm workspace root by looking for pnpm-workspace.yaml
|
|
36
|
+
*/
|
|
37
|
+
function findPnpmWorkspace(cwd) {
|
|
38
|
+
return walkUp(cwd, 'pnpm-workspace.yaml');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find the first lerna workspace root by looking for lerna.json
|
|
42
|
+
*/
|
|
43
|
+
function findLernaWorkspace(cwd) {
|
|
44
|
+
return walkUp(cwd, 'lerna.json');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Find the first npm/yarn workspace root by looking for package.json with workspaces field
|
|
48
|
+
*/
|
|
49
|
+
function findNpmWorkspace(cwd) {
|
|
50
|
+
let currentDir = (0, node_path_1.resolve)(cwd);
|
|
51
|
+
const root = (0, node_path_1.parse)(currentDir).root;
|
|
52
|
+
while (currentDir !== root) {
|
|
53
|
+
const packageJsonPath = (0, node_path_1.resolve)(currentDir, 'package.json');
|
|
54
|
+
if ((0, node_fs_1.existsSync)(packageJsonPath)) {
|
|
55
|
+
try {
|
|
56
|
+
const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, 'utf8'));
|
|
57
|
+
if (packageJson.workspaces) {
|
|
58
|
+
return currentDir;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Ignore JSON parse errors
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
currentDir = (0, node_path_1.dirname)(currentDir);
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Find the nearest package.json directory (fallback)
|
|
71
|
+
*/
|
|
72
|
+
function findPackageRoot(cwd) {
|
|
73
|
+
return walkUp(cwd, 'package.json');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Find the workspace root directory.
|
|
77
|
+
*
|
|
78
|
+
* Search order:
|
|
79
|
+
* 1. pnpm-workspace.yaml (pnpm workspaces)
|
|
80
|
+
* 2. lerna.json (lerna workspaces)
|
|
81
|
+
* 3. package.json with "workspaces" field (npm/yarn workspaces)
|
|
82
|
+
* 4. Nearest package.json (fallback for non-workspace projects)
|
|
83
|
+
*
|
|
84
|
+
* @param cwd - Starting directory to search from (defaults to process.cwd())
|
|
85
|
+
* @returns The workspace root path, or null if nothing found
|
|
86
|
+
*/
|
|
87
|
+
function findWorkspaceRoot(cwd = process.cwd()) {
|
|
88
|
+
return (findPnpmWorkspace(cwd) ??
|
|
89
|
+
findLernaWorkspace(cwd) ??
|
|
90
|
+
findNpmWorkspace(cwd) ??
|
|
91
|
+
findPackageRoot(cwd));
|
|
92
|
+
}
|
|
@@ -5,7 +5,7 @@ export type { GeneratedDocFile, McpTool } from '../docs-utils';
|
|
|
5
5
|
export declare function generateReadme(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): GeneratedDocFile;
|
|
6
6
|
export declare function generateAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): GeneratedDocFile;
|
|
7
7
|
export declare function getCliMcpTools(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): McpTool[];
|
|
8
|
-
export declare function generateSkills(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): GeneratedDocFile[];
|
|
8
|
+
export declare function generateSkills(tables: CleanTable[], customOperations: CleanOperation[], toolName: string, targetName: string): GeneratedDocFile[];
|
|
9
9
|
export interface MultiTargetDocsInput {
|
|
10
10
|
toolName: string;
|
|
11
11
|
builtinNames: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { toKebabCase } from 'komoji';
|
|
2
|
-
import { formatArgType, getEditableFields, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, buildSkillFile, } from '../docs-utils';
|
|
2
|
+
import { formatArgType, getEditableFields, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, buildSkillFile, buildSkillReference, } from '../docs-utils';
|
|
3
3
|
import { getScalarFields, getTableNames, getPrimaryKeyInfo, } from '../utils';
|
|
4
4
|
export { resolveDocsConfig } from '../docs-utils';
|
|
5
5
|
export function generateReadme(tables, customOperations, toolName) {
|
|
@@ -513,12 +513,16 @@ export function getCliMcpTools(tables, customOperations, toolName) {
|
|
|
513
513
|
}
|
|
514
514
|
return tools;
|
|
515
515
|
}
|
|
516
|
-
export function generateSkills(tables, customOperations, toolName) {
|
|
516
|
+
export function generateSkills(tables, customOperations, toolName, targetName) {
|
|
517
517
|
const files = [];
|
|
518
|
+
const skillName = `cli-${targetName}`;
|
|
519
|
+
const referenceNames = [];
|
|
520
|
+
// Context reference
|
|
521
|
+
referenceNames.push('context');
|
|
518
522
|
files.push({
|
|
519
|
-
fileName:
|
|
520
|
-
content:
|
|
521
|
-
|
|
523
|
+
fileName: `${skillName}/references/context.md`,
|
|
524
|
+
content: buildSkillReference({
|
|
525
|
+
title: 'Context Management',
|
|
522
526
|
description: `Manage API endpoint contexts for ${toolName}`,
|
|
523
527
|
usage: [
|
|
524
528
|
`${toolName} context create <name> --endpoint <url>`,
|
|
@@ -542,10 +546,12 @@ export function generateSkills(tables, customOperations, toolName) {
|
|
|
542
546
|
],
|
|
543
547
|
}),
|
|
544
548
|
});
|
|
549
|
+
// Auth reference
|
|
550
|
+
referenceNames.push('auth');
|
|
545
551
|
files.push({
|
|
546
|
-
fileName:
|
|
547
|
-
content:
|
|
548
|
-
|
|
552
|
+
fileName: `${skillName}/references/auth.md`,
|
|
553
|
+
content: buildSkillReference({
|
|
554
|
+
title: 'Authentication',
|
|
549
555
|
description: `Manage authentication tokens for ${toolName}`,
|
|
550
556
|
usage: [
|
|
551
557
|
`${toolName} auth set-token <token>`,
|
|
@@ -564,15 +570,17 @@ export function generateSkills(tables, customOperations, toolName) {
|
|
|
564
570
|
],
|
|
565
571
|
}),
|
|
566
572
|
});
|
|
573
|
+
// Table references
|
|
567
574
|
for (const table of tables) {
|
|
568
575
|
const { singularName } = getTableNames(table);
|
|
569
576
|
const kebab = toKebabCase(singularName);
|
|
570
577
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
571
578
|
const editableFields = getEditableFields(table);
|
|
579
|
+
referenceNames.push(kebab);
|
|
572
580
|
files.push({
|
|
573
|
-
fileName:
|
|
574
|
-
content:
|
|
575
|
-
|
|
581
|
+
fileName: `${skillName}/references/${kebab}.md`,
|
|
582
|
+
content: buildSkillReference({
|
|
583
|
+
title: singularName,
|
|
576
584
|
description: `CRUD operations for ${table.name} records via ${toolName} CLI`,
|
|
577
585
|
usage: [
|
|
578
586
|
`${toolName} ${kebab} list`,
|
|
@@ -596,29 +604,21 @@ export function generateSkills(tables, customOperations, toolName) {
|
|
|
596
604
|
description: `Get a ${singularName} by ${pk.name}`,
|
|
597
605
|
code: [`${toolName} ${kebab} get --${pk.name} <value>`],
|
|
598
606
|
},
|
|
599
|
-
{
|
|
600
|
-
description: `Update a ${singularName}`,
|
|
601
|
-
code: [
|
|
602
|
-
`${toolName} ${kebab} update --${pk.name} <value> --${editableFields[0]?.name || 'field'} "new-value"`,
|
|
603
|
-
],
|
|
604
|
-
},
|
|
605
|
-
{
|
|
606
|
-
description: `Delete a ${singularName}`,
|
|
607
|
-
code: [`${toolName} ${kebab} delete --${pk.name} <value>`],
|
|
608
|
-
},
|
|
609
607
|
],
|
|
610
608
|
}),
|
|
611
609
|
});
|
|
612
610
|
}
|
|
611
|
+
// Custom operation references
|
|
613
612
|
for (const op of customOperations) {
|
|
614
613
|
const kebab = toKebabCase(op.name);
|
|
615
614
|
const usage = op.args.length > 0
|
|
616
615
|
? `${toolName} ${kebab} ${op.args.map((a) => `--${a.name} <value>`).join(' ')}`
|
|
617
616
|
: `${toolName} ${kebab}`;
|
|
617
|
+
referenceNames.push(kebab);
|
|
618
618
|
files.push({
|
|
619
|
-
fileName:
|
|
620
|
-
content:
|
|
621
|
-
|
|
619
|
+
fileName: `${skillName}/references/${kebab}.md`,
|
|
620
|
+
content: buildSkillReference({
|
|
621
|
+
title: op.name,
|
|
622
622
|
description: op.description || `Execute the ${op.name} ${op.kind}`,
|
|
623
623
|
usage: [usage],
|
|
624
624
|
examples: [
|
|
@@ -630,6 +630,39 @@ export function generateSkills(tables, customOperations, toolName) {
|
|
|
630
630
|
}),
|
|
631
631
|
});
|
|
632
632
|
}
|
|
633
|
+
// Overview SKILL.md
|
|
634
|
+
const tableKebabs = tables.slice(0, 5).map((t) => toKebabCase(getTableNames(t).singularName));
|
|
635
|
+
files.push({
|
|
636
|
+
fileName: `${skillName}/SKILL.md`,
|
|
637
|
+
content: buildSkillFile({
|
|
638
|
+
name: skillName,
|
|
639
|
+
description: `CLI tool (${toolName}) for the ${targetName} API — provides CRUD commands for ${tables.length} tables and ${customOperations.length} custom operations`,
|
|
640
|
+
usage: [
|
|
641
|
+
`# Context management`,
|
|
642
|
+
`${toolName} context create <name> --endpoint <url>`,
|
|
643
|
+
`${toolName} context use <name>`,
|
|
644
|
+
'',
|
|
645
|
+
`# Authentication`,
|
|
646
|
+
`${toolName} auth set-token <token>`,
|
|
647
|
+
'',
|
|
648
|
+
`# CRUD for any table (e.g. ${tableKebabs[0] || 'model'})`,
|
|
649
|
+
`${toolName} ${tableKebabs[0] || 'model'} list`,
|
|
650
|
+
`${toolName} ${tableKebabs[0] || 'model'} get --id <value>`,
|
|
651
|
+
`${toolName} ${tableKebabs[0] || 'model'} create --<field> <value>`,
|
|
652
|
+
],
|
|
653
|
+
examples: [
|
|
654
|
+
{
|
|
655
|
+
description: 'Set up and query',
|
|
656
|
+
code: [
|
|
657
|
+
`${toolName} context create local --endpoint http://localhost:5000/graphql`,
|
|
658
|
+
`${toolName} context use local`,
|
|
659
|
+
`${toolName} auth set-token <token>`,
|
|
660
|
+
`${toolName} ${tableKebabs[0] || 'model'} list`,
|
|
661
|
+
],
|
|
662
|
+
},
|
|
663
|
+
],
|
|
664
|
+
}, referenceNames),
|
|
665
|
+
});
|
|
633
666
|
return files;
|
|
634
667
|
}
|
|
635
668
|
export function generateMultiTargetReadme(input) {
|
|
@@ -1256,22 +1289,26 @@ export function getMultiTargetCliMcpTools(input) {
|
|
|
1256
1289
|
export function generateMultiTargetSkills(input) {
|
|
1257
1290
|
const { toolName, builtinNames, targets } = input;
|
|
1258
1291
|
const files = [];
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
`${toolName} ${builtinNames.context} use <name>`,
|
|
1263
|
-
`${toolName} ${builtinNames.context} current`,
|
|
1264
|
-
`${toolName} ${builtinNames.context} delete <name>`,
|
|
1265
|
-
];
|
|
1292
|
+
// Generate one skill per target, plus a shared cli-common skill for context/auth
|
|
1293
|
+
const commonSkillName = 'cli-common';
|
|
1294
|
+
const commonReferenceNames = [];
|
|
1266
1295
|
const contextCreateFlags = targets
|
|
1267
1296
|
.map((t) => `--${t.name}-endpoint <url>`)
|
|
1268
1297
|
.join(' ');
|
|
1298
|
+
// Context reference
|
|
1299
|
+
commonReferenceNames.push('context');
|
|
1269
1300
|
files.push({
|
|
1270
|
-
fileName:
|
|
1271
|
-
content:
|
|
1272
|
-
|
|
1301
|
+
fileName: `${commonSkillName}/references/context.md`,
|
|
1302
|
+
content: buildSkillReference({
|
|
1303
|
+
title: 'Context Management',
|
|
1273
1304
|
description: `Manage API endpoint contexts for ${toolName} (multi-target: ${targets.map((t) => t.name).join(', ')})`,
|
|
1274
|
-
usage:
|
|
1305
|
+
usage: [
|
|
1306
|
+
`${toolName} ${builtinNames.context} create <name>`,
|
|
1307
|
+
`${toolName} ${builtinNames.context} list`,
|
|
1308
|
+
`${toolName} ${builtinNames.context} use <name>`,
|
|
1309
|
+
`${toolName} ${builtinNames.context} current`,
|
|
1310
|
+
`${toolName} ${builtinNames.context} delete <name>`,
|
|
1311
|
+
],
|
|
1275
1312
|
examples: [
|
|
1276
1313
|
{
|
|
1277
1314
|
description: 'Create a context for local development (accept all defaults)',
|
|
@@ -1287,20 +1324,15 @@ export function generateMultiTargetSkills(input) {
|
|
|
1287
1324
|
`${toolName} ${builtinNames.context} use production`,
|
|
1288
1325
|
],
|
|
1289
1326
|
},
|
|
1290
|
-
{
|
|
1291
|
-
description: 'List and switch contexts',
|
|
1292
|
-
code: [
|
|
1293
|
-
`${toolName} ${builtinNames.context} list`,
|
|
1294
|
-
`${toolName} ${builtinNames.context} use staging`,
|
|
1295
|
-
],
|
|
1296
|
-
},
|
|
1297
1327
|
],
|
|
1298
1328
|
}),
|
|
1299
1329
|
});
|
|
1330
|
+
// Auth reference
|
|
1331
|
+
commonReferenceNames.push('auth');
|
|
1300
1332
|
files.push({
|
|
1301
|
-
fileName:
|
|
1302
|
-
content:
|
|
1303
|
-
|
|
1333
|
+
fileName: `${commonSkillName}/references/auth.md`,
|
|
1334
|
+
content: buildSkillReference({
|
|
1335
|
+
title: 'Authentication',
|
|
1304
1336
|
description: `Manage authentication tokens for ${toolName} (shared across all targets)`,
|
|
1305
1337
|
usage: [
|
|
1306
1338
|
`${toolName} ${builtinNames.auth} set-token <token>`,
|
|
@@ -1319,17 +1351,48 @@ export function generateMultiTargetSkills(input) {
|
|
|
1319
1351
|
],
|
|
1320
1352
|
}),
|
|
1321
1353
|
});
|
|
1354
|
+
// Common SKILL.md
|
|
1355
|
+
files.push({
|
|
1356
|
+
fileName: `${commonSkillName}/SKILL.md`,
|
|
1357
|
+
content: buildSkillFile({
|
|
1358
|
+
name: commonSkillName,
|
|
1359
|
+
description: `Shared CLI utilities for ${toolName} — context management and authentication across targets: ${targets.map((t) => t.name).join(', ')}`,
|
|
1360
|
+
usage: [
|
|
1361
|
+
`# Context management`,
|
|
1362
|
+
`${toolName} ${builtinNames.context} create <name>`,
|
|
1363
|
+
`${toolName} ${builtinNames.context} use <name>`,
|
|
1364
|
+
'',
|
|
1365
|
+
`# Authentication`,
|
|
1366
|
+
`${toolName} ${builtinNames.auth} set-token <token>`,
|
|
1367
|
+
`${toolName} ${builtinNames.auth} status`,
|
|
1368
|
+
],
|
|
1369
|
+
examples: [
|
|
1370
|
+
{
|
|
1371
|
+
description: 'Set up and authenticate',
|
|
1372
|
+
code: [
|
|
1373
|
+
`${toolName} ${builtinNames.context} create local`,
|
|
1374
|
+
`${toolName} ${builtinNames.context} use local`,
|
|
1375
|
+
`${toolName} ${builtinNames.auth} set-token <token>`,
|
|
1376
|
+
],
|
|
1377
|
+
},
|
|
1378
|
+
],
|
|
1379
|
+
}, commonReferenceNames),
|
|
1380
|
+
});
|
|
1381
|
+
// Generate one skill per target with table/op references
|
|
1322
1382
|
for (const tgt of targets) {
|
|
1383
|
+
const tgtSkillName = `cli-${tgt.name}`;
|
|
1384
|
+
const tgtReferenceNames = [];
|
|
1323
1385
|
for (const table of tgt.tables) {
|
|
1324
1386
|
const { singularName } = getTableNames(table);
|
|
1325
1387
|
const kebab = toKebabCase(singularName);
|
|
1326
1388
|
const pk = getPrimaryKeyInfo(table)[0];
|
|
1327
1389
|
const editableFields = getEditableFields(table);
|
|
1328
1390
|
const cmd = `${tgt.name}:${kebab}`;
|
|
1391
|
+
tgtReferenceNames.push(kebab);
|
|
1329
1392
|
files.push({
|
|
1330
|
-
fileName:
|
|
1331
|
-
content:
|
|
1332
|
-
|
|
1393
|
+
fileName: `${tgtSkillName}/references/${kebab}.md`,
|
|
1394
|
+
content: buildSkillReference({
|
|
1395
|
+
title: singularName,
|
|
1333
1396
|
description: `CRUD operations for ${table.name} records via ${toolName} CLI (${tgt.name} target)`,
|
|
1334
1397
|
usage: [
|
|
1335
1398
|
`${toolName} ${cmd} list`,
|
|
@@ -1349,10 +1412,6 @@ export function generateMultiTargetSkills(input) {
|
|
|
1349
1412
|
`${toolName} ${cmd} create ${editableFields.map((f) => `--${f.name} "value"`).join(' ')}`,
|
|
1350
1413
|
],
|
|
1351
1414
|
},
|
|
1352
|
-
{
|
|
1353
|
-
description: `Get a ${singularName} by ${pk.name}`,
|
|
1354
|
-
code: [`${toolName} ${cmd} get --${pk.name} <value>`],
|
|
1355
|
-
},
|
|
1356
1415
|
],
|
|
1357
1416
|
}),
|
|
1358
1417
|
});
|
|
@@ -1367,10 +1426,11 @@ export function generateMultiTargetSkills(input) {
|
|
|
1367
1426
|
if (tgt.isAuthTarget && op.kind === 'mutation') {
|
|
1368
1427
|
usageLines.push(`${baseUsage} --save-token`);
|
|
1369
1428
|
}
|
|
1429
|
+
tgtReferenceNames.push(kebab);
|
|
1370
1430
|
files.push({
|
|
1371
|
-
fileName:
|
|
1372
|
-
content:
|
|
1373
|
-
|
|
1431
|
+
fileName: `${tgtSkillName}/references/${kebab}.md`,
|
|
1432
|
+
content: buildSkillReference({
|
|
1433
|
+
title: op.name,
|
|
1374
1434
|
description: `${op.description || `Execute the ${op.name} ${op.kind}`} (${tgt.name} target)`,
|
|
1375
1435
|
usage: usageLines,
|
|
1376
1436
|
examples: [
|
|
@@ -1382,6 +1442,29 @@ export function generateMultiTargetSkills(input) {
|
|
|
1382
1442
|
}),
|
|
1383
1443
|
});
|
|
1384
1444
|
}
|
|
1445
|
+
// Target SKILL.md
|
|
1446
|
+
const firstKebab = tgt.tables.length > 0
|
|
1447
|
+
? toKebabCase(getTableNames(tgt.tables[0]).singularName)
|
|
1448
|
+
: 'model';
|
|
1449
|
+
files.push({
|
|
1450
|
+
fileName: `${tgtSkillName}/SKILL.md`,
|
|
1451
|
+
content: buildSkillFile({
|
|
1452
|
+
name: tgtSkillName,
|
|
1453
|
+
description: `CLI commands for the ${tgt.name} API target — ${tgt.tables.length} tables and ${tgt.customOperations.length} custom operations via ${toolName}`,
|
|
1454
|
+
usage: [
|
|
1455
|
+
`# CRUD for ${tgt.name} tables (e.g. ${firstKebab})`,
|
|
1456
|
+
`${toolName} ${tgt.name}:${firstKebab} list`,
|
|
1457
|
+
`${toolName} ${tgt.name}:${firstKebab} get --id <value>`,
|
|
1458
|
+
`${toolName} ${tgt.name}:${firstKebab} create --<field> <value>`,
|
|
1459
|
+
],
|
|
1460
|
+
examples: [
|
|
1461
|
+
{
|
|
1462
|
+
description: `Query ${tgt.name} records`,
|
|
1463
|
+
code: [`${toolName} ${tgt.name}:${firstKebab} list`],
|
|
1464
|
+
},
|
|
1465
|
+
],
|
|
1466
|
+
}, tgtReferenceNames),
|
|
1467
|
+
});
|
|
1385
1468
|
}
|
|
1386
1469
|
return files;
|
|
1387
1470
|
}
|
|
@@ -20,6 +20,16 @@ export interface SkillDefinition {
|
|
|
20
20
|
}[];
|
|
21
21
|
language?: string;
|
|
22
22
|
}
|
|
23
|
+
export interface SkillReferenceDefinition {
|
|
24
|
+
title: string;
|
|
25
|
+
description: string;
|
|
26
|
+
usage: string[];
|
|
27
|
+
examples: {
|
|
28
|
+
description: string;
|
|
29
|
+
code: string[];
|
|
30
|
+
}[];
|
|
31
|
+
language?: string;
|
|
32
|
+
}
|
|
23
33
|
export declare function getReadmeHeader(title: string): string[];
|
|
24
34
|
export declare function getReadmeFooter(): string[];
|
|
25
35
|
export declare function resolveDocsConfig(docs: DocsConfig | boolean | undefined): DocsConfig;
|
|
@@ -27,4 +37,5 @@ export declare function formatArgType(arg: CleanOperation['args'][number]): stri
|
|
|
27
37
|
export declare function formatTypeRef(t: CleanOperation['args'][number]['type']): string;
|
|
28
38
|
export declare function getEditableFields(table: CleanTable): CleanField[];
|
|
29
39
|
export declare function gqlTypeToJsonSchemaType(gqlType: string): string;
|
|
30
|
-
export declare function buildSkillFile(skill: SkillDefinition): string;
|
|
40
|
+
export declare function buildSkillFile(skill: SkillDefinition, referenceNames?: string[]): string;
|
|
41
|
+
export declare function buildSkillReference(ref: SkillReferenceDefinition): string;
|
|
@@ -79,9 +79,15 @@ export function gqlTypeToJsonSchemaType(gqlType) {
|
|
|
79
79
|
return 'string';
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
-
export function buildSkillFile(skill) {
|
|
82
|
+
export function buildSkillFile(skill, referenceNames) {
|
|
83
83
|
const lang = skill.language ?? 'bash';
|
|
84
84
|
const lines = [];
|
|
85
|
+
// YAML frontmatter (Agent Skills format)
|
|
86
|
+
lines.push('---');
|
|
87
|
+
lines.push(`name: ${skill.name}`);
|
|
88
|
+
lines.push(`description: ${skill.description}`);
|
|
89
|
+
lines.push('---');
|
|
90
|
+
lines.push('');
|
|
85
91
|
lines.push(`# ${skill.name}`);
|
|
86
92
|
lines.push('');
|
|
87
93
|
lines.push('<!-- @constructive-io/graphql-codegen - DO NOT EDIT -->');
|
|
@@ -108,5 +114,46 @@ export function buildSkillFile(skill) {
|
|
|
108
114
|
lines.push('```');
|
|
109
115
|
lines.push('');
|
|
110
116
|
}
|
|
117
|
+
if (referenceNames && referenceNames.length > 0) {
|
|
118
|
+
lines.push('## References');
|
|
119
|
+
lines.push('');
|
|
120
|
+
lines.push('See the `references/` directory for detailed per-entity API documentation:');
|
|
121
|
+
lines.push('');
|
|
122
|
+
for (const name of referenceNames) {
|
|
123
|
+
lines.push(`- [${name}](references/${name}.md)`);
|
|
124
|
+
}
|
|
125
|
+
lines.push('');
|
|
126
|
+
}
|
|
127
|
+
return lines.join('\n');
|
|
128
|
+
}
|
|
129
|
+
export function buildSkillReference(ref) {
|
|
130
|
+
const lang = ref.language ?? 'bash';
|
|
131
|
+
const lines = [];
|
|
132
|
+
lines.push(`# ${ref.title}`);
|
|
133
|
+
lines.push('');
|
|
134
|
+
lines.push('<!-- @constructive-io/graphql-codegen - DO NOT EDIT -->');
|
|
135
|
+
lines.push('');
|
|
136
|
+
lines.push(ref.description);
|
|
137
|
+
lines.push('');
|
|
138
|
+
lines.push('## Usage');
|
|
139
|
+
lines.push('');
|
|
140
|
+
lines.push(`\`\`\`${lang}`);
|
|
141
|
+
for (const u of ref.usage) {
|
|
142
|
+
lines.push(u);
|
|
143
|
+
}
|
|
144
|
+
lines.push('```');
|
|
145
|
+
lines.push('');
|
|
146
|
+
lines.push('## Examples');
|
|
147
|
+
lines.push('');
|
|
148
|
+
for (const ex of ref.examples) {
|
|
149
|
+
lines.push(`### ${ex.description}`);
|
|
150
|
+
lines.push('');
|
|
151
|
+
lines.push(`\`\`\`${lang}`);
|
|
152
|
+
for (const cmd of ex.code) {
|
|
153
|
+
lines.push(cmd);
|
|
154
|
+
}
|
|
155
|
+
lines.push('```');
|
|
156
|
+
lines.push('');
|
|
157
|
+
}
|
|
111
158
|
return lines.join('\n');
|
|
112
159
|
}
|
|
@@ -3,4 +3,4 @@ import type { GeneratedDocFile, McpTool } from './docs-utils';
|
|
|
3
3
|
export declare function generateHooksReadme(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
|
|
4
4
|
export declare function generateHooksAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
|
|
5
5
|
export declare function getHooksMcpTools(tables: CleanTable[], customOperations: CleanOperation[]): McpTool[];
|
|
6
|
-
export declare function generateHooksSkills(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile[];
|
|
6
|
+
export declare function generateHooksSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string): GeneratedDocFile[];
|