agentln 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 abaktiar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # agentln
2
+
3
+ Cross-platform CLI that keeps `CLAUDE.md` and `AGENTS.md` in sync across an
4
+ entire repository by managing **relative symlinks** between them.
5
+
6
+ You make **one decision at the repository root** — which filename is the source
7
+ of truth — and `agentln` applies that decision recursively to every directory
8
+ in the repo.
9
+
10
+ - Works on macOS, Linux, and Windows (PowerShell, Git Bash, WSL).
11
+ - Uses Node's native `fs` symlink APIs. Never shells out to `ln`, `bash`, or
12
+ PowerShell.
13
+ - Designed for monorepos: Nx, Turborepo, pnpm workspaces, and nested apps.
14
+
15
+ ## Install / Run
16
+
17
+ ```bash
18
+ # One-shot
19
+ npx agentln
20
+ pnpm dlx agentln
21
+
22
+ # Or install globally
23
+ npm i -g agentln
24
+ agentln
25
+ ```
26
+
27
+ Requires Node.js 18+.
28
+
29
+ ## What it does
30
+
31
+ At the repository root, `agentln` looks for `CLAUDE.md` and `AGENTS.md` and
32
+ asks you a single question based on what it finds:
33
+
34
+ | Root state | Question |
35
+ | --------------------------- | ------------------------------------------------------------------------------ |
36
+ | Only `CLAUDE.md` | Use `CLAUDE.md` as the source of truth across the repository? |
37
+ | Only `AGENTS.md` | Use `AGENTS.md` as the source of truth across the repository? |
38
+ | Both exist | Which one should be the source of truth? `CLAUDE.md` / `AGENTS.md` / Cancel. |
39
+ | Neither exists | Which file should become the repository standard? `CLAUDE.md` / `AGENTS.md`. |
40
+
41
+ Once you pick a source of truth (call it `SRC`), every directory in the repo
42
+ that contains either file is reconciled to the same convention:
43
+
44
+ | Directory contents | Result |
45
+ | ---------------------- | --------------------------------------------------------------------- |
46
+ | `SRC` only | Create `OTHER` as a symlink → `SRC`. |
47
+ | `OTHER` only | Rename `OTHER` → `SRC` (preserve content), then link `OTHER` → `SRC`. |
48
+ | Both exist | Preserve `SRC`. Replace `OTHER` with a symlink → `SRC`. |
49
+ | Neither | Do nothing. |
50
+
51
+ `agentln` is idempotent. Running it twice in a row will say "already in sync."
52
+
53
+ ### Symlink format
54
+
55
+ Symlinks are always **relative** (`AGENTS.md → CLAUDE.md`), never absolute.
56
+ This keeps the repo portable across machines and clones.
57
+
58
+ ### Ignored directories
59
+
60
+ The scanner skips common build / vendor folders by default:
61
+
62
+ `node_modules`, `.git`, `dist`, `build`, `.next`, `coverage`, `vendor`,
63
+ `.turbo`, `.cache`, `.parcel-cache`, `.svelte-kit`, `.nuxt`, `.output`,
64
+ `.vercel`, `.idea`, `.vscode`, and any other dotfile-prefixed directory.
65
+
66
+ ## CLI flags
67
+
68
+ ```text
69
+ agentln [options]
70
+
71
+ --root <path> Repository root (default: current working directory).
72
+ --source <name> Use CLAUDE.md or AGENTS.md as source of truth (skips prompt).
73
+ -y, --yes Non-interactive mode. Accepts defaults for every prompt.
74
+ --dry-run Show planned changes without writing anything.
75
+ --force Overwrite divergent regular files when replacing with a symlink.
76
+ --copy-fallback On Windows, copy the file instead of failing when symlink
77
+ creation is denied.
78
+ --no-copy-fallback Disable the copy fallback (default).
79
+ --verbose Print debug-level information.
80
+ -h, --help Show help.
81
+ -v, --version Show version.
82
+ ```
83
+
84
+ ### Examples
85
+
86
+ ```bash
87
+ # Preview what would happen
88
+ npx agentln --dry-run
89
+
90
+ # Non-interactive, default to CLAUDE.md when both exist
91
+ npx agentln --yes --source CLAUDE.md
92
+
93
+ # Target a different repo
94
+ npx agentln --root ~/code/my-monorepo
95
+
96
+ # Replace existing AGENTS.md regular files (not just symlinks)
97
+ npx agentln --force
98
+ ```
99
+
100
+ ## Windows notes
101
+
102
+ Creating symbolic links on Windows requires either:
103
+
104
+ - **Developer Mode** enabled (Settings → Privacy & security → For developers), or
105
+ - an elevated shell, or
106
+ - the `SeCreateSymbolicLinkPrivilege` granted to your user.
107
+
108
+ If `agentln` cannot create a symlink, you have two options:
109
+
110
+ 1. Re-run after enabling one of the above (recommended — symlinks keep both
111
+ filenames in sync automatically).
112
+ 2. Pass `--copy-fallback` to write a regular file copy instead. The two files
113
+ will then diverge on subsequent edits until you reconcile them.
114
+
115
+ The fallback is **off by default** so you never silently lose the "single
116
+ source of truth" guarantee.
117
+
118
+ ## Use cases
119
+
120
+ - Tools like Claude Code expect `CLAUDE.md`; other tools expect `AGENTS.md`.
121
+ Symlink them so both worlds read the same file.
122
+ - Monorepos with multiple workspaces, each having their own per-package
123
+ instructions: `agentln` reconciles every workspace in one pass.
124
+
125
+ ## Publishing
126
+
127
+ ```bash
128
+ npm run build
129
+ npm publish --access public
130
+ ```
131
+
132
+ The `prepublishOnly` script rebuilds `dist/` and ships only `dist/` plus the
133
+ README and license.
134
+
135
+ ## License
136
+
137
+ MIT
package/dist/args.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { type CliOptions } from "./types.js";
2
+ export declare function parseArgs(argv: string[]): CliOptions;
3
+ export declare function helpText(): string;
4
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,UAAU,EAEhB,MAAM,YAAY,CAAC;AAqBpB,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAoEpD;AAsBD,wBAAgB,QAAQ,IAAI,MAAM,CAEjC"}
package/dist/args.js ADDED
@@ -0,0 +1,110 @@
1
+ import path from "node:path";
2
+ import { AGENTS_FILE, CLAUDE_FILE, } from "./types.js";
3
+ const HELP_TEXT = `agentln — manage CLAUDE.md ⇄ AGENTS.md symlinks across a repo
4
+
5
+ Usage:
6
+ npx agentln [options]
7
+ pnpm dlx agentln [options]
8
+
9
+ Options:
10
+ --root <path> Repository root (default: current working directory).
11
+ --source <name> Use CLAUDE.md or AGENTS.md as source of truth (skips prompt).
12
+ -y, --yes Non-interactive mode (assume defaults for every prompt).
13
+ --dry-run Show planned changes without writing anything.
14
+ --force Overwrite divergent regular files when replacing with a symlink.
15
+ --copy-fallback On Windows, fall back to a file copy if symlink creation is denied.
16
+ --no-copy-fallback Disable the copy fallback (default).
17
+ --verbose Print debug-level information.
18
+ -h, --help Show this help.
19
+ -v, --version Show version.
20
+ `;
21
+ export function parseArgs(argv) {
22
+ const opts = {
23
+ root: process.cwd(),
24
+ yes: false,
25
+ dryRun: false,
26
+ force: false,
27
+ verbose: false,
28
+ help: false,
29
+ version: false,
30
+ copyFallback: false,
31
+ noCopyFallback: false,
32
+ };
33
+ for (let i = 0; i < argv.length; i++) {
34
+ const arg = argv[i];
35
+ switch (arg) {
36
+ case "-h":
37
+ case "--help":
38
+ opts.help = true;
39
+ break;
40
+ case "-v":
41
+ case "--version":
42
+ opts.version = true;
43
+ break;
44
+ case "-y":
45
+ case "--yes":
46
+ opts.yes = true;
47
+ break;
48
+ case "--dry-run":
49
+ opts.dryRun = true;
50
+ break;
51
+ case "--force":
52
+ opts.force = true;
53
+ break;
54
+ case "--verbose":
55
+ opts.verbose = true;
56
+ break;
57
+ case "--copy-fallback":
58
+ opts.copyFallback = true;
59
+ break;
60
+ case "--no-copy-fallback":
61
+ opts.noCopyFallback = true;
62
+ opts.copyFallback = false;
63
+ break;
64
+ case "--root": {
65
+ const next = argv[++i];
66
+ if (!next)
67
+ throw new Error("--root requires a path argument");
68
+ opts.root = path.resolve(next);
69
+ break;
70
+ }
71
+ case "--source": {
72
+ const next = argv[++i];
73
+ opts.source = parseSource(next);
74
+ break;
75
+ }
76
+ default: {
77
+ if (arg.startsWith("--root=")) {
78
+ opts.root = path.resolve(arg.slice("--root=".length));
79
+ }
80
+ else if (arg.startsWith("--source=")) {
81
+ opts.source = parseSource(arg.slice("--source=".length));
82
+ }
83
+ else {
84
+ throw new Error(`Unknown argument: ${arg}`);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ return opts;
90
+ }
91
+ function parseSource(value) {
92
+ if (!value)
93
+ throw new Error("--source requires a value (CLAUDE.md or AGENTS.md)");
94
+ const normalised = value.trim();
95
+ if (normalised === CLAUDE_FILE ||
96
+ normalised.toLowerCase() === "claude" ||
97
+ normalised.toLowerCase() === "claude.md") {
98
+ return CLAUDE_FILE;
99
+ }
100
+ if (normalised === AGENTS_FILE ||
101
+ normalised.toLowerCase() === "agents" ||
102
+ normalised.toLowerCase() === "agents.md") {
103
+ return AGENTS_FILE;
104
+ }
105
+ throw new Error(`Invalid --source value: ${value}. Expected CLAUDE.md or AGENTS.md.`);
106
+ }
107
+ export function helpText() {
108
+ return HELP_TEXT;
109
+ }
110
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { run } from "./cli.js";
3
+ run().then((code) => process.exit(code), (err) => {
4
+ process.stderr.write(`agentln: unexpected error: ${err.stack ?? String(err)}\n`);
5
+ process.exit(1);
6
+ });
7
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,GAAG,EAAE,CAAC,IAAI,CACR,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5B,CAAC,GAAG,EAAE,EAAE;IACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA+B,GAAa,CAAC,KAAK,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CACF,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function run(argv?: string[]): Promise<number>;
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA0BA,wBAAsB,GAAG,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAuIjF"}
package/dist/cli.js ADDED
@@ -0,0 +1,130 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import pc from "picocolors";
5
+ import { helpText, parseArgs } from "./args.js";
6
+ import { executeAction, isWindowsHost, platformLabel } from "./executor.js";
7
+ import { createLogger } from "./logger.js";
8
+ import { buildPlan, describeAction } from "./planner.js";
9
+ import { classifyRoot, decideRoot } from "./prompts.js";
10
+ import { describeEntry, scanRepository } from "./scanner.js";
11
+ async function readPackageVersion() {
12
+ try {
13
+ const here = fileURLToPath(new URL(".", import.meta.url));
14
+ const pkgPath = path.join(here, "..", "package.json");
15
+ const raw = await fs.readFile(pkgPath, "utf8");
16
+ const pkg = JSON.parse(raw);
17
+ return pkg.version ?? "0.0.0";
18
+ }
19
+ catch {
20
+ return "0.0.0";
21
+ }
22
+ }
23
+ export async function run(argv = process.argv.slice(2)) {
24
+ let options;
25
+ try {
26
+ options = parseArgs(argv);
27
+ }
28
+ catch (err) {
29
+ process.stderr.write(`${pc.red("✗")} ${err.message}\n`);
30
+ process.stderr.write(helpText());
31
+ return 2;
32
+ }
33
+ if (options.help) {
34
+ process.stdout.write(helpText());
35
+ return 0;
36
+ }
37
+ if (options.version) {
38
+ process.stdout.write(`${await readPackageVersion()}\n`);
39
+ return 0;
40
+ }
41
+ const logger = createLogger(options.verbose);
42
+ const root = path.resolve(options.root);
43
+ logger.step("agentln", `${root}`);
44
+ logger.debug(`Platform: ${platformLabel()}`);
45
+ // Verify the root directory exists before doing anything else.
46
+ try {
47
+ const stat = await fs.stat(root);
48
+ if (!stat.isDirectory()) {
49
+ logger.error(`${root} is not a directory.`);
50
+ return 1;
51
+ }
52
+ }
53
+ catch {
54
+ logger.error(`Cannot read ${root}. Does the path exist?`);
55
+ return 1;
56
+ }
57
+ // First pass: just read the root state so we can ask the right question.
58
+ // We do this with an arbitrary source (CLAUDE) — the answer doesn't depend
59
+ // on which file the user later picks, only on which files are present.
60
+ const initialScan = await scanRepository(root, "CLAUDE.md");
61
+ const rootState = initialScan.find((d) => d.dir === root);
62
+ if (!rootState) {
63
+ logger.error(`Failed to scan repository root at ${root}.`);
64
+ return 1;
65
+ }
66
+ logger.debug(`Root CLAUDE.md: ${describeEntry(rootState.claude)}`);
67
+ logger.debug(`Root AGENTS.md: ${describeEntry(rootState.agents)}`);
68
+ const rootCase = classifyRoot(rootState);
69
+ logger.debug(`Root case: ${rootCase}`);
70
+ const decision = await decideRoot(rootState, {
71
+ yes: options.yes,
72
+ sourceFlag: options.source,
73
+ });
74
+ logger.info(`Source of truth: ${pc.bold(decision.source)}${decision.bootstrapRoot ? pc.dim(" (will be created at root)") : ""}`);
75
+ // Re-scan now that we know the actual source, so entry classification can
76
+ // judge whether existing symlinks point at the right sibling.
77
+ const dirs = await scanRepository(root, decision.source);
78
+ logger.debug(`Scanned ${dirs.length} relevant director${dirs.length === 1 ? "y" : "ies"}.`);
79
+ const plan = buildPlan({
80
+ root,
81
+ source: decision.source,
82
+ dirs,
83
+ bootstrapRoot: decision.bootstrapRoot,
84
+ force: options.force,
85
+ });
86
+ if (plan.actions.length === 0) {
87
+ logger.success(`Nothing to do. ${plan.cleanCount} director${plan.cleanCount === 1 ? "y is" : "ies are"} already in sync.`);
88
+ return 0;
89
+ }
90
+ logger.step(options.dryRun ? "Planned changes (dry run)" : "Planned changes", `${plan.actions.length} action${plan.actions.length === 1 ? "" : "s"}`);
91
+ for (const action of plan.actions) {
92
+ logger.raw(` ${pc.dim("•")} ${describeAction(action)}`);
93
+ }
94
+ if (options.dryRun) {
95
+ logger.info("Dry run complete. No changes were written.");
96
+ return 0;
97
+ }
98
+ if (isWindowsHost() && !options.copyFallback) {
99
+ logger.debug("Tip: pass --copy-fallback to write a regular file copy if symlink creation is denied on Windows.");
100
+ }
101
+ let failures = 0;
102
+ let copyFallbacks = 0;
103
+ for (const action of plan.actions) {
104
+ const result = await executeAction(action, {
105
+ dryRun: false,
106
+ force: options.force,
107
+ copyFallback: options.copyFallback,
108
+ });
109
+ if (result.fellBackToCopy)
110
+ copyFallbacks++;
111
+ if (!result.ok) {
112
+ failures++;
113
+ logger.error(`${describeAction(action)} — ${result.message}`);
114
+ }
115
+ else {
116
+ logger.success(`${describeAction(action)} — ${result.message}`);
117
+ }
118
+ }
119
+ if (copyFallbacks > 0) {
120
+ logger.warn(`${copyFallbacks} file${copyFallbacks === 1 ? "" : "s"} written as a copy instead of a symlink. ` +
121
+ "Edits to one file will not propagate to the other until real symlinks are restored.");
122
+ }
123
+ if (failures > 0) {
124
+ logger.error(`${failures} action${failures === 1 ? "" : "s"} failed.`);
125
+ return 1;
126
+ }
127
+ logger.success(`Done. ${plan.actions.length} action${plan.actions.length === 1 ? "" : "s"} applied.`);
128
+ return 0;
129
+ }
130
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ActionResult, PlannedAction } from "./types.js";
2
+ export interface ExecuteOptions {
3
+ dryRun: boolean;
4
+ force: boolean;
5
+ /** If true, fall back to copying the file when symlink creation fails. */
6
+ copyFallback: boolean;
7
+ }
8
+ export declare function executeAction(action: PlannedAction, options: ExecuteOptions): Promise<ActionResult>;
9
+ export declare function isWindowsHost(): boolean;
10
+ export declare function platformLabel(): string;
11
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE9D,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,0EAA0E;IAC1E,YAAY,EAAE,OAAO,CAAC;CACvB;AAgCD,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,YAAY,CAAC,CAsHvB;AAED,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
@@ -0,0 +1,163 @@
1
+ import { promises as fs } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const IS_WINDOWS = process.platform === "win32";
5
+ /**
6
+ * On Windows, `fs.symlink` needs an explicit type hint. We always use `file`
7
+ * for our use case (linking a single file). On POSIX systems the type arg is
8
+ * ignored.
9
+ */
10
+ async function createRelativeSymlink(linkPath, sourcePath) {
11
+ const target = path.relative(path.dirname(linkPath), sourcePath);
12
+ await fs.symlink(target, linkPath, IS_WINDOWS ? "file" : undefined);
13
+ }
14
+ async function safeUnlink(p) {
15
+ try {
16
+ await fs.unlink(p);
17
+ }
18
+ catch (err) {
19
+ const code = err.code;
20
+ if (code !== "ENOENT")
21
+ throw err;
22
+ }
23
+ }
24
+ async function copyFile(sourcePath, destPath) {
25
+ await fs.copyFile(sourcePath, destPath);
26
+ }
27
+ function isSymlinkPermissionError(err) {
28
+ const code = err?.code;
29
+ return code === "EPERM" || code === "EACCES" || code === "ENOSYS";
30
+ }
31
+ export async function executeAction(action, options) {
32
+ switch (action.type) {
33
+ case "skip":
34
+ return { action, ok: true, message: `skipped (${action.reason})` };
35
+ case "create-root-source": {
36
+ if (options.dryRun) {
37
+ return { action, ok: true, message: "would create empty source file" };
38
+ }
39
+ try {
40
+ const handle = await fs.open(action.sourcePath, "wx");
41
+ await handle.close();
42
+ return { action, ok: true, message: "created empty source file" };
43
+ }
44
+ catch (err) {
45
+ const code = err.code;
46
+ if (code === "EEXIST") {
47
+ return { action, ok: true, message: "source file already exists" };
48
+ }
49
+ return { action, ok: false, message: errorMessage(err) };
50
+ }
51
+ }
52
+ case "create-symlink": {
53
+ if (options.dryRun) {
54
+ const verb = action.replacesExisting ? "replace" : "create";
55
+ return { action, ok: true, message: `would ${verb} symlink` };
56
+ }
57
+ try {
58
+ if (action.replacesExisting)
59
+ await safeUnlink(action.linkPath);
60
+ await createRelativeSymlink(action.linkPath, action.sourcePath);
61
+ return { action, ok: true, message: "symlink created" };
62
+ }
63
+ catch (err) {
64
+ if (IS_WINDOWS && isSymlinkPermissionError(err) && options.copyFallback) {
65
+ try {
66
+ await safeUnlink(action.linkPath);
67
+ await copyFile(action.sourcePath, action.linkPath);
68
+ return {
69
+ action,
70
+ ok: true,
71
+ message: "symlink not permitted on this system; wrote a file copy instead. " +
72
+ "Re-run after enabling Developer Mode or with admin privileges " +
73
+ "to use real symlinks.",
74
+ fellBackToCopy: true,
75
+ };
76
+ }
77
+ catch (copyErr) {
78
+ return { action, ok: false, message: errorMessage(copyErr) };
79
+ }
80
+ }
81
+ if (IS_WINDOWS && isSymlinkPermissionError(err)) {
82
+ return {
83
+ action,
84
+ ok: false,
85
+ message: "creating a symlink was denied by the OS. On Windows, enable " +
86
+ "Developer Mode or run an elevated shell, or re-run with " +
87
+ "--copy-fallback to write a regular file copy instead.",
88
+ };
89
+ }
90
+ return { action, ok: false, message: errorMessage(err) };
91
+ }
92
+ }
93
+ case "promote-to-source": {
94
+ if (options.dryRun) {
95
+ return {
96
+ action,
97
+ ok: true,
98
+ message: `would rename ${path.basename(action.fromPath)} → ${path.basename(action.toPath)} and link back`,
99
+ };
100
+ }
101
+ try {
102
+ // If the target source path already exists (race or stale state) and
103
+ // we'd otherwise overwrite, bail out unless --force was requested.
104
+ let targetExists = false;
105
+ try {
106
+ await fs.lstat(action.toPath);
107
+ targetExists = true;
108
+ }
109
+ catch {
110
+ targetExists = false;
111
+ }
112
+ if (targetExists && !options.force) {
113
+ return {
114
+ action,
115
+ ok: false,
116
+ message: `${action.toPath} already exists; refusing to overwrite without --force.`,
117
+ };
118
+ }
119
+ if (targetExists)
120
+ await safeUnlink(action.toPath);
121
+ await fs.rename(action.fromPath, action.toPath);
122
+ await createRelativeSymlink(action.fromPath, action.toPath);
123
+ return {
124
+ action,
125
+ ok: true,
126
+ message: "promoted file to source and linked back",
127
+ };
128
+ }
129
+ catch (err) {
130
+ if (IS_WINDOWS && isSymlinkPermissionError(err) && options.copyFallback) {
131
+ try {
132
+ await copyFile(action.toPath, action.fromPath);
133
+ return {
134
+ action,
135
+ ok: true,
136
+ message: "symlink not permitted; wrote a file copy as fallback. The " +
137
+ "two files will diverge on subsequent edits.",
138
+ fellBackToCopy: true,
139
+ };
140
+ }
141
+ catch (copyErr) {
142
+ return { action, ok: false, message: errorMessage(copyErr) };
143
+ }
144
+ }
145
+ return { action, ok: false, message: errorMessage(err) };
146
+ }
147
+ }
148
+ }
149
+ }
150
+ export function isWindowsHost() {
151
+ return IS_WINDOWS;
152
+ }
153
+ export function platformLabel() {
154
+ return `${process.platform} (${os.release()})`;
155
+ }
156
+ function errorMessage(err) {
157
+ if (err instanceof Error) {
158
+ const code = err.code;
159
+ return code ? `${code}: ${err.message}` : err.message;
160
+ }
161
+ return String(err);
162
+ }
163
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface Logger {
2
+ info(msg: string): void;
3
+ success(msg: string): void;
4
+ warn(msg: string): void;
5
+ error(msg: string): void;
6
+ debug(msg: string): void;
7
+ raw(msg: string): void;
8
+ step(label: string, detail?: string): void;
9
+ }
10
+ export declare function createLogger(verbose: boolean): Logger;
11
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5C;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAyBrD"}