create-invokable 0.1.0
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/LICENSE +21 -0
- package/README.md +57 -0
- package/bin/create-invokable.mjs +4 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/prompt.d.ts +14 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +44 -0
- package/dist/prompt.js.map +1 -0
- package/dist/template.d.ts +18 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +283 -0
- package/dist/template.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Invokable
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# create-invokable
|
|
2
|
+
|
|
3
|
+
Scaffolds an agent-native CLI.
|
|
4
|
+
|
|
5
|
+
```console
|
|
6
|
+
$ npx create-invokable my-deployer
|
|
7
|
+
? Auth server?
|
|
8
|
+
1) hosted (default)
|
|
9
|
+
2) self-host
|
|
10
|
+
? First command? (deploy)
|
|
11
|
+
? Does it spend money or need approval? [Y/n]
|
|
12
|
+
|
|
13
|
+
Created ./my-deployer
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Non-interactive:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx create-invokable my-deployer --yes --command deploy --spends --auth self-host
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## What you get
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
my-deployer/
|
|
26
|
+
├── package.json bin wired, build and test scripts
|
|
27
|
+
├── tsconfig.json
|
|
28
|
+
├── src/tool.ts defineTool with your first command
|
|
29
|
+
├── bin/my-deployer.mjs the entry point, executable
|
|
30
|
+
├── README.md
|
|
31
|
+
└── .github/workflows/ci.yml
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`login`, `logout`, `whoami` and `doctor` are built in and need no code. `init`
|
|
35
|
+
generates agent instructions. If the first command spends money it comes with an
|
|
36
|
+
approval gate already wired, and `requireSpendLimit` set so `--yes` is refused
|
|
37
|
+
without a `--max-spend` cap.
|
|
38
|
+
|
|
39
|
+
The generated CI runs both contract checks:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
- run: npx invokable-test node bin/my-deployer.mjs # the contract holds
|
|
43
|
+
- run: node bin/my-deployer.mjs init --check # instructions are current
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Options
|
|
47
|
+
|
|
48
|
+
| Option | Effect |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `--command <name>` | First command. Default `deploy`. |
|
|
51
|
+
| `--spends` / `--no-spends` | Whether it needs an approval gate. |
|
|
52
|
+
| `--auth <hosted\|self-host>` | Where `login` points. Default `hosted`. |
|
|
53
|
+
| `-y`, `--yes` | Accept every default; never prompt. |
|
|
54
|
+
|
|
55
|
+
The name must be lowercase letters, digits and hyphens: it becomes both the
|
|
56
|
+
binary name and the generated skill name, and the Agent Skills spec constrains
|
|
57
|
+
the latter.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Prompter } from './prompt.js';
|
|
2
|
+
export interface CreateOptions {
|
|
3
|
+
argv: readonly string[];
|
|
4
|
+
cwd?: string;
|
|
5
|
+
stdout?: (s: string) => void;
|
|
6
|
+
stderr?: (s: string) => void;
|
|
7
|
+
prompter?: Prompter;
|
|
8
|
+
}
|
|
9
|
+
export declare function createMain(options: CreateOptions): Promise<number>;
|
|
10
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4C,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAkBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAwGxE"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { scaffold } from './template.js';
|
|
4
|
+
import { nonInteractivePrompter, terminalPrompter } from './prompt.js';
|
|
5
|
+
const USAGE = `Usage: create-invokable <name> [options]
|
|
6
|
+
|
|
7
|
+
Scaffolds an agent-native CLI.
|
|
8
|
+
|
|
9
|
+
Options:
|
|
10
|
+
--command <name> First command. Default: deploy.
|
|
11
|
+
--spends The first command spends money and needs approval.
|
|
12
|
+
--no-spends It does not.
|
|
13
|
+
--auth <mode> hosted | self-host. Default: hosted.
|
|
14
|
+
-y, --yes Accept every default; do not prompt.
|
|
15
|
+
-h, --help Show this help.
|
|
16
|
+
`;
|
|
17
|
+
/** Skill-name rules, so `init` can generate a valid SKILL.md later. */
|
|
18
|
+
const NAME_PATTERN = /^[a-z0-9-]{1,64}$/;
|
|
19
|
+
export async function createMain(options) {
|
|
20
|
+
const stderr = options.stderr ?? ((s) => process.stderr.write(s));
|
|
21
|
+
const cwd = options.cwd ?? process.cwd();
|
|
22
|
+
const argv = [...options.argv];
|
|
23
|
+
let name;
|
|
24
|
+
let commandName;
|
|
25
|
+
let spends;
|
|
26
|
+
let auth;
|
|
27
|
+
let acceptDefaults = false;
|
|
28
|
+
for (let i = 0; i < argv.length; i++) {
|
|
29
|
+
const token = argv[i];
|
|
30
|
+
if (token === '--help' || token === '-h') {
|
|
31
|
+
stderr(USAGE);
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
else if (token === '--yes' || token === '-y') {
|
|
35
|
+
acceptDefaults = true;
|
|
36
|
+
}
|
|
37
|
+
else if (token === '--spends') {
|
|
38
|
+
spends = true;
|
|
39
|
+
}
|
|
40
|
+
else if (token === '--no-spends') {
|
|
41
|
+
spends = false;
|
|
42
|
+
}
|
|
43
|
+
else if (token === '--command') {
|
|
44
|
+
commandName = argv[++i];
|
|
45
|
+
}
|
|
46
|
+
else if (token === '--auth') {
|
|
47
|
+
const value = argv[++i];
|
|
48
|
+
if (value !== 'hosted' && value !== 'self-host') {
|
|
49
|
+
stderr(`error: --auth must be "hosted" or "self-host"; got ${JSON.stringify(value)}\n`);
|
|
50
|
+
return 2;
|
|
51
|
+
}
|
|
52
|
+
auth = value;
|
|
53
|
+
}
|
|
54
|
+
else if (token.startsWith('-')) {
|
|
55
|
+
stderr(`error: unknown option ${token}\n${USAGE}`);
|
|
56
|
+
return 2;
|
|
57
|
+
}
|
|
58
|
+
else if (name === undefined) {
|
|
59
|
+
name = token;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const prompter = options.prompter ??
|
|
63
|
+
(acceptDefaults || !process.stdin.isTTY ? nonInteractivePrompter() : terminalPrompter());
|
|
64
|
+
try {
|
|
65
|
+
if (name === undefined) {
|
|
66
|
+
name = await prompter.text('Tool name?', 'my-tool');
|
|
67
|
+
}
|
|
68
|
+
if (!NAME_PATTERN.test(name)) {
|
|
69
|
+
stderr(`error: "${name}" is not a usable tool name. Use lowercase letters, digits and ` +
|
|
70
|
+
'hyphens — it becomes the binary name and the skill name.\n');
|
|
71
|
+
return 2;
|
|
72
|
+
}
|
|
73
|
+
const target = resolve(cwd, name);
|
|
74
|
+
if (existsSync(target) && readdirSync(target).length > 0) {
|
|
75
|
+
stderr(`error: ${target} already exists and is not empty.\n`);
|
|
76
|
+
return 6;
|
|
77
|
+
}
|
|
78
|
+
if (auth === undefined) {
|
|
79
|
+
auth = await prompter.choice('Auth server?', ['hosted', 'self-host'], 'hosted');
|
|
80
|
+
}
|
|
81
|
+
if (commandName === undefined) {
|
|
82
|
+
commandName = await prompter.text('First command?', 'deploy');
|
|
83
|
+
}
|
|
84
|
+
if (spends === undefined) {
|
|
85
|
+
spends = await prompter.confirm('Does it spend money or need approval?', true);
|
|
86
|
+
}
|
|
87
|
+
const files = scaffold({ name, command: commandName, spends, auth });
|
|
88
|
+
for (const file of files) {
|
|
89
|
+
const path = join(target, file.path);
|
|
90
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
91
|
+
writeFileSync(path, file.content, 'utf8');
|
|
92
|
+
if (file.executable)
|
|
93
|
+
chmodSync(path, 0o755);
|
|
94
|
+
}
|
|
95
|
+
stderr(`\nCreated ${target}\n\n`);
|
|
96
|
+
for (const file of files)
|
|
97
|
+
stderr(` ${file.path}\n`);
|
|
98
|
+
stderr([
|
|
99
|
+
'',
|
|
100
|
+
'Next:',
|
|
101
|
+
` cd ${name}`,
|
|
102
|
+
' npm install',
|
|
103
|
+
' npm run build',
|
|
104
|
+
` node bin/${name}.mjs --help`,
|
|
105
|
+
'',
|
|
106
|
+
'Then check the contract holds:',
|
|
107
|
+
` npx invokable-test node bin/${name}.mjs`,
|
|
108
|
+
'',
|
|
109
|
+
].join('\n'));
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
prompter.close();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createMain } from './cli.js';
|
|
2
|
+
export type { CreateOptions } from './cli.js';
|
|
3
|
+
export { scaffold } from './template.js';
|
|
4
|
+
export type { ScaffoldSpec, ScaffoldFile } from './template.js';
|
|
5
|
+
export { nonInteractivePrompter, terminalPrompter } from './prompt.js';
|
|
6
|
+
export type { Prompter } from './prompt.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACvE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface Prompter {
|
|
2
|
+
text: (question: string, fallback: string) => Promise<string>;
|
|
3
|
+
confirm: (question: string, fallback: boolean) => Promise<boolean>;
|
|
4
|
+
choice: <T extends string>(question: string, options: readonly T[], fallback: T) => Promise<T>;
|
|
5
|
+
close: () => void;
|
|
6
|
+
}
|
|
7
|
+
/** Answers every prompt with its default; used by `--yes` and by tests. */
|
|
8
|
+
export declare function nonInteractivePrompter(): Prompter;
|
|
9
|
+
/**
|
|
10
|
+
* Prompts on stderr and reads stdin, so that scaffolding can be piped without
|
|
11
|
+
* the questions contaminating whatever the caller is capturing.
|
|
12
|
+
*/
|
|
13
|
+
export declare function terminalPrompter(): Prompter;
|
|
14
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/F,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,2EAA2E;AAC3E,wBAAgB,sBAAsB,IAAI,QAAQ,CAOjD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CA8B3C"}
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
/** Answers every prompt with its default; used by `--yes` and by tests. */
|
|
3
|
+
export function nonInteractivePrompter() {
|
|
4
|
+
return {
|
|
5
|
+
text: async (_q, fallback) => fallback,
|
|
6
|
+
confirm: async (_q, fallback) => fallback,
|
|
7
|
+
choice: async (_q, _o, fallback) => fallback,
|
|
8
|
+
close: () => { },
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Prompts on stderr and reads stdin, so that scaffolding can be piped without
|
|
13
|
+
* the questions contaminating whatever the caller is capturing.
|
|
14
|
+
*/
|
|
15
|
+
export function terminalPrompter() {
|
|
16
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
17
|
+
return {
|
|
18
|
+
async text(question, fallback) {
|
|
19
|
+
const answer = (await rl.question(`${question} (${fallback}) `)).trim();
|
|
20
|
+
return answer || fallback;
|
|
21
|
+
},
|
|
22
|
+
async confirm(question, fallback) {
|
|
23
|
+
const hint = fallback ? 'Y/n' : 'y/N';
|
|
24
|
+
const answer = (await rl.question(`${question} [${hint}] `)).trim();
|
|
25
|
+
if (!answer)
|
|
26
|
+
return fallback;
|
|
27
|
+
return /^y(es)?$/i.test(answer);
|
|
28
|
+
},
|
|
29
|
+
async choice(question, options, fallback) {
|
|
30
|
+
const list = options.map((o, i) => ` ${i + 1}) ${o}${o === fallback ? ' (default)' : ''}`);
|
|
31
|
+
const answer = (await rl.question(`${question}\n${list.join('\n')}\n> `)).trim();
|
|
32
|
+
if (!answer)
|
|
33
|
+
return fallback;
|
|
34
|
+
const byIndex = Number(answer);
|
|
35
|
+
if (Number.isInteger(byIndex) && byIndex >= 1 && byIndex <= options.length) {
|
|
36
|
+
return options[byIndex - 1];
|
|
37
|
+
}
|
|
38
|
+
const byName = options.find((o) => o.toLowerCase() === answer.toLowerCase());
|
|
39
|
+
return byName ?? fallback;
|
|
40
|
+
},
|
|
41
|
+
close: () => rl.close(),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AASzD,2EAA2E;AAC3E,MAAM,UAAU,sBAAsB;IACpC,OAAO;QACL,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ;QACtC,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ;QACzC,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ;QAC5C,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;KAChB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE7E,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ;YAC3B,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACxE,OAAO,MAAM,IAAI,QAAQ,CAAC;QAC5B,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ;YAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;YACtC,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACpE,IAAI,CAAC,MAAM;gBAAE,OAAO,QAAQ,CAAC;YAC7B,OAAO,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ;YACtC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7F,MAAM,MAAM,GAAG,CACb,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,QAAQ,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CACzD,CAAC,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,MAAM;gBAAE,OAAO,QAAQ,CAAC;YAE7B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC3E,OAAO,OAAO,CAAC,OAAO,GAAG,CAAC,CAAE,CAAC;YAC/B,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;YAC7E,OAAO,MAAM,IAAI,QAAQ,CAAC;QAC5B,CAAC;QACD,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE;KACxB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ScaffoldSpec {
|
|
2
|
+
/** Binary and package name. Must be a valid skill name too. */
|
|
3
|
+
name: string;
|
|
4
|
+
/** First real command, e.g. `deploy`. */
|
|
5
|
+
command: string;
|
|
6
|
+
/** Whether the first command spends money and needs an approval gate. */
|
|
7
|
+
spends: boolean;
|
|
8
|
+
/** `hosted` points at auth.invokable.dev; `self-host` at localhost. */
|
|
9
|
+
auth: 'hosted' | 'self-host';
|
|
10
|
+
version?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ScaffoldFile {
|
|
13
|
+
path: string;
|
|
14
|
+
content: string;
|
|
15
|
+
executable?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function scaffold(spec: ScaffoldSpec): ScaffoldFile[];
|
|
18
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAqMD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,EAAE,CAiG3D"}
|
package/dist/template.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/** `deploy-thing` -> `DeployThing`, for a generated type name. */
|
|
2
|
+
function pascal(value) {
|
|
3
|
+
return value
|
|
4
|
+
.split(/[^A-Za-z0-9]+/)
|
|
5
|
+
.filter(Boolean)
|
|
6
|
+
.map((part) => part[0].toUpperCase() + part.slice(1))
|
|
7
|
+
.join('');
|
|
8
|
+
}
|
|
9
|
+
const HOSTED_AUTH_URL = 'https://auth.invokable.dev';
|
|
10
|
+
function apiUrls(spec) {
|
|
11
|
+
return spec.auth === 'hosted'
|
|
12
|
+
? { baseUrl: `https://api.${spec.name}.example.com`, authUrl: HOSTED_AUTH_URL }
|
|
13
|
+
: { baseUrl: 'http://127.0.0.1:8787', authUrl: 'http://127.0.0.1:8787' };
|
|
14
|
+
}
|
|
15
|
+
function toolSource(spec) {
|
|
16
|
+
const { baseUrl, authUrl } = apiUrls(spec);
|
|
17
|
+
const envPrefix = spec.name.toUpperCase().replace(/-/g, '_');
|
|
18
|
+
// The response type is declared rather than inferred: `client.post()` returns
|
|
19
|
+
// `unknown` by design, so a template that destructured it straight away would
|
|
20
|
+
// ship a project that does not compile.
|
|
21
|
+
const planType = `
|
|
22
|
+
/** Shape your \`/v1/${spec.command}/plan\` endpoint returns. Adjust to match. */
|
|
23
|
+
interface ${pascal(spec.command)}Plan {
|
|
24
|
+
id: string;
|
|
25
|
+
summary?: unknown;
|
|
26
|
+
credits: number;
|
|
27
|
+
balance: number;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
const gated = `
|
|
31
|
+
${JSON.stringify(spec.command)}: command({
|
|
32
|
+
description: 'Describe what this does — the agent reads this to decide when to run it.',
|
|
33
|
+
options: {
|
|
34
|
+
env: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
required: true,
|
|
37
|
+
choices: ['staging', 'prod'],
|
|
38
|
+
description: 'Which environment to target.',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
// Marks the command as spending money: the generated SKILL.md warns the
|
|
42
|
+
// agent, and \`--yes\` can be refused without \`--max-spend\`.
|
|
43
|
+
spends: true,
|
|
44
|
+
run: async ({ opts, client, ctx }) => {
|
|
45
|
+
const plan = await client.post<${pascal(spec.command)}Plan>(
|
|
46
|
+
'/v1/${spec.command}/plan',
|
|
47
|
+
{ env: opts.env },
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Stops here and exits 10 unless an approval fingerprint was supplied.
|
|
51
|
+
await checkpoint(ctx, {
|
|
52
|
+
gate: '${spec.command}_review',
|
|
53
|
+
title: '${spec.command} plan',
|
|
54
|
+
summary: plan.summary ?? plan,
|
|
55
|
+
subject: plan.id,
|
|
56
|
+
question: \`Run ${spec.command} against \${opts.env}?\`,
|
|
57
|
+
explain: 'Approving starts the work and bills the account.',
|
|
58
|
+
spend: { estimated: plan.credits, balance: plan.balance },
|
|
59
|
+
reject: \`${spec.name} ${spec.command} --env \${opts.env} --dry-run\`,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return client.post('/v1/${spec.command}', { planId: plan.id });
|
|
63
|
+
},
|
|
64
|
+
}),`;
|
|
65
|
+
const plain = `
|
|
66
|
+
${JSON.stringify(spec.command)}: command({
|
|
67
|
+
description: 'Describe what this does — the agent reads this to decide when to run it.',
|
|
68
|
+
options: {
|
|
69
|
+
name: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
required: true,
|
|
72
|
+
description: 'What to act on.',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
run: async ({ opts, client, ctx }) => {
|
|
76
|
+
ctx.io.note(\`working on \${opts.name}…\`); // progress → stderr
|
|
77
|
+
return client.get(\`/v1/${spec.command}/\${encodeURIComponent(opts.name)}\`);
|
|
78
|
+
},
|
|
79
|
+
}),`;
|
|
80
|
+
return `import { command, defineTool${spec.spends ? ', checkpoint' : ''} } from '@invokable/core';
|
|
81
|
+
import { initCommand } from '@invokable/skills';
|
|
82
|
+
|
|
83
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
84
|
+
${spec.spends ? planType : ''}
|
|
85
|
+
export default defineTool({
|
|
86
|
+
name: '${spec.name}',
|
|
87
|
+
version: pkg.version,
|
|
88
|
+
description: 'One line an agent reads to decide whether this tool is relevant.',
|
|
89
|
+
|
|
90
|
+
api: {
|
|
91
|
+
baseUrl: process.env.${envPrefix}_API ?? '${baseUrl}',
|
|
92
|
+
authUrl: process.env.${envPrefix}_AUTH ?? '${authUrl}',
|
|
93
|
+
},
|
|
94
|
+
configDir: '~/.${spec.name}',
|
|
95
|
+
${spec.spends ? '\n // Refuse `--yes` on spending commands unless `--max-spend` is also given.\n requireSpendLimit: true,\n' : ''}
|
|
96
|
+
commands: {
|
|
97
|
+
// Installs agent instructions into this project. login/logout/whoami/doctor
|
|
98
|
+
// are built in and need no declaration.
|
|
99
|
+
init: initCommand(),
|
|
100
|
+
${spec.spends ? gated : plain}
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
function readme(spec) {
|
|
106
|
+
const envPrefix = spec.name.toUpperCase().replace(/-/g, '_');
|
|
107
|
+
return `# ${spec.name}
|
|
108
|
+
|
|
109
|
+
An agent-native CLI built with [invokable](https://github.com/beinvokable/invokable).
|
|
110
|
+
|
|
111
|
+
## Develop
|
|
112
|
+
|
|
113
|
+
\`\`\`bash
|
|
114
|
+
npm install
|
|
115
|
+
npm run build
|
|
116
|
+
node bin/${spec.name}.mjs --help
|
|
117
|
+
\`\`\`
|
|
118
|
+
|
|
119
|
+
## Try it
|
|
120
|
+
|
|
121
|
+
\`\`\`bash
|
|
122
|
+
# Install agent instructions into a project
|
|
123
|
+
node bin/${spec.name}.mjs init
|
|
124
|
+
|
|
125
|
+
# Check connectivity and auth
|
|
126
|
+
node bin/${spec.name}.mjs doctor --json
|
|
127
|
+
\`\`\`
|
|
128
|
+
${spec.auth === 'self-host'
|
|
129
|
+
? `
|
|
130
|
+
### Run the auth server
|
|
131
|
+
|
|
132
|
+
This project is configured for self-hosted auth. Start a server that mounts
|
|
133
|
+
\`@invokable/server\` on \`${apiUrls(spec).authUrl}\`, then:
|
|
134
|
+
|
|
135
|
+
\`\`\`bash
|
|
136
|
+
node bin/${spec.name}.mjs login
|
|
137
|
+
\`\`\`
|
|
138
|
+
`
|
|
139
|
+
: `
|
|
140
|
+
### Sign in
|
|
141
|
+
|
|
142
|
+
Auth points at \`${HOSTED_AUTH_URL}\`. Override for local development:
|
|
143
|
+
|
|
144
|
+
\`\`\`bash
|
|
145
|
+
${envPrefix}_AUTH=http://127.0.0.1:8787 node bin/${spec.name}.mjs login
|
|
146
|
+
\`\`\`
|
|
147
|
+
`}
|
|
148
|
+
## The contract
|
|
149
|
+
|
|
150
|
+
Every command emits **one JSON document on stdout** with \`--json\`, and a
|
|
151
|
+
semantic exit code. Progress goes to stderr.
|
|
152
|
+
|
|
153
|
+
\`\`\`console
|
|
154
|
+
$ ${spec.name} ${spec.command} --json
|
|
155
|
+
{"status":"ok","data":{ ... }}
|
|
156
|
+
|
|
157
|
+
$ echo $?
|
|
158
|
+
0
|
|
159
|
+
\`\`\`
|
|
160
|
+
|
|
161
|
+
Check it holds:
|
|
162
|
+
|
|
163
|
+
\`\`\`bash
|
|
164
|
+
npx invokable-test node bin/${spec.name}.mjs
|
|
165
|
+
\`\`\`
|
|
166
|
+
${spec.spends
|
|
167
|
+
? `
|
|
168
|
+
## Approval gate
|
|
169
|
+
|
|
170
|
+
\`${spec.command}\` spends money, so it stops first:
|
|
171
|
+
|
|
172
|
+
\`\`\`console
|
|
173
|
+
$ ${spec.name} ${spec.command} --env prod --json
|
|
174
|
+
{"status":"checkpoint","gate":"${spec.command}_review","fingerprint":"…",
|
|
175
|
+
"next":{"approve":"${spec.name} ${spec.command} --env prod --json --approve ${spec.command}_review@…"}}
|
|
176
|
+
$ echo $?
|
|
177
|
+
10
|
|
178
|
+
\`\`\`
|
|
179
|
+
|
|
180
|
+
The fingerprint is issued by your API, bound to the plan the user was shown,
|
|
181
|
+
and consumed once. Mount \`checkpointRoutes()\` and \`verifyCheckpoint()\` from
|
|
182
|
+
\`@invokable/server\` to issue and verify them.
|
|
183
|
+
`
|
|
184
|
+
: ''}
|
|
185
|
+
## Agent instructions
|
|
186
|
+
|
|
187
|
+
\`${spec.name} init\` writes a portable \`SKILL.md\` for Claude Code, Codex, Cursor
|
|
188
|
+
and Gemini CLI, plus sections in \`AGENTS.md\` and Copilot instructions. Re-run it
|
|
189
|
+
whenever you add a command; \`init --check\` fails CI when they are stale.
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
192
|
+
export function scaffold(spec) {
|
|
193
|
+
const version = spec.version ?? '0.1.0';
|
|
194
|
+
const pkg = {
|
|
195
|
+
name: spec.name,
|
|
196
|
+
version,
|
|
197
|
+
description: 'An agent-native CLI.',
|
|
198
|
+
type: 'module',
|
|
199
|
+
bin: { [spec.name]: `./bin/${spec.name}.mjs` },
|
|
200
|
+
files: ['bin', 'dist'],
|
|
201
|
+
engines: { node: '>=20' },
|
|
202
|
+
scripts: {
|
|
203
|
+
build: 'tsc -p tsconfig.json',
|
|
204
|
+
dev: 'tsc -p tsconfig.json --watch',
|
|
205
|
+
test: 'invokable-test node bin/' + spec.name + '.mjs',
|
|
206
|
+
prepublishOnly: 'npm run build',
|
|
207
|
+
},
|
|
208
|
+
dependencies: {
|
|
209
|
+
'@invokable/core': '^0.1.0',
|
|
210
|
+
'@invokable/skills': '^0.1.0',
|
|
211
|
+
},
|
|
212
|
+
devDependencies: {
|
|
213
|
+
'@invokable/conformance': '^0.1.0',
|
|
214
|
+
'@types/node': '^22.0.0',
|
|
215
|
+
typescript: '^5.9.0',
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
return [
|
|
219
|
+
{ path: 'package.json', content: JSON.stringify(pkg, null, 2) + '\n' },
|
|
220
|
+
{
|
|
221
|
+
path: 'tsconfig.json',
|
|
222
|
+
content: JSON.stringify({
|
|
223
|
+
compilerOptions: {
|
|
224
|
+
target: 'ES2023',
|
|
225
|
+
module: 'NodeNext',
|
|
226
|
+
moduleResolution: 'NodeNext',
|
|
227
|
+
types: ['node'],
|
|
228
|
+
strict: true,
|
|
229
|
+
declaration: true,
|
|
230
|
+
resolveJsonModule: true,
|
|
231
|
+
outDir: 'dist',
|
|
232
|
+
rootDir: 'src',
|
|
233
|
+
},
|
|
234
|
+
include: ['src/**/*.ts'],
|
|
235
|
+
}, null, 2) + '\n',
|
|
236
|
+
},
|
|
237
|
+
{ path: 'src/tool.ts', content: toolSource(spec) },
|
|
238
|
+
{
|
|
239
|
+
path: `bin/${spec.name}.mjs`,
|
|
240
|
+
executable: true,
|
|
241
|
+
content: `#!/usr/bin/env node
|
|
242
|
+
import { cli } from '@invokable/core';
|
|
243
|
+
import tool from '../dist/tool.js';
|
|
244
|
+
|
|
245
|
+
await cli(tool);
|
|
246
|
+
`,
|
|
247
|
+
},
|
|
248
|
+
{ path: 'README.md', content: readme(spec) },
|
|
249
|
+
{
|
|
250
|
+
path: '.gitignore',
|
|
251
|
+
content: 'node_modules/\ndist/\n*.tsbuildinfo\n.env\n.env.*\n!.env.example\n',
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
path: '.github/workflows/ci.yml',
|
|
255
|
+
content: `name: CI
|
|
256
|
+
|
|
257
|
+
on:
|
|
258
|
+
push:
|
|
259
|
+
branches: ['**']
|
|
260
|
+
pull_request:
|
|
261
|
+
|
|
262
|
+
jobs:
|
|
263
|
+
test:
|
|
264
|
+
runs-on: ubuntu-latest
|
|
265
|
+
steps:
|
|
266
|
+
- uses: actions/checkout@v4
|
|
267
|
+
- uses: actions/setup-node@v4
|
|
268
|
+
with:
|
|
269
|
+
node-version: 22
|
|
270
|
+
- run: npm ci
|
|
271
|
+
- run: npm run build
|
|
272
|
+
|
|
273
|
+
# The contract an agent depends on.
|
|
274
|
+
- run: npx invokable-test node bin/${spec.name}.mjs
|
|
275
|
+
|
|
276
|
+
# Fails when a command was added but the agent instructions were not
|
|
277
|
+
# regenerated.
|
|
278
|
+
- run: node bin/${spec.name}.mjs init --check
|
|
279
|
+
`,
|
|
280
|
+
},
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-invokable",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaffold an agent-native CLI.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"create-invokable": "./bin/create-invokable.mjs"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"bin",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/beinvokable/invokable.git",
|
|
29
|
+
"directory": "packages/create-invokable"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -b",
|
|
36
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
37
|
+
}
|
|
38
|
+
}
|