docstar-cli 1.0.3 → 1.2.1-beta.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/README.md +38 -6
- package/bin/run.js +10 -0
- package/dist/commands/clean.d.ts +13 -0
- package/dist/commands/clean.js +88 -0
- package/dist/commands/complete.d.ts +9 -0
- package/dist/commands/complete.js +20 -0
- package/dist/commands/completion-script.d.ts +6 -0
- package/dist/commands/completion-script.js +39 -0
- package/dist/commands/init.js +14 -9
- package/dist/commands/mcp.js +3 -2
- package/dist/lib/codegen.js +6 -2
- package/dist/lib/complete.d.ts +2 -0
- package/dist/lib/complete.js +30 -0
- package/oclif.manifest.json +88 -103
- package/package.json +3 -8
- package/dist/commands/api-collection/slack/send-message-1.js +0 -30
- package/dist/commands/api-collection-2/viasocket/send-alert.js +0 -30
- package/dist/commands/docstar-apis/public-routes/find-matches-1.js +0 -33
package/README.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
A CLI for calling published DocStar API endpoints directly from your terminal, without leaving the shell to read docs. Point it at a DocStar docs site, pick which modules (e.g. "Slack") you want, and it turns each published endpoint into a real command you can run. You can install from multiple docs sites/collections at once — each is kept separate under its own **CLI name**.
|
|
4
4
|
|
|
5
|
+
## Concepts: collection → module → endpoint
|
|
6
|
+
|
|
7
|
+
Every command follows a fixed three-level hierarchy:
|
|
8
|
+
|
|
9
|
+
1. **Collection** — a docs site, identified by its **CLI name** (e.g. `msg91`). This is what `docstar-cli init` installs.
|
|
10
|
+
2. **Module** — a top-level section within that collection (e.g. `slack`).
|
|
11
|
+
3. **Endpoint** — one published API within a module, turned into a command (e.g. `send-message-1`).
|
|
12
|
+
|
|
13
|
+
Put together: `docstar-cli <collection> <module> <endpoint> [--flags]`, e.g. `docstar-cli msg91 slack send-message-1 --message "hi"`.
|
|
14
|
+
|
|
5
15
|
## Install
|
|
6
16
|
|
|
7
17
|
```sh
|
|
@@ -26,7 +36,7 @@ For a local/non-custom-domain docs site, pass the collection id explicitly:
|
|
|
26
36
|
docstar-cli init localhost:3000 --collectionId paM4R4A26Hvb
|
|
27
37
|
```
|
|
28
38
|
|
|
29
|
-
This fetches the list of published modules for that collection
|
|
39
|
+
This fetches the list of published modules for that collection and shows an interactive checklist (nothing checked by default — press `space` to toggle a module, or select the **"Select all modules"** entry at the bottom of the list to install everything, then `enter` to confirm). For each selected module it:
|
|
30
40
|
|
|
31
41
|
- saves its full endpoint contract to `~/.docstar/modules/<cli-name>/<module>.json`
|
|
32
42
|
- registers a real CLI command for every endpoint: `docstar-cli <cli-name> <module> <command>`
|
|
@@ -65,6 +75,26 @@ docstar-cli msg91 slack send-message-1
|
|
|
65
75
|
|
|
66
76
|
The command prints the response status and body from the actual API call.
|
|
67
77
|
|
|
78
|
+
### 4. Shell tab-completion
|
|
79
|
+
|
|
80
|
+
This is a purpose-built completion, not a generic "complete every command" plugin: it **only**
|
|
81
|
+
completes a module name (once you've typed a collection) and an endpoint name (once you've typed
|
|
82
|
+
a collection and module). It never completes the collection name itself, and never completes
|
|
83
|
+
top-level commands like `list`/`init`/`mcp` — you always type the collection by hand.
|
|
84
|
+
|
|
85
|
+
Set it up once (bash, or zsh with `bashcompinit` loaded):
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
echo 'eval "$(docstar-cli completion-script)"' >> ~/.bashrc # or ~/.zshrc
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then, after typing a known collection name, `Tab` completes modules and endpoints:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
docstar-cli msg91 sla<Tab> # -> slack
|
|
95
|
+
docstar-cli msg91 slack send<Tab> # -> send-message-1
|
|
96
|
+
```
|
|
97
|
+
|
|
68
98
|
## Use from an AI client (MCP)
|
|
69
99
|
|
|
70
100
|
`docstar-cli` can run as an [MCP](https://modelcontextprotocol.io) server, turning every installed
|
|
@@ -88,11 +118,13 @@ Register it with Claude Code (`.mcp.json`) or Claude Desktop (`claude_desktop_co
|
|
|
88
118
|
}
|
|
89
119
|
```
|
|
90
120
|
|
|
91
|
-
Each installed endpoint becomes one tool named `<
|
|
92
|
-
`msg91__slack__send-message-1`)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
121
|
+
Each installed endpoint becomes one tool named `<collection>__<module>__<endpoint>` (e.g.
|
|
122
|
+
`msg91__slack__send-message-1`) — the same collection → module → endpoint hierarchy described
|
|
123
|
+
above, just joined with `__` instead of spaces because MCP tool names can't contain spaces.
|
|
124
|
+
Required/optional parameters are exposed in the tool's schema. A prompt like "call the slack api
|
|
125
|
+
and send a message saying hi" resolves directly to that tool call — the AI client asks you for any
|
|
126
|
+
required value it can't infer, exactly like any other MCP tool. Run `docstar-cli init` first so
|
|
127
|
+
there's at least one installed module for `mcp` to expose.
|
|
96
128
|
|
|
97
129
|
## How it works
|
|
98
130
|
|
package/bin/run.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import {execute} from '@oclif/core'
|
|
4
|
+
import {readFileSync} from 'node:fs'
|
|
5
|
+
|
|
6
|
+
// oclif's built-in --version prints `docstar-cli/1.2.0 darwin-arm64 node-v22.22.3`. We only want
|
|
7
|
+
// the bare version number, so intercept it here before oclif ever sees the flag.
|
|
8
|
+
const args = process.argv.slice(2)
|
|
9
|
+
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v')) {
|
|
10
|
+
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
|
|
11
|
+
console.log(pkg.version)
|
|
12
|
+
process.exit(0)
|
|
13
|
+
}
|
|
4
14
|
|
|
5
15
|
await execute({dir: import.meta.url})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Clean extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
collection: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { checkbox, confirm } from '@inquirer/prompts';
|
|
2
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
3
|
+
import { readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { collectionModulesDir, configPath, modulePath } from '../lib/paths.js';
|
|
6
|
+
export default class Clean extends Command {
|
|
7
|
+
static args = {
|
|
8
|
+
collection: Args.string({ description: 'The collection (CLI name) to clean up, e.g. msg91', required: true }),
|
|
9
|
+
};
|
|
10
|
+
static description = [
|
|
11
|
+
'Uninstall modules from a collection, or the entire collection.',
|
|
12
|
+
'By default shows a checklist of the collection\'s installed modules to remove.',
|
|
13
|
+
'`--all` targets the whole collection instead; combine with `--force` to skip the confirmation prompt.',
|
|
14
|
+
].join('\n');
|
|
15
|
+
static examples = [
|
|
16
|
+
'<%= config.bin %> <%= command.id %> msg91 # pick modules to remove from "msg91"',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> msg91 --all # remove the whole "msg91" collection (asks to confirm)',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> msg91 --all --force # remove the whole "msg91" collection without asking',
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
all: Flags.boolean({ description: 'Remove the entire collection instead of picking modules' }),
|
|
22
|
+
force: Flags.boolean({ description: 'Skip the confirmation prompt (only relevant with --all)' }),
|
|
23
|
+
};
|
|
24
|
+
async run() {
|
|
25
|
+
const { args, flags } = await this.parse(Clean);
|
|
26
|
+
let savedConfig;
|
|
27
|
+
try {
|
|
28
|
+
savedConfig = JSON.parse(await readFile(configPath(), 'utf8'));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
this.error('No installed modules found. Run `docstar-cli init <domain>` first.');
|
|
32
|
+
}
|
|
33
|
+
const collection = savedConfig.collections.find((c) => c.cliName === args.collection);
|
|
34
|
+
if (!collection) {
|
|
35
|
+
this.error(`Collection "${args.collection}" isn't installed. Run \`docstar-cli list\` to see installed collections.`);
|
|
36
|
+
}
|
|
37
|
+
if (flags.all) {
|
|
38
|
+
if (!flags.force) {
|
|
39
|
+
const shouldDelete = await confirm({
|
|
40
|
+
default: false,
|
|
41
|
+
message: `Delete the entire "${collection.cliName}" collection and all ${collection.modules.length} installed module(s)?`,
|
|
42
|
+
});
|
|
43
|
+
if (!shouldDelete) {
|
|
44
|
+
this.log('Aborted, nothing was removed.');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
await rm(collectionModulesDir(collection.cliName), { force: true, recursive: true });
|
|
49
|
+
await rm(join(this.config.root, 'dist', 'commands', collection.cliName), { force: true, recursive: true });
|
|
50
|
+
savedConfig.collections = savedConfig.collections.filter((c) => c.cliName !== collection.cliName);
|
|
51
|
+
await writeFile(configPath(), JSON.stringify(savedConfig, null, 2));
|
|
52
|
+
this.log(`Removed collection "${collection.cliName}" and its ${collection.modules.length} module(s).`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (collection.modules.length === 0) {
|
|
56
|
+
this.log(`Collection "${collection.cliName}" has no installed modules.`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const SELECT_ALL = '__select_all__';
|
|
60
|
+
const rawSelection = await checkbox({
|
|
61
|
+
choices: [
|
|
62
|
+
...collection.modules.map((module) => ({ checked: false, name: `${module.name} (${module.path})`, value: module.path })),
|
|
63
|
+
{ checked: false, name: '➤ Select all modules', value: SELECT_ALL },
|
|
64
|
+
],
|
|
65
|
+
message: `Select modules to remove from "${collection.cliName}" (space to toggle, enter to confirm)`,
|
|
66
|
+
});
|
|
67
|
+
const selectedPaths = rawSelection.includes(SELECT_ALL) ? collection.modules.map((module) => module.path) : rawSelection;
|
|
68
|
+
if (selectedPaths.length === 0) {
|
|
69
|
+
this.log('No modules selected, nothing removed.');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
for (const path of selectedPaths) {
|
|
73
|
+
// eslint-disable-next-line no-await-in-loop
|
|
74
|
+
await rm(modulePath(collection.cliName, path), { force: true });
|
|
75
|
+
// eslint-disable-next-line no-await-in-loop
|
|
76
|
+
await rm(join(this.config.root, 'dist', 'commands', collection.cliName, path), { force: true, recursive: true });
|
|
77
|
+
this.log(` removed: ${collection.cliName} ${path}`);
|
|
78
|
+
}
|
|
79
|
+
collection.modules = collection.modules.filter((module) => !selectedPaths.includes(module.path));
|
|
80
|
+
if (collection.modules.length === 0) {
|
|
81
|
+
await rm(collectionModulesDir(collection.cliName), { force: true, recursive: true });
|
|
82
|
+
savedConfig.collections = savedConfig.collections.filter((c) => c.cliName !== collection.cliName);
|
|
83
|
+
this.log(`Collection "${collection.cliName}" has no modules left, removed it entirely.`);
|
|
84
|
+
}
|
|
85
|
+
await writeFile(configPath(), JSON.stringify(savedConfig, null, 2));
|
|
86
|
+
this.log(`Removed ${selectedPaths.length} module(s) from "${collection.cliName}".`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Complete extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
collection: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
module: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static hidden: boolean;
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import { completeEndpoints, completeModules } from '../lib/complete.js';
|
|
3
|
+
// Hidden helper invoked by the shell completion function (see `completion-script`), not meant to
|
|
4
|
+
// be run directly. Prints one suggestion per line: module names for `docstar-cli complete
|
|
5
|
+
// <collection>`, endpoint command names/aliases for `docstar-cli complete <collection> <module>`.
|
|
6
|
+
// Never suggests a collection name or a top-level command — completion only starts once the
|
|
7
|
+
// collection has already been typed.
|
|
8
|
+
export default class Complete extends Command {
|
|
9
|
+
static args = {
|
|
10
|
+
collection: Args.string({ required: true }),
|
|
11
|
+
module: Args.string({ required: false }),
|
|
12
|
+
};
|
|
13
|
+
static hidden = true;
|
|
14
|
+
async run() {
|
|
15
|
+
const { args } = await this.parse(Complete);
|
|
16
|
+
const suggestions = args.module ? await completeEndpoints(args.collection, args.module) : await completeModules(args.collection);
|
|
17
|
+
for (const suggestion of suggestions)
|
|
18
|
+
this.log(suggestion);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
const scriptFor = (bin) => {
|
|
3
|
+
const fnName = `_${bin.replaceAll(/[^a-zA-Z0-9]/g, '_')}_complete`;
|
|
4
|
+
return `# zsh doesn't support the bash-style \`complete\` builtin used below until compinit (for
|
|
5
|
+
# compdef) and bashcompinit (for complete/compgen) are both loaded, in that order.
|
|
6
|
+
if [ -n "$ZSH_VERSION" ]; then
|
|
7
|
+
autoload -Uz compinit && compinit -u
|
|
8
|
+
autoload -U +X bashcompinit && bashcompinit
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
${fnName}() {
|
|
12
|
+
local cur collection module
|
|
13
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
14
|
+
collection="\${COMP_WORDS[1]}"
|
|
15
|
+
module="\${COMP_WORDS[2]}"
|
|
16
|
+
|
|
17
|
+
COMPREPLY=()
|
|
18
|
+
|
|
19
|
+
if [ "$COMP_CWORD" -eq 2 ] && [ -n "$collection" ]; then
|
|
20
|
+
COMPREPLY=( $(compgen -W "$(${bin} complete "$collection" 2>/dev/null)" -- "$cur") )
|
|
21
|
+
elif [ "$COMP_CWORD" -eq 3 ] && [ -n "$collection" ] && [ -n "$module" ]; then
|
|
22
|
+
COMPREPLY=( $(compgen -W "$(${bin} complete "$collection" "$module" 2>/dev/null)" -- "$cur") )
|
|
23
|
+
fi
|
|
24
|
+
# COMP_CWORD 0/1 (the binary name and the collection itself) intentionally get no
|
|
25
|
+
# suggestions — the collection is always typed out by hand.
|
|
26
|
+
}
|
|
27
|
+
complete -F ${fnName} ${bin}
|
|
28
|
+
`;
|
|
29
|
+
};
|
|
30
|
+
export default class CompletionScript extends Command {
|
|
31
|
+
static description = [
|
|
32
|
+
'Print a shell completion function that completes ONLY module names (once a collection is typed) and endpoint names (once a collection and module are typed).',
|
|
33
|
+
'It never completes the collection name itself, and never completes top-level commands like `list`/`init`/`mcp` — those are typed out by hand.',
|
|
34
|
+
].join('\n');
|
|
35
|
+
static examples = ['echo \'eval "$(<%= config.bin %> <%= command.id %>)"\' >> ~/.bashrc # or ~/.zshrc'];
|
|
36
|
+
async run() {
|
|
37
|
+
this.log(scriptFor(this.config.bin));
|
|
38
|
+
}
|
|
39
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -55,17 +55,22 @@ export default class Init extends Command {
|
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
const { cliName } = listResponse.collection;
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
const SELECT_ALL = '__select_all__';
|
|
59
|
+
const rawSelection = await checkbox({
|
|
60
|
+
choices: [
|
|
61
|
+
...listResponse.modules.map((module) => {
|
|
62
|
+
const count = module.endpointCount ?? module.endpoints?.length ?? 0;
|
|
63
|
+
return {
|
|
64
|
+
checked: false,
|
|
65
|
+
name: `${module.name} (${module.path}) — ${count} endpoint${count === 1 ? '' : 's'}`,
|
|
66
|
+
value: module.path,
|
|
67
|
+
};
|
|
68
|
+
}),
|
|
69
|
+
{ checked: false, name: '➤ Select all modules', value: SELECT_ALL },
|
|
70
|
+
],
|
|
67
71
|
message: `Select modules to install for "${cliName}" (space to toggle, enter to confirm)`,
|
|
68
72
|
});
|
|
73
|
+
const selectedPaths = rawSelection.includes(SELECT_ALL) ? listResponse.modules.map((module) => module.path) : rawSelection;
|
|
69
74
|
if (selectedPaths.length === 0) {
|
|
70
75
|
this.log('No modules selected, nothing installed.');
|
|
71
76
|
return;
|
package/dist/commands/mcp.js
CHANGED
|
@@ -17,7 +17,8 @@ const buildInputShape = (required, optional) => {
|
|
|
17
17
|
export default class Mcp extends Command {
|
|
18
18
|
static description = [
|
|
19
19
|
'Start an MCP (Model Context Protocol) server exposing every installed endpoint (across all installed collections) as a callable tool for AI clients (Claude Desktop, Claude Code, etc.).',
|
|
20
|
-
'
|
|
20
|
+
'Every tool follows the collection -> module -> endpoint hierarchy: e.g. collection "msg91" -> module "slack" -> endpoint "send-message-1" becomes the tool `msg91__slack__send-message-1`.',
|
|
21
|
+
'Run `docstar-cli init <domain>` first so there is at least one installed module to expose.',
|
|
21
22
|
'This command does not exit on its own — it stays running and communicates over stdio for as long as the connecting AI client keeps the connection open.',
|
|
22
23
|
].join('\n');
|
|
23
24
|
static examples = [
|
|
@@ -42,7 +43,7 @@ export default class Mcp extends Command {
|
|
|
42
43
|
for (const endpoint of moduleJson.endpoints) {
|
|
43
44
|
const { cli } = endpoint;
|
|
44
45
|
const toolName = sanitize(`${collection.cliName}__${module.path}__${cli.command.name}`);
|
|
45
|
-
const description =
|
|
46
|
+
const description = `[collection: ${collection.cliName} / module: ${module.path}] ${cli.description || cli.command.name}`;
|
|
46
47
|
const inputShape = buildInputShape(cli.parameters.required, cli.parameters.optional);
|
|
47
48
|
server.tool(toolName, description, inputShape, async (args) => {
|
|
48
49
|
try {
|
package/dist/lib/codegen.js
CHANGED
|
@@ -2,7 +2,11 @@ import { mkdir, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
const flagLine = (name, required) =>
|
|
4
4
|
// Required params are collected interactively when missing, so the flag itself stays optional to oclif.
|
|
5
|
-
|
|
5
|
+
// Param names come from endpoint body/param keys authored in a rich-text editor and can contain
|
|
6
|
+
// quotes or HTML markup (e.g. a stray `<span text-block='true'>...`) — JSON.stringify escapes
|
|
7
|
+
// that safely for embedding in generated source; naive string interpolation does not and can
|
|
8
|
+
// produce invalid JS.
|
|
9
|
+
` ${JSON.stringify(name)}: Flags.string({description: ${JSON.stringify(`${required ? 'Required' : 'Optional'} parameter`)}}),`;
|
|
6
10
|
const commandFileContent = (cliName, modulePathSegment, endpoint) => {
|
|
7
11
|
const { cli } = endpoint;
|
|
8
12
|
const flagLines = [
|
|
@@ -32,7 +36,7 @@ ${flagLines}
|
|
|
32
36
|
Object.assign(params, await promptForMissingParams(REQUIRED_PARAMS, params))
|
|
33
37
|
|
|
34
38
|
try {
|
|
35
|
-
const result = await executeCliCommand(
|
|
39
|
+
const result = await executeCliCommand(${JSON.stringify(cliName)}, ${JSON.stringify(modulePathSegment)}, ${JSON.stringify(cli.command.name)}, params)
|
|
36
40
|
this.log(JSON.stringify(result, null, 2))
|
|
37
41
|
} catch (error) {
|
|
38
42
|
this.error(error.message)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { loadModule } from './http.js';
|
|
3
|
+
import { configPath } from './paths.js';
|
|
4
|
+
const readSavedConfig = async () => {
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(await readFile(configPath(), 'utf8'));
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return { collections: [] };
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
// Suggestions for shell completion — intentionally scoped to module names (given a collection)
|
|
13
|
+
// and endpoint command names/aliases (given a collection + module). Never suggests collection
|
|
14
|
+
// names or top-level commands (list/init/mcp/...); the user is expected to type the collection
|
|
15
|
+
// themselves.
|
|
16
|
+
export const completeModules = async (cliName) => {
|
|
17
|
+
const savedConfig = await readSavedConfig();
|
|
18
|
+
const collection = savedConfig.collections.find((c) => c.cliName === cliName);
|
|
19
|
+
return collection ? collection.modules.map((module) => module.path) : [];
|
|
20
|
+
};
|
|
21
|
+
export const completeEndpoints = async (cliName, modulePathSegment) => {
|
|
22
|
+
try {
|
|
23
|
+
const moduleJson = await loadModule(cliName, modulePathSegment);
|
|
24
|
+
const names = moduleJson.endpoints.flatMap((endpoint) => [endpoint.cli.command.name, ...endpoint.cli.command.aliases]);
|
|
25
|
+
return [...new Set(names)];
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
};
|
package/oclif.manifest.json
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"commands": {
|
|
3
|
-
"
|
|
3
|
+
"clean": {
|
|
4
4
|
"aliases": [],
|
|
5
5
|
"args": {
|
|
6
|
-
"
|
|
7
|
-
"description": "
|
|
8
|
-
"name": "
|
|
6
|
+
"collection": {
|
|
7
|
+
"description": "The collection (CLI name) to clean up, e.g. msg91",
|
|
8
|
+
"name": "collection",
|
|
9
9
|
"required": true
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
-
"description": "
|
|
12
|
+
"description": "Uninstall modules from a collection, or the entire collection.\nBy default shows a checklist of the collection's installed modules to remove.\n`--all` targets the whole collection instead; combine with `--force` to skip the confirmation prompt.",
|
|
13
13
|
"examples": [
|
|
14
|
-
"<%= config.bin %> <%= command.id %>
|
|
15
|
-
"<%= config.bin %> <%= command.id %>
|
|
14
|
+
"<%= config.bin %> <%= command.id %> msg91 # pick modules to remove from \"msg91\"",
|
|
15
|
+
"<%= config.bin %> <%= command.id %> msg91 --all # remove the whole \"msg91\" collection (asks to confirm)",
|
|
16
|
+
"<%= config.bin %> <%= command.id %> msg91 --all --force # remove the whole \"msg91\" collection without asking"
|
|
16
17
|
],
|
|
17
18
|
"flags": {
|
|
18
|
-
"
|
|
19
|
-
"description": "
|
|
20
|
-
"name": "
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
19
|
+
"all": {
|
|
20
|
+
"description": "Remove the entire collection instead of picking modules",
|
|
21
|
+
"name": "all",
|
|
22
|
+
"allowNo": false,
|
|
23
|
+
"type": "boolean"
|
|
24
|
+
},
|
|
25
|
+
"force": {
|
|
26
|
+
"description": "Skip the confirmation prompt (only relevant with --all)",
|
|
27
|
+
"name": "force",
|
|
28
|
+
"allowNo": false,
|
|
29
|
+
"type": "boolean"
|
|
24
30
|
}
|
|
25
31
|
},
|
|
26
32
|
"hasDynamicHelp": false,
|
|
27
33
|
"hiddenAliases": [],
|
|
28
|
-
"id": "
|
|
34
|
+
"id": "clean",
|
|
29
35
|
"pluginAlias": "docstar-cli",
|
|
30
36
|
"pluginName": "docstar-cli",
|
|
31
37
|
"pluginType": "core",
|
|
@@ -35,41 +41,26 @@
|
|
|
35
41
|
"relativePath": [
|
|
36
42
|
"dist",
|
|
37
43
|
"commands",
|
|
38
|
-
"
|
|
44
|
+
"clean.js"
|
|
39
45
|
]
|
|
40
46
|
},
|
|
41
|
-
"
|
|
47
|
+
"complete": {
|
|
42
48
|
"aliases": [],
|
|
43
49
|
"args": {
|
|
44
50
|
"collection": {
|
|
45
|
-
"description": "Show modules installed for this collection (CLI name), e.g. msg91",
|
|
46
51
|
"name": "collection",
|
|
47
|
-
"required":
|
|
52
|
+
"required": true
|
|
48
53
|
},
|
|
49
54
|
"module": {
|
|
50
|
-
"description": "Show all endpoints for this module within the collection, e.g. slack",
|
|
51
55
|
"name": "module",
|
|
52
56
|
"required": false
|
|
53
57
|
}
|
|
54
58
|
},
|
|
55
|
-
"
|
|
56
|
-
"examples": [
|
|
57
|
-
"<%= config.bin %> <%= command.id %> # list every installed collection",
|
|
58
|
-
"<%= config.bin %> <%= command.id %> --all # same as above",
|
|
59
|
-
"<%= config.bin %> <%= command.id %> msg91 # list modules installed for the \"msg91\" collection",
|
|
60
|
-
"<%= config.bin %> <%= command.id %> msg91 slack # list all of Slack's installed endpoints in \"msg91\""
|
|
61
|
-
],
|
|
62
|
-
"flags": {
|
|
63
|
-
"all": {
|
|
64
|
-
"description": "List every installed collection (default when no arguments are given)",
|
|
65
|
-
"name": "all",
|
|
66
|
-
"allowNo": false,
|
|
67
|
-
"type": "boolean"
|
|
68
|
-
}
|
|
69
|
-
},
|
|
59
|
+
"flags": {},
|
|
70
60
|
"hasDynamicHelp": false,
|
|
61
|
+
"hidden": true,
|
|
71
62
|
"hiddenAliases": [],
|
|
72
|
-
"id": "
|
|
63
|
+
"id": "complete",
|
|
73
64
|
"pluginAlias": "docstar-cli",
|
|
74
65
|
"pluginName": "docstar-cli",
|
|
75
66
|
"pluginType": "core",
|
|
@@ -79,22 +70,20 @@
|
|
|
79
70
|
"relativePath": [
|
|
80
71
|
"dist",
|
|
81
72
|
"commands",
|
|
82
|
-
"
|
|
73
|
+
"complete.js"
|
|
83
74
|
]
|
|
84
75
|
},
|
|
85
|
-
"
|
|
76
|
+
"completion-script": {
|
|
86
77
|
"aliases": [],
|
|
87
78
|
"args": {},
|
|
88
|
-
"description": "
|
|
79
|
+
"description": "Print a shell completion function that completes ONLY module names (once a collection is typed) and endpoint names (once a collection and module are typed).\nIt never completes the collection name itself, and never completes top-level commands like `list`/`init`/`mcp` — those are typed out by hand.",
|
|
89
80
|
"examples": [
|
|
90
|
-
"<%= config.bin %> <%= command.id %>"
|
|
91
|
-
"# Then register it in an MCP client config, e.g. .mcp.json:",
|
|
92
|
-
"# { \"mcpServers\": { \"docstar\": { \"command\": \"docstar-cli\", \"args\": [\"mcp\"] } } }"
|
|
81
|
+
"echo 'eval \"$(<%= config.bin %> <%= command.id %>)\"' >> ~/.bashrc # or ~/.zshrc"
|
|
93
82
|
],
|
|
94
83
|
"flags": {},
|
|
95
84
|
"hasDynamicHelp": false,
|
|
96
85
|
"hiddenAliases": [],
|
|
97
|
-
"id": "
|
|
86
|
+
"id": "completion-script",
|
|
98
87
|
"pluginAlias": "docstar-cli",
|
|
99
88
|
"pluginName": "docstar-cli",
|
|
100
89
|
"pluginType": "core",
|
|
@@ -104,17 +93,27 @@
|
|
|
104
93
|
"relativePath": [
|
|
105
94
|
"dist",
|
|
106
95
|
"commands",
|
|
107
|
-
"
|
|
96
|
+
"completion-script.js"
|
|
108
97
|
]
|
|
109
98
|
},
|
|
110
|
-
"
|
|
99
|
+
"init": {
|
|
111
100
|
"aliases": [],
|
|
112
|
-
"args": {
|
|
113
|
-
|
|
101
|
+
"args": {
|
|
102
|
+
"domain": {
|
|
103
|
+
"description": "DocStar docs domain, e.g. docs.msg91.com or localhost:3000",
|
|
104
|
+
"name": "domain",
|
|
105
|
+
"required": true
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"description": "Discover published modules for a DocStar docs site, pick which ones to install, and register their CLI commands.\nYou can run this against multiple docs sites/collections — each is kept separate under its own CLI name (`docstar-cli <cli-name> <module> <command>`).\nAfter installing, run `docstar-cli list` to see what got installed.",
|
|
109
|
+
"examples": [
|
|
110
|
+
"<%= config.bin %> <%= command.id %> docs.msg91.com",
|
|
111
|
+
"<%= config.bin %> <%= command.id %> localhost:3000 --collectionId paM4R4A26Hvb"
|
|
112
|
+
],
|
|
114
113
|
"flags": {
|
|
115
|
-
"
|
|
116
|
-
"description": "
|
|
117
|
-
"name": "
|
|
114
|
+
"collectionId": {
|
|
115
|
+
"description": "Collection id (required when the domain is not a custom domain, e.g. localhost)",
|
|
116
|
+
"name": "collectionId",
|
|
118
117
|
"hasDynamicHelp": false,
|
|
119
118
|
"multiple": false,
|
|
120
119
|
"type": "option"
|
|
@@ -122,7 +121,7 @@
|
|
|
122
121
|
},
|
|
123
122
|
"hasDynamicHelp": false,
|
|
124
123
|
"hiddenAliases": [],
|
|
125
|
-
"id": "
|
|
124
|
+
"id": "init",
|
|
126
125
|
"pluginAlias": "docstar-cli",
|
|
127
126
|
"pluginName": "docstar-cli",
|
|
128
127
|
"pluginType": "core",
|
|
@@ -132,27 +131,41 @@
|
|
|
132
131
|
"relativePath": [
|
|
133
132
|
"dist",
|
|
134
133
|
"commands",
|
|
135
|
-
"
|
|
136
|
-
"viasocket",
|
|
137
|
-
"send-alert.js"
|
|
134
|
+
"init.js"
|
|
138
135
|
]
|
|
139
136
|
},
|
|
140
|
-
"
|
|
137
|
+
"list": {
|
|
141
138
|
"aliases": [],
|
|
142
|
-
"args": {
|
|
143
|
-
|
|
139
|
+
"args": {
|
|
140
|
+
"collection": {
|
|
141
|
+
"description": "Show modules installed for this collection (CLI name), e.g. msg91",
|
|
142
|
+
"name": "collection",
|
|
143
|
+
"required": false
|
|
144
|
+
},
|
|
145
|
+
"module": {
|
|
146
|
+
"description": "Show all endpoints for this module within the collection, e.g. slack",
|
|
147
|
+
"name": "module",
|
|
148
|
+
"required": false
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"description": "List installed collections, the modules within one, or every endpoint of one module",
|
|
152
|
+
"examples": [
|
|
153
|
+
"<%= config.bin %> <%= command.id %> # list every installed collection",
|
|
154
|
+
"<%= config.bin %> <%= command.id %> --all # same as above",
|
|
155
|
+
"<%= config.bin %> <%= command.id %> msg91 # list modules installed for the \"msg91\" collection",
|
|
156
|
+
"<%= config.bin %> <%= command.id %> msg91 slack # list all of Slack's installed endpoints in \"msg91\""
|
|
157
|
+
],
|
|
144
158
|
"flags": {
|
|
145
|
-
"
|
|
146
|
-
"description": "
|
|
147
|
-
"name": "
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"type": "option"
|
|
159
|
+
"all": {
|
|
160
|
+
"description": "List every installed collection (default when no arguments are given)",
|
|
161
|
+
"name": "all",
|
|
162
|
+
"allowNo": false,
|
|
163
|
+
"type": "boolean"
|
|
151
164
|
}
|
|
152
165
|
},
|
|
153
166
|
"hasDynamicHelp": false,
|
|
154
167
|
"hiddenAliases": [],
|
|
155
|
-
"id": "
|
|
168
|
+
"id": "list",
|
|
156
169
|
"pluginAlias": "docstar-cli",
|
|
157
170
|
"pluginName": "docstar-cli",
|
|
158
171
|
"pluginType": "core",
|
|
@@ -162,48 +175,22 @@
|
|
|
162
175
|
"relativePath": [
|
|
163
176
|
"dist",
|
|
164
177
|
"commands",
|
|
165
|
-
"
|
|
166
|
-
"slack",
|
|
167
|
-
"send-message-1.js"
|
|
178
|
+
"list.js"
|
|
168
179
|
]
|
|
169
180
|
},
|
|
170
|
-
"
|
|
181
|
+
"mcp": {
|
|
171
182
|
"aliases": [],
|
|
172
183
|
"args": {},
|
|
173
|
-
"description": "
|
|
174
|
-
"
|
|
175
|
-
"
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
"type": "option"
|
|
181
|
-
},
|
|
182
|
-
"collectionIds": {
|
|
183
|
-
"description": "Required parameter",
|
|
184
|
-
"name": "collectionIds",
|
|
185
|
-
"hasDynamicHelp": false,
|
|
186
|
-
"multiple": false,
|
|
187
|
-
"type": "option"
|
|
188
|
-
},
|
|
189
|
-
"getUrl": {
|
|
190
|
-
"description": "Required parameter",
|
|
191
|
-
"name": "getUrl",
|
|
192
|
-
"hasDynamicHelp": false,
|
|
193
|
-
"multiple": false,
|
|
194
|
-
"type": "option"
|
|
195
|
-
},
|
|
196
|
-
"allowNonDomain": {
|
|
197
|
-
"description": "Required parameter",
|
|
198
|
-
"name": "allowNonDomain",
|
|
199
|
-
"hasDynamicHelp": false,
|
|
200
|
-
"multiple": false,
|
|
201
|
-
"type": "option"
|
|
202
|
-
}
|
|
203
|
-
},
|
|
184
|
+
"description": "Start an MCP (Model Context Protocol) server exposing every installed endpoint (across all installed collections) as a callable tool for AI clients (Claude Desktop, Claude Code, etc.).\nEvery tool follows the collection -> module -> endpoint hierarchy: e.g. collection \"msg91\" -> module \"slack\" -> endpoint \"send-message-1\" becomes the tool `msg91__slack__send-message-1`.\nRun `docstar-cli init <domain>` first so there is at least one installed module to expose.\nThis command does not exit on its own — it stays running and communicates over stdio for as long as the connecting AI client keeps the connection open.",
|
|
185
|
+
"examples": [
|
|
186
|
+
"<%= config.bin %> <%= command.id %>",
|
|
187
|
+
"# Then register it in an MCP client config, e.g. .mcp.json:",
|
|
188
|
+
"# { \"mcpServers\": { \"docstar\": { \"command\": \"docstar-cli\", \"args\": [\"mcp\"] } } }"
|
|
189
|
+
],
|
|
190
|
+
"flags": {},
|
|
204
191
|
"hasDynamicHelp": false,
|
|
205
192
|
"hiddenAliases": [],
|
|
206
|
-
"id": "
|
|
193
|
+
"id": "mcp",
|
|
207
194
|
"pluginAlias": "docstar-cli",
|
|
208
195
|
"pluginName": "docstar-cli",
|
|
209
196
|
"pluginType": "core",
|
|
@@ -213,11 +200,9 @@
|
|
|
213
200
|
"relativePath": [
|
|
214
201
|
"dist",
|
|
215
202
|
"commands",
|
|
216
|
-
"
|
|
217
|
-
"public-routes",
|
|
218
|
-
"find-matches-1.js"
|
|
203
|
+
"mcp.js"
|
|
219
204
|
]
|
|
220
205
|
}
|
|
221
206
|
},
|
|
222
|
-
"version": "1.0
|
|
207
|
+
"version": "1.2.1-beta.0"
|
|
223
208
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docstar-cli",
|
|
3
3
|
"description": "Call published DocStar API endpoints directly from your terminal, or expose them as MCP tools for AI clients.",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.2.1-beta.0",
|
|
5
5
|
"author": "Swayam Maheshwari",
|
|
6
6
|
"bin": {
|
|
7
7
|
"docstar-cli": "./bin/run.js"
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
13
13
|
"@oclif/core": "^4",
|
|
14
14
|
"@oclif/plugin-help": "^6",
|
|
15
|
-
"@oclif/plugin-plugins": "^5",
|
|
16
15
|
"zod": "^4.4.3"
|
|
17
16
|
},
|
|
18
17
|
"devDependencies": {
|
|
@@ -52,13 +51,9 @@
|
|
|
52
51
|
"dirname": "docstar-cli",
|
|
53
52
|
"commands": "./dist/commands",
|
|
54
53
|
"plugins": [
|
|
55
|
-
"@oclif/plugin-help"
|
|
56
|
-
"@oclif/plugin-plugins"
|
|
54
|
+
"@oclif/plugin-help"
|
|
57
55
|
],
|
|
58
|
-
"topicSeparator": " "
|
|
59
|
-
"additionalVersionFlags": [
|
|
60
|
-
"-v"
|
|
61
|
-
]
|
|
56
|
+
"topicSeparator": " "
|
|
62
57
|
},
|
|
63
58
|
"repository": "swayammaheshwari/docstar-cli",
|
|
64
59
|
"scripts": {
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import {Command, Flags} from '@oclif/core'
|
|
2
|
-
import {executeCliCommand} from '../../../lib/http.js'
|
|
3
|
-
import {promptForMissingParams} from '../../../lib/prompt.js'
|
|
4
|
-
|
|
5
|
-
const REQUIRED_PARAMS = ["message"]
|
|
6
|
-
|
|
7
|
-
export default class Generated extends Command {
|
|
8
|
-
static description = "send message"
|
|
9
|
-
static aliases = []
|
|
10
|
-
static flags = {
|
|
11
|
-
'message': Flags.string({description: 'Required parameter'}),
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async run() {
|
|
15
|
-
const {flags} = await this.parse(Generated)
|
|
16
|
-
const params = {}
|
|
17
|
-
for (const [key, value] of Object.entries(flags)) {
|
|
18
|
-
if (value !== undefined) params[key] = value
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
Object.assign(params, await promptForMissingParams(REQUIRED_PARAMS, params))
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const result = await executeCliCommand('api-collection', 'slack', 'send-message-1', params)
|
|
25
|
-
this.log(JSON.stringify(result, null, 2))
|
|
26
|
-
} catch (error) {
|
|
27
|
-
this.error(error.message)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import {Command, Flags} from '@oclif/core'
|
|
2
|
-
import {executeCliCommand} from '../../../lib/http.js'
|
|
3
|
-
import {promptForMissingParams} from '../../../lib/prompt.js'
|
|
4
|
-
|
|
5
|
-
const REQUIRED_PARAMS = ["message"]
|
|
6
|
-
|
|
7
|
-
export default class Generated extends Command {
|
|
8
|
-
static description = "send alert"
|
|
9
|
-
static aliases = []
|
|
10
|
-
static flags = {
|
|
11
|
-
'message': Flags.string({description: 'Required parameter'}),
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async run() {
|
|
15
|
-
const {flags} = await this.parse(Generated)
|
|
16
|
-
const params = {}
|
|
17
|
-
for (const [key, value] of Object.entries(flags)) {
|
|
18
|
-
if (value !== undefined) params[key] = value
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
Object.assign(params, await promptForMissingParams(REQUIRED_PARAMS, params))
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const result = await executeCliCommand('api-collection-2', 'viasocket', 'send-alert', params)
|
|
25
|
-
this.log(JSON.stringify(result, null, 2))
|
|
26
|
-
} catch (error) {
|
|
27
|
-
this.error(error.message)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import {Command, Flags} from '@oclif/core'
|
|
2
|
-
import {executeCliCommand} from '../../../lib/http.js'
|
|
3
|
-
import {promptForMissingParams} from '../../../lib/prompt.js'
|
|
4
|
-
|
|
5
|
-
const REQUIRED_PARAMS = ["query","collectionIds","getUrl","allowNonDomain"]
|
|
6
|
-
|
|
7
|
-
export default class Generated extends Command {
|
|
8
|
-
static description = "Find matches"
|
|
9
|
-
static aliases = []
|
|
10
|
-
static flags = {
|
|
11
|
-
'query': Flags.string({description: 'Required parameter'}),
|
|
12
|
-
'collectionIds': Flags.string({description: 'Required parameter'}),
|
|
13
|
-
'getUrl': Flags.string({description: 'Required parameter'}),
|
|
14
|
-
'allowNonDomain': Flags.string({description: 'Required parameter'}),
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async run() {
|
|
18
|
-
const {flags} = await this.parse(Generated)
|
|
19
|
-
const params = {}
|
|
20
|
-
for (const [key, value] of Object.entries(flags)) {
|
|
21
|
-
if (value !== undefined) params[key] = value
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
Object.assign(params, await promptForMissingParams(REQUIRED_PARAMS, params))
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
const result = await executeCliCommand('docstar-apis', 'public-routes', 'find-matches-1', params)
|
|
28
|
-
this.log(JSON.stringify(result, null, 2))
|
|
29
|
-
} catch (error) {
|
|
30
|
-
this.error(error.message)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|