@wirecat/cli-template 0.1.3 → 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.
@@ -2,7 +2,7 @@ name: Monthly validation
2
2
 
3
3
  on:
4
4
  schedule:
5
- - cron: "41 3 1 * *"
5
+ - cron: "2 3 1 * *"
6
6
  workflow_dispatch:
7
7
 
8
8
  concurrency:
package/CHANGELOG.md CHANGED
@@ -3,6 +3,79 @@
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
+
60
+ ## 0.1.4 — 25.10.2026
61
+
62
+ ### Added
63
+
64
+ - **The run log can be read: `runs list`, `runs show` and `runs path`** (WC-LIFE-15). A failed command was already
65
+ kept as a run by cli-core's `run`, but nothing in the CLI could show it. `src/program.ts` now registers cli-core's
66
+ `runsCommand`. A CLI started from an older template adds the same line; cli-base's `missing-run-log` lint rule
67
+ reports the CLIs that have not.
68
+ - **`new --category messenger` writes `messenger: true` into the CLI's identity.** Every other category keeps the
69
+ template's `messenger: false`. The Reference CLIs page says which to set.
70
+
71
+ ### Changed — may break callers
72
+
73
+ - **The skeleton runs on cli-core 0.422.0 and says `messenger: false` in `src/identity.ts`.** The shared help,
74
+ `commands`, `config` and the MCP tools no longer speak of chats and messages: `--help-all`'s profile example is
75
+ `clidev personal doctor`, `--brief` and `--full` describe found items and their author, and `config set` and
76
+ `unset` no longer list `sender_colors`. cli-base stays on 0.53, because cli-core 0.422.0 accepts only cli-base 0.53.
77
+ A CLI that upgrades sets the field in its own `src/identity.ts` and refreshes its contract snapshots once.
78
+
6
79
  ## 0.1.3 — 25.10.2026
7
80
 
8
81
  ### Added
package/README.md CHANGED
@@ -34,6 +34,7 @@ none is ever assumed. `CLIDEV_PROFILE=personal` does the same for a whole shell
34
34
  | `clidev personal config show` | Shows each setting and where it came from |
35
35
  | `clidev skill install` | Installs the agent skill for Claude Code, Codex and Gemini CLI |
36
36
  | `clidev personal mcp` | Serves the profile's commands to an AI agent over MCP |
37
+ | `clidev runs list` | Lists recorded runs, newest first: every command that failed is kept |
37
38
  | `clidev upgrade --check` | Says whether a newer version is on npm |
38
39
 
39
40
  ## For AI agents
@@ -41,6 +42,32 @@ none is ever assumed. `CLIDEV_PROFILE=personal` does the same for a whole shell
41
42
  `clidev skill show` prints the rules an agent follows with this tool. `clidev personal mcp` serves every command to an
42
43
  agent, writes included: the profile's permissions decide what runs. See [MCP and permissions](docs/usage.md#agents).
43
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
+
44
71
  ## Documentation
45
72
 
46
73
  - [Install](docs/installation.md)
@@ -52,5 +79,6 @@ agent, writes included: the profile's permissions decide what runs. See [MCP and
52
79
 
53
80
  ## Development
54
81
 
55
- The working rules for people and agents are in [AGENTS.md](AGENTS.md). `pnpm release:check` runs every check the
56
- standards ask for. The changes are in [CHANGELOG.md](CHANGELOG.md).
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. Nothing is published until `pnpm release:check` passes.
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-forwards main, and sh reads this file as it goes: a newer bin/release would run half old, half new.
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; raise the version in a pull request" >&2
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
  }
@@ -18,7 +18,7 @@
18
18
  "type": "object"
19
19
  },
20
20
  "command": {
21
- "description": "a command clidev_tools_search returned: \"messages list\"",
21
+ "description": "a command clidev_tools_search returned: \"commands\"",
22
22
  "minLength": 1,
23
23
  "type": "string"
24
24
  }
