@wirecat/cli-template 0.0.0-stage → 0.1.1

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 (132) 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/.gitleaks.toml +5 -0
  10. package/.markdownlint-cli2.jsonc +7 -0
  11. package/.vale.ini +5 -0
  12. package/AGENTS.md +27 -0
  13. package/CHANGELOG.md +31 -0
  14. package/CLAUDE.md +17 -0
  15. package/LICENSE +204 -0
  16. package/README.md +52 -2
  17. package/SECURITY.md +15 -0
  18. package/bin/release +139 -0
  19. package/biome.json +6 -0
  20. package/config/wirecat/LICENSE +204 -0
  21. package/config/wirecat/biome.base.json +157 -0
  22. package/config/wirecat/check.mjs +41 -0
  23. package/config/wirecat/cspell.json +80 -0
  24. package/config/wirecat/lefthook.yml +26 -0
  25. package/config/wirecat/manifest.json +37 -0
  26. package/config/wirecat/markdownlint.json +10 -0
  27. package/config/wirecat/no-deep-wirecat-import.grit +15 -0
  28. package/config/wirecat/no-exec-interpolation.grit +17 -0
  29. package/config/wirecat/no-new-function.grit +8 -0
  30. package/config/wirecat/no-shell-true.grit +8 -0
  31. package/config/wirecat/no-sqlite-driver.grit +15 -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/gitignore +44 -0
  93. package/kit/README.md +127 -0
  94. package/kit/dist/interview.js +170 -0
  95. package/kit/dist/main.js +70 -0
  96. package/kit/dist/manifest.js +111 -0
  97. package/kit/dist/new.js +256 -0
  98. package/kit/dist/rename.js +36 -0
  99. package/kit/dist/source.js +98 -0
  100. package/kit/dist/upgrade.js +230 -0
  101. package/kit/tsconfig.json +17 -0
  102. package/kit/tsconfig.test.json +5 -0
  103. package/lefthook.yml +2 -0
  104. package/lint/baseline/.gitkeep +0 -0
  105. package/lint/overrides.json +1 -0
  106. package/package.json +106 -4
  107. package/pnpm-workspace.yaml +6 -0
  108. package/scripts/audit.sh +22 -0
  109. package/scripts/command-program.ts +1 -0
  110. package/scripts/commands.ts +39 -0
  111. package/scripts/smoke.ts +56 -0
  112. package/scripts/test-matrix-untested.ts +12 -0
  113. package/skills/app-cli/SKILL.md +21 -0
  114. package/src/bin/app.ts +13 -0
  115. package/src/commands/doctor.ts +45 -0
  116. package/src/commands/mcp.ts +38 -0
  117. package/src/config.ts +4 -0
  118. package/src/identity.ts +15 -0
  119. package/src/index.ts +2 -0
  120. package/src/mcp.test.ts +84 -0
  121. package/src/mcp.ts +82 -0
  122. package/src/program.test.ts +66 -0
  123. package/src/program.ts +53 -0
  124. package/src/roots.test.ts +80 -0
  125. package/src/testing/argv-log.ts +1 -0
  126. package/src/testing/sandbox.ts +25 -0
  127. package/src/version.ts +1 -0
  128. package/template.json +40 -0
  129. package/tsconfig.json +9 -0
  130. package/tsconfig.scripts.json +1 -0
  131. package/tsconfig.test.json +1 -0
  132. package/vitest.config.ts +19 -0
