Skip to content

Commit 239a5ca

Browse files
authored
Merge pull request #781 from constructive-io/devin/1772673679-constructive-cli
feat: add @constructive-sdk/cli SDK package with generated CLI commands and skills
2 parents d0d7d39 + 30c1384 commit 239a5ca

File tree

747 files changed

+152218
-7344
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

747 files changed

+152218
-7344
lines changed

pnpm-lock.yaml

Lines changed: 2591 additions & 7344 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/constructive-cli/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# GraphQL SDK
2+
3+
<p align="center" width="100%">
4+
<img height="120" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
5+
</p>
6+
7+
<!-- @constructive-io/graphql-codegen - DO NOT EDIT -->
8+
9+
## APIs
10+
11+
| API | Endpoint | Generators | Docs |
12+
|-----|----------|------------|------|
13+
| admin | - | ORM, CLI | [./src/admin/README.md](./src/admin/README.md) |
14+
| auth | - | ORM, CLI | [./src/auth/README.md](./src/auth/README.md) |
15+
| objects | - | ORM, CLI | [./src/objects/README.md](./src/objects/README.md) |
16+
| public | - | ORM, CLI | [./src/public/README.md](./src/public/README.md) |
17+
18+
---
19+
20+
Built by the [Constructive](https://constructive.io) team.
21+
22+
## Disclaimer
23+
24+
AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
25+
26+
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.

sdk/constructive-cli/SKILL.md

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
---
2+
name: constructive-cli
3+
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.
4+
compatibility: inquirerer, appstash, yanse, Node.js 18+, TypeScript
5+
metadata:
6+
author: constructive-io
7+
version: "0.1.0"
8+
---
9+
10+
# Constructive CLI SDK
11+
12+
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.
13+
14+
## When to Apply
15+
16+
- Creating a new CLI tool in a Constructive project
17+
- Adding interactive prompts or argument parsing to a command
18+
- Managing persistent CLI configuration (contexts, credentials, settings)
19+
- Formatting CLI output with colors, tables, or key-value displays
20+
- Coercing CLI string arguments to proper GraphQL types
21+
- Building nested subcommand structures (e.g. `cli context create`)
22+
- Working with generated CLI code from `@constructive-io/graphql-codegen`
23+
24+
## Installation
25+
26+
```bash
27+
pnpm add @constructive-sdk/cli
28+
```
29+
30+
## Quick Start
31+
32+
### Creating a CLI with Commands
33+
34+
```typescript
35+
import { CLI } from 'inquirerer';
36+
import { buildCommands, CommandHandler } from '@constructive-sdk/cli';
37+
38+
const hello: CommandHandler = async (argv, prompter, _options) => {
39+
const answers = await prompter.prompt(argv, [
40+
{ type: 'text', name: 'name', message: 'Your name' }
41+
]);
42+
console.log(`Hello, ${answers.name}!`);
43+
};
44+
45+
const commands = buildCommands([
46+
{ name: 'hello', handler: hello, usage: 'Say hello' }
47+
]);
48+
49+
const app = new CLI(commands);
50+
app.run();
51+
```
52+
53+
### Config Management with appstash
54+
55+
```typescript
56+
import { getConfigStore } from '@constructive-sdk/cli';
57+
58+
const store = getConfigStore('my-tool');
59+
60+
// Create and manage contexts
61+
store.createContext('production', { endpoint: 'https://api.example.com/graphql' });
62+
store.setCurrentContext('production');
63+
64+
// Store credentials
65+
store.setCredentials('production', { token: 'bearer-token-here' });
66+
67+
// Load current context
68+
const ctx = store.getCurrentContext();
69+
```
70+
71+
### Type Coercion for CLI Arguments
72+
73+
```typescript
74+
import { coerceAnswers, stripUndefined, FieldSchema } from '@constructive-sdk/cli';
75+
76+
const schema: FieldSchema = {
77+
name: 'string',
78+
age: 'int',
79+
active: 'boolean',
80+
metadata: 'json'
81+
};
82+
83+
// CLI args arrive as strings from minimist
84+
const rawArgs = { name: 'Alice', age: '30', active: 'true', metadata: '{"role":"admin"}' };
85+
86+
// Coerce to proper types
87+
const typed = coerceAnswers(rawArgs, schema);
88+
// { name: 'Alice', age: 30, active: true, metadata: { role: 'admin' } }
89+
90+
// Strip undefined values and extra minimist fields
91+
const clean = stripUndefined(typed, schema);
92+
```
93+
94+
### Display Utilities
95+
96+
```typescript
97+
import { printSuccess, printError, printTable, printDetails } from '@constructive-sdk/cli';
98+
99+
printSuccess('Context created');
100+
printError('Connection failed');
101+
102+
printTable(
103+
['Name', 'Endpoint', 'Status'],
104+
[
105+
['production', 'https://api.example.com/graphql', 'active'],
106+
['staging', 'https://staging.example.com/graphql', 'inactive']
107+
]
108+
);
109+
110+
printDetails([
111+
{ key: 'Name', value: 'production' },
112+
{ key: 'Endpoint', value: 'https://api.example.com/graphql' }
113+
]);
114+
```
115+
116+
### Subcommand Dispatching
117+
118+
```typescript
119+
import { createSubcommandHandler, CommandHandler } from '@constructive-sdk/cli';
120+
121+
const createCmd: CommandHandler = async (argv, prompter, options) => {
122+
// Handle 'context create'
123+
};
124+
125+
const listCmd: CommandHandler = async (argv, prompter, options) => {
126+
// Handle 'context list'
127+
};
128+
129+
const contextHandler = createSubcommandHandler(
130+
{ create: createCmd, list: listCmd },
131+
'Usage: my-tool context <create|list>'
132+
);
133+
```
134+
135+
## API Reference
136+
137+
### Config (`@constructive-sdk/cli`)
138+
139+
| Export | Description |
140+
|--------|-------------|
141+
| `getAppDirs(toolName, options?)` | Get XDG-compliant app directories for a CLI tool |
142+
| `getConfigStore(toolName)` | Create a config store with context and credential management |
143+
144+
### Commands (`@constructive-sdk/cli`)
145+
146+
| Export | Description |
147+
|--------|-------------|
148+
| `buildCommands(definitions)` | Build a commands map from command definitions |
149+
| `createSubcommandHandler(subcommands, usage)` | Create a handler that dispatches to subcommands |
150+
| `CommandHandler` | Type: `(argv, prompter, options) => Promise<void>` |
151+
| `CommandDefinition` | Interface: `{ name, handler, usage? }` |
152+
153+
### CLI Utilities (`@constructive-sdk/cli`)
154+
155+
| Export | Description |
156+
|--------|-------------|
157+
| `coerceAnswers(answers, schema)` | Coerce string CLI args to proper GraphQL types |
158+
| `stripUndefined(obj, schema?)` | Remove undefined values and non-schema keys |
159+
| `parseMutationInput(answers)` | Parse JSON input field from CLI mutation commands |
160+
| `buildSelectFromPaths(paths)` | Build ORM select object from dot-notation paths |
161+
162+
### Display (`@constructive-sdk/cli`)
163+
164+
| Export | Description |
165+
|--------|-------------|
166+
| `printSuccess(message)` | Print green success message |
167+
| `printError(message)` | Print red error message to stderr |
168+
| `printWarning(message)` | Print yellow warning to stderr |
169+
| `printInfo(message)` | Print cyan info message |
170+
| `printKeyValue(key, value, indent?)` | Print formatted key-value pair |
171+
| `printDetails(entries, indent?)` | Print aligned key-value block |
172+
| `printTable(headers, rows, indent?)` | Print formatted table |
173+
174+
### Re-exports from inquirerer
175+
176+
| Export | Description |
177+
|--------|-------------|
178+
| `CLI` | Type: The main CLI class |
179+
| `CLIOptions` | Type: CLI configuration options |
180+
| `Inquirerer` | Type: The prompter interface |
181+
| `extractFirst(argv)` | Extract first positional argument |
182+
| `getPackageJson(dir)` | Load package.json from a directory |
183+
184+
## Troubleshooting
185+
186+
### "Cannot find module 'appstash'"
187+
Ensure `appstash` is installed: `pnpm add appstash`
188+
189+
### Type coercion not working
190+
Check that your `FieldSchema` keys match the CLI argument names exactly. Fields not in the schema are ignored by `coerceAnswers`.
191+
192+
### Config store not persisting
193+
The config store writes to `~/.{toolName}/`. Ensure the tool name is consistent across your application. Use `getConfigStore` with the same name everywhere.

sdk/constructive-cli/package.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"name": "@constructive-sdk/cli",
3+
"version": "0.1.0",
4+
"author": "Constructive <developers@constructive.io>",
5+
"description": "Constructive CLI SDK - Auto-generated GraphQL CLI with ORM client, context management, and interactive prompts",
6+
"main": "index.js",
7+
"module": "esm/index.js",
8+
"types": "index.d.ts",
9+
"bin": {
10+
"csdk": "cli.js"
11+
},
12+
"homepage": "https://github.com/constructive-io/constructive",
13+
"license": "MIT",
14+
"publishConfig": {
15+
"access": "public",
16+
"directory": "dist"
17+
},
18+
"repository": {
19+
"type": "git",
20+
"url": "https://github.com/constructive-io/constructive"
21+
},
22+
"bugs": {
23+
"url": "https://github.com/constructive-io/constructive/issues"
24+
},
25+
"scripts": {
26+
"clean": "makage clean",
27+
"prepack": "npm run build",
28+
"build": "makage build",
29+
"build:dev": "makage build --dev",
30+
"generate": "tsx scripts/generate-sdk.ts",
31+
"lint": "eslint . --fix",
32+
"test": "jest --passWithNoTests",
33+
"test:watch": "jest --watch"
34+
},
35+
"keywords": [
36+
"cli",
37+
"sdk",
38+
"graphql",
39+
"orm",
40+
"constructive",
41+
"postgraphile",
42+
"schema-dir",
43+
"command-line",
44+
"interactive"
45+
],
46+
"dependencies": {
47+
"@0no-co/graphql.web": "^1.1.2",
48+
"@constructive-io/graphql-types": "workspace:^",
49+
"appstash": "^0.5.0",
50+
"gql-ast": "workspace:^",
51+
"graphql": "^16.13.0",
52+
"inquirerer": "^4.5.2",
53+
"nested-obj": "^0.2.1",
54+
"yanse": "^0.2.1"
55+
},
56+
"devDependencies": {
57+
"@constructive-io/graphql-codegen": "workspace:^",
58+
"@types/node": "^22.19.11",
59+
"makage": "^0.1.12",
60+
"tsx": "^4.19.0",
61+
"typescript": "^5.9.3"
62+
}
63+
}

0 commit comments

Comments
 (0)