argsbarg 5.1.16 → 6.0.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.
- package/CHANGELOG.md +46 -1
- package/README.md +33 -25
- package/docs/README.md +2 -1
- package/docs/api-server.md +141 -0
- package/docs/bundled-docs.md +24 -10
- package/docs/cli-program.md +17 -2
- package/docs/config-schema.md +18 -8
- package/docs/developing.md +1 -1
- package/docs/mcp.md +5 -5
- package/docs/output-schema.md +78 -47
- package/examples/full-example/README.md +15 -6
- package/examples/full-example/docs/README.md +27 -0
- package/examples/full-example/docs/api.md +511 -0
- package/examples/full-example/docs/cli-schema.json +453 -0
- package/examples/full-example/docs/http.md +81 -0
- package/examples/full-example/docs/mcp.md +159 -0
- package/examples/full-example/docs/openapi.json +222 -0
- package/examples/full-example/docs/skill.md +46 -0
- package/examples/full-example/justfile +6 -2
- package/examples/full-example/schemas/generated/app-config.json +1 -1
- package/examples/full-example/schemas/generated/status.json +1 -1
- package/examples/full-example/scripts/dev-formula.ts +1 -1
- package/examples/full-example/scripts/schemagen/discover-schema-roots.test.ts +3 -3
- package/examples/full-example/scripts/schemagen/discover-schema-roots.ts +90 -35
- package/examples/full-example/scripts/schemagen/naming.ts +17 -0
- package/examples/full-example/scripts/schemagen.ts +14 -3
- package/examples/full-example/src/commands/echo/command.ts +6 -1
- package/examples/full-example/src/commands/status/schema-types.ts +14 -0
- package/examples/full-example/src/commands/status/types.ts +1 -11
- package/examples/full-example/src/{types.ts → config/schema-types.ts} +3 -2
- package/examples/full-example/src/program.ts +3 -0
- package/examples/mcp-test.ts +13 -2
- package/examples/nested.ts +12 -3
- package/examples/servers.ts +72 -0
- package/index.d.ts +66 -7
- package/package.json +17 -17
- package/src/api/openapi.ts +117 -0
- package/src/api/result.ts +111 -0
- package/src/api/schema-deref.test.ts +99 -0
- package/src/api/schema-deref.ts +76 -0
- package/src/api/server.ts +120 -0
- package/src/api.integration.test.ts +441 -0
- package/src/builtins/api.ts +38 -0
- package/src/builtins/dispatch.ts +26 -0
- package/src/builtins/registry.ts +4 -0
- package/src/capabilities.ts +12 -1
- package/src/cli-errors.ts +3 -0
- package/src/cli-tool/full-example-capabilities.test.ts +3 -0
- package/src/cli.ts +60 -8
- package/src/config.integration.test.ts +22 -4
- package/src/context.ts +29 -1
- package/src/docs/api-guide.ts +2 -2
- package/src/docs/builtin.ts +11 -1
- package/src/docs/docs.test.ts +70 -12
- package/src/docs/http-guide.ts +132 -0
- package/src/docs/mcp-guide.ts +3 -3
- package/src/docs/mcp-resources.ts +5 -2
- package/src/docs/resolve.ts +26 -2
- package/src/docs/save.ts +5 -2
- package/src/headless/tool-call.ts +157 -0
- package/src/headless.test.ts +4 -2
- package/src/headless.ts +10 -5
- package/src/index.ts +5 -0
- package/src/mcp/result.ts +39 -34
- package/src/mcp/server.ts +18 -36
- package/src/mcp/tools.ts +14 -3
- package/src/mcp.integration.test.ts +46 -39
- package/src/parse.test.ts +16 -6
- package/src/respond.ts +48 -0
- package/src/schema.ts +1 -1
- package/src/skill/generate.ts +1 -1
- package/src/types.ts +46 -4
- package/src/validate.ts +7 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.1.0",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "full-example",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"description": "Argsbarg full example reference app"
|
|
7
|
+
},
|
|
8
|
+
"paths": {
|
|
9
|
+
"/tools/echo": {
|
|
10
|
+
"post": {
|
|
11
|
+
"operationId": "echo",
|
|
12
|
+
"summary": "echo — Echo a message (MCP-friendly leaf).",
|
|
13
|
+
"requestBody": {
|
|
14
|
+
"required": false,
|
|
15
|
+
"content": {
|
|
16
|
+
"application/json; charset=utf-8": {
|
|
17
|
+
"schema": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"message": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Text to print."
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"additionalProperties": false,
|
|
26
|
+
"required": [
|
|
27
|
+
"message"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"responses": {
|
|
34
|
+
"200": {
|
|
35
|
+
"description": "Successful tool invocation",
|
|
36
|
+
"content": {
|
|
37
|
+
"application/json": {
|
|
38
|
+
"schema": {
|
|
39
|
+
"type": "object"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"400": {
|
|
45
|
+
"description": "Invalid arguments or help requested",
|
|
46
|
+
"content": {
|
|
47
|
+
"application/json; charset=utf-8": {
|
|
48
|
+
"schema": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"properties": {
|
|
51
|
+
"error": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
},
|
|
54
|
+
"exitCode": {
|
|
55
|
+
"type": "integer"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"required": [
|
|
59
|
+
"error"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"404": {
|
|
66
|
+
"description": "Unknown tool",
|
|
67
|
+
"content": {
|
|
68
|
+
"application/json; charset=utf-8": {
|
|
69
|
+
"schema": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"properties": {
|
|
72
|
+
"error": {
|
|
73
|
+
"type": "string"
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"required": [
|
|
77
|
+
"error"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"500": {
|
|
84
|
+
"description": "Handler error",
|
|
85
|
+
"content": {
|
|
86
|
+
"application/json; charset=utf-8": {
|
|
87
|
+
"schema": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"properties": {
|
|
90
|
+
"error": {
|
|
91
|
+
"type": "string"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"required": [
|
|
95
|
+
"error"
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"/tools/status": {
|
|
105
|
+
"post": {
|
|
106
|
+
"operationId": "status",
|
|
107
|
+
"summary": "status — Show resolved config and app version.",
|
|
108
|
+
"requestBody": {
|
|
109
|
+
"required": false,
|
|
110
|
+
"content": {
|
|
111
|
+
"application/json; charset=utf-8": {
|
|
112
|
+
"schema": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"properties": {
|
|
115
|
+
"json": {
|
|
116
|
+
"type": "boolean",
|
|
117
|
+
"description": "Emit JSON."
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"additionalProperties": false
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"responses": {
|
|
126
|
+
"200": {
|
|
127
|
+
"description": "Successful tool invocation",
|
|
128
|
+
"content": {
|
|
129
|
+
"application/json": {
|
|
130
|
+
"schema": {
|
|
131
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
132
|
+
"type": "object",
|
|
133
|
+
"properties": {
|
|
134
|
+
"defaultRegion": {
|
|
135
|
+
"type": "string",
|
|
136
|
+
"description": "Resolved AWS region."
|
|
137
|
+
},
|
|
138
|
+
"maxRetries": {
|
|
139
|
+
"type": "number",
|
|
140
|
+
"description": "Resolved retry count."
|
|
141
|
+
},
|
|
142
|
+
"apiTokenSet": {
|
|
143
|
+
"type": "boolean",
|
|
144
|
+
"description": "Whether apiToken is set (value never included)."
|
|
145
|
+
},
|
|
146
|
+
"version": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"description": "App version from program root."
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"required": [
|
|
152
|
+
"apiTokenSet",
|
|
153
|
+
"version"
|
|
154
|
+
],
|
|
155
|
+
"description": "JSON payload for `full-example status --json`.",
|
|
156
|
+
"definitions": {}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"400": {
|
|
162
|
+
"description": "Invalid arguments or help requested",
|
|
163
|
+
"content": {
|
|
164
|
+
"application/json; charset=utf-8": {
|
|
165
|
+
"schema": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"properties": {
|
|
168
|
+
"error": {
|
|
169
|
+
"type": "string"
|
|
170
|
+
},
|
|
171
|
+
"exitCode": {
|
|
172
|
+
"type": "integer"
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"required": [
|
|
176
|
+
"error"
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"404": {
|
|
183
|
+
"description": "Unknown tool",
|
|
184
|
+
"content": {
|
|
185
|
+
"application/json; charset=utf-8": {
|
|
186
|
+
"schema": {
|
|
187
|
+
"type": "object",
|
|
188
|
+
"properties": {
|
|
189
|
+
"error": {
|
|
190
|
+
"type": "string"
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"required": [
|
|
194
|
+
"error"
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
"500": {
|
|
201
|
+
"description": "Handler error",
|
|
202
|
+
"content": {
|
|
203
|
+
"application/json; charset=utf-8": {
|
|
204
|
+
"schema": {
|
|
205
|
+
"type": "object",
|
|
206
|
+
"properties": {
|
|
207
|
+
"error": {
|
|
208
|
+
"type": "string"
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
"required": [
|
|
212
|
+
"error"
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: full_example
|
|
3
|
+
description: Operates the full-example CLI (echo, status). Use when the user mentions full-example, echo, status, or related tasks.
|
|
4
|
+
---
|
|
5
|
+
<!-- Generated by full-example docs skill --save; do not edit. -->
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# full-example
|
|
9
|
+
|
|
10
|
+
Argsbarg full example reference app
|
|
11
|
+
|
|
12
|
+
## Execution
|
|
13
|
+
|
|
14
|
+
Invoke via shell:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
full-example <subcommand> [options] [args]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
- **`full-example echo`** — Echo a message (MCP-friendly leaf).
|
|
23
|
+
- **`full-example status`** — Show resolved config and app version. (flags: --json)
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
- **apiToken** (`apiToken` (env: `FULL_EXAMPLE_API_TOKEN`)) — Create at https://example.com/settings/tokens
|
|
28
|
+
- **defaultRegion** (`defaultRegion`) — AWS region for API calls.
|
|
29
|
+
- **maxRetries** (`maxRetries`) — HTTP retry count (0–10).
|
|
30
|
+
- **prefs** (`prefs`) — Local cache preferences (not exported to env).
|
|
31
|
+
|
|
32
|
+
## Pitfalls
|
|
33
|
+
|
|
34
|
+
- Pass `--` before arguments that look like flags.
|
|
35
|
+
|
|
36
|
+
## Reference
|
|
37
|
+
|
|
38
|
+
For full detail, open `reference.md` in this skill directory (same as `full-example docs api`).
|
|
39
|
+
|
|
40
|
+
## Cursor install location
|
|
41
|
+
|
|
42
|
+
- Project: `.cursor/skills/full_example/`
|
|
43
|
+
- Global: `~/.cursor/skills/full_example/`
|
|
44
|
+
|
|
45
|
+
Do not install under `~/.cursor/skills-cursor/` (reserved for Cursor built-ins).
|
|
46
|
+
|
|
@@ -27,10 +27,14 @@ check: schemagen format typecheck
|
|
|
27
27
|
dev *ARGS:
|
|
28
28
|
bun --watch ./src/index.ts {{ARGS}}
|
|
29
29
|
|
|
30
|
-
# Regenerate docs
|
|
30
|
+
# Regenerate consumer docs under ./docs/
|
|
31
31
|
docgen: schemagen
|
|
32
|
-
@just run docs schema --save
|
|
32
|
+
@just run docs cli-schema --save
|
|
33
|
+
@just run docs api --save
|
|
33
34
|
@just run docs skill --save
|
|
35
|
+
@just run docs mcp --save
|
|
36
|
+
@just run docs http --save
|
|
37
|
+
@just run docs openapi --save
|
|
34
38
|
|
|
35
39
|
alias fmt := format
|
|
36
40
|
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
"maxRetries"
|
|
36
36
|
],
|
|
37
37
|
"additionalProperties": false,
|
|
38
|
-
"description": "
|
|
38
|
+
"description": "Application settings for `full-example` (`program.appConfig`).",
|
|
39
39
|
"definitions": {}
|
|
40
40
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
3
|
* Stage or restore the dev Homebrew formula for `just install-local`.
|
|
4
|
-
* `install` — back up release `Formula/
|
|
4
|
+
* `install` — back up release `Formula/full-example.rb` and write a `file://` dev formula.
|
|
5
5
|
* `reset` — restore the release formula from backup.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -5,19 +5,19 @@ import { discoverSchemaRoots } from "./discover-schema-roots.ts";
|
|
|
5
5
|
const projectRoot = join(import.meta.dir, "../..");
|
|
6
6
|
|
|
7
7
|
describe("discover-schema-roots", () => {
|
|
8
|
-
test("finds AppConfig and StatusJsonOutput in
|
|
8
|
+
test("finds AppConfig and StatusJsonOutput in schema-types.ts files", () => {
|
|
9
9
|
const roots = discoverSchemaRoots(projectRoot);
|
|
10
10
|
const config = roots.find((r) => r.kind === "config");
|
|
11
11
|
const output = roots.find((r) => r.kind === "output");
|
|
12
12
|
expect(config).toMatchObject({
|
|
13
13
|
typeName: "AppConfig",
|
|
14
|
-
|
|
14
|
+
path: "src/config/schema-types.ts",
|
|
15
15
|
outfile: "app-config.json",
|
|
16
16
|
exportName: "APP_CONFIG_JSON_SCHEMA",
|
|
17
17
|
});
|
|
18
18
|
expect(output).toMatchObject({
|
|
19
19
|
typeName: "StatusJsonOutput",
|
|
20
|
-
|
|
20
|
+
path: "src/commands/status/schema-types.ts",
|
|
21
21
|
outfile: "status.json",
|
|
22
22
|
exportName: "STATUS_JSON_OUTPUT_SCHEMA",
|
|
23
23
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Discovers schema roots in
|
|
2
|
+
Discovers schema roots in schema-types.ts files via configType / inputType / outputType exports.
|
|
3
3
|
Copy per consumer repo — see docs/output-schema.md and docs/config-schema.md.
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -7,87 +7,142 @@ import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
7
7
|
import { join, relative } from "node:path";
|
|
8
8
|
import {
|
|
9
9
|
configSchemaExportName,
|
|
10
|
+
inputSchemaExportName,
|
|
10
11
|
outfileForConfigType,
|
|
12
|
+
outfileForInputType,
|
|
11
13
|
outfileForOutputType,
|
|
12
14
|
outputSchemaExportName,
|
|
13
15
|
} from "./naming.ts";
|
|
14
16
|
|
|
15
|
-
export type SchemaRootKind = "config" | "output";
|
|
17
|
+
export type SchemaRootKind = "config" | "input" | "output";
|
|
18
|
+
|
|
19
|
+
export type SchemaRole = "configType" | "inputType" | "outputType";
|
|
16
20
|
|
|
17
21
|
export interface SchemaRoot {
|
|
18
22
|
kind: SchemaRootKind;
|
|
19
23
|
typeName: string;
|
|
20
|
-
/** Path relative to project root (e.g. src/types.ts). */
|
|
21
|
-
|
|
24
|
+
/** Path relative to project root (e.g. src/config/schema-types.ts). */
|
|
25
|
+
path: string;
|
|
22
26
|
outfile: string;
|
|
23
27
|
exportName: string;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
const
|
|
27
|
-
|
|
30
|
+
const SCHEMA_TYPES_FILE = "schema-types.ts";
|
|
31
|
+
|
|
32
|
+
const ROLE_EXPORT_RE = /export\s+type\s+(configType|inputType|outputType)\s*=\s*(\w+)/g;
|
|
28
33
|
|
|
29
|
-
const
|
|
34
|
+
const ROLE_TO_KIND: Record<SchemaRole, SchemaRootKind> = {
|
|
35
|
+
configType: "config",
|
|
36
|
+
inputType: "input",
|
|
37
|
+
outputType: "output",
|
|
38
|
+
};
|
|
30
39
|
|
|
31
|
-
function
|
|
40
|
+
function listSchemaTypesFiles(srcDir: string, baseDir: string, out: string[]): void {
|
|
32
41
|
for (const ent of readdirSync(srcDir)) {
|
|
33
42
|
const full = join(srcDir, ent);
|
|
34
43
|
const st = statSync(full);
|
|
35
44
|
if (st.isDirectory()) {
|
|
36
|
-
|
|
45
|
+
listSchemaTypesFiles(full, baseDir, out);
|
|
37
46
|
continue;
|
|
38
47
|
}
|
|
39
|
-
if (ent ===
|
|
48
|
+
if (ent === SCHEMA_TYPES_FILE) {
|
|
40
49
|
out.push(relative(baseDir, full));
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
}
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
throw new Error(`${relFile}: ${typeName} has both Config schema and JSON payload markers`);
|
|
54
|
+
/** True when `typeName` is declared in this file (not a re-export alias to another module). */
|
|
55
|
+
function isTypeDefinedInFile(text: string, typeName: string): boolean {
|
|
56
|
+
if (new RegExp(`export\\s+interface\\s+${typeName}\\b`).test(text)) {
|
|
57
|
+
return true;
|
|
50
58
|
}
|
|
51
|
-
if (
|
|
52
|
-
return
|
|
59
|
+
if (new RegExp(`export\\s+type\\s+${typeName}\\s*=`).test(text)) {
|
|
60
|
+
return !["configType", "inputType", "outputType"].includes(typeName);
|
|
53
61
|
}
|
|
54
|
-
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function rootForRole(role: SchemaRole, typeName: string, path: string): SchemaRoot {
|
|
66
|
+
const kind = ROLE_TO_KIND[role];
|
|
67
|
+
if (kind === "config") {
|
|
55
68
|
return {
|
|
56
|
-
kind
|
|
69
|
+
kind,
|
|
57
70
|
typeName,
|
|
58
|
-
|
|
71
|
+
path,
|
|
59
72
|
outfile: outfileForConfigType(typeName),
|
|
60
73
|
exportName: configSchemaExportName(typeName),
|
|
61
74
|
};
|
|
62
75
|
}
|
|
76
|
+
if (kind === "input") {
|
|
77
|
+
return {
|
|
78
|
+
kind,
|
|
79
|
+
typeName,
|
|
80
|
+
path,
|
|
81
|
+
outfile: outfileForInputType(typeName),
|
|
82
|
+
exportName: inputSchemaExportName(typeName),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
63
85
|
return {
|
|
64
|
-
kind
|
|
86
|
+
kind,
|
|
65
87
|
typeName,
|
|
66
|
-
|
|
88
|
+
path,
|
|
67
89
|
outfile: outfileForOutputType(typeName),
|
|
68
90
|
exportName: outputSchemaExportName(typeName),
|
|
69
91
|
};
|
|
70
92
|
}
|
|
71
93
|
|
|
72
|
-
|
|
94
|
+
function discoverFromFile(path: string, text: string): SchemaRoot[] {
|
|
95
|
+
const rolesSeen = new Set<SchemaRole>();
|
|
96
|
+
const roots: SchemaRoot[] = [];
|
|
97
|
+
|
|
98
|
+
for (const match of text.matchAll(ROLE_EXPORT_RE)) {
|
|
99
|
+
const role = match[1] as SchemaRole | undefined;
|
|
100
|
+
const typeName = match[2];
|
|
101
|
+
if (!role || !typeName) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (rolesSeen.has(role)) {
|
|
105
|
+
throw new Error(`${path}: duplicate export type ${role}`);
|
|
106
|
+
}
|
|
107
|
+
rolesSeen.add(role);
|
|
108
|
+
if (!isTypeDefinedInFile(text, typeName)) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
roots.push(rootForRole(role, typeName, path));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return roots;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Find all schema roots under `src/` in files named schema-types.ts. */
|
|
73
118
|
export function discoverSchemaRoots(projectRoot: string): SchemaRoot[] {
|
|
74
119
|
const srcDir = join(projectRoot, "src");
|
|
75
120
|
const files: string[] = [];
|
|
76
|
-
|
|
121
|
+
listSchemaTypesFiles(srcDir, projectRoot, files);
|
|
122
|
+
|
|
77
123
|
const roots: SchemaRoot[] = [];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
roots.push(root);
|
|
124
|
+
const typeOwners = new Map<string, string>();
|
|
125
|
+
|
|
126
|
+
for (const relPath of files.sort()) {
|
|
127
|
+
const text = readFileSync(join(projectRoot, relPath), "utf8");
|
|
128
|
+
for (const root of discoverFromFile(relPath, text)) {
|
|
129
|
+
const prev = typeOwners.get(root.typeName);
|
|
130
|
+
if (prev) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`${relPath}: duplicate schema root type ${root.typeName} (already declared in ${prev})`,
|
|
133
|
+
);
|
|
89
134
|
}
|
|
135
|
+
typeOwners.set(root.typeName, relPath);
|
|
136
|
+
roots.push(root);
|
|
90
137
|
}
|
|
91
138
|
}
|
|
139
|
+
|
|
140
|
+
const configRoots = roots.filter((r) => r.kind === "config");
|
|
141
|
+
if (configRoots.length > 1) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`multiple config schema roots: ${configRoots.map((r) => `${r.typeName} (${r.path})`).join(", ")}`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
92
147
|
return roots;
|
|
93
148
|
}
|
|
@@ -16,6 +16,23 @@ export function camelToScreamingSnake(name: string): string {
|
|
|
16
16
|
return camelToKebab(name).replace(/-/g, "_").toUpperCase();
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** Generated JSON filename for a tool-input schema root. */
|
|
20
|
+
export function outfileForInputType(typeName: string): string {
|
|
21
|
+
if (typeName.endsWith("ToolInput")) {
|
|
22
|
+
return `${camelToKebab(typeName.slice(0, -"ToolInput".length))}-tool-input.json`;
|
|
23
|
+
}
|
|
24
|
+
return `${camelToKebab(typeName)}-tool-input.json`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Bridge export constant for a tool-input schema root. */
|
|
28
|
+
export function inputSchemaExportName(typeName: string): string {
|
|
29
|
+
if (typeName.endsWith("ToolInput")) {
|
|
30
|
+
const base = typeName.slice(0, -"ToolInput".length);
|
|
31
|
+
return `${camelToScreamingSnake(base)}_TOOL_INPUT_SCHEMA`;
|
|
32
|
+
}
|
|
33
|
+
return `${camelToScreamingSnake(typeName)}_TOOL_INPUT_SCHEMA`;
|
|
34
|
+
}
|
|
35
|
+
|
|
19
36
|
/** Generated JSON filename for an output-schema root type. */
|
|
20
37
|
export function outfileForOutputType(typeName: string): string {
|
|
21
38
|
if (typeName.endsWith("JsonOutput")) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Generate JSON Schema artifacts and bridge modules from discovered types.ts roots.
|
|
2
|
+
Generate JSON Schema artifacts and bridge modules from discovered schema-types.ts roots.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
@@ -13,7 +13,7 @@ const generatedDir = join(projectRoot, "schemas", "generated");
|
|
|
13
13
|
|
|
14
14
|
function generateJson(root: SchemaRoot): Record<string, unknown> {
|
|
15
15
|
const generator = createGenerator({
|
|
16
|
-
path: join(projectRoot, root.
|
|
16
|
+
path: join(projectRoot, root.path),
|
|
17
17
|
type: root.typeName,
|
|
18
18
|
tsconfig: join(projectRoot, "tsconfig.json"),
|
|
19
19
|
topRef: false,
|
|
@@ -52,6 +52,7 @@ function writeBridge(path: string, scriptName: string, roots: SchemaRoot[], bann
|
|
|
52
52
|
|
|
53
53
|
const roots = discoverSchemaRoots(projectRoot);
|
|
54
54
|
const configRoots = roots.filter((r) => r.kind === "config");
|
|
55
|
+
const inputRoots = roots.filter((r) => r.kind === "input");
|
|
55
56
|
const outputRoots = roots.filter((r) => r.kind === "output");
|
|
56
57
|
|
|
57
58
|
mkdirSync(generatedDir, { recursive: true });
|
|
@@ -70,6 +71,14 @@ writeBridge(
|
|
|
70
71
|
configRoots,
|
|
71
72
|
"JSON Schema for program.appConfig.jsonSchema from",
|
|
72
73
|
);
|
|
74
|
+
if (inputRoots.length > 0) {
|
|
75
|
+
writeBridge(
|
|
76
|
+
join(projectRoot, "schemas", "inputSchemas.ts"),
|
|
77
|
+
"schemagen.ts",
|
|
78
|
+
inputRoots,
|
|
79
|
+
"JSON Schema for leaf inputSchema from",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
73
82
|
writeBridge(
|
|
74
83
|
join(projectRoot, "schemas", "outputSchemas.ts"),
|
|
75
84
|
"schemagen.ts",
|
|
@@ -77,4 +86,6 @@ writeBridge(
|
|
|
77
86
|
"JSON Schema for leaf outputSchema from",
|
|
78
87
|
);
|
|
79
88
|
|
|
80
|
-
console.log(
|
|
89
|
+
console.log(
|
|
90
|
+
`config roots: ${configRoots.length}, input roots: ${inputRoots.length}, output roots: ${outputRoots.length}`,
|
|
91
|
+
);
|
|
@@ -16,6 +16,11 @@ export const echoCommand = {
|
|
|
16
16
|
},
|
|
17
17
|
],
|
|
18
18
|
handler: (ctx) => {
|
|
19
|
-
|
|
19
|
+
const message = ctx.stringOpt("message") ?? "";
|
|
20
|
+
if (ctx.invocation === "cli") {
|
|
21
|
+
console.log(message);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
return message;
|
|
20
25
|
},
|
|
21
26
|
} satisfies CliLeaf;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** JSON stdout for `full-example status --json`. */
|
|
2
|
+
export interface StatusJsonOutput {
|
|
3
|
+
/** Resolved AWS region. */
|
|
4
|
+
defaultRegion?: string;
|
|
5
|
+
/** Resolved retry count. */
|
|
6
|
+
maxRetries?: number;
|
|
7
|
+
/** Whether apiToken is set (value never included). */
|
|
8
|
+
apiTokenSet: boolean;
|
|
9
|
+
/** App version from program root. */
|
|
10
|
+
version: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Schemagen root for leaf outputSchema. */
|
|
14
|
+
export type outputType = StatusJsonOutput;
|
|
@@ -1,11 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export interface StatusJsonOutput {
|
|
3
|
-
/** Resolved AWS region. */
|
|
4
|
-
defaultRegion?: string;
|
|
5
|
-
/** Resolved retry count. */
|
|
6
|
-
maxRetries?: number;
|
|
7
|
-
/** Whether apiToken is set (value never included). */
|
|
8
|
-
apiTokenSet: boolean;
|
|
9
|
-
/** App version from program root. */
|
|
10
|
-
version: string;
|
|
11
|
-
}
|
|
1
|
+
export type { StatusJsonOutput } from "./schema-types.ts";
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Config schema
|
|
3
|
-
*
|
|
4
2
|
* Application settings for `full-example` (`program.appConfig`).
|
|
5
3
|
*/
|
|
6
4
|
export interface AppConfig {
|
|
@@ -21,3 +19,6 @@ export interface AppConfig {
|
|
|
21
19
|
ttl: number;
|
|
22
20
|
};
|
|
23
21
|
}
|
|
22
|
+
|
|
23
|
+
/** Schemagen root for program.appConfig.jsonSchema. */
|
|
24
|
+
export type configType = AppConfig;
|