@gitpagedocs/cli 1.1.44
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 +169 -0
- package/ai/application/ai-command.ts +24 -0
- package/ai/application/ai-provider-factory.ts +22 -0
- package/ai/application/run-ai-cli-command.ts +136 -0
- package/ai/config.ts +22 -0
- package/ai/core/models/ai-cli-config.ts +31 -0
- package/ai/core/ports/illm-provider.ts +32 -0
- package/ai/infrastructure/ai-config-file.ts +32 -0
- package/ai/infrastructure/file-system-adapter.ts +83 -0
- package/ai/infrastructure/llm/tools-llm-provider.ts +54 -0
- package/ai/presentation/ai-prompts.ts +239 -0
- package/application/config-only/handler.mjs +55 -0
- package/application/home/handler.mjs +83 -0
- package/application/ports/cli-runtime-ports.ts +87 -0
- package/application/report/config-only-reporter.mjs +62 -0
- package/application/use-cases/dispatch-mode.ts +18 -0
- package/builders/config-orchestrator.mjs +40 -0
- package/builders/root-config-builder.mjs +54 -0
- package/builders/route-builders.mjs +111 -0
- package/builders/source-viewer.mjs +330 -0
- package/builders/theme-template.mjs +75 -0
- package/builders/version-config-builder.mjs +151 -0
- package/content/docs-en.mjs +542 -0
- package/content/docs-es.mjs +459 -0
- package/content/docs-pt.mjs +459 -0
- package/content/docs.mjs +9 -0
- package/contracts/doc-versions.mjs +1 -0
- package/contracts/index.mjs +1 -0
- package/data/default-site-config.mjs +312 -0
- package/data/i18n-langmenu.mjs +122 -0
- package/data/i18n-translations.mjs +20 -0
- package/data/layouts.mjs +353 -0
- package/data/path-mappings.mjs +34 -0
- package/data/route-metas.mjs +73 -0
- package/data/theme-colors.mjs +387 -0
- package/data/urls.mjs +7 -0
- package/data/version-constants.mjs +3 -0
- package/domain/models/cli-options.ts +18 -0
- package/domain/services/sanitize-segment.mjs +7 -0
- package/home/constants.mjs +4 -0
- package/home/home-file-writer.mjs +20 -0
- package/home/templates.mjs +48 -0
- package/index.mjs +26 -0
- package/infrastructure/config-only/runtime.mjs +17 -0
- package/infrastructure/home/runtime.mjs +37 -0
- package/package.json +60 -0
- package/presentation/commands/ai-info.ts +57 -0
- package/presentation/commands/config-info.ts +24 -0
- package/presentation/commands/diagnostics.ts +70 -0
- package/presentation/commands/docs.ts +37 -0
- package/presentation/commands/mcp.ts +20 -0
- package/presentation/commands/pages.ts +91 -0
- package/presentation/commands/run-command.ts +62 -0
- package/presentation/index.ts +108 -0
- package/presentation/options/parser.ts +104 -0
- package/presentation/options/resolver.ts +25 -0
- package/presentation/options/schema.ts +10 -0
- package/presentation/ui/banner.ts +13 -0
- package/presentation/ui/clack.ts +74 -0
- package/presentation/ui/prompts.ts +64 -0
- package/runtime/doc-path-resolver.mjs +57 -0
- package/runtime/git-ops.mjs +110 -0
- package/runtime/output.mjs +122 -0
- package/runtime/workflow.mjs +144 -0
- package/start.mjs +12 -0
- package/ui/logger.mjs +9 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { PROVIDER_CATALOG, ALL_PROVIDER_IDS, createModelRegistry, type AiProviderId } from "@gitpagedocs/tools";
|
|
2
|
+
import type { CommandContext } from "./run-command";
|
|
3
|
+
|
|
4
|
+
function capsLabel(caps: { streaming: boolean; vision: boolean; audio: boolean }): string {
|
|
5
|
+
const parts: string[] = [];
|
|
6
|
+
if (caps.streaming) parts.push("stream");
|
|
7
|
+
if (caps.vision) parts.push("vision");
|
|
8
|
+
if (caps.audio) parts.push("audio");
|
|
9
|
+
return parts.join(", ") || "text";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** `gitpagedocs provider [id]` — list providers or show one provider's detail. */
|
|
13
|
+
export async function runProvider(ctx: CommandContext): Promise<void> {
|
|
14
|
+
const requested = ctx.args[1] as AiProviderId | undefined;
|
|
15
|
+
if (requested && PROVIDER_CATALOG[requested]) {
|
|
16
|
+
const spec = PROVIDER_CATALOG[requested];
|
|
17
|
+
// eslint-disable-next-line no-console
|
|
18
|
+
console.log(
|
|
19
|
+
`\n ${spec.label} (${spec.id})\n` +
|
|
20
|
+
` default model : ${spec.defaultModel}\n` +
|
|
21
|
+
` capabilities : ${capsLabel(spec.capabilities)}\n` +
|
|
22
|
+
` base url : ${spec.baseUrl || "(user-provided)"}\n` +
|
|
23
|
+
` models : ${spec.models.map((m) => m.id).join(", ")}\n`,
|
|
24
|
+
);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log(`\n AI providers (${ALL_PROVIDER_IDS.length}):\n`);
|
|
30
|
+
for (const id of ALL_PROVIDER_IDS) {
|
|
31
|
+
const spec = PROVIDER_CATALOG[id];
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.log(` ${id.padEnd(14)} ${spec.label.padEnd(18)} ${spec.defaultModel}`);
|
|
34
|
+
}
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.log("\n Detail: gitpagedocs provider <id>\n");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** `gitpagedocs models [provider]` — list models for one or all providers. */
|
|
40
|
+
export async function runModels(ctx: CommandContext): Promise<void> {
|
|
41
|
+
const models = createModelRegistry();
|
|
42
|
+
const requested = ctx.args[1] as AiProviderId | undefined;
|
|
43
|
+
const ids = requested && PROVIDER_CATALOG[requested] ? [requested] : ALL_PROVIDER_IDS;
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.log("");
|
|
46
|
+
for (const id of ids) {
|
|
47
|
+
const list = models.list(id);
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
console.log(` ${PROVIDER_CATALOG[id].label} (${id}):`);
|
|
50
|
+
for (const m of list) {
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console.log(` - ${m.id}${m.id === PROVIDER_CATALOG[id].defaultModel ? " (default)" : ""}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.log("");
|
|
57
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { defaultConfigLoader, isAppError } from "@gitpagedocs/tools";
|
|
3
|
+
import type { CommandContext } from "./run-command";
|
|
4
|
+
|
|
5
|
+
/** `gitpagedocs config` — resolve and summarize the active gitpagedocs config. */
|
|
6
|
+
export async function runConfig(ctx: CommandContext): Promise<void> {
|
|
7
|
+
try {
|
|
8
|
+
const { config, sourcePath, extension } = await defaultConfigLoader.loadGitPageDocsConfig(ctx.cwd);
|
|
9
|
+
const site = (config as { site?: Record<string, unknown> }).site ?? {};
|
|
10
|
+
// eslint-disable-next-line no-console
|
|
11
|
+
console.log(
|
|
12
|
+
`\n Config: ${path.relative(ctx.cwd, sourcePath)} (${extension})\n` +
|
|
13
|
+
` site name : ${String(site.name ?? "—")}\n` +
|
|
14
|
+
` default language : ${String(site.defaultLanguage ?? "—")}\n` +
|
|
15
|
+
` languages : ${
|
|
16
|
+
Array.isArray(site.supportedLanguages) ? (site.supportedLanguages as string[]).join(", ") : "—"
|
|
17
|
+
}\n`,
|
|
18
|
+
);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
const message = isAppError(error) ? error.message : String(error);
|
|
21
|
+
// eslint-disable-next-line no-console
|
|
22
|
+
console.log(`\n No usable config found: ${message}\n Run \`gitpagedocs init\` to generate one.\n`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
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 ? "✓" : "✗"} ${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
|
+
export async function runUpdate(ctx: CommandContext): Promise<void> {
|
|
61
|
+
const current = readVersion(ctx.pkgRoot);
|
|
62
|
+
// eslint-disable-next-line no-console
|
|
63
|
+
console.log(
|
|
64
|
+
`\n Installed: gitpagedocs ${current}\n` +
|
|
65
|
+
" To update the global CLI:\n" +
|
|
66
|
+
" npm i -g gitpagedocs@latest\n" +
|
|
67
|
+
" Or, in a project:\n" +
|
|
68
|
+
" pnpm add -D gitpagedocs@latest\n",
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
// @ts-expect-error .mjs runtime module
|
|
6
|
+
import { detectRepoFromGit, getCurrentGitBranch, tryConfigurePagesToGitHubActions } from "../../runtime/git-ops.mjs";
|
|
7
|
+
|
|
8
|
+
/** Read `--name value` (or `--name=value`) from a raw arg list. */
|
|
9
|
+
function readFlag(args: string[], name: string): string {
|
|
10
|
+
const eq = args.find((a) => a.startsWith(`${name}=`));
|
|
11
|
+
if (eq) return eq.slice(name.length + 1).trim();
|
|
12
|
+
const i = args.indexOf(name);
|
|
13
|
+
if (i >= 0 && args[i + 1] && !args[i + 1].startsWith("--")) return args[i + 1].trim();
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `gitpagedocs pages actions` / `--pages-actions` — switch the repository's
|
|
19
|
+
* GitHub Pages source to GitHub Actions. Detects owner/repo/branch from git,
|
|
20
|
+
* validates the gh CLI, and confirms before changing anything (no silent
|
|
21
|
+
* overwrite). Does NOT generate docs or push (that is `pages deploy`).
|
|
22
|
+
*/
|
|
23
|
+
export async function runPagesActions(ctx: CommandContext): Promise<void> {
|
|
24
|
+
const repo = detectRepoFromGit(ctx.cwd) as { owner: string; repo: string } | null;
|
|
25
|
+
if (!repo) {
|
|
26
|
+
// eslint-disable-next-line no-console
|
|
27
|
+
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");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const branch = getCurrentGitBranch(ctx.cwd) as string;
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.log(`\n Detected ${repo.owner}/${repo.repo} (branch ${branch}).`);
|
|
33
|
+
const ok = await askConfirm(`Switch GitHub Pages source to GitHub Actions for ${repo.owner}/${repo.repo}?`, false);
|
|
34
|
+
if (!ok) {
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.log(" Aborted. No changes made.\n");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
tryConfigurePagesToGitHubActions(repo.owner, repo.repo, branch, ctx.cwd);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* `gitpagedocs pages deploy` — the full documented deploy flow: resolve
|
|
44
|
+
* owner/repo (from --owner/--repo or the git remote), confirm, then run the
|
|
45
|
+
* proven `--push` path (generate docs + workflow → commit → push → configure
|
|
46
|
+
* Pages→Actions) and print the final site URL. Reuses the legacy push verbatim
|
|
47
|
+
* (spawned), so `--push` behavior stays byte-identical.
|
|
48
|
+
*/
|
|
49
|
+
export async function runPagesDeploy(ctx: CommandContext): Promise<void> {
|
|
50
|
+
let owner = readFlag(ctx.args, "--owner");
|
|
51
|
+
let repo = readFlag(ctx.args, "--repo");
|
|
52
|
+
const docsPath = readFlag(ctx.args, "--path");
|
|
53
|
+
|
|
54
|
+
if (!owner || !repo) {
|
|
55
|
+
const detected = detectRepoFromGit(ctx.cwd) as { owner: string; repo: string } | null;
|
|
56
|
+
if (detected) {
|
|
57
|
+
owner = owner || detected.owner;
|
|
58
|
+
repo = repo || detected.repo;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!owner || !repo) {
|
|
62
|
+
// eslint-disable-next-line no-console
|
|
63
|
+
console.log("\n Could not determine owner/repo. Pass `--owner <o> --repo <r>` or add a git 'origin' remote.\n");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console.log(`\n Deploy ${owner}/${repo} to GitHub Pages via Actions${docsPath ? ` (path: ${docsPath})` : ""}.`);
|
|
69
|
+
const ok = await askConfirm(
|
|
70
|
+
`Generate docs + workflow, commit, push, and switch Pages to Actions for ${owner}/${repo}?`,
|
|
71
|
+
false,
|
|
72
|
+
);
|
|
73
|
+
if (!ok) {
|
|
74
|
+
// eslint-disable-next-line no-console
|
|
75
|
+
console.log(" Aborted. No changes made.\n");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const bin = path.join(ctx.pkgRoot, "cli", "index.mjs");
|
|
80
|
+
const pushArgs = ["--push", "--owner", owner, "--repo", repo, ...(docsPath ? ["--path", docsPath] : [])];
|
|
81
|
+
const result = spawnSync(process.execPath, [bin, ...pushArgs], { stdio: "inherit", cwd: ctx.cwd, env: process.env });
|
|
82
|
+
|
|
83
|
+
if (result.status === 0) {
|
|
84
|
+
const seg = docsPath ? `/${docsPath.replace(/^\/+|\/+$/g, "")}` : "";
|
|
85
|
+
// eslint-disable-next-line no-console
|
|
86
|
+
console.log(`\n Deployed. Final URL: https://${owner}.github.io/${repo}${seg}/\n`);
|
|
87
|
+
} else {
|
|
88
|
+
// eslint-disable-next-line no-console
|
|
89
|
+
console.log("\n Deploy did not complete (push step failed). See output above.\n");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* New-verb command layer (Phase 6). Intercepts the informational/utility
|
|
3
|
+
* commands that are NOT part of the legacy flag flow (version, doctor, provider,
|
|
4
|
+
* models, config, mcp, update) and routes them to thin handlers that consume
|
|
5
|
+
* @gitpagedocs/tools. Alias verbs (init, document, deploy, pages) are mapped in
|
|
6
|
+
* the parser and fall through to the legacy dispatch, so this returns false for
|
|
7
|
+
* them.
|
|
8
|
+
*/
|
|
9
|
+
export interface CommandContext {
|
|
10
|
+
argv: string[];
|
|
11
|
+
args: string[];
|
|
12
|
+
pkgRoot: string;
|
|
13
|
+
cwd: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type CommandHandler = (ctx: CommandContext) => Promise<void>;
|
|
17
|
+
|
|
18
|
+
import { runVersion, runDoctor, runUpdate } from "./diagnostics";
|
|
19
|
+
import { runProvider, runModels } from "./ai-info";
|
|
20
|
+
import { runConfig } from "./config-info";
|
|
21
|
+
import { runMcp } from "./mcp";
|
|
22
|
+
import { runDocs } from "./docs";
|
|
23
|
+
import { runPagesActions, runPagesDeploy } from "./pages";
|
|
24
|
+
|
|
25
|
+
const REGISTRY: Record<string, CommandHandler> = {
|
|
26
|
+
version: runVersion,
|
|
27
|
+
"--version": runVersion,
|
|
28
|
+
"-v": runVersion,
|
|
29
|
+
doctor: runDoctor,
|
|
30
|
+
update: runUpdate,
|
|
31
|
+
provider: runProvider,
|
|
32
|
+
providers: runProvider,
|
|
33
|
+
models: runModels,
|
|
34
|
+
config: runConfig,
|
|
35
|
+
mcp: runMcp,
|
|
36
|
+
docs: runDocs,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Returns true if a new command handled the invocation (skip legacy flow). */
|
|
40
|
+
export async function runNewCommand(argv: string[], pkgRoot: string): Promise<boolean> {
|
|
41
|
+
const args = argv.slice(2);
|
|
42
|
+
const ctx: CommandContext = { argv, args, pkgRoot, cwd: process.cwd() };
|
|
43
|
+
|
|
44
|
+
// Configure-only Pages→Actions: `--pages-actions` flag or `pages actions` verb.
|
|
45
|
+
if (args.includes("--pages-actions") || (args[0] === "pages" && args[1] === "actions")) {
|
|
46
|
+
await runPagesActions(ctx);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
// Full deploy: `pages deploy` (detect owner/repo, confirm, run the proven
|
|
50
|
+
// push flow, print URL). Bare `pages`/`deploy` still fall through to legacy.
|
|
51
|
+
if (args[0] === "pages" && args[1] === "deploy") {
|
|
52
|
+
await runPagesDeploy(ctx);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const handler = REGISTRY[args[0] ?? ""];
|
|
57
|
+
if (!handler) return false;
|
|
58
|
+
await handler(ctx);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const NEW_COMMAND_NAMES = Object.keys(REGISTRY).filter((n) => !n.startsWith("-"));
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { printBanner, printCredits } from "./ui/banner";
|
|
6
|
+
import { resolveOptions } from "./options/resolver";
|
|
7
|
+
import { runNewCommand } from "./commands/run-command";
|
|
8
|
+
import { dispatchMode } from "../application/use-cases/dispatch-mode";
|
|
9
|
+
import type {
|
|
10
|
+
CliRuntimeParams,
|
|
11
|
+
ConfigOnlyRuntimePort,
|
|
12
|
+
HomeRuntimePort,
|
|
13
|
+
} from "../application/ports/cli-runtime-ports";
|
|
14
|
+
// @ts-expect-error .mjs modules are runtime-only in this package.
|
|
15
|
+
import { buildConfigArtifacts } from "../builders/config-orchestrator.mjs";
|
|
16
|
+
// @ts-expect-error .mjs modules are runtime-only in this package.
|
|
17
|
+
import { createThemeTemplate } from "../builders/theme-template.mjs";
|
|
18
|
+
// @ts-expect-error .mjs modules are runtime-only in this package.
|
|
19
|
+
import { LAYOUTS } from "../data/layouts.mjs";
|
|
20
|
+
// @ts-expect-error .mjs modules are runtime-only in this package.
|
|
21
|
+
import { executeConfigOnly } from "../application/config-only/handler.mjs";
|
|
22
|
+
// @ts-expect-error .mjs modules are runtime-only in this package.
|
|
23
|
+
import { executeHome } from "../application/home/handler.mjs";
|
|
24
|
+
// @ts-expect-error .mjs modules are runtime-only in this package.
|
|
25
|
+
import { createConfigOnlyRuntime } from "../infrastructure/config-only/runtime.mjs";
|
|
26
|
+
// @ts-expect-error .mjs modules are runtime-only in this package.
|
|
27
|
+
import { createHomeRuntime } from "../infrastructure/home/runtime.mjs";
|
|
28
|
+
|
|
29
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
30
|
+
const root = process.cwd();
|
|
31
|
+
// cli/ is the published package root (bin = cli/index.mjs). scriptDir is
|
|
32
|
+
// cli/presentation, so pkgRoot = cli/ and the bundled prebuilt/ ships at
|
|
33
|
+
// cli/prebuilt — works identically in-repo and when installed via npm.
|
|
34
|
+
const pkgRoot = path.join(scriptDir, "..");
|
|
35
|
+
const prebuiltDir = path.join(pkgRoot, "prebuilt");
|
|
36
|
+
|
|
37
|
+
const params: CliRuntimeParams = {
|
|
38
|
+
root,
|
|
39
|
+
pkgRoot,
|
|
40
|
+
prebuiltDir,
|
|
41
|
+
buildConfigArtifacts,
|
|
42
|
+
createThemeTemplate,
|
|
43
|
+
layouts: LAYOUTS,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
async function main(): Promise<void> {
|
|
47
|
+
// `mcp start` owns stdout (the MCP stdio channel) — no banner/credits there.
|
|
48
|
+
const stdoutOwnedByCommand = process.argv[2] === "mcp";
|
|
49
|
+
if (!stdoutOwnedByCommand) printBanner();
|
|
50
|
+
|
|
51
|
+
// New informational/utility commands (version, doctor, provider, models,
|
|
52
|
+
// config, mcp, update) are handled before the legacy flag flow. Alias verbs
|
|
53
|
+
// (init, document, deploy, pages) are mapped in the parser and fall through.
|
|
54
|
+
if (await runNewCommand(process.argv, pkgRoot)) {
|
|
55
|
+
if (!stdoutOwnedByCommand) printCredits();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const options = await resolveOptions(process.argv, process.env);
|
|
60
|
+
const configOnlyRuntime = createConfigOnlyRuntime() as ConfigOnlyRuntimePort;
|
|
61
|
+
const homeRuntime = createHomeRuntime() as HomeRuntimePort;
|
|
62
|
+
await dispatchMode(options, params, {
|
|
63
|
+
runConfigOnly: async (context) => {
|
|
64
|
+
const safePrebuiltDir =
|
|
65
|
+
typeof context.prebuiltDir === "string"
|
|
66
|
+
? context.prebuiltDir
|
|
67
|
+
: path.join(String(context.pkgRoot ?? process.cwd()), "prebuilt");
|
|
68
|
+
await executeConfigOnly(
|
|
69
|
+
{
|
|
70
|
+
...context,
|
|
71
|
+
prebuiltDir: safePrebuiltDir,
|
|
72
|
+
},
|
|
73
|
+
configOnlyRuntime,
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
runHome: async (context) => {
|
|
77
|
+
await executeHome(context, homeRuntime);
|
|
78
|
+
},
|
|
79
|
+
runAi: async (context) => {
|
|
80
|
+
console.log("[gitpagedocs] Iniciando Módulo de IA na CLI...");
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
const { runAiCliCommand } = await import("../ai/application/run-ai-cli-command.ts");
|
|
83
|
+
const result = await runAiCliCommand({
|
|
84
|
+
cwd: process.cwd(),
|
|
85
|
+
onInfo: (message: string) => console.log(message),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (result.summary.scannedFilesCount === 0) {
|
|
89
|
+
console.log("[gitpagedocs:ai] Nenhum arquivo elegível foi encontrado nos paths informados.");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (result.runConfigScaffold) {
|
|
94
|
+
console.log("\n[gitpagedocs] Scaffolding GitPageDocs ecosystem automatically...");
|
|
95
|
+
const safePrebuiltDir = typeof context.prebuiltDir === "string" ? context.prebuiltDir : path.join(String(context.pkgRoot ?? process.cwd()), "prebuilt");
|
|
96
|
+
await executeConfigOnly({ ...context, prebuiltDir: safePrebuiltDir }, configOnlyRuntime);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log(`\n🎉 Processo completo! Arquivos gerados: ${result.summary.outputs.join(", ")}`);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
printCredits();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
main().catch((err: unknown) => {
|
|
106
|
+
console.error("Failed to create Git Page Docs scaffold.", err);
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { CliOptions } from "../../domain/models/cli-options";
|
|
2
|
+
import { DEFAULTS } from "./schema";
|
|
3
|
+
|
|
4
|
+
const KNOWN_FLAGS = new Set([
|
|
5
|
+
"--build",
|
|
6
|
+
"--serve",
|
|
7
|
+
"--layoutconfig",
|
|
8
|
+
"--full",
|
|
9
|
+
"--push",
|
|
10
|
+
"--home",
|
|
11
|
+
"--search",
|
|
12
|
+
"--path",
|
|
13
|
+
"--output",
|
|
14
|
+
"--interactive",
|
|
15
|
+
"-i",
|
|
16
|
+
"--ai",
|
|
17
|
+
"--pages-actions",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
function readOptionValue(args: string[], optionName: string): string {
|
|
21
|
+
const equalsArg = args.find((a) => a.startsWith(`${optionName}=`));
|
|
22
|
+
if (equalsArg) return equalsArg.slice(optionName.length + 1).trim();
|
|
23
|
+
const index = args.indexOf(optionName);
|
|
24
|
+
if (index >= 0) {
|
|
25
|
+
const next = args[index + 1];
|
|
26
|
+
if (next && !next.startsWith("--")) return next.trim();
|
|
27
|
+
}
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function parseCliOptions(argv: string[], env: NodeJS.ProcessEnv): CliOptions {
|
|
32
|
+
const args = argv.slice(2);
|
|
33
|
+
|
|
34
|
+
let githubOwner = readOptionValue(args, "--owner");
|
|
35
|
+
let githubRepo = readOptionValue(args, "--repo");
|
|
36
|
+
const docsPath = readOptionValue(args, "--path");
|
|
37
|
+
const outputDir = readOptionValue(args, "--output");
|
|
38
|
+
const searchRaw = readOptionValue(args, "--search");
|
|
39
|
+
|
|
40
|
+
const fallbackDashedArgs = args
|
|
41
|
+
.filter((a) => a.startsWith("--"))
|
|
42
|
+
.filter((a) => !KNOWN_FLAGS.has(a))
|
|
43
|
+
.filter((a) => !a.startsWith("--owner"))
|
|
44
|
+
.filter((a) => !a.startsWith("--repo"))
|
|
45
|
+
.filter((a) => !a.startsWith("--path"))
|
|
46
|
+
.filter((a) => !a.startsWith("--home"))
|
|
47
|
+
.filter((a) => !a.startsWith("--output"))
|
|
48
|
+
.filter((a) => !a.startsWith("--search"))
|
|
49
|
+
.map((a) => a.replace(/^--/, "").trim())
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
|
|
52
|
+
if (!githubOwner && fallbackDashedArgs[0]) githubOwner = fallbackDashedArgs[0];
|
|
53
|
+
if (!githubRepo && fallbackDashedArgs[1]) githubRepo = fallbackDashedArgs[1];
|
|
54
|
+
|
|
55
|
+
// New verb aliases (Phase 6) that map onto existing modes without new modes.
|
|
56
|
+
const command = args[0];
|
|
57
|
+
const isDocument = command === "document" || (command?.startsWith("document:") ?? false);
|
|
58
|
+
const isDeploy = command === "deploy" || command === "pages";
|
|
59
|
+
|
|
60
|
+
const isBuild = args.includes("--build") || env.GITPAGEDOCS_BUILD === "1";
|
|
61
|
+
const isServe = args.includes("--serve");
|
|
62
|
+
const useLocalLayoutConfig = args.includes("--layoutconfig");
|
|
63
|
+
const shouldPush = args.includes("--push") || isDeploy;
|
|
64
|
+
const isHome = args.includes("--home");
|
|
65
|
+
const isInteractive = args.includes("--interactive") || args.includes("-i");
|
|
66
|
+
const isAi = command === "ai" || args.includes("--ai") || isDocument;
|
|
67
|
+
const aiCommand =
|
|
68
|
+
command === "ai"
|
|
69
|
+
? args[1]
|
|
70
|
+
: command === "document"
|
|
71
|
+
? args[1] ?? "repo"
|
|
72
|
+
: command?.startsWith("document:")
|
|
73
|
+
? command.split(":")[1]
|
|
74
|
+
: undefined;
|
|
75
|
+
|
|
76
|
+
const mode: import("../../domain/models/cli-options").CliMode = isAi ? "ai" : isHome ? "home" : args.includes("--full") ? "full" : "config-only";
|
|
77
|
+
const repositorySearch =
|
|
78
|
+
searchRaw === "true"
|
|
79
|
+
? true
|
|
80
|
+
: searchRaw === "false"
|
|
81
|
+
? false
|
|
82
|
+
: isHome
|
|
83
|
+
? DEFAULTS.home.repositorySearch
|
|
84
|
+
: undefined;
|
|
85
|
+
|
|
86
|
+
const normalizedPath = docsPath.trim();
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
isBuild,
|
|
90
|
+
isServe,
|
|
91
|
+
mode,
|
|
92
|
+
aiCommand,
|
|
93
|
+
outputDir: outputDir || (isHome ? DEFAULTS.home.outputDir : DEFAULTS.outputDir),
|
|
94
|
+
useLocalLayoutConfig,
|
|
95
|
+
shouldPush,
|
|
96
|
+
githubOwner,
|
|
97
|
+
githubRepo,
|
|
98
|
+
docsPath: normalizedPath,
|
|
99
|
+
basePath: normalizedPath,
|
|
100
|
+
repositorySearch,
|
|
101
|
+
isInteractive,
|
|
102
|
+
hasArgs: args.length > 0,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CliOptions } from "../../domain/models/cli-options";
|
|
2
|
+
import { parseCliOptions } from "./parser";
|
|
3
|
+
import { shouldRunInteractive, promptConfigOnlyOptions, promptHomeOptions } from "../ui/prompts";
|
|
4
|
+
import { DEFAULTS } from "./schema";
|
|
5
|
+
|
|
6
|
+
export async function resolveOptions(argv: string[], env: NodeJS.ProcessEnv): Promise<CliOptions> {
|
|
7
|
+
const parsed = parseCliOptions(argv, env);
|
|
8
|
+
|
|
9
|
+
if (parsed.mode === "home") {
|
|
10
|
+
parsed.repositorySearch = parsed.repositorySearch ?? DEFAULTS.home.repositorySearch;
|
|
11
|
+
parsed.basePath = parsed.basePath ?? parsed.docsPath ?? DEFAULTS.home.basePath;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const runHomeInteractive = parsed.isInteractive || (shouldRunInteractive(argv) && parsed.mode === "home");
|
|
15
|
+
if (runHomeInteractive && parsed.mode === "home") {
|
|
16
|
+
return promptHomeOptions(parsed);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const runConfigInteractive = parsed.isInteractive || (shouldRunInteractive(argv) && parsed.mode === "config-only");
|
|
20
|
+
if (runConfigInteractive && parsed.mode === "config-only") {
|
|
21
|
+
return promptConfigOnlyOptions(parsed);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function printBanner(): void {
|
|
2
|
+
console.log("");
|
|
3
|
+
console.log(" Git Page Docs");
|
|
4
|
+
console.log(" by Vidigal-code");
|
|
5
|
+
console.log(" https://github.com/Vidigal-code/git-page-docs");
|
|
6
|
+
console.log("");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function printCredits(): void {
|
|
10
|
+
console.log("");
|
|
11
|
+
console.log(" Powered by Vidigal-code - git-page-docs");
|
|
12
|
+
console.log("");
|
|
13
|
+
}
|