@@ -492,7 +492,7 @@
492
492
  "type": "object"
493
493
  },
494
494
  "command": {
495
- "description": "a command clidev_tools_search returned: \"messages list\"",
495
+ "description": "a command clidev_tools_search returned: \"commands\"",
496
496
  "minLength": 1,
497
497
  "type": "string"
498
498
  }
@@ -36,7 +36,7 @@
36
36
  },
37
37
  {
38
38
  "id": "missing-explicit-profile",
39
- "args": ["mcp", "--json"],
39
+ "args": ["mcp"],
40
40
  "exit": 2,
41
41
  "stream": "stderr",
42
42
  "kind": "json",
package/cspell.json CHANGED
@@ -1,10 +1,6 @@
1
1
  {
2
2
  "version": "0.2",
3
- "import": [
4
- "./config/wirecat/cspell.json",
5
- "@cspell/dict-en-gb/cspell-ext.json",
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 an unreadable config is a field in the answer, never a throw. */
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
- exists: boolean;
17
- error?: string;
16
+ found: boolean;
17
+ issues: import("@wirecat/cli-core/cli").ConfigIssue[];
18
18
  };
19
- profiles: string[] | null;
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,iHAAiH;AACjH,eAAO,MAAM,YAAY,QAAS,MAAM,CAAC,UAAU;;;QAapC,IAAI;QAAyB,QAAQ;;;QACvC,MAAM;QAAgB,KAAK;QAAe,KAAK;;;cAXtC,MAAM;gBAAU,OAAO;gBAAU,MAAM;;;;CAgB5D,CAAA;AAED,eAAO,MAAM,aAAa,QAAO,OAS3B,CAAA"}
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"}
@@ -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 an unreadable config is a field in the answer, never a throw. */
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 file = configFilePath(paths.config);
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,iHAAiH;AACjH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAsB,EAAE,EAAE;IACrD,MAAM,KAAK,GAAG,YAAY,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAA;IAC1F,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACzC,IAAI,MAAyD,CAAA;IAC7D,IAAI,QAAQ,GAAoB,IAAI,CAAA;IACpC,IAAI,CAAC;QACH,QAAQ,GAAG,aAAa,CAAC,kBAAkB,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;QACpE,MAAM,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAA;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAA;IAC1E,CAAC;IACD,OAAO;QACL,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE;QACpE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE;QACvE,MAAM;QACN,QAAQ;QACR,SAAS,EAAE,cAAc,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,IAAI;KACjD,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,GAAY,EAAE,CACzC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE;IAC9B,OAAO,EAAE,UAAU;IACnB,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,OAAO,SAAS,EAAE,GAAG,QAAQ,CAAC,OAAO,yBAAyB,CAAC;CACvF,CAAC;KACC,WAAW,CAAC,kFAAkF,CAAC;KAC/F,MAAM,CAAC;IACN,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAA;IAClD,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAChE,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD,eAAO,MAAM,OAAO,wBAAwB,CAAA;AAE5C,eAAO,MAAM,QAAQ,EAAE,WAOtB,CAAA;AAED,eAAO,MAAM,KAAK,mBAA4D,CAAA"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD,eAAO,MAAM,OAAO,wBAAwB,CAAA;AAE5C,eAAO,MAAM,QAAQ,EAAE,WAQtB,CAAA;AAED,eAAO,MAAM,KAAK,mBAA4D,CAAA"}
package/dist/identity.js CHANGED
@@ -7,6 +7,7 @@ export const IDENTITY = {
7
7
  description: "a new WireCat CLI, started from cli-template",
8
8
  version: VERSION,
9
9
  issues: "https://github.com/WireCatLabs/clidev-cli/issues/new",
10
+ messenger: false,
10
11
  };
11
12
  export const SKILL = new URL("../skills/clidev-cli/SKILL.md", import.meta.url);
12
13
  //# sourceMappingURL=identity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,OAAO,GAAG,qBAAqB,CAAA;AAE5C,MAAM,CAAC,MAAM,QAAQ,GAAgB;IACnC,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,YAAY;IACrB,SAAS,EAAE,QAAQ;IACnB,WAAW,EAAE,8CAA8C;IAC3D,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,sDAAsD;CAC/D,CAAA;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,+BAA+B,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAA"}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,OAAO,GAAG,qBAAqB,CAAA;AAE5C,MAAM,CAAC,MAAM,QAAQ,GAAgB;IACnC,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,YAAY;IACrB,SAAS,EAAE,QAAQ;IACnB,WAAW,EAAE,8CAA8C;IAC3D,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,sDAAsD;IAC9D,SAAS,EAAE,KAAK;CACjB,CAAA;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,+BAA+B,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAA"}
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;AAID,6GAA6G;AAC7G,eAAO,MAAM,eAAe,YAAa,UAAU,KAAG,SAuCrD,CAAA;AAED,+EAA+E;AAC/E,eAAO,MAAM,QAAQ,YAAmB,UAAU,KAAG,OAAO,CAAC,IAAI,CAgBhE,CAAA"}
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":"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"}
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,UAAU,EAIhB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAiBxC,eAAO,MAAM,UAAU,QAAO,iBAgB5B,CAAA;AAEF,eAAO,MAAM,aAAa,aAAc,cAAc,KAAG,OAAqD,CAAA;AAE9G,eAAO,MAAM,GAAG,SAAU,MAAM,EAAE,YAAY,UAAU,KAAG,OAAO,CAAC,MAAM,CAA2C,CAAA"}
1
+ {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,UAAU,EAKhB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAiBxC,eAAO,MAAM,UAAU,QAAO,iBAiB5B,CAAA;AAEF,eAAO,MAAM,aAAa,aAAc,cAAc,KAAG,OAAqD,CAAA;AAE9G,eAAO,MAAM,GAAG,SAAU,MAAM,EAAE,YAAY,UAAU,KAAG,OAAO,CAAC,MAAM,CAA2C,CAAA"}
package/dist/program.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { join } from "node:path";
2
2
  import { resolvePaths } from "@wirecat/cli-base";
3
- import { commandsCommand, configCommand, createProgram as createSharedProgram, outputFor, run as runShared, skillCommand, upgradeCommand, } from "@wirecat/cli-core/cli";
3
+ import { commandsCommand, configCommand, createProgram as createSharedProgram, outputFor, run as runShared, runsCommand, skillCommand, upgradeCommand, } from "@wirecat/cli-core/cli";
4
4
  import { appUpdate } from "@wirecat/cli-core/update";
5
5
  import { doctorCommand } from "./commands/doctor.js";
6
6
  import { mcpCommand } from "./commands/mcp.js";
@@ -30,6 +30,7 @@ export const definition = () => ({
30
30
  install: (argv) => update.runUpdate(argv, {}),
31
31
  })),
32
32
  configCommand(IDENTITY, configuration),
33
+ runsCommand(IDENTITY),
33
34
  ],
