Skip to content
Merged
  •  
  •  
  •  
9,935 changes: 2,591 additions & 7,344 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions sdk/constructive-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# GraphQL SDK

<p align="center" width="100%">
<img height="120" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
</p>

<!-- @constructive-io/graphql-codegen - DO NOT EDIT -->

## APIs

| API | Endpoint | Generators | Docs |
|-----|----------|------------|------|
| admin | - | ORM, CLI | [./src/admin/README.md](./src/admin/README.md) |
| auth | - | ORM, CLI | [./src/auth/README.md](./src/auth/README.md) |
| objects | - | ORM, CLI | [./src/objects/README.md](./src/objects/README.md) |
| public | - | ORM, CLI | [./src/public/README.md](./src/public/README.md) |

---

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.
193 changes: 193 additions & 0 deletions sdk/constructive-cli/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
name: constructive-cli
description: Build interactive CLI tools with the Constructive CLI SDK. Use when asked to "create a CLI", "build a command-line tool", "add CLI prompts", "create interactive prompts", "store CLI config", "add terminal colors", or when building any CLI application in a Constructive project. This package provides runtime utilities for type coercion, config management, display formatting, and command handler patterns used by generated and custom CLIs.
compatibility: inquirerer, appstash, yanse, Node.js 18+, TypeScript
metadata:
author: constructive-io
version: "0.1.0"
---

# Constructive CLI SDK

Runtime utilities for building interactive command-line interfaces using Constructive's CLI toolkit: **inquirerer** for prompts and argument parsing, **appstash** for persistent storage, and **yanse** for terminal colors.

## When to Apply

- Creating a new CLI tool in a Constructive project
- Adding interactive prompts or argument parsing to a command
- Managing persistent CLI configuration (contexts, credentials, settings)
- Formatting CLI output with colors, tables, or key-value displays
- Coercing CLI string arguments to proper GraphQL types
- Building nested subcommand structures (e.g. `cli context create`)
- Working with generated CLI code from `@constructive-io/graphql-codegen`

## Installation

```bash
pnpm add @constructive-sdk/cli
```

## Quick Start

### Creating a CLI with Commands

```typescript
import { CLI } from 'inquirerer';
import { buildCommands, CommandHandler } from '@constructive-sdk/cli';

const hello: CommandHandler = async (argv, prompter, _options) => {
const answers = await prompter.prompt(argv, [
{ type: 'text', name: 'name', message: 'Your name' }
]);
console.log(`Hello, ${answers.name}!`);
};

const commands = buildCommands([
{ name: 'hello', handler: hello, usage: 'Say hello' }
]);

const app = new CLI(commands);
app.run();
```

### Config Management with appstash

```typescript
import { getConfigStore } from '@constructive-sdk/cli';

const store = getConfigStore('my-tool');

// Create and manage contexts
store.createContext('production', { endpoint: 'https://api.example.com/graphql' });
store.setCurrentContext('production');

// Store credentials
store.setCredentials('production', { token: 'bearer-token-here' });

// Load current context
const ctx = store.getCurrentContext();
```

### Type Coercion for CLI Arguments

```typescript
import { coerceAnswers, stripUndefined, FieldSchema } from '@constructive-sdk/cli';

const schema: FieldSchema = {
name: 'string',
age: 'int',
active: 'boolean',
metadata: 'json'
};

// CLI args arrive as strings from minimist
const rawArgs = { name: 'Alice', age: '30', active: 'true', metadata: '{"role":"admin"}' };

// Coerce to proper types
const typed = coerceAnswers(rawArgs, schema);
// { name: 'Alice', age: 30, active: true, metadata: { role: 'admin' } }

// Strip undefined values and extra minimist fields
const clean = stripUndefined(typed, schema);
```

### Display Utilities

```typescript
import { printSuccess, printError, printTable, printDetails } from '@constructive-sdk/cli';

printSuccess('Context created');
printError('Connection failed');

printTable(
['Name', 'Endpoint', 'Status'],
[
['production', 'https://api.example.com/graphql', 'active'],
['staging', 'https://staging.example.com/graphql', 'inactive']
]
);

printDetails([
{ key: 'Name', value: 'production' },
{ key: 'Endpoint', value: 'https://api.example.com/graphql' }
]);
```

### Subcommand Dispatching

