dogsbay 0.2.0-beta.3 → 0.2.0-beta.31
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/dist/commands/agent.js +305 -0
- package/dist/commands/site-build.js +70 -14
- package/dist/commands/site-dev.js +181 -23
- package/dist/commands/site-init.js +199 -32
- package/dist/config/defaults.js +30 -23
- package/dist/config/load.js +16 -32
- package/dist/config/to-astro-options.js +3 -0
- package/dist/import-content.js +16 -12
- package/dist/index.js +19 -4
- package/dist/passthrough-astro.js +152 -0
- package/dist/registry.js +8 -0
- package/package.json +11 -9
- package/skills/platform/agent-readiness/SKILL.md +262 -0
- package/skills/platform/cli-commands/SKILL.md +205 -0
- package/skills/platform/config-yml/SKILL.md +219 -0
- package/skills/platform/frontmatter-fields/SKILL.md +310 -0
- package/skills/platform/markdown-directives/SKILL.md +366 -0
- package/skills/platform/multi-source/SKILL.md +294 -0
- package/skills/platform/nav-file/SKILL.md +107 -0
- package/skills/platform/openapi-source/SKILL.md +237 -0
- package/skills/platform/plugin-api/SKILL.md +280 -0
- package/skills/platform/project-anatomy/SKILL.md +156 -0
- package/skills/platform/taxonomy-config/SKILL.md +392 -0
- package/skills/platform/theme-tokens/SKILL.md +276 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dogsbay agent install` — wire skill discovery for an LLM agent
|
|
3
|
+
* (Claude Code, Cursor, Copilot, etc.).
|
|
4
|
+
*
|
|
5
|
+
* Bundled platform skills live at `<cli-install-dir>/skills/platform/*.md`.
|
|
6
|
+
* This command:
|
|
7
|
+
* 1. Resolves the bundled platform skills directory.
|
|
8
|
+
* 2. Symlinks them into `<project>/.dogsbay/skills/platform/`.
|
|
9
|
+
* 3. Creates `.dogsbay/skills/site/` (empty + README) and
|
|
10
|
+
* `.dogsbay/skills/plugins/` (empty placeholder).
|
|
11
|
+
* 4. For each requested --agent, writes the per-agent discovery
|
|
12
|
+
* path (`.claude/skills/dogsbay/`, `.cursor/rules/dogsbay/`).
|
|
13
|
+
*
|
|
14
|
+
* Re-running is idempotent — symlinks are recreated; existing
|
|
15
|
+
* site/ files are never touched.
|
|
16
|
+
*
|
|
17
|
+
* See plans/dogsbay-agent-skills.md for the four-tier ownership
|
|
18
|
+
* model and how this fits.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, symlinkSync, unlinkSync, writeFileSync, statSync, readlinkSync } from "node:fs";
|
|
21
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
|
+
import pc from "picocolors";
|
|
24
|
+
const SUPPORTED_AGENTS = ["claude", "cursor"];
|
|
25
|
+
const AGENT_TARGETS = {
|
|
26
|
+
claude: {
|
|
27
|
+
name: "claude",
|
|
28
|
+
skillsDir: ".claude/skills",
|
|
29
|
+
label: "Claude Code",
|
|
30
|
+
},
|
|
31
|
+
cursor: {
|
|
32
|
+
name: "cursor",
|
|
33
|
+
skillsDir: ".cursor/rules",
|
|
34
|
+
label: "Cursor",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Prefix every Dogsbay-shipped skill so it doesn't collide with
|
|
39
|
+
* the user's own skills under the same agent's discovery path.
|
|
40
|
+
*/
|
|
41
|
+
const PLATFORM_PREFIX = "dogsbay-";
|
|
42
|
+
const SITE_PREFIX = "dogsbay-site-";
|
|
43
|
+
const PLUGIN_PREFIX = "dogsbay-plugin-";
|
|
44
|
+
export async function agentInstall(cwd, options) {
|
|
45
|
+
const projectRoot = resolve(cwd || ".");
|
|
46
|
+
// Resolve the bundled platform-skills directory. We're running
|
|
47
|
+
// from <cli-install>/dist/commands/agent.js, so walk up to
|
|
48
|
+
// <cli-install>/skills/platform/.
|
|
49
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
50
|
+
const platformSkills = resolve(here, "..", "..", "skills", "platform");
|
|
51
|
+
if (!existsSync(platformSkills)) {
|
|
52
|
+
console.error(pc.red(`Error: bundled platform skills not found at ${platformSkills}.`));
|
|
53
|
+
console.error(` The dogsbay CLI install seems incomplete. Reinstall with`);
|
|
54
|
+
console.error(` 'npm install -g dogsbay@latest'.`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// Pick the agents to install.
|
|
58
|
+
const agents = pickAgents(options);
|
|
59
|
+
if (agents.length === 0) {
|
|
60
|
+
printDetected(projectRoot);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
console.log(pc.cyan("→ Installing skill discovery"));
|
|
64
|
+
// 1. Always set up .dogsbay/skills/{platform,site,plugins}.
|
|
65
|
+
const dogsbayDir = join(projectRoot, ".dogsbay");
|
|
66
|
+
const skillsDir = join(dogsbayDir, "skills");
|
|
67
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
68
|
+
const platformLink = join(skillsDir, "platform");
|
|
69
|
+
refreshSymlink(platformLink, platformSkills);
|
|
70
|
+
console.log(pc.green(` ✓ ${relative(projectRoot, platformLink)} → bundled platform skills`));
|
|
71
|
+
const siteDir = join(skillsDir, "site");
|
|
72
|
+
if (!existsSync(siteDir)) {
|
|
73
|
+
mkdirSync(siteDir, { recursive: true });
|
|
74
|
+
writeFileSync(join(siteDir, "README.md"), `# Site skills
|
|
75
|
+
|
|
76
|
+
This directory holds **site-specific** skills — your team's style
|
|
77
|
+
guide, voice / tone, terminology, glossary, internal conventions.
|
|
78
|
+
Anything an LLM should know that's specific to THIS site.
|
|
79
|
+
|
|
80
|
+
Each skill is a single \`.md\` file with frontmatter:
|
|
81
|
+
|
|
82
|
+
\`\`\`markdown
|
|
83
|
+
---
|
|
84
|
+
name: site:style-guide
|
|
85
|
+
description: Our team's writing voice, terminology, and PR conventions.
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
# Style guide
|
|
89
|
+
|
|
90
|
+
We use Oxford commas. Sentence-case headings. ...
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
These skills are picked up automatically by any agent you've
|
|
94
|
+
installed via \`dogsbay agent install --agent <name>\`.
|
|
95
|
+
|
|
96
|
+
To override a platform skill (e.g. a different opinion on
|
|
97
|
+
\`nav-file.md\`), put your version under \`overrides/<skill-name>.md\`.
|
|
98
|
+
The agent loader checks overrides first.
|
|
99
|
+
`);
|
|
100
|
+
console.log(pc.green(` ✓ ${relative(projectRoot, siteDir)} created (empty + README)`));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.log(pc.gray(` · ${relative(projectRoot, siteDir)} already exists (preserved)`));
|
|
104
|
+
}
|
|
105
|
+
const pluginsDir = join(skillsDir, "plugins");
|
|
106
|
+
if (!existsSync(pluginsDir)) {
|
|
107
|
+
mkdirSync(pluginsDir, { recursive: true });
|
|
108
|
+
}
|
|
109
|
+
// 2. Per-agent discovery — symlink each skill INDIVIDUALLY at
|
|
110
|
+
// the top level of the agent's skills dir. Claude Code (and
|
|
111
|
+
// similar harnesses) discover skills as direct children of
|
|
112
|
+
// .claude/skills/, each containing a SKILL.md. Nested
|
|
113
|
+
// .claude/skills/dogsbay/platform/<skill>/ doesn't get scanned.
|
|
114
|
+
for (const agent of agents) {
|
|
115
|
+
const target = AGENT_TARGETS[agent];
|
|
116
|
+
const agentSkillsDir = join(projectRoot, target.skillsDir);
|
|
117
|
+
mkdirSync(agentSkillsDir, { recursive: true });
|
|
118
|
+
let installed = 0;
|
|
119
|
+
let removed = 0;
|
|
120
|
+
// Platform skills: dogsbay-<name>
|
|
121
|
+
for (const entry of readSkillDirs(join(skillsDir, "platform"))) {
|
|
122
|
+
const dest = join(agentSkillsDir, `${PLATFORM_PREFIX}${entry.name}`);
|
|
123
|
+
refreshSymlink(dest, entry.path);
|
|
124
|
+
installed++;
|
|
125
|
+
}
|
|
126
|
+
// Site skills: dogsbay-site-<name> (skipping overrides/ and the README)
|
|
127
|
+
for (const entry of readSkillDirs(join(skillsDir, "site"))) {
|
|
128
|
+
if (entry.name === "overrides")
|
|
129
|
+
continue;
|
|
130
|
+
const dest = join(agentSkillsDir, `${SITE_PREFIX}${entry.name}`);
|
|
131
|
+
refreshSymlink(dest, entry.path);
|
|
132
|
+
installed++;
|
|
133
|
+
}
|
|
134
|
+
// Plugin skills: dogsbay-plugin-<name>
|
|
135
|
+
for (const entry of readSkillDirs(join(skillsDir, "plugins"))) {
|
|
136
|
+
const dest = join(agentSkillsDir, `${PLUGIN_PREFIX}${entry.name}`);
|
|
137
|
+
refreshSymlink(dest, entry.path);
|
|
138
|
+
installed++;
|
|
139
|
+
}
|
|
140
|
+
// Sweep any stale dogsbay-* symlinks whose target no longer
|
|
141
|
+
// exists (e.g. user uninstalled a plugin, or a platform skill
|
|
142
|
+
// was renamed across CLI versions).
|
|
143
|
+
if (existsSync(agentSkillsDir)) {
|
|
144
|
+
for (const name of readdirSync(agentSkillsDir)) {
|
|
145
|
+
if (!name.startsWith("dogsbay-"))
|
|
146
|
+
continue;
|
|
147
|
+
const path = join(agentSkillsDir, name);
|
|
148
|
+
if (isBrokenSymlink(path)) {
|
|
149
|
+
try {
|
|
150
|
+
unlinkSync(path);
|
|
151
|
+
removed++;
|
|
152
|
+
}
|
|
153
|
+
catch { /* ignore */ }
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
console.log(pc.green(` ✓ ${target.label}: ${installed} skills under ${target.skillsDir}/`));
|
|
158
|
+
if (removed > 0) {
|
|
159
|
+
console.log(pc.gray(` (cleaned up ${removed} stale symlink${removed === 1 ? "" : "s"})`));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
console.log("");
|
|
163
|
+
console.log(pc.cyan("Next:"));
|
|
164
|
+
console.log(" Open your editor — the agent should now see Dogsbay platform skills");
|
|
165
|
+
console.log(" on next prompt.");
|
|
166
|
+
console.log("");
|
|
167
|
+
console.log(" Add team-specific skills to .dogsbay/skills/site/.");
|
|
168
|
+
console.log(" Override a platform skill with .dogsbay/skills/site/overrides/<name>.md.");
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Decide which agents to set up. Priority:
|
|
172
|
+
* --all → every supported agent
|
|
173
|
+
* --agent claude,cursor → exactly that list
|
|
174
|
+
* neither → return [], caller prints detected agents and exits
|
|
175
|
+
*/
|
|
176
|
+
function pickAgents(options) {
|
|
177
|
+
if (options.all)
|
|
178
|
+
return [...SUPPORTED_AGENTS];
|
|
179
|
+
if (options.agent) {
|
|
180
|
+
const requested = options.agent.split(",").map((a) => a.trim().toLowerCase());
|
|
181
|
+
const valid = [];
|
|
182
|
+
for (const r of requested) {
|
|
183
|
+
if (SUPPORTED_AGENTS.includes(r)) {
|
|
184
|
+
valid.push(r);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
console.error(pc.yellow(` warn: unknown agent "${r}" (supported: ${SUPPORTED_AGENTS.join(", ")})`));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return valid;
|
|
191
|
+
}
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* When called without --agent or --all, just probe the project
|
|
196
|
+
* for known agent-config dirs and suggest commands.
|
|
197
|
+
*/
|
|
198
|
+
function printDetected(projectRoot) {
|
|
199
|
+
const detected = [];
|
|
200
|
+
if (existsSync(join(projectRoot, ".claude"))) {
|
|
201
|
+
detected.push({ agent: "claude", signal: ".claude/" });
|
|
202
|
+
}
|
|
203
|
+
if (existsSync(join(projectRoot, ".cursor")) ||
|
|
204
|
+
existsSync(join(projectRoot, ".cursorrules"))) {
|
|
205
|
+
detected.push({ agent: "cursor", signal: ".cursor/ or .cursorrules" });
|
|
206
|
+
}
|
|
207
|
+
console.log(pc.cyan("Dogsbay agent install"));
|
|
208
|
+
console.log("");
|
|
209
|
+
console.log("Wires Dogsbay platform skills into the discovery path of an");
|
|
210
|
+
console.log("LLM agent so it picks them up on every prompt.");
|
|
211
|
+
console.log("");
|
|
212
|
+
if (detected.length > 0) {
|
|
213
|
+
console.log(pc.green("Detected in this project:"));
|
|
214
|
+
for (const d of detected) {
|
|
215
|
+
console.log(` ${d.agent.padEnd(8)} (${d.signal})`);
|
|
216
|
+
}
|
|
217
|
+
console.log("");
|
|
218
|
+
console.log("Run:");
|
|
219
|
+
for (const d of detected) {
|
|
220
|
+
console.log(` dogsbay agent install --agent ${d.agent}`);
|
|
221
|
+
}
|
|
222
|
+
console.log(" dogsbay agent install --all # set up every detected agent");
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
console.log(pc.yellow("No supported agent configs detected in this project."));
|
|
226
|
+
console.log("");
|
|
227
|
+
console.log("Run:");
|
|
228
|
+
console.log(" dogsbay agent install --agent claude");
|
|
229
|
+
console.log(" dogsbay agent install --agent cursor");
|
|
230
|
+
console.log(" dogsbay agent install --all");
|
|
231
|
+
}
|
|
232
|
+
console.log("");
|
|
233
|
+
console.log(`Supported agents: ${SUPPORTED_AGENTS.join(", ")}`);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* List each subdirectory of `dir` that looks like a skill — i.e.
|
|
237
|
+
* contains a SKILL.md file. Returns an empty array if `dir`
|
|
238
|
+
* doesn't exist or has no skill subdirs.
|
|
239
|
+
*/
|
|
240
|
+
function readSkillDirs(dir) {
|
|
241
|
+
if (!existsSync(dir))
|
|
242
|
+
return [];
|
|
243
|
+
const out = [];
|
|
244
|
+
for (const name of readdirSync(dir)) {
|
|
245
|
+
if (name.startsWith("."))
|
|
246
|
+
continue;
|
|
247
|
+
const path = join(dir, name);
|
|
248
|
+
let isDir = false;
|
|
249
|
+
try {
|
|
250
|
+
isDir = statSync(path).isDirectory();
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
isDir = false;
|
|
254
|
+
}
|
|
255
|
+
if (!isDir)
|
|
256
|
+
continue;
|
|
257
|
+
if (!existsSync(join(path, "SKILL.md")))
|
|
258
|
+
continue;
|
|
259
|
+
out.push({ name, path });
|
|
260
|
+
}
|
|
261
|
+
return out;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Replace any existing entry at `linkPath` with a fresh symlink
|
|
265
|
+
* pointing at `target`. Idempotent: if the link already points at
|
|
266
|
+
* the right place, leaves it alone.
|
|
267
|
+
*/
|
|
268
|
+
function refreshSymlink(linkPath, target) {
|
|
269
|
+
if (existsSync(linkPath) || isBrokenSymlink(linkPath)) {
|
|
270
|
+
try {
|
|
271
|
+
const current = readlinkSync(linkPath);
|
|
272
|
+
const resolved = resolve(dirname(linkPath), current);
|
|
273
|
+
if (resolved === target)
|
|
274
|
+
return; // already correct
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// not a symlink
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
unlinkSync(linkPath);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// ignore
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
symlinkSync(target, linkPath, "dir");
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* True only when `p` IS a symlink AND its target doesn't exist.
|
|
290
|
+
* Used by the cleanup pass to remove links pointing at gone paths
|
|
291
|
+
* (e.g. an uninstalled plugin or a renamed platform skill across
|
|
292
|
+
* CLI versions).
|
|
293
|
+
*/
|
|
294
|
+
function isBrokenSymlink(p) {
|
|
295
|
+
try {
|
|
296
|
+
const lst = lstatSync(p);
|
|
297
|
+
if (!lst.isSymbolicLink())
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
// It's a symlink. existsSync follows the link; false → target gone.
|
|
304
|
+
return !existsSync(p);
|
|
305
|
+
}
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
import { existsSync } from "node:fs";
|
|
18
18
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
19
19
|
import pc from "picocolors";
|
|
20
|
-
import { emitAstroPages, emitSiteConfig, emitConfigDerivedFiles, emitAgentReadinessFiles, emitMissingTranslationStubs, emitSwitcherMap, emitTaxonomyRoutes, emitPluginRuntime, normalizeBasePath, basePathSegments, } from "@dogsbay/format-astro";
|
|
20
|
+
import { emitAstroPages, emitPassthroughAstroPages, emitSiteConfig, emitConfigDerivedFiles, emitDeployArtifacts, emitAgentReadinessFiles, emitMissingTranslationStubs, emitSwitcherMap, emitTaxonomyRoutes, emitPluginRuntime, normalizeBasePath, basePathSegments, resolvePrefixes, } from "@dogsbay/format-astro";
|
|
21
|
+
import { collectPassthroughEntries } from "../passthrough-astro.js";
|
|
21
22
|
import { configToAstroOptions, findConfig, loadConfig, resolveOutputDir, } from "../config/index.js";
|
|
22
23
|
import { filterSourcesForMode, importContent, primaryModeFiltersAnything, } from "../import-content.js";
|
|
23
24
|
import { resolveSource } from "../source-resolver.js";
|
|
@@ -66,11 +67,21 @@ export async function siteBuild(cwd, options) {
|
|
|
66
67
|
console.error(` Run \`dogsbay site init ${siteRoot}\` first.`);
|
|
67
68
|
process.exit(1);
|
|
68
69
|
}
|
|
69
|
-
// 5.
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
70
|
+
// 5. Default to publish mode (every declared source builds) so
|
|
71
|
+
// multi-locale / multi-version / multi-namespace sites Just Work
|
|
72
|
+
// when the writer runs `dogsbay site build`. Use --primary-only
|
|
73
|
+
// for fast-iteration CI jobs that don't need the full matrix
|
|
74
|
+
// (rare). The `--publish` flag is kept as a deprecated no-op for
|
|
75
|
+
// older scripts.
|
|
76
|
+
//
|
|
77
|
+
// Previous default was "primary-only" — silently dropped non-
|
|
78
|
+
// primary sources from production builds. With i18n and version
|
|
79
|
+
// axes, that meant declaring `locales:` resulted in only the
|
|
80
|
+
// English source shipping, surprising every writer who ran
|
|
81
|
+
// `dogsbay site build` after declaring fr. Inverted to match the
|
|
82
|
+
// strongest signal: declaring more sources means you want them
|
|
83
|
+
// all. See plans/beta-launch-followups.md.
|
|
84
|
+
const mode = options.primaryOnly ? "primary" : "all";
|
|
74
85
|
const originalSources = config.content.sources;
|
|
75
86
|
const totalSources = originalSources.length;
|
|
76
87
|
const { sources: activeSources, indices: activeIndices } = filterSourcesForMode(originalSources, mode);
|
|
@@ -156,12 +167,45 @@ export async function siteBuild(cwd, options) {
|
|
|
156
167
|
// generalize this to copy assets per-source.
|
|
157
168
|
const astroOpts = configToAstroOptions(config);
|
|
158
169
|
astroOpts.sourceDir = resolvedSources[0];
|
|
170
|
+
// siteRoot = repo root (where dogsbay.config.yml lives). Used by
|
|
171
|
+
// emitDeployArtifacts to drop GH Actions workflows at
|
|
172
|
+
// <siteRoot>/.github/ rather than inside the Astro subdir, where
|
|
173
|
+
// GitHub wouldn't find them.
|
|
174
|
+
astroOpts.projectDir = siteRoot;
|
|
159
175
|
// Refresh src/data/site.json from the current config — purely
|
|
160
176
|
// config-derived, so changes in dogsbay.config.yml propagate without
|
|
161
177
|
// re-running site init.
|
|
162
178
|
emitSiteConfig(outputDir, config.site.name, astroOpts);
|
|
163
|
-
const { generated, outputNav } = await emitAstroPages(pages, nav, outputDir, astroOpts);
|
|
179
|
+
const { generated, outputNav, generatedPaths } = await emitAstroPages(pages, nav, outputDir, astroOpts);
|
|
180
|
+
// Passthrough Astro pages — hand-authored .astro files that live
|
|
181
|
+
// alongside the markdown sources and are listed in nav.yml. Picked
|
|
182
|
+
// up by walking the source directories for *.astro and intersecting
|
|
183
|
+
// with the resolved nav hrefs. Collisions with generated pages are
|
|
184
|
+
// a build error (loud, not silent overwrite). See
|
|
185
|
+
// plans/passthrough-astro-pages.md.
|
|
186
|
+
const passthroughCopies = [];
|
|
187
|
+
for (const sourceDir of resolvedSources) {
|
|
188
|
+
const entries = collectPassthroughEntries(sourceDir, outputNav, {
|
|
189
|
+
basePath: normalizeBasePath(astroOpts.basePath),
|
|
190
|
+
});
|
|
191
|
+
for (const entry of entries) {
|
|
192
|
+
passthroughCopies.push({
|
|
193
|
+
source: entry.source,
|
|
194
|
+
sourceAbs: entry.sourceAbs,
|
|
195
|
+
outputRelPath: entry.outputRelPath,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (passthroughCopies.length > 0) {
|
|
200
|
+
const { copied } = emitPassthroughAstroPages(passthroughCopies, outputDir, generatedPaths);
|
|
201
|
+
console.log(` Copied ${copied} passthrough .astro page${copied === 1 ? "" : "s"}`);
|
|
202
|
+
}
|
|
164
203
|
emitConfigDerivedFiles(outputDir, astroOpts);
|
|
204
|
+
// Deploy artifacts — write-if-missing so adding `deploy: github-pages`
|
|
205
|
+
// to dogsbay.config.yml on an existing site materializes the workflow
|
|
206
|
+
// on the next build. Author edits survive (only fired when the file
|
|
207
|
+
// doesn't exist; --force regeneration goes through site init).
|
|
208
|
+
emitDeployArtifacts(outputDir, astroOpts);
|
|
165
209
|
emitAgentReadinessFiles(pages, outputNav, outputDir, config.site.name, astroOpts);
|
|
166
210
|
// switcherMap.json — per-page version-equivalent lookup. No-op
|
|
167
211
|
// when the version axis isn't active; cleans up a stale file
|
|
@@ -206,9 +250,18 @@ export async function siteBuild(cwd, options) {
|
|
|
206
250
|
labels: raw.labels,
|
|
207
251
|
};
|
|
208
252
|
}
|
|
253
|
+
// Filesystem layout uses dogsbay basePath; URL composition uses
|
|
254
|
+
// the combined prefix (urlBase + basePath) so chip / term hrefs
|
|
255
|
+
// resolve under subpath-mounted deploys. Passing combined as
|
|
256
|
+
// `basePath` previously pushed taxonomy route files into
|
|
257
|
+
// src/pages/<urlBase>/<basePath>/<indexPath>/, which then
|
|
258
|
+
// double-prefixed once Astro's `base` config applied at request
|
|
259
|
+
// time (same family as the section-llms.txt bug fixed in 47db909).
|
|
260
|
+
const taxoCombined = resolvePrefixes(config.site.url, config.site.basePath).combined;
|
|
209
261
|
const emitted = emitTaxonomyRoutes(pages, outputDir, taxonomyConfigs, {
|
|
210
262
|
section: config.content.section,
|
|
211
263
|
basePath: config.site.basePath,
|
|
264
|
+
urlPrefix: taxoCombined,
|
|
212
265
|
});
|
|
213
266
|
if (emitted.length > 0) {
|
|
214
267
|
console.log(` Emitted taxonomy routes: ${emitted.join(", ")}`);
|
|
@@ -231,16 +284,16 @@ export async function siteBuild(cwd, options) {
|
|
|
231
284
|
console.warn(` Copy markdown + View as markdown work without it.`);
|
|
232
285
|
}
|
|
233
286
|
console.log(pc.green(`\nDone! Built ${generated} pages into ${outputDir}`));
|
|
234
|
-
// When the writer
|
|
235
|
-
//
|
|
236
|
-
// for
|
|
237
|
-
//
|
|
287
|
+
// When the writer ran with --primary-only and we filtered the
|
|
288
|
+
// matrix, surface a hint that drops them back to the default.
|
|
289
|
+
// Skip for single-source / full-matrix builds — both already
|
|
290
|
+
// built everything.
|
|
238
291
|
if (mode === "primary" && primaryModeFiltersAnything(originalSources)) {
|
|
239
292
|
const skipped = totalSources - activeIndices.length;
|
|
240
293
|
console.log("");
|
|
241
|
-
console.log(pc.dim(
|
|
242
|
-
|
|
243
|
-
`
|
|
294
|
+
console.log(pc.dim(`--primary-only built ${activeIndices.length} of ${totalSources} ` +
|
|
295
|
+
`sources; ${skipped} non-primary skipped. ` +
|
|
296
|
+
`Drop --primary-only to build the full matrix.`));
|
|
244
297
|
}
|
|
245
298
|
console.log("");
|
|
246
299
|
console.log(pc.cyan("Next:"));
|
|
@@ -315,6 +368,9 @@ function mergeOverrides(config, opts) {
|
|
|
315
368
|
if (opts.deploy === "cloudflare-workers") {
|
|
316
369
|
next.deploy = { target: "cloudflare-workers" };
|
|
317
370
|
}
|
|
371
|
+
else if (opts.deploy === "github-pages") {
|
|
372
|
+
next.deploy = { target: "github-pages" };
|
|
373
|
+
}
|
|
318
374
|
// Analytics
|
|
319
375
|
if (opts.plausibleDomain) {
|
|
320
376
|
next.analytics = next.analytics ?? {};
|