34
35
  });
35
36
  export const createProgram = (options) => createSharedProgram(definition(), options);
@@ -1 +1 @@
1
- {"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,IAAI,mBAAmB,EACpC,SAAS,EAIT,GAAG,IAAI,SAAS,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAExD,MAAM,MAAM,GAAG,SAAS,CAAC;IACvB,WAAW,EAAE,OAAO;IACpB,OAAO,EAAE,QAAQ,CAAC,OAAO;IACzB,OAAO,EAAE,QAAQ,CAAC,OAAO;IACzB,KAAK,EAAE,OAAO,IAAI,CAAC,GAAG;IACtB,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,CACjB,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,mBAAmB,CAAC;IAC/G,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,eAAe,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,WAAW;IAC7F,YAAY,EAAE,CAAC,GAAG,QAAQ,CAAC,SAAS,kBAAkB,CAAC;CACxD,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,GAAsB,EAAE,CAAC,CAAC;IAClD,GAAG,EAAE,QAAQ;IACb,aAAa,EAAE,EAAE,eAAe,EAAE,aAAa,CAAC,eAAe,EAAE;IACjE,QAAQ,EAAE,GAAG,EAAE,CAAC;QACd,eAAe,CAAC,QAAQ,CAAC;QACzB,aAAa,EAAE;QACf,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC7B,UAAU,EAAE;QACZ,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC9C,GAAG,SAAS,CAAC,OAAO,CAAC;YACrB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;SAC9C,CAAC,CAAC;QACH,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC;KACvC;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAwB,EAAW,EAAE,CAAC,mBAAmB,CAAC,UAAU,EAAE,EAAE,OAAO,CAAC,CAAA;AAE9G,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,IAAc,EAAE,OAAoB,EAAmB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,CAAA"}
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,IAAI,mBAAmB,EACpC,SAAS,EAIT,GAAG,IAAI,SAAS,EAChB,WAAW,EACX,YAAY,EACZ,cAAc,GACf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAExD,MAAM,MAAM,GAAG,SAAS,CAAC;IACvB,WAAW,EAAE,OAAO;IACpB,OAAO,EAAE,QAAQ,CAAC,OAAO;IACzB,OAAO,EAAE,QAAQ,CAAC,OAAO;IACzB,KAAK,EAAE,OAAO,IAAI,CAAC,GAAG;IACtB,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,CACjB,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,mBAAmB,CAAC;IAC/G,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,eAAe,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,WAAW;IAC7F,YAAY,EAAE,CAAC,GAAG,QAAQ,CAAC,SAAS,kBAAkB,CAAC;CACxD,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,GAAsB,EAAE,CAAC,CAAC;IAClD,GAAG,EAAE,QAAQ;IACb,aAAa,EAAE,EAAE,eAAe,EAAE,aAAa,CAAC,eAAe,EAAE;IACjE,QAAQ,EAAE,GAAG,EAAE,CAAC;QACd,eAAe,CAAC,QAAQ,CAAC;QACzB,aAAa,EAAE;QACf,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC7B,UAAU,EAAE;QACZ,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC9C,GAAG,SAAS,CAAC,OAAO,CAAC;YACrB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;SAC9C,CAAC,CAAC;QACH,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC;QACtC,WAAW,CAAC,QAAQ,CAAC;KACtB;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAwB,EAAW,EAAE,CAAC,mBAAmB,CAAC,UAAU,EAAE,EAAE,OAAO,CAAC,CAAA;AAE9G,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,IAAc,EAAE,OAAoB,EAAmB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,CAAA"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.3";
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.3";
1
+ export const VERSION = "0.1.5";
2
2
  //# sourceMappingURL=version.js.map