```typescript
import { createSubcommandHandler, CommandHandler } from '@constructive-sdk/cli';

const createCmd: CommandHandler = async (argv, prompter, options) => {
// Handle 'context create'
};

const listCmd: CommandHandler = async (argv, prompter, options) => {
// Handle 'context list'
};

const contextHandler = createSubcommandHandler(
{ create: createCmd, list: listCmd },
'Usage: my-tool context <create|list>'
);
```

## API Reference

### Config (`@constructive-sdk/cli`)

| Export | Description |
|--------|-------------|
| `getAppDirs(toolName, options?)` | Get XDG-compliant app directories for a CLI tool |
| `getConfigStore(toolName)` | Create a config store with context and credential management |

### Commands (`@constructive-sdk/cli`)

| Export | Description |
|--------|-------------|
| `buildCommands(definitions)` | Build a commands map from command definitions |
| `createSubcommandHandler(subcommands, usage)` | Create a handler that dispatches to subcommands |
| `CommandHandler` | Type: `(argv, prompter, options) => Promise<void>` |
| `CommandDefinition` | Interface: `{ name, handler, usage? }` |

### CLI Utilities (`@constructive-sdk/cli`)

| Export | Description |
|--------|-------------|
| `coerceAnswers(answers, schema)` | Coerce string CLI args to proper GraphQL types |
| `stripUndefined(obj, schema?)` | Remove undefined values and non-schema keys |
| `parseMutationInput(answers)` | Parse JSON input field from CLI mutation commands |
| `buildSelectFromPaths(paths)` | Build ORM select object from dot-notation paths |

### Display (`@constructive-sdk/cli`)

| Export | Description |
|--------|-------------|
| `printSuccess(message)` | Print green success message |
| `printError(message)` | Print red error message to stderr |
| `printWarning(message)` | Print yellow warning to stderr |
| `printInfo(message)` | Print cyan info message |
| `printKeyValue(key, value, indent?)` | Print formatted key-value pair |
| `printDetails(entries, indent?)` | Print aligned key-value block |
| `printTable(headers, rows, indent?)` | Print formatted table |

### Re-exports from inquirerer

| Export | Description |
|--------|-------------|
| `CLI` | Type: The main CLI class |
| `CLIOptions` | Type: CLI configuration options |
| `Inquirerer` | Type: The prompter interface |
| `extractFirst(argv)` | Extract first positional argument |
| `getPackageJson(dir)` | Load package.json from a directory |

## Troubleshooting

### "Cannot find module 'appstash'"
Ensure `appstash` is installed: `pnpm add appstash`

### Type coercion not working
Check that your `FieldSchema` keys match the CLI argument names exactly. Fields not in the schema are ignored by `coerceAnswers`.

### Config store not persisting
The config store writes to `~/.{toolName}/`. Ensure the tool name is consistent across your application. Use `getConfigStore` with the same name everywhere.
63 changes: 63 additions & 0 deletions sdk/constructive-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"name": "@constructive-sdk/cli",
"version": "0.1.0",
"author": "Constructive <developers@constructive.io>",
"description": "Constructive CLI SDK - Auto-generated GraphQL CLI with ORM client, context management, and interactive prompts",
"main": "index.js",
"module": "esm/index.js",
"types": "index.d.ts",
"bin": {
"csdk": "cli.js"
},
"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",
"generate": "tsx scripts/generate-sdk.ts",
"lint": "eslint . --fix",
"test": "jest --passWithNoTests",
"test:watch": "jest --watch"
},
"keywords": [
"cli",
"sdk",
"graphql",
"orm",
"constructive",
"postgraphile",
"schema-dir",
"command-line",
"interactive"
],
"dependencies": {
"@0no-co/graphql.web": "^1.1.2",
"@constructive-io/graphql-types": "workspace:^",
"appstash": "^0.5.0",
"gql-ast": "workspace:^",
"graphql": "^16.13.0",
"inquirerer": "^4.5.2",
"nested-obj": "^0.2.1",
"yanse": "^0.2.1"
},
"devDependencies": {
"@constructive-io/graphql-codegen": "workspace:^",
"@types/node": "^22.19.11",
"makage": "^0.1.12",
"tsx": "^4.19.0",
"typescript": "^5.9.3"
}
}
Loading