@wirecat/cli-template 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/.github/dependabot.yml +23 -0
  2. package/.github/workflows/changelog-guard.yml +12 -0
  3. package/.github/workflows/ci.yml +88 -0
  4. package/.github/workflows/main.yml +17 -0
  5. package/.github/workflows/monthly.yml +28 -0
  6. package/.github/workflows/pr.yml +19 -0
  7. package/.github/workflows/release.yml +191 -0
  8. package/.github/workflows/standards.yml +51 -0
  9. package/.gitignore +42 -0
  10. package/.gitleaks.toml +5 -0
  11. package/.markdownlint-cli2.jsonc +7 -0
  12. package/.vale.ini +5 -0
  13. package/AGENTS.md +27 -0
  14. package/CHANGELOG.md +19 -0
  15. package/CLAUDE.md +17 -0
  16. package/LICENSE +204 -0
  17. package/README.md +52 -2
  18. package/SECURITY.md +15 -0
  19. package/bin/release +139 -0
  20. package/biome.json +6 -0
  21. package/config/wirecat/LICENSE +204 -0
  22. package/config/wirecat/biome.base.json +140 -0
  23. package/config/wirecat/check.mjs +41 -0
  24. package/config/wirecat/cspell.json +80 -0
  25. package/config/wirecat/lefthook.yml +26 -0
  26. package/config/wirecat/manifest.json +34 -0
  27. package/config/wirecat/markdownlint.json +10 -0
  28. package/config/wirecat/no-deep-wirecat-import.grit +15 -0
  29. package/config/wirecat/no-exec-interpolation.grit +17 -0
  30. package/config/wirecat/no-new-function.grit +8 -0
  31. package/config/wirecat/no-shell-true.grit +8 -0
  32. package/config/wirecat/no-unbounded-spread.grit +10 -0
  33. package/config/wirecat/release-lock-info.mjs +71 -0
  34. package/config/wirecat/release-lock.sh +34 -0
  35. package/config/wirecat/rumdl.toml +9 -0
  36. package/contracts/help.snap +16 -0
  37. package/contracts/mcp-tools.json +922 -0
  38. package/contracts/no-leak.json +3 -0
  39. package/contracts/plan.json +50 -0
  40. package/cspell.json +10 -0
  41. package/dist/bin/app.d.ts +3 -0
  42. package/dist/bin/app.d.ts.map +1 -0
  43. package/dist/bin/app.js +14 -0
  44. package/dist/bin/app.js.map +1 -0
  45. package/dist/commands/doctor.d.ts +23 -0
  46. package/dist/commands/doctor.d.ts.map +1 -0
  47. package/dist/commands/doctor.js +44 -0
  48. package/dist/commands/doctor.js.map +1 -0
  49. package/dist/commands/mcp.d.ts +3 -0
  50. package/dist/commands/mcp.d.ts.map +1 -0
  51. package/dist/commands/mcp.js +36 -0
  52. package/dist/commands/mcp.js.map +1 -0
  53. package/dist/config.d.ts +3 -0
  54. package/dist/config.d.ts.map +1 -0
  55. package/dist/config.js +4 -0
  56. package/dist/config.js.map +1 -0
  57. package/dist/identity.d.ts +5 -0
  58. package/dist/identity.d.ts.map +1 -0
  59. package/dist/identity.js +12 -0
  60. package/dist/identity.js.map +1 -0
  61. package/dist/index.d.ts +3 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +3 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/mcp.d.ts +19 -0
  66. package/dist/mcp.d.ts.map +1 -0
  67. package/dist/mcp.js +62 -0
  68. package/dist/mcp.js.map +1 -0
  69. package/dist/program.d.ts +6 -0
  70. package/dist/program.d.ts.map +1 -0
  71. package/dist/program.js +37 -0
  72. package/dist/program.js.map +1 -0
  73. package/dist/version.d.ts +2 -0
  74. package/dist/version.d.ts.map +1 -0
  75. package/dist/version.js +2 -0
  76. package/dist/version.js.map +1 -0
  77. package/docs/commands.md +227 -0
  78. package/docs/configuration.md +18 -0
  79. package/docs/dev/DESIGN.md +13 -0
  80. package/docs/dev/TESTING.md +16 -0
  81. package/docs/dev/design-questions.md +24 -0
  82. package/docs/dev/reference-clis.md +15 -0
  83. package/docs/dev/skills/design-interview/SKILL.md +34 -0
  84. package/docs/dev/skills/release/SKILL.md +24 -0
  85. package/docs/dev/test-matrix.md +52 -0
  86. package/docs/index.md +7 -0
  87. package/docs/installation.md +17 -0
  88. package/docs/meta.json +18 -0
  89. package/docs/security.md +8 -0
  90. package/docs/troubleshooting.md +14 -0
  91. package/docs/usage.md +31 -0
  92. package/kit/README.md +123 -0
  93. package/kit/dist/interview.js +170 -0
  94. package/kit/dist/main.js +70 -0
  95. package/kit/dist/manifest.js +111 -0
  96. package/kit/dist/new.js +249 -0
  97. package/kit/dist/rename.js +35 -0
  98. package/kit/dist/source.js +74 -0
  99. package/kit/dist/upgrade.js +223 -0
  100. package/kit/tsconfig.json +17 -0
  101. package/kit/tsconfig.test.json +5 -0
  102. package/lefthook.yml +2 -0
  103. package/lint/baseline/.gitkeep +0 -0
  104. package/lint/overrides.json +1 -0
  105. package/package.json +105 -4
  106. package/pnpm-workspace.yaml +6 -0
  107. package/scripts/audit.sh +22 -0
  108. package/scripts/command-program.ts +1 -0
  109. package/scripts/commands.ts +39 -0
  110. package/scripts/smoke.ts +56 -0
  111. package/scripts/test-matrix-untested.ts +12 -0
  112. package/skills/app-cli/SKILL.md +21 -0
  113. package/src/bin/app.ts +13 -0
  114. package/src/commands/doctor.ts +45 -0
  115. package/src/commands/mcp.ts +38 -0
  116. package/src/config.ts +4 -0
  117. package/src/identity.ts +15 -0
  118. package/src/index.ts +2 -0
  119. package/src/mcp.test.ts +84 -0
  120. package/src/mcp.ts +82 -0
  121. package/src/program.test.ts +66 -0
  122. package/src/program.ts +53 -0
  123. package/src/roots.test.ts +80 -0
  124. package/src/testing/argv-log.ts +1 -0
  125. package/src/testing/sandbox.ts +25 -0
  126. package/src/version.ts +1 -0
  127. package/template.json +40 -0
  128. package/tsconfig.json +9 -0
  129. package/tsconfig.scripts.json +1 -0
  130. package/tsconfig.test.json +1 -0
  131. package/vitest.config.ts +19 -0
