api-to-cli 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/LICENSE +21 -0
  2. package/PROJECT_BRIEF.md +65 -0
  3. package/README.md +130 -0
  4. package/SPEC.md +99 -0
  5. package/bin/api-to-cli.js +5 -0
  6. package/examples/trello/api-to-cli.config.js +60 -0
  7. package/examples/trello/trelloapi-agent/README.md +11 -0
  8. package/examples/trello/trelloapi-agent/agentbridge.manifest.json +68 -0
  9. package/examples/trello/trelloapi-agent/cli/README.md +25 -0
  10. package/examples/trello/trelloapi-agent/cli/bin/trelloapi.js +37 -0
  11. package/examples/trello/trelloapi-agent/cli/commands/get-board.js +41 -0
  12. package/examples/trello/trelloapi-agent/cli/commands/list-board-lists.js +41 -0
  13. package/examples/trello/trelloapi-agent/cli/commands/list-list-cards.js +41 -0
  14. package/examples/trello/trelloapi-agent/cli/lib/client.js +90 -0
  15. package/examples/trello/trelloapi-agent/cli/lib/output.js +21 -0
  16. package/examples/trello/trelloapi-agent/cli/package.json +16 -0
  17. package/examples/trello/trelloapi-agent/skill/SKILL.md +34 -0
  18. package/examples/trello/trelloapi-cli/README.md +25 -0
  19. package/examples/trello/trelloapi-cli/bin/trelloapi.js +37 -0
  20. package/examples/trello/trelloapi-cli/commands/get-board.js +41 -0
  21. package/examples/trello/trelloapi-cli/commands/list-board-lists.js +41 -0
  22. package/examples/trello/trelloapi-cli/commands/list-list-cards.js +41 -0
  23. package/examples/trello/trelloapi-cli/lib/client.js +90 -0
  24. package/examples/trello/trelloapi-cli/lib/output.js +21 -0
  25. package/examples/trello/trelloapi-cli/package.json +16 -0
  26. package/package.json +48 -0
  27. package/src/commands/generate.js +36 -0
  28. package/src/commands/scaffold.js +110 -0
  29. package/src/commands/validate.js +30 -0
  30. package/src/index.js +92 -0
  31. package/src/lib/config-utils.js +21 -0
  32. package/src/lib/generate-cli.js +295 -0
  33. package/src/lib/generate-manifest.js +51 -0
  34. package/src/lib/generate-skill.js +50 -0
  35. package/src/lib/load-config.js +120 -0
