@wirecat/cli-template 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +2 -1
- package/.github/workflows/main.yml +2 -1
- package/.github/workflows/monthly.yml +1 -1
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +77 -0
- package/README.md +34 -6
- package/bin/release +68 -6
- package/biome.json +23 -1
- package/config/parity-groups.json +6 -0
- package/contracts/help.snap +2 -2
- package/contracts/mcp-tools.json +2 -2
- package/contracts/plan.json +1 -1
- package/cspell.json +3 -7
- package/dist/commands/doctor.d.ts +25 -4
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +26 -19
- package/dist/commands/doctor.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +6 -2
- package/dist/mcp.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/commands.md +6 -6
- package/docs/configuration.md +1 -1
- package/docs/dev/TESTING.md +2 -0
- package/docs/dev/reference-clis.md +6 -2
- package/docs/dev/skills/release/SKILL.md +1 -1
- package/docs/index.md +3 -3
- package/docs/installation.md +1 -1
- package/docs/security.md +1 -1
- package/docs/troubleshooting.md +2 -2
- package/docs/usage.md +1 -1
- package/kit/dist/main.js +1 -1
- package/kit/dist/manifest.js +18 -30
- package/kit/dist/new.js +9 -7
- package/kit/dist/rename.js +1 -0
- package/kit/dist/source.js +7 -4
- package/package.json +7 -7
- package/scripts/commands.ts +1 -1
- package/scripts/docs-rules.ts +15 -0
- package/scripts/smoke.ts +26 -7
- package/skills/clidev-cli/SKILL.md +3 -3
- package/src/commands/doctor.ts +36 -19
- package/src/mcp.test.ts +0 -1
- package/src/mcp.ts +7 -2
- package/src/program.test.ts +26 -5
- package/src/version.ts +1 -1
- package/vitest.config.ts +11 -3
- package/kit/README.md +0 -136
- package/kit/tsconfig.json +0 -17
- package/kit/tsconfig.test.json +0 -5
package/src/program.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
2
2
|
import { join } from "node:path"
|
|
3
3
|
import { expect, it } from "vitest"
|
|
4
|
+
import { doctorReport } from "./commands/doctor.js"
|
|
4
5
|
import { IDENTITY } from "./identity.js"
|
|
5
6
|
import { run } from "./program.js"
|
|
6
7
|
|
|
@@ -25,20 +26,40 @@ it("lists the required root commands and config", async () => {
|
|
|
25
26
|
it("doctor answers without a config file", async () => {
|
|
26
27
|
const { code, out } = await ran(["doctor", "--json"])
|
|
27
28
|
expect(code).toBe(0)
|
|
28
|
-
expect(json(out).item).toMatchObject({ config: {
|
|
29
|
+
expect(json(out).item).toMatchObject({ config: { found: false, issues: [] }, profiles: [], read_only: null })
|
|
29
30
|
})
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
const withConfig = async (text: string, check: (config: unknown) => void) => {
|
|
32
33
|
const dir = process.env.CLIDEV_CONFIG_DIR ?? ""
|
|
33
34
|
mkdirSync(dir, { recursive: true })
|
|
34
|
-
writeFileSync(join(dir, "config.json"),
|
|
35
|
+
writeFileSync(join(dir, "config.json"), text)
|
|
35
36
|
try {
|
|
36
37
|
const { code, out } = await ran(["doctor", "--json"])
|
|
37
38
|
expect(code).toBe(0)
|
|
38
|
-
|
|
39
|
+
check(json(out).item.config)
|
|
39
40
|
} finally {
|
|
40
41
|
rmSync(join(dir, "config.json"))
|
|
41
42
|
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
it("doctor reports invalid JSON as an issue", async () => {
|
|
46
|
+
await withConfig('{\n "defaults": {\n', (config) =>
|
|
47
|
+
expect(config).toMatchObject({ found: true, issues: [{ kind: "invalid_json", line: 3 }] }),
|
|
48
|
+
)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it("doctor reports an unknown setting with the command that removes it", async () => {
|
|
52
|
+
await withConfig('{ "defaults": { "limt": 3 } }', (config) =>
|
|
53
|
+
expect(config).toMatchObject({
|
|
54
|
+
found: true,
|
|
55
|
+
issues: [{ kind: "unknown_setting", key: "limt", fix: "clidev config unset --defaults limt" }],
|
|
56
|
+
}),
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it("doctor reports settings an environment variable breaks, and still answers", () => {
|
|
61
|
+
const report = doctorReport({ ...process.env, CLIDEV_EMBEDDING_DIMS: "many" })
|
|
62
|
+
expect(report).toMatchObject({ config: { issues: [] }, settings: { error: expect.any(String) }, profiles: null })
|
|
42
63
|
})
|
|
43
64
|
|
|
44
65
|
it("config show names the profile it was given", async () => {
|
|
@@ -65,7 +86,7 @@ it("upgrade --check installs nothing when npm cannot be reached", async () => {
|
|
|
65
86
|
expect(json(out).item).toMatchObject({ current: IDENTITY.version, latest: null, updated: false })
|
|
66
87
|
})
|
|
67
88
|
|
|
68
|
-
it("keeps a failed command as a run, and runs list, show and path read it back
|
|
89
|
+
it("keeps a failed command as a run, and runs list, show and path read it back", async () => {
|
|
69
90
|
expect((await ran(["--no-such-flag"])).code).toBe(2)
|
|
70
91
|
const listed = await ran(["runs", "list", "--limit", "5", "--page", "1", "--json"])
|
|
71
92
|
expect(listed.code).toBe(0)
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.1.
|
|
1
|
+
export const VERSION = "0.1.6"
|
package/vitest.config.ts
CHANGED
|
@@ -10,10 +10,18 @@ export default defineConfig({
|
|
|
10
10
|
globalSetup: ["src/testing/argv-log.ts"],
|
|
11
11
|
coverage: {
|
|
12
12
|
provider: "v8",
|
|
13
|
-
include: ["src/**/*.ts"],
|
|
14
|
-
exclude: [
|
|
13
|
+
include: ["src/**/*.ts", "kit/src/**/*.ts"],
|
|
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
|
+
// The kit's entry point runs only as a separate process, in kit/test/package.test.ts.
|
|
21
|
+
"kit/src/main.ts",
|
|
22
|
+
],
|
|
15
23
|
reporter: ["text-summary", "json-summary"],
|
|
16
|
-
thresholds: { lines: 90, statements: 90, functions: 80, branches: 70 },
|
|
24
|
+
thresholds: { lines: 90, statements: 90, functions: 80, branches: 70, perFile: { lines: 50 } },
|
|
17
25
|
},
|
|
18
26
|
},
|
|
19
27
|
})
|
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
|
-
}
|