@inditextech/docouture-cli 0.1.0-SNAPSHOT.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/build/bin.js +321 -0
  2. package/build/commands/build.js +22 -0
  3. package/build/commands/completion.js +69 -0
  4. package/build/commands/dev.js +67 -0
  5. package/build/commands/doctor.js +156 -0
  6. package/build/commands/eject.js +71 -0
  7. package/build/commands/new.js +512 -0
  8. package/build/commands/publish.js +111 -0
  9. package/build/commands/teardown.js +66 -0
  10. package/build/commands/upgrade.js +156 -0
  11. package/build/commands/version.js +60 -0
  12. package/build/lib/agents-md.js +67 -0
  13. package/build/lib/antora-log.js +36 -0
  14. package/build/lib/antora-yml.js +47 -0
  15. package/build/lib/args.js +26 -0
  16. package/build/lib/cli-context.js +17 -0
  17. package/build/lib/cli-info.js +20 -0
  18. package/build/lib/config-resolver.js +23 -0
  19. package/build/lib/copy-template.js +162 -0
  20. package/build/lib/debug-log.js +21 -0
  21. package/build/lib/detect-package-manager.js +79 -0
  22. package/build/lib/dev-server.js +337 -0
  23. package/build/lib/doctor-checks.js +240 -0
  24. package/build/lib/global-flags.js +31 -0
  25. package/build/lib/kroki-compose.js +54 -0
  26. package/build/lib/playbook-yml.js +90 -0
  27. package/build/lib/repo-root.js +21 -0
  28. package/build/lib/run-script.js +50 -0
  29. package/build/lib/theme.js +51 -0
  30. package/build/templates/agent-support/.claude/skills/docs-internals/SKILL.md +53 -0
  31. package/build/templates/agent-support/.claude/skills/docs-internals/reference/antora-extensions.md +54 -0
  32. package/build/templates/agent-support/.claude/skills/docs-internals/reference/naming.md +41 -0
  33. package/build/templates/agent-support/.claude/skills/docs-internals/reference/page-patterns.md +238 -0
  34. package/build/templates/agent-support/.claude/skills/docs-versioning.versioned/SKILL.md +37 -0
  35. package/build/templates/agent-support/.claude/skills/docs-versioning.versioned/reference/releasing.md +106 -0
  36. package/build/templates/agent-support/.claude/skills/documenting-your-repo/SKILL.md +55 -0
  37. package/build/templates/agent-support/.claude/skills/documenting-your-repo/reference/content-sourcing.md +55 -0
  38. package/build/templates/agent-support/.claude/skills/documenting-your-repo/reference/maintenance-loop.md +68 -0
  39. package/build/templates/agent-support/.claude/skills/documenting-your-repo/reference/structure-planning.md +102 -0
  40. package/build/templates/agent-support/.claude/skills/writing-docs-pages/SKILL.md +112 -0
  41. package/build/templates/agent-support/.claude/skills/writing-docs-pages/reference/docouture-blocks.md +170 -0
  42. package/build/templates/agent-support/.claude/skills/writing-docs-pages/reference/language-basics.md +402 -0
  43. package/build/templates/agent-support/.opencode/skills/docs-internals/SKILL.md +53 -0
  44. package/build/templates/agent-support/.opencode/skills/docs-internals/reference/antora-extensions.md +54 -0
  45. package/build/templates/agent-support/.opencode/skills/docs-internals/reference/naming.md +41 -0
  46. package/build/templates/agent-support/.opencode/skills/docs-internals/reference/page-patterns.md +238 -0
  47. package/build/templates/agent-support/.opencode/skills/docs-versioning.versioned/SKILL.md +37 -0
  48. package/build/templates/agent-support/.opencode/skills/docs-versioning.versioned/reference/releasing.md +106 -0
  49. package/build/templates/agent-support/.opencode/skills/documenting-your-repo/SKILL.md +55 -0
  50. package/build/templates/agent-support/.opencode/skills/documenting-your-repo/reference/content-sourcing.md +55 -0
  51. package/build/templates/agent-support/.opencode/skills/documenting-your-repo/reference/maintenance-loop.md +68 -0
  52. package/build/templates/agent-support/.opencode/skills/documenting-your-repo/reference/structure-planning.md +102 -0
  53. package/build/templates/agent-support/.opencode/skills/writing-docs-pages/SKILL.md +112 -0
  54. package/build/templates/agent-support/.opencode/skills/writing-docs-pages/reference/docouture-blocks.md +170 -0
  55. package/build/templates/agent-support/.opencode/skills/writing-docs-pages/reference/language-basics.md +402 -0
  56. package/build/templates/agent-support/AGENTS.md +72 -0
  57. package/build/templates/starter/antora-playbook.local.yml +99 -0
  58. package/build/templates/starter/antora-playbook.versioned.yml +167 -0
  59. package/build/templates/starter/antora-playbook.yml +189 -0
  60. package/build/templates/starter/gitignore +3 -0
  61. package/build/templates/starter/package.json +37 -0
  62. package/build/templates/starter/scripts/check-links.mjs +178 -0
  63. package/build/templates/starter/src/antora.yml +85 -0
  64. package/build/templates/starter/src/modules/ROOT/images/card-placeholder.png +0 -0
  65. package/build/templates/starter/src/modules/ROOT/images/feature-placeholder-dark.png +0 -0
  66. package/build/templates/starter/src/modules/ROOT/images/feature-placeholder.png +0 -0
  67. package/build/templates/starter/src/modules/ROOT/images/hero-placeholder.png +0 -0
  68. package/build/templates/starter/src/modules/ROOT/pages/index.adoc +124 -0
  69. package/build/templates/starter/src/modules/main/nav.adoc +19 -0
  70. package/build/templates/starter/src/modules/main/pages/app.adoc +5 -0
  71. package/build/templates/starter/src/modules/main/pages/architecture.adoc +6 -0
  72. package/build/templates/starter/src/modules/main/pages/changelog/index.adoc +9 -0
  73. package/build/templates/starter/src/modules/main/pages/components.adoc +701 -0
  74. package/build/templates/starter/src/modules/main/pages/contributing.adoc +6 -0
  75. package/build/templates/starter/src/modules/main/pages/getting-started.adoc +169 -0
  76. package/build/templates/starter/src/modules/main/pages/glossary.adoc +8 -0
  77. package/build/templates/starter/src/modules/main/pages/index.adoc +9 -0
  78. package/build/templates/starter/src/modules/main/pages/prerequisites.adoc +55 -0
  79. package/build/templates/starter/src/modules/main/pages/quickstart.adoc +21 -0
  80. package/build/templates/starter/src/modules/main/pages/release-notes/index.adoc +9 -0
  81. package/build/templates/starter/src/modules/main/pages/requirements.adoc +8 -0
  82. package/build/templates/starter/src/modules/main/pages/roadmap.adoc +4 -0
  83. package/build/templates/starter/src/modules/main/pages/structure.adoc +5 -0
  84. package/build/templates/starter/src/release-version.versioned +1 -0
  85. package/build/templates/starter/supplemental-ui/favicon.ico +0 -0
  86. package/build/templates/starter/supplemental-ui/product-logo-dark.png +0 -0
  87. package/build/templates/starter/supplemental-ui/product-logo.png +0 -0
  88. package/build/templates/workflows/docouture-kroki-cache-warm.yml +101 -0
  89. package/build/templates/workflows/docouture-pr-verify.yml +120 -0
  90. package/build/templates/workflows/docouture-publish-prerelease.yml +122 -0
  91. package/build/templates/workflows/docouture-publish.yml +138 -0
  92. package/build/templates/workflows/docouture-release-preview.yml +198 -0
  93. package/build/templates/workflows/docouture-release.yml +414 -0
  94. package/package.json +38 -0
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+ import { createRequire } from 'node:module';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { join, resolve } from 'node:path';
5
+ import ora from 'ora';
6
+ import { parseArgs } from '../lib/args.js';
7
+ import { exists } from '../lib/copy-template.js';
8
+ import { findRepoRoot } from '../lib/repo-root.js';
9
+ import { readOutputDir } from '../lib/playbook-yml.js';
10
+ import { resolveConfig } from '../lib/config-resolver.js';
11
+ import { debugLog } from '../lib/debug-log.js';
12
+ /**
13
+ * Resolved from the SITE's own node_modules (via its package.json), not
14
+ * this CLI's — a driver is a devDependency of the site being published,
15
+ * not of docouture itself. Exposed as its own function — rather than inlined
16
+ * in runPublish — so tests can inject a fake in its place: under the test
17
+ * runner, a bare `require()` of a package name that also happens to be a
18
+ * real published dependency of this monorepo can resolve to THAT real
19
+ * package instead of failing, regardless of the `from` path passed to
20
+ * `createRequire` — the dynamic-require equivalent of `vi.mock` not
21
+ * intercepting a plain CommonJS `require()` (see
22
+ * @inditextech/docouture-publish-gh-pages's own index.js for the same
23
+ * seam-over-mock reasoning applied to its `gh-pages` dependency).
24
+ */
25
+ function loadDriver(packageJsonFile, driverPackageName) {
26
+ return createRequire(packageJsonFile)(driverPackageName);
27
+ }
28
+ async function readJson(file) {
29
+ try {
30
+ return JSON.parse(await readFile(file, 'utf8'));
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ /**
37
+ * Every flag except `--dir` (which this command consumes itself) is
38
+ * forwarded to the driver as one of its own options. `--user-name`/
39
+ * `--user-email`, if given, are combined into the nested `user: {name,
40
+ * email}` shape @inditextech/docouture-publish-gh-pages (and presumably other
41
+ * drivers needing a commit identity) expects — the only nested option
42
+ * shape a flat `--flag value` CLI syntax can't express directly.
43
+ */
44
+ function flagsToOptions(flags) {
45
+ const { 'user-name': userName, 'user-email': userEmail, ...rest } = flags;
46
+ delete rest.dir;
47
+ const options = { ...rest };
48
+ if (userName !== undefined || userEmail !== undefined) {
49
+ options.user = { name: userName, email: userEmail };
50
+ }
51
+ return options;
52
+ }
53
+ export async function runPublish(argv, deps = {}) {
54
+ const resolveDriver = deps.loadDriver ?? loadDriver;
55
+ const { positional, flags } = parseArgs(argv);
56
+ const target = positional[0];
57
+ if (!target) {
58
+ console.error('usage: docouture publish <target> [--dir <path>] [--<option> <value> ...]');
59
+ console.error('example: docouture publish gh-pages --branch gh-pages');
60
+ return 1;
61
+ }
62
+ const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
63
+ const repoRoot = await findRepoRoot(startDir);
64
+ const siteRoot = join(repoRoot, 'docs');
65
+ const packageJsonFile = join(siteRoot, 'package.json');
66
+ if (!(await exists(packageJsonFile))) {
67
+ console.error(`no package.json found at '${siteRoot}'`);
68
+ console.error('pass --dir <path> to a component root (the parent of docs/), or run docouture new first');
69
+ return 1;
70
+ }
71
+ const driverPackageName = `@inditextech/docouture-publish-${target}`;
72
+ let driver;
73
+ try {
74
+ driver = resolveDriver(packageJsonFile, driverPackageName);
75
+ }
76
+ catch {
77
+ console.error(`could not load publish driver '${driverPackageName}'`);
78
+ console.error(`add it to ${packageJsonFile}'s devDependencies, e.g.: npm install --save-dev ${driverPackageName}`);
79
+ return 1;
80
+ }
81
+ const playbookFile = join(siteRoot, 'antora-playbook.yml');
82
+ const playbookContent = (await exists(playbookFile)) ? await readFile(playbookFile, 'utf8') : '';
83
+ const siteDir = resolve(siteRoot, readOutputDir(playbookContent) || 'build/site');
84
+ if (!(await exists(siteDir))) {
85
+ console.error(`no built site found at '${siteDir}' — run 'docouture build' first`);
86
+ return 1;
87
+ }
88
+ const packageJson = await readJson(packageJsonFile);
89
+ const configuredOptions = packageJson?.docouture?.publish?.[target] ?? {};
90
+ // CLI flags win over docs/package.json's own "docouture".publish.<target>
91
+ // config, which wins over whatever defaults the driver applies itself —
92
+ // see lib/config-resolver.ts for this precedence rule shared with upgrade.
93
+ const options = resolveConfig({}, configuredOptions, flagsToOptions(flags));
94
+ debugLog(`publishing '${target}' from ${siteDir} with options: ${JSON.stringify(options)}`);
95
+ const spinner = ora({ text: `Publishing to ${target}…`, stream: process.stderr }).start();
96
+ try {
97
+ const published = await driver(siteDir, options);
98
+ if (published === false) {
99
+ spinner.fail(`'${target}' driver reported it did not publish`);
100
+ return 1;
101
+ }
102
+ spinner.succeed(`Published to ${target}`);
103
+ return 0;
104
+ }
105
+ catch (err) {
106
+ spinner.fail(`publishing to '${target}' failed`);
107
+ console.error(err instanceof Error ? err.stack : String(err));
108
+ return 1;
109
+ }
110
+ }
111
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+ import { execFile } from 'node:child_process';
3
+ import { promisify } from 'node:util';
4
+ import { join, resolve } from 'node:path';
5
+ import { parseArgs } from '../lib/args.js';
6
+ import { exists } from '../lib/copy-template.js';
7
+ import { findRepoRoot } from '../lib/repo-root.js';
8
+ import { resolveEffectiveComposeFile } from '../lib/kroki-compose.js';
9
+ // `docouture teardown kroki` — the explicit, manual stop side of GH-44's Kroki
10
+ // auto-start. `kroki-prewarm.js` (in @inditextech/docouture-antora-extensions)
11
+ // starts the service on demand but never stops it — see kroki-docker.js's
12
+ // own header for why: a build has no natural "I'm completely done with this
13
+ // machine for good" moment to hook, and stopping it mid-session would only
14
+ // force the next build to pay the startup latency again. This command is
15
+ // the escape hatch for the moment a human actually does know that: done for
16
+ // the day, freeing the port for something else, before switching to a
17
+ // project with different diagram types configured, etc.
18
+ //
19
+ // Targets whichever compose file is ACTUALLY in effect — an ejected
20
+ // override at the site root if one exists, else the bundled default — via
21
+ // the exact same resolution order kroki-docker.js's own auto-start uses
22
+ // (lib/kroki-compose.js's `resolveEffectiveComposeFile`), so this always
23
+ // stops the containers a build would actually have started, not
24
+ // necessarily the package's un-customized default.
25
+ const execFileAsync = promisify(execFile);
26
+ const SUPPORTED_TARGETS = {
27
+ kroki: { description: 'Kroki diagram rendering service (docker compose)' },
28
+ };
29
+ export async function runTeardown(argv, deps = {}) {
30
+ const { positional, flags } = parseArgs(argv);
31
+ const target = positional[0];
32
+ if (!target || !(target in SUPPORTED_TARGETS)) {
33
+ console.error('usage: docouture teardown <target> [--dir <path>]');
34
+ console.error('supported targets:');
35
+ for (const [name, { description }] of Object.entries(SUPPORTED_TARGETS)) {
36
+ console.error(` ${name} — ${description}`);
37
+ }
38
+ return 1;
39
+ }
40
+ const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
41
+ const repoRoot = await findRepoRoot(startDir);
42
+ const siteRoot = join(repoRoot, 'docs');
43
+ if (!(await exists(join(siteRoot, 'package.json')))) {
44
+ console.error(`no package.json found at '${siteRoot}'`);
45
+ console.error('pass --dir <path> to a component root (the parent of docs/), or run docouture new first');
46
+ return 1;
47
+ }
48
+ const resolveFile = deps.resolveEffectiveComposeFile || resolveEffectiveComposeFile;
49
+ const composeFile = await resolveFile(siteRoot);
50
+ if (!composeFile) {
51
+ console.error(`could not find @inditextech/docouture-antora-extensions' bundled kroki-compose.yml, and no override exists at '${join(siteRoot, 'kroki-compose.yml')}'`);
52
+ console.error('add @inditextech/docouture-antora-extensions to devDependencies, or run docouture eject kroki first');
53
+ return 1;
54
+ }
55
+ try {
56
+ const runDockerCompose = deps.execFileAsync || execFileAsync;
57
+ await runDockerCompose('docker', ['compose', '-f', composeFile, 'down']);
58
+ }
59
+ catch (err) {
60
+ console.error(err instanceof Error ? err.message : String(err));
61
+ return 1;
62
+ }
63
+ console.log(`stopped the Kroki service (${composeFile})`);
64
+ return 0;
65
+ }
66
+ //# sourceMappingURL=teardown.js.map
@@ -0,0 +1,156 @@
1
+ 'use strict';
2
+ import { execFileSync } from 'node:child_process';
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { dirname, join, relative, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { parseArgs } from '../lib/args.js';
7
+ import { readCliInfo } from '../lib/cli-info.js';
8
+ import { copyTemplate, exists, renderTemplateFile } from '../lib/copy-template.js';
9
+ import { detectPackageManager, packageManagerPlan } from '../lib/detect-package-manager.js';
10
+ import { resolveConfig } from '../lib/config-resolver.js';
11
+ import { findRepoRoot } from '../lib/repo-root.js';
12
+ import { AGENTS_MD_FILENAME, mergeAgentsMd } from '../lib/agents-md.js';
13
+ // Same check `new.ts` uses, duplicated rather than imported — it's three
14
+ // lines, and `doctor-checks.ts` already sets the precedent of duplicating a
15
+ // small piece of `new.ts`'s own logic instead of reaching across into a
16
+ // module built around the interactive wizard (see its own comment on
17
+ // AGENT_SUPPORT_CHECK_PATHS).
18
+ function isInsideGitWorkTree(dir) {
19
+ try {
20
+ execFileSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: dir, stdio: 'ignore' });
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ function titleCase(name) {
28
+ return name
29
+ .split('-')
30
+ .filter(Boolean)
31
+ .map((word) => word[0].toUpperCase() + word.slice(1))
32
+ .join(' ');
33
+ }
34
+ // docs/antora.yml's `name:`/`title:` are read the same way antora-yml.ts
35
+ // reads `version:` — a plain top-of-line regex, not a full YAML parse, so
36
+ // comments and formatting a human added survive untouched (this file is
37
+ // only ever read here, never rewritten). Unlike antora-yml.ts, upgrade.ts
38
+ // never writes this file, so that module's write-side helpers don't apply
39
+ // here and this stays a small, local, read-only counterpart instead of
40
+ // growing antora-yml.ts's own scope to cover a field it doesn't otherwise
41
+ // need.
42
+ function readAntoraField(content, key) {
43
+ const match = new RegExp(`^${key}:.*$`, 'm').exec(content);
44
+ if (!match)
45
+ return null;
46
+ const value = match[0].slice(key.length + 1).trim();
47
+ return value.length > 0 ? value : null;
48
+ }
49
+ export async function runUpgrade(argv) {
50
+ const { flags } = parseArgs(argv);
51
+ const dryRun = flags['dry-run'] === true;
52
+ const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
53
+ if (!isInsideGitWorkTree(startDir)) {
54
+ console.error(`'${startDir}' is not inside a git repository`);
55
+ console.error('docouture upgrade re-syncs an already-scaffolded repository — run it from your repo root, or pass --dir <path> to one');
56
+ return 1;
57
+ }
58
+ // --dir/cwd can be anywhere inside the repository — findRepoRoot walks up
59
+ // to the actual top-level, same as new.ts and the rest of the CLI.
60
+ const target = await findRepoRoot(startDir);
61
+ // build/commands/upgrade.js -> build/templates/{workflows,agent-support} —
62
+ // same resolution new.ts uses, see its own comment.
63
+ const here = dirname(fileURLToPath(import.meta.url));
64
+ const templatesRoot = join(here, '..', 'templates');
65
+ const workflowsTemplateDir = join(templatesRoot, 'workflows');
66
+ const agentSupportDir = join(templatesRoot, 'agent-support');
67
+ // build/commands/upgrade.js -> package root, 2 levels up — see
68
+ // readCliInfo's own comment. Always re-read fresh: an upgrade run re-pins
69
+ // whatever templates reference the CLI version to the one actually
70
+ // installed now, which may well differ from whatever `docouture new`
71
+ // originally stamped in.
72
+ const { version: cliVersion } = await readCliInfo(import.meta.url, 2);
73
+ // Re-detected fresh too, same reasoning — a site may have picked up a
74
+ // lockfile (or a packageManager field) since it was first scaffolded.
75
+ const pm = packageManagerPlan(detectPackageManager(target));
76
+ // AGENTS.md's own __DOCOUTURE_TITLE__ placeholder (the only name/title token
77
+ // used outside `new.ts`'s starter/docs templates — see copy-template.ts's
78
+ // PLACEHOLDERS) needs a value from somewhere, but `upgrade` takes no
79
+ // <name> argument: it re-syncs an existing site, so the value it already
80
+ // recorded in docs/antora.yml at scaffold time is read back rather than
81
+ // asked for again. --title still overrides it, and a site with no
82
+ // docs/antora.yml (workflows/skills used standalone of the starter
83
+ // subtree) falls back to a plain, title-cased directory name.
84
+ const descriptorPath = join(target, 'docs', 'src', 'antora.yml');
85
+ let name = 'docs';
86
+ let fromYmlTitle;
87
+ if (await exists(descriptorPath)) {
88
+ const content = await readFile(descriptorPath, 'utf8');
89
+ name = readAntoraField(content, 'name') ?? name;
90
+ fromYmlTitle = readAntoraField(content, 'title') ?? undefined;
91
+ }
92
+ // Same three-tier precedence publish.ts uses (see lib/config-resolver.ts):
93
+ // --title flag > the value already recorded in docs/antora.yml > a plain
94
+ // title-cased fallback computed from the name above.
95
+ const { title } = resolveConfig({ title: titleCase(name) }, { title: fromYmlTitle }, { title: typeof flags.title === 'string' ? flags.title : undefined });
96
+ const values = {
97
+ name,
98
+ title,
99
+ // Same reasoning as repoIgnoreGlob below: __DOCOUTURE_COMPONENT_NAME__ only
100
+ // appears in docs/antora.yml and antora-playbook.yml, neither of which
101
+ // upgrade ever re-copies (see the comment above `name` itself) — this
102
+ // value is structurally required by TemplateValues but never actually
103
+ // substituted anywhere upgrade touches.
104
+ componentName: 'unused-by-upgrade',
105
+ cliVersion,
106
+ pmName: pm.pm,
107
+ pmCacheName: pm.cacheName,
108
+ pmLockfile: pm.lockfile,
109
+ pmCiCmd: pm.ciCmd,
110
+ pmSetupStepYaml: pm.setupStepYaml,
111
+ // upgrade never re-copies docs/ (see below) — package.json, the only
112
+ // template file this placeholder appears in, is never touched here — so
113
+ // there's nothing meaningful to compute it from. Not an empty string:
114
+ // an empty glob (see check-links.mjs's globToRegExp) compiles to a
115
+ // pattern matching every URL, which would be a silent, dangerous no-op
116
+ // if this value were ever actually substituted somewhere.
117
+ repoIgnoreGlob: 'unused-by-upgrade',
118
+ };
119
+ const workflowsDir = join(target, '.github', 'workflows');
120
+ const agentsMdFile = join(target, AGENTS_MD_FILENAME);
121
+ // Unlike `new.ts`, this command's whole purpose is to overwrite what's
122
+ // already there — workflows and agent skills are meant to be regenerable
123
+ // from the template on every upgrade, not merged with local edits (there
124
+ // is no content-hash/diff tracking anywhere in this CLI to tell a stock
125
+ // file from a user-edited one). `docs/` itself — the starter content a
126
+ // site has since written its own pages into — is never touched here.
127
+ // AGENTS.md is the one exception: copyTemplate's own SKIP_FILENAMES skip
128
+ // (see copy-template.ts) leaves it untouched by the walk above, and it's
129
+ // merged instead — see lib/agents-md.ts for why a blind overwrite here
130
+ // would silently destroy the 'Documentation state' table
131
+ // `documenting-your-repo` maintains outside docouture' own managed section.
132
+ if (dryRun) {
133
+ const plannedWorkflows = await copyTemplate(workflowsTemplateDir, workflowsDir, values, { dryRun: true });
134
+ const plannedAgentSupport = await copyTemplate(agentSupportDir, target, values, { dryRun: true });
135
+ console.log('would write:');
136
+ for (const path of [...plannedWorkflows, ...plannedAgentSupport, agentsMdFile]) {
137
+ console.log(` ${relative(target, path)}`);
138
+ }
139
+ return 0;
140
+ }
141
+ await copyTemplate(workflowsTemplateDir, workflowsDir, values);
142
+ await copyTemplate(agentSupportDir, target, values);
143
+ const existingAgentsMd = (await exists(agentsMdFile)) ? await readFile(agentsMdFile, 'utf8') : undefined;
144
+ const renderedAgentsMd = await renderTemplateFile(join(agentSupportDir, AGENTS_MD_FILENAME), values);
145
+ await writeFile(agentsMdFile, mergeAgentsMd(existingAgentsMd, renderedAgentsMd), 'utf8');
146
+ // Relative to `target` (the repo root), not process.cwd() — cwd may be a
147
+ // nested subdirectory findRepoRoot walked up from, in which case
148
+ // relative(cwd, ...) produces a useless '../../..' chain for paths that
149
+ // are actually just '.github/workflows', 'AGENTS.md' etc. at the root.
150
+ console.log(`updated ${relative(target, workflowsDir)}`);
151
+ console.log(`updated ${relative(target, agentsMdFile)}`);
152
+ console.log(`updated ${relative(target, join(target, '.opencode', 'skills'))}`);
153
+ console.log(`updated ${relative(target, join(target, '.claude', 'skills'))}`);
154
+ return 0;
155
+ }
156
+ //# sourceMappingURL=upgrade.js.map
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+ import { readFile, writeFile } from 'node:fs/promises';
3
+ import { join, resolve } from 'node:path';
4
+ import { parseArgs } from '../lib/args.js';
5
+ import { exists } from '../lib/copy-template.js';
6
+ import { findRepoRoot } from '../lib/repo-root.js';
7
+ import { patchVersion, readVersion } from '../lib/antora-yml.js';
8
+ // Unlike `just bump` — which moves this workspace's own package.json files
9
+ // through npm semver arithmetic — this takes the target version as a literal
10
+ // value rather than a bump level (major/minor/patch/...). Antora's component
11
+ // `version` is a free-form string, not required to be semver: sites in the
12
+ // wild use "2.1", "v3", "latest". Guessing what "minor" means for "2.1" would
13
+ // be presumptuous, and wrong for the sites where it does not apply. If your
14
+ // documented version does happen to be semver, compute the next value
15
+ // yourself and pass it here.
16
+ export async function runVersion(argv) {
17
+ const { positional, flags } = parseArgs(argv);
18
+ const value = positional[0];
19
+ if (!value) {
20
+ console.error('usage: docouture version <value> [--dir <path>] [--file <path>] [--prerelease | --stable]');
21
+ return 1;
22
+ }
23
+ if (flags.prerelease && flags.stable) {
24
+ console.error('--prerelease and --stable are mutually exclusive');
25
+ return 1;
26
+ }
27
+ // --dir (or cwd) can be anywhere inside the repository — findRepoRoot
28
+ // walks up to the actual repository root, same as dev/build/doctor, so
29
+ // this works whether run from the repo root, from inside docs/, or from a
30
+ // nested page directory. --file bypasses all of this with a literal path.
31
+ let file;
32
+ if (typeof flags.file === 'string') {
33
+ file = resolve(flags.file);
34
+ }
35
+ else {
36
+ const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
37
+ const repoRoot = await findRepoRoot(startDir);
38
+ file = join(repoRoot, 'docs', 'src', 'antora.yml');
39
+ }
40
+ if (!(await exists(file))) {
41
+ console.error(`no antora.yml found at '${file}'`);
42
+ console.error('pass --dir <path> (anywhere inside the repository) or --file <path> directly');
43
+ return 1;
44
+ }
45
+ const content = await readFile(file, 'utf8');
46
+ const before = readVersion(content);
47
+ let patched;
48
+ try {
49
+ patched = patchVersion(content, { version: value, prerelease: flags.prerelease === true });
50
+ }
51
+ catch (err) {
52
+ console.error(err instanceof Error ? err.message : String(err));
53
+ console.error(`'${file}' does not look like an Antora component descriptor`);
54
+ return 1;
55
+ }
56
+ await writeFile(file, patched, 'utf8');
57
+ console.log(` ${before ?? '(none)'} → ${value}${flags.prerelease ? ' (prerelease)' : ''}`);
58
+ return 0;
59
+ }
60
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+ // AGENTS.md is the one scaffolded file `docouture new`/`docouture upgrade` never
3
+ // treat as all-or-nothing: unlike a workflow file or a skill directory, a
4
+ // repository may well already have its own AGENTS.md before `docouture new`
5
+ // ever runs, and the `documenting-your-repo` skill edits this file's own
6
+ // '## Documentation state' table over time as a repo's docs grow — content
7
+ // no template regenerates. Rather than clobber the whole file (today's
8
+ // behavior for every other scaffolded file) or refuse outright, docouture' own
9
+ // content lives inside a clearly marked, mechanically findable block —
10
+ // everything outside it (a human's own notes, or that evolving table) is
11
+ // never touched. The template itself
12
+ // (templates/agent-support/AGENTS.md) places MANAGED_END right before
13
+ // '## Documentation state', so that section is deliberately outside the
14
+ // block even on a brand-new scaffold.
15
+ export const MANAGED_START = '<!-- docouture:start - managed by docouture; edits inside this block are overwritten by `docouture new`/`docouture upgrade` -->';
16
+ export const MANAGED_END = '<!-- docouture:end -->';
17
+ export const AGENTS_MD_FILENAME = 'AGENTS.md';
18
+ // True when `content` already has a complete docouture-managed block — the
19
+ // signal `new.ts` uses to decide whether an existing AGENTS.md counts as a
20
+ // genuine overwrite conflict (subject to the same confirm-before-overwrite
21
+ // prompt as workflows/skills) versus a foreign file that's simply safe to
22
+ // append to without asking.
23
+ export function hasManagedSection(content) {
24
+ const start = content.indexOf(MANAGED_START);
25
+ const end = content.indexOf(MANAGED_END);
26
+ return start !== -1 && end !== -1 && end > start;
27
+ }
28
+ function extractManagedBlock(templateContent) {
29
+ const start = templateContent.indexOf(MANAGED_START);
30
+ const end = templateContent.indexOf(MANAGED_END);
31
+ if (start === -1 || end === -1 || end <= start) {
32
+ // Only reachable if templates/agent-support/AGENTS.md itself loses its
33
+ // own markers — a packaging bug, not a runtime/user condition.
34
+ throw new Error('templates/agent-support/AGENTS.md is missing its docouture:start/docouture:end markers');
35
+ }
36
+ return templateContent.slice(start, end + MANAGED_END.length);
37
+ }
38
+ // Merges `templateContent` — this run's freshly rendered AGENTS.md, exactly
39
+ // as `docouture new` would write it from scratch (placeholders already
40
+ // substituted, both markers and the default '## Documentation state' tail
41
+ // all present) — into `existing`, the AGENTS.md content already on disk (or
42
+ // `undefined` if there wasn't one):
43
+ // - no existing file -> templateContent as-is, unchanged from
44
+ // today's whole-file scaffold output.
45
+ // - existing has a managed -> only that block is replaced, sliced out
46
+ // block already of templateContent; everything in
47
+ // `existing` before/after it (a human's
48
+ // own notes, the Documentation state
49
+ // table) survives byte for byte.
50
+ // - existing has no managed -> templateContent (block + its own
51
+ // block (a foreign/human default tail) is appended to the end
52
+ // AGENTS.md, or one from of `existing`, blank-line separated;
53
+ // before this existed) nothing already there is touched.
54
+ export function mergeAgentsMd(existing, templateContent) {
55
+ if (existing === undefined)
56
+ return templateContent;
57
+ const start = existing.indexOf(MANAGED_START);
58
+ const end = existing.indexOf(MANAGED_END);
59
+ if (start !== -1 && end !== -1 && end > start) {
60
+ const before = existing.slice(0, start);
61
+ const after = existing.slice(end + MANAGED_END.length);
62
+ return `${before}${extractManagedBlock(templateContent)}${after}`;
63
+ }
64
+ const separator = existing.length === 0 ? '' : existing.endsWith('\n\n') ? '' : existing.endsWith('\n') ? '\n' : '\n\n';
65
+ return `${existing}${separator}${templateContent}`;
66
+ }
67
+ //# sourceMappingURL=agents-md.js.map
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+ // Antora's own default log level is `warn` — set explicitly by @antora/
3
+ // playbook-builder's convict schema, not the `info` @antora/logger falls
4
+ // back to on its own when nothing configures it. Left alone, every one of
5
+ // docouture's own Antora pipeline extensions (kroki-prewarm.js, search-index.js,
6
+ // llms-txt.js, footer.js, nav-modules.js, not-found-page.js,
7
+ // version-report.js — anything calling `context.getLogger('docouture-...')`)
8
+ // logs its own healthy-run observability at `info` and is silently dropped
9
+ // before it ever reaches stdout. `docouture build`/`docouture dev` pass this to
10
+ // every Antora invocation they make so those lines are actually emitted —
11
+ // the monorepo's own `just dev`/`just build-site` recipes do the same for
12
+ // the identical reason (see justfile's own comments on this).
13
+ export const ANTORA_LOG_LEVEL_ARGS = ['--log-level=info'];
14
+ // Matches a JSON log line carrying `"level":"warn"` or `"level":"error"`
15
+ // (Antora's own genuine signal, regardless of source), or one from any
16
+ // `docouture-*` named logger regardless of level (every current and future
17
+ // docouture extension follows that naming convention — see the file list
18
+ // above) — docouture's own healthy-run observability, which is otherwise
19
+ // indistinguishable from Antora's own internal `info` chatter that
20
+ // `--log-level=info` also just turned on.
21
+ const OBSERVABLE_LINE = /"level":"(?:warn|error)"|"name":"docouture-/;
22
+ /**
23
+ * Filters raw Antora output (stdout, or stdout+stderr concatenated) down to
24
+ * the lines worth showing on an otherwise-quiet, successful run: see
25
+ * `OBSERVABLE_LINE`'s own comment for exactly which ones. Returns '' when
26
+ * nothing qualifies — callers should skip printing entirely rather than
27
+ * print a blank line. A failed run should show its raw, unfiltered output
28
+ * instead of this — the whole story a filter might otherwise cut short.
29
+ */
30
+ export function filterObservableAntoraLog(output) {
31
+ return output
32
+ .split('\n')
33
+ .filter((line) => OBSERVABLE_LINE.test(line))
34
+ .join('\n');
35
+ }
36
+ //# sourceMappingURL=antora-log.js.map
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+ // Patches the two fields `docouture version` cares about — `version:` and
3
+ // `prerelease:` — by line, rather than parsing the file into a YAML document
4
+ // and re-serialising it. A full parse/dump round-trip would lose the comments
5
+ // antora.yml carries (see docs-site-package's own antora.yml for an example),
6
+ // and this file's whole reason to be readable is that a human edits it too.
7
+ //
8
+ // This only ever touches top-level keys, matched at the start of a line
9
+ // (column 0), which is where Antora requires `name`/`title`/`version` to live
10
+ // in a component descriptor.
11
+ const VERSION_LINE = /^version:.*$/m;
12
+ const PRERELEASE_LINE = /^prerelease:.*$/m;
13
+ export function readVersion(content) {
14
+ const match = VERSION_LINE.exec(content);
15
+ if (!match)
16
+ return null;
17
+ const value = match[0].slice('version:'.length).trim();
18
+ return value.length > 0 ? value : null;
19
+ }
20
+ export function hasVersionLine(content) {
21
+ return VERSION_LINE.test(content);
22
+ }
23
+ export function patchVersion(content, patch) {
24
+ if (!VERSION_LINE.test(content)) {
25
+ throw new Error("no top-level 'version:' line found");
26
+ }
27
+ let next = content.replace(VERSION_LINE, `version: ${patch.version}`);
28
+ if (patch.prerelease) {
29
+ if (PRERELEASE_LINE.test(next)) {
30
+ next = next.replace(PRERELEASE_LINE, 'prerelease: true');
31
+ }
32
+ else {
33
+ // Insert directly after the version line, so a reader sees the two
34
+ // together rather than prerelease landing wherever the regex first
35
+ // matched something else.
36
+ next = next.replace(VERSION_LINE, (line) => `${line}\nprerelease: true`);
37
+ }
38
+ }
39
+ else if (PRERELEASE_LINE.test(next)) {
40
+ next = next.replace(`${PRERELEASE_LINE.exec(next)[0]}\n`, '');
41
+ // The line may be the last one in the file, with no trailing newline to
42
+ // consume in the replacement above — handle that case too.
43
+ next = next.replace(PRERELEASE_LINE, '').replace(/\n{3,}/g, '\n\n');
44
+ }
45
+ return next;
46
+ }
47
+ //# sourceMappingURL=antora-yml.js.map
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+ export function parseArgs(argv) {
3
+ const positional = [];
4
+ const flags = {};
5
+ for (let i = 0; i < argv.length; i++) {
6
+ const arg = argv[i];
7
+ if (arg === undefined)
8
+ continue;
9
+ if (arg.startsWith('--')) {
10
+ const name = arg.slice(2);
11
+ const next = argv[i + 1];
12
+ if (next !== undefined && !next.startsWith('--')) {
13
+ flags[name] = next;
14
+ i++;
15
+ }
16
+ else {
17
+ flags[name] = true;
18
+ }
19
+ }
20
+ else {
21
+ positional.push(arg);
22
+ }
23
+ }
24
+ return { positional, flags };
25
+ }
26
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+ const DEFAULT_CONTEXT = { json: false, verbose: false, noColor: false };
3
+ let context = { ...DEFAULT_CONTEXT };
4
+ export function setContext(next) {
5
+ context = { ...context, ...next };
6
+ }
7
+ export function getContext() {
8
+ return context;
9
+ }
10
+ // Test-only: every other consumer only ever wants to add to context, never
11
+ // to guarantee a clean slate — vitest, however, runs every *.spec.ts in the
12
+ // same process, so a --json/--verbose/--no-color set by one test would
13
+ // otherwise leak into the next.
14
+ export function resetContext() {
15
+ context = { ...DEFAULT_CONTEXT };
16
+ }
17
+ //# sourceMappingURL=cli-context.js.map
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ // Reads THIS package's own package.json — the one npm actually published (or
6
+ // `just release-local` snapshot-published) — never a value baked in at
7
+ // compile time, so it can never drift from what's really running. Callers
8
+ // pass their own import.meta.url plus how many directories separate them
9
+ // from the package root (bin.js lives at build/bin.js, one level down;
10
+ // new.js lives at build/commands/new.js, two levels down), since that
11
+ // differs by caller and there is no other reliable anchor at runtime.
12
+ export async function readCliInfo(callerUrl, levelsToPackageRoot) {
13
+ const here = dirname(fileURLToPath(callerUrl));
14
+ const upSegments = Array.from({ length: levelsToPackageRoot }, () => '..');
15
+ const pkgPath = join(here, ...upSegments, 'package.json');
16
+ const raw = await readFile(pkgPath, 'utf8');
17
+ const pkg = JSON.parse(raw);
18
+ return { name: pkg.name ?? '@inditextech/docouture-cli', version: pkg.version ?? '0.0.0' };
19
+ }
20
+ //# sourceMappingURL=cli-info.js.map
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+ // The one config-precedence rule this CLI applies in more than one place —
3
+ // CLI flags win over a persisted/configured value, which wins over a
4
+ // hardcoded default — pulled out into a single, tested function instead of
5
+ // each command re-deriving the same `{ ...a, ...b, ...c }` merge with its
6
+ // own slightly different shape (publish.ts's options, upgrade.ts's title).
7
+ // Per-command commands still own *what* the three layers are (reading
8
+ // docs/package.json, docs/antora.yml, flags) — this only owns the merge
9
+ // order itself, and only overrides a key when the flag/configured layer
10
+ // actually provided a value: `undefined` never wins over a real default.
11
+ export function resolveConfig(defaults, configured = {}, flags = {}) {
12
+ const result = { ...defaults };
13
+ for (const [key, value] of Object.entries(configured)) {
14
+ if (value !== undefined)
15
+ result[key] = value;
16
+ }
17
+ for (const [key, value] of Object.entries(flags)) {
18
+ if (value !== undefined)
19
+ result[key] = value;
20
+ }
21
+ return result;
22
+ }
23
+ //# sourceMappingURL=config-resolver.js.map