@@ -0,0 +1,227 @@
1
+ <!-- Generated from the command tree by pnpm generate. Do not edit. -->
2
+
3
+ # Commands
4
+
5
+ Every command, option and exit code, generated from the program itself. For the same list as JSON, run
6
+ `app commands --json`.
7
+
8
+ **The first word is the profile**, a name you choose: `app personal doctor`. `APP_PROFILE`
9
+ does the same for a whole shell session. No profile is assumed: a command that uses an account stops without one and
10
+ lists the profiles on this computer.
11
+
12
+ ## Options for every command
13
+
14
+ | Option | What it does |
15
+ |---|---|
16
+ | `-V, --version` | output the version number. |
17
+ | `-v, --verbose` | more detail in what is shown: -v ids, -vv everything we know. Default: `0`. |
18
+ | `--json` | machine-readable output: one JSON value on stdout; --json=id,text keeps only those fields. |
19
+ | `--jsonl` | machine-readable output: one JSON object per line, for streaming and jq. |
20
+ | `--quiet` | diagnostics off; a failure is still said. |
21
+ | `--trace` | the connection's own log lines on stderr — never message content. |
22
+ | `--debug` | on a failure, its whole record — cause and stack — in place of the short line. |
23
+ | `--timeout <duration>` | give up on the whole command after this — 30s, 2m, 500ms. |
24
+ | `--offline` | answer from what was recorded and never connect; fails if nothing was. |
25
+ | `--no-input` | never prompt or open interactive login; piped input remains available. |
26
+ | `--max-input-bytes <bytes>` | maximum buffered input bytes (default: 16777216). |
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. |
30
+ | `--dry-run` | preview parsed arguments and permissions before running the action. |
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. |
33
+ | `--no-record` | do not keep it, whatever the configuration says. |
34
+
35
+ ## `app commands`
36
+
37
+ every command by section; --json prints its contract, one path per call
38
+
39
+ ### `app commands info`
40
+
41
+ one command contract, or all schemas, error reasons and explicit overrides
42
+
43
+ ```sh
44
+ app commands info [path]
45
+ ```
46
+
47
+ | Argument | | What it is |
48
+ |---|---|---|
49
+ | `path` | optional | one command path; without one, export schemas, error reasons and explicit overrides. |
50
+
51
+ **Returns:** one command contract, or all schemas, error reasons and explicit overrides
52
+
53
+ **Example:** `app commands info messages list`
54
+
55
+ ## `app doctor`
56
+
57
+ check this installation: version, paths, config, profiles; no keyring or network
58
+
59
+ ```sh
60
+ app doctor
61
+ ```
62
+
63
+ **Returns:** check this installation: version, paths, config, profiles; no keyring or network
64
+
65
+ **Example:** `app personal doctor`, `app personal doctor --json`
66
+
67
+ ## `app skill`
68
+
69
+ the instructions an agent is given for this tool
70
+
71
+ ### `app skill show`
72
+
73
+ print SKILL.md — `app skill install` puts it where Claude Code, Codex and Gemini CLI look for it
74
+
75
+ ```sh
76
+ app skill show [name]
77
+ ```
78
+
79
+ | Argument | | What it is |
80
+ |---|---|---|
81
+ | `name` | optional | one of the skills shipped for a task: follow-ups, link-conversations. |
82
+
83
+ **Returns:** print the tool's instructions for AI agents
84
+
85
+ **Example:** `app skill show`, `app skill show follow-ups`
86
+
87
+ ### `app skill install`
88
+
89
+ write SKILL.md to \~/.claude/skills/app-cli/ (Claude Code) and \~/.agents/skills/app-cli/ (Codex, Gemini CLI)
90
+
91
+ **Changes something on this computer only.**
92
+
93
+ ```sh
94
+ app skill install [options]
95
+ ```
96
+
97
+ | Option | What it does |
98
+ |---|---|
99
+ | `--for <agents>` | which agents to install for. One of: `claude`, `agents`, `all`. Default: `all`. |
100
+
101
+ **Returns:** install the tool's instructions for AI agents
102
+
103
+ **Example:** `app skill install --for agents`
104
+
105
+ ## `app mcp`
106
+
107
+ serve this profile's commands to an agent over MCP, on stdin and stdout, writes included; the profile's permissions decide — `claude mcp add app -- app personal mcp`
108
+
109
+ **Changes something in the service.**
110
+
111
+ ```sh
112
+ app mcp
113
+ ```
114
+
115
+ **Returns:** serve this profile's commands to an AI agent
116
+
117
+ **Example:** `app personal mcp`
118
+
119
+ ## `app upgrade`
120
+
121
+ upgrade app with the package manager that installed it; --check only looks
122
+
123
+ **Changes something on this computer only.**
124
+
125
+ ```sh
126
+ app upgrade [options]
127
+ ```
128
+
129
+ | Option | What it does |
130
+ |---|---|
131
+ | `--check` | say whether a newer version exists, and install nothing. |
132
+
133
+ **Returns:** upgrade app with the package manager that installed it; --check only looks
134
+
135
+ **Example:** `app upgrade --check`, `app upgrade`
136
+
137
+ ## `app config`
138
+
139
+ the settings in force, and where each one came from
140
+
141
+ ### `app config show`
142
+
143
+ the profile, the profiles that exist, and each setting with where it came from
144
+
145
+ ```sh
146
+ app config show [options]
147
+ ```
148
+
149
+ | Option | What it does |
150
+ |---|---|
151
+ | `--bot` | the settings a bot command on this profile gets, rather than the personal account's. |
152
+
153
+ **Returns:** the profile, the profiles that exist, and each setting with where it came from
154
+
155
+ **Example:** `app personal config show`
156
+
157
+ ### `app config set`
158
+
159
+ save a setting to the configuration file
160
+
161
+ **Changes something on this computer only.**
162
+
163
+ ```sh
164
+ app config set <setting> <value> [options]
165
+ ```
166
+
167
+ | Argument | | What it is |
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>. |
170
+ | `value` | required | a number, true or false, or for allow a list like send,reaction; for a group tg:work,max:Alice,email:me@x. |
171
+
172
+ | Option | What it does |
173
+ |---|---|
174
+ | `--defaults` | change what every profile gets, rather than this profile. |
175
+ | `--personal` | only for personal accounts — the personal section of the file. |
176
+ | `--bot` | only for bots — the bot section of the file. |
177
+
178
+ **Returns:** save a setting to the configuration file
179
+
180
+ **Example:** `app personal config set limit 20`
181
+
182
+ ### `app config unset`
183
+
184
+ remove a setting from the configuration file
185
+
186
+ **Changes something on this computer only.**
187
+
188
+ ```sh
189
+ app config unset <setting> [options]
190
+ ```
191
+
192
+ | Argument | | What it is |
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>. |
195
+
196
+ | Option | What it does |
197
+ |---|---|
198
+ | `--defaults` | change what every profile gets, rather than this profile. |
199
+ | `--personal` | only for personal accounts — the personal section of the file. |
200
+ | `--bot` | only for bots — the bot section of the file. |
201
+
202
+ **Returns:** remove a setting from the configuration file
203
+
204
+ **Example:** `app personal config unset limit`
205
+
206
+ ## Exit codes
207
+
208
+ Branch on the code, not on the text: the text can change, the code does not.
209
+
210
+ | Code | When |
211
+ |---|---|
212
+ | `0` | it worked |
213
+ | `2` | `validation_error` |
214
+ | `3` | `configuration_error` |
215
+ | `4` | `authentication_error` |
216
+ | `5` | `permission_error` |
217
+ | `6` | `not_found` |
218
+ | `7` | `confirmation_required` |
219
+ | `8` | `rate_limited` |
220
+ | `9` | `timeout` |
221
+ | `10` | `network_error` |
222
+ | `11` | `provider_error` |
223
+ | `12` | `provider_unavailable` |
224
+ | `13` | `invalid_response` |
225
+ | `14` | `outcome_unknown` |
226
+ | `130` | `cancelled` |
227
+ | `1` | anything else |
@@ -0,0 +1,18 @@
1
+ # Settings
2
+
3
+ Settings live in one file, `config.json`, in the folder `app personal doctor` shows. Each profile has its own part,
4
+ and `defaults` apply to every profile. This page lists how to see and change them.
5
+
6
+ ```sh
7
+ app personal config show
8
+ app personal config set limit 50
9
+ app personal config unset limit
10
+ ```
11
+
12
+ `config show` lists each setting and where it came from: the file, a variable or the default.
13
+
14
+ ## Permissions
15
+
16
+ `permissions` decides what a command may do for this profile: `allow`, `ask`, `readonly` or `deny`, per permission
17
+ key. Over MCP there is no one to ask, so `ask` lets the command run. `APP_READ_ONLY=1` refuses every write, whatever
18
+ the permissions say.
@@ -0,0 +1,13 @@
1
+ # Design
2
+
3
+ `npx @wirecat/cli-template new` writes this page from the answers to the
4
+ [design interview](skills/design-interview/SKILL.md). It is empty in the template: ask the interview and write the
5
+ answers here before building anything.
6
+
7
+ ## Answers
8
+
9
+ Not answered yet.
10
+
11
+ ## Open questions for the owner
12
+
13
+ None yet.
@@ -0,0 +1,16 @@
1
+ # Testing
2
+
3
+ How the tests run, and what they never touch. The rules are the handbook's
4
+ [Testing page](../../node_modules/@wirecat/cli-base/docs/standards/testing.md).
5
+
6
+ - `pnpm test` runs vitest. `@wirecat/cli-base/testing/setup` gives each test file its own temporary home folder, a
7
+ keyring that throws and no network; `src/testing/sandbox.ts` points this CLI's config, state, cache and the shared
8
+ store into that folder.
9
+ - `pnpm test:coverage` adds the coverage floor in `vitest.config.ts`.
10
+ - Each run logs the command lines the tests drove (`coverage/argv.jsonl`). `pnpm test:matrix` turns that log into
11
+ [test-matrix.md](test-matrix.md); a command or option no test drives needs a reason in
12
+ `scripts/test-matrix-untested.ts`.
13
+ - `pnpm contracts:check` runs the built CLI against `contracts/plan.json`: help, discovery, errors and the MCP tool
14
+ list. `cli-contract contracts/plan.json --update` rewrites the snapshots; review the diff.
15
+ - `pnpm smoke` installs the packed package into a temporary folder and runs it offline.
16
+ - `pnpm audit:commands` audits the command tree beside the latest published WireCat CLIs.
@@ -0,0 +1,24 @@
1
+ # Further design questions
2
+
3
+ Ask these only when the reference CLI does not already answer them, after the seven questions of the
4
+ [design interview](skills/design-interview/SKILL.md). Write each answer into [DESIGN.md](DESIGN.md).
5
+
6
+ ## Storing, indexing, syncing and deleting
7
+
8
+ 1. Which of the service's records are kept locally, and which core entity does each become (people, events,
9
+ documents, files, messages, meetings)? (WC-DATA-1, WC-DATA-2)
10
+ 2. How does it sync: everything each time, from a cursor, or by change token? On demand, on a schedule, or kept
11
+ running?
12
+ 3. When the service deletes something, what happens here? Proposed: mark it deleted, keep it, never wipe. (WC-DATA-7)
13
+ 4. Can the person delete local data themselves, and how much: one record, one account, everything?
14
+
15
+ ## Search
16
+
17
+ 1. What should a search find: titles only, the full text, names of people?
18
+ 2. Should `search all` and `memo context <person>` show its records?
19
+ 3. What must work without a network?
20
+
21
+ ## Security
22
+
23
+ 1. Where are the secrets kept? Proposed: the system keyring. (WC-SECR)
24
+ 2. Which writes need a permission, and which are never allowed whatever the permissions? (WC-PERM)
@@ -0,0 +1,15 @@
1
+ # Reference CLIs
2
+
3
+ The template has no sample code. The agent that builds a new CLI learns from the WireCat CLI closest to it, picked by
4
+ the first question of the [design interview](skills/design-interview/SKILL.md):
5
+
6
+ | Category | Learn from |
7
+ | --- | --- |
8
+ | messenger | [whatsapp-cli](https://github.com/WireCatLabs/whatsapp-cli), the handbook's worked example; then [tg-cli](https://github.com/WireCatLabs/tg-cli) and [max-cli](https://github.com/WireCatLabs/max-cli) |
9
+ | files and documents | [drive-cli](https://github.com/WireCatLabs/drive-cli) |
10
+ | meetings | [zoom-cli](https://github.com/WireCatLabs/zoom-cli) |
11
+ | notes | [cli-memo](https://github.com/WireCatLabs/cli-memo) |
12
+ | mail, calendar, other | none yet: pick the nearest one with the owner |
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.
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: design-interview
3
+ description: Ask the owner the seven design questions for a new WireCat CLI before building anything, then the further questions the reference CLI does not answer, and write the answers to docs/dev/DESIGN.md. Use when DESIGN.md has no answers, when asked to design or start the CLI, «спроектировать», «начать CLI».
4
+ ---
5
+
6
+ # Design interview
7
+
8
+ Ask before you build. Each question is multiple choice, and every one also takes "Other", typed by hand. Ask them in
9
+ this order, one at a time, and write each answer down as it comes. `npx @wirecat/cli-template new` asks the same
10
+ questions in a terminal and takes them as flags without one; when `DESIGN.md` already has the answers, read them and
11
+ go to the further questions.
12
+
13
+ 1. **What kind of tool is it?** messenger · mail · notes · files and documents · calendar · meetings · other. It
14
+ picks the reference CLI (see [Reference CLIs](../../reference-clis.md)).
15
+ 2. **Which languages is the content in?** Any languages, several allowed: the messages, documents or transcripts the
16
+ CLI handles. The answer picks the language-dependent parts: search stemming, the embedding and speech models.
17
+ 3. **How does a person sign in?** OAuth in the browser · an API token or key · a session (phone code, QR) · none ·
18
+ other.
19
+ 4. **What does the service offer to build on?** an official API with an official client library · official API
20
+ documentation only · nothing official · other.
21
+ 5. **How do people filter what they find?** by time · by person · by container (chat, folder, calendar) · by words in
22
+ the text · by status or label · other; several allowed.
23
+ 6. **What must never reach the store, a log or the output?** tokens and passwords · one-time codes and passcodes ·
24
+ links that carry a secret · the full content, keep only metadata · other; several allowed.
25
+ 7. **What untrusted input arrives?** messages from other people · files and attachments · links · names and titles set
26
+ by others · none · other; several allowed.
27
+
28
+ Then read the reference CLI and ask the [further questions](../../design-questions.md) it does not answer.
29
+
30
+ ## What to write
31
+
32
+ `docs/dev/DESIGN.md`: the seven answers, the reference CLI, the further answers, and a list of open questions for
33
+ the owner. Name the reference CLI in `AGENTS.md`, between the `cli-template:reference-cli` markers. Start building
34
+ only after the owner has read `DESIGN.md`.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: release
3
+ description: Prepare a release of this CLI for the owner to publish — run release:check, write the changelog under the handbook's headings, check the docs against what changed, raise the version in a pull request; the owner runs bin/release. Use when asked to release, prepare a release, cut a version, «выпуск», «выпустить версию».
4
+ ---
5
+
6
+ # Release
7
+
8
+ The rules are the handbook's [Releases page](../../../../node_modules/@wirecat/cli-base/docs/standards/releases.md)
9
+ (`WC-REL-<n>`), which wins over this skill. An agent prepares the release; the owner merges the release pull request
10
+ and runs `bin/release`.
11
+
12
+ 1. **What changed.** In a worktree off `origin/main`: `git log --oneline v<last version>..origin/main`. The
13
+ `docs/commands.md` diff shows what a cli-core or cli-base release changed in the commands.
14
+ 2. **The checks.** `pnpm release:check`. Before the version is raised, only the changelog's `## Unreleased` may be
15
+ left; report anything else and fix it in the release pull request.
16
+ 3. **The changelog.** Write `## Unreleased` under the handbook's headings, each at most once: `Added`,
17
+ `Changed — may break callers`, `Fixed`, `Security`, `Removed` (`WC-DOC-14` to `WC-DOC-18`). No internal ids.
18
+ 4. **The docs.** `README.md`, `docs/*.md` and `skills/app-cli/SKILL.md` state what the code does now (`WC-DOC-3`).
19
+ `pnpm generate` and `pnpm test:matrix` rewrite the generated pages (`WC-DOC-4`).
20
+ 5. **The version.** `npm view @wirecat/app-cli version` is what is published. Set the next version in
21
+ `package.json`, run `pnpm version:sync`, date the section as `## <version> — DD.MM.YYYY`, run
22
+ `pnpm release:check` again, and open the pull request `chore(release): <version>`.
23
+ 6. **The owner** merges it and runs `bin/release` on a clean `main`. `bin/release --local` publishes from this
24
+ computer when GitHub Actions cannot, with the owner's own npm login or `NPM_TOKEN`.
@@ -0,0 +1,52 @@
1
+ <!-- Generated by cli-dev test-matrix from the command tree and a test run. Do not edit; `pnpm test:matrix`. -->
2
+
3
+ # Test matrix
4
+
5
+ Every command and option of `app`, and whether an offline test drives it through the CLI. ✅ a test
6
+ ran it · ⛔ not tested offline, with the reason and where it is checked instead · ❌ nothing.
7
+ Measured from the test run (`coverage/argv.jsonl`), not searched for — see
8
+ [TESTING.md](TESTING.md) for how, and for the states and failures that cut across commands.
9
+
10
+ **37 ✅ · 2 ⛔ · 0 ❌** — 11 commands, 28 options.
11
+
12
+ | Command | Option | | Note |
13
+ |---|---|---|---|
14
+ | `commands` | | ✅ | |
15
+ | `commands info` | | ✅ | |
16
+ | `doctor` | | ✅ | |
17
+ | `skill show` | | ✅ | |
18
+ | `skill install` | | ✅ | |
19
+ | `skill install` | `--for` | ✅ | |
20
+ | `mcp` | | ✅ | |
21
+ | `upgrade` | | ✅ | |
22
+ | `upgrade` | `--check` | ✅ | |
23
+ | `config show` | | ✅ | |
24
+ | `config show` | `--bot` | ✅ | |
25
+ | `config set` | | ✅ | |
26
+ | `config set` | `--defaults` | ✅ | |
27
+ | `config set` | `--personal` | ✅ | |
28
+ | `config set` | `--bot` | ✅ | |
29
+ | `config unset` | | ✅ | |
30
+ | `config unset` | `--defaults` | ✅ | |
31
+ | `config unset` | `--personal` | ✅ | |
32
+ | `config unset` | `--bot` | ✅ | |
33
+ | `help` | | ✅ | |
34
+ | *global* | `--version` | ⛔ | Commander answers before any command runs, so the trace never sees it; `pnpm version:check` checks it |
35
+ | *global* | `--verbose` | ✅ | |
36
+ | *global* | `--json` | ✅ | |
37
+ | *global* | `--jsonl` | ✅ | |
38
+ | *global* | `--quiet` | ✅ | |
39
+ | *global* | `--trace` | ✅ | |
40
+ | *global* | `--debug` | ✅ | |
41
+ | *global* | `--timeout` | ✅ | |
42
+ | *global* | `--offline` | ✅ | |
43
+ | *global* | `--no-input` | ✅ | |
44
+ | *global* | `--max-input-bytes` | ✅ | |
45
+ | *global* | `--max-output-bytes` | ✅ | |
46
+ | *global* | `--brief` | ✅ | |
47
+ | *global* | `--full` | ✅ | |
48
+ | *global* | `--dry-run` | ✅ | |
49
+ | *global* | `--yes` | ✅ | |
50
+ | *global* | `--record` | ✅ | |
51
+ | *global* | `--no-record` | ✅ | |
52
+ | *global* | `--help-all` | ⛔ | Commander answers before any command runs, so the trace never sees it; contracts/help.snap covers help |
package/docs/index.md ADDED
@@ -0,0 +1,7 @@
1
+ # app
2
+
3
+ `app` brings a service to the terminal, to scripts and to AI agents. This page is where a reader starts: say what
4
+ the tool does, who it is for, and the first command to run. It will say that once the tool connects to its service.
5
+
6
+ Today it has the commands every WireCat CLI shares: `doctor`, `commands`, `config`, `skill`, `mcp` and `upgrade`.
7
+ Start with [installation](installation.md), then [usage](usage.md).
@@ -0,0 +1,17 @@
1
+ # Install
2
+
3
+ Install `app` once on each computer where you or an agent use it. You need Node.js 22.16 or later, or 24.
4
+
5
+ ```sh
6
+ npm install -g @wirecat/app-cli
7
+ app --version
8
+ ```
9
+
10
+ Check the installation, then pick a profile name for your account, such as `personal`:
11
+
12
+ ```sh
13
+ app personal doctor
14
+ ```
15
+
16
+ `doctor` needs no network and opens no keyring. It shows the version, where settings live and the profiles that
17
+ exist. Next: [use it](usage.md).
package/docs/meta.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "title": "app",
3
+ "pages": [
4
+ "---Start---",
5
+ "index",
6
+ "installation",
7
+ "---Guides---",
8
+ "usage",
9
+ "---Reference---",
10
+ "commands",
11
+ "configuration",
12
+ "---When something goes wrong---",
13
+ "troubleshooting",
14
+ "security",
15
+ "---Project---",
16
+ "changelog"
17
+ ]
18
+ }
@@ -0,0 +1,8 @@
1
+ # Security
2
+
3
+ What WireCat CLIs share — the keyring, permissions, MCP and what never reaches a log — is on the
4
+ [shared security page](https://wirecat.dev/en/docs/security). This page keeps only what is particular to `app`.
5
+
6
+ `app` keeps its settings in `config.json` and its state in the folders `app personal doctor` shows. It sends nothing
7
+ to any service yet. Report a vulnerability as [SECURITY.md](https://github.com/WireCatLabs/app-cli/blob/main/SECURITY.md)
8
+ says.
@@ -0,0 +1,14 @@
1
+ # When something goes wrong
2
+
3
+ Start with the symptom, run the check, then retry the command. Keep the error code and the profile name for a
4
+ support request.
5
+
6
+ | Symptom | What to do |
7
+ | --- | --- |
8
+ | Not sure what is set up | Run `app personal doctor`. It reads no keyring and uses no network. |
9
+ | `app` is not found | Open a new terminal, and check that npm's global folder is on `PATH`. |
10
+ | "name a profile" | Put the profile first, as in `app personal config show`, or set `APP_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. |
12
+ | A write is refused | The profile's permissions or `APP_READ_ONLY` stop it; see [settings](configuration.md#permissions). |
13
+
14
+ Never paste a token, a password or private content into a support request.
package/docs/usage.md ADDED
@@ -0,0 +1,31 @@
1
+ # Use it
2
+
3
+ This page explains how a command is built, and how an agent uses the tool. Read it before your first command.
4
+
5
+ ## The profile comes first
6
+
7
+ A profile is a name you choose for one account. It is the first word of every command that uses an account:
8
+
9
+ ```sh
10
+ app personal config show
11
+ ```
12
+
13
+ `APP_PROFILE=personal` does the same for one shell session. No profile is ever assumed, not even the only one: a
14
+ command without one stops with exit code 2 and lists the profiles on this computer.
15
+
16
+ ## Output for scripts
17
+
18
+ Every command takes `--json`, and prints one JSON value on stdout. Errors are JSON on stderr, with a code to branch
19
+ on. The [command reference](commands.md) lists the codes.
20
+
21
+ ## Agents
22
+
23
+ `app skill install` puts the tool's skill where Claude Code, Codex and Gemini CLI look for it. `app personal mcp`
24
+ serves the profile's commands over MCP, on stdin and stdout:
25
+
26
+ ```sh
27
+ claude mcp add app -- app personal mcp
28
+ ```
29
+
30
+ The server offers every command, the ones that write included. The profile's permissions decide what runs; see
31
+ [settings](configuration.md#permissions).
package/kit/README.md ADDED
@@ -0,0 +1,123 @@
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
+ The content languages go only into `DESIGN.md`. The file also holds the speech-model order a messenger definition
39
+ should use, and the warnings for languages that have no stemmer or speech model in cli-core. `new` writes no search
40
+ or model setting.
41
+
42
+ ## `upgrade`
43
+
44
+ Run it in a CLI made from the template, with a clean git working tree. For each template-owned file that the
45
+ template changed since the recorded version, it asks one of four things:
46
+
47
+ - **apply** merges the change with `git merge-file`. The base is the recorded version, downloaded with `npm pack`.
48
+ "Ours" is the CLI's file and "theirs" is this version. Both template versions are renamed first, so the command
49
+ word never conflicts.
50
+ - **replace** takes the template's file as it is.
51
+ - **skip** keeps the CLI's file. It is asked again only when the template changes that file again.
52
+ - **diff** shows the template's change.
53
+
54
+ Without a terminal, `--apply`, `--replace` or `--skip` sets the answer for every file. `--dry-run` lists what each
55
+ file would get and writes nothing. Seeds and `package.json` are never touched.
56
+
57
+ A conflict leaves git markers, and `template.json` keeps the new state under `pending`. Resolve the markers and run
58
+ `upgrade` again: it checks that the markers are gone and then moves to the new version.
59
+
60
+ ## `template.json` in this repository
61
+
62
+ ```json
63
+ {
64
+ "files": [".github/", "bin/release", "AGENTS.md", "docs/meta.json"],
65
+ "seeds": ["src/", "README.md", "docs/", "config/wirecat/"],
66
+ "placeholders": {
67
+ "command": "app", "name": "app-cli", "package": "@wirecat/app-cli", "repository": "WireCatLabs/app-cli"
68
+ }
69
+ }
70
+ ```
71
+
72
+ - `files` lists the template-owned files that `upgrade` replays. `seeds` lists the files copied once and never
73
+ touched again; `config/wirecat/` goes there, because community's installer owns it. An entry ending in `/` is a
74
+ folder. The most specific entry wins: an exact path beats a folder, a deeper folder beats a shallower one, and a
75
+ seed wins a tie. Only listed files are copied. A listed file missing from the package stops `new`.
76
+ - `template.json` and `kit/` are never copied, whatever the lists say. The kit's tests run inside `pnpm test`, so
77
+ the configs name `kit/`: `new` and `upgrade` drop every `"kit/…"` entry from `vitest.config.ts`, `biome.json`,
78
+ `cspell.json`, `tsconfig*.json` and `.markdownlint-cli2.jsonc`. Do not add a kit-only workflow: `.github/` is
79
+ template-owned and would carry it into every new CLI.
80
+ - `README.md` and `CHANGELOG.md` describe the template, so `new` writes short ones for the new CLI.
81
+ - `docs/dev/DESIGN.md` is always a seed: it holds the CLI's interview answers.
82
+ - `package.json` is always a seed. `new` rewrites it: `name` becomes `--package`, `version` becomes `0.0.0`, and
83
+ `repository`, `homepage` and `bugs` point at `<owner>/<name>`. It also drops every `bin`, `files` entry and script
84
+ that points into `kit/`, and the `&& pnpm kit:…` parts of other scripts. `src/version.ts` gets `VERSION = "0.0.0"`.
85
+ - `placeholders` is optional; the defaults are shown above. The command word is replaced as a whole word, so `app`
86
+ changes but `application` does not. Its upper-case form changes too: `APP_HOME` becomes `CAL_HOME`. Followed by a
87
+ colon and a space it is a key and stays, so cli-core's `app: APP` becomes `app: CAL`. `name` (`app-cli`) is the repository
88
+ name: `skills/app-cli/` becomes `skills/cal-cli/`. The word `app` cannot appear in prose for any other meaning.
89
+ Paths are renamed the same way: `src/bin/app.ts` becomes `src/bin/cal.ts`.
90
+ - The version is `package.json`'s, never a second copy.
91
+ - `AGENTS.md` can hold the block that `new` fills with the reference CLI:
92
+
93
+ ```md
94
+ <!-- cli-template:reference-cli:start -->
95
+ <!-- cli-template:reference-cli:end -->
96
+ ```
97
+
98
+ Without the block, `new` appends one.
99
+
100
+ ## `template.json` in a CLI made from it
101
+
102
+ `new` writes this shape and `upgrade` keeps it current:
103
+
104
+ ```json
105
+ {
106
+ "template": "@wirecat/cli-template",
107
+ "version": "0.1.0",
108
+ "names": { "name": "cal-cli", "command": "cal", "package": "@wirecat/cal-cli", "owner": "WireCatLabs" },
109
+ "files": { ".github/workflows/ci.yml": "<sha256 of the renamed template file at this version>" },
110
+ "seeds": ["src/", "README.md", "docs/", "config/wirecat/"]
111
+ }
112
+ ```
113
+
114
+ ## What the package needs from the repository
115
+
116
+ - `"bin": { "cli-template": "kit/dist/main.js" }`, and `kit/dist` in `files`. No ignore file inside `kit/`: npm
117
+ applies it and drops `kit/dist` from the package. npm also never packs `pnpm-lock.yaml`, so it is not a seed; the
118
+ new CLI makes its own on `pnpm install`.
119
+ - Every template-owned file and seed in `files`. npm drops a nested `.gitignore` and any `.npmrc`; the test in
120
+ `kit/test/package.test.ts` runs the bin from a real tarball to catch that.
121
+ - `kit:build` (`tsc -p kit`) run by `build`, because `bin/release` publishes with `--ignore-scripts`. Node does not
122
+ strip types under `node_modules`, so the bin must be JavaScript.
123
+ - `kit/test/**/*.test.ts` in vitest's `include`; `kit/dist` is ignored by the root `**/dist` rules of Biome and git.