company-dossier 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -140,10 +140,69 @@ Tool input (same as the stdio server):
140
140
  { "target": "acme.com", "sections": ["overview", "tech", "risk"] }
141
141
  ```
142
142
 
143
+ ## Docs-site output
144
+
145
+ `company-dossier` can turn a generated dossier into a themed, **static
146
+ [Astro Starlight](https://starlight.astro.build) docs site** — one page per
147
+ section, an autogenerated sidebar, and a "case file" (ink-on-paper) theme.
148
+
149
+ ```bash
150
+ # scaffold a Starlight site under "<Company> DOSSIER/site/"
151
+ company-dossier acme.com --out ./out --format site
152
+
153
+ # then build it (Node-only):
154
+ cd "./out/Acme DOSSIER/site" && npm install && npm run build # → site/dist
155
+ ```
156
+
157
+ Site-related flags:
158
+
159
+ | Flag | Default | Meaning |
160
+ |------|---------|---------|
161
+ | `--format <folder\|site>` | `folder` | `site` scaffolds the Starlight project. |
162
+ | `--deploy <none\|gh-pages>` | `none` | `gh-pages` installs, builds, and publishes `site/dist` via the [`gh-pages`](https://www.npmjs.com/package/gh-pages) package (an optional dep of the *generated* site). |
163
+ | `--subdomain <host>` | — | Writes `public/CNAME` for a custom host. |
164
+ | `--no-noindex` | (noindex on) | Opt out of the unlisted/noindex policy below. |
165
+
166
+ ### Unlisted by default (policy)
167
+
168
+ Per-company dossier sites are **UNLISTED + NOINDEX by default**. Every generated
169
+ page carries:
170
+
171
+ - `<meta name="robots" content="noindex,nofollow">` (injected via Starlight `head`),
172
+ - a visible disclaimer — *"Auto-generated from public sources — may be
173
+ inaccurate; not affiliated with the company"* — plus a takedown/contact line, and
174
+ - a `public/robots.txt` with `Disallow: /` (and `public/.nojekyll`).
175
+
176
+ Pass `--no-noindex` to allow indexing (robots meta is dropped and robots.txt
177
+ becomes `Allow: /`).
178
+
179
+ The scaffold (`<Company> DOSSIER/site/`) contains `package.json`
180
+ (`astro` + `@astrojs/starlight`, optional `gh-pages`), `astro.config.mjs`,
181
+ `src/content/config.ts` (loose docs schema), `src/content/docs/*.md`
182
+ (one per section + an `index.md` hero from the overview),
183
+ `src/styles/case-file.css`, and `public/` assets.
184
+
185
+ ### Library usage
186
+
187
+ ```ts
188
+ import { buildDossier, writeDossier, generateSite } from 'company-dossier';
189
+
190
+ const result = await buildDossier('acme.com');
191
+ const folder = writeDossier(result, './out'); // "<Company> DOSSIER/"
192
+ const site = await generateSite(result, folder, { // → folder + "/site/"
193
+ noindex: true, // default; set false to allow indexing
194
+ subdomain: 'acme.example.com', // optional → public/CNAME
195
+ title: 'Acme', // optional override (defaults to company name)
196
+ });
197
+ console.log(site.siteDir, site.files.length);
198
+ ```
199
+
143
200
  ## Output
144
201
 
145
202
  - A `<Company> DOSSIER/` folder with `README.md`, nine numbered markdown
146
203
  sections, and `dossier.json`.
204
+ - With `--format site`, additionally a `<Company> DOSSIER/site/` Astro
205
+ Starlight project (build it with `npm install && npm run build`).
147
206
  - With `--json`, the structured dossier is printed to stdout instead.
148
207
 
149
208
  ## Public sources only
package/dist/cli.js CHANGED
@@ -20,6 +20,9 @@ function writeFileSafe(filePath, content) {
20
20
  function todayISO() {
21
21
  return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
22
22
  }
23
+ function slugify(name) {
24
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/_+$/, "").replace(/^_+/, "");
25
+ }
23
26
  function titleCase(str) {
24
27
  return str.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
25
28
  }
@@ -913,7 +916,7 @@ var SECTIONS = [
913
916
  "relationships",
914
917
  "risk"
915
918
  ];
916
- var GENERATOR = "company-dossier v0.1.0";
919
+ var GENERATOR = "company-dossier v0.3.0";
917
920
  var HOMEPAGE = "https://companydossier.lol";
918
921
  var GAP = "_Gap: no public data found \u2014 requires manual research._";
919
922
  function deriveCompanyName(target, website) {
@@ -1405,19 +1408,304 @@ async function buildDossier(target, opts = {}) {
1405
1408
  }
1406
1409
 
1407
1410
  // src/index.ts
1411
+ import * as path3 from "path";
1412
+
1413
+ // src/generators/site.ts
1408
1414
  import * as path2 from "path";
1415
+ var ASTRO_VERSION = "^5.7.0";
1416
+ var STARLIGHT_VERSION = "^0.34.0";
1417
+ var GH_PAGES_VERSION = "^6.3.0";
1418
+ var DISCLAIMER = "Auto-generated from public sources \u2014 may be inaccurate; not affiliated with the company.";
1419
+ var TAKEDOWN = "Takedown / contact: open an issue at https://github.com/ever-just/company-dossier/issues";
1420
+ function stripFrontmatter(content) {
1421
+ if (content.startsWith("---\n")) {
1422
+ const end = content.indexOf("\n---", 4);
1423
+ if (end !== -1) {
1424
+ const after = content.indexOf("\n", end + 1);
1425
+ return after !== -1 ? content.slice(after + 1) : "";
1426
+ }
1427
+ }
1428
+ return content;
1429
+ }
1430
+ function extractTitle2(content, fallback) {
1431
+ const m = content.match(/^#\s+(.+?)\s*$/m);
1432
+ if (!m) return fallback;
1433
+ let t = m[1].trim();
1434
+ t = t.replace(/^\d+\.\s*/, "");
1435
+ t = t.replace(/\s*[—–-]\s*[^—–-]+$/, "");
1436
+ return t.trim() || fallback;
1437
+ }
1438
+ function orderFromFilename(filePath) {
1439
+ const m = path2.basename(filePath).match(/^(\d+)/);
1440
+ return m ? parseInt(m[1], 10) : void 0;
1441
+ }
1442
+ function yamlString(value) {
1443
+ return '"' + value.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
1444
+ }
1445
+ function isOverview(filePath) {
1446
+ const b = path2.basename(filePath).toLowerCase();
1447
+ return b === "readme.md" || /^0*1[_.]/.test(b) || b.includes("overview");
1448
+ }
1449
+ function buildPackageJson(name, deploy) {
1450
+ const pkg = {
1451
+ name: `${slugify(name) || "company"}-dossier-site`,
1452
+ private: true,
1453
+ version: "0.0.1",
1454
+ type: "module",
1455
+ scripts: {
1456
+ dev: "astro dev",
1457
+ build: "astro build",
1458
+ preview: "astro preview",
1459
+ ...deploy ? { deploy: "gh-pages -d dist -t true" } : {}
1460
+ },
1461
+ dependencies: {
1462
+ astro: ASTRO_VERSION,
1463
+ "@astrojs/starlight": STARLIGHT_VERSION
1464
+ },
1465
+ // gh-pages is an optional dep of the GENERATED site, not the main package.
1466
+ optionalDependencies: {
1467
+ "gh-pages": GH_PAGES_VERSION
1468
+ }
1469
+ };
1470
+ return JSON.stringify(pkg, null, 2) + "\n";
1471
+ }
1472
+ function buildAstroConfig(title, noindex) {
1473
+ const headEntry = noindex ? `
1474
+ head: [
1475
+ {
1476
+ tag: 'meta',
1477
+ attrs: { name: 'robots', content: 'noindex,nofollow' },
1478
+ },
1479
+ ],` : "";
1480
+ return `import { defineConfig } from 'astro/config';
1481
+ import starlight from '@astrojs/starlight';
1482
+
1483
+ // Generated by company-dossier (--format site).
1484
+ // Per policy, per-company dossier sites are UNLISTED + NOINDEX by default.
1485
+ export default defineConfig({
1486
+ integrations: [
1487
+ starlight({
1488
+ title: ${yamlString(title)},
1489
+ description: ${yamlString(DISCLAIMER)},
1490
+ tableOfContents: true,
1491
+ customCss: ['./src/styles/case-file.css'],${headEntry}
1492
+ // Sidebar autogenerated from the docs tree.
1493
+ sidebar: [
1494
+ {
1495
+ label: 'Dossier',
1496
+ autogenerate: { directory: '.' },
1497
+ },
1498
+ ],
1499
+ // Footer disclaimer + takedown line on every page.
1500
+ credits: false,
1501
+ lastUpdated: false,
1502
+ pagination: false,
1503
+ }),
1504
+ ],
1505
+ });
1506
+ `;
1507
+ }
1508
+ function buildContentConfig() {
1509
+ return `import { defineCollection } from 'astro:content';
1510
+ import { docsLoader } from '@astrojs/starlight/loaders';
1511
+ import { docsSchema } from '@astrojs/starlight/schema';
1512
+ import { z } from 'astro:content';
1513
+
1514
+ export const collections = {
1515
+ docs: defineCollection({
1516
+ loader: docsLoader(),
1517
+ schema: docsSchema({
1518
+ // Tolerant extra frontmatter keys for generated content.
1519
+ extend: z.object({
1520
+ order: z.number().optional(),
1521
+ section: z.string().optional(),
1522
+ sourceFile: z.string().optional(),
1523
+ generatedAt: z.string().optional(),
1524
+ company: z.string().optional(),
1525
+ domain: z.string().optional(),
1526
+ customNote: z.string().optional(),
1527
+ }),
1528
+ }),
1529
+ }),
1530
+ };
1531
+ `;
1532
+ }
1533
+ function buildCaseFileCss() {
1534
+ return `/* company-dossier "case file" theme.
1535
+ Ink-on-paper light scheme expressed through Starlight design tokens. */
1536
+
1537
+ :root {
1538
+ /* Manila / paper accent (amber-brown ink stamp). */
1539
+ --sl-color-accent-low: #efe6d2;
1540
+ --sl-color-accent: #8a5a1f;
1541
+ --sl-color-accent-high: #4d3210;
1542
+
1543
+ /* Ink on aged paper. */
1544
+ --sl-color-white: #1c1a16;
1545
+ --sl-color-gray-1: #2a2620;
1546
+ --sl-color-gray-2: #423c31;
1547
+ --sl-color-gray-3: #6b6253;
1548
+ --sl-color-gray-4: #8c8270;
1549
+ --sl-color-gray-5: #b9ad95;
1550
+ --sl-color-gray-6: #e6dcc6;
1551
+ --sl-color-gray-7: #f1e9d6;
1552
+ --sl-color-black: #faf4e4;
1553
+
1554
+ --sl-color-bg: #faf4e4;
1555
+ --sl-color-bg-nav: #f1e9d6;
1556
+ --sl-color-bg-sidebar: #f4ecda;
1557
+ --sl-color-text: #1c1a16;
1558
+ --sl-color-text-accent: #8a5a1f;
1559
+ --sl-color-hairline: #d8cbac;
1560
+ --sl-color-hairline-light: #e6dcc6;
1561
+
1562
+ --sl-font: ui-serif, Georgia, 'Times New Roman', serif;
1563
+ --sl-font-mono: ui-monospace, 'SFMono-Regular', 'Courier New', monospace;
1564
+ }
1565
+
1566
+ /* Keep a dark variant readable too (folder-at-night). */
1567
+ :root[data-theme='dark'] {
1568
+ --sl-color-accent-low: #2a2012;
1569
+ --sl-color-accent: #d9a441;
1570
+ --sl-color-accent-high: #f3d79a;
1571
+ --sl-color-bg: #16140f;
1572
+ --sl-color-bg-nav: #1d1a13;
1573
+ --sl-color-bg-sidebar: #1b1812;
1574
+ --sl-color-text: #ece2cc;
1575
+ --sl-color-hairline: #36301f;
1576
+ }
1577
+
1578
+ /* Markdown content reads like a typed report. */
1579
+ .sl-markdown-content {
1580
+ line-height: 1.6;
1581
+ }
1582
+ .sl-markdown-content table {
1583
+ font-family: var(--sl-font-mono);
1584
+ font-size: 0.85rem;
1585
+ }
1586
+
1587
+ /* CONFIDENTIAL / disclaimer banner injected at the top of every page. */
1588
+ .dossier-banner {
1589
+ border: 1px dashed var(--sl-color-accent);
1590
+ background: var(--sl-color-accent-low);
1591
+ color: var(--sl-color-accent-high);
1592
+ padding: 0.6rem 0.9rem;
1593
+ margin: 0 0 1.25rem 0;
1594
+ border-radius: 4px;
1595
+ font-family: var(--sl-font-mono);
1596
+ font-size: 0.8rem;
1597
+ line-height: 1.45;
1598
+ }
1599
+ .dossier-banner strong {
1600
+ display: inline-block;
1601
+ letter-spacing: 0.18em;
1602
+ text-transform: uppercase;
1603
+ margin-right: 0.5rem;
1604
+ }
1605
+ .dossier-banner .dossier-takedown {
1606
+ display: block;
1607
+ margin-top: 0.35rem;
1608
+ opacity: 0.85;
1609
+ }
1610
+ `;
1611
+ }
1612
+ function buildRobotsTxt(noindex) {
1613
+ if (noindex) {
1614
+ return "User-agent: *\nDisallow: /\n";
1615
+ }
1616
+ return "User-agent: *\nAllow: /\n";
1617
+ }
1618
+ function disclaimerMarkdown() {
1619
+ return `:::caution[Disclaimer]
1620
+ **CONFIDENTIAL \u2014 UNVERIFIED.** ${DISCLAIMER}
1621
+
1622
+ ${TAKEDOWN}
1623
+ :::
1624
+
1625
+ `;
1626
+ }
1627
+ function buildIndexDoc(result, files, title) {
1628
+ const overviewFile = files.find((f) => path2.basename(f.path).toLowerCase() === "readme.md") || files.find((f) => isOverview(f.path));
1629
+ const body = overviewFile ? stripFrontmatter(overviewFile.content) : "";
1630
+ const bodyNoH1 = body.replace(/^#\s+.+?\r?\n/, "");
1631
+ const frontmatter = [
1632
+ "---",
1633
+ `title: ${yamlString(title)}`,
1634
+ "template: splash",
1635
+ "hero:",
1636
+ ` title: ${yamlString(title)}`,
1637
+ ` tagline: ${yamlString("Intelligence dossier compiled from public sources.")}`,
1638
+ " actions:",
1639
+ ` - text: Read the dossier`,
1640
+ ` link: ./overview-identity/`,
1641
+ ` icon: right-arrow`,
1642
+ "---",
1643
+ ""
1644
+ ].join("\n");
1645
+ return frontmatter + disclaimerMarkdown() + bodyNoH1.trim() + "\n";
1646
+ }
1647
+ function buildSectionDoc(file, title, order) {
1648
+ const body = stripFrontmatter(file.content).replace(/^#\s+.+?\r?\n/, "");
1649
+ const fm = ["---", `title: ${yamlString(title)}`];
1650
+ if (order !== void 0) {
1651
+ fm.push("sidebar:", ` order: ${order}`);
1652
+ }
1653
+ fm.push("---", "");
1654
+ return fm.join("\n") + disclaimerMarkdown() + body.trim() + "\n";
1655
+ }
1656
+ async function generateSite(result, outDir, opts = {}) {
1657
+ const noindex = opts.noindex !== false;
1658
+ const title = (opts.title || result.meta.companyName || "Company Dossier").trim();
1659
+ const siteDir = path2.join(outDir, "site");
1660
+ const written = [];
1661
+ const write = (rel, content) => {
1662
+ const abs = path2.join(siteDir, rel);
1663
+ writeFileSafe(abs, content);
1664
+ written.push(abs);
1665
+ };
1666
+ const files = result.files;
1667
+ write("package.json", buildPackageJson(title, true));
1668
+ write("astro.config.mjs", buildAstroConfig(title, noindex));
1669
+ write("src/content/config.ts", buildContentConfig());
1670
+ write("src/styles/case-file.css", buildCaseFileCss());
1671
+ write("tsconfig.json", JSON.stringify({ extends: "astro/tsconfigs/strict" }, null, 2) + "\n");
1672
+ write("src/content/docs/index.md", buildIndexDoc(result, files, title));
1673
+ const usedSlugs = /* @__PURE__ */ new Set(["index"]);
1674
+ for (const file of files) {
1675
+ const base = path2.basename(file.path).toLowerCase();
1676
+ if (base === "dossier.json") continue;
1677
+ if (base === "readme.md") continue;
1678
+ if (!base.endsWith(".md")) continue;
1679
+ const docTitle = extractTitle2(file.content, path2.basename(file.path, ".md"));
1680
+ let slug = slugify(docTitle).replace(/_/g, "-") || slugify(base.replace(/\.md$/, ""));
1681
+ let unique = slug;
1682
+ let n = 2;
1683
+ while (usedSlugs.has(unique)) unique = `${slug}-${n++}`;
1684
+ usedSlugs.add(unique);
1685
+ const order = orderFromFilename(file.path);
1686
+ write(`src/content/docs/${unique}.md`, buildSectionDoc(file, docTitle, order));
1687
+ }
1688
+ write("public/.nojekyll", "");
1689
+ write("public/robots.txt", buildRobotsTxt(noindex));
1690
+ if (opts.subdomain && opts.subdomain.trim()) {
1691
+ write("public/CNAME", opts.subdomain.trim() + "\n");
1692
+ }
1693
+ return { siteDir, files: written, noindex };
1694
+ }
1695
+
1696
+ // src/index.ts
1409
1697
  function writeDossier(result, outDir) {
1410
1698
  const folderName = `${result.meta.companyName} DOSSIER`.trim();
1411
1699
  const safeFolder = folderName.replace(/[/\\]/g, "_");
1412
- const target = path2.join(outDir, safeFolder);
1700
+ const target = path3.join(outDir, safeFolder);
1413
1701
  for (const file of result.files) {
1414
- writeFileSafe(path2.join(target, file.path), file.content);
1702
+ writeFileSafe(path3.join(target, file.path), file.content);
1415
1703
  }
1416
1704
  return target;
1417
1705
  }
1418
1706
 
1419
1707
  // src/cli.ts
1420
- var VERSION = "0.1.0";
1708
+ var VERSION = "0.3.0";
1421
1709
  var HELP = `company-dossier v${VERSION}
1422
1710
  Build a complete, sourced intelligence dossier on any company from public data.
1423
1711
 
@@ -1436,6 +1724,15 @@ OPTIONS
1436
1724
  Available: ${SECTIONS.join(", ")}
1437
1725
  --max-pages <n> Max internal pages to crawl (default: 25).
1438
1726
  --no-social-probe Skip slow HEAD-probing of social platforms.
1727
+ --format <fmt> Output format: folder (default) or site.
1728
+ "site" scaffolds a themed, static Astro Starlight
1729
+ docs site under "<Company> DOSSIER/site/".
1730
+ --deploy <target> Deploy target for --format site: none (default) or
1731
+ gh-pages (builds site/dist and publishes via the
1732
+ gh-pages npm package).
1733
+ --subdomain <host> Custom host for the site; writes public/CNAME.
1734
+ --no-noindex Allow indexing. By POLICY, dossier sites are
1735
+ UNLISTED + NOINDEX by default; this opts out.
1439
1736
  --quiet Suppress progress output.
1440
1737
  -h, --help Show this help.
1441
1738
  -v, --version Show version.
@@ -1450,6 +1747,12 @@ EXAMPLES
1450
1747
  company-dossier "Acme Corporation"
1451
1748
  company-dossier acme.com --json > acme.json
1452
1749
  company-dossier acme.com --sections overview,tech,risk
1750
+ company-dossier acme.com --out ./out --format site
1751
+ company-dossier acme.com --format site --deploy gh-pages --subdomain acme.example.com
1752
+
1753
+ Generated dossier sites are UNLISTED + NOINDEX by default (noindex meta,
1754
+ robots.txt Disallow, and a visible "auto-generated / not affiliated"
1755
+ disclaimer on every page). Use --no-noindex to opt out.
1453
1756
 
1454
1757
  Public sources only \u2014 no private databases, no API keys required.
1455
1758
  Learn more: https://companydossier.lol
@@ -1460,6 +1763,9 @@ function parseArgs(argv) {
1460
1763
  json: false,
1461
1764
  quiet: false,
1462
1765
  skipSocialProbe: false,
1766
+ format: "folder",
1767
+ deploy: "none",
1768
+ noindex: true,
1463
1769
  help: false,
1464
1770
  version: false
1465
1771
  };
@@ -1483,6 +1789,30 @@ function parseArgs(argv) {
1483
1789
  case "--no-social-probe":
1484
1790
  out.skipSocialProbe = true;
1485
1791
  break;
1792
+ case "--no-noindex":
1793
+ out.noindex = false;
1794
+ break;
1795
+ case "--format": {
1796
+ const v = (argv[++i] ?? "").trim().toLowerCase();
1797
+ if (v === "folder" || v === "site") {
1798
+ out.format = v;
1799
+ } else {
1800
+ out.error = `Invalid --format: ${v || "(missing)"}. Use folder or site.`;
1801
+ }
1802
+ break;
1803
+ }
1804
+ case "--deploy": {
1805
+ const v = (argv[++i] ?? "").trim().toLowerCase();
1806
+ if (v === "none" || v === "gh-pages") {
1807
+ out.deploy = v;
1808
+ } else {
1809
+ out.error = `Invalid --deploy: ${v || "(missing)"}. Use none or gh-pages.`;
1810
+ }
1811
+ break;
1812
+ }
1813
+ case "--subdomain":
1814
+ out.subdomain = (argv[++i] ?? "").trim() || void 0;
1815
+ break;
1486
1816
  case "--out":
1487
1817
  out.out = argv[++i] ?? out.out;
1488
1818
  break;
@@ -1565,11 +1895,65 @@ Run "company-dossier --help".
1565
1895
  log(`
1566
1896
  Dossier written to: ${folder}`);
1567
1897
  log(`Files: ${result.files.map((f) => f.path).join(", ")}`);
1898
+ if (args.format === "site") {
1899
+ log("\nScaffolding Astro Starlight docs site...");
1900
+ const site = await generateSite(result, folder, {
1901
+ subdomain: args.subdomain,
1902
+ noindex: args.noindex
1903
+ });
1904
+ log(`Site scaffolded at: ${site.siteDir}`);
1905
+ log(
1906
+ ` ${site.files.length} files written` + (site.noindex ? " (UNLISTED + NOINDEX: robots meta + Disallow-all)" : " (indexable)")
1907
+ );
1908
+ log(` Build it: cd "${site.siteDir}" && npm install && npm run build`);
1909
+ if (args.deploy === "gh-pages") {
1910
+ const code = await deployGhPages(site.siteDir, log);
1911
+ if (code !== 0) return code;
1912
+ } else {
1913
+ log(" Deploy: skipped (--deploy none).");
1914
+ }
1915
+ if (!args.quiet) {
1916
+ process.stdout.write(site.siteDir + "\n");
1917
+ }
1918
+ return 0;
1919
+ }
1568
1920
  if (!args.quiet) {
1569
1921
  process.stdout.write(folder + "\n");
1570
1922
  }
1571
1923
  return 0;
1572
1924
  }
1925
+ async function deployGhPages(siteDir, log) {
1926
+ const { spawn } = await import("child_process");
1927
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
1928
+ const run = (cmd, cmdArgs) => new Promise((resolve) => {
1929
+ log(` $ ${cmd} ${cmdArgs.join(" ")}`);
1930
+ const child = spawn(cmd, cmdArgs, { cwd: siteDir, stdio: "inherit" });
1931
+ child.on("error", (err) => {
1932
+ process.stderr.write(`Deploy error: ${err.message}
1933
+ `);
1934
+ resolve(1);
1935
+ });
1936
+ child.on("close", (c) => resolve(c ?? 0));
1937
+ });
1938
+ log("\nDeploying to GitHub Pages via gh-pages...");
1939
+ let code = await run(npmCmd, ["install"]);
1940
+ if (code !== 0) {
1941
+ process.stderr.write("Deploy aborted: npm install failed in site/.\n");
1942
+ return code;
1943
+ }
1944
+ code = await run(npmCmd, ["run", "build"]);
1945
+ if (code !== 0) {
1946
+ process.stderr.write("Deploy aborted: astro build failed.\n");
1947
+ return code;
1948
+ }
1949
+ code = await run(npmCmd, ["run", "deploy"]);
1950
+ if (code !== 0) {
1951
+ process.stderr.write("Deploy aborted: gh-pages publish failed.\n");
1952
+ return code;
1953
+ }
1954
+ log("Deployed site/dist to gh-pages branch.");
1955
+ return 0;
1956
+ }
1573
1957
  main().then((code) => process.exit(code)).catch((err) => {
1574
1958
  process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
1575
1959
  `);
package/dist/index.d.ts CHANGED
@@ -200,10 +200,42 @@ interface DossierResult {
200
200
  */
201
201
  declare function buildDossier(target: string, opts?: BuildOptions): Promise<DossierResult>;
202
202
 
203
+ interface GenerateSiteOptions {
204
+ /** Custom subdomain/host. When set, writes a public/CNAME file. */
205
+ subdomain?: string;
206
+ /**
207
+ * Inject `<meta name="robots" content="noindex,nofollow">` on every page
208
+ * and a Disallow-all robots.txt. Defaults to TRUE (unlisted by policy).
209
+ */
210
+ noindex?: boolean;
211
+ /** Override the site title. Defaults to the dossier company name. */
212
+ title?: string;
213
+ }
214
+ interface GenerateSiteResult {
215
+ /** Absolute path to the generated `site/` directory. */
216
+ siteDir: string;
217
+ /** Absolute paths of every file written, relative to nothing — fully qualified. */
218
+ files: string[];
219
+ /** Whether noindex/unlisted policy was applied. */
220
+ noindex: boolean;
221
+ }
222
+ /**
223
+ * Turn a generated dossier into a themed, static Astro Starlight docs site.
224
+ *
225
+ * Scaffolds an Astro Starlight project under `<outDir>/site/`. Per policy,
226
+ * the site is UNLISTED + NOINDEX by default: every page carries a
227
+ * `noindex,nofollow` robots meta, a visible disclaimer, and a takedown line,
228
+ * and robots.txt disallows all crawling.
229
+ *
230
+ * Build it with `cd site && npm install && npm run build` (output in
231
+ * `site/dist`). Deploy is pluggable; pass a subdomain to emit a CNAME.
232
+ */
233
+ declare function generateSite(result: DossierResult, outDir: string, opts?: GenerateSiteOptions): Promise<GenerateSiteResult>;
234
+
203
235
  /**
204
236
  * Write a built dossier to disk. Files are placed inside a
205
237
  * "<Company> DOSSIER/" folder under `outDir`. Returns the folder path.
206
238
  */
207
239
  declare function writeDossier(result: DossierResult, outDir: string): string;
208
240
 
209
- export { type BuildOptions, type DnsData, type DossierData, type DossierFile, type DossierMeta, type DossierResult, type PageData, type ProgressCallback, SECTIONS, type SearchData, type SectionId, type SocialProfile, type TechStackData, type USASpendingContract, type WaybackData, type WebsiteData, buildDossier, collectDns, collectSearch, collectWayback, collectWebsite, extractTechStack, slugify, writeDossier };
241
+ export { type BuildOptions, type DnsData, type DossierData, type DossierFile, type DossierMeta, type DossierResult, type GenerateSiteOptions, type GenerateSiteResult, type PageData, type ProgressCallback, SECTIONS, type SearchData, type SectionId, type SocialProfile, type TechStackData, type USASpendingContract, type WaybackData, type WebsiteData, buildDossier, collectDns, collectSearch, collectWayback, collectWebsite, extractTechStack, generateSite, slugify, writeDossier };