package/docs/commands.md CHANGED
@@ -18,18 +18,18 @@ lists the profiles on this computer.
18
18
  | `--json` | machine-readable output: one JSON value on stdout; --json=id,text keeps only those fields. |
19
19
  | `--jsonl` | machine-readable output: one JSON object per line, for streaming and jq. |
20
20
  | `--quiet` | diagnostics off; a failure is still said. |
21
- | `--trace` | the connection's own log lines on stderr — never message content. |
21
+ | `--trace` | the connection's own log lines on stderr — never content. |
22
22
  | `--debug` | on a failure, its whole record — cause and stack — in place of the short line. |
23
23
  | `--timeout <duration>` | give up on the whole command after this — 30s, 2m, 500ms. |
24
24
  | `--offline` | answer from what was recorded and never connect; fails if nothing was. |
25
25
  | `--no-input` | never prompt or open interactive login; piped input remains available. |
26
26
  | `--max-input-bytes <bytes>` | maximum buffered input bytes (default: 16777216). |
27
27
  | `--max-output-bytes <bytes>` | maximum machine output bytes (default: 4194304; 0 disables). |
28
- | `--brief` | found messages as reference, title, time, sender and excerpt only. |
29
- | `--full` | found messages with their whole text and every provider field. |
28
+ | `--brief` | found items as reference, title, time, author and excerpt only. |
29
+ | `--full` | found items with their whole text and every provider field. |
30
30
  | `--dry-run` | preview parsed arguments and permissions before running the action. |
