@wirecat/cli-template 0.1.4 → 0.1.5
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/monthly.yml +1 -1
- package/CHANGELOG.md +54 -0
- package/README.md +29 -2
- package/bin/release +56 -3
- package/biome.json +23 -1
- package/contracts/plan.json +1 -1
- package/cspell.json +3 -7
- package/dist/commands/doctor.d.ts +25 -4
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +22 -15
- package/dist/commands/doctor.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +5 -1
- package/dist/mcp.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/commands.md +4 -4
- package/docs/dev/reference-clis.md +6 -2
- package/docs/troubleshooting.md +1 -1
- package/kit/dist/new.js +1 -1
- package/package.json +7 -7
- package/scripts/docs-rules.ts +15 -0
- package/scripts/smoke.ts +12 -2
- package/src/commands/doctor.ts +21 -14
- package/src/mcp.test.ts +0 -1
- package/src/mcp.ts +6 -1
- package/src/program.test.ts +26 -5
- package/src/version.ts +1 -1
- package/vitest.config.ts +8 -2
- package/kit/README.md +0 -136
- package/kit/tsconfig.json +0 -17
- package/kit/tsconfig.test.json +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,60 @@
|
|
|
3
3
|
Notable changes to `@wirecat/cli-template`. One section per version, newest first; versions follow
|
|
4
4
|
[semantic versioning](https://semver.org), so before `1.0.0` the skeleton may still change.
|
|
5
5
|
|
|
6
|
+
## 0.1.5 — 25.10.2026
|
|
7
|
+
|
|
8
|
+
### Changed — may break callers
|
|
9
|
+
|
|
10
|
+
- **The skeleton runs on cli-core 0.424.0.** When no profile exists on the computer, the error shows the command
|
|
11
|
+
that was typed with a profile added, such as `clidev personal mcp`. It used to suggest a `setup` command, which the
|
|
12
|
+
skeleton does not have. A run made without a profile is now recorded with no profile instead of as `default`, so
|
|
13
|
+
`runs list` and `runs show` leave `profile` out for such a run.
|
|
14
|
+
- **`doctor --json` reports the config file's problems as `config: { path, found, issues }`, the shape the shared
|
|
15
|
+
messenger doctor has (cli-core 0.423.0).** `config.exists` and `config.error` are gone. Each issue has a `kind`
|
|
16
|
+
(`invalid_json`, `unknown_setting`, `invalid_value`, …), the `file`, the `key`, `line` and `column` for invalid JSON,
|
|
17
|
+
a sentence that says what the value has to be, and `fix`, the `config unset` command that removes it. A profile lock
|
|
18
|
+
or an environment variable the settings refuse is reported in `settings.error`. A CLI started from an older template
|
|
19
|
+
copies `src/commands/doctor.ts`.
|
|
20
|
+
- **The skeleton runs on cli-core 0.423.0.** `config set` and `unset` no longer list `default_profile`,
|
|
21
|
+
`analysis_provider`, `analysis_model` or `analysis_base_url`; analysis is set with `models.analysis.*`. A file that
|
|
22
|
+
still has one of them is refused with the `config unset` that removes it.
|
|
23
|
+
- **`pnpm docs:check` checks the user pages for internal ids, correction marks and struck-out text.** It now loads
|
|
24
|
+
`scripts/docs-rules.ts`, which names `README.md`, `SECURITY.md` and `docs/*.md` as user pages. A page that carries
|
|
25
|
+
a journal id fails the check until the id is removed.
|
|
26
|
+
- **The spell check covers `SECURITY.md` and imports only the shared word list.** `cspell.json` no longer imports the
|
|
27
|
+
British English and Russian dictionaries again, and skips the generated `docs/commands.md` as tg and max do.
|
|
28
|
+
- **The coverage floor holds for each file too: every file in `src/` keeps at least half of its lines covered.**
|
|
29
|
+
Each excluded file now says why it is excluded.
|
|
30
|
+
- **Only `src/mcp.ts` may import the MCP SDK.** `biome.json` refuses `@modelcontextprotocol/*` anywhere else in
|
|
31
|
+
`src/`, so commands and services do not learn the protocol.
|
|
32
|
+
- **`bin/release` takes the next free version when npm already has the one in `package.json`, or a higher one.** It
|
|
33
|
+
used to stop and ask for a new pull request. Now it raises the number (after `x.y.0` the next minor, else the next
|
|
34
|
+
patch), lands that through its own pull request once the checks pass, and publishes it, as tg, max and memo do. A
|
|
35
|
+
pre-release version is still chosen by hand. A CLI made from the template gets this with `upgrade`.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **`<profile> mcp` stops when the stop comes before its server is up.** A stop that arrived while the server was
|
|
40
|
+
still loading was lost, and the command served until it was killed. A command result that is not an object, such
|
|
41
|
+
as a list, now reaches the agent as `{ "result": … }`, because MCP accepts only an object as a tool's structured
|
|
42
|
+
result. A CLI made from an older template takes both from `src/mcp.ts`.
|
|
43
|
+
- **The missing-profile contract tests the missing profile.** It ran `mcp --json`, which is refused for `--json`
|
|
44
|
+
before the profile is read. It now runs `mcp`. A CLI made from an older template changes the case's `args` in
|
|
45
|
+
`contracts/plan.json` the same way.
|
|
46
|
+
- **The package no longer ships `kit/README.md` and the kit's `tsconfig` files.** npm reads a name in `files` with no
|
|
47
|
+
leading slash as a match in every folder; the three root files are now anchored with `/`. `pnpm package:check`
|
|
48
|
+
fails when `npm pack` would ship a path that `files` does not name.
|
|
49
|
+
- **The monthly run starts at 03:02 UTC, inside the window the handbook gives.** The last step `new` prints asks for
|
|
50
|
+
a free minute in that window; it spoke of a day and of a placeholder CI rejects, and no check rejects one.
|
|
51
|
+
- **The 0.1.4 notes cite a handbook rule and a lint rule that no published cli-base has.** The run-log rule and
|
|
52
|
+
cli-base's `missing-run-log` lint rule are only on cli-base's main branch. They are in no release up to 0.55.1, so
|
|
53
|
+
nothing reports a CLI without `runs` until a cli-base release ships them. The 0.1.4 section stays as it was released.
|
|
54
|
+
- **The README introduces the skeleton in full:** profiles, settings and permissions, limits, security, and what to do
|
|
55
|
+
when it does not work.
|
|
56
|
+
- **The Reference CLIs page says how a new CLI's records join search.** A CLI with a provider of its own registers it
|
|
57
|
+
with cli-core's `registerProvider`; until it does, search lists its accounts as skipped. The page said that cli-core
|
|
58
|
+
had no provider registry, and that search would read such records as messages.
|
|
59
|
+
|
|
6
60
|
## 0.1.4 — 25.10.2026
|
|
7
61
|
|
|
8
62
|
### Added
|
package/README.md
CHANGED
|
@@ -42,6 +42,32 @@ none is ever assumed. `CLIDEV_PROFILE=personal` does the same for a whole shell
|
|
|
42
42
|
`clidev skill show` prints the rules an agent follows with this tool. `clidev personal mcp` serves every command to an
|
|
43
43
|
agent, writes included: the profile's permissions decide what runs. See [MCP and permissions](docs/usage.md#agents).
|
|
44
44
|
|
|
45
|
+
## Profiles
|
|
46
|
+
|
|
47
|
+
A profile is a name you choose for one account, such as `personal` or `work`. It is the first word of every command
|
|
48
|
+
that uses an account. No profile is ever assumed: a command without one stops and lists the profiles on this
|
|
49
|
+
computer. The skeleton has no sign-in yet; the CLI built from it adds one.
|
|
50
|
+
|
|
51
|
+
## Settings and permissions
|
|
52
|
+
|
|
53
|
+
`clidev personal config show` lists each setting and where it came from. A profile's `permissions` say what a command
|
|
54
|
+
may do: `allow`, `ask`, `readonly` or `deny`. `CLIDEV_READ_ONLY=1` refuses every write. See
|
|
55
|
+
[Settings](docs/configuration.md).
|
|
56
|
+
|
|
57
|
+
## Limits
|
|
58
|
+
|
|
59
|
+
The skeleton connects to no service, keeps no messages and has no commands of its own beyond the shared ones above.
|
|
60
|
+
|
|
61
|
+
## Security
|
|
62
|
+
|
|
63
|
+
`clidev` keeps its settings and state on this computer and sends nothing to any service yet. See
|
|
64
|
+
[Security](docs/security.md), and report a vulnerability privately as [SECURITY.md](SECURITY.md) says.
|
|
65
|
+
|
|
66
|
+
## When it does not work
|
|
67
|
+
|
|
68
|
+
Run `clidev personal doctor` first: it checks the installation without the network.
|
|
69
|
+
[When something goes wrong](docs/troubleshooting.md) lists the usual symptoms.
|
|
70
|
+
|
|
45
71
|
## Documentation
|
|
46
72
|
|
|
47
73
|
- [Install](docs/installation.md)
|
|
@@ -53,5 +79,6 @@ agent, writes included: the profile's permissions decide what runs. See [MCP and
|
|
|
53
79
|
|
|
54
80
|
## Development
|
|
55
81
|
|
|
56
|
-
The working rules for people and agents are in [AGENTS.md](AGENTS.md). `pnpm release:check` runs
|
|
57
|
-
|
|
82
|
+
The working rules for people and agents are in [AGENTS.md](AGENTS.md). `pnpm release:check` runs the checks
|
|
83
|
+
that need no install from npm; `pnpm package:check` installs the packed package and checks what it ships, and
|
|
84
|
+
`pnpm security:no-leak` checks that nothing private reaches the output. The changes are in [CHANGELOG.md](CHANGELOG.md).
|
package/bin/release
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env sh
|
|
2
2
|
# Publishes the version in package.json and tags it. Run on a clean `main` that matches origin/main; the owner runs
|
|
3
|
-
# it, never an agent.
|
|
3
|
+
# it, never an agent. When npm already has that version, or a higher one, it raises it to the next free one through a
|
|
4
|
+
# pull request and publishes that (WC-REL-5). Nothing is published until `pnpm release:check` passes.
|
|
4
5
|
#
|
|
5
6
|
# bin/release # publishes from GitHub Actions (.github/workflows/release.yml, npm trusted publishing)
|
|
6
7
|
# bin/release --local # publishes from this computer: NPM_TOKEN if exported, else your own `npm login`
|
|
@@ -23,7 +24,8 @@ done
|
|
|
23
24
|
|
|
24
25
|
cd "$(dirname "$0")/.."
|
|
25
26
|
self=$(git hash-object bin/release)
|
|
26
|
-
# The fold fast-
|
|
27
|
+
# The fold and land() fast-forward main, and sh reads this file as it goes: a newer bin/release would run half old,
|
|
28
|
+
# half new.
|
|
27
29
|
rerun_if_changed() {
|
|
28
30
|
[ "$(git hash-object bin/release)" != "$self" ] || return 0
|
|
29
31
|
[ -z "${WIRECAT_RELEASE_RERUN:-}" ] || { echo "bin/release changed again while releasing — run it again" >&2; exit 1; }
|
|
@@ -50,19 +52,70 @@ published() {
|
|
|
50
52
|
npm view "$package@$version" version --prefer-online >/dev/null 2>&1
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
# main takes changes only through a pull request (the repository's ruleset).
|
|
56
|
+
land() {
|
|
57
|
+
branch=$1 title=$2 body=$3
|
|
58
|
+
git switch -q -c "$branch"
|
|
59
|
+
git commit -q -am "$title" -m "$body"
|
|
60
|
+
git push -q -u origin "$branch"
|
|
61
|
+
git switch -q main
|
|
62
|
+
pr=$(gh pr create --base main --head "$branch" --title "$title" --body "$body")
|
|
63
|
+
# Checks register a few seconds after the pull request; --watch with none exits at once.
|
|
64
|
+
for _ in $(seq 30); do
|
|
65
|
+
[ "$(gh pr checks "$pr" --json name --jq length 2>/dev/null || echo 0)" -gt 0 ] && break
|
|
66
|
+
sleep 2
|
|
67
|
+
done
|
|
68
|
+
gh pr checks "$pr" --watch --fail-fast >/dev/null ||
|
|
69
|
+
{ echo "checks failed on $pr — nothing is published" >&2; exit 1; }
|
|
70
|
+
git fetch -q origin
|
|
71
|
+
[ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ] ||
|
|
72
|
+
{ echo "main advanced while $pr was being prepared — stop and review the current version before merging" >&2; exit 1; }
|
|
73
|
+
gh pr merge "$pr" --squash --delete-branch --subject "$title (#${pr##*/})"
|
|
74
|
+
git fetch -q origin
|
|
75
|
+
git merge -q --ff-only origin/main
|
|
76
|
+
[ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ] ||
|
|
77
|
+
{ echo "main is not origin/main after $pr" >&2; exit 1; }
|
|
78
|
+
}
|
|
79
|
+
|
|
53
80
|
# A merge after the version was dated, but before it was published, adds Unreleased above it; --land folds it on a
|
|
54
81
|
# fresh main through its own pull request.
|
|
55
82
|
folded=""
|
|
56
83
|
published || folded=$(pnpm exec cli-dev changelog fold --land --version "$version")
|
|
57
84
|
[ -z "$folded" ] || echo "$folded"
|
|
58
85
|
rerun_if_changed "$@"
|
|
86
|
+
|
|
87
|
+
# Two sessions preparing the same number is normal; the second takes the next free one.
|
|
88
|
+
latest=$(npm view "$package" version --prefer-online 2>/dev/null || true)
|
|
89
|
+
versions=$(npm view "$package" versions --json --prefer-online 2>/dev/null || true)
|
|
90
|
+
next=$(pnpm exec cli-dev next-version "$version" "$latest" "$versions")
|
|
91
|
+
if [ "$next" != "$version" ]; then
|
|
92
|
+
taken=""
|
|
93
|
+
if published; then taken=1; why="$version is already on npm"; else why="$version is not above npm's latest $latest"; fi
|
|
94
|
+
echo "$why — publishing $next instead"
|
|
95
|
+
# A version npm already has keeps its changelog section; the unreleased one becomes the new version.
|
|
96
|
+
node -e 'const fs = require("fs"); const [o, n, taken] = process.argv.slice(1)
|
|
97
|
+
const swap = (file, from, to) => {
|
|
98
|
+
const text = fs.readFileSync(file, "utf8")
|
|
99
|
+
if (!text.includes(from)) throw new Error(`${file} has no ${JSON.stringify(from.trim())} to raise ${o} to ${n}`)
|
|
100
|
+
fs.writeFileSync(file, text.replace(from, to))
|
|
101
|
+
}
|
|
102
|
+
swap("package.json", `"version": "${o}"`, `"version": "${n}"`)
|
|
103
|
+
swap("src/version.ts", `VERSION = "${o}"`, `VERSION = "${n}"`)
|
|
104
|
+
const today = new Date().toLocaleDateString("en-GB").replaceAll("/", ".")
|
|
105
|
+
if (taken) swap("CHANGELOG.md", "\n## Unreleased\n", `\n## ${n} — ${today}\n`)
|
|
106
|
+
else swap("CHANGELOG.md", `\n## ${o} — `, `\n## ${n} — `)' "$version" "$next" "$taken"
|
|
107
|
+
body="bin/release: $why. Only the number changes: package.json, src/version.ts, the CHANGELOG heading."
|
|
108
|
+
land "release/$next" "chore(release): $next" "$body"
|
|
109
|
+
rerun_if_changed "$@"
|
|
110
|
+
version=$next
|
|
111
|
+
fi
|
|
59
112
|
top=$(sed -n '/^## /{p;q;}' CHANGELOG.md)
|
|
60
113
|
case "$top" in
|
|
61
114
|
"## $version — "[0-9][0-9].[0-9][0-9].[0-9][0-9][0-9][0-9]) ;;
|
|
62
115
|
*) echo "date the top changelog section as \"## $version — DD.MM.YYYY\" in a pull request before releasing" >&2; exit 1 ;;
|
|
63
116
|
esac
|
|
64
117
|
if published; then
|
|
65
|
-
echo "$package@$version is already on npm
|
|
118
|
+
echo "$package@$version is already on npm — raise the version first" >&2
|
|
66
119
|
exit 1
|
|
67
120
|
fi
|
|
68
121
|
|
package/biome.json
CHANGED
|
@@ -2,5 +2,27 @@
|
|
|
2
2
|
"files": {
|
|
3
3
|
"includes": ["**", "!**/node_modules", "!**/dist", "!.worktrees", "!pnpm-lock.yaml", "!config/wirecat"]
|
|
4
4
|
},
|
|
5
|
-
"extends": ["./config/wirecat/biome.base.json"]
|
|
5
|
+
"extends": ["./config/wirecat/biome.base.json"],
|
|
6
|
+
"overrides": [
|
|
7
|
+
{
|
|
8
|
+
"includes": ["src/**", "!src/mcp.ts", "!**/*.test.ts"],
|
|
9
|
+
"linter": {
|
|
10
|
+
"rules": {
|
|
11
|
+
"style": {
|
|
12
|
+
"noRestrictedImports": {
|
|
13
|
+
"level": "error",
|
|
14
|
+
"options": {
|
|
15
|
+
"patterns": [
|
|
16
|
+
{
|
|
17
|
+
"group": ["@modelcontextprotocol/**"],
|
|
18
|
+
"message": "only src/mcp.ts serves MCP; commands and services do not know the protocol"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
6
28
|
}
|
package/contracts/plan.json
CHANGED
package/cspell.json
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.2",
|
|
3
|
-
"import": [
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"@cspell/dict-ru_ru/cspell-ext.json"
|
|
7
|
-
],
|
|
8
|
-
"ignorePaths": ["docs/dev/**", "CHANGELOG.md"],
|
|
9
|
-
"words": ["clidev", "cyrillic", "latin", "wirecat"]
|
|
3
|
+
"import": ["./config/wirecat/cspell.json"],
|
|
4
|
+
"ignorePaths": ["docs/commands.md", "docs/dev/**", "CHANGELOG.md"],
|
|
5
|
+
"words": ["clidev"]
|
|
10
6
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
/** It is run when everything else is broken, so
|
|
2
|
+
/** It is run when everything else is broken, so a problem in the config file is a field in the answer, never a throw. */
|
|
3
3
|
export declare const doctorReport: (env: NodeJS.ProcessEnv) => {
|
|
4
4
|
version: string;
|
|
5
5
|
runtime: {
|
|
@@ -13,10 +13,31 @@ export declare const doctorReport: (env: NodeJS.ProcessEnv) => {
|
|
|
13
13
|
};
|
|
14
14
|
config: {
|
|
15
15
|
path: string;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
found: boolean;
|
|
17
|
+
issues: import("@wirecat/cli-core/cli").ConfigIssue[];
|
|
18
18
|
};
|
|
19
|
-
profiles: string[]
|
|
19
|
+
profiles: string[];
|
|
20
|
+
read_only: string | null;
|
|
21
|
+
} | {
|
|
22
|
+
version: string;
|
|
23
|
+
runtime: {
|
|
24
|
+
node: string;
|
|
25
|
+
platform: NodeJS.Platform;
|
|
26
|
+
};
|
|
27
|
+
paths: {
|
|
28
|
+
config: string;
|
|
29
|
+
state: string;
|
|
30
|
+
cache: string;
|
|
31
|
+
};
|
|
32
|
+
config: {
|
|
33
|
+
path: string;
|
|
34
|
+
found: boolean;
|
|
35
|
+
issues: never[];
|
|
36
|
+
};
|
|
37
|
+
settings: {
|
|
38
|
+
error: string;
|
|
39
|
+
};
|
|
40
|
+
profiles: null;
|
|
20
41
|
read_only: string | null;
|
|
21
42
|
};
|
|
22
43
|
export declare const doctorCommand: () => Command;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AASnC,
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AASnC,yHAAyH;AACzH,eAAO,MAAM,YAAY,QAAS,MAAM,CAAC,UAAU;;;QAIpC,IAAI;QAAyB,QAAQ;;;QACvC,MAAM;QAAgB,KAAK;QAAe,KAAK;;;QAO5C,IAAI;QAAuB,KAAK;QAAwB,MAAM;;;;;;;QAR/D,IAAI;QAAyB,QAAQ;;;QACvC,MAAM;QAAgB,KAAK;QAAe,KAAK;;;QAe5C,IAAI;QAAQ,KAAK;QAAoB,MAAM;;;QACzC,KAAK;;;;CAKtB,CAAA;AAED,eAAO,MAAM,aAAa,QAAO,OAS3B,CAAA"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -10,27 +10,34 @@ const failure = (error) => {
|
|
|
10
10
|
return error.message;
|
|
11
11
|
throw error;
|
|
12
12
|
};
|
|
13
|
-
/** It is run when everything else is broken, so
|
|
13
|
+
/** It is run when everything else is broken, so a problem in the config file is a field in the answer, never a throw. */
|
|
14
14
|
export const doctorReport = (env) => {
|
|
15
15
|
const paths = resolvePaths({ appName: IDENTITY.appName, prefix: IDENTITY.envPrefix, env });
|
|
16
|
-
const
|
|
17
|
-
let config;
|
|
18
|
-
let profiles = null;
|
|
19
|
-
try {
|
|
20
|
-
profiles = configuration.configuredProfiles({ env, readOnly: true });
|
|
21
|
-
config = { path: file, exists: existsSync(file) };
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
config = { path: file, exists: existsSync(file), error: failure(error) };
|
|
25
|
-
}
|
|
26
|
-
return {
|
|
16
|
+
const common = {
|
|
27
17
|
version: IDENTITY.version,
|
|
28
18
|
runtime: { node: process.versions.node, platform: process.platform },
|
|
29
19
|
paths: { config: paths.config, state: paths.state, cache: paths.cache },
|
|
30
|
-
config,
|
|
31
|
-
profiles,
|
|
32
|
-
read_only: readOnlyLockOf(IDENTITY, env) ?? null,
|
|
33
20
|
};
|
|
21
|
+
const readOnly = readOnlyLockOf(IDENTITY, env) ?? null;
|
|
22
|
+
try {
|
|
23
|
+
const settings = configuration.resolveSettings({}, { env, readOnly: true, needsProfile: false, withIssues: true });
|
|
24
|
+
return {
|
|
25
|
+
...common,
|
|
26
|
+
config: { path: settings.configPath, found: settings.configFound, issues: settings.configIssues ?? [] },
|
|
27
|
+
profiles: settings.configuredProfiles,
|
|
28
|
+
read_only: readOnly,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const file = configFilePath(paths.config);
|
|
33
|
+
return {
|
|
34
|
+
...common,
|
|
35
|
+
config: { path: file, found: existsSync(file), issues: [] },
|
|
36
|
+
settings: { error: failure(error) },
|
|
37
|
+
profiles: null,
|
|
38
|
+
read_only: readOnly,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
34
41
|
};
|
|
35
42
|
export const doctorCommand = () => annotate(new Command("doctor"), {
|
|
36
43
|
effects: LOCAL_READ,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAClH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,MAAM,OAAO,GAAG,CAAC,KAAc,EAAE,EAAE;IACjC,IAAI,KAAK,YAAY,QAAQ;QAAE,OAAO,KAAK,CAAC,OAAO,CAAA;IACnD,MAAM,KAAK,CAAA;AACb,CAAC,CAAA;AAED,
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAGxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAGpD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;IAChC,gGAAgG;IAChG,MAAM,EAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;IAC7C,SAAS,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAA;IAC/C,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACjE,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;CAChD;
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAGxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAGpD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;IAChC,gGAAgG;IAChG,MAAM,EAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;IAC7C,SAAS,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAA;IAC/C,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACjE,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;CAChD;AAOD,6GAA6G;AAC7G,eAAO,MAAM,eAAe,YAAa,UAAU,KAAG,SAuCrD,CAAA;AAED,+EAA+E;AAC/E,eAAO,MAAM,QAAQ,YAAmB,UAAU,KAAG,OAAO,CAAC,IAAI,CAkBhE,CAAA"}
|
package/dist/mcp.js
CHANGED
|
@@ -4,6 +4,7 @@ import { mcpErrorResult } from "@wirecat/cli-base";
|
|
|
4
4
|
import { registerCommands, runCommandForAgent, toolsFromCommands } from "@wirecat/cli-core/mcp";
|
|
5
5
|
import { IDENTITY } from "./identity.js";
|
|
6
6
|
const EXCLUDED = [["mcp"]];
|
|
7
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
8
|
/** The shared three tools — search, read, write — over every command, writes included; the levels decide. */
|
|
8
9
|
export const createMcpServer = (options) => {
|
|
9
10
|
const server = new McpServer({ name: IDENTITY.command, version: IDENTITY.version }, {
|
|
@@ -32,7 +33,7 @@ export const createMcpServer = (options) => {
|
|
|
32
33
|
const answer = await runCommandForAgent(program, tool.path, input, context);
|
|
33
34
|
if ("error" in answer)
|
|
34
35
|
return mcpErrorResult(answer.error);
|
|
35
|
-
const value = (answer.result
|
|
36
|
+
const value = isObject(answer.result) ? answer.result : { result: answer.result };
|
|
36
37
|
return { content: [{ type: "text", text: JSON.stringify(value) }], structuredContent: value };
|
|
37
38
|
},
|
|
38
39
|
},
|
|
@@ -51,6 +52,9 @@ export const serveMcp = async (options) => {
|
|
|
51
52
|
void server.close().then(resolve, reject);
|
|
52
53
|
};
|
|
53
54
|
options.signal?.addEventListener("abort", stop, { once: true });
|
|
55
|
+
// A stop that came while the server module was loading has already fired, and the listener never will.
|
|
56
|
+
if (options.signal?.aborted)
|
|
57
|
+
stop();
|
|
54
58
|
void server.connect(options.transport ?? new StdioServerTransport()).catch(reject);
|
|
55
59
|
});
|
|
56
60
|
}
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAGlD,OAAO,EAAmB,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAEhH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAaxC,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;AAE1B,6GAA6G;AAC7G,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAmB,EAAa,EAAE;IAChE,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,EACrD;QACE,YAAY,EACV,GAAG,QAAQ,CAAC,OAAO,uBAAuB,OAAO,CAAC,OAAO,yBAAyB,QAAQ,CAAC,OAAO,iBAAiB;YACnH,oBAAoB,QAAQ,CAAC,OAAO,YAAY,QAAQ,CAAC,OAAO,kCAAkC;YAClG,iEAAiE;KACpE,CACF,CAAA;IACD,MAAM,OAAO,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAA;IAChE,MAAM,OAAO,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAA;IAClG,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,QAAQ,EAAE;QAChD,kBAAkB,EAAE,IAAI;QACxB,aAAa,EAAE,OAAO,CAAC,MAAM;QAC7B,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;QACxB,OAAO,EAAE,QAAQ;QACjB,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC,CAAA;IACF,MAAM,QAAQ,GAA+B,MAAM,CAAC,WAAW,CAC7D,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB,IAAI,CAAC,OAAO;QACZ;YACE,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;gBACnB,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;gBAC3E,IAAI,OAAO,IAAI,MAAM;oBAAE,OAAO,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBAC1D,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.5";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = "0.1.
|
|
1
|
+
export const VERSION = "0.1.5";
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/docs/commands.md
CHANGED
|
@@ -166,7 +166,7 @@ clidev config set <setting> <value> [options]
|
|
|
166
166
|
|
|
167
167
|
| Argument | | What it is |
|
|
168
168
|
|---|---|---|
|
|
169
|
-
| `setting` | required | one of: limit, timeout_ms, color, record, keep_runs_for_days, permissions, sends_per_hour, requests_per_minute, transcribe_with, speech_model, search_catch_up, picture_search, embedding_provider, embedding_model, embedding_base_url, embedding_dims,
|
|
169
|
+
| `setting` | required | one of: limit, timeout_ms, color, record, keep_runs_for_days, permissions, sends_per_hour, requests_per_minute, transcribe_with, speech_model, search_catch_up, picture_search, embedding_provider, embedding_model, embedding_base_url, embedding_dims, models, read_other_bots, update_check, skill_hint, search_stemmers.cyrillic, search_stemmers.latin, phone_countries, task_categories.<name>, task_agent_close, task_agent_close_min, task_first_pass_per_chat, account_groups.<name>. |
|
|
170
170
|
| `value` | required | a number, true or false; for a group tg:work,max:Alice,email:me@x. |
|
|
171
171
|
|
|
172
172
|
| Option | What it does |
|
|
@@ -191,7 +191,7 @@ clidev config unset <setting> [options]
|
|
|
191
191
|
|
|
192
192
|
| Argument | | What it is |
|
|
193
193
|
|---|---|---|
|
|
194
|
-
| `setting` | required | one of: limit, timeout_ms, color, record, keep_runs_for_days, permissions, sends_per_hour, requests_per_minute, transcribe_with, speech_model, search_catch_up, picture_search, embedding_provider, embedding_model, embedding_base_url, embedding_dims,
|
|
194
|
+
| `setting` | required | one of: limit, timeout_ms, color, record, keep_runs_for_days, permissions, sends_per_hour, requests_per_minute, transcribe_with, speech_model, search_catch_up, picture_search, embedding_provider, embedding_model, embedding_base_url, embedding_dims, models, read_other_bots, update_check, skill_hint, search_stemmers.cyrillic, search_stemmers.latin, phone_countries, task_categories.<name>, task_agent_close, task_agent_close_min, task_first_pass_per_chat, account_groups.<name>. |
|
|
195
195
|
|
|
196
196
|
| Option | What it does |
|
|
197
197
|
|---|---|
|
|
@@ -224,7 +224,7 @@ clidev runs list [query] [options]
|
|
|
224
224
|
| `--limit <n>` | how many to show. Default: `20`. |
|
|
225
225
|
| `--page <n>` | which page, starting at 1. Default: `1`. |
|
|
226
226
|
|
|
227
|
-
**Returns:** run — run_id, command,
|
|
227
|
+
**Returns:** run — run_id, command, started_at, status, cli_version, profile, completed_at, requests, and 15 more; every field: clidev commands info runs list
|
|
228
228
|
|
|
229
229
|
**Example:** `clidev personal runs list --limit 10`
|
|
230
230
|
|
|
@@ -244,7 +244,7 @@ clidev runs show <run-id> [options]
|
|
|
244
244
|
|---|---|
|
|
245
245
|
| `--problems` | only failed responses, warnings and crashes. |
|
|
246
246
|
|
|
247
|
-
**Returns:** run details — run_id, command,
|
|
247
|
+
**Returns:** run details — run_id, command, started_at, status, cli_version, directory, event_count, events, and 14 more; every field: clidev commands info runs show
|
|
248
248
|
|
|
249
249
|
**Example:** `clidev personal runs show run-101`
|
|
250
250
|
|
|
@@ -15,5 +15,9 @@ A messenger CLI sets `messenger: true` in `src/identity.ts`, and `new --category
|
|
|
15
15
|
category keeps the template's `messenger: false`, so the shared help, `commands` and `config` do not speak of chats,
|
|
16
16
|
messages and senders.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
`
|
|
18
|
+
cli-core keeps a provider registry (WC-ARCH-21). A CLI whose provider is not built into cli-core calls
|
|
19
|
+
`registerProvider(store, { name, kind, aliases, command })` from `@wirecat/cli-core/store` on every run that opens
|
|
20
|
+
the shared store. `kind` is one of `messages`, `mail`, `notes`, `documents`, `meetings` or `events`. Until the CLI
|
|
21
|
+
has run once on a computer, `search` there leaves its accounts out and lists them under `skipped`. The reference CLIs
|
|
22
|
+
do not show this call: their providers are built into cli-core. The skeleton opens no store and has no provider, so
|
|
23
|
+
it registers nothing; the call comes with the CLI's first store code.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -8,7 +8,7 @@ support request.
|
|
|
8
8
|
| Not sure what is set up | Run `clidev personal doctor`. It reads no keyring and uses no network. |
|
|
9
9
|
| `clidev` is not found | Open a new terminal, and check that npm's global folder is on `PATH`. |
|
|
10
10
|
| "name a profile" | Put the profile first, as in `clidev personal config show`, or set `CLIDEV_PROFILE`. |
|
|
11
|
-
| The settings file does not read | `doctor`
|
|
11
|
+
| The settings file does not read | `doctor` lists every problem in the file under `config.issues`. Run the `config unset` each one gives, or fix the file by hand. |
|
|
12
12
|
| A write is refused | The profile's permissions or `CLIDEV_READ_ONLY` stop it; see [settings](configuration.md#permissions). |
|
|
13
13
|
|
|
14
14
|
Never paste a token, a password or private content into a support request.
|
package/kit/dist/new.js
CHANGED
|
@@ -214,7 +214,7 @@ export const markExecutable = async (cwd, paths) => {
|
|
|
214
214
|
export const manualSteps = (names) => [
|
|
215
215
|
`1. Create the repository ${names.owner}/${names.name} and its main ruleset (cli-base docs/standards/new-cli.md).`,
|
|
216
216
|
`2. Set npm trusted publishing for ${names.package}, naming release.yml (WC-REL-11), and add any secrets.`,
|
|
217
|
-
"3.
|
|
217
|
+
"3. Give .github/workflows/monthly.yml a cron minute between 02:17 and 03:20 UTC that no other repository uses.",
|
|
218
218
|
"4. WireCat CLIs only: a pull request to cli-base's PARITY_GROUPS, a line in cli-core's WIRECAT_TOOLS and in cli-docs' tools.json.",
|
|
219
219
|
];
|
|
220
220
|
export const runNew = async (cwd, source, args, io) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-template",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"CHANGELOG.md",
|
|
33
33
|
"CLAUDE.md",
|
|
34
34
|
"LICENSE",
|
|
35
|
-
"README.md",
|
|
35
|
+
"/README.md",
|
|
36
36
|
"SECURITY.md",
|
|
37
37
|
".gitattributes",
|
|
38
38
|
".gitleaks.toml",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"lefthook.yml",
|
|
44
44
|
"pnpm-workspace.yaml",
|
|
45
45
|
"template.json",
|
|
46
|
-
"tsconfig.json",
|
|
46
|
+
"/tsconfig.json",
|
|
47
47
|
"tsconfig.scripts.json",
|
|
48
|
-
"tsconfig.test.json",
|
|
48
|
+
"/tsconfig.test.json",
|
|
49
49
|
"vitest.config.ts",
|
|
50
50
|
"gitignore"
|
|
51
51
|
],
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"smoke": "node --experimental-strip-types scripts/smoke.ts",
|
|
64
64
|
"smoke:bun": "bun scripts/smoke.ts",
|
|
65
65
|
"package:check": "pnpm smoke",
|
|
66
|
-
"docs:check": "cli-dev docs-check --
|
|
66
|
+
"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 CLAUDE.md AGENTS.md docs/*.md docs/dev/*.md && pnpm docs:lint",
|
|
67
67
|
"docs:lint": "markdownlint-cli2",
|
|
68
68
|
"contracts:check": "cli-contract contracts/plan.json",
|
|
69
69
|
"security:no-leak": "cli-testing security no-leak . contracts/no-leak.json",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"release:check": "pnpm standards:check && pnpm lint && pnpm typecheck && pnpm test:coverage && pnpm lint:commands && pnpm audit:commands && pnpm contracts:check && pnpm test:matrix:check && pnpm docs:check && pnpm generate:check && pnpm version:check",
|
|
77
77
|
"prepare": "lefthook install || true",
|
|
78
78
|
"prepack": "pnpm build",
|
|
79
|
-
"docs:spell": "cspell lint --config cspell.json --no-progress README.md",
|
|
79
|
+
"docs:spell": "cspell lint --config cspell.json --no-progress --no-summary README.md SECURITY.md 'docs/*.md'",
|
|
80
80
|
"kit:build": "tsc -p kit && node -e \"require('node:fs').copyFileSync('.gitignore', 'gitignore')\"",
|
|
81
81
|
"kit:typecheck": "tsc -p kit/tsconfig.test.json",
|
|
82
82
|
"kit:e2e": "bash kit/e2e.sh"
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"dependencies": {
|
|
85
85
|
"@modelcontextprotocol/server": "2.3.0",
|
|
86
86
|
"@wirecat/cli-base": "0.53.0",
|
|
87
|
-
"@wirecat/cli-core": "0.
|
|
87
|
+
"@wirecat/cli-core": "0.424.0",
|
|
88
88
|
"commander": "15.0.0"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type DocsRules, JOURNAL_IDS, markdownFiles } from "@wirecat/cli-base/release"
|
|
2
|
+
|
|
3
|
+
export const CHANGELOG = {
|
|
4
|
+
headings: ["Added", "Changed — may break callers", "Fixed", "Security", "Removed"],
|
|
5
|
+
unreleased: "Unreleased",
|
|
6
|
+
ids: JOURNAL_IDS,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const USER_PAGES = /^(README|SECURITY)\.md$|^docs\/[^/]+\.md$/
|
|
10
|
+
|
|
11
|
+
export const docsRules = (root: string): DocsRules => ({
|
|
12
|
+
files: markdownFiles(root, new Set(["node_modules", "dist", "coverage", ".git"])),
|
|
13
|
+
ids: JOURNAL_IDS,
|
|
14
|
+
userPage: (name) => USER_PAGES.test(name.replaceAll("\\", "/")),
|
|
15
|
+
})
|
package/scripts/smoke.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import assert from "node:assert/strict"
|
|
2
2
|
import { execFileSync } from "node:child_process"
|
|
3
|
-
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs"
|
|
4
4
|
import { tmpdir } from "node:os"
|
|
5
5
|
import { join, resolve } from "node:path"
|
|
6
|
+
import { packContents } from "@wirecat/cli-base/release"
|
|
6
7
|
|
|
7
8
|
const root = resolve(import.meta.dirname, "..")
|
|
8
|
-
const { name, version } = JSON.parse(readFileSync(join(root, "package.json"), "utf8"))
|
|
9
|
+
const { name, version, files } = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as {
|
|
10
|
+
name: string
|
|
11
|
+
version: string
|
|
12
|
+
files: string[]
|
|
13
|
+
}
|
|
14
|
+
// npm reads `files` like .gitignore: a name with no leading slash also matches the same name in any folder.
|
|
15
|
+
const shipped = files
|
|
16
|
+
.map((entry) => entry.replace(/^\//, ""))
|
|
17
|
+
.map((entry) => (statSync(join(root, entry), { throwIfNoEntry: false })?.isDirectory() ? `${entry}/` : entry))
|
|
18
|
+
assert.deepEqual(packContents(root, ["package.json", ...shipped], "package.json and the paths in its files list")(), [])
|
|
9
19
|
const temp = mkdtempSync(join(tmpdir(), "clidev-package-"))
|
|
10
20
|
try {
|
|
11
21
|
const userConfig = join(temp, "npm-user-config")
|
package/src/commands/doctor.ts
CHANGED
|
@@ -11,25 +11,32 @@ const failure = (error: unknown) => {
|
|
|
11
11
|
throw error
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
/** It is run when everything else is broken, so
|
|
14
|
+
/** It is run when everything else is broken, so a problem in the config file is a field in the answer, never a throw. */
|
|
15
15
|
export const doctorReport = (env: NodeJS.ProcessEnv) => {
|
|
16
16
|
const paths = resolvePaths({ appName: IDENTITY.appName, prefix: IDENTITY.envPrefix, env })
|
|
17
|
-
const
|
|
18
|
-
let config: { path: string; exists: boolean; error?: string }
|
|
19
|
-
let profiles: string[] | null = null
|
|
20
|
-
try {
|
|
21
|
-
profiles = configuration.configuredProfiles({ env, readOnly: true })
|
|
22
|
-
config = { path: file, exists: existsSync(file) }
|
|
23
|
-
} catch (error) {
|
|
24
|
-
config = { path: file, exists: existsSync(file), error: failure(error) }
|
|
25
|
-
}
|
|
26
|
-
return {
|
|
17
|
+
const common = {
|
|
27
18
|
version: IDENTITY.version,
|
|
28
19
|
runtime: { node: process.versions.node, platform: process.platform },
|
|
29
20
|
paths: { config: paths.config, state: paths.state, cache: paths.cache },
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
}
|
|
22
|
+
const readOnly = readOnlyLockOf(IDENTITY, env) ?? null
|
|
23
|
+
try {
|
|
24
|
+
const settings = configuration.resolveSettings({}, { env, readOnly: true, needsProfile: false, withIssues: true })
|
|
25
|
+
return {
|
|
26
|
+
...common,
|
|
27
|
+
config: { path: settings.configPath, found: settings.configFound, issues: settings.configIssues ?? [] },
|
|
28
|
+
profiles: settings.configuredProfiles,
|
|
29
|
+
read_only: readOnly,
|
|
30
|
+
}
|
|
31
|
+
} catch (error) {
|
|
32
|
+
const file = configFilePath(paths.config)
|
|
33
|
+
return {
|
|
34
|
+
...common,
|
|
35
|
+
config: { path: file, found: existsSync(file), issues: [] },
|
|
36
|
+
settings: { error: failure(error) },
|
|
37
|
+
profiles: null,
|
|
38
|
+
read_only: readOnly,
|
|
39
|
+
}
|
|
33
40
|
}
|
|
34
41
|
}
|
|
35
42
|
|
package/src/mcp.test.ts
CHANGED
|
@@ -78,7 +78,6 @@ it("clidev <profile> mcp starts the server and ends on a signal", async () => {
|
|
|
78
78
|
tty: false,
|
|
79
79
|
streams: { data: () => {}, diagnostic: () => {} },
|
|
80
80
|
})
|
|
81
|
-
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
82
81
|
stop.abort()
|
|
83
82
|
expect(await serving).toBe(0)
|
|
84
83
|
})
|
package/src/mcp.ts
CHANGED
|
@@ -20,6 +20,9 @@ export interface McpOptions {
|
|
|
20
20
|
|
|
21
21
|
const EXCLUDED = [["mcp"]]
|
|
22
22
|
|
|
23
|
+
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
24
|
+
typeof value === "object" && value !== null && !Array.isArray(value)
|
|
25
|
+
|
|
23
26
|
/** The shared three tools — search, read, write — over every command, writes included; the levels decide. */
|
|
24
27
|
export const createMcpServer = (options: McpOptions): McpServer => {
|
|
25
28
|
const server = new McpServer(
|
|
@@ -52,7 +55,7 @@ export const createMcpServer = (options: McpOptions): McpServer => {
|
|
|
52
55
|
run: async (input) => {
|
|
53
56
|
const answer = await runCommandForAgent(program, tool.path, input, context)
|
|
54
57
|
if ("error" in answer) return mcpErrorResult(answer.error)
|
|
55
|
-
const value = (answer.result
|
|
58
|
+
const value = isObject(answer.result) ? answer.result : { result: answer.result }
|
|
56
59
|
return { content: [{ type: "text" as const, text: JSON.stringify(value) }], structuredContent: value }
|
|
57
60
|
},
|
|
58
61
|
},
|
|
@@ -73,6 +76,8 @@ export const serveMcp = async (options: McpOptions): Promise<void> => {
|
|
|
73
76
|
void server.close().then(resolve, reject)
|
|
74
77
|
}
|
|
75
78
|
options.signal?.addEventListener("abort", stop, { once: true })
|
|
79
|
+
// A stop that came while the server module was loading has already fired, and the listener never will.
|
|
80
|
+
if (options.signal?.aborted) stop()
|
|
76
81
|
void server.connect(options.transport ?? new StdioServerTransport()).catch(reject)
|
|
77
82
|
})
|
|
78
83
|
} finally {
|
package/src/program.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
2
2
|
import { join } from "node:path"
|
|
3
3
|
import { expect, it } from "vitest"
|
|
4
|
+
import { doctorReport } from "./commands/doctor.js"
|
|
4
5
|
import { IDENTITY } from "./identity.js"
|
|
5
6
|
import { run } from "./program.js"
|
|
6
7
|
|
|
@@ -25,20 +26,40 @@ it("lists the required root commands and config", async () => {
|
|
|
25
26
|
it("doctor answers without a config file", async () => {
|
|
26
27
|
const { code, out } = await ran(["doctor", "--json"])
|
|
27
28
|
expect(code).toBe(0)
|
|
28
|
-
expect(json(out).item).toMatchObject({ config: {
|
|
29
|
+
expect(json(out).item).toMatchObject({ config: { found: false, issues: [] }, profiles: [], read_only: null })
|
|
29
30
|
})
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
const withConfig = async (text: string, check: (config: unknown) => void) => {
|
|
32
33
|
const dir = process.env.CLIDEV_CONFIG_DIR ?? ""
|
|
33
34
|
mkdirSync(dir, { recursive: true })
|
|
34
|
-
writeFileSync(join(dir, "config.json"),
|
|
35
|
+
writeFileSync(join(dir, "config.json"), text)
|
|
35
36
|
try {
|
|
36
37
|
const { code, out } = await ran(["doctor", "--json"])
|
|
37
38
|
expect(code).toBe(0)
|
|
38
|
-
|
|
39
|
+
check(json(out).item.config)
|
|
39
40
|
} finally {
|
|
40
41
|
rmSync(join(dir, "config.json"))
|
|
41
42
|
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
it("doctor reports invalid JSON as an issue", async () => {
|
|
46
|
+
await withConfig('{\n "defaults": {\n', (config) =>
|
|
47
|
+
expect(config).toMatchObject({ found: true, issues: [{ kind: "invalid_json", line: 3 }] }),
|
|
48
|
+
)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it("doctor reports an unknown setting with the command that removes it", async () => {
|
|
52
|
+
await withConfig('{ "defaults": { "limt": 3 } }', (config) =>
|
|
53
|
+
expect(config).toMatchObject({
|
|
54
|
+
found: true,
|
|
55
|
+
issues: [{ kind: "unknown_setting", key: "limt", fix: "clidev config unset --defaults limt" }],
|
|
56
|
+
}),
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it("doctor reports settings an environment variable breaks, and still answers", () => {
|
|
61
|
+
const report = doctorReport({ ...process.env, CLIDEV_EMBEDDING_DIMS: "many" })
|
|
62
|
+
expect(report).toMatchObject({ config: { issues: [] }, settings: { error: expect.any(String) }, profiles: null })
|
|
42
63
|
})
|
|
43
64
|
|
|
44
65
|
it("config show names the profile it was given", async () => {
|
|
@@ -65,7 +86,7 @@ it("upgrade --check installs nothing when npm cannot be reached", async () => {
|
|
|
65
86
|
expect(json(out).item).toMatchObject({ current: IDENTITY.version, latest: null, updated: false })
|
|
66
87
|
})
|
|
67
88
|
|
|
68
|
-
it("keeps a failed command as a run, and runs list, show and path read it back
|
|
89
|
+
it("keeps a failed command as a run, and runs list, show and path read it back", async () => {
|
|
69
90
|
expect((await ran(["--no-such-flag"])).code).toBe(2)
|
|
70
91
|
const listed = await ran(["runs", "list", "--limit", "5", "--page", "1", "--json"])
|
|
71
92
|
expect(listed.code).toBe(0)
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.1.
|
|
1
|
+
export const VERSION = "0.1.5"
|
package/vitest.config.ts
CHANGED
|
@@ -11,9 +11,15 @@ export default defineConfig({
|
|
|
11
11
|
coverage: {
|
|
12
12
|
provider: "v8",
|
|
13
13
|
include: ["src/**/*.ts"],
|
|
14
|
-
exclude: [
|
|
14
|
+
exclude: [
|
|
15
|
+
"src/**/*.test.ts",
|
|
16
|
+
// Test setup: the sandbox and the argv log run only under vitest.
|
|
17
|
+
"src/testing/**",
|
|
18
|
+
// The entry point: one line handing argv to run(), which the suite drives directly.
|
|
19
|
+
"src/bin/**",
|
|
20
|
+
],
|
|
15
21
|
reporter: ["text-summary", "json-summary"],
|
|
16
|
-
thresholds: { lines: 90, statements: 90, functions: 80, branches: 70 },
|
|
22
|
+
thresholds: { lines: 90, statements: 90, functions: 80, branches: 70, perFile: { lines: 50 } },
|
|
17
23
|
},
|
|
18
24
|
},
|
|
19
25
|
})
|
package/kit/README.md
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
# kit — `new` and `upgrade`
|
|
2
|
-
|
|
3
|
-
The copy tool behind `npx @wirecat/cli-template`. It is the package's `cli-template` bin
|
|
4
|
-
(`kit/dist/main.js`, built from `kit/src` by `tsc -p kit`). It is never copied into a new CLI.
|
|
5
|
-
|
|
6
|
-
```sh
|
|
7
|
-
npx @wirecat/cli-template new cal-cli --command cal --package @wirecat/cal-cli --owner WireCatLabs \
|
|
8
|
-
--category notes --content-lang en,ru --sign-in token --api official-docs --filters time,person \
|
|
9
|
-
--never-store tokens --untrusted messages,links
|
|
10
|
-
npx @wirecat/cli-template upgrade --dry-run
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## `new <name>`
|
|
14
|
-
|
|
15
|
-
`new` copies the files of its own package version into `./<name>`. It renames the placeholders, rewrites
|
|
16
|
-
`package.json`, runs `git init` and writes `template.json`, `docs/dev/DESIGN.md` and the reference CLI into
|
|
17
|
-
`AGENTS.md`. It then prints the four steps that need a person. It does not install packages, commit or push.
|
|
18
|
-
|
|
19
|
-
In a terminal it asks for every missing answer. Without a terminal, a missing answer is an error that lists the
|
|
20
|
-
choices. Nothing is guessed.
|
|
21
|
-
|
|
22
|
-
| Flag | Answers (several are comma-separated) |
|
|
23
|
-
|---|---|
|
|
24
|
-
| `<name>` | the folder and repository name |
|
|
25
|
-
| `--command` | the command word |
|
|
26
|
-
| `--package` | the npm package name |
|
|
27
|
-
| `--owner` | the GitHub owner |
|
|
28
|
-
| `--category` | `messenger`, `mail`, `notes`, `files`, `calendar`, `meetings` |
|
|
29
|
-
| `--content-lang` | language codes, several: `en,ru` |
|
|
30
|
-
| `--sign-in` | `oauth`, `token`, `session`, `none` |
|
|
31
|
-
| `--api` | `official-client`, `official-docs`, `unofficial` |
|
|
32
|
-
| `--filters` | several of `time`, `person`, `container`, `text`, `status` |
|
|
33
|
-
| `--never-store` | several of `tokens`, `codes`, `secret-links`, `content` |
|
|
34
|
-
| `--untrusted` | several of `messages`, `files`, `links`, `names`, or `none` alone |
|
|
35
|
-
|
|
36
|
-
Every question except the names and `--content-lang` also takes `other:<your answer>`.
|
|
37
|
-
|
|
38
|
-
`new` writes the content languages into `contentLanguages` in `src/identity.ts`; on the first run, cli-core ticks them
|
|
39
|
-
in advance in the search-language question, and the person confirms. `DESIGN.md` records them too, with the
|
|
40
|
-
speech-model order a messenger definition should use and the warnings for languages that have no stemmer or speech
|
|
41
|
-
model in cli-core. `new` writes no search or model setting.
|
|
42
|
-
|
|
43
|
-
## `upgrade`
|
|
44
|
-
|
|
45
|
-
Run it in a CLI made from the template, with a clean git working tree. For each template-owned file that the
|
|
46
|
-
template changed since the recorded version, it asks one of four things:
|
|
47
|
-
|
|
48
|
-
- **apply** merges the change with `git merge-file`. The base is the recorded version, downloaded with `npm pack`.
|
|
49
|
-
"Ours" is the CLI's file and "theirs" is this version. Both template versions are renamed first, so the command
|
|
50
|
-
word never conflicts.
|
|
51
|
-
- **replace** takes the template's file as it is.
|
|
52
|
-
- **skip** keeps the CLI's file. It is asked again only when the template changes that file again.
|
|
53
|
-
- **diff** shows the template's change.
|
|
54
|
-
|
|
55
|
-
Without a terminal, `--apply`, `--replace` or `--skip` sets the answer for every file. `--dry-run` lists what each
|
|
56
|
-
file would get and writes nothing. Seeds and `package.json` are never touched.
|
|
57
|
-
|
|
58
|
-
A conflict leaves git markers, and `template.json` keeps the new state under `pending`. Resolve the markers and run
|
|
59
|
-
`upgrade` again: it checks that the markers are gone and then moves to the new version.
|
|
60
|
-
|
|
61
|
-
## `template.json` in this repository
|
|
62
|
-
|
|
63
|
-
```json
|
|
64
|
-
{
|
|
65
|
-
"files": [".github/", "bin/release", "AGENTS.md", "docs/meta.json"],
|
|
66
|
-
"seeds": ["src/", "README.md", "docs/", "config/wirecat/"],
|
|
67
|
-
"placeholders": {
|
|
68
|
-
"command": "clidev", "name": "clidev-cli", "package": "@wirecat/clidev-cli", "repository": "WireCatLabs/clidev-cli"
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
- `files` lists the template-owned files that `upgrade` replays. `seeds` lists the files copied once and never
|
|
74
|
-
touched again; `config/wirecat/` goes there, because community's installer owns it. An entry ending in `/` is a
|
|
75
|
-
folder. The most specific entry wins: an exact path beats a folder, a deeper folder beats a shallower one, and a
|
|
76
|
-
seed wins a tie. Only listed files are copied. A listed file missing from the package stops `new`.
|
|
77
|
-
- `template.json` and `kit/` are never copied, whatever the lists say. The kit's tests run inside `pnpm test`, so
|
|
78
|
-
the configs name `kit/`: `new` and `upgrade` drop every `"kit/…"` entry from `vitest.config.ts`, `biome.json`,
|
|
79
|
-
`cspell.json`, `tsconfig*.json` and `.markdownlint-cli2.jsonc`. Do not add a kit-only workflow: `.github/` is
|
|
80
|
-
template-owned and would carry it into every new CLI.
|
|
81
|
-
- `README.md` and `CHANGELOG.md` describe the template, so `new` writes short ones for the new CLI.
|
|
82
|
-
- `config/parity-groups.json` puts the template in parity groups with the published CLIs, so `scripts/audit.sh`
|
|
83
|
-
compares its `commands`, `upgrade`, `config` and `skill` paths with theirs. It is in neither list, so a new CLI does
|
|
84
|
-
not get it: the file replaces cli-base's `PARITY_GROUPS`, and a new CLI joins those groups instead (step 4 of
|
|
85
|
-
`new`'s output).
|
|
86
|
-
- `docs/dev/DESIGN.md` is always a seed: it holds the CLI's interview answers.
|
|
87
|
-
- `package.json` is always a seed. `new` rewrites it: `name` becomes `--package`, `version` becomes `0.0.0`, and
|
|
88
|
-
`repository`, `homepage` and `bugs` point at `<owner>/<name>`. It also drops every `bin`, `files` entry and script
|
|
89
|
-
that points into `kit/`, and the `&& pnpm kit:…` parts of other scripts. `src/version.ts` gets `VERSION = "0.0.0"`.
|
|
90
|
-
- `placeholders` names the words `new` and `upgrade` replace. The command word is replaced as a whole word, so
|
|
91
|
-
`clidev` changes but `clidevs` and cli-base's `cli-dev` do not. Its upper-case form changes too: `CLIDEV_HOME`
|
|
92
|
-
becomes `CAL_HOME`. Followed by a colon and a space it is a key and stays: `clidev: CLIDEV` becomes `clidev: CAL`.
|
|
93
|
-
`name` (`clidev-cli`) is the repository name: `skills/clidev-cli/` becomes `skills/cal-cli/`. The word `clidev`
|
|
94
|
-
cannot appear in prose for any other meaning. Paths are renamed the same way: `src/bin/clidev.ts` becomes
|
|
95
|
-
`src/bin/cal.ts`.
|
|
96
|
-
- Each template version is renamed with its own `placeholders`, so `upgrade` from 0.1.0 or 0.1.1, which used `app`,
|
|
97
|
-
merges cleanly. A key missing from `placeholders` means its `app` value (`app`, `app-cli`, `@wirecat/app-cli`,
|
|
98
|
-
`WireCatLabs/app-cli`): those versions left `name` out.
|
|
99
|
-
- The version is `package.json`'s, never a second copy.
|
|
100
|
-
- `AGENTS.md` can hold the block that `new` fills with the reference CLI:
|
|
101
|
-
|
|
102
|
-
```md
|
|
103
|
-
<!-- cli-template:reference-cli:start -->
|
|
104
|
-
<!-- cli-template:reference-cli:end -->
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Without the block, `new` appends one.
|
|
108
|
-
|
|
109
|
-
## `template.json` in a CLI made from it
|
|
110
|
-
|
|
111
|
-
`new` writes this shape and `upgrade` keeps it current:
|
|
112
|
-
|
|
113
|
-
```json
|
|
114
|
-
{
|
|
115
|
-
"template": "@wirecat/cli-template",
|
|
116
|
-
"version": "0.1.0",
|
|
117
|
-
"names": { "name": "cal-cli", "command": "cal", "package": "@wirecat/cal-cli", "owner": "WireCatLabs" },
|
|
118
|
-
"files": { ".github/workflows/ci.yml": "<sha256 of the renamed template file at this version>" },
|
|
119
|
-
"seeds": ["src/", "README.md", "docs/", "config/wirecat/"]
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## What the package needs from the repository
|
|
124
|
-
|
|
125
|
-
- `"bin": { "cli-template": "kit/dist/main.js" }`, and `kit/dist` in `files`. No ignore file inside `kit/`: npm
|
|
126
|
-
applies it and drops `kit/dist` from the package. npm also never packs `pnpm-lock.yaml`, so it is not a seed; the
|
|
127
|
-
new CLI makes its own on `pnpm install`.
|
|
128
|
-
- Every template-owned file and seed in `files`. npm drops a nested `.gitignore` and any `.npmrc` when it packs.
|
|
129
|
-
When it installs a package, it renames `.gitignore` to `.npmignore`. So the package ships the root `.gitignore` as
|
|
130
|
-
`gitignore`, and `new` and `upgrade` read it back as `.gitignore`. The test in `kit/test/package.test.ts` runs the
|
|
131
|
-
bin from a tarball installed with `npm install` to catch that.
|
|
132
|
-
- `kit:build` (`tsc -p kit`, then the copy to `gitignore`) run by `build`, because `bin/release` and CI pack with
|
|
133
|
-
`--ignore-scripts`. Node does not strip types under `node_modules`, so the bin must be JavaScript.
|
|
134
|
-
- `pnpm kit:e2e` packs this repository, installs the tarball with npm, runs `new` and then `pnpm install` and
|
|
135
|
-
`pnpm ci:quick` in the new CLI. It needs the network and takes a few minutes, so it is not part of `pnpm test`.
|
|
136
|
-
- `kit/test/**/*.test.ts` in vitest's `include`; `kit/dist` is ignored by the root `**/dist` rules of Biome and git.
|
package/kit/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2023",
|
|
4
|
-
"lib": ["ES2023"],
|
|
5
|
-
"module": "NodeNext",
|
|
6
|
-
"moduleResolution": "NodeNext",
|
|
7
|
-
"types": ["node"],
|
|
8
|
-
"strict": true,
|
|
9
|
-
"noUncheckedIndexedAccess": true,
|
|
10
|
-
"verbatimModuleSyntax": true,
|
|
11
|
-
"isolatedModules": true,
|
|
12
|
-
"skipLibCheck": true,
|
|
13
|
-
"rootDir": "src",
|
|
14
|
-
"outDir": "dist"
|
|
15
|
-
},
|
|
16
|
-
"include": ["src/**/*.ts"]
|
|
17
|
-
}
|