docugate 0.2.9 → 0.3.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.
- package/README.md +200 -57
- package/dist/api.d.ts +44 -0
- package/dist/api.js +11 -5
- package/dist/args.d.ts +29 -0
- package/dist/args.js +121 -0
- package/dist/auth.d.ts +21 -0
- package/dist/banner.d.ts +13 -0
- package/dist/banner.js +50 -0
- package/dist/check.d.ts +22 -0
- package/dist/check.js +4 -3
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +201 -165
- package/dist/commands.d.ts +35 -0
- package/dist/commands.js +293 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.js +43 -3
- package/dist/developers.d.ts +35 -0
- package/dist/developers.js +439 -0
- package/dist/init.d.ts +130 -0
- package/dist/init.js +93 -32
- package/dist/keys/core.d.ts +108 -0
- package/dist/keys/core.js +142 -0
- package/dist/keys/express.d.ts +21 -0
- package/dist/keys/express.js +27 -0
- package/dist/keys/fastify.d.ts +21 -0
- package/dist/keys/fastify.js +21 -0
- package/dist/keys/format.d.ts +11 -0
- package/dist/keys/format.js +46 -0
- package/dist/keys/hono.d.ts +19 -0
- package/dist/keys/hono.js +21 -0
- package/dist/keys/index.d.ts +13 -0
- package/dist/keys/index.js +12 -0
- package/dist/logo-pixels.d.ts +4 -0
- package/dist/logo-pixels.js +35 -0
- package/dist/logo.d.ts +19 -0
- package/dist/logo.js +68 -0
- package/dist/menu.d.ts +29 -0
- package/dist/menu.js +213 -0
- package/dist/openapi.d.ts +38 -0
- package/dist/openapi.js +2 -1
- package/dist/rules.d.ts +69 -0
- package/dist/rules.js +2 -1
- package/dist/select.d.ts +81 -0
- package/dist/select.js +248 -0
- package/dist/session.d.ts +46 -0
- package/dist/session.js +54 -0
- package/dist/spinner.d.ts +12 -0
- package/dist/spinner.js +12 -12
- package/dist/status.d.ts +46 -0
- package/dist/status.js +166 -0
- package/dist/sync/ai.d.ts +44 -0
- package/dist/sync/ai.js +147 -0
- package/dist/sync/apply.d.ts +25 -0
- package/dist/sync/apply.js +104 -0
- package/dist/sync/changes.d.ts +58 -0
- package/dist/sync/changes.js +167 -0
- package/dist/sync/command.d.ts +12 -0
- package/dist/sync/command.js +236 -0
- package/dist/sync/guard.d.ts +15 -0
- package/dist/sync/guard.js +21 -0
- package/dist/sync/hook.d.ts +10 -0
- package/dist/sync/hook.js +62 -0
- package/dist/sync/impact.d.ts +23 -0
- package/dist/sync/impact.js +93 -0
- package/dist/sync/pr.d.ts +37 -0
- package/dist/sync/pr.js +106 -0
- package/dist/sync/run.d.ts +50 -0
- package/dist/sync/run.js +124 -0
- package/dist/sync/state.d.ts +8 -0
- package/dist/sync/state.js +22 -0
- package/dist/sync/workflow.d.ts +20 -0
- package/dist/sync/workflow.js +84 -0
- package/dist/text.d.ts +11 -0
- package/dist/text.js +16 -0
- package/dist/tour-init.d.ts +61 -0
- package/dist/tour-install.d.ts +38 -0
- package/dist/tour-server.d.ts +21 -0
- package/dist/tour.d.ts +60 -0
- package/dist/ui.d.ts +29 -0
- package/dist/ui.js +63 -0
- package/package.json +67 -47
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const LOGO_PALETTE: Record<string, [number, number, number]>;
|
|
2
|
+
export declare const LOGO_ROWS: string[];
|
|
3
|
+
/** The same owl as a small PNG, for terminals that can show real images. */
|
|
4
|
+
export declare const LOGO_PNG_BASE64 = "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";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Generated from DocuGate's owl glyph by a script; the grid the terminal banner draws.
|
|
2
|
+
// Each character is one pixel: . transparent, c cream, g gold, d deep gold.
|
|
3
|
+
export const LOGO_PALETTE = {
|
|
4
|
+
c: [245, 240, 227],
|
|
5
|
+
g: [244, 196, 63],
|
|
6
|
+
d: [217, 154, 30],
|
|
7
|
+
};
|
|
8
|
+
export const LOGO_ROWS = [
|
|
9
|
+
'.....ccccccc..........cccccc......',
|
|
10
|
+
'....ccccccccc........ccccccccc....',
|
|
11
|
+
'...cccccccccccc....cccccccccccc...',
|
|
12
|
+
'..ccccc...ccccc....ccccc...ccccc..',
|
|
13
|
+
'.cccc....c..cccc..cccc....c..cccc.',
|
|
14
|
+
'.ccc...ccccc.ccc..ccc...ccccc.ccc.',
|
|
15
|
+
'cccc...cccccccccccccc..ccccccccccc',
|
|
16
|
+
'ccc...ccccccc.cccccc...ccccccc.ccc',
|
|
17
|
+
'ccc...ccccccc.cccccc...ccccccc.ccc',
|
|
18
|
+
'ccc....ccccc..cccccc....ccccc..ccc',
|
|
19
|
+
'ccc.....cccc..cccccc.....cccc..ccc',
|
|
20
|
+
'cccc.........cccccccc.........cccc',
|
|
21
|
+
'.ccc.........ccc..ccc.........ccc.',
|
|
22
|
+
'.cccc.......ccc.gd.ccc.......cccc.',
|
|
23
|
+
'..cccccc.ccccc.ggdd.ccccc.cccccc..',
|
|
24
|
+
'...cccccccccc.gggddd.cccccccccc...',
|
|
25
|
+
'....cccccccc.ggggdddd.cccccccc....',
|
|
26
|
+
'......ccccc.gggggddddd.ccccc......',
|
|
27
|
+
'.............ggggdddd.............',
|
|
28
|
+
'..............gggddd..............',
|
|
29
|
+
'..............gggddd..............',
|
|
30
|
+
'...............ggdd...............',
|
|
31
|
+
'................gd................',
|
|
32
|
+
'................g.................',
|
|
33
|
+
];
|
|
34
|
+
/** The same owl as a small PNG, for terminals that can show real images. */
|
|
35
|
+
export const LOGO_PNG_BASE64 = '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';
|
package/dist/logo.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type Rgb = [number, number, number];
|
|
2
|
+
/** How much color this terminal shows, from what it says about itself. */
|
|
3
|
+
export declare function colorDepth(env?: NodeJS.ProcessEnv): 'truecolor' | '256';
|
|
4
|
+
/** Whether the terminal draws inline images (the iTerm2 image protocol). */
|
|
5
|
+
export declare function showsImages(env?: NodeJS.ProcessEnv): boolean;
|
|
6
|
+
export declare function fg(rgb: Rgb, depth: 'truecolor' | '256'): string;
|
|
7
|
+
/**
|
|
8
|
+
* The owl as lines of colored half blocks: each character is the pixel above
|
|
9
|
+
* (its color) and the pixel below (its background). Transparent pixels show
|
|
10
|
+
* the terminal's own background, so it sits well on dark and light themes.
|
|
11
|
+
*/
|
|
12
|
+
export declare function logoCells(depth?: 'truecolor' | '256'): string[];
|
|
13
|
+
/** The owl as a real image, for terminals that draw them: one escape sequence. */
|
|
14
|
+
export declare function logoImage(rows?: number): string;
|
|
15
|
+
/**
|
|
16
|
+
* The banner: the owl on the left and `text` beside it, vertically centered.
|
|
17
|
+
* `text` lines may carry their own color codes.
|
|
18
|
+
*/
|
|
19
|
+
export declare function logoBanner(text: string[], env?: NodeJS.ProcessEnv): string[];
|
package/dist/logo.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { LOGO_PALETTE, LOGO_PNG_BASE64, LOGO_ROWS } from './logo-pixels.js';
|
|
2
|
+
/** How much color this terminal shows, from what it says about itself. */
|
|
3
|
+
export function colorDepth(env = process.env) {
|
|
4
|
+
const term = `${env.COLORTERM ?? ''} ${env.TERM ?? ''}`.toLowerCase();
|
|
5
|
+
if (/truecolor|24bit|direct/.test(term))
|
|
6
|
+
return 'truecolor';
|
|
7
|
+
if (env.WT_SESSION || env.TERM_PROGRAM === 'vscode' || env.TERM_PROGRAM === 'iTerm.app' || env.TERM_PROGRAM === 'WezTerm') {
|
|
8
|
+
return 'truecolor';
|
|
9
|
+
}
|
|
10
|
+
return '256';
|
|
11
|
+
}
|
|
12
|
+
/** Whether the terminal draws inline images (the iTerm2 image protocol). */
|
|
13
|
+
export function showsImages(env = process.env) {
|
|
14
|
+
return env.TERM_PROGRAM === 'iTerm.app' || env.TERM_PROGRAM === 'WezTerm';
|
|
15
|
+
}
|
|
16
|
+
/** The nearest color of the xterm 256-color cube. */
|
|
17
|
+
function to256([r, g, b]) {
|
|
18
|
+
const step = (v) => (v < 48 ? 0 : v < 115 ? 1 : Math.min(5, Math.floor((v - 35) / 40)));
|
|
19
|
+
return 16 + 36 * step(r) + 6 * step(g) + step(b);
|
|
20
|
+
}
|
|
21
|
+
export function fg(rgb, depth) {
|
|
22
|
+
return depth === 'truecolor' ? `\x1b[38;2;${rgb.join(';')}m` : `\x1b[38;5;${to256(rgb)}m`;
|
|
23
|
+
}
|
|
24
|
+
function bg(rgb, depth) {
|
|
25
|
+
return depth === 'truecolor' ? `\x1b[48;2;${rgb.join(';')}m` : `\x1b[48;5;${to256(rgb)}m`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The owl as lines of colored half blocks: each character is the pixel above
|
|
29
|
+
* (its color) and the pixel below (its background). Transparent pixels show
|
|
30
|
+
* the terminal's own background, so it sits well on dark and light themes.
|
|
31
|
+
*/
|
|
32
|
+
export function logoCells(depth = colorDepth()) {
|
|
33
|
+
const lines = [];
|
|
34
|
+
for (let y = 0; y < LOGO_ROWS.length; y += 2) {
|
|
35
|
+
const top = LOGO_ROWS[y];
|
|
36
|
+
const bottom = LOGO_ROWS[y + 1] ?? '';
|
|
37
|
+
let line = '';
|
|
38
|
+
for (let x = 0; x < top.length; x++) {
|
|
39
|
+
const up = LOGO_PALETTE[top[x]];
|
|
40
|
+
const down = LOGO_PALETTE[bottom[x] ?? '.'];
|
|
41
|
+
if (up && down)
|
|
42
|
+
line += `${fg(up, depth)}${bg(down, depth)}▀\x1b[0m`;
|
|
43
|
+
else if (up)
|
|
44
|
+
line += `${fg(up, depth)}▀\x1b[0m`;
|
|
45
|
+
else if (down)
|
|
46
|
+
line += `${fg(down, depth)}▄\x1b[0m`;
|
|
47
|
+
else
|
|
48
|
+
line += ' ';
|
|
49
|
+
}
|
|
50
|
+
lines.push(line);
|
|
51
|
+
}
|
|
52
|
+
return lines;
|
|
53
|
+
}
|
|
54
|
+
/** The owl as a real image, for terminals that draw them: one escape sequence. */
|
|
55
|
+
export function logoImage(rows = 5) {
|
|
56
|
+
return `\x1b]1337;File=inline=1;height=${rows};preserveAspectRatio=1:${LOGO_PNG_BASE64}\x07`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The banner: the owl on the left and `text` beside it, vertically centered.
|
|
60
|
+
* `text` lines may carry their own color codes.
|
|
61
|
+
*/
|
|
62
|
+
export function logoBanner(text, env = process.env) {
|
|
63
|
+
if (showsImages(env))
|
|
64
|
+
return ['', ` ${logoImage()}`, ...text.map((t) => ` ${t}`)];
|
|
65
|
+
const cells = logoCells(colorDepth(env));
|
|
66
|
+
const start = Math.max(0, Math.floor((cells.length - text.length) / 2));
|
|
67
|
+
return ['', ...cells.map((cell, i) => ` ${cell} ${text[i - start] ?? ''}`.trimEnd()), ''];
|
|
68
|
+
}
|
package/dist/menu.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { select } from './select.js';
|
|
2
|
+
import type { Choice } from './select.js';
|
|
3
|
+
import type { Status } from './status.js';
|
|
4
|
+
/** The words of a command to run, like ['sync', '--dry-run']. */
|
|
5
|
+
export type Action = string[];
|
|
6
|
+
/** The menu's choices, from the project's state. Pure, so it is tested without a terminal. */
|
|
7
|
+
export declare function buildChoices(s: Status, now?: Date): Choice<Action>[];
|
|
8
|
+
/** The line under the list: who you are, and the one thing worth doing. */
|
|
9
|
+
export declare function menuNote(s: Status, now?: Date): string;
|
|
10
|
+
/** Who is signed in: the one line the launch screen keeps above the list. */
|
|
11
|
+
export declare function whoNote(s: Status): string;
|
|
12
|
+
export type Section = 'main' | 'tours' | 'more';
|
|
13
|
+
/**
|
|
14
|
+
* The launch menu, short enough to see at once: the few things people come
|
|
15
|
+
* for on the first screen, the rest one step away under Tours and More. A step
|
|
16
|
+
* that cannot run yet is left out rather than shown greyed, so every line is
|
|
17
|
+
* something to do. Labels and rules come from buildChoices, written once.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildMenu(s: Status, section: Section): Choice<Action>[];
|
|
20
|
+
export type MenuDeps = {
|
|
21
|
+
pick?: typeof select<Action>;
|
|
22
|
+
/** For terminals that cannot do arrow keys: a numbered list instead. */
|
|
23
|
+
numbered?: boolean;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Shows the menu and returns the command chosen, or null to quit. Tours and
|
|
27
|
+
* More open their own short list, with Back to return.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runMenu(status: Status, deps?: MenuDeps): Promise<Action | null>;
|
package/dist/menu.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { select } from './select.js';
|
|
3
|
+
import { ago } from './status.js';
|
|
4
|
+
import { bold, dim, gold, plural } from './ui.js';
|
|
5
|
+
/** The menu's choices, from the project's state. Pure, so it is tested without a terminal. */
|
|
6
|
+
export function buildChoices(s, now = new Date()) {
|
|
7
|
+
const p = s.project;
|
|
8
|
+
const y = s.sync;
|
|
9
|
+
const needsSetup = p.setUp ? undefined : 'Set the repository up first';
|
|
10
|
+
const needsGit = p.git ? undefined : 'This is not a git repository';
|
|
11
|
+
const needsDocs = needsSetup ?? needsGit;
|
|
12
|
+
const signedIn = s.account.state === 'signed-in';
|
|
13
|
+
const choices = [];
|
|
14
|
+
if (!signedIn) {
|
|
15
|
+
choices.push({
|
|
16
|
+
value: ['login'],
|
|
17
|
+
label: 'Connect your DocuGate account',
|
|
18
|
+
status: s.account.state === 'offline' ? 'cannot reach DocuGate' : 'not connected',
|
|
19
|
+
description: 'Sign in with GitHub in your browser. It lets DocuGate publish your docs and link this repository to a space.',
|
|
20
|
+
keywords: 'login sign in account github',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
choices.push({
|
|
24
|
+
value: ['developers'],
|
|
25
|
+
label: 'Set up a developer space',
|
|
26
|
+
status: '',
|
|
27
|
+
description: 'API reference from your OpenAPI file, first guides, and a public space for them. On Pro: a subdomain, the API console and API keys, with the code your backend needs.',
|
|
28
|
+
keywords: 'developers api openapi keys reference space portal',
|
|
29
|
+
}, {
|
|
30
|
+
value: ['init'],
|
|
31
|
+
label: p.setUp ? 'Set up again or change settings' : 'Set up this repository',
|
|
32
|
+
status: p.setUp ? 'set up' : 'not set up',
|
|
33
|
+
description: 'A short guided setup: where the docs live, the kind of repository, and linking a DocuGate space. It never overwrites your files.',
|
|
34
|
+
keywords: 'init setup configure',
|
|
35
|
+
}, {
|
|
36
|
+
value: ['sync'],
|
|
37
|
+
label: 'Update the docs from the code',
|
|
38
|
+
status: y.commits === null ? '' : y.commits === 0 ? 'up to date' : `${plural(y.commits, 'commit')} behind`,
|
|
39
|
+
description: 'Reads what changed in the code, asks your AI model for the docs edits, and writes them. Add --pr from the command line to open a pull request instead.',
|
|
40
|
+
keywords: 'sync update docs changes ai',
|
|
41
|
+
disabled: needsDocs,
|
|
42
|
+
}, {
|
|
43
|
+
value: ['sync', '--dry-run'],
|
|
44
|
+
label: 'Preview which pages are out of date',
|
|
45
|
+
status: y.affected ? `${plural(y.affected, 'page')} likely` : '',
|
|
46
|
+
description: 'Free and instant: lists the pages the latest changes probably touch. Nothing is sent and nothing is written.',
|
|
47
|
+
keywords: 'sync dry run preview impact',
|
|
48
|
+
disabled: needsDocs,
|
|
49
|
+
}, {
|
|
50
|
+
value: ['sync', 'init'],
|
|
51
|
+
label: 'Keep the docs current automatically',
|
|
52
|
+
status: y.workflow ? `on · ${p.freshness}` : 'not set up',
|
|
53
|
+
description: `Adds a GitHub Action that runs sync ${p.freshness === 'push' ? 'on every push' : p.freshness === 'daily' ? 'every day' : 'every week'} and opens a pull request with the edits. You review and merge.`,
|
|
54
|
+
keywords: 'sync init github action workflow schedule freshness',
|
|
55
|
+
disabled: needsDocs,
|
|
56
|
+
}, {
|
|
57
|
+
value: y.hook ? ['sync', 'hook', 'remove'] : ['sync', 'hook', 'install'],
|
|
58
|
+
label: y.hook ? 'Turn off the after-commit heads-up' : 'Get a heads-up after each commit',
|
|
59
|
+
status: y.hook ? 'on' : 'off',
|
|
60
|
+
description: 'A git hook that tells you which pages your commit may have left out of date. It sends nothing and changes nothing.',
|
|
61
|
+
keywords: 'sync hook git commit',
|
|
62
|
+
disabled: needsGit,
|
|
63
|
+
}, {
|
|
64
|
+
value: ['status'],
|
|
65
|
+
label: 'Where things stand',
|
|
66
|
+
status: '',
|
|
67
|
+
description: 'Your account, docs, sync and tour at a glance, and what to do next.',
|
|
68
|
+
keywords: 'status overview',
|
|
69
|
+
}, {
|
|
70
|
+
value: ['check'],
|
|
71
|
+
label: 'Check the docs',
|
|
72
|
+
status: p.setUp ? `${plural(p.pages, 'page')}` : '',
|
|
73
|
+
description: 'Checks the docs the way DocuGate will read them: broken links, a missing landing page, tours that no longer match the code.',
|
|
74
|
+
keywords: 'check lint validate',
|
|
75
|
+
}, {
|
|
76
|
+
value: ['tour', 'init'],
|
|
77
|
+
label: 'Write the first tour of the app',
|
|
78
|
+
status: s.tour.screens ? `${plural(s.tour.screens, 'screen')}` : 'none yet',
|
|
79
|
+
description: 'IBM Bob reads your code and writes a guided tour of each screen. The tour files are yours to edit.',
|
|
80
|
+
keywords: 'tour bob walkthrough pill',
|
|
81
|
+
}, {
|
|
82
|
+
value: ['tour', 'install'],
|
|
83
|
+
label: 'Add the Tour button to the app',
|
|
84
|
+
status: s.tour.pill ? 'installed' : 'not installed',
|
|
85
|
+
description: 'Adds a one-line loader to your app so the Tour button shows while you develop.',
|
|
86
|
+
keywords: 'tour install pill',
|
|
87
|
+
}, {
|
|
88
|
+
value: ['tour', 'serve'],
|
|
89
|
+
label: 'Start the tour server',
|
|
90
|
+
status: '',
|
|
91
|
+
description: 'Serves your tour to the Tour button on localhost until you press Ctrl+C.',
|
|
92
|
+
keywords: 'tour serve server',
|
|
93
|
+
}, {
|
|
94
|
+
value: ['openapi'],
|
|
95
|
+
label: 'Prepare an OpenAPI spec',
|
|
96
|
+
status: '',
|
|
97
|
+
description: "Cleans up an OpenAPI file for DocuGate's API reference. Needs --from or api.generate in docugate.json.",
|
|
98
|
+
keywords: 'openapi api spec',
|
|
99
|
+
});
|
|
100
|
+
if (signedIn) {
|
|
101
|
+
choices.push({
|
|
102
|
+
value: ['logout'],
|
|
103
|
+
label: 'Sign out',
|
|
104
|
+
status: s.account.state === 'signed-in' ? `@${s.account.login}` : '',
|
|
105
|
+
description: 'Deletes the saved sign-in from this computer.',
|
|
106
|
+
keywords: 'logout sign out',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
choices.push({
|
|
110
|
+
value: ['help'],
|
|
111
|
+
label: 'Help: every command',
|
|
112
|
+
status: '',
|
|
113
|
+
description: 'The full list of commands. Run `docugate help <command>` for one command in detail.',
|
|
114
|
+
keywords: 'help commands',
|
|
115
|
+
});
|
|
116
|
+
const recommended = s.next.command;
|
|
117
|
+
const match = choices.find((c) => c.value.join(' ') === recommended);
|
|
118
|
+
if (match && !match.disabled)
|
|
119
|
+
match.recommended = true;
|
|
120
|
+
return choices;
|
|
121
|
+
}
|
|
122
|
+
/** The line under the list: who you are, and the one thing worth doing. */
|
|
123
|
+
export function menuNote(s, now = new Date()) {
|
|
124
|
+
const who = s.account.state === 'signed-in' ? `signed in as ${gold(`@${s.account.login}`)}` : s.account.state === 'offline' ? 'offline' : 'not connected';
|
|
125
|
+
const bits = [who];
|
|
126
|
+
if (s.project.setUp)
|
|
127
|
+
bits.push(`${plural(s.project.pages, 'page')} in ${s.project.docsDir}/`);
|
|
128
|
+
if (s.project.setUp && s.project.git) {
|
|
129
|
+
bits.push(s.sync.commits ? `last sync ${ago(s.sync.lastRunAt, now)}` : 'docs match the code');
|
|
130
|
+
}
|
|
131
|
+
return dim(bits.join(' · '));
|
|
132
|
+
}
|
|
133
|
+
/** Who is signed in: the one line the launch screen keeps above the list. */
|
|
134
|
+
export function whoNote(s) {
|
|
135
|
+
if (s.account.state === 'signed-in')
|
|
136
|
+
return `signed in as ${gold(`@${s.account.login}`)}`;
|
|
137
|
+
return dim(s.account.state === 'offline' ? 'offline' : 'not signed in');
|
|
138
|
+
}
|
|
139
|
+
/** Values that move around the menu rather than run a command. */
|
|
140
|
+
const GO = { tours: ['@tours'], more: ['@more'], back: ['@back'], quit: ['@quit'] };
|
|
141
|
+
/**
|
|
142
|
+
* The launch menu, short enough to see at once: the few things people come
|
|
143
|
+
* for on the first screen, the rest one step away under Tours and More. A step
|
|
144
|
+
* that cannot run yet is left out rather than shown greyed, so every line is
|
|
145
|
+
* something to do. Labels and rules come from buildChoices, written once.
|
|
146
|
+
*/
|
|
147
|
+
export function buildMenu(s, section) {
|
|
148
|
+
const all = buildChoices(s);
|
|
149
|
+
const pick = (...commands) => commands
|
|
150
|
+
.map((command) => all.find((c) => c.value.join(' ') === command))
|
|
151
|
+
.filter((c) => Boolean(c && !c.disabled))
|
|
152
|
+
.map((c) => ({ ...c, status: undefined, recommended: c.recommended }));
|
|
153
|
+
if (section === 'tours') {
|
|
154
|
+
return [...pick('tour init', 'tour install', 'tour serve'), { value: [...GO.back], label: 'Back' }];
|
|
155
|
+
}
|
|
156
|
+
if (section === 'more') {
|
|
157
|
+
const hook = all.find((c) => c.value[0] === 'sync' && c.value[1] === 'hook');
|
|
158
|
+
return [
|
|
159
|
+
...pick('sync --dry-run', 'sync init', ...(hook ? [hook.value.join(' ')] : []), 'status', 'openapi', 'help'),
|
|
160
|
+
{ value: [...GO.back], label: 'Back' },
|
|
161
|
+
];
|
|
162
|
+
}
|
|
163
|
+
const signedIn = s.account.state === 'signed-in';
|
|
164
|
+
return [
|
|
165
|
+
...pick('developers', 'init', 'sync', 'check'),
|
|
166
|
+
{ value: [...GO.tours], label: 'Tours' },
|
|
167
|
+
{ value: [...GO.more], label: 'More' },
|
|
168
|
+
...pick(signedIn ? 'logout' : 'login'),
|
|
169
|
+
{ value: [...GO.quit], label: 'Quit' },
|
|
170
|
+
];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Shows the menu and returns the command chosen, or null to quit. Tours and
|
|
174
|
+
* More open their own short list, with Back to return.
|
|
175
|
+
*/
|
|
176
|
+
export async function runMenu(status, deps = {}) {
|
|
177
|
+
const pick = deps.pick ?? select;
|
|
178
|
+
let section = 'main';
|
|
179
|
+
for (;;) {
|
|
180
|
+
const choices = buildMenu(status, section);
|
|
181
|
+
const note = section === 'main' ? whoNote(status) : dim(section === 'tours' ? 'Tours' : 'More');
|
|
182
|
+
const chosen = deps.numbered ? await numbered(choices, note) : await pick({ choices, note, minimal: true });
|
|
183
|
+
if (!chosen || chosen[0] === GO.quit[0])
|
|
184
|
+
return null;
|
|
185
|
+
if (chosen[0] === GO.back[0])
|
|
186
|
+
section = 'main';
|
|
187
|
+
else if (chosen[0] === GO.tours[0])
|
|
188
|
+
section = 'tours';
|
|
189
|
+
else if (chosen[0] === GO.more[0])
|
|
190
|
+
section = 'more';
|
|
191
|
+
else
|
|
192
|
+
return chosen;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function numbered(choices, note) {
|
|
196
|
+
console.log(`
|
|
197
|
+
${note}
|
|
198
|
+
`);
|
|
199
|
+
choices.forEach((c, i) => console.log(` ${dim(String(i + 1).padStart(2))} ${c.label}`));
|
|
200
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
201
|
+
try {
|
|
202
|
+
const def = Math.max(0, choices.findIndex((c) => c.recommended)) + 1;
|
|
203
|
+
const answer = (await rl.question(`
|
|
204
|
+
${dim(`Choose 1-${choices.length} [${def}]`)}: `)).trim().toLowerCase();
|
|
205
|
+
if (answer === 'q')
|
|
206
|
+
return null;
|
|
207
|
+
const n = answer === '' ? def : Number(answer);
|
|
208
|
+
return Number.isInteger(n) && choices[n - 1] ? choices[n - 1].value : null;
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
rl.close();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type OpenApiOptions = {
|
|
2
|
+
/** A spec file (.json, .yaml, .yml) or an http(s) URL serving one. */
|
|
3
|
+
from: string;
|
|
4
|
+
/** Where to write the result, relative to the repository root. */
|
|
5
|
+
output: string;
|
|
6
|
+
/** Path globs to keep. Empty keeps every path. */
|
|
7
|
+
include: string[];
|
|
8
|
+
/** Path globs to drop, applied after `include`. */
|
|
9
|
+
exclude: string[];
|
|
10
|
+
/** Property names removed from every schema and example. */
|
|
11
|
+
redact: string[];
|
|
12
|
+
};
|
|
13
|
+
export type OpenApiResult = {
|
|
14
|
+
spec: Record<string, unknown>;
|
|
15
|
+
text: string;
|
|
16
|
+
operations: number;
|
|
17
|
+
droppedPaths: string[];
|
|
18
|
+
redacted: number;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* `*` matches within one path segment and `**` across them, so
|
|
22
|
+
* `/internal/**` drops everything under /internal and `/v1/*` keeps only
|
|
23
|
+
* direct children of /v1.
|
|
24
|
+
*/
|
|
25
|
+
export declare function globToRegExp(glob: string): RegExp;
|
|
26
|
+
/**
|
|
27
|
+
* Turns an existing OpenAPI 3 spec into the file DocuGate will read from the
|
|
28
|
+
* repository: only the paths meant to be public, without redacted fields, in
|
|
29
|
+
* a stable order so it diffs cleanly in review.
|
|
30
|
+
*
|
|
31
|
+
* It does not infer a spec from code. Frameworks that know their own schemas
|
|
32
|
+
* (NestJS, Fastify, FastAPI, Hono with zod-openapi) already emit one; point
|
|
33
|
+
* `from` at that file or at the URL the running server serves it on.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildOpenApi(root: string, opts: OpenApiOptions): Promise<OpenApiResult>;
|
|
36
|
+
/** Whether the committed file already matches what would be written. */
|
|
37
|
+
export declare function isCurrent(root: string, output: string, text: string): boolean;
|
|
38
|
+
export declare function writeOpenApi(root: string, output: string, text: string): void;
|
package/dist/openapi.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { parse as parseYaml } from 'yaml';
|
|
4
|
+
import { readText } from './text.js';
|
|
4
5
|
const METHODS = new Set(['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']);
|
|
5
6
|
/**
|
|
6
7
|
* `*` matches within one path segment and `**` across them, so
|
|
@@ -35,7 +36,7 @@ async function readSource(root, from) {
|
|
|
35
36
|
const path = resolve(root, from);
|
|
36
37
|
if (!existsSync(path))
|
|
37
38
|
throw new Error(`${from} does not exist.`);
|
|
38
|
-
return { text:
|
|
39
|
+
return { text: readText(path), yaml: /\.ya?ml$/i.test(from) };
|
|
39
40
|
}
|
|
40
41
|
/** Every `$ref` in the document, so external ones can be refused. */
|
|
41
42
|
function refsIn(value, out = []) {
|
package/dist/rules.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How DocuGate reads a repository, restated for the command line.
|
|
3
|
+
*
|
|
4
|
+
* These mirror src/lib/docs.ts and server/github.ts in the DocuGate app. They
|
|
5
|
+
* are copied rather than imported because this package is published on its
|
|
6
|
+
* own; if the app changes how it reads a repository, change them here too, or
|
|
7
|
+
* `docugate check` will pass docs the server then renders differently.
|
|
8
|
+
*/
|
|
9
|
+
/** The server refuses a docs folder with more markdown files than this. */
|
|
10
|
+
export declare const MAX_FILES = 300;
|
|
11
|
+
/** Turn `guides/deploying.md` into `guides/deploying`, and `index.md` into ''. */
|
|
12
|
+
export declare function pathToSlug(path: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a relative markdown link, as authored inside `fromPath`, to the slug
|
|
15
|
+
* DocuGate routes it under. Null for links this does not apply to: external
|
|
16
|
+
* URLs, in-page anchors and anything that is not a markdown file.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveRelativeLink(href: string, fromPath: string): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Where a docs folder has no index.md, its top-level README becomes the
|
|
21
|
+
* landing page — the same file GitHub shows first.
|
|
22
|
+
*/
|
|
23
|
+
export declare function landingPath(paths: string[]): string | null;
|
|
24
|
+
/** The page title DocuGate shows: the first `# heading`, if there is one. */
|
|
25
|
+
export declare function firstHeading(content: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* How `docugate sync` keeps the docs in step with the code. The server never
|
|
28
|
+
* reads this: it is only for the command line and the workflow it writes.
|
|
29
|
+
*/
|
|
30
|
+
export type SyncConfig = {
|
|
31
|
+
/** Which AI provider writes the edits. Defaults to whichever key is set. */
|
|
32
|
+
provider?: 'anthropic' | 'openai';
|
|
33
|
+
model?: string;
|
|
34
|
+
/** Path globs of code worth documenting. Empty means every changed file. */
|
|
35
|
+
include?: string[];
|
|
36
|
+
/** Path globs to leave out, on top of what is always left out. */
|
|
37
|
+
exclude?: string[];
|
|
38
|
+
/** Pin a page to the code it documents: `{"docs/api.md": ["server/routes/**"]}`. */
|
|
39
|
+
map?: Record<string, string[]>;
|
|
40
|
+
/** Most pages one run may change. */
|
|
41
|
+
maxFiles?: number;
|
|
42
|
+
/** Also write a short "what changed" page for each run. */
|
|
43
|
+
digest?: boolean;
|
|
44
|
+
};
|
|
45
|
+
export declare const FRESHNESS_VALUES: readonly ["push", "daily", "weekly", "off"];
|
|
46
|
+
/** The fields docugate.json may carry, and what each must be. */
|
|
47
|
+
export type DocugateConfig = {
|
|
48
|
+
docsDir?: string;
|
|
49
|
+
title?: string;
|
|
50
|
+
role?: 'frontend' | 'backend' | 'both';
|
|
51
|
+
/** How often DocuGate checks the docs still match the code. */
|
|
52
|
+
freshness?: 'push' | 'daily' | 'weekly' | 'off';
|
|
53
|
+
/** The DocuGate space the docs publish to, as owner/slug. */
|
|
54
|
+
space?: string;
|
|
55
|
+
sidebar?: string[];
|
|
56
|
+
sync?: SyncConfig;
|
|
57
|
+
api?: {
|
|
58
|
+
/** Where the OpenAPI file is, when it is not openapi.json/.yaml at the root or in the docs folder. */
|
|
59
|
+
spec?: string;
|
|
60
|
+
generate?: {
|
|
61
|
+
from?: string;
|
|
62
|
+
output?: string;
|
|
63
|
+
include?: string[];
|
|
64
|
+
exclude?: string[];
|
|
65
|
+
redact?: string[];
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export declare const KNOWN_KEYS: Set<string>;
|
package/dist/rules.js
CHANGED
|
@@ -58,4 +58,5 @@ export function firstHeading(content) {
|
|
|
58
58
|
const heading = content.match(/^#\s+(.+)$/m);
|
|
59
59
|
return heading ? heading[1].trim() : null;
|
|
60
60
|
}
|
|
61
|
-
export const
|
|
61
|
+
export const FRESHNESS_VALUES = ['push', 'daily', 'weekly', 'off'];
|
|
62
|
+
export const KNOWN_KEYS = new Set(['$schema', 'docsDir', 'title', 'role', 'freshness', 'space', 'sidebar', 'sync', 'api']);
|
package/dist/select.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export type Choice<T> = {
|
|
2
|
+
value: T;
|
|
3
|
+
label: string;
|
|
4
|
+
/** A short dim column to the right: a status, a group, a time. */
|
|
5
|
+
status?: string;
|
|
6
|
+
/** Shown under the list while this choice is highlighted. */
|
|
7
|
+
description?: string;
|
|
8
|
+
/** Words that also match a search, like a command's name. */
|
|
9
|
+
keywords?: string;
|
|
10
|
+
/** The suggested choice: marked, and the one Enter lands on to begin with. */
|
|
11
|
+
recommended?: boolean;
|
|
12
|
+
/** Why it cannot be chosen right now, if so. */
|
|
13
|
+
disabled?: string;
|
|
14
|
+
};
|
|
15
|
+
export type PickerState = {
|
|
16
|
+
query: string;
|
|
17
|
+
cursor: number;
|
|
18
|
+
top: number;
|
|
19
|
+
};
|
|
20
|
+
export type Key = {
|
|
21
|
+
name?: string;
|
|
22
|
+
ctrl?: boolean;
|
|
23
|
+
meta?: boolean;
|
|
24
|
+
sequence?: string;
|
|
25
|
+
};
|
|
26
|
+
/** How well `text` matches a search: lower is better, null is no match. */
|
|
27
|
+
export declare function matchScore(text: string, query: string): number | null;
|
|
28
|
+
export declare function filterChoices<T>(choices: Choice<T>[], query: string): Choice<T>[];
|
|
29
|
+
/** The first choice Enter should land on: the recommended one, else the first that can be chosen. */
|
|
30
|
+
export declare function initialCursor<T>(choices: Choice<T>[]): number;
|
|
31
|
+
export type KeyResult<T> = {
|
|
32
|
+
state: PickerState;
|
|
33
|
+
} | {
|
|
34
|
+
done: 'chosen';
|
|
35
|
+
choice: Choice<T>;
|
|
36
|
+
} | {
|
|
37
|
+
done: 'cancelled';
|
|
38
|
+
} | {
|
|
39
|
+
done: 'blocked';
|
|
40
|
+
reason: string;
|
|
41
|
+
state: PickerState;
|
|
42
|
+
};
|
|
43
|
+
/** One key press, applied to the picker's state. */
|
|
44
|
+
export declare function handleKey<T>(choices: Choice<T>[], state: PickerState, str: string | undefined, key: Key, rows: number,
|
|
45
|
+
/** False for a short menu: letters do not search, a number picks, q quits. */
|
|
46
|
+
search?: boolean): KeyResult<T>;
|
|
47
|
+
export type RenderOptions = {
|
|
48
|
+
title?: string;
|
|
49
|
+
width: number;
|
|
50
|
+
rows: number;
|
|
51
|
+
footer?: string;
|
|
52
|
+
note?: string;
|
|
53
|
+
legend?: boolean;
|
|
54
|
+
/** Just the note and a numbered list: no search, rules, description, legend or key hints. */
|
|
55
|
+
minimal?: boolean;
|
|
56
|
+
};
|
|
57
|
+
/** The picker as lines of text. */
|
|
58
|
+
export declare function renderPicker<T>(choices: Choice<T>[], state: PickerState, o: RenderOptions): string[];
|
|
59
|
+
export type PickerIO = {
|
|
60
|
+
stdin: NodeJS.ReadStream;
|
|
61
|
+
stdout: NodeJS.WriteStream;
|
|
62
|
+
};
|
|
63
|
+
export type SelectOptions<T> = {
|
|
64
|
+
title?: string;
|
|
65
|
+
choices: Choice<T>[];
|
|
66
|
+
footer?: string;
|
|
67
|
+
/** A line shown under the list, like "signed in as @you". */
|
|
68
|
+
note?: string;
|
|
69
|
+
/** How many choices show at once. */
|
|
70
|
+
rows?: number;
|
|
71
|
+
legend?: boolean;
|
|
72
|
+
/** The short form: see renderPicker. Every choice shows; a number picks one. */
|
|
73
|
+
minimal?: boolean;
|
|
74
|
+
};
|
|
75
|
+
/** Whether there is a real terminal to draw a picker in. */
|
|
76
|
+
export declare const canPick: (io?: PickerIO) => boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Draws the picker and resolves with the chosen value, or null if the person
|
|
79
|
+
* pressed escape or Ctrl+C. Draws in place and leaves one line behind.
|
|
80
|
+
*/
|
|
81
|
+
export declare function select<T>(options: SelectOptions<T>, io?: PickerIO): Promise<T | null>;
|