31
31
  | `--yes` | go ahead without the question an ask level puts before a write. |
32
- | `--record` | keep this run — ids and timings, never message content. |
32
+ | `--record` | keep this run — ids and timings, never content. |
33
33
  | `--no-record` | do not keep it, whatever the configuration says. |
34
34
 
35
35
  ## `clidev commands`
@@ -50,7 +50,7 @@ clidev commands info [path]
50
50
 
51
51
  **Returns:** one command contract, or all schemas, error reasons and explicit overrides
52
52
 
53
- **Example:** `clidev commands info messages list`
53
+ **Example:** `clidev commands info commands`
54
54
 
55
55
  ## `clidev doctor`
56
56
 
@@ -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, sender_colors, 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, analysis_provider, analysis_model, analysis_base_url, 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>. |
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, sender_colors, 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, analysis_provider, analysis_model, analysis_base_url, models, read_other_bots, update_check, skill_hint, default_profile, 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>. |
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
  |---|---|
@@ -203,6 +203,67 @@ clidev config unset <setting> [options]
203
203
 
204
204
  **Example:** `clidev personal config unset limit`
205
205
 
206
+ ## `clidev runs`
207
+
208
+ recorded runs — what this tool did, and when
209
+
210
+ ### `clidev runs list`
211
+
212
+ recorded runs, newest first
213
+
214
+ ```sh
215
+ clidev runs list [query] [options]
216
+ ```
217
+
218
+ | Argument | | What it is |
219
+ |---|---|---|
220
+ | `query` | optional | field:value terms that narrow the list — the fields are below. |
221
+
222
+ | Option | What it does |
223
+ |---|---|
224
+ | `--limit <n>` | how many to show. Default: `20`. |
225
+ | `--page <n>` | which page, starting at 1. Default: `1`. |
226
+
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
+
229
+ **Example:** `clidev personal runs list --limit 10`
230
+
231
+ ### `clidev runs show`
232
+
233
+ one run: what it was, how it ended, and every event it logged
234
+
235
+ ```sh
236
+ clidev runs show <run-id> [options]
237
+ ```
238
+
239
+ | Argument | | What it is |
240
+ |---|---|---|
241
+ | `run-id` | required | an id from `clidev runs list`. |
242
+
243
+ | Option | What it does |
244
+ |---|---|
245
+ | `--problems` | only failed responses, warnings and crashes. |
246
+
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
+
249
+ **Example:** `clidev personal runs show run-101`
250
+
251
+ ### `clidev runs path`
252
+
253
+ the directory holding one run
254
+
255
+ ```sh
256
+ clidev runs path <run-id>
257
+ ```
258
+
259
+ | Argument | | What it is |
260
+ |---|---|---|
261
+ | `run-id` | required | an id from `clidev runs list`. |
262
+
263
+ **Returns:** run location — path; every field: clidev commands info runs path
264
+
265
+ **Example:** `clidev personal runs path run-101`
266
+
206
267
  ## Exit codes
207
268
 
208
269
  Branch on the code, not on the text: the text can change, the code does not.