package/src/program.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { join } from "node:path"
2
+ import { resolvePaths } from "@wirecat/cli-base"
3
+ import {
4
+ commandsCommand,
5
+ configCommand,
6
+ createProgram as createSharedProgram,
7
+ outputFor,
8
+ type ProgramDefinition,
9
+ type ProgramOptions,
10
+ type RunOptions,
11
+ run as runShared,
12
+ skillCommand,
13
+ upgradeCommand,
14
+ } from "@wirecat/cli-core/cli"
15
+ import { appUpdate } from "@wirecat/cli-core/update"
16
+ import type { Command } from "commander"
17
+ import { doctorCommand } from "./commands/doctor.js"
18
+ import { mcpCommand } from "./commands/mcp.js"
19
+ import { configuration } from "./config.js"
20
+ import { IDENTITY, PACKAGE, SKILL } from "./identity.js"
21
+
22
+ const update = appUpdate({
23
+ packageName: PACKAGE,
24
+ command: IDENTITY.command,
25
+ version: IDENTITY.version,
26
+ entry: import.meta.url,
27
+ statePath: (env) =>
28
+ join(resolvePaths({ appName: IDENTITY.appName, prefix: IDENTITY.envPrefix, env }).state, "update-check.json"),
29
+ enabled: (env) => configuration.resolveSettings({}, { env, needsProfile: false }).updateCheck,
30
+ offVariables: [`${IDENTITY.envPrefix}_NO_UPDATE_CHECK`],
31
+ })
32
+
33
+ export const definition = (): ProgramDefinition => ({
34
+ app: IDENTITY,
35
+ configuration: { resolveSettings: configuration.resolveSettings },
36
+ commands: () => [
37
+ commandsCommand(IDENTITY),
38
+ doctorCommand(),
39
+ skillCommand(IDENTITY, SKILL),
40
+ mcpCommand(),
41
+ upgradeCommand(IDENTITY, PACKAGE, (command) => ({
42
+ ...outputFor(command),
43
+ installer: () => update.installer({}),
44
+ latest: () => update.latest({}),
45
+ install: (argv) => update.runUpdate(argv, {}),
46
+ })),
47
+ configCommand(IDENTITY, configuration),
48
+ ],
49
+ })
50
+
51
+ export const createProgram = (options?: ProgramOptions): Command => createSharedProgram(definition(), options)
52
+
53
+ export const run = (argv: string[], options?: RunOptions): Promise<number> => runShared(argv, definition(), options)
@@ -0,0 +1,80 @@
1
+ import { existsSync } from "node:fs"
2
+ import { homedir } from "node:os"
3
+ import { join } from "node:path"
4
+ import { expect, it } from "vitest"
5
+ import { run } from "./program.js"
6
+
7
+ const ran = async (argv: string[]) => {
8
+ const out: string[] = []
9
+ const err: string[] = []
10
+ const code = await run(argv, {
11
+ streams: { data: (text) => out.push(text), diagnostic: (text) => err.push(text) },
12
+ tty: false,
13
+ })
14
+ return { code, out: out.join("\n"), err: err.join("\n") }
15
+ }
16
+
17
+ it.each([
18
+ ["--verbose"],
19
+ ["--quiet"],
20
+ ["--trace"],
21
+ ["--debug"],
22
+ ["--timeout", "30s"],
23
+ ["--offline"],
24
+ ["--no-input"],
25
+ ["--max-input-bytes", "1024"],
26
+ ["--max-output-bytes", "0"],
27
+ ["--brief"],
28
+ ["--full"],
29
+ ["--yes"],
30
+ ["--record"],
31
+ ["--no-record"],
32
+ ])("doctor runs with the global option %s", async (...flag) => {
33
+ const { code, out } = await ran([...flag, "doctor", "--json"])
34
+ expect(code).toBe(0)
35
+ expect(JSON.parse(out).item.version).toEqual(expect.any(String))
36
+ })
37
+
38
+ it("--jsonl prints one object per line", async () => {
39
+ const { code, out } = await ran(["--jsonl", "personal", "config", "show"])
40
+ expect(code).toBe(0)
41
+ for (const line of out.split("\n").filter(Boolean)) expect(() => JSON.parse(line)).not.toThrow()
42
+ })
43
+
44
+ it("--dry-run previews a write without making it", async () => {
45
+ const { code } = await ran(["--dry-run", "personal", "config", "set", "limit", "5", "--json"])
46
+ expect(code).toBe(0)
47
+ const { out } = await ran(["personal", "config", "show", "--json"])
48
+ expect(JSON.parse(out).item.settings).toContainEqual(expect.objectContaining({ setting: "limit", value: 20 }))
49
+ })
50
+
51
+ it("config set and unset change one setting and put it back", async () => {
52
+ for (const scope of [[], ["--defaults"], ["--personal"], ["--bot"]]) {
53
+ expect((await ran(["personal", "config", "set", "limit", "7", ...scope, "--json"])).code).toBe(0)
54
+ expect((await ran(["personal", "config", "unset", "limit", ...scope, "--json"])).code).toBe(0)
55
+ }
56
+ })
57
+
58
+ it("config show --bot shows what a bot command would get", async () => {
59
+ const { code, out } = await ran(["personal", "config", "show", "--bot", "--json"])
60
+ expect(code).toBe(0)
61
+ expect(JSON.parse(out).item.profile).toBe("personal")
62
+ })
63
+
64
+ it("commands info exports the schemas and reasons", async () => {
65
+ const { code, out } = await ran(["commands", "info", "--json"])
66
+ expect(code).toBe(0)
67
+ expect(out.length).toBeGreaterThan(0)
68
+ })
69
+
70
+ it("skill install writes the skill for Claude Code", async () => {
71
+ const { code } = await ran(["skill", "install", "--for", "claude", "--json"])
72
+ expect(code).toBe(0)
73
+ expect(existsSync(join(homedir(), ".claude", "skills", "app-cli", "SKILL.md"))).toBe(true)
74
+ })
75
+
76
+ it("help prints the first screen", async () => {
77
+ const { code, out } = await ran(["help"])
78
+ expect(code).toBe(0)
79
+ expect(out).toContain("app")
80
+ })
@@ -0,0 +1 @@
1
+ export { ARGV_LOG, argvLogSetup as default } from "@wirecat/cli-core/testing"
@@ -0,0 +1,25 @@
1
+ import { tmpdir } from "node:os"
2
+ import { join } from "node:path"
3
+ import { afterAll } from "vitest"
4
+ import { ARGV_LOG } from "./argv-log.js"
5
+
6
+ const saved = { ...process.env }
7
+
8
+ // cli-base's setup already gave this file a fresh TMPDIR and HOME, a keyring that throws and no network.
9
+ const sandbox = tmpdir()
10
+ process.env.APP_CONFIG_DIR = join(sandbox, "config")
11
+ process.env.APP_STATE_DIR = join(sandbox, "state")
12
+ process.env.APP_CACHE_DIR = join(sandbox, "cache")
13
+ process.env.MESSAGING_STORE = join(sandbox, "messages.db")
14
+ process.env.WIRECAT_STATE_DIR = join(sandbox, "wirecat")
15
+ process.env.APP_NO_UPDATE_CHECK = "1"
16
+ process.env.APP_TEST_ARGV_LOG = ARGV_LOG
17
+ delete process.env.APP_PROFILE
18
+ delete process.env.APP_READ_ONLY
19
+ delete process.env.AI_AGENT
20
+ delete process.env.CLAUDECODE
21
+
22
+ afterAll(() => {
23
+ for (const key of Object.keys(process.env)) if (!(key in saved)) delete process.env[key]
24
+ Object.assign(process.env, saved)
25
+ })
package/src/version.ts ADDED
@@ -0,0 +1 @@
1
+ export const VERSION = "0.1.1"
package/template.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "files": [
3
+ ".github/",
4
+ ".gitignore",
5
+ ".gitleaks.toml",
6
+ ".vale.ini",
7
+ "AGENTS.md",
8
+ "bin/release",
9
+ "docs/dev/design-questions.md",
10
+ "docs/dev/reference-clis.md",
11
+ "docs/dev/skills/",
12
+ "docs/dev/TESTING.md",
13
+ "lefthook.yml",
14
+ "scripts/audit.sh",
15
+ "tsconfig.json",
16
+ "tsconfig.scripts.json",
17
+ "tsconfig.test.json"
18
+ ],
19
+ "seeds": [
20
+ ".markdownlint-cli2.jsonc",
21
+ "CHANGELOG.md",
22
+ "CLAUDE.md",
23
+ "LICENSE",
24
+ "README.md",
25
+ "SECURITY.md",
26
+ "biome.json",
27
+ "config/wirecat/",
28
+ "contracts/",
29
+ "cspell.json",
30
+ "docs/",
31
+ "lint/",
32
+ "package.json",
33
+ "pnpm-workspace.yaml",
34
+ "scripts/",
35
+ "skills/",
36
+ "src/",
37
+ "vitest.config.ts"
38
+ ],
39
+ "placeholders": { "command": "app", "package": "@wirecat/app-cli", "repository": "WireCatLabs/app-cli" }
40
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@wirecat/cli-base/tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*.ts"],
8
+ "exclude": ["src/**/*.test.ts", "src/testing/**"]
9
+ }
@@ -0,0 +1 @@
1
+ { "extends": ["./tsconfig.json", "@wirecat/cli-base/tsconfig.scripts.json"] }
@@ -0,0 +1 @@
1
+ { "extends": ["./tsconfig.json", "@wirecat/cli-base/tsconfig.test.json"] }
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from "vitest/config"
2
+
3
+ export default defineConfig({
4
+ // Oxc does not resolve TypeScript package exports; tsc still checks the project configs.
5
+ tsconfig: "./node_modules/@wirecat/cli-base/config/tsconfig.base.json",
6
+ test: {
7
+ include: ["src/**/*.test.ts", "scripts/**/*.test.ts", "kit/test/**/*.test.ts"],
8
+ maxWorkers: 4,
9
+ setupFiles: ["@wirecat/cli-base/testing/setup", "src/testing/sandbox.ts"],
10
+ globalSetup: ["src/testing/argv-log.ts"],
11
+ coverage: {
12
+ provider: "v8",
13
+ include: ["src/**/*.ts"],
14
+ exclude: ["src/**/*.test.ts", "src/testing/**", "src/bin/**"],
15
+ reporter: ["text-summary", "json-summary"],
16
+ thresholds: { lines: 90, statements: 90, functions: 80, branches: 70 },
17
+ },
18
+ },
19
+ })