@@ -0,0 +1,90 @@
1
+ async function request(command, options) {
2
+ const auth = {
3
+ "credentials": [
4
+ {
5
+ "envVar": "TRELLO_KEY",
6
+ "in": "query",
7
+ "name": "key"
8
+ },
9
+ {
10
+ "envVar": "TRELLO_TOKEN",
11
+ "in": "query",
12
+ "name": "token"
13
+ }
14
+ ]
15
+ };
16
+ const params = new URLSearchParams();
17
+ const commandParams = command.params || {};
18
+ let resolvedPath = command.path;
19
+ const headers = {
20
+ accept: 'application/json'
21
+ };
22
+
23
+ (auth.credentials || []).forEach((credential) => {
24
+ const envValue = process.env[credential.envVar];
25
+
26
+ if (!envValue) {
27
+ throw new Error(`Missing required auth environment variable: ${credential.envVar}`);
28
+ }
29
+
30
+ const authValue = credential.prefix ? `${credential.prefix}${envValue}` : envValue;
31
+
32
+ if (credential.in === 'header') {
33
+ headers[credential.name] = authValue;
34
+ return;
35
+ }
36
+
37
+ params.append(credential.name, authValue);
38
+ });
39
+
40
+ Object.entries(commandParams).forEach(([name, schema]) => {
41
+ const value = options[name];
42
+
43
+ if ((value === undefined || value === null || value === '') && schema.required) {
44
+ throw new Error(`Missing required parameter: --${name}`);
45
+ }
46
+
47
+ if (value === undefined || value === null || value === '') {
48
+ return;
49
+ }
50
+
51
+ const token = `{${name}}`;
52
+
53
+ if (resolvedPath.includes(token)) {
54
+ resolvedPath = resolvedPath.replaceAll(token, encodeURIComponent(String(value)));
55
+ return;
56
+ }
57
+
58
+ params.append(name, String(value));
59
+ });
60
+
61
+ const query = params.toString();
62
+ const url = 'https://api.trello.com/1' + resolvedPath + (query ? '?' + query : '');
63
+
64
+ const response = await fetch(url, {
65
+ method: command.method,
66
+ headers
67
+ });
68
+
69
+ const text = await response.text();
70
+ let body = text;
71
+
72
+ try {
73
+ body = text ? JSON.parse(text) : null;
74
+ } catch (_err) {
75
+ body = text;
76
+ }
77
+
78
+ if (!response.ok) {
79
+ const error = new Error(`HTTP ${response.status}`);
80
+ error.statusCode = response.status;
81
+ error.responseBody = body;
82
+ throw error;
83
+ }
84
+
85
+ return body;
86
+ }
87
+
88
+ module.exports = {
89
+ request
90
+ };
@@ -0,0 +1,21 @@
1
+ function json(data, pretty) {
2
+ const text = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
3
+ process.stdout.write(text + '\n');
4
+ }
5
+
6
+ function error(payload, pretty) {
7
+ const envelope = {
8
+ error: true,
9
+ code: payload.code || 'REQUEST_FAILED',
10
+ message: payload.message || 'Request failed',
11
+ details: payload.details || {}
12
+ };
13
+
14
+ const text = pretty ? JSON.stringify(envelope, null, 2) : JSON.stringify(envelope);
15
+ process.stderr.write(text + '\n');
16
+ }
17
+
18
+ module.exports = {
19
+ json,
20
+ error
21
+ };
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "trelloapi-cli",
3
+ "version": "1.0.0",
4
+ "description": "trelloapi CLI generated by AgentBridge",
5
+ "license": "MIT",
6
+ "type": "commonjs",
7
+ "bin": {
8
+ "trelloapi": "./bin/trelloapi.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node ./bin/trelloapi.js"
12
+ },
13
+ "dependencies": {
14
+ "commander": "^12.1.0"
15
+ }
16
+ }
@@ -0,0 +1,34 @@
1
+ # trelloapi CLI Skill
2
+
3
+ ## Purpose
4
+ Use the generated trelloapi CLI from AgentBridge. Always prefer JSON output for machine parsing.
5
+
6
+ ## Location
7
+ - CLI project: ./cli
8
+ - Binary name: trelloapi
9
+
10
+ ## Setup
11
+ 1. cd ./cli
12
+ 2. npm install
13
+ 3. npm link
14
+
15
+ ## Auth
16
+ - export TRELLO_KEY="<value>"
17
+ - export TRELLO_TOKEN="<value>"
18
+
19
+ ## Commands
20
+ - get-board: Get a board by ID
21
+ - --board-id <value> (required)
22
+ - example: trelloapi get-board --board-id <value>
23
+ - list-board-lists: List lists on a board
24
+ - --board-id <value> (required)
25
+ - example: trelloapi list-board-lists --board-id <value>
26
+ - list-list-cards: List cards in a list
27
+ - --list-id <value> (required)
28
+ - example: trelloapi list-list-cards --list-id <value>
29
+
30
+ ## Rules
31
+ - Do not echo or log auth secrets.
32
+ - Do not pass credentials as command flags.
33
+ - Parse command stdout as JSON.
34
+ - Treat non-zero exits as failure and read stderr JSON envelope.
@@ -0,0 +1,25 @@
1
+ # trelloapi CLI
2
+
3
+ Generated by AgentBridge (api-to-cli).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install
9
+ npm link
10
+ ```
11
+
12
+ ## Auth
13
+
14
+ Set required environment variables before running commands:
15
+
16
+ - `TRELLO_KEY`
17
+ - `TRELLO_TOKEN`
18
+
19
+ Do not pass secrets as command flags.
20
+
21
+ ## Commands
22
+
23
+ - `trelloapi get-board` - Get a board by ID
24
+ - `trelloapi list-board-lists` - List lists on a board
25
+ - `trelloapi list-list-cards` - List cards in a list
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+
5
+ const cmd0 = require('../commands/get-board');
6
+ const cmd1 = require('../commands/list-board-lists');
7
+ const cmd2 = require('../commands/list-list-cards');
8
+
9
+ const program = new Command();
10
+
11
+ program
12
+ .name('trelloapi')
13
+ .description('trelloapi CLI generated by AgentBridge')
14
+ .version('1.0.0');
15
+
16
+ program
17
+ .command('get-board')
18
+ .description('Get a board by ID')
19
+ .option('--board-id <value>', 'Trello board ID')
20
+ .option('--pretty', 'Pretty-print JSON')
21
+ .action((options) => cmd0.run(options));
22
+
23
+ program
24
+ .command('list-board-lists')
25
+ .description('List lists on a board')
26
+ .option('--board-id <value>', 'Trello board ID')
27
+ .option('--pretty', 'Pretty-print JSON')
28
+ .action((options) => cmd1.run(options));
29
+
30
+ program
31
+ .command('list-list-cards')
32
+ .description('List cards in a list')
33
+ .option('--list-id <value>', 'Trello list ID')
34
+ .option('--pretty', 'Pretty-print JSON')
35
+ .action((options) => cmd2.run(options));
36
+
37
+ program.parse(process.argv);
@@ -0,0 +1,41 @@
1
+ const { request } = require('../lib/client');
2
+ const output = require('../lib/output');
3
+
4
+ const command = {
5
+ "name": "get-board",
6
+ "description": "Get a board by ID",
7
+ "method": "GET",
8
+ "path": "/boards/{boardId}",
9
+ "params": {
10
+ "boardId": {
11
+ "type": "string",
12
+ "required": true,
13
+ "description": "Trello board ID"
14
+ }
15
+ }
16
+ };
17
+
18
+ async function getBoard(options) {
19
+ try {
20
+ const data = await request(command, options);
21
+ output.json(data, Boolean(options.pretty));
22
+ } catch (error) {
23
+ output.error(
24
+ {
25
+ code: error.statusCode ? 'HTTP_ERROR' : 'REQUEST_FAILED',
26
+ message: error.message,
27
+ details: {
28
+ statusCode: error.statusCode || null,
29
+ command: command.name
30
+ }
31
+ },
32
+ Boolean(options.pretty)
33
+ );
34
+ process.exit(1);
35
+ }
36
+ }
37
+
38
+ module.exports = {
39
+ run: getBoard,
40
+ command
41
+ };
@@ -0,0 +1,41 @@
1
+ const { request } = require('../lib/client');
2
+ const output = require('../lib/output');
3
+
4
+ const command = {
5
+ "name": "list-board-lists",
6
+ "description": "List lists on a board",
7
+ "method": "GET",
8
+ "path": "/boards/{boardId}/lists",
9
+ "params": {
10
+ "boardId": {
11
+ "type": "string",
12
+ "required": true,
13
+ "description": "Trello board ID"
14
+ }
15
+ }
16
+ };
17
+
18
+ async function listBoardLists(options) {
19
+ try {
20
+ const data = await request(command, options);
21
+ output.json(data, Boolean(options.pretty));
22
+ } catch (error) {
23
+ output.error(
24
+ {
25
+ code: error.statusCode ? 'HTTP_ERROR' : 'REQUEST_FAILED',
26
+ message: error.message,
27
+ details: {
28
+ statusCode: error.statusCode || null,
29
+ command: command.name
30
+ }
31
+ },
32
+ Boolean(options.pretty)
33
+ );
34
+ process.exit(1);
35
+ }
36
+ }
37
+
38
+ module.exports = {
39
+ run: listBoardLists,
40
+ command
41
+ };
@@ -0,0 +1,41 @@
1
+ const { request } = require('../lib/client');
2
+ const output = require('../lib/output');
3
+
4
+ const command = {
5
+ "name": "list-list-cards",
6
+ "description": "List cards in a list",
7
+ "method": "GET",
8
+ "path": "/lists/{listId}/cards",
9
+ "params": {
10
+ "listId": {
11
+ "type": "string",
12
+ "required": true,
13
+ "description": "Trello list ID"
14
+ }
15
+ }
16
+ };
17
+
18
+ async function listListCards(options) {
19
+ try {
20
+ const data = await request(command, options);
21
+ output.json(data, Boolean(options.pretty));
22
+ } catch (error) {
23
+ output.error(
24
+ {
25
+ code: error.statusCode ? 'HTTP_ERROR' : 'REQUEST_FAILED',
26
+ message: error.message,
27
+ details: {
28
+ statusCode: error.statusCode || null,
29
+ command: command.name
30
+ }
31
+ },
32
+ Boolean(options.pretty)
33
+ );
34
+ process.exit(1);
35
+ }
36
+ }
37
+
38
+ module.exports = {
39
+ run: listListCards,
40
+ command
41
+ };
@@ -0,0 +1,90 @@
1
+ async function request(command, options) {
2
+ const auth = {
3
+ "credentials": [
4
+ {
5
+ "envVar": "TRELLO_KEY",
6
+ "in": "query",
7
+ "name": "key"
8
+ },
9
+ {
10
+ "envVar": "TRELLO_TOKEN",
11
+ "in": "query",
12
+ "name": "token"
13
+ }
14
+ ]
15
+ };
16
+ const params = new URLSearchParams();
17
+ const commandParams = command.params || {};
18
+ let resolvedPath = command.path;
19
+ const headers = {
20
+ accept: 'application/json'
21
+ };
22
+
23
+ (auth.credentials || []).forEach((credential) => {
24
+ const envValue = process.env[credential.envVar];
25
+
26
+ if (!envValue) {
27
+ throw new Error(`Missing required auth environment variable: ${credential.envVar}`);
28
+ }
29
+
30
+ const authValue = credential.prefix ? `${credential.prefix}${envValue}` : envValue;
31
+
32
+ if (credential.in === 'header') {
33
+ headers[credential.name] = authValue;
34
+ return;
35
+ }
36
+
37
+ params.append(credential.name, authValue);
38
+ });
39
+
40
+ Object.entries(commandParams).forEach(([name, schema]) => {
41
+ const value = options[name];
42
+
43
+ if ((value === undefined || value === null || value === '') && schema.required) {
44
+ throw new Error(`Missing required parameter: --${name}`);
45
+ }
46
+
47
+ if (value === undefined || value === null || value === '') {
48
+ return;
49
+ }
50
+
51
+ const token = `{${name}}`;
52
+
53
+ if (resolvedPath.includes(token)) {
54
+ resolvedPath = resolvedPath.replaceAll(token, encodeURIComponent(String(value)));
55
+ return;
56
+ }
57
+
58
+ params.append(name, String(value));
59
+ });
60
+
61
+ const query = params.toString();
62
+ const url = 'https://api.trello.com/1' + resolvedPath + (query ? '?' + query : '');
63
+
64
+ const response = await fetch(url, {
65
+ method: command.method,
66
+ headers
67
+ });
68
+
69
+ const text = await response.text();
70
+ let body = text;
71
+
72
+ try {
73
+ body = text ? JSON.parse(text) : null;
74
+ } catch (_err) {
75
+ body = text;
76
+ }
77
+
78
+ if (!response.ok) {
79
+ const error = new Error(`HTTP ${response.status}`);
80
+ error.statusCode = response.status;
81
+ error.responseBody = body;
82
+ throw error;
83
+ }
84
+
85
+ return body;
86
+ }
87
+
88
+ module.exports = {
89
+ request
90
+ };
@@ -0,0 +1,21 @@
1
+ function json(data, pretty) {
2
+ const text = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
3
+ process.stdout.write(text + '\n');
4
+ }
5
+
6
+ function error(payload, pretty) {
7
+ const envelope = {
8
+ error: true,
9
+ code: payload.code || 'REQUEST_FAILED',
10
+ message: payload.message || 'Request failed',
11
+ details: payload.details || {}
12
+ };
13
+
14
+ const text = pretty ? JSON.stringify(envelope, null, 2) : JSON.stringify(envelope);
15
+ process.stderr.write(text + '\n');
16
+ }
17
+
18
+ module.exports = {
19
+ json,
20
+ error
21
+ };
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "trelloapi-cli",
3
+ "version": "1.0.0",
4
+ "description": "trelloapi CLI generated by AgentBridge",
5
+ "license": "MIT",
6
+ "type": "commonjs",
7
+ "bin": {
8
+ "trelloapi": "./bin/trelloapi.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node ./bin/trelloapi.js"
12
+ },
13
+ "dependencies": {
14
+ "commander": "^12.1.0"
15
+ }
16
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "api-to-cli",
3
+ "version": "0.1.1",
4
+ "description": "Generate AI-agent-friendly CLIs, skills, and manifests from API configs",
5
+ "license": "MIT",
6
+ "type": "commonjs",
7
+ "bin": {
8
+ "api-to-cli": "./bin/api-to-cli.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "src",
13
+ "examples",
14
+ "README.md",
15
+ "LICENSE",
16
+ "PROJECT_BRIEF.md",
17
+ "SPEC.md"
18
+ ],
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/RandyVentures/AgentBridge.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/RandyVentures/AgentBridge/issues"
28
+ },
29
+ "homepage": "https://github.com/RandyVentures/AgentBridge#readme",
30
+ "keywords": [
31
+ "cli",
32
+ "api",
33
+ "ai-agent",
34
+ "generator",
35
+ "mcp",
36
+ "developer-tools"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "scripts": {
42
+ "validate:trello": "node ./bin/api-to-cli.js validate --config ./examples/trello/api-to-cli.config.js",
43
+ "generate:trello": "node ./bin/api-to-cli.js generate --config ./examples/trello/api-to-cli.config.js --output ./examples/trello/trelloapi-cli",
44
+ "scaffold:trello": "node ./bin/api-to-cli.js scaffold --config ./examples/trello/api-to-cli.config.js --output ./examples/trello/trelloapi-agent",
45
+ "test:smoke": "npm run validate:trello && npm run generate:trello && npm run scaffold:trello",
46
+ "pack:check": "npm pack --dry-run"
47
+ }
48
+ }
@@ -0,0 +1,36 @@
1
+ const path = require('path');
2
+ const { loadConfig } = require('../lib/load-config');
3
+ const { generateCliProject } = require('../lib/generate-cli');
4
+
5
+ async function generate(flags) {
6
+ if (!flags.config) {
7
+ throw new Error('Missing required flag: --config <path>');
8
+ }
9
+
10
+ if (!flags.output) {
11
+ throw new Error('Missing required flag: --output <dir>');
12
+ }
13
+
14
+ const configPath = path.resolve(process.cwd(), String(flags.config));
15
+ const outputPath = path.resolve(process.cwd(), String(flags.output));
16
+
17
+ const config = loadConfig(configPath);
18
+
19
+ generateCliProject({
20
+ config,
21
+ outputPath
22
+ });
23
+
24
+ console.log(
25
+ JSON.stringify({
26
+ ok: true,
27
+ command: 'generate',
28
+ outputPath,
29
+ generatedCommands: config.commands.map((command) => command.name)
30
+ })
31
+ );
32
+ }
33
+
34
+ module.exports = {
35
+ generate
36
+ };
@@ -0,0 +1,110 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { loadConfig } = require('../lib/load-config');
4
+ const { generateCliProject } = require('../lib/generate-cli');
5
+ const { writeSkillPackage } = require('../lib/generate-skill');
6
+ const { buildManifest, writeManifest } = require('../lib/generate-manifest');
7
+
8
+ function hasOwn(obj, key) {
9
+ return Object.prototype.hasOwnProperty.call(obj, key);
10
+ }
11
+
12
+ function resolveArtifactFlags(flags) {
13
+ const hasSkill = hasOwn(flags, 'with-skill');
14
+ const hasManifest = hasOwn(flags, 'with-manifest');
15
+
16
+ if (!hasSkill && !hasManifest) {
17
+ return {
18
+ withSkill: true,
19
+ withManifest: true
20
+ };
21
+ }
22
+
23
+ return {
24
+ withSkill: Boolean(flags['with-skill']),
25
+ withManifest: Boolean(flags['with-manifest'])
26
+ };
27
+ }
28
+
29
+ function writeScaffoldReadme(outputPath, data) {
30
+ const lines = [
31
+ '# AgentBridge Scaffold Output',
32
+ '',
33
+ '## Contents',
34
+ `- CLI project: ${data.cliProjectPath}`
35
+ ];
36
+
37
+ if (data.skillPath) {
38
+ lines.push(`- Skill file: ${data.skillPath}`);
39
+ }
40
+
41
+ if (data.manifestPath) {
42
+ lines.push(`- Manifest: ${data.manifestPath}`);
43
+ }
44
+
45
+ lines.push('', '## Next Steps', '1. cd ./cli', '2. npm install', '3. npm link');
46
+
47
+ fs.writeFileSync(path.join(outputPath, 'README.md'), `${lines.join('\n')}\n`, 'utf8');
48
+ }
49
+
50
+ async function scaffold(flags) {
51
+ if (!flags.config) {
52
+ throw new Error('Missing required flag: --config <path>');
53
+ }
54
+
55
+ if (!flags.output) {
56
+ throw new Error('Missing required flag: --output <dir>');
57
+ }
58
+
59
+ const { withSkill, withManifest } = resolveArtifactFlags(flags);
60
+ const cliRelativePath = './cli';
61
+ const skillRelativePath = './skill/SKILL.md';
62
+ const manifestRelativePath = './agentbridge.manifest.json';
63
+
64
+ const configPath = path.resolve(process.cwd(), String(flags.config));
65
+ const outputPath = path.resolve(process.cwd(), String(flags.output));
66
+ const cliProjectPath = path.join(outputPath, 'cli');
67
+
68
+ const config = loadConfig(configPath);
69
+ fs.mkdirSync(outputPath, { recursive: true });
70
+
71
+ generateCliProject({
72
+ config,
73
+ outputPath: cliProjectPath
74
+ });
75
+
76
+ let skillPath = null;
77
+ if (withSkill) {
78
+ writeSkillPackage(outputPath, config, cliRelativePath);
79
+ skillPath = skillRelativePath;
80
+ }
81
+
82
+ let manifestPath = null;
83
+ if (withManifest) {
84
+ const manifest = buildManifest(config, cliRelativePath, skillPath);
85
+ writeManifest(outputPath, manifest);
86
+ manifestPath = manifestRelativePath;
87
+ }
88
+
89
+ writeScaffoldReadme(outputPath, {
90
+ cliProjectPath: cliRelativePath,
91
+ skillPath,
92
+ manifestPath
93
+ });
94
+
95
+ console.log(
96
+ JSON.stringify({
97
+ ok: true,
98
+ command: 'scaffold',
99
+ outputPath,
100
+ cliProjectPath: cliRelativePath,
101
+ skillPath,
102
+ manifestPath,
103
+ generatedCommands: config.commands.map((command) => command.name)
104
+ })
105
+ );
106
+ }
107
+
108
+ module.exports = {
109
+ scaffold
110
+ };