@gitpagedocs/cli 1.1.62 → 1.1.63
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 +193 -193
- package/ai/application/ai-command.ts +24 -24
- package/ai/application/ai-provider-factory.ts +22 -22
- package/ai/application/docs-pattern.ts +68 -68
- package/ai/application/run-ai-cli-command.ts +167 -167
- package/ai/config.ts +50 -50
- package/ai/core/models/ai-cli-config.ts +33 -33
- package/ai/core/models/parse-ai-cli-config.ts +78 -78
- package/ai/core/ports/illm-provider.ts +32 -32
- package/ai/infrastructure/ai-config-file.ts +101 -101
- package/ai/infrastructure/file-system-adapter.ts +85 -85
- package/ai/infrastructure/llm/tools-llm-provider.ts +54 -54
- package/ai/infrastructure/version-docs-writer.ts +143 -143
- package/ai/presentation/ai-prompts.ts +239 -239
- package/application/ports/cli-runtime-ports.ts +91 -91
- package/application/report/config-only-reporter.mjs +64 -64
- package/application/use-cases/dispatch-mode.ts +18 -18
- package/builders/config-orchestrator.mjs +34 -34
- package/builders/project-links.mjs +50 -50
- package/builders/root-config-builder.mjs +61 -61
- package/builders/route-builders.mjs +136 -136
- package/builders/theme-template.mjs +76 -76
- package/builders/version-config-builder.mjs +186 -186
- package/content/docs-en.mjs +549 -549
- package/content/docs-es.mjs +462 -462
- package/content/docs-pt.mjs +462 -462
- package/contracts/doc-versions.mjs +3 -3
- package/data/default-site-config.mjs +321 -321
- package/data/i18n-langmenu.mjs +347 -347
- package/data/layouts.mjs +436 -436
- package/data/path-mappings.mjs +26 -26
- package/data/route-metas.mjs +93 -93
- package/data/theme-colors.mjs +443 -443
- package/data/urls.mjs +12 -12
- package/domain/models/cli-options.ts +33 -33
- package/home/constants.mjs +4 -4
- package/index.mjs +37 -37
- package/infrastructure/gitpagedocs-config-file.ts +47 -47
- package/infrastructure/home/runtime.mjs +37 -37
- package/package.json +3 -3
- package/presentation/commands/ai-info.ts +57 -57
- package/presentation/commands/config-clear.ts +23 -23
- package/presentation/commands/config-info.ts +24 -24
- package/presentation/commands/diagnostics.ts +164 -164
- package/presentation/commands/docs.ts +37 -37
- package/presentation/commands/mcp.ts +20 -20
- package/presentation/commands/pages.ts +101 -101
- package/presentation/commands/password.ts +62 -62
- package/presentation/commands/run-command.ts +70 -70
- package/presentation/index.ts +124 -124
- package/presentation/options/parser.ts +119 -119
- package/presentation/options/resolver.ts +46 -46
- package/presentation/ui/clack.ts +91 -91
- package/presentation/ui/prompts.ts +146 -146
- package/runtime/git-ops.mjs +110 -110
- package/runtime/output.mjs +159 -159
- package/runtime/workflow.mjs +129 -129
- package/start.mjs +12 -12
- package/ui/logger.mjs +9 -9
|
@@ -1,164 +1,164 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
3
|
-
import { execSync } from "node:child_process";
|
|
4
|
-
import { defaultConfigLoader } from "@gitpagedocs/tools";
|
|
5
|
-
import type { CommandContext } from "./run-command";
|
|
6
|
-
|
|
7
|
-
function readVersion(pkgRoot: string): string {
|
|
8
|
-
try {
|
|
9
|
-
const pkg = JSON.parse(readFileSync(path.join(pkgRoot, "package.json"), "utf8"));
|
|
10
|
-
return String(pkg.version ?? "unknown");
|
|
11
|
-
} catch {
|
|
12
|
-
return "unknown";
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function runVersion(ctx: CommandContext): Promise<void> {
|
|
17
|
-
// eslint-disable-next-line no-console
|
|
18
|
-
console.log(`gitpagedocs ${readVersion(ctx.pkgRoot)} (node ${process.version})`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function probe(label: string, command: string): { label: string; ok: boolean; detail: string } {
|
|
22
|
-
try {
|
|
23
|
-
const out = execSync(command, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
24
|
-
return { label, ok: true, detail: out.split("\n")[0] };
|
|
25
|
-
} catch {
|
|
26
|
-
return { label, ok: false, detail: "not found" };
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export async function runDoctor(ctx: CommandContext): Promise<void> {
|
|
31
|
-
const checks: Array<{ label: string; ok: boolean; detail: string }> = [];
|
|
32
|
-
checks.push({ label: "node", ok: true, detail: process.version });
|
|
33
|
-
checks.push(probe("git", "git --version"));
|
|
34
|
-
checks.push(probe("gh (GitHub CLI)", "gh --version"));
|
|
35
|
-
checks.push(probe("pnpm", "pnpm --version"));
|
|
36
|
-
|
|
37
|
-
const configPath = await defaultConfigLoader.resolveConfigPath(ctx.cwd);
|
|
38
|
-
checks.push({
|
|
39
|
-
label: "gitpagedocs config",
|
|
40
|
-
ok: Boolean(configPath),
|
|
41
|
-
detail: configPath ? path.relative(ctx.cwd, configPath) : "not found (run `gitpagedocs init`)",
|
|
42
|
-
});
|
|
43
|
-
checks.push({
|
|
44
|
-
label: "gitpagedocs/ directory",
|
|
45
|
-
ok: existsSync(path.join(ctx.cwd, "gitpagedocs")),
|
|
46
|
-
detail: existsSync(path.join(ctx.cwd, "gitpagedocs")) ? "present" : "missing",
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// eslint-disable-next-line no-console
|
|
50
|
-
console.log("\n gitpagedocs doctor\n");
|
|
51
|
-
for (const c of checks) {
|
|
52
|
-
// eslint-disable-next-line no-console
|
|
53
|
-
console.log(` ${c.ok ? "[ok]" : "[x] "} ${c.label.padEnd(22)} ${c.detail}`);
|
|
54
|
-
}
|
|
55
|
-
const failed = checks.filter((c) => !c.ok && c.label !== "gh (GitHub CLI)").length;
|
|
56
|
-
// eslint-disable-next-line no-console
|
|
57
|
-
console.log(`\n ${failed === 0 ? "All required checks passed." : `${failed} check(s) need attention.`}\n`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Used only when package.json is unreadable — the CLI's published name. */
|
|
61
|
-
const FALLBACK_PACKAGE = "@gitpagedocs/cli";
|
|
62
|
-
/** The bin this package installs; shown alongside the package name. */
|
|
63
|
-
const BIN_NAME = "gitpagedocs";
|
|
64
|
-
|
|
65
|
-
interface PackageIdentity {
|
|
66
|
-
name: string;
|
|
67
|
-
version: string;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Read the identity from the installed package.json rather than hardcoding it,
|
|
72
|
-
* so the hint always names the package the user actually has. A hardcoded name
|
|
73
|
-
* is how `update` ended up advertising an unpublished package.
|
|
74
|
-
*/
|
|
75
|
-
function readPackageIdentity(pkgRoot: string): PackageIdentity {
|
|
76
|
-
try {
|
|
77
|
-
const pkg = JSON.parse(readFileSync(path.join(pkgRoot, "package.json"), "utf8"));
|
|
78
|
-
const name = typeof pkg.name === "string" && pkg.name.length > 0 ? pkg.name : FALLBACK_PACKAGE;
|
|
79
|
-
return { name, version: String(pkg.version ?? "unknown") };
|
|
80
|
-
} catch {
|
|
81
|
-
return { name: FALLBACK_PACKAGE, version: "unknown" };
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
type RegistryLookup =
|
|
86
|
-
| { status: "ok"; latest: string }
|
|
87
|
-
| { status: "missing" }
|
|
88
|
-
| { status: "unreachable" };
|
|
89
|
-
|
|
90
|
-
function registryBase(): string {
|
|
91
|
-
const configured = process.env.npm_config_registry || "https://registry.npmjs.org";
|
|
92
|
-
return configured.endsWith("/") ? configured.slice(0, -1) : configured;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** Resolve the dist-tag `latest` for a package. Never throws. */
|
|
96
|
-
async function fetchLatest(name: string, timeoutMs = 6000): Promise<RegistryLookup> {
|
|
97
|
-
const encoded = name.split("/").map(encodeURIComponent).join("/");
|
|
98
|
-
try {
|
|
99
|
-
const response = await fetch(`${registryBase()}/${encoded}/latest`, {
|
|
100
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
101
|
-
});
|
|
102
|
-
if (response.status === 404) return { status: "missing" };
|
|
103
|
-
if (!response.ok) return { status: "unreachable" };
|
|
104
|
-
const body = (await response.json()) as { version?: unknown };
|
|
105
|
-
if (typeof body.version === "string" && body.version.length > 0) {
|
|
106
|
-
return { status: "ok", latest: body.version };
|
|
107
|
-
}
|
|
108
|
-
return { status: "unreachable" };
|
|
109
|
-
} catch {
|
|
110
|
-
return { status: "unreachable" };
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** Numeric version compare, prerelease suffix ignored. */
|
|
115
|
-
function compareVersions(a: string, b: string): number {
|
|
116
|
-
const parts = (value: string): number[] =>
|
|
117
|
-
value.split("-")[0].split(".").map((piece) => Number.parseInt(piece, 10) || 0);
|
|
118
|
-
const left = parts(a);
|
|
119
|
-
const right = parts(b);
|
|
120
|
-
for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
|
|
121
|
-
const diff = (left[i] ?? 0) - (right[i] ?? 0);
|
|
122
|
-
if (diff !== 0) return diff < 0 ? -1 : 1;
|
|
123
|
-
}
|
|
124
|
-
return 0;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export async function runUpdate(ctx: CommandContext): Promise<void> {
|
|
128
|
-
const { name, version } = readPackageIdentity(ctx.pkgRoot);
|
|
129
|
-
const lookup = await fetchLatest(name);
|
|
130
|
-
const label = name === BIN_NAME ? `${BIN_NAME} ${version}` : `${BIN_NAME} ${version} (${name})`;
|
|
131
|
-
const lines = ["", ` Installed: ${label}`];
|
|
132
|
-
|
|
133
|
-
if (lookup.status === "ok") {
|
|
134
|
-
lines.push(` Latest: ${lookup.latest}`);
|
|
135
|
-
} else if (lookup.status === "missing") {
|
|
136
|
-
lines.push(` Latest: unavailable — the registry has no published ${name}`);
|
|
137
|
-
} else {
|
|
138
|
-
lines.push(" Latest: unknown — could not reach the npm registry");
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (lookup.status === "missing") {
|
|
142
|
-
lines.push(
|
|
143
|
-
"",
|
|
144
|
-
" Nothing can be installed under that name. If the package was renamed",
|
|
145
|
-
" or unpublished, check the current one:",
|
|
146
|
-
" https://www.npmjs.com/package/@gitpagedocs/cli",
|
|
147
|
-
);
|
|
148
|
-
} else if (lookup.status === "ok" && compareVersions(version, lookup.latest) >= 0) {
|
|
149
|
-
lines.push("", " Already on the latest published version.");
|
|
150
|
-
} else {
|
|
151
|
-
const target = lookup.status === "ok" ? lookup.latest : "latest";
|
|
152
|
-
lines.push(
|
|
153
|
-
"",
|
|
154
|
-
" To update the global CLI:",
|
|
155
|
-
` npm i -g ${name}@${target}`,
|
|
156
|
-
" Or, in a project:",
|
|
157
|
-
` pnpm add -D ${name}@${target}`,
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
lines.push("");
|
|
162
|
-
// eslint-disable-next-line no-console
|
|
163
|
-
console.log(lines.join("\n"));
|
|
164
|
-
}
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { defaultConfigLoader } from "@gitpagedocs/tools";
|
|
5
|
+
import type { CommandContext } from "./run-command";
|
|
6
|
+
|
|
7
|
+
function readVersion(pkgRoot: string): string {
|
|
8
|
+
try {
|
|
9
|
+
const pkg = JSON.parse(readFileSync(path.join(pkgRoot, "package.json"), "utf8"));
|
|
10
|
+
return String(pkg.version ?? "unknown");
|
|
11
|
+
} catch {
|
|
12
|
+
return "unknown";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function runVersion(ctx: CommandContext): Promise<void> {
|
|
17
|
+
// eslint-disable-next-line no-console
|
|
18
|
+
console.log(`gitpagedocs ${readVersion(ctx.pkgRoot)} (node ${process.version})`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function probe(label: string, command: string): { label: string; ok: boolean; detail: string } {
|
|
22
|
+
try {
|
|
23
|
+
const out = execSync(command, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
24
|
+
return { label, ok: true, detail: out.split("\n")[0] };
|
|
25
|
+
} catch {
|
|
26
|
+
return { label, ok: false, detail: "not found" };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function runDoctor(ctx: CommandContext): Promise<void> {
|
|
31
|
+
const checks: Array<{ label: string; ok: boolean; detail: string }> = [];
|
|
32
|
+
checks.push({ label: "node", ok: true, detail: process.version });
|
|
33
|
+
checks.push(probe("git", "git --version"));
|
|
34
|
+
checks.push(probe("gh (GitHub CLI)", "gh --version"));
|
|
35
|
+
checks.push(probe("pnpm", "pnpm --version"));
|
|
36
|
+
|
|
37
|
+
const configPath = await defaultConfigLoader.resolveConfigPath(ctx.cwd);
|
|
38
|
+
checks.push({
|
|
39
|
+
label: "gitpagedocs config",
|
|
40
|
+
ok: Boolean(configPath),
|
|
41
|
+
detail: configPath ? path.relative(ctx.cwd, configPath) : "not found (run `gitpagedocs init`)",
|
|
42
|
+
});
|
|
43
|
+
checks.push({
|
|
44
|
+
label: "gitpagedocs/ directory",
|
|
45
|
+
ok: existsSync(path.join(ctx.cwd, "gitpagedocs")),
|
|
46
|
+
detail: existsSync(path.join(ctx.cwd, "gitpagedocs")) ? "present" : "missing",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// eslint-disable-next-line no-console
|
|
50
|
+
console.log("\n gitpagedocs doctor\n");
|
|
51
|
+
for (const c of checks) {
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
console.log(` ${c.ok ? "[ok]" : "[x] "} ${c.label.padEnd(22)} ${c.detail}`);
|
|
54
|
+
}
|
|
55
|
+
const failed = checks.filter((c) => !c.ok && c.label !== "gh (GitHub CLI)").length;
|
|
56
|
+
// eslint-disable-next-line no-console
|
|
57
|
+
console.log(`\n ${failed === 0 ? "All required checks passed." : `${failed} check(s) need attention.`}\n`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Used only when package.json is unreadable — the CLI's published name. */
|
|
61
|
+
const FALLBACK_PACKAGE = "@gitpagedocs/cli";
|
|
62
|
+
/** The bin this package installs; shown alongside the package name. */
|
|
63
|
+
const BIN_NAME = "gitpagedocs";
|
|
64
|
+
|
|
65
|
+
interface PackageIdentity {
|
|
66
|
+
name: string;
|
|
67
|
+
version: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Read the identity from the installed package.json rather than hardcoding it,
|
|
72
|
+
* so the hint always names the package the user actually has. A hardcoded name
|
|
73
|
+
* is how `update` ended up advertising an unpublished package.
|
|
74
|
+
*/
|
|
75
|
+
function readPackageIdentity(pkgRoot: string): PackageIdentity {
|
|
76
|
+
try {
|
|
77
|
+
const pkg = JSON.parse(readFileSync(path.join(pkgRoot, "package.json"), "utf8"));
|
|
78
|
+
const name = typeof pkg.name === "string" && pkg.name.length > 0 ? pkg.name : FALLBACK_PACKAGE;
|
|
79
|
+
return { name, version: String(pkg.version ?? "unknown") };
|
|
80
|
+
} catch {
|
|
81
|
+
return { name: FALLBACK_PACKAGE, version: "unknown" };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type RegistryLookup =
|
|
86
|
+
| { status: "ok"; latest: string }
|
|
87
|
+
| { status: "missing" }
|
|
88
|
+
| { status: "unreachable" };
|
|
89
|
+
|
|
90
|
+
function registryBase(): string {
|
|
91
|
+
const configured = process.env.npm_config_registry || "https://registry.npmjs.org";
|
|
92
|
+
return configured.endsWith("/") ? configured.slice(0, -1) : configured;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Resolve the dist-tag `latest` for a package. Never throws. */
|
|
96
|
+
async function fetchLatest(name: string, timeoutMs = 6000): Promise<RegistryLookup> {
|
|
97
|
+
const encoded = name.split("/").map(encodeURIComponent).join("/");
|
|
98
|
+
try {
|
|
99
|
+
const response = await fetch(`${registryBase()}/${encoded}/latest`, {
|
|
100
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
101
|
+
});
|
|
102
|
+
if (response.status === 404) return { status: "missing" };
|
|
103
|
+
if (!response.ok) return { status: "unreachable" };
|
|
104
|
+
const body = (await response.json()) as { version?: unknown };
|
|
105
|
+
if (typeof body.version === "string" && body.version.length > 0) {
|
|
106
|
+
return { status: "ok", latest: body.version };
|
|
107
|
+
}
|
|
108
|
+
return { status: "unreachable" };
|
|
109
|
+
} catch {
|
|
110
|
+
return { status: "unreachable" };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Numeric version compare, prerelease suffix ignored. */
|
|
115
|
+
function compareVersions(a: string, b: string): number {
|
|
116
|
+
const parts = (value: string): number[] =>
|
|
117
|
+
value.split("-")[0].split(".").map((piece) => Number.parseInt(piece, 10) || 0);
|
|
118
|
+
const left = parts(a);
|
|
119
|
+
const right = parts(b);
|
|
120
|
+
for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
|
|
121
|
+
const diff = (left[i] ?? 0) - (right[i] ?? 0);
|
|
122
|
+
if (diff !== 0) return diff < 0 ? -1 : 1;
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function runUpdate(ctx: CommandContext): Promise<void> {
|
|
128
|
+
const { name, version } = readPackageIdentity(ctx.pkgRoot);
|
|
129
|
+
const lookup = await fetchLatest(name);
|
|
130
|
+
const label = name === BIN_NAME ? `${BIN_NAME} ${version}` : `${BIN_NAME} ${version} (${name})`;
|
|
131
|
+
const lines = ["", ` Installed: ${label}`];
|
|
132
|
+
|
|
133
|
+
if (lookup.status === "ok") {
|
|
134
|
+
lines.push(` Latest: ${lookup.latest}`);
|
|
135
|
+
} else if (lookup.status === "missing") {
|
|
136
|
+
lines.push(` Latest: unavailable — the registry has no published ${name}`);
|
|
137
|
+
} else {
|
|
138
|
+
lines.push(" Latest: unknown — could not reach the npm registry");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (lookup.status === "missing") {
|
|
142
|
+
lines.push(
|
|
143
|
+
"",
|
|
144
|
+
" Nothing can be installed under that name. If the package was renamed",
|
|
145
|
+
" or unpublished, check the current one:",
|
|
146
|
+
" https://www.npmjs.com/package/@gitpagedocs/cli",
|
|
147
|
+
);
|
|
148
|
+
} else if (lookup.status === "ok" && compareVersions(version, lookup.latest) >= 0) {
|
|
149
|
+
lines.push("", " Already on the latest published version.");
|
|
150
|
+
} else {
|
|
151
|
+
const target = lookup.status === "ok" ? lookup.latest : "latest";
|
|
152
|
+
lines.push(
|
|
153
|
+
"",
|
|
154
|
+
" To update the global CLI:",
|
|
155
|
+
` npm i -g ${name}@${target}`,
|
|
156
|
+
" Or, in a project:",
|
|
157
|
+
` pnpm add -D ${name}@${target}`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
lines.push("");
|
|
162
|
+
// eslint-disable-next-line no-console
|
|
163
|
+
console.log(lines.join("\n"));
|
|
164
|
+
}
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FileService,
|
|
3
|
-
DocUpdater,
|
|
4
|
-
providersSection,
|
|
5
|
-
cliCommandsSection,
|
|
6
|
-
devWorkflowSection,
|
|
7
|
-
securityNoteSection,
|
|
8
|
-
CLI_COMMANDS,
|
|
9
|
-
} from "@gitpagedocs/tools";
|
|
10
|
-
import type { CommandContext } from "./run-command";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* `gitpagedocs docs` — refresh the managed regions of the standard docs files
|
|
14
|
-
* with deterministic content. Idempotent and marker-bounded: manual content
|
|
15
|
-
* outside `<!-- gitpagedocs:start/end -->` is preserved; missing files are
|
|
16
|
-
* created. Covers README, CONTRIBUTING and SECURITY (master-spec docs set).
|
|
17
|
-
*/
|
|
18
|
-
export async function runDocs(ctx: CommandContext): Promise<void> {
|
|
19
|
-
const updater = new DocUpdater(new FileService(ctx.cwd));
|
|
20
|
-
|
|
21
|
-
const updates = [
|
|
22
|
-
{ path: "README.md", generated: `${providersSection()}\n\n${cliCommandsSection(CLI_COMMANDS)}` },
|
|
23
|
-
{ path: "CONTRIBUTING.md", generated: `${devWorkflowSection()}\n\n${cliCommandsSection(CLI_COMMANDS)}` },
|
|
24
|
-
{ path: "SECURITY.md", generated: securityNoteSection() },
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
const results = await updater.updateManagedFiles(updates);
|
|
28
|
-
// eslint-disable-next-line no-console
|
|
29
|
-
console.log("");
|
|
30
|
-
for (const r of results) {
|
|
31
|
-
const state = r.changed ? (r.replaced ? "managed region updated" : "managed region added") : "already up to date";
|
|
32
|
-
// eslint-disable-next-line no-console
|
|
33
|
-
console.log(` ${r.path}: ${state}.`);
|
|
34
|
-
}
|
|
35
|
-
// eslint-disable-next-line no-console
|
|
36
|
-
console.log("");
|
|
37
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
FileService,
|
|
3
|
+
DocUpdater,
|
|
4
|
+
providersSection,
|
|
5
|
+
cliCommandsSection,
|
|
6
|
+
devWorkflowSection,
|
|
7
|
+
securityNoteSection,
|
|
8
|
+
CLI_COMMANDS,
|
|
9
|
+
} from "@gitpagedocs/tools";
|
|
10
|
+
import type { CommandContext } from "./run-command";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `gitpagedocs docs` — refresh the managed regions of the standard docs files
|
|
14
|
+
* with deterministic content. Idempotent and marker-bounded: manual content
|
|
15
|
+
* outside `<!-- gitpagedocs:start/end -->` is preserved; missing files are
|
|
16
|
+
* created. Covers README, CONTRIBUTING and SECURITY (master-spec docs set).
|
|
17
|
+
*/
|
|
18
|
+
export async function runDocs(ctx: CommandContext): Promise<void> {
|
|
19
|
+
const updater = new DocUpdater(new FileService(ctx.cwd));
|
|
20
|
+
|
|
21
|
+
const updates = [
|
|
22
|
+
{ path: "README.md", generated: `${providersSection()}\n\n${cliCommandsSection(CLI_COMMANDS)}` },
|
|
23
|
+
{ path: "CONTRIBUTING.md", generated: `${devWorkflowSection()}\n\n${cliCommandsSection(CLI_COMMANDS)}` },
|
|
24
|
+
{ path: "SECURITY.md", generated: securityNoteSection() },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const results = await updater.updateManagedFiles(updates);
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log("");
|
|
30
|
+
for (const r of results) {
|
|
31
|
+
const state = r.changed ? (r.replaced ? "managed region updated" : "managed region added") : "already up to date";
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.log(` ${r.path}: ${state}.`);
|
|
34
|
+
}
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.log("");
|
|
37
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import type { CommandContext } from "./run-command";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* `gitpagedocs mcp start` — entry point for the MCP server. The server package
|
|
5
|
-
* (@gitpagedocs/mcp) is implemented in Phase 7; this command is wired to launch
|
|
6
|
-
* it there. Until then it reports status honestly rather than failing silently.
|
|
7
|
-
*/
|
|
8
|
-
export async function runMcp(ctx: CommandContext): Promise<void> {
|
|
9
|
-
const sub = ctx.args[1] ?? "start";
|
|
10
|
-
if (sub !== "start") {
|
|
11
|
-
// eslint-disable-next-line no-console
|
|
12
|
-
console.log(`\n Unknown mcp subcommand: ${sub}\n Usage: gitpagedocs mcp start\n`);
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
// stderr only: stdout is the MCP stdio channel and must stay protocol-clean.
|
|
16
|
-
console.error("[gitpagedocs] Starting MCP server over stdio (Ctrl-C to stop)...");
|
|
17
|
-
// Dynamic import keeps the MCP SDK out of the frontend bundle.
|
|
18
|
-
const { startMcpServer } = await import("@gitpagedocs/mcp");
|
|
19
|
-
await startMcpServer(ctx.cwd);
|
|
20
|
-
}
|
|
1
|
+
import type { CommandContext } from "./run-command";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `gitpagedocs mcp start` — entry point for the MCP server. The server package
|
|
5
|
+
* (@gitpagedocs/mcp) is implemented in Phase 7; this command is wired to launch
|
|
6
|
+
* it there. Until then it reports status honestly rather than failing silently.
|
|
7
|
+
*/
|
|
8
|
+
export async function runMcp(ctx: CommandContext): Promise<void> {
|
|
9
|
+
const sub = ctx.args[1] ?? "start";
|
|
10
|
+
if (sub !== "start") {
|
|
11
|
+
// eslint-disable-next-line no-console
|
|
12
|
+
console.log(`\n Unknown mcp subcommand: ${sub}\n Usage: gitpagedocs mcp start\n`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
// stderr only: stdout is the MCP stdio channel and must stay protocol-clean.
|
|
16
|
+
console.error("[gitpagedocs] Starting MCP server over stdio (Ctrl-C to stop)...");
|
|
17
|
+
// Dynamic import keeps the MCP SDK out of the frontend bundle.
|
|
18
|
+
const { startMcpServer } = await import("@gitpagedocs/mcp");
|
|
19
|
+
await startMcpServer(ctx.cwd);
|
|
20
|
+
}
|
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import type { CommandContext } from "./run-command";
|
|
4
|
-
import { askConfirm } from "../ui/clack";
|
|
5
|
-
import { askOwnerRepo, interactivePromptsAvailable } from "../ui/prompts";
|
|
6
|
-
// @ts-expect-error .mjs runtime module
|
|
7
|
-
import { detectRepoFromGit, getCurrentGitBranch, tryConfigurePagesToGitHubActions } from "../../runtime/git-ops.mjs";
|
|
8
|
-
|
|
9
|
-
/** Read `--name value` (or `--name=value`) from a raw arg list. */
|
|
10
|
-
function readFlag(args: string[], name: string): string {
|
|
11
|
-
const eq = args.find((a) => a.startsWith(`${name}=`));
|
|
12
|
-
if (eq) return eq.slice(name.length + 1).trim();
|
|
13
|
-
const i = args.indexOf(name);
|
|
14
|
-
if (i >= 0 && args[i + 1] && !args[i + 1].startsWith("--")) return args[i + 1].trim();
|
|
15
|
-
return "";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* `gitpagedocs pages actions` / `--pages-actions` — switch the repository's
|
|
20
|
-
* GitHub Pages source to GitHub Actions. Detects owner/repo/branch from git,
|
|
21
|
-
* validates the gh CLI, and confirms before changing anything (no silent
|
|
22
|
-
* overwrite). Does NOT generate docs or push (that is `pages deploy`).
|
|
23
|
-
*/
|
|
24
|
-
export async function runPagesActions(ctx: CommandContext): Promise<void> {
|
|
25
|
-
let repo = detectRepoFromGit(ctx.cwd) as { owner: string; repo: string } | null;
|
|
26
|
-
if (!repo) {
|
|
27
|
-
if (!interactivePromptsAvailable()) {
|
|
28
|
-
// eslint-disable-next-line no-console
|
|
29
|
-
console.log("\n Could not detect a GitHub repo from the git 'origin' remote.\n Add a remote or use `--push --owner <o> --repo <r>`.\n");
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
repo = await askOwnerRepo(null);
|
|
33
|
-
}
|
|
34
|
-
const branch = getCurrentGitBranch(ctx.cwd) as string;
|
|
35
|
-
// eslint-disable-next-line no-console
|
|
36
|
-
console.log(`\n Detected ${repo.owner}/${repo.repo} (branch ${branch}).`);
|
|
37
|
-
const ok = await askConfirm(`Switch GitHub Pages source to GitHub Actions for ${repo.owner}/${repo.repo}?`, false);
|
|
38
|
-
if (!ok) {
|
|
39
|
-
// eslint-disable-next-line no-console
|
|
40
|
-
console.log(" Aborted. No changes made.\n");
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
tryConfigurePagesToGitHubActions(repo.owner, repo.repo, branch, ctx.cwd);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* `gitpagedocs pages deploy` — the full documented deploy flow: resolve
|
|
48
|
-
* owner/repo (from --owner/--repo or the git remote), confirm, then run the
|
|
49
|
-
* proven `--push` path (generate docs + workflow → commit → push → configure
|
|
50
|
-
* Pages→Actions) and print the final site URL. Reuses the legacy push verbatim
|
|
51
|
-
* (spawned), so `--push` behavior stays byte-identical.
|
|
52
|
-
*/
|
|
53
|
-
export async function runPagesDeploy(ctx: CommandContext): Promise<void> {
|
|
54
|
-
let owner = readFlag(ctx.args, "--owner");
|
|
55
|
-
let repo = readFlag(ctx.args, "--repo");
|
|
56
|
-
const docsPath = readFlag(ctx.args, "--path");
|
|
57
|
-
|
|
58
|
-
if (!owner || !repo) {
|
|
59
|
-
const detected = detectRepoFromGit(ctx.cwd) as { owner: string; repo: string } | null;
|
|
60
|
-
if (detected) {
|
|
61
|
-
owner = owner || detected.owner;
|
|
62
|
-
repo = repo || detected.repo;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (!owner || !repo) {
|
|
66
|
-
if (!interactivePromptsAvailable()) {
|
|
67
|
-
// eslint-disable-next-line no-console
|
|
68
|
-
console.log("\n Could not determine owner/repo. Pass `--owner <o> --repo <r>` or add a git 'origin' remote.\n");
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const answered = await askOwnerRepo(null, { owner, repo });
|
|
72
|
-
owner = answered.owner;
|
|
73
|
-
repo = answered.repo;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// eslint-disable-next-line no-console
|
|
77
|
-
console.log(`\n Deploy ${owner}/${repo} to GitHub Pages via Actions${docsPath ? ` (path: ${docsPath})` : ""}.`);
|
|
78
|
-
const ok = await askConfirm(
|
|
79
|
-
`Generate docs + workflow, commit, push, and switch Pages to Actions for ${owner}/${repo}?`,
|
|
80
|
-
false,
|
|
81
|
-
);
|
|
82
|
-
if (!ok) {
|
|
83
|
-
// eslint-disable-next-line no-console
|
|
84
|
-
console.log(" Aborted. No changes made.\n");
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// pkgRoot is the cli package root (cli/), where the bin lives as index.mjs.
|
|
89
|
-
const bin = path.join(ctx.pkgRoot, "index.mjs");
|
|
90
|
-
const pushArgs = ["--push", "--owner", owner, "--repo", repo, ...(docsPath ? ["--path", docsPath] : [])];
|
|
91
|
-
const result = spawnSync(process.execPath, [bin, ...pushArgs], { stdio: "inherit", cwd: ctx.cwd, env: process.env });
|
|
92
|
-
|
|
93
|
-
if (result.status === 0) {
|
|
94
|
-
const seg = docsPath ? `/${docsPath.replace(/^\/+|\/+$/g, "")}` : "";
|
|
95
|
-
// eslint-disable-next-line no-console
|
|
96
|
-
console.log(`\n Deployed. Final URL: https://${owner}.github.io/${repo}${seg}/\n`);
|
|
97
|
-
} else {
|
|
98
|
-
// eslint-disable-next-line no-console
|
|
99
|
-
console.log("\n Deploy did not complete (push step failed). See output above.\n");
|
|
100
|
-
}
|
|
101
|
-
}
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import type { CommandContext } from "./run-command";
|
|
4
|
+
import { askConfirm } from "../ui/clack";
|
|
5
|
+
import { askOwnerRepo, interactivePromptsAvailable } from "../ui/prompts";
|
|
6
|
+
// @ts-expect-error .mjs runtime module
|
|
7
|
+
import { detectRepoFromGit, getCurrentGitBranch, tryConfigurePagesToGitHubActions } from "../../runtime/git-ops.mjs";
|
|
8
|
+
|
|
9
|
+
/** Read `--name value` (or `--name=value`) from a raw arg list. */
|
|
10
|
+
function readFlag(args: string[], name: string): string {
|
|
11
|
+
const eq = args.find((a) => a.startsWith(`${name}=`));
|
|
12
|
+
if (eq) return eq.slice(name.length + 1).trim();
|
|
13
|
+
const i = args.indexOf(name);
|
|
14
|
+
if (i >= 0 && args[i + 1] && !args[i + 1].startsWith("--")) return args[i + 1].trim();
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `gitpagedocs pages actions` / `--pages-actions` — switch the repository's
|
|
20
|
+
* GitHub Pages source to GitHub Actions. Detects owner/repo/branch from git,
|
|
21
|
+
* validates the gh CLI, and confirms before changing anything (no silent
|
|
22
|
+
* overwrite). Does NOT generate docs or push (that is `pages deploy`).
|
|
23
|
+
*/
|
|
24
|
+
export async function runPagesActions(ctx: CommandContext): Promise<void> {
|
|
25
|
+
let repo = detectRepoFromGit(ctx.cwd) as { owner: string; repo: string } | null;
|
|
26
|
+
if (!repo) {
|
|
27
|
+
if (!interactivePromptsAvailable()) {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log("\n Could not detect a GitHub repo from the git 'origin' remote.\n Add a remote or use `--push --owner <o> --repo <r>`.\n");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
repo = await askOwnerRepo(null);
|
|
33
|
+
}
|
|
34
|
+
const branch = getCurrentGitBranch(ctx.cwd) as string;
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.log(`\n Detected ${repo.owner}/${repo.repo} (branch ${branch}).`);
|
|
37
|
+
const ok = await askConfirm(`Switch GitHub Pages source to GitHub Actions for ${repo.owner}/${repo.repo}?`, false);
|
|
38
|
+
if (!ok) {
|
|
39
|
+
// eslint-disable-next-line no-console
|
|
40
|
+
console.log(" Aborted. No changes made.\n");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
tryConfigurePagesToGitHubActions(repo.owner, repo.repo, branch, ctx.cwd);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* `gitpagedocs pages deploy` — the full documented deploy flow: resolve
|
|
48
|
+
* owner/repo (from --owner/--repo or the git remote), confirm, then run the
|
|
49
|
+
* proven `--push` path (generate docs + workflow → commit → push → configure
|
|
50
|
+
* Pages→Actions) and print the final site URL. Reuses the legacy push verbatim
|
|
51
|
+
* (spawned), so `--push` behavior stays byte-identical.
|
|
52
|
+
*/
|
|
53
|
+
export async function runPagesDeploy(ctx: CommandContext): Promise<void> {
|
|
54
|
+
let owner = readFlag(ctx.args, "--owner");
|
|
55
|
+
let repo = readFlag(ctx.args, "--repo");
|
|
56
|
+
const docsPath = readFlag(ctx.args, "--path");
|
|
57
|
+
|
|
58
|
+
if (!owner || !repo) {
|
|
59
|
+
const detected = detectRepoFromGit(ctx.cwd) as { owner: string; repo: string } | null;
|
|
60
|
+
if (detected) {
|
|
61
|
+
owner = owner || detected.owner;
|
|
62
|
+
repo = repo || detected.repo;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!owner || !repo) {
|
|
66
|
+
if (!interactivePromptsAvailable()) {
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console.log("\n Could not determine owner/repo. Pass `--owner <o> --repo <r>` or add a git 'origin' remote.\n");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const answered = await askOwnerRepo(null, { owner, repo });
|
|
72
|
+
owner = answered.owner;
|
|
73
|
+
repo = answered.repo;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// eslint-disable-next-line no-console
|
|
77
|
+
console.log(`\n Deploy ${owner}/${repo} to GitHub Pages via Actions${docsPath ? ` (path: ${docsPath})` : ""}.`);
|
|
78
|
+
const ok = await askConfirm(
|
|
79
|
+
`Generate docs + workflow, commit, push, and switch Pages to Actions for ${owner}/${repo}?`,
|
|
80
|
+
false,
|
|
81
|
+
);
|
|
82
|
+
if (!ok) {
|
|
83
|
+
// eslint-disable-next-line no-console
|
|
84
|
+
console.log(" Aborted. No changes made.\n");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// pkgRoot is the cli package root (cli/), where the bin lives as index.mjs.
|
|
89
|
+
const bin = path.join(ctx.pkgRoot, "index.mjs");
|
|
90
|
+
const pushArgs = ["--push", "--owner", owner, "--repo", repo, ...(docsPath ? ["--path", docsPath] : [])];
|
|
91
|
+
const result = spawnSync(process.execPath, [bin, ...pushArgs], { stdio: "inherit", cwd: ctx.cwd, env: process.env });
|
|
92
|
+
|
|
93
|
+
if (result.status === 0) {
|
|
94
|
+
const seg = docsPath ? `/${docsPath.replace(/^\/+|\/+$/g, "")}` : "";
|
|
95
|
+
// eslint-disable-next-line no-console
|
|
96
|
+
console.log(`\n Deployed. Final URL: https://${owner}.github.io/${repo}${seg}/\n`);
|
|
97
|
+
} else {
|
|
98
|
+
// eslint-disable-next-line no-console
|
|
99
|
+
console.log("\n Deploy did not complete (push step failed). See output above.\n");
|
|
100
|
+
}
|
|
101
|
+
}
|