@aicopycoders/exodus 2026.7.200-beta.5 → 2026.7.200-beta.6
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/brand.js +2 -1
- package/dist/commands/doctor.js +22 -16
- package/dist/commands/init.js +3 -2
- package/dist/commands/meme.js +5 -4
- package/dist/commands/migrate.js +7 -6
- package/dist/lib/channel.d.ts +5 -0
- package/dist/lib/channel.js +15 -0
- package/dist/lib/scaffold.d.ts +5 -4
- package/dist/lib/scaffold.js +27 -6
- package/package.json +1 -1
package/dist/commands/brand.js
CHANGED
|
@@ -5,6 +5,7 @@ import { sniffImageType, BRAND_IMAGE_MAX_BYTES } from "../lib/image-sniff.js";
|
|
|
5
5
|
import { getActiveBrand, setActiveBrand, clearActiveBrand, findWorkspaceRoot, } from "../lib/state.js";
|
|
6
6
|
import { detectLayout, ensureBrandDir, resolveActiveBrand, brandStateDir, brandDirFor, } from "../lib/layout.js";
|
|
7
7
|
import { promptYesNo } from "../lib/prompts.js";
|
|
8
|
+
import { pkgRef } from "../lib/channel.js";
|
|
8
9
|
const AUTO_END_MARKER = "<!-- exodus:auto-section-end — manual brand notes below survive `brand use` refreshes -->";
|
|
9
10
|
export const helpText = `
|
|
10
11
|
exodus brand — list, switch, and inspect brands available to your CLI
|
|
@@ -180,7 +181,7 @@ async function runCreate(name) {
|
|
|
180
181
|
console.log(`✓ Wrote ${refresh.pathRel}.`);
|
|
181
182
|
}
|
|
182
183
|
console.log(`\nNext: set up the brand's primer to unlock the pipelines —\n` +
|
|
183
|
-
` say "exodus, set up my brand primer" (or run \`npx
|
|
184
|
+
` say "exodus, set up my brand primer" (or run \`npx ${pkgRef()} primer\`).`);
|
|
184
185
|
}
|
|
185
186
|
async function runDelete(slug, flags) {
|
|
186
187
|
if (!slug)
|
package/dist/commands/doctor.js
CHANGED
|
@@ -7,6 +7,7 @@ import { findWorkspaceRoot } from "../lib/state.js";
|
|
|
7
7
|
import { detectLayout, listBrandDirs, resolveActiveBrand, brandStateDir, } from "../lib/layout.js";
|
|
8
8
|
import { auth401Hint, isPlaceholderApiUrl } from "../lib/backend-hint.js";
|
|
9
9
|
import { compareVersions, readPackageVersion, } from "../lib/release.js";
|
|
10
|
+
import { channelOf, pkgRef, stampChannel } from "../lib/channel.js";
|
|
10
11
|
export const helpText = `
|
|
11
12
|
exodus doctor — Preflight checks for your local Claude Code setup
|
|
12
13
|
|
|
@@ -58,7 +59,7 @@ export function checkEnvFile() {
|
|
|
58
59
|
return {
|
|
59
60
|
ok: false,
|
|
60
61
|
label: ".env",
|
|
61
|
-
fix:
|
|
62
|
+
fix: `run \`npx ${pkgRef()} init\`, then paste your dashboard .env block (Settings → Claude Code → Copy .env block)`,
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
const content = fs.readFileSync(envPath, "utf8");
|
|
@@ -80,7 +81,7 @@ export function checkEnvFile() {
|
|
|
80
81
|
ok: false,
|
|
81
82
|
label: ".env",
|
|
82
83
|
detail: "missing EXODUS_API_URL or EXODUS_API_KEY",
|
|
83
|
-
fix:
|
|
84
|
+
fix: `run \`npx ${pkgRef()} init\`, then paste your dashboard .env block (Settings → Claude Code → Copy .env block)`,
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
export async function checkWhoami() {
|
|
@@ -124,7 +125,7 @@ export async function checkWhoami() {
|
|
|
124
125
|
todo: true,
|
|
125
126
|
label: "Brand primer",
|
|
126
127
|
detail: `${d.workspaceSlug} has no primer yet — set one up to unlock the pipelines`,
|
|
127
|
-
fix: `say "exodus, set up my brand primer" (or run \`npx
|
|
128
|
+
fix: `say "exodus, set up my brand primer" (or run \`npx ${pkgRef()} foundation\` from a source doc, or paste it at /settings?tab=brands&brand=${d.workspaceSlug})`,
|
|
128
129
|
};
|
|
129
130
|
}
|
|
130
131
|
return {
|
|
@@ -182,7 +183,7 @@ export async function checkActiveBrandMatch() {
|
|
|
182
183
|
ok: false,
|
|
183
184
|
label: "Active brand",
|
|
184
185
|
detail: `local active brand "${resolved.slug}" (${source}) is NOT in your accessible list — the server ignores it and every command silently targets the key's default brand. available: ${available}`,
|
|
185
|
-
fix: `run \`npx
|
|
186
|
+
fix: `run \`npx ${pkgRef()} brand use <slug>\` with one of your brands, or \`npx ${pkgRef()} brand clear\` to fall back to the key default`,
|
|
186
187
|
};
|
|
187
188
|
}
|
|
188
189
|
const FRESHNESS_SUBTREES = ["commands", "lib", "bin"];
|
|
@@ -251,7 +252,9 @@ export function checkExodusDistFreshness(pkgRootOverride) {
|
|
|
251
252
|
}
|
|
252
253
|
return { ok: true, label: "Exodus CLI build", detail: "dist up-to-date" };
|
|
253
254
|
}
|
|
254
|
-
|
|
255
|
+
function npmRegistryUrl(channel) {
|
|
256
|
+
return `https://registry.npmjs.org/@aicopycoders/exodus/${channel}`;
|
|
257
|
+
}
|
|
255
258
|
export async function checkVersionCurrency(pkgRootOverride, fetchImpl = fetch) {
|
|
256
259
|
let pkgRoot;
|
|
257
260
|
if (pkgRootOverride) {
|
|
@@ -265,28 +268,29 @@ export async function checkVersionCurrency(pkgRootOverride, fetchImpl = fetch) {
|
|
|
265
268
|
}
|
|
266
269
|
}
|
|
267
270
|
const local = readPackageVersion(pkgRoot);
|
|
271
|
+
const channel = channelOf(local);
|
|
268
272
|
try {
|
|
269
273
|
const ctrl = new AbortController();
|
|
270
274
|
const t = setTimeout(() => ctrl.abort(), 8_000);
|
|
271
275
|
let res;
|
|
272
276
|
try {
|
|
273
|
-
res = await fetchImpl(
|
|
277
|
+
res = await fetchImpl(npmRegistryUrl(channel), { signal: ctrl.signal });
|
|
274
278
|
}
|
|
275
279
|
finally {
|
|
276
280
|
clearTimeout(t);
|
|
277
281
|
}
|
|
278
282
|
if (!res.ok)
|
|
279
283
|
throw new Error(`HTTP ${res.status}`);
|
|
280
|
-
const
|
|
281
|
-
if (compareVersions(local,
|
|
284
|
+
const published = (await res.json()).version;
|
|
285
|
+
if (compareVersions(local, published) >= 0) {
|
|
282
286
|
return { ok: true, label: "Exodus version", detail: `${local} — up to date` };
|
|
283
287
|
}
|
|
284
288
|
return {
|
|
285
289
|
ok: true,
|
|
286
290
|
warn: true,
|
|
287
291
|
label: "Exodus version",
|
|
288
|
-
detail: `${local} installed, ${
|
|
289
|
-
fix:
|
|
292
|
+
detail: `${local} installed, ${published} available`,
|
|
293
|
+
fix: `run \`npx ${pkgRef(channel)} init\` to update (or \`npm install -g ${pkgRef(channel)}\`)`,
|
|
290
294
|
};
|
|
291
295
|
}
|
|
292
296
|
catch (err) {
|
|
@@ -326,7 +330,7 @@ export async function checkApiAndDrive() {
|
|
|
326
330
|
ok: false,
|
|
327
331
|
label: "Dashboard API key",
|
|
328
332
|
detail: `HTTP ${res.status}`,
|
|
329
|
-
fix:
|
|
333
|
+
fix: `regenerate at Settings → API Keys, then paste the new .env block (or re-run \`npx ${pkgRef()} init\`)`,
|
|
330
334
|
},
|
|
331
335
|
{
|
|
332
336
|
ok: false,
|
|
@@ -445,7 +449,9 @@ export async function checkDashboardAuth() {
|
|
|
445
449
|
ok: false,
|
|
446
450
|
label: "Dashboard auth (dashboard-routed pipelines)",
|
|
447
451
|
detail: res.data?.error ?? "401 from /api/doctor/dashboard-ping",
|
|
448
|
-
fix: hint
|
|
452
|
+
fix: hint
|
|
453
|
+
? stampChannel(hint)
|
|
454
|
+
: `run \`npx ${pkgRef()} init\` to update the CLI — older releases skipped the Bearer header on dashboard routes`,
|
|
449
455
|
};
|
|
450
456
|
}
|
|
451
457
|
return {
|
|
@@ -476,7 +482,7 @@ export function checkLayout() {
|
|
|
476
482
|
return {
|
|
477
483
|
ok: true,
|
|
478
484
|
label: "Layout",
|
|
479
|
-
detail:
|
|
485
|
+
detail: `single-brand (legacy) — run \`npx ${pkgRef()} migrate\` to switch to the multi-brand layout`,
|
|
480
486
|
};
|
|
481
487
|
}
|
|
482
488
|
const brands = listBrandDirs(root);
|
|
@@ -490,7 +496,7 @@ export function checkLayout() {
|
|
|
490
496
|
warn: true,
|
|
491
497
|
label: "Layout",
|
|
492
498
|
detail: `multi-brand — no brand folders yet; ${active}`,
|
|
493
|
-
fix:
|
|
499
|
+
fix: `run \`npx ${pkgRef()} init\` (or \`npx ${pkgRef()} brand use <slug>\`) to create your brand folders`,
|
|
494
500
|
};
|
|
495
501
|
}
|
|
496
502
|
const missingProfiles = brands.filter((b) => !fs.existsSync(path.join(b.dir, "state", "brand-profile.md")));
|
|
@@ -500,7 +506,7 @@ export function checkLayout() {
|
|
|
500
506
|
warn: true,
|
|
501
507
|
label: "Layout",
|
|
502
508
|
detail: `multi-brand, ${brands.length} brand folder(s); ${active}; missing brand profile in: ${missingProfiles.map((b) => b.slug).join(", ")}`,
|
|
503
|
-
fix:
|
|
509
|
+
fix: `run \`npx ${pkgRef()} init\` to refresh every brand folder's profile`,
|
|
504
510
|
};
|
|
505
511
|
}
|
|
506
512
|
return {
|
|
@@ -518,7 +524,7 @@ export function checkBrandProfileGenesisDepth() {
|
|
|
518
524
|
ok: false,
|
|
519
525
|
label: "brand-profile",
|
|
520
526
|
detail: `${rel} is missing`,
|
|
521
|
-
fix:
|
|
527
|
+
fix: `run \`npx ${pkgRef()} brand use <slug>\` to generate it`,
|
|
522
528
|
};
|
|
523
529
|
}
|
|
524
530
|
const contents = fs.readFileSync(filePath, "utf-8");
|
package/dist/commands/init.js
CHANGED
|
@@ -3,12 +3,13 @@ import path from "node:path";
|
|
|
3
3
|
import { writeEnvScaffold, ensureGitignore, writeSkills, writeReferences, writeDocs, ensureBaseDirs, } from "../lib/scaffold.js";
|
|
4
4
|
import { loadWorkspaceEnv } from "../lib/load-env.js";
|
|
5
5
|
import { ensureBrandDir } from "../lib/layout.js";
|
|
6
|
+
import { pkgRef } from "../lib/channel.js";
|
|
6
7
|
export const helpText = `
|
|
7
8
|
exodus init — Set up (or refresh) an Exodus workspace in the current folder
|
|
8
9
|
|
|
9
10
|
Usage:
|
|
10
|
-
npx
|
|
11
|
-
npx
|
|
11
|
+
npx ${pkgRef()} init Scaffold the workspace + install skills
|
|
12
|
+
npx ${pkgRef()} init --root <dir> Target a specific folder
|
|
12
13
|
|
|
13
14
|
Run this in a fresh, empty folder. It creates the workspace layout, writes the
|
|
14
15
|
Exodus + Genesis skills into .claude/skills/, scaffolds a comment-only .env, and
|
package/dist/commands/meme.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { apiPostDashboard, getDashboardUrl } from "../lib/client.js";
|
|
2
2
|
import { formatError } from "../lib/format.js";
|
|
3
|
+
import { pkgRef } from "../lib/channel.js";
|
|
3
4
|
import { readFileSync } from "node:fs";
|
|
4
5
|
export const helpText = `
|
|
5
6
|
exodus meme — Meme generator: brief → recommended formats → one batched run
|
|
@@ -26,7 +27,7 @@ Flow:
|
|
|
26
27
|
|
|
27
28
|
Keys (strict BYOK — checked server-side before anything starts):
|
|
28
29
|
classic (layer 1) memes need your Imgflip login; AI (layer 2/3) memes need
|
|
29
|
-
your Kie.ai key; captions always need your LLM key. Run \`npx
|
|
30
|
+
your Kie.ai key; captions always need your LLM key. Run \`npx ${pkgRef()} doctor\`.
|
|
30
31
|
|
|
31
32
|
Examples:
|
|
32
33
|
exodus meme recommend --brief "grounding sheets reduce inflammation"
|
|
@@ -114,7 +115,7 @@ async function runRecommend(flags) {
|
|
|
114
115
|
if (!res.ok) {
|
|
115
116
|
console.error(formatError(res));
|
|
116
117
|
if (res.status === 400) {
|
|
117
|
-
console.error(
|
|
118
|
+
console.error(`Hint: run \`npx ${pkgRef()} doctor\` to check your keys.`);
|
|
118
119
|
}
|
|
119
120
|
process.exit(1);
|
|
120
121
|
return;
|
|
@@ -171,7 +172,7 @@ async function runRun(flags) {
|
|
|
171
172
|
if (!res.ok) {
|
|
172
173
|
console.error(formatError(res));
|
|
173
174
|
if (res.status === 400) {
|
|
174
|
-
console.error(
|
|
175
|
+
console.error(`Hint: run \`npx ${pkgRef()} doctor\` to check your keys.`);
|
|
175
176
|
}
|
|
176
177
|
process.exit(1);
|
|
177
178
|
return;
|
|
@@ -233,7 +234,7 @@ async function runRegenerate(flags) {
|
|
|
233
234
|
if (!res.ok) {
|
|
234
235
|
console.error(formatError(res));
|
|
235
236
|
if (res.status === 400) {
|
|
236
|
-
console.error(
|
|
237
|
+
console.error(`Hint: run \`npx ${pkgRef()} doctor\` to check your keys.`);
|
|
237
238
|
}
|
|
238
239
|
process.exit(1);
|
|
239
240
|
return;
|
package/dist/commands/migrate.js
CHANGED
|
@@ -4,6 +4,7 @@ import { execSync } from "node:child_process";
|
|
|
4
4
|
import { apiGet, apiGetDashboard } from "../lib/client.js";
|
|
5
5
|
import { getActiveBrand, setLayoutVersion, findWorkspaceRoot } from "../lib/state.js";
|
|
6
6
|
import { detectLayout, brandDirFor, ensureBrandDir, BRAND_MARKER_FILE, } from "../lib/layout.js";
|
|
7
|
+
import { pkgRef } from "../lib/channel.js";
|
|
7
8
|
export const helpText = `
|
|
8
9
|
exodus migrate — convert a single-brand install to the multi-brand layout
|
|
9
10
|
|
|
@@ -15,7 +16,7 @@ What it does (one-time, opt-in — updates never force this):
|
|
|
15
16
|
falling back to the key's bound brand).
|
|
16
17
|
2. Creates a subfolder for it and MOVES your brand-specific files in:
|
|
17
18
|
state/ and output/ → <brand>/state/ and <brand>/output/
|
|
18
|
-
3. Marks the install as multi-brand. From then on, \`npx
|
|
19
|
+
3. Marks the install as multi-brand. From then on, \`npx ${pkgRef()} init\`
|
|
19
20
|
creates a subfolder for every brand you own, and running a command
|
|
20
21
|
from inside a brand's folder targets that brand automatically.
|
|
21
22
|
|
|
@@ -94,7 +95,7 @@ async function resolveBrand() {
|
|
|
94
95
|
}
|
|
95
96
|
if (!slug) {
|
|
96
97
|
fail("could not determine which brand this install belongs to.\n" +
|
|
97
|
-
|
|
98
|
+
` Run \`npx ${pkgRef()} brand use <slug>\` first (or check your network/.env), then re-run \`npx ${pkgRef()} migrate\`.`);
|
|
98
99
|
}
|
|
99
100
|
if (!name) {
|
|
100
101
|
try {
|
|
@@ -115,7 +116,7 @@ export async function run(flags) {
|
|
|
115
116
|
const root = findWorkspaceRoot();
|
|
116
117
|
if (detectLayout(root) === "v2") {
|
|
117
118
|
console.log("✓ Already on the multi-brand layout — nothing to migrate.");
|
|
118
|
-
console.log(
|
|
119
|
+
console.log(` Run \`npx ${pkgRef()} init\` to sync brand folders.`);
|
|
119
120
|
return;
|
|
120
121
|
}
|
|
121
122
|
const brand = await resolveBrand();
|
|
@@ -128,7 +129,7 @@ export async function run(flags) {
|
|
|
128
129
|
if (conflicts.length > 0) {
|
|
129
130
|
fail(`"${brandDirRel}/" already contains different versions of:\n` +
|
|
130
131
|
conflicts.map((f) => ` ${f}`).join("\n") +
|
|
131
|
-
|
|
132
|
+
`\n Reconcile or remove them, then re-run \`npx ${pkgRef()} migrate\`. Nothing was changed.`);
|
|
132
133
|
}
|
|
133
134
|
console.log(`Migrating this install to the multi-brand layout...`);
|
|
134
135
|
console.log(` brand: ${brand.slug} (${brand.name})`);
|
|
@@ -151,8 +152,8 @@ export async function run(flags) {
|
|
|
151
152
|
Shared files (.env, exodus/, .claude/skills/, references/) did not move.
|
|
152
153
|
|
|
153
154
|
Next steps:
|
|
154
|
-
• Run \`npx
|
|
155
|
+
• Run \`npx ${pkgRef()} init\` to pull folders for your other brands.
|
|
155
156
|
• Run commands from inside ${brandDirRel}/ to target it automatically,
|
|
156
|
-
or keep using \`npx
|
|
157
|
+
or keep using \`npx ${pkgRef()} brand use <slug>\` from anywhere.
|
|
157
158
|
`.trimEnd());
|
|
158
159
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type Channel = "beta" | "latest";
|
|
2
|
+
export declare function channelOf(version: string): Channel;
|
|
3
|
+
export declare function getChannel(): Channel;
|
|
4
|
+
export declare function pkgRef(channel?: Channel): string;
|
|
5
|
+
export declare function stampChannel(content: string, channel?: Channel): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getVersion } from "./version.js";
|
|
2
|
+
export function channelOf(version) {
|
|
3
|
+
return version.includes("-beta") ? "beta" : "latest";
|
|
4
|
+
}
|
|
5
|
+
export function getChannel() {
|
|
6
|
+
return channelOf(getVersion());
|
|
7
|
+
}
|
|
8
|
+
export function pkgRef(channel = getChannel()) {
|
|
9
|
+
return `@aicopycoders/exodus@${channel}`;
|
|
10
|
+
}
|
|
11
|
+
export function stampChannel(content, channel = getChannel()) {
|
|
12
|
+
if (channel !== "beta")
|
|
13
|
+
return content;
|
|
14
|
+
return content.replace(/npx @aicopycoders\/exodus(@[A-Za-z0-9.-]+)?/g, `npx ${pkgRef(channel)}`);
|
|
15
|
+
}
|
package/dist/lib/scaffold.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { type Channel } from "./channel.js";
|
|
1
2
|
export declare const ENV_SCAFFOLD = "# Exodus config \u2014 paste your dashboard .env block below this line.\n# Get it from your Exodus dashboard -> Settings -> Claude Code -> \"Copy .env block\".\n# ONE key covers every brand you own; switch brands with brand subfolders or\n# `npx @aicopycoders/exodus brand use <slug>`, never by editing this file.\n";
|
|
2
|
-
export declare function writeEnvScaffold(root: string): {
|
|
3
|
+
export declare function writeEnvScaffold(root: string, channel?: Channel): {
|
|
3
4
|
created: boolean;
|
|
4
5
|
};
|
|
5
6
|
export declare function ensureGitignore(root: string): void;
|
|
6
|
-
export declare function writeSkills(root: string, srcOverride?: string): string[];
|
|
7
|
-
export declare function writeReferences(root: string, srcOverride?: string): void;
|
|
8
|
-
export declare function writeDocs(root: string, srcOverride?: string): string[];
|
|
7
|
+
export declare function writeSkills(root: string, srcOverride?: string, channel?: Channel): string[];
|
|
8
|
+
export declare function writeReferences(root: string, srcOverride?: string, channel?: Channel): void;
|
|
9
|
+
export declare function writeDocs(root: string, srcOverride?: string, channel?: Channel): string[];
|
|
9
10
|
export declare function ensureBaseDirs(root: string): void;
|
package/dist/lib/scaffold.js
CHANGED
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { skillsDir, referencesDir, docsDir } from "./assets.js";
|
|
4
|
+
import { getChannel, stampChannel } from "./channel.js";
|
|
4
5
|
export const ENV_SCAFFOLD = `# Exodus config — paste your dashboard .env block below this line.
|
|
5
6
|
# Get it from your Exodus dashboard -> Settings -> Claude Code -> "Copy .env block".
|
|
6
7
|
# ONE key covers every brand you own; switch brands with brand subfolders or
|
|
7
8
|
# \`npx @aicopycoders/exodus brand use <slug>\`, never by editing this file.
|
|
8
9
|
`;
|
|
9
|
-
export function writeEnvScaffold(root) {
|
|
10
|
+
export function writeEnvScaffold(root, channel = getChannel()) {
|
|
10
11
|
const envPath = path.join(root, ".env");
|
|
11
12
|
if (fs.existsSync(envPath))
|
|
12
13
|
return { created: false };
|
|
13
|
-
fs.writeFileSync(envPath, ENV_SCAFFOLD);
|
|
14
|
+
fs.writeFileSync(envPath, stampChannel(ENV_SCAFFOLD, channel));
|
|
14
15
|
return { created: true };
|
|
15
16
|
}
|
|
17
|
+
function stampTree(dir, channel) {
|
|
18
|
+
if (channel !== "beta")
|
|
19
|
+
return;
|
|
20
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
21
|
+
const p = path.join(dir, entry.name);
|
|
22
|
+
if (entry.isDirectory()) {
|
|
23
|
+
stampTree(p, channel);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (!entry.isFile())
|
|
27
|
+
continue;
|
|
28
|
+
const raw = fs.readFileSync(p, "utf8");
|
|
29
|
+
const stamped = stampChannel(raw, channel);
|
|
30
|
+
if (stamped !== raw)
|
|
31
|
+
fs.writeFileSync(p, stamped);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
16
34
|
export function ensureGitignore(root) {
|
|
17
35
|
const giPath = path.join(root, ".gitignore");
|
|
18
36
|
const lines = fs.existsSync(giPath)
|
|
@@ -22,7 +40,7 @@ export function ensureGitignore(root) {
|
|
|
22
40
|
lines.push(".env");
|
|
23
41
|
fs.writeFileSync(giPath, lines.join("\n").replace(/\n+$/, "") + "\n");
|
|
24
42
|
}
|
|
25
|
-
export function writeSkills(root, srcOverride) {
|
|
43
|
+
export function writeSkills(root, srcOverride, channel = getChannel()) {
|
|
26
44
|
const src = skillsDir(srcOverride);
|
|
27
45
|
const dest = path.join(root, ".claude", "skills");
|
|
28
46
|
fs.mkdirSync(dest, { recursive: true });
|
|
@@ -33,11 +51,12 @@ export function writeSkills(root, srcOverride) {
|
|
|
33
51
|
const target = path.join(dest, entry.name);
|
|
34
52
|
fs.rmSync(target, { recursive: true, force: true });
|
|
35
53
|
fs.cpSync(path.join(src, entry.name), target, { recursive: true });
|
|
54
|
+
stampTree(target, channel);
|
|
36
55
|
names.push(entry.name);
|
|
37
56
|
}
|
|
38
57
|
return names;
|
|
39
58
|
}
|
|
40
|
-
export function writeReferences(root, srcOverride) {
|
|
59
|
+
export function writeReferences(root, srcOverride, channel = getChannel()) {
|
|
41
60
|
let src;
|
|
42
61
|
try {
|
|
43
62
|
src = referencesDir(srcOverride);
|
|
@@ -50,8 +69,9 @@ export function writeReferences(root, srcOverride) {
|
|
|
50
69
|
const dest = path.join(root, "references");
|
|
51
70
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
52
71
|
fs.cpSync(src, dest, { recursive: true });
|
|
72
|
+
stampTree(dest, channel);
|
|
53
73
|
}
|
|
54
|
-
export function writeDocs(root, srcOverride) {
|
|
74
|
+
export function writeDocs(root, srcOverride, channel = getChannel()) {
|
|
55
75
|
let src;
|
|
56
76
|
try {
|
|
57
77
|
src = docsDir(srcOverride);
|
|
@@ -65,7 +85,8 @@ export function writeDocs(root, srcOverride) {
|
|
|
65
85
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
66
86
|
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
67
87
|
continue;
|
|
68
|
-
fs.
|
|
88
|
+
const content = fs.readFileSync(path.join(src, entry.name), "utf8");
|
|
89
|
+
fs.writeFileSync(path.join(root, entry.name), stampChannel(content, channel));
|
|
69
90
|
names.push(entry.name);
|
|
70
91
|
}
|
|
71
92
|
return names.sort();
|