@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.
Files changed (66) hide show
  1. package/README.md +169 -0
  2. package/ai/application/ai-command.ts +24 -0
  3. package/ai/application/ai-provider-factory.ts +22 -0
  4. package/ai/application/run-ai-cli-command.ts +136 -0
  5. package/ai/config.ts +22 -0
  6. package/ai/core/models/ai-cli-config.ts +31 -0
  7. package/ai/core/ports/illm-provider.ts +32 -0
  8. package/ai/infrastructure/ai-config-file.ts +32 -0
  9. package/ai/infrastructure/file-system-adapter.ts +83 -0
  10. package/ai/infrastructure/llm/tools-llm-provider.ts +54 -0
  11. package/ai/presentation/ai-prompts.ts +239 -0
  12. package/application/config-only/handler.mjs +55 -0
  13. package/application/home/handler.mjs +83 -0
  14. package/application/ports/cli-runtime-ports.ts +87 -0
  15. package/application/report/config-only-reporter.mjs +62 -0
  16. package/application/use-cases/dispatch-mode.ts +18 -0
  17. package/builders/config-orchestrator.mjs +40 -0
  18. package/builders/root-config-builder.mjs +54 -0
  19. package/builders/route-builders.mjs +111 -0
  20. package/builders/source-viewer.mjs +330 -0
  21. package/builders/theme-template.mjs +75 -0
  22. package/builders/version-config-builder.mjs +151 -0
  23. package/content/docs-en.mjs +542 -0
  24. package/content/docs-es.mjs +459 -0
  25. package/content/docs-pt.mjs +459 -0
  26. package/content/docs.mjs +9 -0
  27. package/contracts/doc-versions.mjs +1 -0
  28. package/contracts/index.mjs +1 -0
  29. package/data/default-site-config.mjs +312 -0
  30. package/data/i18n-langmenu.mjs +122 -0
  31. package/data/i18n-translations.mjs +20 -0
  32. package/data/layouts.mjs +353 -0
  33. package/data/path-mappings.mjs +34 -0
  34. package/data/route-metas.mjs +73 -0
  35. package/data/theme-colors.mjs +387 -0
  36. package/data/urls.mjs +7 -0
  37. package/data/version-constants.mjs +3 -0
  38. package/domain/models/cli-options.ts +18 -0
  39. package/domain/services/sanitize-segment.mjs +7 -0
  40. package/home/constants.mjs +4 -0
  41. package/home/home-file-writer.mjs +20 -0
  42. package/home/templates.mjs +48 -0
  43. package/index.mjs +26 -0
  44. package/infrastructure/config-only/runtime.mjs +17 -0
  45. package/infrastructure/home/runtime.mjs +37 -0
  46. package/package.json +60 -0
  47. package/presentation/commands/ai-info.ts +57 -0
  48. package/presentation/commands/config-info.ts +24 -0
  49. package/presentation/commands/diagnostics.ts +70 -0
  50. package/presentation/commands/docs.ts +37 -0
  51. package/presentation/commands/mcp.ts +20 -0
  52. package/presentation/commands/pages.ts +91 -0
  53. package/presentation/commands/run-command.ts +62 -0
  54. package/presentation/index.ts +108 -0
  55. package/presentation/options/parser.ts +104 -0
  56. package/presentation/options/resolver.ts +25 -0
  57. package/presentation/options/schema.ts +10 -0
  58. package/presentation/ui/banner.ts +13 -0
  59. package/presentation/ui/clack.ts +74 -0
  60. package/presentation/ui/prompts.ts +64 -0
  61. package/runtime/doc-path-resolver.mjs +57 -0
  62. package/runtime/git-ops.mjs +110 -0
  63. package/runtime/output.mjs +122 -0
  64. package/runtime/workflow.mjs +144 -0
  65. package/start.mjs +12 -0
  66. 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,10 @@
1
+ export const DEFAULTS = {
2
+ outputDir: "gitpagedocs",
3
+ repositorySearch: false,
4
+ basePath: "",
5
+ home: {
6
+ outputDir: "gitpagedocshome",
7
+ repositorySearch: false,
8
+ basePath: "",
9
+ },
10
+ } as const;
@@ -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
+ }