diff --git a/graphile/graphile-authz/package.json b/graphile/graphile-authz/package.json
index 7319d4ab0..1e185811a 100644
--- a/graphile/graphile-authz/package.json
+++ b/graphile/graphile-authz/package.json
@@ -25,7 +25,7 @@
"build": "makage build",
"build:dev": "makage build --dev",
"lint": "eslint . --fix",
- "test": "jest --passWithNoTests",
+ "test": "jest",
"test:watch": "jest --watch"
},
"devDependencies": {
diff --git a/graphile/graphile-query/package.json b/graphile/graphile-query/package.json
index 2a3c3675a..c03309d52 100644
--- a/graphile/graphile-query/package.json
+++ b/graphile/graphile-query/package.json
@@ -25,7 +25,7 @@
"build": "makage build",
"build:dev": "makage build --dev",
"lint": "eslint . --fix",
- "test": "jest --passWithNoTests",
+ "test": "jest",
"test:watch": "jest --watch"
},
"dependencies": {
diff --git a/graphile/graphile-schema/README.md b/graphile/graphile-schema/README.md
new file mode 100644
index 000000000..4049037ee
--- /dev/null
+++ b/graphile/graphile-schema/README.md
@@ -0,0 +1,97 @@
+# graphile-schema
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Lightweight GraphQL SDL builder for PostgreSQL using PostGraphile v5. Build schemas directly from a database or fetch them from a running GraphQL endpoint — no server dependencies required.
+
+## Installation
+
+```bash
+npm install graphile-schema
+```
+
+## Usage
+
+### Build SDL from a PostgreSQL Database
+
+```typescript
+import { buildSchemaSDL } from 'graphile-schema';
+
+const sdl = await buildSchemaSDL({
+ database: 'mydb',
+ schemas: ['app_public'],
+});
+
+console.log(sdl);
+```
+
+### Fetch SDL from a GraphQL Endpoint
+
+```typescript
+import { fetchEndpointSchemaSDL } from 'graphile-schema';
+
+const sdl = await fetchEndpointSchemaSDL('https://api.example.com/graphql', {
+ auth: 'Bearer my-token',
+ headers: { 'X-Custom-Header': 'value' },
+});
+
+console.log(sdl);
+```
+
+### With Custom Graphile Presets
+
+```typescript
+import { buildSchemaSDL } from 'graphile-schema';
+
+const sdl = await buildSchemaSDL({
+ database: 'mydb',
+ schemas: ['app_public', 'app_private'],
+ graphile: {
+ extends: [MyCustomPreset],
+ schema: { pgSimplifyPatch: false },
+ },
+});
+```
+
+## API
+
+### `buildSchemaSDL(opts)`
+
+Builds a GraphQL SDL string directly from a PostgreSQL database using PostGraphile v5 introspection.
+
+| Option | Type | Description |
+|--------|------|-------------|
+| `database` | `string` | Database name (default: `'constructive'`) |
+| `schemas` | `string[]` | PostgreSQL schemas to introspect |
+| `graphile` | `Partial` | Optional Graphile preset overrides |
+
+### `fetchEndpointSchemaSDL(endpoint, opts?)`
+
+Fetches a GraphQL SDL string from a running GraphQL endpoint via introspection query.
+
+| Option | Type | Description |
+|--------|------|-------------|
+| `endpoint` | `string` | GraphQL endpoint URL |
+| `opts.headerHost` | `string` | Override the `Host` header |
+| `opts.auth` | `string` | `Authorization` header value |
+| `opts.headers` | `Record` | Additional request headers |
+
+## Disclaimer
+
+AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
+
+No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
diff --git a/graphile/graphile-schema/__tests__/fetch-endpoint-schema.test.ts b/graphile/graphile-schema/__tests__/fetch-endpoint-schema.test.ts
new file mode 100644
index 000000000..c94afba81
--- /dev/null
+++ b/graphile/graphile-schema/__tests__/fetch-endpoint-schema.test.ts
@@ -0,0 +1,109 @@
+import * as http from 'node:http';
+import { getIntrospectionQuery, buildSchema, introspectionFromSchema } from 'graphql';
+
+import { fetchEndpointSchemaSDL } from '../src/fetch-endpoint-schema';
+
+const TEST_SDL = `
+ type Query {
+ hello: String
+ version: Int
+ }
+`;
+
+function createMockServer(handler: (req: http.IncomingMessage, res: http.ServerResponse) => void): Promise<{ server: http.Server; port: number }> {
+ return new Promise((resolve) => {
+ const server = http.createServer(handler);
+ server.listen(0, '127.0.0.1', () => {
+ const addr = server.address() as { port: number };
+ resolve({ server, port: addr.port });
+ });
+ });
+}
+
+function introspectionHandler(_req: http.IncomingMessage, res: http.ServerResponse) {
+ const schema = buildSchema(TEST_SDL);
+ const introspection = introspectionFromSchema(schema);
+ res.writeHead(200, { 'Content-Type': 'application/json' });
+ res.end(JSON.stringify({ data: introspection }));
+}
+
+describe('fetchEndpointSchemaSDL', () => {
+ let server: http.Server;
+ let port: number;
+
+ beforeAll(async () => {
+ ({ server, port } = await createMockServer(introspectionHandler));
+ });
+
+ afterAll(() => {
+ server.close();
+ });
+
+ it('fetches and returns SDL from a live endpoint', async () => {
+ const sdl = await fetchEndpointSchemaSDL(`http://127.0.0.1:${port}/graphql`);
+
+ expect(sdl).toContain('type Query');
+ expect(sdl).toContain('hello');
+ expect(sdl).toContain('version');
+ });
+
+ it('throws on HTTP error responses', async () => {
+ const { server: errServer, port: errPort } = await createMockServer((_req, res) => {
+ res.writeHead(500);
+ res.end('Internal Server Error');
+ });
+
+ await expect(
+ fetchEndpointSchemaSDL(`http://127.0.0.1:${errPort}/graphql`),
+ ).rejects.toThrow('HTTP 500');
+
+ errServer.close();
+ });
+
+ it('throws on invalid JSON response', async () => {
+ const { server: badServer, port: badPort } = await createMockServer((_req, res) => {
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end('not json');
+ });
+
+ await expect(
+ fetchEndpointSchemaSDL(`http://127.0.0.1:${badPort}/graphql`),
+ ).rejects.toThrow('Failed to parse response');
+
+ badServer.close();
+ });
+
+ it('throws when introspection returns errors', async () => {
+ const { server: errServer, port: errPort } = await createMockServer((_req, res) => {
+ res.writeHead(200, { 'Content-Type': 'application/json' });
+ res.end(JSON.stringify({ errors: [{ message: 'Not allowed' }] }));
+ });
+
+ await expect(
+ fetchEndpointSchemaSDL(`http://127.0.0.1:${errPort}/graphql`),
+ ).rejects.toThrow('Introspection returned errors');
+
+ errServer.close();
+ });
+
+ it('passes custom headers to the endpoint', async () => {
+ let receivedHeaders: http.IncomingHttpHeaders = {};
+
+ const { server: headerServer, port: headerPort } = await createMockServer((req, res) => {
+ receivedHeaders = req.headers;
+ introspectionHandler(req, res);
+ });
+
+ await fetchEndpointSchemaSDL(`http://127.0.0.1:${headerPort}/graphql`, {
+ auth: 'Bearer test-token',
+ headerHost: 'custom.host.io',
+ headers: { 'X-Custom': 'value123' },
+ });
+
+ expect(receivedHeaders['authorization']).toBe('Bearer test-token');
+ expect(receivedHeaders['host']).toBe('custom.host.io');
+ expect(receivedHeaders['x-custom']).toBe('value123');
+
+ headerServer.close();
+ });
+});
diff --git a/graphile/graphile-schema/jest.config.js b/graphile/graphile-schema/jest.config.js
new file mode 100644
index 000000000..057a9420e
--- /dev/null
+++ b/graphile/graphile-schema/jest.config.js
@@ -0,0 +1,18 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+ preset: 'ts-jest',
+ testEnvironment: 'node',
+ transform: {
+ '^.+\\.tsx?$': [
+ 'ts-jest',
+ {
+ babelConfig: false,
+ tsconfig: 'tsconfig.json',
+ },
+ ],
+ },
+ transformIgnorePatterns: [`/node_modules/*`],
+ testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
+ modulePathIgnorePatterns: ['dist/*']
+};
diff --git a/graphile/graphile-schema/package.json b/graphile/graphile-schema/package.json
new file mode 100644
index 000000000..f9ab9b29c
--- /dev/null
+++ b/graphile/graphile-schema/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "graphile-schema",
+ "version": "1.0.0",
+ "author": "Constructive ",
+ "description": "Build GraphQL SDL from PostgreSQL databases using PostGraphile v5",
+ "main": "index.js",
+ "module": "esm/index.js",
+ "types": "index.d.ts",
+ "homepage": "https://github.com/constructive-io/constructive",
+ "license": "MIT",
+ "publishConfig": {
+ "access": "public",
+ "directory": "dist"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/constructive-io/constructive"
+ },
+ "bugs": {
+ "url": "https://github.com/constructive-io/constructive/issues"
+ },
+ "scripts": {
+ "clean": "makage clean",
+ "prepack": "npm run build",
+ "build": "makage build",
+ "build:dev": "makage build --dev",
+ "lint": "eslint . --fix",
+ "test": "jest",
+ "test:watch": "jest --watch"
+ },
+ "dependencies": {
+ "deepmerge": "^4.3.1",
+ "graphile-build": "^5.0.0-rc.3",
+ "graphile-config": "1.0.0-rc.3",
+ "graphile-settings": "workspace:^",
+ "graphql": "^16.9.0",
+ "pg-cache": "workspace:^",
+ "pg-env": "workspace:^"
+ },
+ "devDependencies": {
+ "makage": "^0.1.10",
+ "ts-node": "^10.9.2"
+ },
+ "keywords": [
+ "graphile",
+ "schema",
+ "graphql",
+ "sdl",
+ "postgraphile",
+ "introspection",
+ "constructive"
+ ]
+}
diff --git a/graphile/graphile-schema/src/build-schema.ts b/graphile/graphile-schema/src/build-schema.ts
new file mode 100644
index 000000000..34cf5b3ab
--- /dev/null
+++ b/graphile/graphile-schema/src/build-schema.ts
@@ -0,0 +1,44 @@
+import deepmerge from 'deepmerge'
+import { printSchema } from 'graphql'
+import { ConstructivePreset, makePgService } from 'graphile-settings'
+import { makeSchema } from 'graphile-build'
+import { buildConnectionString } from 'pg-cache'
+import { getPgEnvOptions } from 'pg-env'
+import type { GraphileConfig } from 'graphile-config'
+
+export type BuildSchemaOptions = {
+ database?: string;
+ schemas: string[];
+ graphile?: Partial;
+};
+
+export async function buildSchemaSDL(opts: BuildSchemaOptions): Promise {
+ const database = opts.database ?? 'constructive'
+ const schemas = Array.isArray(opts.schemas) ? opts.schemas : []
+
+ const config = getPgEnvOptions({ database })
+ const connectionString = buildConnectionString(
+ config.user,
+ config.password,
+ config.host,
+ config.port,
+ config.database,
+ )
+
+ const basePreset: GraphileConfig.Preset = {
+ extends: [ConstructivePreset],
+ pgServices: [
+ makePgService({
+ connectionString,
+ schemas,
+ }),
+ ],
+ }
+
+ const preset: GraphileConfig.Preset = opts.graphile
+ ? deepmerge(basePreset, opts.graphile)
+ : basePreset
+
+ const { schema } = await makeSchema(preset)
+ return printSchema(schema)
+}
diff --git a/graphql/server/src/schema.ts b/graphile/graphile-schema/src/fetch-endpoint-schema.ts
similarity index 52%
rename from graphql/server/src/schema.ts
rename to graphile/graphile-schema/src/fetch-endpoint-schema.ts
index 29daf8305..11a8d33b9 100644
--- a/graphql/server/src/schema.ts
+++ b/graphile/graphile-schema/src/fetch-endpoint-schema.ts
@@ -1,51 +1,14 @@
-import { printSchema, getIntrospectionQuery, buildClientSchema } from 'graphql'
-import { ConstructivePreset, makePgService } from 'graphile-settings'
-import { makeSchema } from 'graphile-build'
-import { getPgPool } from 'pg-cache'
-import type { GraphileConfig } from 'graphile-config'
+import { getIntrospectionQuery, buildClientSchema, printSchema } from 'graphql'
import * as http from 'node:http'
import * as https from 'node:https'
-export type BuildSchemaOptions = {
- database?: string;
- schemas: string[];
- graphile?: Partial;
+export type FetchEndpointSchemaOptions = {
+ headerHost?: string;
+ headers?: Record;
+ auth?: string;
};
-// Build GraphQL Schema SDL directly from Postgres using PostGraphile v5, without HTTP.
-export async function buildSchemaSDL(opts: BuildSchemaOptions): Promise {
- const database = opts.database ?? 'constructive'
- const schemas = Array.isArray(opts.schemas) ? opts.schemas : []
-
- // Get pool config for connection string
- const pool = getPgPool({ database })
- const poolConfig = (pool as any).options || {}
- const connectionString = `postgres://${poolConfig.user || 'postgres'}:${poolConfig.password || ''}@${poolConfig.host || 'localhost'}:${poolConfig.port || 5432}/${database}`
-
- // Build v5 preset
- const preset: GraphileConfig.Preset = {
- extends: [
- ConstructivePreset,
- ...(opts.graphile?.extends ?? []),
- ],
- ...(opts.graphile?.disablePlugins && { disablePlugins: opts.graphile.disablePlugins }),
- ...(opts.graphile?.plugins && { plugins: opts.graphile.plugins }),
- ...(opts.graphile?.schema && { schema: opts.graphile.schema }),
- pgServices: [
- makePgService({
- connectionString,
- schemas,
- }),
- ],
- }
-
- const { schema } = await makeSchema(preset)
- return printSchema(schema)
-}
-
-// Fetch GraphQL Schema SDL from a running GraphQL endpoint via introspection.
-// This centralizes GraphQL client usage in the server package to avoid duplicating deps in the CLI.
-export async function fetchEndpointSchemaSDL(endpoint: string, opts?: { headerHost?: string, headers?: Record, auth?: string }): Promise {
+export async function fetchEndpointSchemaSDL(endpoint: string, opts?: FetchEndpointSchemaOptions): Promise {
const url = new URL(endpoint)
const requestUrl = url
diff --git a/graphile/graphile-schema/src/index.ts b/graphile/graphile-schema/src/index.ts
new file mode 100644
index 000000000..840b76710
--- /dev/null
+++ b/graphile/graphile-schema/src/index.ts
@@ -0,0 +1,4 @@
+export { buildSchemaSDL } from './build-schema';
+export type { BuildSchemaOptions } from './build-schema';
+export { fetchEndpointSchemaSDL } from './fetch-endpoint-schema';
+export type { FetchEndpointSchemaOptions } from './fetch-endpoint-schema';
diff --git a/graphile/graphile-schema/tsconfig.esm.json b/graphile/graphile-schema/tsconfig.esm.json
new file mode 100644
index 000000000..d35ab5318
--- /dev/null
+++ b/graphile/graphile-schema/tsconfig.esm.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "dist/esm",
+ "module": "ESNext",
+ "moduleResolution": "bundler"
+ }
+}
diff --git a/graphile/graphile-schema/tsconfig.json b/graphile/graphile-schema/tsconfig.json
new file mode 100644
index 000000000..a54950429
--- /dev/null
+++ b/graphile/graphile-schema/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src",
+ "moduleResolution": "nodenext",
+ "module": "nodenext"
+ },
+ "include": ["src/**/*"]
+}
diff --git a/graphile/graphile-search-plugin/package.json b/graphile/graphile-search-plugin/package.json
index f28b2ce92..a1ba1cb13 100644
--- a/graphile/graphile-search-plugin/package.json
+++ b/graphile/graphile-search-plugin/package.json
@@ -14,7 +14,7 @@
"build": "makage build",
"build:dev": "makage build --dev",
"lint": "eslint . --fix",
- "test": "jest --passWithNoTests",
+ "test": "jest",
"test:watch": "jest --watch"
},
"publishConfig": {
diff --git a/graphile/graphile-settings/package.json b/graphile/graphile-settings/package.json
index eed8207da..44a2f19ec 100644
--- a/graphile/graphile-settings/package.json
+++ b/graphile/graphile-settings/package.json
@@ -25,7 +25,7 @@
"build": "makage build",
"build:dev": "makage build --dev",
"lint": "eslint . --fix",
- "test": "jest --passWithNoTests",
+ "test": "jest",
"test:watch": "jest --watch"
},
"dependencies": {
diff --git a/graphile/graphile-test/package.json b/graphile/graphile-test/package.json
index 08a8698f0..498bc554b 100644
--- a/graphile/graphile-test/package.json
+++ b/graphile/graphile-test/package.json
@@ -25,7 +25,7 @@
"build": "makage build",
"build:dev": "makage build --dev",
"lint": "eslint . --fix",
- "test": "jest --passWithNoTests",
+ "test": "jest",
"test:watch": "jest --watch"
},
"devDependencies": {
diff --git a/graphile/postgraphile-plugin-pgvector/package.json b/graphile/postgraphile-plugin-pgvector/package.json
index 055ec5465..08fd62c69 100644
--- a/graphile/postgraphile-plugin-pgvector/package.json
+++ b/graphile/postgraphile-plugin-pgvector/package.json
@@ -25,7 +25,7 @@
"build": "makage build",
"build:dev": "makage build --dev",
"lint": "eslint . --fix",
- "test": "jest --passWithNoTests",
+ "test": "jest",
"test:watch": "jest --watch"
},
"devDependencies": {
diff --git a/graphql/codegen/package.json b/graphql/codegen/package.json
index ac766470e..bc067f506 100644
--- a/graphql/codegen/package.json
+++ b/graphql/codegen/package.json
@@ -42,7 +42,7 @@
"lint": "eslint . --fix",
"fmt": "oxfmt --write .",
"fmt:check": "oxfmt --check .",
- "test": "jest --passWithNoTests",
+ "test": "jest",
"test:watch": "jest --watch",
"example:codegen:sdk": "tsx src/cli/index.ts --config examples/multi-target.config.ts --react-query",
"example:codegen:orm": "tsx src/cli/index.ts --config examples/multi-target.config.ts --orm",
@@ -56,7 +56,6 @@
"@0no-co/graphql.web": "^1.1.2",
"@babel/generator": "^7.28.6",
"@babel/types": "^7.28.6",
- "@constructive-io/graphql-server": "workspace:^",
"@constructive-io/graphql-types": "workspace:^",
"@inquirerer/utils": "^3.2.3",
"@pgpmjs/core": "workspace:^",
@@ -64,10 +63,12 @@
"deepmerge": "^4.3.1",
"find-and-require-package-json": "^0.9.0",
"gql-ast": "workspace:^",
+ "graphile-schema": "workspace:^",
"graphql": "^16.9.0",
"inflekt": "^0.3.1",
"inquirerer": "^4.4.0",
"jiti": "^2.6.1",
+ "komoji": "^0.8.0",
"oxfmt": "^0.26.0",
"pg-cache": "workspace:^",
"pg-env": "workspace:^",
diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/cli-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/cli-generator.test.ts.snap
new file mode 100644
index 000000000..280608677
--- /dev/null
+++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/cli-generator.test.ts.snap
@@ -0,0 +1,5328 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`cli docs generator generates CLI AGENTS.md 1`] = `
+"# myapp CLI - Agent Reference
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+> This document is structured for LLM/agent consumption.
+
+## OVERVIEW
+
+\`myapp\` is a CLI tool for interacting with a GraphQL API.
+All commands output JSON to stdout. All commands accept \`--help\` or \`-h\` for usage.
+Configuration is stored at \`~/.myapp/config/\` via appstash.
+
+## PREREQUISITES
+
+Before running any data commands, you must:
+
+1. Create a context: \`myapp context create --endpoint \`
+2. Activate it: \`myapp context use \`
+3. Authenticate: \`myapp auth set-token \`
+
+## TOOLS
+
+### TOOL: context
+
+Manage named API endpoint contexts (like kubectl contexts).
+
+\`\`\`
+SUBCOMMANDS:
+ myapp context create --endpoint Create a new context
+ myapp context list List all contexts
+ myapp context use Set active context
+ myapp context current Show active context
+ myapp context delete Delete a context
+
+INPUT:
+ name: string (required) - Context identifier
+ endpoint: string (required for create) - GraphQL endpoint URL
+
+OUTPUT: JSON
+ create: { name, endpoint }
+ list: [{ name, endpoint, isCurrent, hasCredentials }]
+ use: { name, endpoint }
+ current: { name, endpoint }
+ delete: { deleted: name }
+\`\`\`
+
+### TOOL: auth
+
+Manage authentication tokens per context.
+
+\`\`\`
+SUBCOMMANDS:
+ myapp auth set-token Store bearer token for current context
+ myapp auth status Show auth status for all contexts
+ myapp auth logout Remove credentials for current context
+
+INPUT:
+ token: string (required for set-token) - Bearer token value
+
+OUTPUT: JSON
+ set-token: { context, status: "authenticated" }
+ status: [{ context, authenticated: boolean }]
+ logout: { context, status: "logged out" }
+\`\`\`
+
+### TOOL: car
+
+CRUD operations for Car records.
+
+\`\`\`
+SUBCOMMANDS:
+ myapp car list List all records
+ myapp car get --id Get one record
+ myapp car create --make --model --year --isElectric
+ myapp car update --id [--make ] [--model ] [--year ] [--isElectric ]
+ myapp car delete --id Delete one record
+
+INPUT FIELDS:
+ id: UUID (primary key)
+ make: String
+ model: String
+ year: Int
+ isElectric: Boolean
+ createdAt: Datetime
+
+EDITABLE FIELDS (for create/update):
+ make: String
+ model: String
+ year: Int
+ isElectric: Boolean
+
+OUTPUT: JSON
+ list: [{ id, make, model, year, isElectric, createdAt }]
+ get: { id, make, model, year, isElectric, createdAt }
+ create: { id, make, model, year, isElectric, createdAt }
+ update: { id, make, model, year, isElectric, createdAt }
+ delete: { id }
+\`\`\`
+
+### TOOL: driver
+
+CRUD operations for Driver records.
+
+\`\`\`
+SUBCOMMANDS:
+ myapp driver list List all records
+ myapp driver get --id Get one record
+ myapp driver create --name --licenseNumber
+ myapp driver update --id [--name ] [--licenseNumber ]
+ myapp driver delete --id Delete one record
+
+INPUT FIELDS:
+ id: UUID (primary key)
+ name: String
+ licenseNumber: String
+
+EDITABLE FIELDS (for create/update):
+ name: String
+ licenseNumber: String
+
+OUTPUT: JSON
+ list: [{ id, name, licenseNumber }]
+ get: { id, name, licenseNumber }
+ create: { id, name, licenseNumber }
+ update: { id, name, licenseNumber }
+ delete: { id }
+\`\`\`
+
+### TOOL: current-user
+
+Get the currently authenticated user
+
+\`\`\`
+TYPE: query
+USAGE: myapp current-user
+
+INPUT: none
+
+OUTPUT: JSON
+\`\`\`
+
+### TOOL: login
+
+Authenticate a user
+
+\`\`\`
+TYPE: mutation
+USAGE: myapp login --email --password
+
+INPUT:
+ email: String (required)
+ password: String (required)
+
+OUTPUT: JSON
+\`\`\`
+
+## WORKFLOWS
+
+### Initial setup
+
+\`\`\`bash
+myapp context create dev --endpoint http://localhost:5000/graphql
+myapp context use dev
+myapp auth set-token eyJhbGciOiJIUzI1NiIs...
+\`\`\`
+
+### CRUD workflow (car)
+
+\`\`\`bash
+# List all
+myapp car list
+
+# Create
+myapp car create --make "value" --model "value" --year "value" --isElectric "value"
+
+# Get by id
+myapp car get --id
+
+# Update
+myapp car update --id --make "new-value"
+
+# Delete
+myapp car delete --id
+\`\`\`
+
+### Piping output
+
+\`\`\`bash
+# Pretty print
+myapp car list | jq '.'
+
+# Extract field
+myapp car list | jq '.[].id'
+
+# Count results
+myapp car list | jq 'length'
+\`\`\`
+
+## ERROR HANDLING
+
+All errors are written to stderr. Exit codes:
+- \`0\`: Success
+- \`1\`: Error (auth failure, not found, validation error, network error)
+
+Common errors:
+- "No active context": Run \`context use \` first
+- "Not authenticated": Run \`auth set-token \` first
+- "Record not found": The requested ID does not exist
+"
+`;
+
+exports[`cli docs generator generates CLI README 1`] = `
+"# myapp CLI
+
+
+
+
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+## Setup
+
+\`\`\`bash
+# Create a context pointing at your GraphQL endpoint
+myapp context create production --endpoint https://api.example.com/graphql
+
+# Set the active context
+myapp context use production
+
+# Authenticate
+myapp auth set-token
+\`\`\`
+
+## Commands
+
+| Command | Description |
+|---------|-------------|
+| \`context\` | Manage API contexts (endpoints) |
+| \`auth\` | Manage authentication tokens |
+| \`car\` | car CRUD operations |
+| \`driver\` | driver CRUD operations |
+| \`current-user\` | Get the currently authenticated user |
+| \`login\` | Authenticate a user |
+
+## Infrastructure Commands
+
+### \`context\`
+
+Manage named API contexts (kubectl-style).
+
+| Subcommand | Description |
+|------------|-------------|
+| \`create --endpoint \` | Create a new context |
+| \`list\` | List all contexts |
+| \`use \` | Set the active context |
+| \`current\` | Show current context |
+| \`delete \` | Delete a context |
+
+Configuration is stored at \`~/.myapp/config/\`.
+
+### \`auth\`
+
+Manage authentication tokens per context.
+
+| Subcommand | Description |
+|------------|-------------|
+| \`set-token \` | Store bearer token for current context |
+| \`status\` | Show auth status across all contexts |
+| \`logout\` | Remove credentials for current context |
+
+## Table Commands
+
+### \`car\`
+
+CRUD operations for Car records.
+
+| Subcommand | Description |
+|------------|-------------|
+| \`list\` | List all car records |
+| \`get\` | Get a car by id |
+| \`create\` | Create a new car |
+| \`update\` | Update an existing car |
+| \`delete\` | Delete a car |
+
+**Fields:**
+
+| Field | Type |
+|-------|------|
+| \`id\` | UUID |
+| \`make\` | String |
+| \`model\` | String |
+| \`year\` | Int |
+| \`isElectric\` | Boolean |
+| \`createdAt\` | Datetime |
+
+**Create fields:** \`make\`, \`model\`, \`year\`, \`isElectric\`
+
+### \`driver\`
+
+CRUD operations for Driver records.
+
+| Subcommand | Description |
+|------------|-------------|
+| \`list\` | List all driver records |
+| \`get\` | Get a driver by id |
+| \`create\` | Create a new driver |
+| \`update\` | Update an existing driver |
+| \`delete\` | Delete a driver |
+
+**Fields:**
+
+| Field | Type |
+|-------|------|
+| \`id\` | UUID |
+| \`name\` | String |
+| \`licenseNumber\` | String |
+
+**Create fields:** \`name\`, \`licenseNumber\`
+
+## Custom Operations
+
+### \`current-user\`
+
+Get the currently authenticated user
+
+- **Type:** query
+- **Arguments:** none
+
+### \`login\`
+
+Authenticate a user
+
+- **Type:** mutation
+- **Arguments:**
+
+ | Argument | Type |
+ |----------|------|
+ | \`email\` | String (required) |
+ | \`password\` | String (required) |
+
+## Output
+
+All commands output JSON to stdout. Pipe to \`jq\` for formatting:
+
+\`\`\`bash
+myapp car list | jq '.[]'
+myapp car get --id | jq '.'
+\`\`\`
+
+---
+
+Built by the [Constructive](https://constructive.io) team.
+
+## Disclaimer
+
+AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
+
+No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
+"
+`;
+
+exports[`cli docs generator generates CLI skill files 1`] = `
+"# myapp-context
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Manage API endpoint contexts for myapp
+
+## Usage
+
+\`\`\`bash
+myapp context create --endpoint
+myapp context list
+myapp context use
+myapp context current
+myapp context delete
+\`\`\`
+
+## Examples
+
+### Create and activate a context
+
+\`\`\`bash
+myapp context create production --endpoint https://api.example.com/graphql
+myapp context use production
+\`\`\`
+
+### List all contexts
+
+\`\`\`bash
+myapp context list
+\`\`\`
+"
+`;
+
+exports[`cli docs generator generates CLI skill files 2`] = `
+"# myapp-auth
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Manage authentication tokens for myapp
+
+## Usage
+
+\`\`\`bash
+myapp auth set-token
+myapp auth status
+myapp auth logout
+\`\`\`
+
+## Examples
+
+### Authenticate with a token
+
+\`\`\`bash
+myapp auth set-token eyJhbGciOiJIUzI1NiIs...
+\`\`\`
+
+### Check auth status
+
+\`\`\`bash
+myapp auth status
+\`\`\`
+"
+`;
+
+exports[`cli docs generator generates CLI skill files 3`] = `
+"# myapp-car
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+CRUD operations for Car records via myapp CLI
+
+## Usage
+
+\`\`\`bash
+myapp car list
+myapp car get --id
+myapp car create --make --model --year --isElectric
+myapp car update --id [--make ] [--model ] [--year ] [--isElectric ]
+myapp car delete --id
+\`\`\`
+
+## Examples
+
+### List all car records
+
+\`\`\`bash
+myapp car list
+\`\`\`
+
+### Create a car
+
+\`\`\`bash
+myapp car create --make "value" --model "value" --year "value" --isElectric "value"
+\`\`\`
+
+### Get a car by id
+
+\`\`\`bash
+myapp car get --id
+\`\`\`
+
+### Update a car
+
+\`\`\`bash
+myapp car update --id --make "new-value"
+\`\`\`
+
+### Delete a car
+
+\`\`\`bash
+myapp car delete --id
+\`\`\`
+"
+`;
+
+exports[`cli docs generator generates CLI skill files 4`] = `
+"# myapp-driver
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+CRUD operations for Driver records via myapp CLI
+
+## Usage
+
+\`\`\`bash
+myapp driver list
+myapp driver get --id
+myapp driver create --name --licenseNumber
+myapp driver update --id [--name ] [--licenseNumber ]
+myapp driver delete --id
+\`\`\`
+
+## Examples
+
+### List all driver records
+
+\`\`\`bash
+myapp driver list
+\`\`\`
+
+### Create a driver
+
+\`\`\`bash
+myapp driver create --name "value" --licenseNumber "value"
+\`\`\`
+
+### Get a driver by id
+
+\`\`\`bash
+myapp driver get --id
+\`\`\`
+
+### Update a driver
+
+\`\`\`bash
+myapp driver update --id --name "new-value"
+\`\`\`
+
+### Delete a driver
+
+\`\`\`bash
+myapp driver delete --id
+\`\`\`
+"
+`;
+
+exports[`cli docs generator generates CLI skill files 5`] = `
+"# myapp-current-user
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Get the currently authenticated user
+
+## Usage
+
+\`\`\`bash
+myapp current-user
+\`\`\`
+
+## Examples
+
+### Run currentUser
+
+\`\`\`bash
+myapp current-user
+\`\`\`
+"
+`;
+
+exports[`cli docs generator generates CLI skill files 6`] = `
+"# myapp-login
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Authenticate a user
+
+## Usage
+
+\`\`\`bash
+myapp login --email --password
+\`\`\`
+
+## Examples
+
+### Run login
+
+\`\`\`bash
+myapp login --email --password
+\`\`\`
+"
+`;
+
+exports[`cli-generator generates commands.ts (command map) 1`] = `
+"/**
+ * CLI command map and entry point
+ * @generated by @constructive-io/graphql-codegen
+ * DO NOT EDIT - changes will be overwritten
+ */
+import { CLIOptions, Inquirerer, extractFirst } from "inquirerer";
+import contextCmd from "./commands/context";
+import authCmd from "./commands/auth";
+import carCmd from "./commands/car";
+import driverCmd from "./commands/driver";
+import currentUserCmd from "./commands/current-user";
+import loginCmd from "./commands/login";
+const createCommandMap = () => ({
+ "context": contextCmd,
+ "auth": authCmd,
+ "car": carCmd,
+ "driver": driverCmd,
+ "current-user": currentUserCmd,
+ "login": loginCmd
+});
+const usage = "\\nmyapp \\n\\nCommands:\\n context Manage API contexts\\n auth Manage authentication\\n car car CRUD operations\\n driver driver CRUD operations\\n current-user Get the currently authenticated user\\n login Authenticate a user\\n\\n --help, -h Show this help message\\n --version, -v Show version\\n";
+export const commands = async (argv: Partial>, prompter: Inquirerer, options: CLIOptions) => {
+ if (argv.help || argv.h) {
+ console.log(usage);
+ process.exit(0);
+ }
+ let {
+ first: command,
+ newArgv
+ } = extractFirst(argv);
+ const commandMap = createCommandMap();
+ if (!command) {
+ const answer = await prompter.prompt(argv, [{
+ type: "autocomplete",
+ name: "command",
+ message: "What do you want to do?",
+ options: Object.keys(commandMap)
+ }]);
+ command = answer.command;
+ }
+ const commandFn = commandMap[command];
+ if (!commandFn) {
+ console.log(usage);
+ console.error(\`Unknown command: \${command}\`);
+ process.exit(1);
+ }
+ await commandFn(newArgv, prompter, options);
+ prompter.close();
+ return argv;
+};"
+`;
+
+exports[`cli-generator generates commands/auth.ts 1`] = `
+"/**
+ * Authentication commands
+ * @generated by @constructive-io/graphql-codegen
+ * DO NOT EDIT - changes will be overwritten
+ */
+import { CLIOptions, Inquirerer, extractFirst } from "inquirerer";
+import { getStore } from "../executor";
+const usage = "\\nmyapp auth \\n\\nCommands:\\n set-token Set API token for the current context\\n status Show authentication status\\n logout Remove credentials for the current context\\n\\nOptions:\\n --context Specify context (defaults to current context)\\n\\n --help, -h Show this help message\\n";
+export default async (argv: Partial>, prompter: Inquirerer, _options: CLIOptions) => {
+ if (argv.help || argv.h) {
+ console.log(usage);
+ process.exit(0);
+ }
+ const store = getStore();
+ const {
+ first: subcommand,
+ newArgv
+ } = extractFirst(argv);
+ if (!subcommand) {
+ const answer = await prompter.prompt(argv, [{
+ type: "autocomplete",
+ name: "subcommand",
+ message: "What do you want to do?",
+ options: ["set-token", "status", "logout"]
+ }]);
+ return handleAuthSubcommand(answer.subcommand, newArgv, prompter, store);
+ }
+ return handleAuthSubcommand(subcommand, newArgv, prompter, store);
+};
+async function handleAuthSubcommand(subcommand: string, argv: Partial>, prompter: Inquirerer, store: ReturnType) {
+ switch (subcommand) {
+ case "set-token":
+ return handleSetToken(argv, prompter, store);
+ case "status":
+ return handleStatus(store);
+ case "logout":
+ return handleLogout(argv, prompter, store);
+ default:
+ console.log(usage);
+ process.exit(1);
+ }
+}
+async function handleSetToken(argv: Partial>, prompter: Inquirerer, store: ReturnType) {
+ const current = store.getCurrentContext();
+ if (!current) {
+ console.error("No active context. Run \\"context create\\" first.");
+ process.exit(1);
+ }
+ const {
+ first: token
+ } = extractFirst(argv);
+ let tokenValue = token;
+ if (!tokenValue) {
+ const answer = await prompter.prompt(argv, [{
+ type: "password",
+ name: "token",
+ message: "API Token",
+ required: true
+ }]);
+ tokenValue = answer.token;
+ }
+ store.setCredentials(current.name, {
+ token: String.call(tokenValue || "").trim()
+ });
+ console.log(\`Token saved for context: \${current.name}\`);
+}
+function handleStatus(store: ReturnType) {
+ const contexts = store.listContexts();
+ const settings = store.loadSettings();
+ if (contexts.length === 0) {
+ console.log("No contexts configured.");
+ return;
+ }
+ console.log("Authentication Status:");
+ for (const ctx of contexts) {
+ const isCurrent = ctx.name === settings.currentContext;
+ const hasAuth = store.hasValidCredentials(ctx.name);
+ const marker = isCurrent ? "* " : " ";
+ const status = hasAuth ? "authenticated" : "no token";
+ console.log(\`\${marker}\${ctx.name} [\${status}]\`);
+ }
+}
+async function handleLogout(argv: Partial>, prompter: Inquirerer, store: ReturnType) {
+ const current = store.getCurrentContext();
+ if (!current) {
+ console.log("No active context.");
+ return;
+ }
+ const confirm = await prompter.prompt(argv, [{
+ type: "confirm",
+ name: "confirm",
+ message: \`Remove credentials for "\${current.name}"?\`,
+ default: false
+ }]);
+ if (!confirm.confirm) {
+ return;
+ }
+ if (store.removeCredentials(current.name)) {
+ console.log(\`Credentials removed for: \${current.name}\`);
+ } else {
+ console.log(\`No credentials found for: \${current.name}\`);
+ }
+}"
+`;
+
+exports[`cli-generator generates commands/car.ts 1`] = `
+"/**
+ * CLI commands for Car
+ * @generated by @constructive-io/graphql-codegen
+ * DO NOT EDIT - changes will be overwritten
+ */
+import { CLIOptions, Inquirerer, extractFirst } from "inquirerer";
+import { getClient } from "../executor";
+const usage = "\\ncar \\n\\nCommands:\\n list List all car records\\n get Get a car by ID\\n create Create a new car\\n update Update an existing car\\n delete Delete a car\\n\\n --help, -h Show this help message\\n";
+export default async (argv: Partial>, prompter: Inquirerer, _options: CLIOptions) => {
+ if (argv.help || argv.h) {
+ console.log(usage);
+ process.exit(0);
+ }
+ const {
+ first: subcommand,
+ newArgv
+ } = extractFirst(argv);
+ if (!subcommand) {
+ const answer = await prompter.prompt(argv, [{
+ type: "autocomplete",
+ name: "subcommand",
+ message: "What do you want to do?",
+ options: ["list", "get", "create", "update", "delete"]
+ }]);
+ return handleTableSubcommand(answer.subcommand, newArgv, prompter);
+ }
+ return handleTableSubcommand(subcommand, newArgv, prompter);
+};
+async function handleTableSubcommand(subcommand: string, argv: Partial>, prompter: Inquirerer) {
+ switch (subcommand) {
+ case "list":
+ return handleList(argv, prompter);
+ case "get":
+ return handleGet(argv, prompter);
+ case "create":
+ return handleCreate(argv, prompter);
+ case "update":
+ return handleUpdate(argv, prompter);
+ case "delete":
+ return handleDelete(argv, prompter);
+ default:
+ console.log(usage);
+ process.exit(1);
+ }
+}
+async function handleList(_argv: Partial>, _prompter: Inquirerer) {
+ try {
+ const client = getClient();
+ const result = await client.car.findMany({
+ select: {
+ id: true,
+ make: true,
+ model: true,
+ year: true,
+ isElectric: true,
+ createdAt: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed to list records.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}
+async function handleGet(argv: Partial>, prompter: Inquirerer) {
+ try {
+ const answers = await prompter.prompt(argv, [{
+ type: "text",
+ name: "id",
+ message: "id",
+ required: true
+ }]);
+ const client = getClient();
+ const result = await client.car.findOne({
+ id: answers.id,
+ select: {
+ id: true,
+ make: true,
+ model: true,
+ year: true,
+ isElectric: true,
+ createdAt: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Record not found.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}
+async function handleCreate(argv: Partial>, prompter: Inquirerer) {
+ try {
+ const answers = await prompter.prompt(argv, [{
+ type: "text",
+ name: "make",
+ message: "make",
+ required: true
+ }, {
+ type: "text",
+ name: "model",
+ message: "model",
+ required: true
+ }, {
+ type: "text",
+ name: "year",
+ message: "year",
+ required: true
+ }, {
+ type: "text",
+ name: "isElectric",
+ message: "isElectric",
+ required: true
+ }]);
+ const client = getClient();
+ const result = await client.car.create({
+ data: {
+ make: answers.make,
+ model: answers.model,
+ year: answers.year,
+ isElectric: answers.isElectric
+ },
+ select: {
+ id: true,
+ make: true,
+ model: true,
+ year: true,
+ isElectric: true,
+ createdAt: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed to create record.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}
+async function handleUpdate(argv: Partial>, prompter: Inquirerer) {
+ try {
+ const answers = await prompter.prompt(argv, [{
+ type: "text",
+ name: "id",
+ message: "id",
+ required: true
+ }, {
+ type: "text",
+ name: "make",
+ message: "make",
+ required: false
+ }, {
+ type: "text",
+ name: "model",
+ message: "model",
+ required: false
+ }, {
+ type: "text",
+ name: "year",
+ message: "year",
+ required: false
+ }, {
+ type: "text",
+ name: "isElectric",
+ message: "isElectric",
+ required: false
+ }]);
+ const client = getClient();
+ const result = await client.car.update({
+ id: answers.id,
+ data: {
+ make: answers.make,
+ model: answers.model,
+ year: answers.year,
+ isElectric: answers.isElectric
+ },
+ select: {
+ id: true,
+ make: true,
+ model: true,
+ year: true,
+ isElectric: true,
+ createdAt: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed to update record.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}
+async function handleDelete(argv: Partial>, prompter: Inquirerer) {
+ try {
+ const answers = await prompter.prompt(argv, [{
+ type: "text",
+ name: "id",
+ message: "id",
+ required: true
+ }]);
+ const client = getClient();
+ const result = await client.car.delete({
+ id: answers.id,
+ select: {
+ id: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed to delete record.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}"
+`;
+
+exports[`cli-generator generates commands/context.ts 1`] = `
+"/**
+ * Context management commands
+ * @generated by @constructive-io/graphql-codegen
+ * DO NOT EDIT - changes will be overwritten
+ */
+import { CLIOptions, Inquirerer, extractFirst } from "inquirerer";
+import { getStore } from "../executor";
+const usage = "\\nmyapp context \\n\\nCommands:\\n create Create a new context\\n list List all contexts\\n use Set the active context\\n current Show current context\\n delete Delete a context\\n\\nCreate Options:\\n --endpoint GraphQL endpoint URL\\n\\n --help, -h Show this help message\\n";
+export default async (argv: Partial>, prompter: Inquirerer, _options: CLIOptions) => {
+ if (argv.help || argv.h) {
+ console.log(usage);
+ process.exit(0);
+ }
+ const store = getStore();
+ const {
+ first: subcommand,
+ newArgv
+ } = extractFirst(argv);
+ if (!subcommand) {
+ const answer = await prompter.prompt(argv, [{
+ type: "autocomplete",
+ name: "subcommand",
+ message: "What do you want to do?",
+ options: ["create", "list", "use", "current", "delete"]
+ }]);
+ return handleSubcommand(answer.subcommand, newArgv, prompter, store);
+ }
+ return handleSubcommand(subcommand, newArgv, prompter, store);
+};
+async function handleSubcommand(subcommand: string, argv: Partial>, prompter: Inquirerer, store: ReturnType) {
+ switch (subcommand) {
+ case "create":
+ return handleCreate(argv, prompter, store);
+ case "list":
+ return handleList(store);
+ case "use":
+ return handleUse(argv, prompter, store);
+ case "current":
+ return handleCurrent(store);
+ case "delete":
+ return handleDelete(argv, prompter, store);
+ default:
+ console.log(usage);
+ process.exit(1);
+ }
+}
+async function handleCreate(argv: Partial>, prompter: Inquirerer, store: ReturnType) {
+ const {
+ first: name,
+ newArgv: restArgv
+ } = extractFirst(argv);
+ const answers = await prompter.prompt({
+ name,
+ ...restArgv
+ }, [{
+ type: "text",
+ name: "name",
+ message: "Context name",
+ required: true
+ }, {
+ type: "text",
+ name: "endpoint",
+ message: "GraphQL endpoint URL",
+ required: true
+ }]);
+ const contextName = answers.name;
+ const endpoint = answers.endpoint;
+ store.createContext(contextName, {
+ endpoint: endpoint
+ });
+ const settings = store.loadSettings();
+ if (!settings.currentContext) {
+ store.setCurrentContext(contextName);
+ }
+ console.log(\`Created context: \${contextName}\`);
+ console.log(\` Endpoint: \${endpoint}\`);
+}
+function handleList(store: ReturnType) {
+ const contexts = store.listContexts();
+ const settings = store.loadSettings();
+ if (contexts.length === 0) {
+ console.log("No contexts configured.");
+ return;
+ }
+ console.log("Contexts:");
+ for (const ctx of contexts) {
+ const marker = ctx.name === settings.currentContext ? "* " : " ";
+ const authStatus = store.hasValidCredentials(ctx.name) ? "[authenticated]" : "[no token]";
+ console.log(\`\${marker}\${ctx.name} \${authStatus}\`);
+ console.log(\` Endpoint: \${ctx.endpoint}\`);
+ }
+}
+async function handleUse(argv: Partial>, prompter: Inquirerer, store: ReturnType) {
+ const {
+ first: name
+ } = extractFirst(argv);
+ const contexts = store.listContexts();
+ if (contexts.length === 0) {
+ console.log("No contexts configured.");
+ return;
+ }
+ let contextName = name;
+ if (!contextName) {
+ const answer = await prompter.prompt(argv, [{
+ type: "autocomplete",
+ name: "name",
+ message: "Select context",
+ options: contexts.map(c => c.name)
+ }]);
+ contextName = answer.name;
+ }
+ if (store.setCurrentContext(contextName)) {
+ console.log(\`Switched to context: \${contextName}\`);
+ } else {
+ console.error(\`Context "\${contextName}" not found.\`);
+ process.exit(1);
+ }
+}
+function handleCurrent(store: ReturnType) {
+ const current = store.getCurrentContext();
+ if (!current) {
+ console.log("No current context set.");
+ return;
+ }
+ console.log(\`Current context: \${current.name}\`);
+ console.log(\` Endpoint: \${current.endpoint}\`);
+ const hasAuth = store.hasValidCredentials(current.name);
+ console.log(\` Auth: \${hasAuth ? "authenticated" : "not authenticated"}\`);
+}
+async function handleDelete(argv: Partial>, prompter: Inquirerer, store: ReturnType) {
+ const {
+ first: name
+ } = extractFirst(argv);
+ const contexts = store.listContexts();
+ if (contexts.length === 0) {
+ console.log("No contexts configured.");
+ return;
+ }
+ let contextName = name;
+ if (!contextName) {
+ const answer = await prompter.prompt(argv, [{
+ type: "autocomplete",
+ name: "name",
+ message: "Select context to delete",
+ options: contexts.map(c => c.name)
+ }]);
+ contextName = answer.name;
+ }
+ if (store.deleteContext(contextName)) {
+ console.log(\`Deleted context: \${contextName}\`);
+ } else {
+ console.error(\`Context "\${contextName}" not found.\`);
+ process.exit(1);
+ }
+}"
+`;
+
+exports[`cli-generator generates commands/current-user.ts (custom query) 1`] = `
+"/**
+ * CLI command for query currentUser
+ * @generated by @constructive-io/graphql-codegen
+ * DO NOT EDIT - changes will be overwritten
+ */
+import { CLIOptions, Inquirerer } from "inquirerer";
+import { getClient } from "../executor";
+export default async (argv: Partial>, prompter: Inquirerer, _options: CLIOptions) => {
+ try {
+ if (argv.help || argv.h) {
+ console.log("current-user - Get the currently authenticated user\\n\\nUsage: current-user [OPTIONS]\\n");
+ process.exit(0);
+ }
+ const client = getClient();
+ const result = await client.query.currentUser({}).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed: currentUser");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+};"
+`;
+
+exports[`cli-generator generates commands/driver.ts 1`] = `
+"/**
+ * CLI commands for Driver
+ * @generated by @constructive-io/graphql-codegen
+ * DO NOT EDIT - changes will be overwritten
+ */
+import { CLIOptions, Inquirerer, extractFirst } from "inquirerer";
+import { getClient } from "../executor";
+const usage = "\\ndriver \\n\\nCommands:\\n list List all driver records\\n get Get a driver by ID\\n create Create a new driver\\n update Update an existing driver\\n delete Delete a driver\\n\\n --help, -h Show this help message\\n";
+export default async (argv: Partial>, prompter: Inquirerer, _options: CLIOptions) => {
+ if (argv.help || argv.h) {
+ console.log(usage);
+ process.exit(0);
+ }
+ const {
+ first: subcommand,
+ newArgv
+ } = extractFirst(argv);
+ if (!subcommand) {
+ const answer = await prompter.prompt(argv, [{
+ type: "autocomplete",
+ name: "subcommand",
+ message: "What do you want to do?",
+ options: ["list", "get", "create", "update", "delete"]
+ }]);
+ return handleTableSubcommand(answer.subcommand, newArgv, prompter);
+ }
+ return handleTableSubcommand(subcommand, newArgv, prompter);
+};
+async function handleTableSubcommand(subcommand: string, argv: Partial>, prompter: Inquirerer) {
+ switch (subcommand) {
+ case "list":
+ return handleList(argv, prompter);
+ case "get":
+ return handleGet(argv, prompter);
+ case "create":
+ return handleCreate(argv, prompter);
+ case "update":
+ return handleUpdate(argv, prompter);
+ case "delete":
+ return handleDelete(argv, prompter);
+ default:
+ console.log(usage);
+ process.exit(1);
+ }
+}
+async function handleList(_argv: Partial>, _prompter: Inquirerer) {
+ try {
+ const client = getClient();
+ const result = await client.driver.findMany({
+ select: {
+ id: true,
+ name: true,
+ licenseNumber: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed to list records.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}
+async function handleGet(argv: Partial>, prompter: Inquirerer) {
+ try {
+ const answers = await prompter.prompt(argv, [{
+ type: "text",
+ name: "id",
+ message: "id",
+ required: true
+ }]);
+ const client = getClient();
+ const result = await client.driver.findOne({
+ id: answers.id,
+ select: {
+ id: true,
+ name: true,
+ licenseNumber: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Record not found.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}
+async function handleCreate(argv: Partial>, prompter: Inquirerer) {
+ try {
+ const answers = await prompter.prompt(argv, [{
+ type: "text",
+ name: "name",
+ message: "name",
+ required: true
+ }, {
+ type: "text",
+ name: "licenseNumber",
+ message: "licenseNumber",
+ required: true
+ }]);
+ const client = getClient();
+ const result = await client.driver.create({
+ data: {
+ name: answers.name,
+ licenseNumber: answers.licenseNumber
+ },
+ select: {
+ id: true,
+ name: true,
+ licenseNumber: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed to create record.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}
+async function handleUpdate(argv: Partial>, prompter: Inquirerer) {
+ try {
+ const answers = await prompter.prompt(argv, [{
+ type: "text",
+ name: "id",
+ message: "id",
+ required: true
+ }, {
+ type: "text",
+ name: "name",
+ message: "name",
+ required: false
+ }, {
+ type: "text",
+ name: "licenseNumber",
+ message: "licenseNumber",
+ required: false
+ }]);
+ const client = getClient();
+ const result = await client.driver.update({
+ id: answers.id,
+ data: {
+ name: answers.name,
+ licenseNumber: answers.licenseNumber
+ },
+ select: {
+ id: true,
+ name: true,
+ licenseNumber: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed to update record.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}
+async function handleDelete(argv: Partial>, prompter: Inquirerer) {
+ try {
+ const answers = await prompter.prompt(argv, [{
+ type: "text",
+ name: "id",
+ message: "id",
+ required: true
+ }]);
+ const client = getClient();
+ const result = await client.driver.delete({
+ id: answers.id,
+ select: {
+ id: true
+ }
+ }).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed to delete record.");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+}"
+`;
+
+exports[`cli-generator generates commands/login.ts (custom mutation) 1`] = `
+"/**
+ * CLI command for mutation login
+ * @generated by @constructive-io/graphql-codegen
+ * DO NOT EDIT - changes will be overwritten
+ */
+import { CLIOptions, Inquirerer } from "inquirerer";
+import { getClient } from "../executor";
+export default async (argv: Partial>, prompter: Inquirerer, _options: CLIOptions) => {
+ try {
+ if (argv.help || argv.h) {
+ console.log("login - Authenticate a user\\n\\nUsage: login [OPTIONS]\\n");
+ process.exit(0);
+ }
+ const answers = await prompter.prompt(argv, [{
+ type: "text",
+ name: "email",
+ message: "email",
+ required: true
+ }, {
+ type: "text",
+ name: "password",
+ message: "password",
+ required: true
+ }]);
+ const client = getClient();
+ const result = await client.mutation.login(answers).execute();
+ console.log(JSON.stringify(result, null, 2));
+ } catch (error) {
+ console.error("Failed: login");
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
+ }
+};"
+`;
+
+exports[`cli-generator generates executor.ts 1`] = `
+"/**
+ * Executor and config store for CLI
+ * @generated by @constructive-io/graphql-codegen
+ * DO NOT EDIT - changes will be overwritten
+ */
+import { createConfigStore } from "appstash";
+import { createClient } from "../orm";
+const store = createConfigStore("myapp");
+export const getStore = () => store;
+export function getClient(contextName?: string) {
+ let ctx = null;
+ if (contextName) {
+ ctx = store.loadContext(contextName);
+ if (!ctx) {
+ throw new Error(\`Context "\${contextName}" not found.\`);
+ }
+ } else {
+ ctx = store.getCurrentContext();
+ if (!ctx) {
+ throw new Error("No active context. Run \\"context create\\" or \\"context use\\" first.");
+ }
+ }
+ const headers = {};
+ if (store.hasValidCredentials(ctx.name)) {
+ const creds = store.getCredentials(ctx.name);
+ if (creds?.token) {
+ headers.Authorization = \`Bearer \${creds.token}\`;
+ }
+ }
+ return createClient({
+ endpoint: ctx.endpoint,
+ headers: headers
+ });
+}"
+`;
+
+exports[`hooks docs generator generates hooks AGENTS.md 1`] = `
+"# React Query Hooks - Agent Reference
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+> This document is structured for LLM/agent consumption.
+
+## OVERVIEW
+
+React Query hooks wrapping ORM operations for data fetching and mutations.
+All query hooks return \`UseQueryResult\`. All mutation hooks return \`UseMutationResult\`.
+
+## SETUP
+
+\`\`\`typescript
+import { configure } from './hooks';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+
+configure({ endpoint: 'https://api.example.com/graphql' });
+const queryClient = new QueryClient();
+// Wrap app in
+\`\`\`
+
+## HOOKS
+
+### HOOK: useCarsQuery
+
+List all cars.
+
+\`\`\`
+TYPE: query
+USAGE: useCarsQuery({ selection: { fields: { ... } } })
+
+INPUT:
+ selection: { fields: Record } - Fields to select
+
+OUTPUT: UseQueryResult>
+\`\`\`
+
+### HOOK: useCarQuery
+
+Get a single car by id.
+
+\`\`\`
+TYPE: query
+USAGE: useCarQuery({ id: '', selection: { fields: { ... } } })
+
+INPUT:
+ id: string (required)
+ selection: { fields: Record } - Fields to select
+
+OUTPUT: UseQueryResult<{
+ id: string
+ make: string
+ model: string
+ year: number
+ isElectric: boolean
+ createdAt: string
+}>
+\`\`\`
+
+### HOOK: useCreateCarMutation
+
+Create a new car.
+
+\`\`\`
+TYPE: mutation
+USAGE: const { mutate } = useCreateCarMutation({ selection: { fields: { ... } } })
+
+OUTPUT: UseMutationResult
+\`\`\`
+
+### HOOK: useUpdateCarMutation
+
+Update an existing car.
+
+\`\`\`
+TYPE: mutation
+USAGE: const { mutate } = useUpdateCarMutation({ selection: { fields: { ... } } })
+
+OUTPUT: UseMutationResult
+\`\`\`
+
+### HOOK: useDeleteCarMutation
+
+Delete a car.
+
+\`\`\`
+TYPE: mutation
+USAGE: const { mutate } = useDeleteCarMutation({})
+
+OUTPUT: UseMutationResult
+\`\`\`
+
+### HOOK: useDriversQuery
+
+List all drivers.
+
+\`\`\`
+TYPE: query
+USAGE: useDriversQuery({ selection: { fields: { ... } } })
+
+INPUT:
+ selection: { fields: Record } - Fields to select
+
+OUTPUT: UseQueryResult>
+\`\`\`
+
+### HOOK: useDriverQuery
+
+Get a single driver by id.
+
+\`\`\`
+TYPE: query
+USAGE: useDriverQuery({ id: '', selection: { fields: { ... } } })
+
+INPUT:
+ id: string (required)
+ selection: { fields: Record } - Fields to select
+
+OUTPUT: UseQueryResult<{
+ id: string
+ name: string
+ licenseNumber: string
+}>
+\`\`\`
+
+### HOOK: useCreateDriverMutation
+
+Create a new driver.
+
+\`\`\`
+TYPE: mutation
+USAGE: const { mutate } = useCreateDriverMutation({ selection: { fields: { ... } } })
+
+OUTPUT: UseMutationResult
+\`\`\`
+
+### HOOK: useUpdateDriverMutation
+
+Update an existing driver.
+
+\`\`\`
+TYPE: mutation
+USAGE: const { mutate } = useUpdateDriverMutation({ selection: { fields: { ... } } })
+
+OUTPUT: UseMutationResult
+\`\`\`
+
+### HOOK: useDeleteDriverMutation
+
+Delete a driver.
+
+\`\`\`
+TYPE: mutation
+USAGE: const { mutate } = useDeleteDriverMutation({})
+
+OUTPUT: UseMutationResult
+\`\`\`
+
+## CUSTOM OPERATION HOOKS
+
+### HOOK: useCurrentUserQuery
+
+Get the currently authenticated user
+
+\`\`\`
+TYPE: query
+USAGE: useCurrentUserQuery()
+
+INPUT: none
+
+OUTPUT: UseQueryResult
+\`\`\`
+
+### HOOK: useLoginMutation
+
+Authenticate a user
+
+\`\`\`
+TYPE: mutation
+USAGE: const { mutate } = useLoginMutation()
+ mutate({ email: , password: })
+
+INPUT:
+ email: String (required)
+ password: String (required)
+
+OUTPUT: UseMutationResult
+\`\`\`
+"
+`;
+
+exports[`hooks docs generator generates hooks README 1`] = `
+"# React Query Hooks
+
+
+
+
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+## Setup
+
+\`\`\`typescript
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { configure } from './hooks';
+
+configure({
+ endpoint: 'https://api.example.com/graphql',
+ headers: { Authorization: 'Bearer ' },
+});
+
+const queryClient = new QueryClient();
+
+function App() {
+ return (
+
+
+
+ );
+}
+\`\`\`
+
+## Hooks
+
+| Hook | Type | Description |
+|------|------|-------------|
+| \`useCarsQuery\` | Query | List all cars |
+| \`useCarQuery\` | Query | Get one car |
+| \`useCreateCarMutation\` | Mutation | Create a car |
+| \`useUpdateCarMutation\` | Mutation | Update a car |
+| \`useDeleteCarMutation\` | Mutation | Delete a car |
+| \`useDriversQuery\` | Query | List all drivers |
+| \`useDriverQuery\` | Query | Get one driver |
+| \`useCreateDriverMutation\` | Mutation | Create a driver |
+| \`useUpdateDriverMutation\` | Mutation | Update a driver |
+| \`useDeleteDriverMutation\` | Mutation | Delete a driver |
+| \`useCurrentUserQuery\` | Query | Get the currently authenticated user |
+| \`useLoginMutation\` | Mutation | Authenticate a user |
+
+## Table Hooks
+
+### Car
+
+\`\`\`typescript
+// List all cars
+const { data, isLoading } = useCarsQuery({
+ selection: { fields: { id: true, make: true, model: true, year: true, isElectric: true, createdAt: true } },
+});
+
+// Get one car
+const { data: item } = useCarQuery({
+ id: '',
+ selection: { fields: { id: true, make: true, model: true, year: true, isElectric: true, createdAt: true } },
+});
+
+// Create a car
+const { mutate: create } = useCreateCarMutation({
+ selection: { fields: { id: true } },
+});
+create({ make: '', model: '', year: '', isElectric: '' });
+\`\`\`
+
+### Driver
+
+\`\`\`typescript
+// List all drivers
+const { data, isLoading } = useDriversQuery({
+ selection: { fields: { id: true, name: true, licenseNumber: true } },
+});
+
+// Get one driver
+const { data: item } = useDriverQuery({
+ id: '',
+ selection: { fields: { id: true, name: true, licenseNumber: true } },
+});
+
+// Create a driver
+const { mutate: create } = useCreateDriverMutation({
+ selection: { fields: { id: true } },
+});
+create({ name: '', licenseNumber: '' });
+\`\`\`
+
+## Custom Operation Hooks
+
+### \`useCurrentUserQuery\`
+
+Get the currently authenticated user
+
+- **Type:** query
+- **Arguments:** none
+
+### \`useLoginMutation\`
+
+Authenticate a user
+
+- **Type:** mutation
+- **Arguments:**
+
+ | Argument | Type |
+ |----------|------|
+ | \`email\` | String (required) |
+ | \`password\` | String (required) |
+
+---
+
+Built by the [Constructive](https://constructive.io) team.
+
+## Disclaimer
+
+AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
+
+No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
+"
+`;
+
+exports[`hooks docs generator generates hooks skill files 1`] = `
+"# hooks-car
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+React Query hooks for Car data operations
+
+## Usage
+
+\`\`\`typescript
+useCarsQuery({ selection: { fields: { id: true, make: true, model: true, year: true, isElectric: true, createdAt: true } } })
+useCarQuery({ id: '', selection: { fields: { id: true, make: true, model: true, year: true, isElectric: true, createdAt: true } } })
+useCreateCarMutation({ selection: { fields: { id: true } } })
+useUpdateCarMutation({ selection: { fields: { id: true } } })
+useDeleteCarMutation({})
+\`\`\`
+
+## Examples
+
+### List all cars
+
+\`\`\`typescript
+const { data, isLoading } = useCarsQuery({
+ selection: { fields: { id: true, make: true, model: true, year: true, isElectric: true, createdAt: true } },
+});
+\`\`\`
+
+### Create a car
+
+\`\`\`typescript
+const { mutate } = useCreateCarMutation({
+ selection: { fields: { id: true } },
+});
+mutate({ make: '', model: '', year: '', isElectric: '' });
+\`\`\`
+"
+`;
+
+exports[`hooks docs generator generates hooks skill files 2`] = `
+"# hooks-driver
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+React Query hooks for Driver data operations
+
+## Usage
+
+\`\`\`typescript
+useDriversQuery({ selection: { fields: { id: true, name: true, licenseNumber: true } } })
+useDriverQuery({ id: '', selection: { fields: { id: true, name: true, licenseNumber: true } } })
+useCreateDriverMutation({ selection: { fields: { id: true } } })
+useUpdateDriverMutation({ selection: { fields: { id: true } } })
+useDeleteDriverMutation({})
+\`\`\`
+
+## Examples
+
+### List all drivers
+
+\`\`\`typescript
+const { data, isLoading } = useDriversQuery({
+ selection: { fields: { id: true, name: true, licenseNumber: true } },
+});
+\`\`\`
+
+### Create a driver
+
+\`\`\`typescript
+const { mutate } = useCreateDriverMutation({
+ selection: { fields: { id: true } },
+});
+mutate({ name: '', licenseNumber: '' });
+\`\`\`
+"
+`;
+
+exports[`hooks docs generator generates hooks skill files 3`] = `
+"# hooks-currentUser
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Get the currently authenticated user
+
+## Usage
+
+\`\`\`typescript
+useCurrentUserQuery()
+\`\`\`
+
+## Examples
+
+### Use useCurrentUserQuery
+
+\`\`\`typescript
+const { data, isLoading } = useCurrentUserQuery();
+\`\`\`
+"
+`;
+
+exports[`hooks docs generator generates hooks skill files 4`] = `
+"# hooks-login
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Authenticate a user
+
+## Usage
+
+\`\`\`typescript
+const { mutate } = useLoginMutation(); mutate({ email: '', password: '' });
+\`\`\`
+
+## Examples
+
+### Use useLoginMutation
+
+\`\`\`typescript
+const { mutate, isLoading } = useLoginMutation();
+mutate({ email: '', password: '' });
+\`\`\`
+"
+`;
+
+exports[`multi-target cli docs generates multi-target AGENTS.md 1`] = `
+"# myapp CLI - Agent Reference
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+> This document is structured for LLM/agent consumption.
+
+## OVERVIEW
+
+\`myapp\` is a unified multi-target CLI for interacting with multiple GraphQL APIs.
+All commands output JSON to stdout. All commands accept \`--help\` or \`-h\` for usage.
+Configuration is stored at \`~/.myapp/config/\` via appstash.
+
+TARGETS:
+ auth: http://auth.localhost/graphql
+ members: http://members.localhost/graphql
+ app: http://app.localhost/graphql
+
+COMMAND FORMAT:
+ myapp : [flags] Target-specific commands
+ myapp context [flags] Context management
+ myapp credentials [flags] Authentication
+
+## PREREQUISITES
+
+Before running any data commands, you must:
+
+1. Create a context: \`myapp context create \`
+ (prompts for per-target endpoints, defaults baked from config)
+2. Activate it: \`myapp context use \`
+3. Authenticate: \`myapp credentials set-token \`
+
+For local development, create a context accepting all defaults:
+
+\`\`\`bash
+myapp context create local
+myapp context use local
+myapp credentials set-token
+\`\`\`
+
+## TOOLS
+
+### TOOL: context
+
+Manage named API endpoint contexts. Each context stores per-target endpoint overrides.
+
+\`\`\`
+SUBCOMMANDS:
+ myapp context create Create a new context
+ myapp context list List all contexts
+ myapp context use Set active context
+ myapp context current Show active context
+ myapp context delete Delete a context
+
+CREATE OPTIONS:
+ --auth-endpoint: string (default: http://auth.localhost/graphql)
+ --members-endpoint: string (default: http://members.localhost/graphql)
+ --app-endpoint: string (default: http://app.localhost/graphql)
+
+OUTPUT: JSON
+ create: { name, endpoint, targets }
+ list: [{ name, endpoint, isCurrent, hasCredentials }]
+ use: { name, endpoint }
+ current: { name, endpoint }
+ delete: { deleted: name }
+\`\`\`
+
+### TOOL: credentials
+
+Manage authentication tokens per context. One shared token across all targets.
+
+\`\`\`
+SUBCOMMANDS:
+ myapp credentials set-token Store bearer token for current context
+ myapp credentials status Show auth status for all contexts
+ myapp credentials logout Remove credentials for current context
+
+INPUT:
+ token: string (required for set-token) - Bearer token value
+
+OUTPUT: JSON
+ set-token: { context, status: "authenticated" }
+ status: [{ context, authenticated: boolean }]
+ logout: { context, status: "logged out" }
+\`\`\`
+
+### TOOL: auth:user
+
+CRUD operations for User records (auth target).
+
+\`\`\`
+SUBCOMMANDS:
+ myapp auth:user list List all records
+ myapp auth:user get --id Get one record
+ myapp auth:user create --email --name
+ myapp auth:user update --id [--email ] [--name ]
+ myapp auth:user delete --id Delete one record
+
+INPUT FIELDS:
+ id: UUID (primary key)
+ email: String
+ name: String
+
+EDITABLE FIELDS (for create/update):
+ email: String
+ name: String
+
+OUTPUT: JSON
+ list: [{ id, email, name }]
+ get: { id, email, name }
+ create: { id, email, name }
+ update: { id, email, name }
+ delete: { id }
+\`\`\`
+
+### TOOL: auth:current-user
+
+Get the currently authenticated user
+
+\`\`\`
+TYPE: query
+USAGE: myapp auth:current-user
+
+INPUT: none
+
+OUTPUT: JSON
+\`\`\`
+
+### TOOL: auth:login
+
+Authenticate a user
+
+\`\`\`
+TYPE: mutation
+USAGE: myapp auth:login --email --password
+
+INPUT:
+ email: String (required)
+ password: String (required)
+
+FLAGS:
+ --save-token: boolean - Auto-save returned token to credentials
+
+OUTPUT: JSON
+\`\`\`
+
+### TOOL: members:member
+
+CRUD operations for Member records (members target).
+
+\`\`\`
+SUBCOMMANDS:
+ myapp members:member list List all records
+ myapp members:member get --id Get one record
+ myapp members:member create --role
+ myapp members:member update --id [--role ]
+ myapp members:member delete --id Delete one record
+
+INPUT FIELDS:
+ id: UUID (primary key)
+ role: String
+
+EDITABLE FIELDS (for create/update):
+ role: String
+
+OUTPUT: JSON
+ list: [{ id, role }]
+ get: { id, role }
+ create: { id, role }
+ update: { id, role }
+ delete: { id }
+\`\`\`
+
+### TOOL: app:car
+
+CRUD operations for Car records (app target).
+
+\`\`\`
+SUBCOMMANDS:
+ myapp app:car list List all records
+ myapp app:car get --id Get one record
+ myapp app:car create --make --model --year --isElectric
+ myapp app:car update --id [--make ] [--model ] [--year ] [--isElectric ]
+ myapp app:car delete --id Delete one record
+
+INPUT FIELDS:
+ id: UUID (primary key)
+ make: String
+ model: String
+ year: Int
+ isElectric: Boolean
+ createdAt: Datetime
+
+EDITABLE FIELDS (for create/update):
+ make: String
+ model: String
+ year: Int
+ isElectric: Boolean
+
+OUTPUT: JSON
+ list: [{ id, make, model, year, isElectric, createdAt }]
+ get: { id, make, model, year, isElectric, createdAt }
+ create: { id, make, model, year, isElectric, createdAt }
+ update: { id, make, model, year, isElectric, createdAt }
+ delete: { id }
+\`\`\`
+
+## WORKFLOWS
+
+### Initial setup
+
+\`\`\`bash
+myapp context create dev
+myapp context use dev
+myapp credentials set-token eyJhbGciOiJIUzI1NiIs...
+\`\`\`
+
+### Switch environment
+
+\`\`\`bash
+myapp context create production \\
+ --auth-endpoint https://auth.prod.example.com/graphql \\
+ --members-endpoint https://members.prod.example.com/graphql \\
+ --app-endpoint https://app.prod.example.com/graphql
+myapp context use production
+\`\`\`
+
+### CRUD workflow (auth:user)
+
+\`\`\`bash
+myapp auth:user list
+myapp auth:user create --email "value" --name "value"
+myapp auth:user get --id
+myapp auth:user update --id --email "new-value"
+myapp auth:user delete --id
+\`\`\`
+
+### Piping output
+
+\`\`\`bash
+myapp auth:user list | jq '.'
+myapp auth:user list | jq '.[].id'
+myapp auth:user list | jq 'length'
+\`\`\`
+
+## ERROR HANDLING
+
+All errors are written to stderr. Exit codes:
+- \`0\`: Success
+- \`1\`: Error (auth failure, not found, validation error, network error)
+
+Common errors:
+- "No active context": Run \`context use \` first
+- "Not authenticated": Run \`credentials set-token \` first
+- "Unknown target": The target name is not recognized
+- "Record not found": The requested ID does not exist
+"
+`;
+
+exports[`multi-target cli docs generates multi-target MCP tools 1`] = `
+[
+ {
+ "description": "Create a named API context with per-target endpoint overrides",
+ "inputSchema": {
+ "properties": {
+ "app_endpoint": {
+ "description": "app GraphQL endpoint (default: http://app.localhost/graphql)",
+ "type": "string",
+ },
+ "auth_endpoint": {
+ "description": "auth GraphQL endpoint (default: http://auth.localhost/graphql)",
+ "type": "string",
+ },
+ "members_endpoint": {
+ "description": "members GraphQL endpoint (default: http://members.localhost/graphql)",
+ "type": "string",
+ },
+ "name": {
+ "description": "Context name",
+ "type": "string",
+ },
+ },
+ "required": [
+ "name",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_context_create",
+ },
+ {
+ "description": "List all configured API contexts",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_context_list",
+ },
+ {
+ "description": "Set the active API context (switches all targets at once)",
+ "inputSchema": {
+ "properties": {
+ "name": {
+ "description": "Context name to activate",
+ "type": "string",
+ },
+ },
+ "required": [
+ "name",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_context_use",
+ },
+ {
+ "description": "Show the currently active API context",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_context_current",
+ },
+ {
+ "description": "Delete an API context",
+ "inputSchema": {
+ "properties": {
+ "name": {
+ "description": "Context name to delete",
+ "type": "string",
+ },
+ },
+ "required": [
+ "name",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_context_delete",
+ },
+ {
+ "description": "Store a bearer token for the current context (shared across all targets)",
+ "inputSchema": {
+ "properties": {
+ "token": {
+ "description": "Bearer token value",
+ "type": "string",
+ },
+ },
+ "required": [
+ "token",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_credentials_set_token",
+ },
+ {
+ "description": "Show authentication status for all contexts",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_credentials_status",
+ },
+ {
+ "description": "Remove credentials for the current context",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_credentials_logout",
+ },
+ {
+ "description": "List all User records (auth target)",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_auth_user_list",
+ },
+ {
+ "description": "Get a single User record by id (auth target)",
+ "inputSchema": {
+ "properties": {
+ "id": {
+ "description": "User id",
+ "type": "string",
+ },
+ },
+ "required": [
+ "id",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_auth_user_get",
+ },
+ {
+ "description": "Create a new User record (auth target)",
+ "inputSchema": {
+ "properties": {
+ "email": {
+ "description": "User email",
+ "type": "string",
+ },
+ "name": {
+ "description": "User name",
+ "type": "string",
+ },
+ },
+ "required": [
+ "email",
+ "name",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_auth_user_create",
+ },
+ {
+ "description": "Update an existing User record (auth target)",
+ "inputSchema": {
+ "properties": {
+ "email": {
+ "description": "User email",
+ "type": "string",
+ },
+ "id": {
+ "description": "User id",
+ "type": "string",
+ },
+ "name": {
+ "description": "User name",
+ "type": "string",
+ },
+ },
+ "required": [
+ "id",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_auth_user_update",
+ },
+ {
+ "description": "Delete a User record by id (auth target)",
+ "inputSchema": {
+ "properties": {
+ "id": {
+ "description": "User id",
+ "type": "string",
+ },
+ },
+ "required": [
+ "id",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_auth_user_delete",
+ },
+ {
+ "_meta": {
+ "fields": [
+ {
+ "editable": false,
+ "name": "id",
+ "primaryKey": true,
+ "type": "UUID",
+ },
+ {
+ "editable": true,
+ "name": "email",
+ "primaryKey": false,
+ "type": "String",
+ },
+ {
+ "editable": true,
+ "name": "name",
+ "primaryKey": false,
+ "type": "String",
+ },
+ ],
+ },
+ "description": "List available fields for User (auth target)",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_auth_user_fields",
+ },
+ {
+ "description": "Get the currently authenticated user (auth target)",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_auth_current-user",
+ },
+ {
+ "description": "Authenticate a user (auth target)",
+ "inputSchema": {
+ "properties": {
+ "email": {
+ "description": "email",
+ "type": "string",
+ },
+ "password": {
+ "description": "password",
+ "type": "string",
+ },
+ "save_token": {
+ "description": "Auto-save returned token to credentials",
+ "type": "boolean",
+ },
+ },
+ "required": [
+ "email",
+ "password",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_auth_login",
+ },
+ {
+ "description": "List all Member records (members target)",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_members_member_list",
+ },
+ {
+ "description": "Get a single Member record by id (members target)",
+ "inputSchema": {
+ "properties": {
+ "id": {
+ "description": "Member id",
+ "type": "string",
+ },
+ },
+ "required": [
+ "id",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_members_member_get",
+ },
+ {
+ "description": "Create a new Member record (members target)",
+ "inputSchema": {
+ "properties": {
+ "role": {
+ "description": "Member role",
+ "type": "string",
+ },
+ },
+ "required": [
+ "role",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_members_member_create",
+ },
+ {
+ "description": "Update an existing Member record (members target)",
+ "inputSchema": {
+ "properties": {
+ "id": {
+ "description": "Member id",
+ "type": "string",
+ },
+ "role": {
+ "description": "Member role",
+ "type": "string",
+ },
+ },
+ "required": [
+ "id",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_members_member_update",
+ },
+ {
+ "description": "Delete a Member record by id (members target)",
+ "inputSchema": {
+ "properties": {
+ "id": {
+ "description": "Member id",
+ "type": "string",
+ },
+ },
+ "required": [
+ "id",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_members_member_delete",
+ },
+ {
+ "_meta": {
+ "fields": [
+ {
+ "editable": false,
+ "name": "id",
+ "primaryKey": true,
+ "type": "UUID",
+ },
+ {
+ "editable": true,
+ "name": "role",
+ "primaryKey": false,
+ "type": "String",
+ },
+ ],
+ },
+ "description": "List available fields for Member (members target)",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_members_member_fields",
+ },
+ {
+ "description": "List all Car records (app target)",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_app_car_list",
+ },
+ {
+ "description": "Get a single Car record by id (app target)",
+ "inputSchema": {
+ "properties": {
+ "id": {
+ "description": "Car id",
+ "type": "string",
+ },
+ },
+ "required": [
+ "id",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_app_car_get",
+ },
+ {
+ "description": "Create a new Car record (app target)",
+ "inputSchema": {
+ "properties": {
+ "isElectric": {
+ "description": "Car isElectric",
+ "type": "boolean",
+ },
+ "make": {
+ "description": "Car make",
+ "type": "string",
+ },
+ "model": {
+ "description": "Car model",
+ "type": "string",
+ },
+ "year": {
+ "description": "Car year",
+ "type": "integer",
+ },
+ },
+ "required": [
+ "make",
+ "model",
+ "year",
+ "isElectric",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_app_car_create",
+ },
+ {
+ "description": "Update an existing Car record (app target)",
+ "inputSchema": {
+ "properties": {
+ "id": {
+ "description": "Car id",
+ "type": "string",
+ },
+ "isElectric": {
+ "description": "Car isElectric",
+ "type": "boolean",
+ },
+ "make": {
+ "description": "Car make",
+ "type": "string",
+ },
+ "model": {
+ "description": "Car model",
+ "type": "string",
+ },
+ "year": {
+ "description": "Car year",
+ "type": "integer",
+ },
+ },
+ "required": [
+ "id",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_app_car_update",
+ },
+ {
+ "description": "Delete a Car record by id (app target)",
+ "inputSchema": {
+ "properties": {
+ "id": {
+ "description": "Car id",
+ "type": "string",
+ },
+ },
+ "required": [
+ "id",
+ ],
+ "type": "object",
+ },
+ "name": "myapp_app_car_delete",
+ },
+ {
+ "_meta": {
+ "fields": [
+ {
+ "editable": false,
+ "name": "id",
+ "primaryKey": true,
+ "type": "UUID",
+ },
+ {
+ "editable": true,
+ "name": "make",
+ "primaryKey": false,
+ "type": "String",
+ },
+ {
+ "editable": true,
+ "name": "model",
+ "primaryKey": false,
+ "type": "String",
+ },
+ {
+ "editable": true,
+ "name": "year",
+ "primaryKey": false,
+ "type": "Int",
+ },
+ {
+ "editable": true,
+ "name": "isElectric",
+ "primaryKey": false,
+ "type": "Boolean",
+ },
+ {
+ "editable": false,
+ "name": "createdAt",
+ "primaryKey": false,
+ "type": "Datetime",
+ },
+ ],
+ },
+ "description": "List available fields for Car (app target)",
+ "inputSchema": {
+ "properties": {},
+ "type": "object",
+ },
+ "name": "myapp_app_car_fields",
+ },
+]
+`;
+
+exports[`multi-target cli docs generates multi-target README 1`] = `
+"# myapp CLI
+
+
+
+
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+## Setup
+
+### Create a context
+
+A context stores per-target endpoint overrides for an environment (dev, staging, production).
+Default endpoints are baked in from the codegen config, so local development works with zero configuration.
+
+\`\`\`bash
+# Interactive - prompts for each target endpoint (defaults shown)
+myapp context create local
+
+# Non-interactive
+myapp context create production \\
+ --auth-endpoint https://auth.prod.example.com/graphql \\
+ --members-endpoint https://members.prod.example.com/graphql \\
+ --app-endpoint https://app.prod.example.com/graphql
+\`\`\`
+
+### Activate a context
+
+\`\`\`bash
+myapp context use production
+\`\`\`
+
+### Authenticate
+
+\`\`\`bash
+myapp credentials set-token
+\`\`\`
+
+Or authenticate via a login mutation (auto-saves token):
+
+\`\`\`bash
+myapp auth:login --email --password --save-token
+\`\`\`
+
+## API Targets
+
+| Target | Default Endpoint | Tables | Custom Operations |
+|--------|-----------------|--------|-------------------|
+| \`auth\` | http://auth.localhost/graphql | 1 | 2 |
+| \`members\` | http://members.localhost/graphql | 1 | 0 |
+| \`app\` | http://app.localhost/graphql | 1 | 0 |
+
+## Commands
+
+### Infrastructure
+
+| Command | Description |
+|---------|-------------|
+| \`context\` | Manage API contexts (per-target endpoints) |
+| \`credentials\` | Manage authentication tokens |
+
+### auth
+
+| Command | Description |
+|---------|-------------|
+| \`auth:user\` | user CRUD operations |
+| \`auth:current-user\` | Get the currently authenticated user |
+| \`auth:login\` | Authenticate a user |
+
+### members
+
+| Command | Description |
+|---------|-------------|
+| \`members:member\` | member CRUD operations |
+
+### app
+
+| Command | Description |
+|---------|-------------|
+| \`app:car\` | car CRUD operations |
+
+## Infrastructure Commands
+
+### \`context\`
+
+Manage named API contexts (kubectl-style). Each context stores per-target endpoint overrides.
+
+| Subcommand | Description |
+|------------|-------------|
+| \`create \` | Create a new context (prompts for per-target endpoints) |
+| \`list\` | List all contexts |
+| \`use \` | Set the active context |
+| \`current\` | Show current context |
+| \`delete \` | Delete a context |
+
+Create options:
+
+- \`--auth-endpoint \` (default: http://auth.localhost/graphql)
+- \`--members-endpoint \` (default: http://members.localhost/graphql)
+- \`--app-endpoint \` (default: http://app.localhost/graphql)
+
+Configuration is stored at \`~/.myapp/config/\`.
+
+### \`credentials\`
+
+Manage authentication tokens per context. One shared token is used across all targets.
+
+| Subcommand | Description |
+|------------|-------------|
+| \`set-token \` | Store bearer token for current context |
+| \`status\` | Show auth status across all contexts |
+| \`logout\` | Remove credentials for current context |
+
+## auth Commands
+
+### \`auth:user\`
+
+CRUD operations for User records.
+
+| Subcommand | Description |
+|------------|-------------|
+| \`list\` | List all user records |
+| \`get\` | Get a user by id |
+| \`create\` | Create a new user |
+| \`update\` | Update an existing user |
+| \`delete\` | Delete a user |
+
+**Fields:**
+
+| Field | Type |
+|-------|------|
+| \`id\` | UUID |
+| \`email\` | String |
+| \`name\` | String |
+
+**Create fields:** \`email\`, \`name\`
+
+### \`auth:current-user\`
+
+Get the currently authenticated user
+
+- **Type:** query
+- **Arguments:** none
+
+### \`auth:login\`
+
+Authenticate a user
+
+- **Type:** mutation
+- **Arguments:**
+
+ | Argument | Type |
+ |----------|------|
+ | \`email\` | String (required) |
+ | \`password\` | String (required) |
+- **Flags:** \`--save-token\` auto-saves returned token to credentials
+
+## members Commands
+
+### \`members:member\`
+
+CRUD operations for Member records.
+
+| Subcommand | Description |
+|------------|-------------|
+| \`list\` | List all member records |
+| \`get\` | Get a member by id |
+| \`create\` | Create a new member |
+| \`update\` | Update an existing member |
+| \`delete\` | Delete a member |
+
+**Fields:**
+
+| Field | Type |
+|-------|------|
+| \`id\` | UUID |
+| \`role\` | String |
+
+**Create fields:** \`role\`
+
+## app Commands
+
+### \`app:car\`
+
+CRUD operations for Car records.
+
+| Subcommand | Description |
+|------------|-------------|
+| \`list\` | List all car records |
+| \`get\` | Get a car by id |
+| \`create\` | Create a new car |
+| \`update\` | Update an existing car |
+| \`delete\` | Delete a car |
+
+**Fields:**
+
+| Field | Type |
+|-------|------|
+| \`id\` | UUID |
+| \`make\` | String |
+| \`model\` | String |
+| \`year\` | Int |
+| \`isElectric\` | Boolean |
+| \`createdAt\` | Datetime |
+
+**Create fields:** \`make\`, \`model\`, \`year\`, \`isElectric\`
+
+## Output
+
+All commands output JSON to stdout. Pipe to \`jq\` for formatting:
+
+\`\`\`bash
+myapp auth:user list | jq '.[]'
+myapp auth:user get --id | jq '.'
+\`\`\`
+
+---
+
+Built by the [Constructive](https://constructive.io) team.
+
+## Disclaimer
+
+AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
+
+No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
+"
+`;
+
+exports[`multi-target cli docs generates multi-target skills 1`] = `
+[
+ {
+ "content": "# myapp-context
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Manage API endpoint contexts for myapp (multi-target: auth, members, app)
+
+## Usage
+
+\`\`\`bash
+myapp context create
+myapp context list
+myapp context use
+myapp context current
+myapp context delete
+\`\`\`
+
+## Examples
+
+### Create a context for local development (accept all defaults)
+
+\`\`\`bash
+myapp context create local
+myapp context use local
+\`\`\`
+
+### Create a production context with custom endpoints
+
+\`\`\`bash
+myapp context create production --auth-endpoint --members-endpoint --app-endpoint
+myapp context use production
+\`\`\`
+
+### List and switch contexts
+
+\`\`\`bash
+myapp context list
+myapp context use staging
+\`\`\`
+",
+ "fileName": "skills/context.md",
+ },
+ {
+ "content": "# myapp-credentials
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Manage authentication tokens for myapp (shared across all targets)
+
+## Usage
+
+\`\`\`bash
+myapp credentials set-token
+myapp credentials status
+myapp credentials logout
+\`\`\`
+
+## Examples
+
+### Authenticate with a token
+
+\`\`\`bash
+myapp credentials set-token eyJhbGciOiJIUzI1NiIs...
+\`\`\`
+
+### Check auth status
+
+\`\`\`bash
+myapp credentials status
+\`\`\`
+",
+ "fileName": "skills/credentials.md",
+ },
+ {
+ "content": "# myapp-auth:user
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+CRUD operations for User records via myapp CLI (auth target)
+
+## Usage
+
+\`\`\`bash
+myapp auth:user list
+myapp auth:user get --id
+myapp auth:user create --email --name
+myapp auth:user update --id [--email ] [--name ]
+myapp auth:user delete --id
+\`\`\`
+
+## Examples
+
+### List all user records
+
+\`\`\`bash
+myapp auth:user list
+\`\`\`
+
+### Create a user
+
+\`\`\`bash
+myapp auth:user create --email "value" --name "value"
+\`\`\`
+
+### Get a user by id
+
+\`\`\`bash
+myapp auth:user get --id
+\`\`\`
+",
+ "fileName": "skills/auth-user.md",
+ },
+ {
+ "content": "# myapp-auth:current-user
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Get the currently authenticated user (auth target)
+
+## Usage
+
+\`\`\`bash
+myapp auth:current-user
+\`\`\`
+
+## Examples
+
+### Run currentUser
+
+\`\`\`bash
+myapp auth:current-user
+\`\`\`
+",
+ "fileName": "skills/auth-current-user.md",
+ },
+ {
+ "content": "# myapp-auth:login
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+Authenticate a user (auth target)
+
+## Usage
+
+\`\`\`bash
+myapp auth:login --email --password
+myapp auth:login --email --password --save-token
+\`\`\`
+
+## Examples
+
+### Run login
+
+\`\`\`bash
+myapp auth:login --email --password
+\`\`\`
+",
+ "fileName": "skills/auth-login.md",
+ },
+ {
+ "content": "# myapp-members:member
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+CRUD operations for Member records via myapp CLI (members target)
+
+## Usage
+
+\`\`\`bash
+myapp members:member list
+myapp members:member get --id
+myapp members:member create --role
+myapp members:member update --id [--role ]
+myapp members:member delete --id
+\`\`\`
+
+## Examples
+
+### List all member records
+
+\`\`\`bash
+myapp members:member list
+\`\`\`
+
+### Create a member
+
+\`\`\`bash
+myapp members:member create --role "value"
+\`\`\`
+
+### Get a member by id
+
+\`\`\`bash
+myapp members:member get --id
+\`\`\`
+",
+ "fileName": "skills/members-member.md",
+ },
+ {
+ "content": "# myapp-app:car
+
+> @generated by @constructive-io/graphql-codegen - DO NOT EDIT
+
+CRUD operations for Car records via myapp CLI (app target)
+
+## Usage
+
+\`\`\`bash
+myapp app:car list
+myapp app:car get --id
+myapp app:car create --make --model --year --isElectric
+myapp app:car update --id [--make ] [--model ] [--year