Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions packages/cli/src/init.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from "node:fs";
import { createHash } from "node:crypto";
import { pathToFileURL } from "node:url";
import { dirname, join } from "node:path";
import { dirname, join, resolve } from "node:path";
import { detect } from "./detect.mjs";
import { render, hasManagedBlock, appendManagedBlock } from "./render.mjs";
import { ask, confirm, closePrompts } from "./prompts.mjs";
Expand Down Expand Up @@ -518,12 +518,26 @@ export async function init(flags, pkgRoot, version) {
lstatSync(agentsSkillsLink);
skip(".agents/skills exists — left untouched");
} catch {
mkdirSync(join(dir, ".agents"), { recursive: true });
try {
mkdirSync(join(dir, ".agents"), { recursive: true });
symlinkSync("../.claude/skills", agentsSkillsLink, "dir");
ok(".agents/skills → .claude/skills");
} catch {
warn(".agents/skills symlink could not be created (filesystem without symlink support) — skipped.");
} catch (error) {
// On Windows, plain symlinks need Developer Mode or elevation (EPERM).
// Directory junctions need neither — they just require an absolute
// target — so fall back to one instead of misreporting the cause.
if (process.platform === "win32" && (error.code === "EPERM" || error.code === "EINVAL")) {
try {
symlinkSync(resolve(dir, ".claude/skills"), agentsSkillsLink, "junction");
ok(".agents/skills → .claude/skills (junction — absolute; recreate after moving the repo)");
} catch {
warn(
".agents/skills link could not be created: Windows symlinks need Developer Mode or an elevated shell, and the junction fallback also failed — skipped.",
);
}
} else {
warn(".agents/skills symlink could not be created (filesystem without symlink support) — skipped.");
}
}
}

Expand Down
10 changes: 7 additions & 3 deletions packages/cli/test/init.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import { parse as parseYaml } from "yaml";

const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const cli = join(pkgRoot, "bin", "facility.mjs");
const biome = resolve(pkgRoot, "..", "..", "node_modules", ".bin", "biome");
// Biome's .bin shim is a POSIX script that spawnSync cannot execute on
// Windows (status null); its JS entry runs through node on every platform.
const biomeJs = resolve(pkgRoot, "..", "..", "node_modules", "@biomejs", "biome", "bin", "biome");
const biome = process.execPath;
const biomeArgs = (args) => [biomeJs, ...args];

function makeTargetRepo() {
const dir = mkdtempSync(join(tmpdir(), "facility-test-"));
Expand Down Expand Up @@ -445,12 +449,12 @@ test("init installs the method end to end", async (t) => {
2,
)}\n`,
);
const formatConfig = spawnSync(biome, ["format", "--write", "biome.json"], {
const formatConfig = spawnSync(biome, biomeArgs(["format", "--write", "biome.json"]), {
cwd: dir,
encoding: "utf8",
});
assert.equal(formatConfig.status, 0, formatConfig.stdout + formatConfig.stderr);
const strictBiome = spawnSync(biome, ["check", "."], { cwd: dir, encoding: "utf8" });
const strictBiome = spawnSync(biome, biomeArgs(["check", "."]), { cwd: dir, encoding: "utf8" });
assert.equal(strictBiome.status, 0, strictBiome.stdout + strictBiome.stderr);

const doctor = runCli(["doctor", `--dir=${dir}`], dir);
Expand Down
Loading