@@ -11,5 +11,13 @@ the first question of the [design interview](skills/design-interview/SKILL.md):
11
11
  | notes | [cli-memo](https://github.com/WireCatLabs/cli-memo) |
12
12
  | mail, calendar, other | none yet: pick the nearest one with the owner |
13
13
 
14
- Until cli-core has a provider registry, a CLI made from this template is not listed as a provider of its own, and
15
- `search` finds its records as messages.
14
+ A messenger CLI sets `messenger: true` in `src/identity.ts`, and `new --category messenger` writes it. Every other
15
+ category keeps the template's `messenger: false`, so the shared help, `commands` and `config` do not speak of chats,
16
+ messages and senders.
17
+
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.
@@ -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
- **37 ✅ · 2 ⛔ · 0 ❌** — 11 commands, 28 options.
10
+ **43 ✅ · 2 ⛔ · 0 ❌** — 14 commands, 31 options.
11
11
 
12
12
  | Command | Option | | Note |
13
13
  |---|---|---|---|
@@ -30,6 +30,12 @@ Measured from the test run (`coverage/argv.jsonl`), not searched for — see
30
30
  | `config unset` | `--defaults` | ✅ | |
31
31
  | `config unset` | `--personal` | ✅ | |
32
32
  | `config unset` | `--bot` | ✅ | |
33
+ | `runs list` | | ✅ | |
34
+ | `runs list` | `--limit` | ✅ | |
35
+ | `runs list` | `--page` | ✅ | |
36
+ | `runs show` | | ✅ | |
37
+ | `runs show` | `--problems` | ✅ | |
38
+ | `runs path` | | ✅ | |
33
39
  | `help` | | ✅ | |
34
40
  | *global* | `--version` | ⛔ | Commander answers before any command runs, so the trace never sees it; `pnpm version:check` checks it |
35
41
  | *global* | `--verbose` | ✅ | |
@@ -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` names the file and the error. Fix it, or move it away and set the values again. |
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
@@ -167,6 +167,8 @@ export const withContentLanguages = (text, languages) => {
167
167
  const line = ` contentLanguages: [${languages.map((code) => JSON.stringify(code)).join(", ")}],\n`;
168
168
  return text.slice(0, end) + line + text.slice(end);
169
169
  };
170
+ // cli-core treats an unset or true `messenger` as a messenger and speaks of chats; the template says false.
171
+ export const withMessenger = (text, category) => category === "messenger" ? text.replace(/^ {2}messenger: false,$/m, " messenger: true,") : text;
170
172
  export const cliReadme = (names) => [
171
173
  `# ${names.command}`,
172
174
  "",
@@ -212,7 +214,7 @@ export const markExecutable = async (cwd, paths) => {
212
214
  export const manualSteps = (names) => [
213
215
  `1. Create the repository ${names.owner}/${names.name} and its main ruleset (cli-base docs/standards/new-cli.md).`,
214
216
  `2. Set npm trusted publishing for ${names.package}, naming release.yml (WC-REL-11), and add any secrets.`,
215
- "3. Pick a monthly cron day no other repository uses: .github/workflows/monthly.yml holds a placeholder CI rejects.",
217
+ "3. Give .github/workflows/monthly.yml a cron minute between 02:17 and 03:20 UTC that no other repository uses.",
216
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.",
217
219
  ];
218
220
  export const runNew = async (cwd, source, args, io) => {
@@ -227,7 +229,7 @@ export const runNew = async (cwd, source, args, io) => {
227
229
  "README.md": () => cliReadme(names),
228
230
  "CHANGELOG.md": () => cliChangelog(names, source),
229
231
  [VERSION_FILE]: (text) => text.replace(/VERSION = "[^"]*"/, 'VERSION = "0.0.0"'),
230
- [IDENTITY_FILE]: (text) => withContentLanguages(text, answers.contentLang ?? []),
232
+ [IDENTITY_FILE]: (text) => withMessenger(withContentLanguages(text, answers.contentLang ?? []), category),
231
233
  "AGENTS.md": (text) => withReference(text, category),
232
234
  };
233
235
  const hashes = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-template",
3
- "version": "0.1.3",
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 --pages && cspell --no-progress --no-summary README.md 'docs/*.md' && rumdl check --config config/wirecat/rumdl.toml README.md CLAUDE.md AGENTS.md docs/*.md docs/dev/*.md && pnpm docs:lint",
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.421.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")
@@ -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 an unreadable config is a field in the answer, never a throw. */
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 file = configFilePath(paths.config)
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
- config,
31
- profiles,
32
- read_only: readOnlyLockOf(IDENTITY, env) ?? null,
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/identity.ts CHANGED
@@ -10,6 +10,7 @@ export const IDENTITY: AppIdentity = {
10
10
  description: "a new WireCat CLI, started from cli-template",
11
11
  version: VERSION,
12
12
  issues: "https://github.com/WireCatLabs/clidev-cli/issues/new",
13
+ messenger: false,
13
14
  }
14
15
 
15
16
  export const SKILL = new URL("../skills/clidev-cli/SKILL.md", import.meta.url)
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 ?? {}) as Record<string, unknown>
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 {
@@ -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
 
@@ -19,26 +20,46 @@ it("lists the required root commands and config", async () => {
19
20
  const { code, out } = await ran(["commands", "--json"])
20
21
  expect(code).toBe(0)
21
22
  const names = (JSON.parse(out) as { commands: { name: string }[] }).commands.map((command) => command.name)
22
- expect(names).toEqual(expect.arrayContaining(["commands", "doctor", "skill", "mcp", "upgrade", "config"]))
23
+ expect(names).toEqual(expect.arrayContaining(["commands", "doctor", "skill", "mcp", "upgrade", "config", "runs"]))
23
24
  })
24
25
 
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: { exists: false }, profiles: [], read_only: null })
29
+ expect(json(out).item).toMatchObject({ config: { found: false, issues: [] }, profiles: [], read_only: null })
29
30
  })
