@wirecat/cli-template 0.8.0 → 0.10.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/.github/workflows/ci.yml +3 -3
- package/.github/workflows/release.yml +63 -0
- package/.gitleaks.toml +18 -0
- package/AGENTS.md +9 -15
- package/CHANGELOG.md +87 -1
- package/README.md +4 -3
- package/bin/release +5 -3
- package/contracts/plan.json +2 -2
- package/dist/bin/clidev.js +34 -12
- package/dist/bundle/all-profiles-XHC7BG3P.js +24 -0
- package/dist/bundle/anthropic-N6B3ZZHQ.js +70 -0
- package/dist/bundle/bun-HUJLS7KP.js +169 -0
- package/dist/bundle/bun-sqlite-OWFO2TVR.js +69 -0
- package/dist/bundle/chunk-222FANS2.js +161 -0
- package/dist/bundle/chunk-2CFBHYFA.js +884 -0
- package/dist/bundle/chunk-2KQZ24GI.js +82 -0
- package/dist/bundle/chunk-2TBVT4SZ.js +31 -0
- package/dist/bundle/chunk-437W3Z2M.js +76 -0
- package/dist/bundle/chunk-4QRLPOJ7.js +18 -0
- package/dist/bundle/chunk-54AJONOM.js +54 -0
- package/dist/bundle/chunk-5D4AEUVA.js +24783 -0
- package/dist/bundle/chunk-6CBWO7OF.js +73 -0
- package/dist/bundle/chunk-75LVQQEK.js +35782 -0
- package/dist/bundle/chunk-7RB76E2L.js +264 -0
- package/dist/bundle/chunk-7RXBN7HP.js +63 -0
- package/dist/bundle/chunk-ABP7FDMJ.js +122 -0
- package/dist/bundle/chunk-ADLS6FVG.js +24600 -0
- package/dist/bundle/chunk-AP7QKYBG.js +3531 -0
- package/dist/bundle/chunk-ASMH7JWZ.js +288 -0
- package/dist/bundle/chunk-B4EED2RF.js +100 -0
- package/dist/bundle/chunk-B4LM2B6F.js +1900 -0
- package/dist/bundle/chunk-BDSDQCV4.js +281 -0
- package/dist/bundle/chunk-DFDN6AUB.js +21 -0
- package/dist/bundle/chunk-DMCN22H6.js +35 -0
- package/dist/bundle/chunk-E42W72PQ.js +151 -0
- package/dist/bundle/chunk-EFHLQXG6.js +5104 -0
- package/dist/bundle/chunk-FIHDGZIB.js +34352 -0
- package/dist/bundle/chunk-FWVKHD2L.js +48 -0
- package/dist/bundle/chunk-FXS3X76A.js +193 -0
- package/dist/bundle/chunk-HTQTIKWP.js +928 -0
- package/dist/bundle/chunk-IJ3IA7EK.js +24 -0
- package/dist/bundle/chunk-JEBSORH7.js +3379 -0
- package/dist/bundle/chunk-JTTW5ZKF.js +45 -0
- package/dist/bundle/chunk-LSEF4QC3.js +3075 -0
- package/dist/bundle/chunk-LY7GS2YI.js +58 -0
- package/dist/bundle/chunk-MFS52LTZ.js +570 -0
- package/dist/bundle/chunk-MNSD5BX2.js +78 -0
- package/dist/bundle/chunk-MTGLJCAZ.js +24 -0
- package/dist/bundle/chunk-MZVV2JZT.js +744 -0
- package/dist/bundle/chunk-NE3NSB5O.js +8341 -0
- package/dist/bundle/chunk-NTLPDH76.js +440 -0
- package/dist/bundle/chunk-O3PBQVAB.js +447 -0
- package/dist/bundle/chunk-O5EHUB2V.js +2 -0
- package/dist/bundle/chunk-O5LMDQTO.js +82 -0
- package/dist/bundle/chunk-OPJ24VGH.js +1028 -0
- package/dist/bundle/chunk-OPPMGPFW.js +164 -0
- package/dist/bundle/chunk-QGNNMPUB.js +158 -0
- package/dist/bundle/chunk-QVJLOUQB.js +2 -0
- package/dist/bundle/chunk-S57AMKLX.js +6848 -0
- package/dist/bundle/chunk-TARN7CHN.js +215 -0
- package/dist/bundle/chunk-TJLGI3N6.js +1095 -0
- package/dist/bundle/chunk-VMTM46FH.js +7532 -0
- package/dist/bundle/chunk-W2IDKBNB.js +2315 -0
- package/dist/bundle/chunk-XFXBXQBS.js +630 -0
- package/dist/bundle/chunk-Y5E2WNWB.js +35 -0
- package/dist/bundle/chunk-ZVVMGPBJ.js +50 -0
- package/dist/bundle/clidev.js +22 -0
- package/dist/bundle/command-contract-PEM3S634.js +29 -0
- package/dist/bundle/dist-ESHIQU4M.js +7577 -0
- package/dist/bundle/echarts-2QGL2JP5.js +42501 -0
- package/dist/bundle/embed-HHAAGHA6.js +35 -0
- package/dist/bundle/embedder-W2OGVVYH.js +108 -0
- package/dist/bundle/first-run-WCJUDUBU.js +23 -0
- package/dist/bundle/first-screen-U4DFUKUC.js +27 -0
- package/dist/bundle/instructions-OTZHT6NV.js +9 -0
- package/dist/bundle/mcp-OQBZXELQ.js +165 -0
- package/dist/bundle/mcp-VVF6J7SQ.js +151 -0
- package/dist/bundle/mcp-Y7VCIZ2J.js +78 -0
- package/dist/bundle/node-P4E63KXA.js +2340 -0
- package/dist/bundle/node-SYD4GX5O.js +176 -0
- package/dist/bundle/node-TLZSIHIJ.js +21 -0
- package/dist/bundle/node-sqlite-X2DGRFOZ.js +45 -0
- package/dist/bundle/oauth-42M34GQ5.js +17 -0
- package/dist/bundle/open-SWXRCDLN.js +19 -0
- package/dist/bundle/process-KAFGXZ7Q.js +91 -0
- package/dist/bundle/program-EHR4G7VN.js +16967 -0
- package/dist/bundle/prompt-ZQTMHRGX.js +12 -0
- package/dist/bundle/recognize-TTED4SE3.js +190 -0
- package/dist/bundle/remote-C37XJ4BP.js +26 -0
- package/dist/bundle/rerank-APFZAMQL.js +49 -0
- package/dist/bundle/serve-DDPIHDVZ.js +19 -0
- package/dist/bundle/server-L5GNG7KC.js +793 -0
- package/dist/bundle/server-LKVYA2AE.js +39 -0
- package/dist/bundle/state-WWPPHERF.js +33 -0
- package/dist/bundle/store-H6UIA4PK.js +120 -0
- package/dist/bundle/store-maintenance-command-EYBT64A2.js +40 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +45 -30
- package/dist/commands/mcp.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/commands.md +38 -22
- package/docs/dev/TESTING.md +14 -3
- package/docs/dev/reference-clis.md +17 -9
- package/docs/dev/skills/release/SKILL.md +9 -6
- package/docs/dev/skills/release/fresh-audit.md +4 -0
- package/docs/dev/test-matrix.md +10 -11
- package/docs/security.md +4 -0
- package/docs/usage.md +7 -6
- package/kit/dist/new.js +17 -2
- package/package.json +7 -5
- package/scripts/audit.sh +5 -3
- package/scripts/smoke-bundle.ts +63 -0
- package/scripts/test-matrix-untested.ts +4 -10
- package/scripts/version-level.test.ts +17 -1
- package/scripts/version-level.ts +35 -7
- package/skills/clidev-cli/SKILL.md +8 -0
- package/src/commands/mcp.ts +54 -33
- package/src/mcp-http.test.ts +20 -13
- package/src/mcp.test.ts +3 -3
- package/src/program.test.ts +8 -2
- package/src/roots.test.ts +2 -7
- package/src/version.ts +1 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: release
|
|
3
|
-
description:
|
|
3
|
+
description: Release this CLI — run release:check, write the changelog under the handbook's headings, check the docs against what changed, raise the version in a pull request, merge it and publish with bin/release from GitHub Actions. Use when asked to release, prepare a release, cut a version, «выпуск», «выпустить версию».
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Release
|
|
7
7
|
|
|
8
8
|
The rules are the handbook's [Releases page](../../../../node_modules/@wirecat/cli-base/docs/standards/releases.md)
|
|
9
|
-
(`WC-REL-<n>`), which wins over this skill. An agent prepares the release
|
|
10
|
-
|
|
9
|
+
(`WC-REL-<n>`), which wins over this skill. An agent prepares the release, merges the release pull request and runs
|
|
10
|
+
`bin/release`, which publishes from GitHub Actions. Only the first release is the owner's, from this computer.
|
|
11
11
|
|
|
12
12
|
1. **What changed.** In a worktree off `origin/main`: `git log --oneline "$(git describe --tags --abbrev=0 origin/main)..origin/main"`. The
|
|
13
13
|
`docs/commands.md` diff shows what a cli-core or cli-base release changed in the commands.
|
|
@@ -24,10 +24,13 @@ and runs `bin/release`.
|
|
|
24
24
|
before `1.0.0` this narrows `WC-REL-7`: a section with `Changed — may break callers` or `Removed` raises the minor
|
|
25
25
|
(`0.1.8` to `0.2.0`), and so does one with `Added`; only a section of `Fixed`, `Security` or docs raises the patch.
|
|
26
26
|
From `1.0.0`, a breaking change raises the major. `pnpm version:check` refuses a dated section whose number is
|
|
27
|
-
raised less than its headings need
|
|
27
|
+
raised less than its headings need, and when `bin/release` moves to the next free number on npm it raises that
|
|
28
|
+
number to the level the headings need. Set the next version in `package.json`, run `pnpm version:sync`, date the
|
|
28
29
|
section as `## <version> — DD.MM.YYYY`, run `pnpm release:check` again, and open the pull request
|
|
29
30
|
`chore(release): <version>`.
|
|
30
|
-
7. **The
|
|
31
|
-
|
|
31
|
+
7. **The publish.** Merge it when its checks pass, then run `bin/release` on a clean `main`: it starts
|
|
32
|
+
`release.yml` and follows it (`WC-REL-11`). A messenger CLI also needs the owner's signed release report
|
|
33
|
+
(`WC-REL-10`). Never `bin/release --local`: that is the owner's, for the first release before npm trusts
|
|
34
|
+
`release.yml`, with `NPM_TOKEN`, the npm token in the keyring, or the owner's own npm login.
|
|
32
35
|
The keyring is read only when `NPM_KEYRING_ACCOUNT` names the account the token is stored under (service `npm`);
|
|
33
36
|
there is no default account. When that lookup fails, `bin/release` stops; it does not fall back to the npm login.
|
|
@@ -9,6 +9,10 @@ You audit a WireCat CLI repository, in the current folder. Use only three source
|
|
|
9
9
|
`node_modules/@wirecat/cli-base/docs/standards/README.md`, the repository itself, and the cli-base workflow files its
|
|
10
10
|
own workflows pin (WC-CI-8). Do not read plans, journals or earlier reviews. Run `pnpm install` first if `node_modules` is missing.
|
|
11
11
|
|
|
12
|
+
Never use the owner's accounts. Do not sign in, and run the built CLI only with every config, state and cache folder
|
|
13
|
+
moved to a temporary folder and `DBUS_SESSION_BUS_ADDRESS=disabled:`, so it cannot reach the system keyring (removing
|
|
14
|
+
the variable is not enough). Run no command that writes to a service: no `--yes`, no `api` call that changes data.
|
|
15
|
+
|
|
12
16
|
Check the repository against every handbook page that applies to it: the code, the docs, the configs and the
|
|
13
17
|
workflows. You may run the package scripts. Change nothing.
|
|
14
18
|
|
package/docs/dev/test-matrix.md
CHANGED
|
@@ -7,7 +7,7 @@ ran it · ⛔ not tested offline, with the reason and where it is checked instea
|
|
|
7
7
|
Measured from the test run (`coverage/argv.jsonl`), not searched for — see
|
|
8
8
|
[TESTING.md](TESTING.md) for how, and for the states and failures that cut across commands.
|
|
9
9
|
|
|
10
|
-
**50 ✅ ·
|
|
10
|
+
**50 ✅ · 6 ⛔ · 0 ❌** — 19 commands, 37 options.
|
|
11
11
|
|
|
12
12
|
| Command | Option | | Note |
|
|
13
13
|
|---|---|---|---|
|
|
@@ -19,16 +19,15 @@ Measured from the test run (`coverage/argv.jsonl`), not searched for — see
|
|
|
19
19
|
| `skill install` | | ✅ | |
|
|
20
20
|
| `skill install` | `--for` | ✅ | |
|
|
21
21
|
| `skill status` | | ✅ | |
|
|
22
|
-
| `mcp` | | ✅ | |
|
|
23
|
-
| `mcp` | `--http` | ✅ | |
|
|
24
|
-
| `mcp` | `--port` | ✅ | |
|
|
25
|
-
| `mcp` | `--public-url` | ✅ | |
|
|
26
|
-
| `mcp
|
|
27
|
-
| `mcp server start` | | ⛔ | starts a detached `mcp --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/ |
|
|
28
|
-
| `mcp server start` | `--port` | ⛔ | starts a detached `mcp --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/ |
|
|
29
|
-
| `mcp server start` | `--public-url` | ⛔ | starts a detached `mcp --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/ |
|
|
30
|
-
| `mcp server
|
|
31
|
-
| `mcp server stop` | | ⛔ | starts a detached `mcp --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/ |
|
|
22
|
+
| `mcp serve` | | ✅ | |
|
|
23
|
+
| `mcp serve` | `--http` | ✅ | |
|
|
24
|
+
| `mcp serve` | `--port` | ✅ | |
|
|
25
|
+
| `mcp serve` | `--public-url` | ✅ | |
|
|
26
|
+
| `mcp revoke` | | ✅ | |
|
|
27
|
+
| `mcp server start` | | ⛔ | starts a detached `mcp serve --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/ |
|
|
28
|
+
| `mcp server start` | `--port` | ⛔ | starts a detached `mcp serve --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/ |
|
|
29
|
+
| `mcp server start` | `--public-url` | ⛔ | starts a detached `mcp serve --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/ |
|
|
30
|
+
| `mcp server stop` | | ⛔ | starts a detached `mcp serve --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/ |
|
|
32
31
|
| `mcp server status` | | ✅ | |
|
|
33
32
|
| `upgrade` | | ✅ | |
|
|
34
33
|
| `upgrade` | `--check` | ✅ | |
|
package/docs/security.md
CHANGED
|
@@ -6,6 +6,10 @@ What WireCat CLIs share — the keyring, permissions, MCP and what never reaches
|
|
|
6
6
|
`clidev` keeps its settings in `config.json` and its state in the folders `clidev doctor` shows. It sends nothing
|
|
7
7
|
to any service yet.
|
|
8
8
|
|
|
9
|
+
`clidev personal mcp serve --http` listens on `127.0.0.1` only, for browser apps that reach it through your own
|
|
10
|
+
tunnel. Each app logs in with a one-time code, and the server keeps a hash of each login it gives, in the state
|
|
11
|
+
folder, readable by you only. `clidev personal mcp revoke` forgets every one of them.
|
|
12
|
+
|
|
9
13
|
A run record keeps the command line as you typed it, so `clidev runs show` tells you what was run. A secret is still
|
|
10
14
|
stored as `[redacted]`: an option named like a token or password, a password in a link, a long opaque string. A
|
|
11
15
|
phone number is replaced by `[redacted]` inside the word you typed, message text included: one written with its "+" and
|
package/docs/usage.md
CHANGED
|
@@ -15,24 +15,25 @@ command without one stops with exit code 2 and lists the profiles on this comput
|
|
|
15
15
|
|
|
16
16
|
## Output for scripts
|
|
17
17
|
|
|
18
|
-
Every command except `mcp` on stdin and stdout takes `--json`, and prints one JSON value on stdout. Errors are JSON on stderr, with a code to branch
|
|
18
|
+
Every command except `mcp serve` on stdin and stdout takes `--json`, and prints one JSON value on stdout. Errors are JSON on stderr, with a code to branch
|
|
19
19
|
on. The [command reference](commands.md) lists the codes.
|
|
20
20
|
|
|
21
21
|
## Agents
|
|
22
22
|
|
|
23
23
|
`clidev skill install` puts the tool's skill where Claude Code, Codex, Gemini CLI and OpenClaw look for it.
|
|
24
24
|
`clidev skill install --for hermes` also adds that folder to Hermes Agent's own settings, so a bare `skill install`
|
|
25
|
-
leaves Hermes out. `clidev personal mcp` serves the profile's commands over MCP, on stdin and stdout:
|
|
25
|
+
leaves Hermes out. `clidev personal mcp serve` serves the profile's commands over MCP, on stdin and stdout:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
claude mcp add clidev -- clidev personal mcp
|
|
28
|
+
claude mcp add clidev -- clidev personal mcp serve
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
The server offers every command, the ones that write included. The profile's permissions decide what runs; see
|
|
32
32
|
[settings](configuration.md#permissions).
|
|
33
33
|
|
|
34
|
-
For ChatGPT and Claude in the browser,
|
|
35
|
-
`127.0.0.1` (port 8765, or
|
|
36
|
-
|
|
34
|
+
For ChatGPT and Claude in the browser,
|
|
35
|
+
`clidev personal mcp serve --http --public-url https://laptop.example.ts.net` serves on `127.0.0.1` (port 8765, or
|
|
36
|
+
`--port`) behind your own tunnel. A browser app logs in with a one-time code that the command prints;
|
|
37
|
+
`clidev personal mcp revoke` ends every login. `clidev personal mcp server start` runs the same
|
|
37
38
|
server in the background, `mcp server status` shows its port, the login code and the apps logged in, and
|
|
38
39
|
`mcp server stop` ends it.
|
package/kit/dist/new.js
CHANGED
|
@@ -5,11 +5,12 @@ import { createInterface } from "node:readline/promises";
|
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
import { ask, choiceList, INTERVIEW, labelOf, NAME_QUESTIONS, parseAnswer } from "./interview.js";
|
|
7
7
|
import { isRecord, MANIFEST, sha256 } from "./manifest.js";
|
|
8
|
-
import {
|
|
8
|
+
import { renderTemplate } from "./source.js";
|
|
9
9
|
const run = promisify(execFile);
|
|
10
10
|
const REFERENCES = {
|
|
11
11
|
messenger: ["whatsapp-cli", "tg-cli", "max-cli"],
|
|
12
12
|
files: ["drive-cli"],
|
|
13
|
+
calendar: ["gcal-cli"],
|
|
13
14
|
meetings: ["zoom-cli"],
|
|
14
15
|
notes: ["cli-memo"],
|
|
15
16
|
};
|
|
@@ -107,6 +108,18 @@ export const designDoc = (names, answers) => {
|
|
|
107
108
|
`Written by \`npx @wirecat/cli-template new\`. The agent reads it first, asks the further questions the reference CLI`,
|
|
108
109
|
"does not answer, and starts building only after the owner has read it.",
|
|
109
110
|
"",
|
|
111
|
+
"## First task",
|
|
112
|
+
"",
|
|
113
|
+
"Delete this section once every step is done (WC-DOC-3).",
|
|
114
|
+
"",
|
|
115
|
+
"1. If an answer below is missing, ask the owner the [design interview](skills/design-interview/SKILL.md).",
|
|
116
|
+
"2. Learn from the reference CLI below: its provider, its store and its commands.",
|
|
117
|
+
"3. Ask the further questions in the interview that the reference CLI does not answer, and write the answers here.",
|
|
118
|
+
" Build only after the owner has read this page.",
|
|
119
|
+
"4. When the first command reaches the service, rewrite `README.md`, `docs/index.md` and the description in",
|
|
120
|
+
` \`skills/${names.name}/SKILL.md\` to say what the tool does and who it is for. Rewrite the MCP server's`,
|
|
121
|
+
" instructions in `src/mcp.ts` too: name the kinds of outside text the CLI carries (WC-MCP-11).",
|
|
122
|
+
"",
|
|
110
123
|
"## Answers",
|
|
111
124
|
"",
|
|
112
125
|
...rows,
|
|
@@ -137,6 +150,7 @@ const removeKit = (command) => command
|
|
|
137
150
|
.split(" && ")
|
|
138
151
|
.filter((part) => !/\bkit[:/]/.test(part))
|
|
139
152
|
.join(" && ");
|
|
153
|
+
const CLI_FILES = ["dist", "skills", "/README.md", "CHANGELOG.md", "LICENSE"];
|
|
140
154
|
export const cliPackageJson = (text, names) => {
|
|
141
155
|
const pkg = JSON.parse(text);
|
|
142
156
|
if (!isRecord(pkg))
|
|
@@ -149,8 +163,9 @@ export const cliPackageJson = (text, names) => {
|
|
|
149
163
|
...Object.fromEntries(Object.entries(bin).filter(([, value]) => !isKit(value))),
|
|
150
164
|
[names.command]: `dist/bin/${names.command}.js`,
|
|
151
165
|
};
|
|
166
|
+
// The template's own list ships the skeleton's sources for `new` to copy; a CLI ships what runs (WC-REL-16).
|
|
152
167
|
if (Array.isArray(pkg.files))
|
|
153
|
-
next.files =
|
|
168
|
+
next.files = CLI_FILES;
|
|
154
169
|
if (isRecord(pkg.scripts)) {
|
|
155
170
|
next.scripts = Object.fromEntries(Object.entries(pkg.scripts)
|
|
156
171
|
.filter((entry) => typeof entry[1] === "string" && !entry[0].startsWith("kit:"))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-template",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Start a new WireCat CLI: a working skeleton on cli-base and cli-core, copied by new and kept current by upgrade",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"gitignore"
|
|
51
51
|
],
|
|
52
52
|
"scripts": {
|
|
53
|
-
"build": "tsc --build --force && pnpm kit:build",
|
|
53
|
+
"build": "tsc --build --force && cli-dev bundle --bin dist/bin/clidev.js && pnpm kit:build",
|
|
54
54
|
"typecheck": "tsc --build --force --emitDeclarationOnly && tsc -p tsconfig.test.json && tsc -p tsconfig.scripts.json && pnpm kit:typecheck",
|
|
55
55
|
"lint": "biome check .",
|
|
56
56
|
"lint:fix": "biome check --write .",
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
"generate": "pnpm build && node --experimental-strip-types scripts/commands.ts",
|
|
62
62
|
"generate:check": "pnpm build && node --experimental-strip-types scripts/commands.ts --check",
|
|
63
63
|
"smoke": "node --experimental-strip-types scripts/smoke.ts",
|
|
64
|
-
"smoke:bun": "bun scripts/smoke.ts",
|
|
64
|
+
"smoke:bun": "bun scripts/smoke.ts && bun scripts/smoke-bundle.ts",
|
|
65
|
+
"smoke:node": "node --experimental-strip-types scripts/smoke-bundle.ts",
|
|
65
66
|
"package:check": "pnpm smoke",
|
|
66
67
|
"docs:check": "cli-dev docs-check --rules scripts/docs-rules.ts --pages && pnpm docs:spell && rumdl check --config config/wirecat/rumdl.toml README.md SECURITY.md CHANGELOG.md CLAUDE.md AGENTS.md docs/*.md docs/dev/*.md && pnpm docs:lint && pnpm kit:docs",
|
|
67
68
|
"docs:lint": "markdownlint-cli2",
|
|
@@ -84,8 +85,8 @@
|
|
|
84
85
|
},
|
|
85
86
|
"dependencies": {
|
|
86
87
|
"@modelcontextprotocol/server": "2.4.0",
|
|
87
|
-
"@wirecat/cli-base": "0.
|
|
88
|
-
"@wirecat/cli-core": "0.
|
|
88
|
+
"@wirecat/cli-base": "0.65.0",
|
|
89
|
+
"@wirecat/cli-core": "0.469.0",
|
|
89
90
|
"commander": "15.0.0"
|
|
90
91
|
},
|
|
91
92
|
"devDependencies": {
|
|
@@ -98,6 +99,7 @@
|
|
|
98
99
|
"@vitest/coverage-v8": "5.0.5",
|
|
99
100
|
"@wirecat/cli-testing": "0.5.3",
|
|
100
101
|
"cspell": "10.4.0",
|
|
102
|
+
"esbuild": "0.28.2",
|
|
101
103
|
"lefthook": "2.2.2",
|
|
102
104
|
"markdownlint-cli2": "0.24.0",
|
|
103
105
|
"rumdl": "0.3.0",
|
package/scripts/audit.sh
CHANGED
|
@@ -11,14 +11,16 @@ trap 'rm -rf "$scratch"' EXIT
|
|
|
11
11
|
export HOME="$scratch/home" MESSAGING_STORE="$scratch/store" WIRECAT_STATE_DIR="$scratch/state"
|
|
12
12
|
mkdir -p "$HOME"
|
|
13
13
|
npm install -g --prefix "$scratch/prefix" --ignore-scripts --no-audit --no-fund --loglevel error \
|
|
14
|
-
@wirecat/tg-cli @wirecat/max-cli @wirecat/whatsapp-cli @wirecat/cli-memo @wirecat/drive-cli @wirecat/zoom-cli
|
|
14
|
+
@wirecat/tg-cli @wirecat/max-cli @wirecat/whatsapp-cli @wirecat/cli-memo @wirecat/drive-cli @wirecat/zoom-cli \
|
|
15
|
+
@wirecat/gcal-cli
|
|
15
16
|
bin="$scratch/prefix/bin"
|
|
16
17
|
others=()
|
|
17
18
|
# A groups file replaces cli-base's PARITY_GROUPS. The template keeps one because it is in no group of cli-base's.
|
|
18
19
|
[ -f "$root/config/parity-groups.json" ] && others+=(--groups "$root/config/parity-groups.json")
|
|
19
|
-
for cli in tg max wa memo drive zm; do
|
|
20
|
+
for cli in tg max wa memo drive zm gcal; do
|
|
20
21
|
[ "$cli" = "$command" ] || others+=(--cli "$bin/$cli")
|
|
21
22
|
done
|
|
22
23
|
"$root/node_modules/.bin/cli-dev" audit --cli "$root/dist/bin/$command.js" "${others[@]}" --for "$command" \
|
|
23
24
|
--lint "$command=$root/lint" >"$scratch/scorecard.txt" || { cat "$scratch/scorecard.txt"; exit 1; }
|
|
24
|
-
|
|
25
|
+
awk -v cli="$command" 'NR == 1 || $1 == cli' "$scratch/scorecard.txt"
|
|
26
|
+
grep -q "^$command *total " "$scratch/scorecard.txt" || { echo "audit: no scorecard row for $command" >&2; exit 1; }
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The published bin is a loader for `dist/bundle/` (cli-base's `cli-dev bundle`, WC-LAY-10). This runs it, with
|
|
3
|
+
* directories of its own, under Node (`pnpm smoke:node`) and Bun (`pnpm smoke:bun`): the vitest suite runs against
|
|
4
|
+
* `src/`, so this is the proof the bundle a user installs works.
|
|
5
|
+
*
|
|
6
|
+
* pnpm build && pnpm smoke:node
|
|
7
|
+
*/
|
|
8
|
+
import { spawnSync } from "node:child_process"
|
|
9
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"
|
|
10
|
+
import { tmpdir } from "node:os"
|
|
11
|
+
import { join } from "node:path"
|
|
12
|
+
|
|
13
|
+
const COMMAND = "clidev"
|
|
14
|
+
const PREFIX = "CLIDEV"
|
|
15
|
+
|
|
16
|
+
const runtime = typeof (globalThis as { Bun?: unknown }).Bun === "undefined" ? "node" : "bun"
|
|
17
|
+
const failures: string[] = []
|
|
18
|
+
const check = (what: string, condition: boolean) => {
|
|
19
|
+
if (!condition) failures.push(what)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Overridden directories also move the keyring entry, so nothing here reads the owner's accounts or store.
|
|
23
|
+
const isolated = mkdtempSync(join(tmpdir(), `${COMMAND}-bundle-smoke-`))
|
|
24
|
+
const run = (args: string[], env: Record<string, string> = {}) =>
|
|
25
|
+
spawnSync(process.execPath, [`dist/bin/${COMMAND}.js`, ...args], {
|
|
26
|
+
encoding: "utf8",
|
|
27
|
+
env: {
|
|
28
|
+
...process.env,
|
|
29
|
+
[`${PREFIX}_CONFIG_DIR`]: join(isolated, "config"),
|
|
30
|
+
[`${PREFIX}_STATE_DIR`]: join(isolated, "state"),
|
|
31
|
+
[`${PREFIX}_CACHE_DIR`]: join(isolated, "cache"),
|
|
32
|
+
[`${PREFIX}_NO_UPDATE_CHECK`]: "1",
|
|
33
|
+
MESSAGING_STORE: join(isolated, "store.db"),
|
|
34
|
+
...env,
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const loader = readFileSync(`dist/bin/${COMMAND}.js`, "utf8")
|
|
39
|
+
check("the bin starts the bundle", loader.includes(`import("../bundle/${COMMAND}.js")`))
|
|
40
|
+
const preloaded = [...loader.matchAll(/"\.\.\/bundle\/([^"]+)"/g)].map(([, chunk]) => chunk ?? "")
|
|
41
|
+
check(
|
|
42
|
+
"a server preloads only chunks the bundle has",
|
|
43
|
+
preloaded.length > 1 && preloaded.every((chunk) => existsSync(join("dist/bundle", chunk))),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const version = run(["--version"])
|
|
47
|
+
check("--version prints a version and nothing else", /^\d+\.\d+\.\d+\n$/.test(version.stdout) && version.stderr === "")
|
|
48
|
+
const recorded = run(["--version"], { [`${PREFIX}_TEST_ARGV_LOG`]: join(isolated, "argv.jsonl") })
|
|
49
|
+
check("the loader's --version is the bundle's", recorded.stdout === version.stdout)
|
|
50
|
+
check("--help names the command", run(["--help"]).stdout.includes(`Usage: ${COMMAND}`))
|
|
51
|
+
check("skill show prints the SKILL.md the package ships", run(["skill", "show"]).stdout.startsWith("---"))
|
|
52
|
+
check(
|
|
53
|
+
"doctor finds the document readers",
|
|
54
|
+
run(["doctor", "--json"]).stdout.includes('"files":{"checked":true,"ok":true'),
|
|
55
|
+
)
|
|
56
|
+
rmSync(isolated, { recursive: true, force: true })
|
|
57
|
+
|
|
58
|
+
if (failures.length > 0) {
|
|
59
|
+
console.error(`${COMMAND} bundle smoke FAILED under ${runtime}:`)
|
|
60
|
+
for (const failure of failures) console.error(` - ${failure}`)
|
|
61
|
+
process.exit(1)
|
|
62
|
+
}
|
|
63
|
+
console.log(`${COMMAND} bundle smoke passed under ${runtime}`)
|
|
@@ -12,29 +12,23 @@ export const UNTESTED: { command: string; option?: string; reason: string }[] =
|
|
|
12
12
|
{
|
|
13
13
|
command: "mcp server start",
|
|
14
14
|
reason:
|
|
15
|
-
"starts a detached `mcp --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/",
|
|
15
|
+
"starts a detached `mcp serve --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/",
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
command: "mcp server start",
|
|
19
19
|
option: "--port",
|
|
20
20
|
reason:
|
|
21
|
-
"starts a detached `mcp --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/",
|
|
21
|
+
"starts a detached `mcp serve --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/",
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
command: "mcp server start",
|
|
25
25
|
option: "--public-url",
|
|
26
26
|
reason:
|
|
27
|
-
"starts a detached `mcp --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/",
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
command: "mcp server start",
|
|
31
|
-
option: "--permission",
|
|
32
|
-
reason:
|
|
33
|
-
"cli-core adds it and passes it to `mcp --http`, which no longer takes it, so the started server refuses it; checked by hand against dist/",
|
|
27
|
+
"starts a detached `mcp serve --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/",
|
|
34
28
|
},
|
|
35
29
|
{
|
|
36
30
|
command: "mcp server stop",
|
|
37
31
|
reason:
|
|
38
|
-
"starts a detached `mcp --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/",
|
|
32
|
+
"starts a detached `mcp serve --http` from the built entry; cli-core tests mcpServerCommand, and the background run was checked by hand against dist/",
|
|
39
33
|
},
|
|
40
34
|
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, it } from "vitest"
|
|
2
|
-
import { versionLevelProblem } from "./version-level.js"
|
|
2
|
+
import { nextVersion, versionLevelProblem } from "./version-level.js"
|
|
3
3
|
|
|
4
4
|
const changelog = (version: string, headings: string[], previous = "0.1.8") =>
|
|
5
5
|
[`## ${version} — 26.10.2026`, ...headings.flatMap((h) => [`### ${h}`, "- x"]), `## ${previous} — 25.10.2026`].join(
|
|
@@ -28,3 +28,19 @@ it("refuses a patch release that adds something", () => {
|
|
|
28
28
|
it("checks nothing while the top section is Unreleased", () => {
|
|
29
29
|
expect(versionLevelProblem("## Unreleased\n\n### Removed\n\n- x\n\n## 0.1.8 — 25.10.2026\n")).toBeUndefined()
|
|
30
30
|
})
|
|
31
|
+
|
|
32
|
+
const unreleased = (headings: string[], previous = "0.1.1") =>
|
|
33
|
+
["## Unreleased", ...headings.flatMap((h) => [`### ${h}`, "- x"]), `## ${previous} — 29.10.2026`].join("\n")
|
|
34
|
+
|
|
35
|
+
it.each([
|
|
36
|
+
[["Fixed"], "0.1.2", "0.1.2"],
|
|
37
|
+
[["Added"], "0.1.2", "0.2.0"],
|
|
38
|
+
[["Removed"], "0.1.2", "0.2.0"],
|
|
39
|
+
[["Added"], "0.3.1", "0.3.1"],
|
|
40
|
+
])("raises the next free number for %j from %s to %s", (headings, next, expected) => {
|
|
41
|
+
expect(nextVersion(unreleased(headings), next)).toBe(expected)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it("raises a 1.x release that may break callers to the next major", () => {
|
|
45
|
+
expect(nextVersion(unreleased(["Changed — may break callers"], "1.4.2"), "1.4.3")).toBe("2.0.0")
|
|
46
|
+
})
|
package/scripts/version-level.ts
CHANGED
|
@@ -21,13 +21,35 @@ const needed = (version: string, section: string): Level => {
|
|
|
21
21
|
return /^### Added$/m.test(section) ? "minor" : "patch"
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
const raise = (from: string, level: Level) => {
|
|
25
|
+
const [a = 0, b = 0, c = 0] = parse(from)
|
|
26
|
+
if (level === "major") return `${a + 1}.0.0`
|
|
27
|
+
return level === "minor" ? `${a}.${b + 1}.0` : `${a}.${b}.${c + 1}`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const later = (x: string, y: string) => {
|
|
31
|
+
const [a, b] = [parse(x), parse(y)]
|
|
32
|
+
const i = a.findIndex((n, k) => n !== b[k])
|
|
33
|
+
return i === -1 || (a[i] ?? 0) > (b[i] ?? 0) ? x : y
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const sections = (changelog: string) => {
|
|
37
|
+
const [top, below] = [...changelog.matchAll(/^## (\S+).*$/gm)]
|
|
38
|
+
if (!top || !below?.[1]) return undefined
|
|
39
|
+
return { version: top[1], previous: below[1], section: changelog.slice(top.index, below.index) }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The next free number on npm is a patch; the top section may need a minor or major over the version below it.
|
|
43
|
+
export const nextVersion = (changelog: string, next: string): string => {
|
|
44
|
+
const parts = sections(changelog)
|
|
45
|
+
if (!parts) return next
|
|
46
|
+
return later(next, raise(parts.previous, needed(next, parts.section)))
|
|
47
|
+
}
|
|
48
|
+
|
|
24
49
|
export const versionLevelProblem = (changelog: string): string | undefined => {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const version =
|
|
28
|
-
const previous = below?.[1]
|
|
29
|
-
if (!top || !below || !version || !previous || !/^\d+\.\d+\.\d+$/.test(version)) return undefined
|
|
30
|
-
const section = changelog.slice(top.index, below.index)
|
|
50
|
+
const parts = sections(changelog)
|
|
51
|
+
if (!parts?.version || !/^\d+\.\d+\.\d+$/.test(parts.version)) return undefined
|
|
52
|
+
const { version, previous, section } = parts
|
|
31
53
|
const want = needed(version, section)
|
|
32
54
|
const got = raised(previous, version)
|
|
33
55
|
if (LEVELS.indexOf(got) >= LEVELS.indexOf(want)) return undefined
|
|
@@ -35,7 +57,13 @@ export const versionLevelProblem = (changelog: string): string | undefined => {
|
|
|
35
57
|
}
|
|
36
58
|
|
|
37
59
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
38
|
-
const
|
|
60
|
+
const changelog = readFileSync(resolve(import.meta.dirname, "../CHANGELOG.md"), "utf8")
|
|
61
|
+
const [flag, next] = process.argv.slice(2)
|
|
62
|
+
if (flag === "--next" && next) {
|
|
63
|
+
console.log(nextVersion(changelog, next))
|
|
64
|
+
process.exit(0)
|
|
65
|
+
}
|
|
66
|
+
const problem = versionLevelProblem(changelog)
|
|
39
67
|
if (problem) {
|
|
40
68
|
console.error(`${problem} (docs/dev/skills/release/SKILL.md, step 6)`)
|
|
41
69
|
process.exit(1)
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: clidev-cli
|
|
3
3
|
description: Use clidev, a WireCat CLI started from cli-template, from an agent — find a command, run it with the owner's profile, read JSON answers. Use when a task needs clidev. It does not connect to a service yet.
|
|
4
|
+
metadata:
|
|
5
|
+
openclaw:
|
|
6
|
+
requires:
|
|
7
|
+
bins: [clidev]
|
|
8
|
+
install:
|
|
9
|
+
- kind: node
|
|
10
|
+
package: "@wirecat/clidev-cli"
|
|
11
|
+
bins: [clidev]
|
|
4
12
|
---
|
|
5
13
|
|
|
6
14
|
# clidev
|
package/src/commands/mcp.ts
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
type GlobalFlags,
|
|
7
7
|
httpTokenFile,
|
|
8
8
|
itemOf,
|
|
9
|
+
LOCAL_READ,
|
|
10
|
+
LOCAL_WRITE,
|
|
9
11
|
MCP_HTTP_DEFAULT_PORT,
|
|
10
12
|
mcpServerCommand,
|
|
11
13
|
outputFor,
|
|
@@ -14,7 +16,6 @@ import {
|
|
|
14
16
|
renderAnswer,
|
|
15
17
|
rootOf,
|
|
16
18
|
} from "@wirecat/cli-core/cli"
|
|
17
|
-
import { portOf, publicUrlOf } from "@wirecat/cli-core/mcp"
|
|
18
19
|
import { Command } from "commander"
|
|
19
20
|
import { configuration } from "../config.js"
|
|
20
21
|
import { IDENTITY } from "../identity.js"
|
|
@@ -23,9 +24,10 @@ interface McpFlags extends GlobalFlags {
|
|
|
23
24
|
http?: boolean
|
|
24
25
|
port?: string
|
|
25
26
|
publicUrl?: string
|
|
26
|
-
revoke?: boolean
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
const SETUP = `claude mcp add ${IDENTITY.command} -- ${IDENTITY.command} personal mcp serve`
|
|
30
|
+
|
|
29
31
|
const settingsOf = (command: Command) => {
|
|
30
32
|
const flags = command.optsWithGlobals<McpFlags>()
|
|
31
33
|
const env = environmentOf(command).env ?? process.env
|
|
@@ -33,16 +35,19 @@ const settingsOf = (command: Command) => {
|
|
|
33
35
|
return { flags, env, profile, tokenFile: httpTokenFile(IDENTITY, profile, env) }
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
annotate(new Command("
|
|
38
|
+
const serveCommand = (): Command =>
|
|
39
|
+
annotate(new Command("serve"), {
|
|
38
40
|
effects: REMOTE_WRITE,
|
|
39
41
|
lifetime: "persistent",
|
|
40
42
|
mcp: false,
|
|
41
|
-
examples: [
|
|
43
|
+
examples: [
|
|
44
|
+
`${IDENTITY.command} personal mcp serve`,
|
|
45
|
+
`${IDENTITY.command} personal mcp serve --http --public-url https://laptop.example.ts.net`,
|
|
46
|
+
],
|
|
42
47
|
})
|
|
43
48
|
.summary("serve this profile's commands to an AI agent")
|
|
44
49
|
.description(
|
|
45
|
-
`serve this profile's commands to an agent over MCP, on stdin and stdout, writes included; the profile's permissions decide —
|
|
50
|
+
`serve this profile's commands to an agent over MCP, on stdin and stdout, writes included; the profile's permissions decide — \`${SETUP}\``,
|
|
46
51
|
)
|
|
47
52
|
.option(
|
|
48
53
|
"--http",
|
|
@@ -50,39 +55,13 @@ export const mcpCommand = (): Command =>
|
|
|
50
55
|
)
|
|
51
56
|
.option("--port <port>", `the local port for --http (default ${MCP_HTTP_DEFAULT_PORT})`)
|
|
52
57
|
.option("--public-url <url>", "the tunnel's https address the browser apps use, e.g. https://<name>.ts.net")
|
|
53
|
-
.option("--revoke", "forget every login given to a browser app; each must log in again")
|
|
54
|
-
.addCommand(
|
|
55
|
-
mcpServerCommand({
|
|
56
|
-
example: `${IDENTITY.command} personal`,
|
|
57
|
-
target: (self) => {
|
|
58
|
-
const { env, profile, tokenFile } = settingsOf(self)
|
|
59
|
-
return {
|
|
60
|
-
invocation: `${IDENTITY.command} ${profile}`,
|
|
61
|
-
profile,
|
|
62
|
-
envPrefix: IDENTITY.envPrefix,
|
|
63
|
-
env,
|
|
64
|
-
tokenFile,
|
|
65
|
-
argv: [profile, "mcp", "--http"],
|
|
66
|
-
version: IDENTITY.version,
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
answer: (self, item, lines) => renderAnswer(self, outputFor(self), itemOf(item), () => lines.join("\n")),
|
|
70
|
-
}),
|
|
71
|
-
)
|
|
72
58
|
.action(async function (this: Command) {
|
|
73
59
|
const { flags, env, profile, tokenFile } = settingsOf(this)
|
|
74
|
-
if (flags.revoke) {
|
|
75
|
-
refuseUnderReadOnly(this, "mcp --revoke")
|
|
76
|
-
const { revokeAll } = await import("@wirecat/cli-core/mcp")
|
|
77
|
-
revokeAll(tokenFile)
|
|
78
|
-
renderAnswer(this, outputFor(this), itemOf({ revoked: true, profile }))
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
60
|
if (!flags.http && (flags.json || flags.jsonl))
|
|
82
61
|
throw new CliError("validation_error", "MCP over stdio controls its own output; leave out --json and --jsonl")
|
|
83
62
|
const environment = environmentOf(this)
|
|
84
63
|
const run = environment.runCommand
|
|
85
|
-
if (!run) throw new Error("mcp runs inside the program's run(), which provides runCommand")
|
|
64
|
+
if (!run) throw new Error("mcp serve runs inside the program's run(), which provides runCommand")
|
|
86
65
|
const program = rootOf(this)
|
|
87
66
|
const options = {
|
|
88
67
|
profile,
|
|
@@ -95,6 +74,7 @@ export const mcpCommand = (): Command =>
|
|
|
95
74
|
// Loaded here, not at the top: every other command would otherwise pay for the MCP SDK.
|
|
96
75
|
const { serveMcp, serveMcpOverHttp } = await import("../mcp.js")
|
|
97
76
|
if (!flags.http) return serveMcp(options)
|
|
77
|
+
const { portOf, publicUrlOf } = await import("@wirecat/cli-core/mcp")
|
|
98
78
|
const { renderer } = outputFor(this)
|
|
99
79
|
await serveMcpOverHttp(options, {
|
|
100
80
|
publicUrl: publicUrlOf(flags.publicUrl, {
|
|
@@ -107,3 +87,44 @@ export const mcpCommand = (): Command =>
|
|
|
107
87
|
note: (line) => renderer.note(line),
|
|
108
88
|
})
|
|
109
89
|
})
|
|
90
|
+
|
|
91
|
+
const revokeCommand = (): Command =>
|
|
92
|
+
annotate(new Command("revoke"), {
|
|
93
|
+
effects: LOCAL_WRITE,
|
|
94
|
+
mcp: false,
|
|
95
|
+
examples: [`${IDENTITY.command} personal mcp revoke`],
|
|
96
|
+
})
|
|
97
|
+
.summary("forget every login given to a browser app")
|
|
98
|
+
.description("forget every login `mcp serve --http` gave a browser app; each must log in again")
|
|
99
|
+
.action(async function (this: Command) {
|
|
100
|
+
refuseUnderReadOnly(this, "mcp revoke")
|
|
101
|
+
const { profile, tokenFile } = settingsOf(this)
|
|
102
|
+
const { revokeAll } = await import("@wirecat/cli-core/mcp")
|
|
103
|
+
revokeAll(tokenFile)
|
|
104
|
+
renderAnswer(this, outputFor(this), itemOf({ revoked: true, profile }))
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
export const mcpCommand = (): Command =>
|
|
108
|
+
annotate(new Command("mcp"), { effects: LOCAL_READ, mcp: false })
|
|
109
|
+
.summary("serve this profile's commands to an AI agent")
|
|
110
|
+
.description(`serve this profile's commands to an agent over MCP — \`${SETUP}\``)
|
|
111
|
+
.addCommand(serveCommand())
|
|
112
|
+
.addCommand(revokeCommand())
|
|
113
|
+
.addCommand(
|
|
114
|
+
mcpServerCommand({
|
|
115
|
+
example: `${IDENTITY.command} personal`,
|
|
116
|
+
target: (self) => {
|
|
117
|
+
const { env, profile, tokenFile } = settingsOf(self)
|
|
118
|
+
return {
|
|
119
|
+
invocation: `${IDENTITY.command} ${profile}`,
|
|
120
|
+
profile,
|
|
121
|
+
envPrefix: IDENTITY.envPrefix,
|
|
122
|
+
env,
|
|
123
|
+
tokenFile,
|
|
124
|
+
argv: [profile, "mcp", "serve", "--http"],
|
|
125
|
+
version: IDENTITY.version,
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
answer: (self, item, lines) => renderAnswer(self, outputFor(self), itemOf(item), () => lines.join("\n")),
|
|
129
|
+
}),
|
|
130
|
+
)
|