@readme/cli 0.0.26
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/README.md +55 -0
- package/bin/readme.js +8 -0
- package/package.json +58 -0
- package/src/bootstrap.js +97 -0
- package/src/cli.js +189 -0
- package/src/commands/dev.js +119 -0
- package/src/commands/eyes.js +37 -0
- package/src/commands/import.js +2565 -0
- package/src/commands/lint.js +70 -0
- package/src/commands/oas-sync.js +364 -0
- package/src/commands/oas-validate.js +208 -0
- package/src/commands/play.js +17 -0
- package/src/commands/pretty.js +133 -0
- package/src/commands/setup.js +256 -0
- package/src/commands/versions.js +81 -0
- package/src/dev/.next/app-build-manifest.json +20 -0
- package/src/dev/.next/build-manifest.json +31 -0
- package/src/dev/.next/cache/.rscinfo +1 -0
- package/src/dev/.next/cache/next-devtools-config.json +1 -0
- package/src/dev/.next/cache/webpack/client-development/0.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/1.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/10.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/11.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/2.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/3.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/3.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/client-development/4.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/5.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/5.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/client-development/6.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/7.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/7.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/client-development/8.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/9.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/index.pack.gz.old +0 -0
- package/src/dev/.next/cache/webpack/client-development-fallback/0.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development-fallback/1.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development-fallback/index.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development-fallback/index.pack.gz.old +0 -0
- package/src/dev/.next/cache/webpack/edge-server-development/0.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/edge-server-development/1.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/edge-server-development/index.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/edge-server-development/index.pack.gz.old +0 -0
- package/src/dev/.next/cache/webpack/server-development/0.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/1.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/10.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/11.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/12.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/13.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/14.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/15.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/2.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/2.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/3.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/3.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/4.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/5.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/6.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/6.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/7.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/7.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/8.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/9.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/9.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/index.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/index.pack.gz.old +0 -0
- package/src/dev/.next/package.json +1 -0
- package/src/dev/.next/prerender-manifest.json +11 -0
- package/src/dev/.next/react-loadable-manifest.json +1 -0
- package/src/dev/.next/routes-manifest.json +1 -0
- package/src/dev/.next/server/app/[...slug]/page.js +360 -0
- package/src/dev/.next/server/app/[...slug]/page_client-reference-manifest.js +1 -0
- package/src/dev/.next/server/app/page.js +349 -0
- package/src/dev/.next/server/app/page_client-reference-manifest.js +1 -0
- package/src/dev/.next/server/app-paths-manifest.json +3 -0
- package/src/dev/.next/server/edge-runtime-webpack.js +1151 -0
- package/src/dev/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/src/dev/.next/server/middleware-build-manifest.js +33 -0
- package/src/dev/.next/server/middleware-manifest.json +32 -0
- package/src/dev/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/src/dev/.next/server/middleware.js +1113 -0
- package/src/dev/.next/server/next-font-manifest.js +1 -0
- package/src/dev/.next/server/next-font-manifest.json +1 -0
- package/src/dev/.next/server/pages-manifest.json +5 -0
- package/src/dev/.next/server/server-reference-manifest.js +1 -0
- package/src/dev/.next/server/server-reference-manifest.json +5 -0
- package/src/dev/.next/server/static/webpack/633457081244afec._.hot-update.json +1 -0
- package/src/dev/.next/server/vendor-chunks/@readme.js +25 -0
- package/src/dev/.next/server/vendor-chunks/@swc.js +55 -0
- package/src/dev/.next/server/vendor-chunks/next.js +3659 -0
- package/src/dev/.next/server/webpack-runtime.js +209 -0
- package/src/dev/.next/static/chunks/app/[...slug]/loading.js +28 -0
- package/src/dev/.next/static/chunks/app/[...slug]/page.js +28 -0
- package/src/dev/.next/static/chunks/app/layout.js +171 -0
- package/src/dev/.next/static/chunks/app/page.js +28 -0
- package/src/dev/.next/static/chunks/app-pages-internals.js +182 -0
- package/src/dev/.next/static/chunks/main-app.js +1882 -0
- package/src/dev/.next/static/chunks/polyfills.js +1 -0
- package/src/dev/.next/static/chunks/webpack.js +1393 -0
- package/src/dev/.next/static/css/app/layout.css +559 -0
- package/src/dev/.next/static/development/_buildManifest.js +1 -0
- package/src/dev/.next/static/development/_ssgManifest.js +1 -0
- package/src/dev/.next/static/webpack/633457081244afec._.hot-update.json +1 -0
- package/src/dev/.next/static/webpack/ec52a3fce0f78db0.webpack.hot-update.json +1 -0
- package/src/dev/.next/static/webpack/webpack.ec52a3fce0f78db0.hot-update.js +12 -0
- package/src/dev/.next/trace +21 -0
- package/src/dev/.next/types/app/[...slug]/page.ts +84 -0
- package/src/dev/.next/types/app/layout.ts +84 -0
- package/src/dev/.next/types/app/page.ts +84 -0
- package/src/dev/.next/types/cache-life.d.ts +141 -0
- package/src/dev/.next/types/package.json +1 -0
- package/src/dev/.next/types/routes.d.ts +55 -0
- package/src/dev/app/Sidebar.js +149 -0
- package/src/dev/app/[...slug]/loading.js +16 -0
- package/src/dev/app/[...slug]/page.js +43 -0
- package/src/dev/app/globals.css +167 -0
- package/src/dev/app/layout.js +73 -0
- package/src/dev/app/page.js +19 -0
- package/src/dev/lib/docs.js +337 -0
- package/src/dev/middleware.js +7 -0
- package/src/dev/next.config.mjs +22 -0
- package/src/index.js +12 -0
- package/src/prompts/index.js +352 -0
- package/src/utils/claude.js +15 -0
- package/src/utils/eyes.js +365 -0
- package/src/utils/git.js +143 -0
- package/src/utils/lint.js +99 -0
- package/src/utils/reporter.js +319 -0
- package/src/utils/setup-templates.js +323 -0
- package/src/utils/styles.js +50 -0
- package/src/utils/tamagotchi.js +1139 -0
- package/src/utils/tips.js +90 -0
- package/src/validators/components.js +230 -0
- package/src/validators/content.js +53 -0
- package/src/validators/duplicates.js +45 -0
- package/src/validators/frontmatter.js +247 -0
- package/src/validators/links.js +68 -0
- package/src/validators/nesting.js +50 -0
- package/src/validators/numbering.js +136 -0
- package/src/validators/oas-reference.js +126 -0
- package/src/validators/oas-schema.js +106 -0
- package/src/validators/ordering.js +121 -0
- package/src/validators/recipes.js +143 -0
- package/vendor/TOOLS.md +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ReadMe CLI
|
|
2
|
+
|
|
3
|
+
A command-line tool for writing, previewing, and managing your [ReadMe](https://readme.com) docs from your terminal.
|
|
4
|
+
|
|
5
|
+
> **Currently in beta!** Things may change, break, or otherwise be a little rough around the edges. We'd love your feedback. Please [open an issue](https://github.com/readmeio/cli/issues) if you run into anything or have ideas for improvements.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
### Linting
|
|
10
|
+
|
|
11
|
+
Checks your docs for errors: things like invalid frontmatter, missing files, duplicate slugs, and more. Handy for catching issues before you push.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @readme/cli lint [--fix] [--json]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Use `--fix` to automatically fix what it can, or `--json` for machine-readable output (great for CI).
|
|
18
|
+
|
|
19
|
+
### OpenAPI Sync
|
|
20
|
+
|
|
21
|
+
Syncs your `reference/` directory with your OpenAPI spec. It'll create, update, or remove pages based on your spec's operations.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @readme/cli oas:sync
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Import
|
|
28
|
+
|
|
29
|
+
Imports content from an external folder and converts it into ReadMe's format using Claude. Great for migrating existing docs.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx @readme/cli import
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Versions
|
|
36
|
+
|
|
37
|
+
Lists all your doc versions and their branches.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx @readme/cli versions
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Dev Server
|
|
44
|
+
|
|
45
|
+
> **Very early beta!** This command is still a work in progress, and doesn't use our rendering engine yet.
|
|
46
|
+
|
|
47
|
+
Starts a local dev server so you can preview your docs as you write them. Watches for changes and refreshes automatically.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx @readme/cli dev
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Feedback
|
|
54
|
+
|
|
55
|
+
This is a beta! If something doesn't work right, or if you have suggestions, please [open an issue](https://github.com/readmeio/cli/issues). We're actively building this and want to hear from you.
|
package/bin/readme.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@readme/cli",
|
|
3
|
+
"version": "0.0.26",
|
|
4
|
+
"description": "The ReadMe CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"readme": "./bin/readme.js",
|
|
8
|
+
"readme_": "./bin/readme.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/index.js",
|
|
12
|
+
"./commands/lint": "./src/commands/lint.js",
|
|
13
|
+
"./commands/oas-sync": "./src/commands/oas-sync.js",
|
|
14
|
+
"./commands/oas-validate": "./src/commands/oas-validate.js",
|
|
15
|
+
"./commands/import": "./src/commands/import.js",
|
|
16
|
+
"./commands/pretty": "./src/commands/pretty.js",
|
|
17
|
+
"./prompts": "./src/prompts/index.js",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"bin",
|
|
22
|
+
"src",
|
|
23
|
+
"vendor",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"start": "node bin/readme.js",
|
|
28
|
+
"test": "echo \"No tests yet\""
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"readme",
|
|
35
|
+
"cli",
|
|
36
|
+
"docs"
|
|
37
|
+
],
|
|
38
|
+
"license": "ISC",
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public",
|
|
41
|
+
"registry": "https://registry.npmjs.org"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.114",
|
|
45
|
+
"@readme/markdown": "^13.3.0",
|
|
46
|
+
"@readmeio/git-format": "readmeio/git-format",
|
|
47
|
+
"ajv": "^8.18.0",
|
|
48
|
+
"chalk": "^5.4.1",
|
|
49
|
+
"commander": "^13.1.0",
|
|
50
|
+
"gray-matter": "^4.0.3",
|
|
51
|
+
"next": "^15.0.0",
|
|
52
|
+
"oas-normalize": "^15.7.1",
|
|
53
|
+
"ora": "^9.3.0",
|
|
54
|
+
"react": "^18.3.1",
|
|
55
|
+
"react-dom": "^18.3.1",
|
|
56
|
+
"update-notifier": "^7.3.1"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/bootstrap.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import updateNotifier from 'update-notifier';
|
|
6
|
+
import * as styles from './utils/styles.js';
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const pkg = require('../package.json');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Run all bootstrap checks and return context about the repo.
|
|
13
|
+
* Exits with a friendly error if anything is wrong.
|
|
14
|
+
*/
|
|
15
|
+
export default async function bootstrap({ skipValidation = false } = {}) {
|
|
16
|
+
// 1. Check for CLI updates (runs at most once every 24 h)
|
|
17
|
+
checkForUpdates();
|
|
18
|
+
|
|
19
|
+
// 2. Find the git root (or fall back to cwd if not in a git repo)
|
|
20
|
+
const gitRoot = findGitRoot() || process.cwd();
|
|
21
|
+
|
|
22
|
+
// 3. Make sure this is a ReadMe project repo
|
|
23
|
+
if (!skipValidation) {
|
|
24
|
+
if (!findGitRoot()) {
|
|
25
|
+
styles.error('This doesn\'t appear to be a git repository.');
|
|
26
|
+
styles.info('Run this command from inside a git repo to get started.');
|
|
27
|
+
styles.info(`You can skip this check with ${styles.bold(`${styles.binName()} --no-check`)}.`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
validateReadMeRepo(gitRoot);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return { gitRoot };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ── update check ────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
function checkForUpdates() {
|
|
39
|
+
const notifier = updateNotifier({ pkg, updateCheckInterval: 1000 * 60 * 60 * 24 });
|
|
40
|
+
notifier.notify({ isGlobal: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── git helpers ─────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
function findGitRoot() {
|
|
46
|
+
try {
|
|
47
|
+
const root = execSync('git rev-parse --show-toplevel', {
|
|
48
|
+
encoding: 'utf-8',
|
|
49
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
50
|
+
}).trim();
|
|
51
|
+
return root;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function validateReadMeRepo(gitRoot) {
|
|
58
|
+
// Check for a branch matching ^v[0-9] (e.g. v1, v2.0, etc.)
|
|
59
|
+
let branches;
|
|
60
|
+
try {
|
|
61
|
+
branches = execSync('git branch -a --format="%(refname:short)"', {
|
|
62
|
+
cwd: gitRoot,
|
|
63
|
+
encoding: 'utf-8',
|
|
64
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
65
|
+
}).trim();
|
|
66
|
+
} catch {
|
|
67
|
+
branches = '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const hasVersionBranch = branches
|
|
71
|
+
.split('\n')
|
|
72
|
+
.some((b) => /^(origin\/)?v[0-9]/.test(b.trim()));
|
|
73
|
+
|
|
74
|
+
// Check for /docs or /reference at the repo root
|
|
75
|
+
const hasDocs = fs.existsSync(path.join(gitRoot, 'docs'));
|
|
76
|
+
const hasReference = fs.existsSync(path.join(gitRoot, 'reference'));
|
|
77
|
+
|
|
78
|
+
if (!hasVersionBranch || (!hasDocs && !hasReference)) {
|
|
79
|
+
styles.error('This doesn\'t look like a ReadMe docs repo.');
|
|
80
|
+
|
|
81
|
+
if (!hasVersionBranch) {
|
|
82
|
+
styles.info(
|
|
83
|
+
`We couldn't find a version branch (e.g. ${styles.bold('v1')}, ${styles.bold('v2')}).`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
if (!hasDocs && !hasReference) {
|
|
87
|
+
styles.info(
|
|
88
|
+
`We couldn't find a ${styles.bold('/docs')} or ${styles.bold('/reference')} folder.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log();
|
|
93
|
+
styles.info('Make sure you\'re running this inside a ReadMe docs project.');
|
|
94
|
+
styles.info(`You can skip this check with ${styles.bold(`${styles.binName()} --no-check`)}.`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import bootstrap from './bootstrap.js';
|
|
7
|
+
import * as styles from './utils/styles.js';
|
|
8
|
+
import { header, setPalette, isAgenticCli } from './utils/eyes.js';
|
|
9
|
+
import { loadPet, applyDecay, getPetHeader } from './utils/tamagotchi.js';
|
|
10
|
+
import { getRandomTip } from './utils/tips.js';
|
|
11
|
+
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const pkg = require('../package.json');
|
|
14
|
+
|
|
15
|
+
const isRunningInClaude = isAgenticCli();
|
|
16
|
+
|
|
17
|
+
export async function main() {
|
|
18
|
+
const program = new Command();
|
|
19
|
+
|
|
20
|
+
// Load tamagotchi state if it exists for personalized header
|
|
21
|
+
let petGreeting, petExpression;
|
|
22
|
+
let hasPet = false;
|
|
23
|
+
if (!isRunningInClaude) {
|
|
24
|
+
try {
|
|
25
|
+
const pet = loadPet();
|
|
26
|
+
if (pet) {
|
|
27
|
+
hasPet = true;
|
|
28
|
+
if (pet.color) setPalette(pet.color);
|
|
29
|
+
const updated = applyDecay(pet);
|
|
30
|
+
({ greeting: petGreeting, expression: petExpression } = getPetHeader(updated));
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
// No pet yet, use defaults
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
program
|
|
38
|
+
.name(styles.binName())
|
|
39
|
+
.version(pkg.version, '-v, --version')
|
|
40
|
+
.option('--no-check', 'Skip ReadMe project validation checks');
|
|
41
|
+
|
|
42
|
+
if (!isRunningInClaude) {
|
|
43
|
+
program.addHelpText('beforeAll', () => {
|
|
44
|
+
return '\n' + header({
|
|
45
|
+
version: pkg.version,
|
|
46
|
+
binName: styles.binName(),
|
|
47
|
+
greeting: petGreeting,
|
|
48
|
+
expression: petExpression,
|
|
49
|
+
}).map(l => ' ' + l).join('\n') + '\n';
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Auto-discover and register every command in src/commands/
|
|
54
|
+
const commandsDir = path.join(path.dirname(new URL(import.meta.url).pathname), 'commands');
|
|
55
|
+
const files = fs.readdirSync(commandsDir).filter((f) => f.endsWith('.js'));
|
|
56
|
+
|
|
57
|
+
// Load all modules, then sort by order (default 0)
|
|
58
|
+
const mods = [];
|
|
59
|
+
for (const file of files) {
|
|
60
|
+
const mod = await import(pathToFileURL(path.join(commandsDir, file)).href);
|
|
61
|
+
mods.push(mod);
|
|
62
|
+
}
|
|
63
|
+
mods.sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
64
|
+
|
|
65
|
+
// Map of registered primary commands to their category, for the categorized
|
|
66
|
+
// help renderer below.
|
|
67
|
+
const commandCategories = new Map();
|
|
68
|
+
|
|
69
|
+
for (const mod of mods) {
|
|
70
|
+
const isPlay = mod.command === 'play';
|
|
71
|
+
const hidePlay = isPlay && (!hasPet || isRunningInClaude);
|
|
72
|
+
const cmd = program.command(mod.command, { hidden: !!((mod.hidden && !isPlay) || hidePlay) });
|
|
73
|
+
|
|
74
|
+
if (mod.description) {
|
|
75
|
+
const desc = mod.beta ? `${mod.description} ${styles.brand('[beta]')}` : mod.description;
|
|
76
|
+
cmd.description(desc);
|
|
77
|
+
}
|
|
78
|
+
if (mod.aliases) {
|
|
79
|
+
for (const alias of mod.aliases) {
|
|
80
|
+
const hidden = program.command(alias, { hidden: true });
|
|
81
|
+
if (mod.args) mod.args(hidden);
|
|
82
|
+
hidden.action(async (...args) => {
|
|
83
|
+
const ctx = await bootstrap({ skipValidation: !program.opts().check });
|
|
84
|
+
await mod.run(...args, ctx);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (mod.args) mod.args(cmd); // let the command define its own arguments/options
|
|
89
|
+
|
|
90
|
+
cmd.action(async (...args) => {
|
|
91
|
+
if (mod.skipBootstrap) {
|
|
92
|
+
await mod.run(...args);
|
|
93
|
+
} else {
|
|
94
|
+
const ctx = await bootstrap({ skipValidation: !program.opts().check });
|
|
95
|
+
await mod.run(...args, ctx);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (mod.category) commandCategories.set(cmd, mod.category);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Suppress commander's auto-generated Commands: section — we render our own
|
|
103
|
+
// categorized version below via addHelpText('after').
|
|
104
|
+
program.configureHelp({ visibleCommands: () => [] });
|
|
105
|
+
|
|
106
|
+
program.addHelpText('after', () => {
|
|
107
|
+
// Group registered commands by category. The built-in `help` command
|
|
108
|
+
// doesn't go through the loop above, so we add it manually.
|
|
109
|
+
const groups = { 'Linting': [], 'OAS Tooling': [], 'Other': [] };
|
|
110
|
+
for (const [cmd, category] of commandCategories.entries()) {
|
|
111
|
+
if (cmd._hidden) continue;
|
|
112
|
+
if (!groups[category]) groups[category] = [];
|
|
113
|
+
groups[category].push(cmd);
|
|
114
|
+
}
|
|
115
|
+
// Commander's built-in help command isn't in program.commands — fetch via private getter.
|
|
116
|
+
const helpCmd = program._getHelpCommand && program._getHelpCommand();
|
|
117
|
+
if (helpCmd) groups['Other'].push(helpCmd);
|
|
118
|
+
|
|
119
|
+
const allCmds = Object.values(groups).flat();
|
|
120
|
+
if (allCmds.length === 0) return '';
|
|
121
|
+
const nameWidth = Math.max(...allCmds.map((c) => c.name().length));
|
|
122
|
+
|
|
123
|
+
// Map cmd → its source module so we can pull optional helpHint.
|
|
124
|
+
const cmdToMod = new Map();
|
|
125
|
+
for (const mod of mods) {
|
|
126
|
+
const cmd = program.commands.find((c) => c.name() === mod.command);
|
|
127
|
+
if (cmd) cmdToMod.set(cmd, mod);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const lines = [''];
|
|
131
|
+
for (const [category, cmds] of Object.entries(groups)) {
|
|
132
|
+
if (cmds.length === 0) continue;
|
|
133
|
+
lines.push(`${styles.bold(`${category} Commands:`)}`);
|
|
134
|
+
for (const cmd of cmds) {
|
|
135
|
+
const name = cmd.name().padEnd(nameWidth);
|
|
136
|
+
const desc = cmd.description() || '';
|
|
137
|
+
lines.push(` ${name} ${desc}`);
|
|
138
|
+
const hint = cmdToMod.get(cmd)?.helpHint;
|
|
139
|
+
if (hint) {
|
|
140
|
+
for (const hintLine of hint.split('\n')) {
|
|
141
|
+
lines.push(` ${' '.repeat(nameWidth)} ${styles.dim(hintLine)}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
lines.push('');
|
|
146
|
+
}
|
|
147
|
+
return lines.join('\n').trimEnd();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Show a random tip after help
|
|
151
|
+
program.addHelpText('afterAll', () => {
|
|
152
|
+
const tip = getRandomTip({ command: 'help' });
|
|
153
|
+
if (tip) {
|
|
154
|
+
let output = '';
|
|
155
|
+
const origLog = console.log;
|
|
156
|
+
console.log = (...args) => { output += args.join(' ') + '\n'; };
|
|
157
|
+
tip.render();
|
|
158
|
+
console.log = origLog;
|
|
159
|
+
return '\n' + output;
|
|
160
|
+
}
|
|
161
|
+
return '';
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Friendly fallback for unknown commands
|
|
165
|
+
program.on('command:*', ([cmd]) => {
|
|
166
|
+
styles.error(`Unknown command: ${styles.bold(cmd)}`);
|
|
167
|
+
styles.info(`Run ${styles.binName()} --help to see available commands.`);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Show help if no command given
|
|
172
|
+
if (process.argv.length <= 2) {
|
|
173
|
+
program.outputHelp();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Support colon syntax (e.g. "eyes:right" → "eyes right"). Find the first
|
|
178
|
+
// non-flag positional so global options like --no-check don't bypass the rewrite.
|
|
179
|
+
const argv = [...process.argv];
|
|
180
|
+
let cmdIdx = 2;
|
|
181
|
+
while (cmdIdx < argv.length && argv[cmdIdx].startsWith('-')) cmdIdx++;
|
|
182
|
+
const cmdArg = argv[cmdIdx];
|
|
183
|
+
if (cmdArg && cmdArg.includes(':') && !program.commands.some((c) => c.name() === cmdArg)) {
|
|
184
|
+
const [base, ...rest] = cmdArg.split(':');
|
|
185
|
+
argv.splice(cmdIdx, 1, base, rest.join(':'));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
await program.parseAsync(argv);
|
|
189
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { createServer } from 'http';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { binName } from '../utils/styles.js';
|
|
8
|
+
import { header, setPalette, isAgenticCli } from '../utils/eyes.js';
|
|
9
|
+
import { loadPet } from '../utils/tamagotchi.js';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
export const command = 'dev';
|
|
14
|
+
export const order = 6;
|
|
15
|
+
export const category = 'Other';
|
|
16
|
+
export const description = 'Start a local dev server to preview your docs';
|
|
17
|
+
export const beta = true;
|
|
18
|
+
|
|
19
|
+
export function args(cmd) {
|
|
20
|
+
cmd.option('--port <number>', 'Port to run the dev server on');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function run(options, _cmd, ctx) {
|
|
24
|
+
const { gitRoot } = ctx;
|
|
25
|
+
|
|
26
|
+
process.env.DOCS_ROOT = gitRoot;
|
|
27
|
+
process.env.NEXT_TELEMETRY_DISABLED = '1';
|
|
28
|
+
|
|
29
|
+
const devDir = path.join(__dirname, '..', 'dev');
|
|
30
|
+
|
|
31
|
+
const require = createRequire(import.meta.url);
|
|
32
|
+
const pkg = require('../../package.json');
|
|
33
|
+
|
|
34
|
+
// Load pet palette if available
|
|
35
|
+
try {
|
|
36
|
+
const pet = loadPet();
|
|
37
|
+
if (pet?.color) setPalette(pet.color);
|
|
38
|
+
} catch {}
|
|
39
|
+
|
|
40
|
+
// Suppress Next.js noisy compile/request logging
|
|
41
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
42
|
+
const original = stream.write.bind(stream);
|
|
43
|
+
stream.write = (chunk, ...args) => {
|
|
44
|
+
const str = typeof chunk === 'string' ? chunk : chunk.toString();
|
|
45
|
+
if (/^ [○✓⨯▲ ]*(Compiled|Compiling|GET |POST |⚠|Warning:)/.test(str)) return true;
|
|
46
|
+
return original(chunk, ...args);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const next = (await import('next')).default;
|
|
51
|
+
const app = next({ dev: true, dir: devDir, quiet: true });
|
|
52
|
+
await app.prepare();
|
|
53
|
+
|
|
54
|
+
const handle = app.getRequestHandler();
|
|
55
|
+
|
|
56
|
+
// SSE clients for live reload
|
|
57
|
+
const reloadClients = new Set();
|
|
58
|
+
|
|
59
|
+
const server = createServer((req, res) => {
|
|
60
|
+
if (req.url === '/__reload') {
|
|
61
|
+
res.writeHead(200, {
|
|
62
|
+
'Content-Type': 'text/event-stream',
|
|
63
|
+
'Cache-Control': 'no-cache',
|
|
64
|
+
Connection: 'keep-alive',
|
|
65
|
+
});
|
|
66
|
+
res.write('data: connected\n\n');
|
|
67
|
+
reloadClients.add(res);
|
|
68
|
+
req.on('close', () => reloadClients.delete(res));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
handle(req, res);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Watch for .md / .yaml file changes and notify browsers
|
|
75
|
+
fs.watch(gitRoot, { recursive: true }, (_event, filename) => {
|
|
76
|
+
if (filename && (filename.endsWith('.md') || filename.endsWith('.yaml'))) {
|
|
77
|
+
for (const client of reloadClients) {
|
|
78
|
+
client.write('data: reload\n\n');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const startPort = options.port ? parseInt(options.port, 10) : 4523;
|
|
84
|
+
|
|
85
|
+
function listen(port) {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
const onError = (err) => {
|
|
88
|
+
if (err.code === 'EADDRINUSE') {
|
|
89
|
+
resolve(listen(port + 1));
|
|
90
|
+
} else {
|
|
91
|
+
reject(err);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
server.once('error', onError);
|
|
95
|
+
server.listen(port, () => {
|
|
96
|
+
server.removeListener('error', onError);
|
|
97
|
+
resolve(port);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const port = await listen(startPort);
|
|
103
|
+
|
|
104
|
+
console.log();
|
|
105
|
+
if (!isAgenticCli()) {
|
|
106
|
+
const headerLines = header({
|
|
107
|
+
version: pkg.version,
|
|
108
|
+
binName: `${binName()} dev`,
|
|
109
|
+
});
|
|
110
|
+
for (const line of headerLines) {
|
|
111
|
+
console.log(' ' + line);
|
|
112
|
+
}
|
|
113
|
+
console.log();
|
|
114
|
+
}
|
|
115
|
+
console.log(` Dev server is running!`);
|
|
116
|
+
console.log(` ${chalk.dim('Changes to your files will auto-reload.')}`);
|
|
117
|
+
console.log(` ${chalk.dim('→')} ${chalk.cyan(`http://localhost:${port}`)}`);
|
|
118
|
+
console.log();
|
|
119
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { printEyes, animate, expressions, animations } from '../utils/eyes.js';
|
|
3
|
+
import { binName } from '../utils/styles.js';
|
|
4
|
+
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const pkg = require('../../package.json');
|
|
7
|
+
|
|
8
|
+
export const command = 'eyes [variant]';
|
|
9
|
+
export const hidden = true;
|
|
10
|
+
export const skipBootstrap = true;
|
|
11
|
+
|
|
12
|
+
export async function run(variant) {
|
|
13
|
+
if (!variant) {
|
|
14
|
+
// Show all expressions side by side
|
|
15
|
+
const chalk = (await import('chalk')).default;
|
|
16
|
+
const names = Object.keys(expressions);
|
|
17
|
+
const rendered = Object.fromEntries(names.map(n => [n, expressions[n] ? require : null]));
|
|
18
|
+
|
|
19
|
+
console.log('');
|
|
20
|
+
const ctrl = animate('all', { indent: ' ', version: pkg.version, binName: binName() });
|
|
21
|
+
process.on('SIGINT', () => { ctrl.stop(); process.exit(); });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (expressions[variant]) {
|
|
26
|
+
console.log('');
|
|
27
|
+
printEyes(variant, ' ');
|
|
28
|
+
console.log('');
|
|
29
|
+
} else if (animations[variant]) {
|
|
30
|
+
console.log('');
|
|
31
|
+
const ctrl = animate(variant, { indent: ' ', version: pkg.version, binName: binName() });
|
|
32
|
+
process.on('SIGINT', () => { ctrl.stop(); process.exit(); });
|
|
33
|
+
} else {
|
|
34
|
+
console.error(`Unknown: "${variant}". Try: ${[...Object.keys(expressions), ...Object.keys(animations)].join(', ')}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|