30
31
 
31
- it("doctor reports an unreadable config as a field", async () => {
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"), "not 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
- expect(json(out).item.config).toMatchObject({ exists: true, error: expect.any(String) })
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 () => {
@@ -64,3 +85,21 @@ it("upgrade --check installs nothing when npm cannot be reached", async () => {
64
85
  expect(code).toBe(0)
65
86
  expect(json(out).item).toMatchObject({ current: IDENTITY.version, latest: null, updated: false })
66
87
  })
88
+
89
+ it("keeps a failed command as a run, and runs list, show and path read it back", async () => {
90
+ expect((await ran(["--no-such-flag"])).code).toBe(2)
91
+ const listed = await ran(["runs", "list", "--limit", "5", "--page", "1", "--json"])
92
+ expect(listed.code).toBe(0)
93
+ const failed = (JSON.parse(listed.out) as { items: { run_id: string; status: string }[] }).items.find(
94
+ (run) => run.status === "failed",
95
+ )
96
+ expect(failed).toBeDefined()
97
+ const id = failed?.run_id ?? ""
98
+ const shown = await ran(["runs", "show", id, "--json"])
99
+ expect(shown.code).toBe(0)
100
+ expect(shown.out).toContain(id)
101
+ expect((await ran(["runs", "show", id, "--problems", "--json"])).code).toBe(0)
102
+ const path = await ran(["runs", "path", id])
103
+ expect(path.code).toBe(0)
104
+ expect(path.out).toContain(id)
105
+ })
package/src/program.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  type ProgramOptions,
10
10
  type RunOptions,
11
11
  run as runShared,
12
+ runsCommand,
12
13
  skillCommand,
13
14
  upgradeCommand,
14
15
  } from "@wirecat/cli-core/cli"
@@ -45,6 +46,7 @@ export const definition = (): ProgramDefinition => ({
45
46
  install: (argv) => update.runUpdate(argv, {}),
46
47
  })),
47
48
  configCommand(IDENTITY, configuration),
49
+ runsCommand(IDENTITY),
48
50
  ],
49
51
  })
50
52
 
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.1.3"
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: ["src/**/*.test.ts", "src/testing/**", "src/bin/**"],
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
- }
@@ -1,5 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": { "noEmit": true, "rootDir": "." },
4
- "include": ["src/**/*.ts", "test/**/*.ts"]
5
- }