@cmkk/agentlink 0.1.0-beta.1 → 0.1.0-beta.2

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/CHANGELOG.md CHANGED
@@ -4,7 +4,30 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
- ## [0.1.0] - 2026-05-11
7
+ ## [0.1.0-beta.2] - 2026-06-09
8
+
9
+ ### 新增
10
+
11
+ - **config 持久化路径**:`agentlink.config.json` 支持可选的 `source`、`target`、`user` 字段,避免每次 `sync` 都带 flag;CLI flag / 环境变量仍优先于 config
12
+ - **source / destination 解析链扩展**:`config.source` 位于 `cwd/.agents` 与 `~/.agents` 之间;`config.target` / `config.user` 位于 CLI flag 与默认 `cwd` 之间
13
+ - **测试基础设施**:`tests/helpers/` 共享 sandbox / fs / io 工具;`load-context` 与 config 驱动 `sync` 集成测
14
+
15
+ ### 改进
16
+
17
+ - `loadContext` 先解析 config 再解析路径,避免重复读配置文件
18
+ - `init` 自定义 source 时写入 config 的 `source` 优先使用相对路径或 `~/…` 形式,便于跨机器移植
19
+ - `SourceNotFoundError` 诊断区分「config 未加载」「config 里未设置 source」以及 `cwd/.agents` 对 `config.source` 的优先关系
20
+ - 发布脚本:`preversion` / `publish:beta` / `publish:latest`;发布手册移至根目录 `RELEASE.md`
21
+
22
+ ### 文档
23
+
24
+ - README 补充 destination 解析链、config 字段说明及「整文件生效、不按字段合并」语义
25
+
26
+ ### 工程
27
+
28
+ - vitest 用例增至 122 个(11 个测试文件)
29
+
30
+ ## [0.1.0-beta.1] - 2026-05-11
8
31
 
9
32
  首次公开发布。
10
33
 
@@ -29,5 +52,6 @@
29
52
  - ESM-only,tsup 打包,dts 同步生成
30
53
  - Node ≥ 18
31
54
 
32
- [Unreleased]: https://github.com/rankangkang/dot-agents/compare/agentlink-v0.1.0...HEAD
33
- [0.1.0]: https://github.com/rankangkang/dot-agents/releases/tag/agentlink-v0.1.0
55
+ [Unreleased]: https://github.com/rankangkang/agentlink/compare/v0.1.0-beta.2...HEAD
56
+ [0.1.0-beta.2]: https://github.com/rankangkang/agentlink/compare/v0.1.0-beta.1...v0.1.0-beta.2
57
+ [0.1.0-beta.1]: https://github.com/rankangkang/agentlink/releases/tag/v0.1.0-beta.1
package/README.md CHANGED
@@ -62,8 +62,8 @@ agentlink init [--local | --source <path>] [--force] [--yes]
62
62
  (--local 写 cwd,否则写 ~),不在 source 内部。
63
63
 
64
64
  agentlink sync [--source <path>] [--target <dir> | --user] [-n] [-f] [--strict]
65
- 刷新所有 symlink。source 按以下优先级解析:
66
- --source flag $AGENTLINK_SOURCE ./.agents ~/.agents
65
+ 刷新所有 symlink。source / destination 见下方解析链;也可写在
66
+ agentlink.config.json source / target / user 字段里持久化。
67
67
  destination root 默认是 cwd;--user / -u 改为 $HOME(把 source
68
68
  投影到 ~/.cursor / ~/.claude / ...),--target <dir> 指定任意项目根。
69
69
  --dry-run / -n 预览将做什么,不动文件系统
@@ -85,7 +85,9 @@ agentlink doctor
85
85
 
86
86
  ## source 与 config 解析
87
87
 
88
- agentlink 把"**对什么数据操作**"(`.agents/` source)与"**怎么操作**"(`agentlink.config.json`)解耦:两者各有独立解析链,可以共享同一份 source 但每个项目用自己的 config 覆盖行为。
88
+ agentlink 把"**对什么数据操作**"(`.agents/` source)与"**怎么操作**"(`agentlink.config.json`)大体解耦:config 主要描述投影行为,也可选地持久化 `source` / `target` 默认值。两者各有独立解析链。
89
+
90
+ **配置文件是整文件生效**——从 cwd 向上找到最近的一份 `agentlink.config.json` 后,**整份**用于 adapters / include 等;不会把「项目 config 的 adapters」和「全局 config 的 source」按字段合并。若项目目录里已有 config 文件,就不会再去读 `~/agentlink.config.json`。
89
91
 
90
92
  ### source 解析
91
93
 
@@ -93,11 +95,23 @@ agentlink 把"**对什么数据操作**"(`.agents/` source)与"**怎么操
93
95
  |---|---|---|
94
96
  | 1 | `--source <path>` | 显式 flag |
95
97
  | 2 | `AGENTLINK_SOURCE` 环境变量 | 适合写在 shell rc |
96
- | 3 | `./.agents` | **本地优先** — 克隆的项目自带 `.agents/` 时压过个人全局 |
97
- | 4 | `~/.agents` | 个人推荐默认 |
98
- | 5 | _(报错)_ | 提示用户跑 `agentlink init` |
98
+ | 3 | `./.agents` | **本地优先** — 只要该目录存在(哪怕只有 `.gitkeep`)就压过后面的 `config.source` 与 `~/.agents` |
99
+ | 4 | `config.source` | 来自**已解析的那份** config;相对路径相对于 config 文件所在目录 |
100
+ | 5 | `~/.agents` | 个人推荐默认 |
101
+ | 6 | _(报错)_ | 提示用户跑 `agentlink init` |
99
102
 
100
- ### config 解析
103
+ ### destination 解析
104
+
105
+ | # | 来源 | 说明 |
106
+ |---|---|---|
107
+ | 1 | `--target <dir>` | 显式 flag |
108
+ | 2 | `--user` / `-u` | 投影根改为 `$HOME` |
109
+ | 3 | `config.target` / `config.user` | 来自已解析的那份 config;`target` 相对路径同样相对 config 文件目录 |
110
+ | 4 | `cwd` | 默认 |
111
+
112
+ `config.target` 与 `config.user` 互斥(schema 校验)。
113
+
114
+ ### config 文件位置
101
115
 
102
116
  | # | 来源 | 说明 |
103
117
  |---|---|---|
@@ -117,8 +131,13 @@ agentlink 把"**对什么数据操作**"(`.agents/` source)与"**怎么操
117
131
 
118
132
  ```jsonc
119
133
  {
134
+ // 可选。持久化 source / destination,省得每次带 flag。
135
+ // 相对路径相对于本文件所在目录解析。
136
+ // "source": "~/.dotfiles/agents",
137
+ // "target": ".",
138
+ // "user": true, // 与 target 二选一,等价于 --user
139
+
120
140
  // asset 类型白名单。默认:["skills", "rules", "commands"]
121
- // 想启用 prompts / templates / agents 在这里加上即可
122
141
  "include": ["skills", "rules", "commands"],
123
142
 
124
143
  // asset 类型黑名单(永远压过 include)
package/dist/cli.js CHANGED
@@ -231,11 +231,23 @@ var adapterOverrideSchema = z.union([
231
231
  var adapterNameSchema = z.enum(
232
232
  Object.keys(adapterRegistry)
233
233
  );
234
+ var rootPathSchema = z.string().min(1);
234
235
  var configSchema = z.object({
236
+ source: rootPathSchema.optional(),
237
+ target: rootPathSchema.optional(),
238
+ user: z.boolean().optional(),
235
239
  include: z.array(assetTypeSchema).optional(),
236
240
  exclude: z.array(assetTypeSchema).optional(),
237
241
  adapters: z.partialRecord(adapterNameSchema, adapterOverrideSchema).optional()
238
- }).strict();
242
+ }).strict().superRefine((val, ctx) => {
243
+ if (val.target !== void 0 && val.user === true) {
244
+ ctx.addIssue({
245
+ code: "custom",
246
+ message: "`target` and `user` are mutually exclusive",
247
+ path: ["target"]
248
+ });
249
+ }
250
+ });
239
251
  var ConfigParseError = class extends AgentlinkError {
240
252
  issues;
241
253
  constructor(filePath, issues) {
@@ -245,13 +257,17 @@ ${lines.join("\n")}`);
245
257
  this.issues = issues;
246
258
  }
247
259
  };
248
- async function loadConfig(opts) {
260
+ async function loadConfigBundle(opts = {}) {
249
261
  const location = await resolveConfigPath(opts);
250
- const userConfig = location.path ? await readUserConfig(location.path) : {};
262
+ const parsed = location.path ? await readUserConfig(location.path) : {};
263
+ const configBase = location.path ? path2.dirname(location.path) : null;
264
+ return { location, parsed, configBase };
265
+ }
266
+ function resolveConfigFromParsed(userConfig, location, source) {
251
267
  const adapters = mergeAdapters(userConfig.adapters);
252
268
  const assetTypes = resolveAssetTypes(userConfig.include, userConfig.exclude);
253
269
  return {
254
- source: opts.source,
270
+ source,
255
271
  adapters,
256
272
  assetTypes,
257
273
  configPath: location.path,
@@ -459,6 +475,16 @@ async function resolveSource(opts = {}) {
459
475
  if (await isDir(local)) {
460
476
  return { path: local, origin: "cwd-local" };
461
477
  }
478
+ const configSource = opts.configSource;
479
+ if (configSource !== void 0 && configSource !== "") {
480
+ const base = opts.configBase ?? cwd;
481
+ const abs = await assertDir(
482
+ resolveConfigPathValue(configSource, base, home),
483
+ "config.source",
484
+ cwd
485
+ );
486
+ return { path: abs, origin: "config" };
487
+ }
462
488
  const homeAgents = path3.join(home, AGENTS_DIRNAME);
463
489
  if (await isDir(homeAgents)) {
464
490
  return { path: homeAgents, origin: "home-global" };
@@ -466,10 +492,23 @@ async function resolveSource(opts = {}) {
466
492
  throw new SourceNotFoundError([
467
493
  { origin: "--source flag", status: "not provided" },
468
494
  { origin: `$${ENV_SOURCE_KEY}`, status: "not set" },
469
- { origin: local, status: "missing" },
495
+ { origin: local, status: localProbeStatus(local) },
496
+ { origin: "config.source", status: configSourceStatus(opts) },
470
497
  { origin: homeAgents, status: "missing" }
471
498
  ]);
472
499
  }
500
+ function localProbeStatus(local) {
501
+ return `missing (if ${local} exists \u2014 even empty \u2014 it takes priority over config.source)`;
502
+ }
503
+ function configSourceStatus(opts) {
504
+ if (opts.configSource !== void 0 && opts.configSource !== "") {
505
+ return "not reached";
506
+ }
507
+ if (opts.resolvedConfigPath) {
508
+ return `not set in ${opts.resolvedConfigPath}`;
509
+ }
510
+ return "no config file loaded";
511
+ }
473
512
  async function resolveDestRoot(opts = {}) {
474
513
  const cwd = opts.cwd ?? process.cwd();
475
514
  const home = opts.home ?? os2.homedir();
@@ -483,8 +522,27 @@ async function resolveDestRoot(opts = {}) {
483
522
  if (opts.user) {
484
523
  return { path: home, origin: "user" };
485
524
  }
525
+ if (opts.configTarget !== void 0 && opts.configTarget !== "" && opts.configUser) {
526
+ throw new ConflictError("config `target` and `user` are mutually exclusive");
527
+ }
528
+ if (opts.configTarget !== void 0 && opts.configTarget !== "") {
529
+ const base = opts.configBase ?? cwd;
530
+ const abs = await assertDir(
531
+ resolveConfigPathValue(opts.configTarget, base, home),
532
+ "config.target",
533
+ cwd
534
+ );
535
+ return { path: abs, origin: "config" };
536
+ }
537
+ if (opts.configUser) {
538
+ return { path: home, origin: "config" };
539
+ }
486
540
  return { path: cwd, origin: "cwd" };
487
541
  }
542
+ function resolveConfigPathValue(input, configBase, home) {
543
+ const expanded = expandTilde2(input, home);
544
+ return path3.isAbsolute(expanded) ? expanded : path3.resolve(configBase, expanded);
545
+ }
488
546
  function expandTilde2(p, home) {
489
547
  if (p === "~") return home;
490
548
  if (p.startsWith("~/")) return path3.join(home, p.slice(2));
@@ -511,15 +569,20 @@ function isInside2(child, parent) {
511
569
 
512
570
  // src/commands/_shared.ts
513
571
  async function loadContext(flags) {
572
+ const bundle = await loadConfigBundle({
573
+ ...flags.config !== void 0 ? { config: flags.config } : {}
574
+ });
514
575
  const paths = await resolvePaths({
515
576
  ...flags.source !== void 0 ? { source: flags.source } : {},
516
577
  ...flags.target !== void 0 ? { target: flags.target } : {},
517
- ...flags.user !== void 0 ? { user: flags.user } : {}
518
- });
519
- const rawConfig = await loadConfig({
520
- source: paths.source,
521
- ...flags.config !== void 0 ? { config: flags.config } : {}
578
+ ...flags.user !== void 0 ? { user: flags.user } : {},
579
+ ...bundle.parsed.source !== void 0 ? { configSource: bundle.parsed.source } : {},
580
+ ...bundle.parsed.target !== void 0 ? { configTarget: bundle.parsed.target } : {},
581
+ ...bundle.parsed.user !== void 0 ? { configUser: bundle.parsed.user } : {},
582
+ ...bundle.configBase !== null ? { configBase: bundle.configBase } : {},
583
+ resolvedConfigPath: bundle.location.path
522
584
  });
585
+ const rawConfig = resolveConfigFromParsed(bundle.parsed, bundle.location, paths.source);
523
586
  const config = await gateAdapters(rawConfig, paths.destRoot);
524
587
  return { paths, config };
525
588
  }
@@ -1175,14 +1238,32 @@ async function writeConfigFile(answers, quiet, dryRun) {
1175
1238
  if (!quiet) console.log(`${pc5.green("[NEW]")} ${configPath}`);
1176
1239
  return 1;
1177
1240
  }
1241
+ function formatSourceForConfig(target, configPath) {
1242
+ const resolved = path8.resolve(target);
1243
+ const configDir = path8.resolve(path8.dirname(configPath));
1244
+ const home = path8.resolve(os3.homedir());
1245
+ const rel = path8.relative(configDir, resolved);
1246
+ if (rel !== "" && !rel.startsWith("..") && !path8.isAbsolute(rel)) {
1247
+ return rel;
1248
+ }
1249
+ if (resolved === home || resolved.startsWith(home + path8.sep)) {
1250
+ const tail = path8.relative(home, resolved);
1251
+ return tail === "" ? "~" : `~/${tail}`;
1252
+ }
1253
+ return resolved;
1254
+ }
1178
1255
  function renderConfig(answers) {
1179
1256
  const includeJson = JSON.stringify(answers.include);
1180
1257
  const adapterLines = ALL_ADAPTER_NAMES.map((name) => {
1181
1258
  const value = answers.adapters.includes(name) ? "true" : "false";
1182
1259
  return ` "${name}": ${value}`;
1183
1260
  }).join(",\n");
1261
+ const sourceBlock = answers.targetCategory === "custom" ? ` // Persistent default for \`agentlink sync\` (relative paths resolve from this file).
1262
+ "source": ${JSON.stringify(formatSourceForConfig(answers.target, answers.configPath))},
1263
+
1264
+ ` : "";
1184
1265
  return `{
1185
- // Asset type allowlist. Default: ["skills", "rules", "commands"].
1266
+ ${sourceBlock} // Asset type allowlist. Default: ["skills", "rules", "commands"].
1186
1267
  // Add "prompts", "templates" or "agents" here to opt them in.
1187
1268
  "include": ${includeJson},
1188
1269
 
@@ -1207,8 +1288,9 @@ function printNonDefaultLocationHint(target) {
1207
1288
  console.log(pc5.bold("Tip \u2014 your source is at a non-default location:"));
1208
1289
  console.log(` ${target}`);
1209
1290
  console.log("");
1210
- console.log("Future invocations of `agentlink sync` need to know about it. Either:");
1211
- console.log(` - pass ${pc5.cyan(`--source ${target}`)} each time, or`);
1291
+ console.log("Future invocations of `agentlink sync` will read `source` from your");
1292
+ console.log("`agentlink.config.json` (written during init). You can also:");
1293
+ console.log(` - pass ${pc5.cyan(`--source ${target}`)} to override, or`);
1212
1294
  console.log(` - export ${pc5.cyan(`AGENTLINK_SOURCE=${target}`)} in your shell rc, or`);
1213
1295
  console.log(` - symlink it: ${pc5.cyan(`ln -s ${target} ~/.agents`)}`);
1214
1296
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/commands/clean.ts","../src/core/linker.ts","../src/commands/_shared.ts","../src/adapters/claude.ts","../src/adapters/codebuddy.ts","../src/adapters/cursor.ts","../src/adapters/index.ts","../src/core/config.ts","../src/core/errors.ts","../src/core/types.ts","../src/core/resolve.ts","../src/commands/doctor.ts","../src/core/layout.ts","../src/commands/init.ts","../src/commands/sync.ts","../src/commands/unlink.ts"],"sourcesContent":["import { cac } from 'cac';\nimport pc from 'picocolors';\n\nimport { runClean } from './commands/clean.js';\nimport { runDoctor } from './commands/doctor.js';\nimport { runInit } from './commands/init.js';\nimport { runSync } from './commands/sync.js';\nimport { runUnlink } from './commands/unlink.js';\nimport type { CommandFlags } from './commands/_shared.js';\nimport {\n AgentlinkError,\n ConflictError,\n InvalidPathError,\n SourceNotFoundError,\n} from './core/errors.js';\nimport { ConfigParseError } from './core/config.js';\nimport { StrictModeViolation } from './commands/sync.js';\n\nconst PKG_NAME = '@cmkk/agentlink';\nconst PKG_VERSION = '0.1.0';\n\nfunction applyGlobalOptions(cli: ReturnType<typeof cac>): void {\n cli\n .option('--source <path>', 'Override the source .agents/ location')\n .option('-t, --target <dir>', 'Run against a specific destination root')\n .option('-u, --user', 'Link into $HOME (user-level install target)')\n .option('--config <path>', 'Load a specific agentlink.config.json')\n .option('-n, --dry-run', 'Preview changes without touching the filesystem')\n .option('-f, --force', 'Replace symlinks that point somewhere unexpected')\n .option('-q, --quiet', 'Only print changes and errors')\n .option('-v, --verbose', 'Print debug-level output')\n .option('--strict', 'Treat collisions as errors instead of warnings');\n}\n\nasync function main(): Promise<void> {\n const cli = cac('agentlink');\n\n cli\n .command('init', 'Create an empty .agents/ scaffold and run sync once')\n .option('--local', 'Scaffold into ./.agents instead of ~/.agents')\n .option('-y, --yes', 'Skip interactive prompts and use defaults (CI-friendly)')\n .action((flags: CommandFlags & { local?: boolean; yes?: boolean }) => runInit(flags));\n\n cli\n .command('sync', 'Refresh all symlinks (default command)')\n .action((flags: CommandFlags) => runSync(flags));\n\n cli\n .command('clean', 'Remove dangling agentlink-managed symlinks')\n .action((flags: CommandFlags) => runClean(flags));\n\n cli\n .command('unlink', 'Remove every symlink agentlink ever created')\n .action((flags: CommandFlags) => runUnlink(flags));\n\n cli\n .command('doctor', 'Diagnose configuration, adapters, and link health')\n .action((flags: CommandFlags) => runDoctor(flags));\n\n cli\n .command('', 'Default command (alias for `sync`)')\n .action((flags: CommandFlags) => runSync(flags));\n\n applyGlobalOptions(cli);\n cli.help();\n cli.version(PKG_VERSION);\n\n try {\n cli.parse(process.argv, { run: false });\n await cli.runMatchedCommand();\n } catch (err) {\n renderError(err);\n process.exit(1);\n }\n}\n\nfunction renderError(err: unknown): void {\n if (err instanceof SourceNotFoundError) {\n console.error(pc.red(`[${PKG_NAME}] no source .agents/ directory found.\\n`));\n console.error('Searched:');\n for (const { origin, status } of err.searched) {\n console.error(` ${origin.padEnd(40)} ${pc.dim(status)}`);\n }\n console.error('');\n console.error('Hints:');\n console.error(' - run `agentlink init` to scaffold ~/.agents/');\n console.error(' - run `agentlink init --local` to scaffold ./.agents/');\n console.error(' - or pass `--source <path>` explicitly');\n return;\n }\n\n if (err instanceof StrictModeViolation) {\n console.error(pc.red(`[${PKG_NAME}] aborted: ${err.message}`));\n console.error(pc.dim(' rerun without --strict to accept the warnings, or fix the source.'));\n return;\n }\n\n if (err instanceof ConfigParseError) {\n console.error(pc.red(`[${PKG_NAME}] ${err.message}`));\n return;\n }\n\n if (err instanceof InvalidPathError || err instanceof ConflictError) {\n console.error(pc.red(`[${PKG_NAME}] ${err.message}`));\n return;\n }\n\n if (err instanceof AgentlinkError) {\n console.error(pc.red(`[${PKG_NAME}] ${err.message}`));\n return;\n }\n\n const msg = err instanceof Error ? err.message : String(err);\n console.error(pc.red(`[${PKG_NAME}] ${msg}`));\n if (process.env['AGENTLINK_DEBUG']) {\n console.error(err);\n }\n}\n\nawait main();\n","import pc from 'picocolors';\n\nimport { findStaleLinks, removeLinks } from '../core/linker.js';\nimport {\n enumerateTargetDirs,\n loadContext,\n printHeader,\n type CommandFlags,\n} from './_shared.js';\n\nexport async function runClean(flags: CommandFlags = {}): Promise<void> {\n const ctx = await loadContext(flags);\n printHeader(ctx, 'clean', flags);\n\n const targets = enumerateTargetDirs(ctx).map((t) => t.targetDir);\n const stale = (await Promise.all(targets.map(findStaleLinks))).flat();\n\n if (stale.length === 0) {\n console.log(pc.dim(' no dangling symlinks found'));\n return;\n }\n\n const outcomes = await removeLinks(stale, flags.dryRun ? { dryRun: true } : {});\n\n let cleaned = 0;\n let dry = 0;\n let errored = 0;\n for (const o of outcomes) {\n if (o.status === 'removed') {\n cleaned++;\n if (!flags.quiet) console.log(`${pc.yellow('[CLEAN]')} ${o.path}`);\n } else if (o.status === 'dry-remove') {\n dry++;\n if (!flags.quiet) console.log(`${pc.dim('[DRY]')} would clean ${o.path}`);\n } else if (o.status === 'errored') {\n errored++;\n console.error(`${pc.red('[ERR]')} ${o.path} ${pc.dim(`(${o.message ?? ''})`)}`);\n }\n }\n\n console.log('');\n const parts: string[] = [];\n if (cleaned) parts.push(pc.yellow(`${cleaned} cleaned`));\n if (dry) parts.push(pc.dim(`${dry} dry-run`));\n if (errored) parts.push(pc.red(`${errored} errored`));\n console.log(parts.length === 0 ? pc.dim(' no changes') : ` ${parts.join(', ')}`);\n\n if (errored > 0) process.exitCode = 1;\n}\n","/**\n * Filesystem layer for agentlink. All symlink reads/writes funnel through this\n * module so command implementations stay declarative and the test suite can\n * exercise edge cases against a real tmpdir.\n *\n * Conventions:\n * - Symlinks are written **relatively** (`path.relative(targetDir, source)`)\n * so they survive moving the project root, mirroring the legacy\n * `init.sh` behaviour.\n * - Existing real files / directories are never overwritten — they always\n * produce a `warned` outcome so the user can decide.\n * - `dry-run` is a hard mode switch: no writes, but every check still runs\n * so the user sees exactly what `sync` _would_ do.\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport type { LinkPlan } from './layout.js';\n\nexport type LinkStatus =\n | 'created' // new symlink written\n | 'skipped' // already pointed at the right place\n | 'fixed' // existed but pointed elsewhere; replaced (force)\n | 'warned' // exists with conflicting kind; left untouched\n | 'errored' // unexpected fs failure\n | 'dry-create' // dry-run: would create\n | 'dry-fix'; // dry-run: would replace\n\nexport interface LinkOutcome {\n plan: LinkPlan;\n status: LinkStatus;\n message?: string;\n}\n\nexport interface ApplyOptions {\n dryRun?: boolean;\n /** Replace symlinks that point somewhere unexpected. */\n force?: boolean;\n}\n\n/**\n * Apply each plan exactly once, returning per-plan outcomes in input order.\n * Never throws on per-plan errors; surfaces them as `errored` outcomes so a\n * single bad file does not halt a large sync.\n */\nexport async function applyPlans(\n plans: readonly LinkPlan[],\n options: ApplyOptions = {},\n): Promise<LinkOutcome[]> {\n const outcomes: LinkOutcome[] = [];\n for (const plan of plans) {\n outcomes.push(await applyOne(plan, options));\n }\n return outcomes;\n}\n\nasync function applyOne(plan: LinkPlan, options: ApplyOptions): Promise<LinkOutcome> {\n const { dryRun = false, force = false } = options;\n const targetDir = path.dirname(plan.target);\n const expected = path.relative(targetDir, plan.source);\n\n try {\n const lstat = await fs.lstat(plan.target).catch(() => null);\n\n if (lstat?.isSymbolicLink()) {\n const current = await fs.readlink(plan.target);\n if (current === expected) return { plan, status: 'skipped' };\n if (force) {\n if (dryRun) {\n return { plan, status: 'dry-fix', message: `${current} -> ${expected}` };\n }\n await fs.unlink(plan.target);\n await fs.symlink(expected, plan.target);\n return { plan, status: 'fixed', message: `${current} -> ${expected}` };\n }\n return {\n plan,\n status: 'warned',\n message: `points to ${current} (use --force to repair)`,\n };\n }\n\n if (lstat) {\n const kind = lstat.isDirectory() ? 'directory' : 'file';\n return { plan, status: 'warned', message: `${kind} already exists at target` };\n }\n\n if (dryRun) {\n return { plan, status: 'dry-create' };\n }\n\n await fs.mkdir(targetDir, { recursive: true });\n await fs.symlink(expected, plan.target);\n return { plan, status: 'created' };\n } catch (err) {\n return { plan, status: 'errored', message: err instanceof Error ? err.message : String(err) };\n }\n}\n\n/* ────────────────────────── Stale link detection ─────────────────────── */\n\n/**\n * Inspect a directory and return the absolute paths of every entry that is a\n * symlink whose target no longer resolves (`fs.stat` fails — dangling).\n *\n * Non-recursive: only the top level of `targetDir` is scanned, matching the\n * legacy `init.sh` behaviour. Symlinks pointing to nested-but-existing files\n * are left alone.\n */\nexport async function findStaleLinks(targetDir: string): Promise<string[]> {\n const entries = await fs.readdir(targetDir, { withFileTypes: true }).catch(() => null);\n if (!entries) return [];\n\n const stale: string[] = [];\n for (const entry of entries) {\n if (!entry.isSymbolicLink()) continue;\n const full = path.join(targetDir, entry.name);\n const exists = await fs.stat(full).catch(() => null);\n if (!exists) stale.push(full);\n }\n return stale;\n}\n\n/* ────────────────────────── Managed-link detection (unlink) ──────────── */\n\n/**\n * Inspect a directory and return symlinks whose resolved target lives under\n * `sourceRoot`. Used by `agentlink unlink` to find every symlink we ever\n * created without requiring a \"manifest\" file.\n *\n * Resolves the symlink target lexically (no `realpath`) so the user can swap\n * the source directory at any time without bricking the unlink path.\n *\n * Defaults to a non-recursive single-directory scan (mirroring what sync\n * publishes one level deep). Pass `{ recursive: true }` to descend into every\n * subdirectory — used by `unlink` and `doctor` to catch links that live in\n * asset subdirs the current config has dropped.\n */\nexport async function findManagedLinks(\n targetDir: string,\n sourceRoot: string,\n options: { recursive?: boolean } = {},\n): Promise<string[]> {\n const sourceAbs = path.resolve(sourceRoot);\n const out: string[] = [];\n\n await (async function descend(dir: string): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => null);\n if (!entries) return;\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isSymbolicLink()) {\n const raw = await fs.readlink(full).catch(() => null);\n if (!raw) continue;\n const resolved = path.isAbsolute(raw) ? path.normalize(raw) : path.resolve(dir, raw);\n if (resolved === sourceAbs || resolved.startsWith(sourceAbs + path.sep)) {\n out.push(full);\n }\n } else if (options.recursive && entry.isDirectory()) {\n await descend(full);\n }\n }\n })(targetDir);\n\n return out;\n}\n\n/* ────────────────────────── Removal helpers ─────────────────────────── */\n\nexport type RemovalStatus = 'removed' | 'dry-remove' | 'errored';\n\nexport interface RemovalOutcome {\n path: string;\n status: RemovalStatus;\n message?: string;\n}\n\nexport interface RemoveOptions {\n dryRun?: boolean;\n}\n\n/**\n * Delete the supplied symlinks. Errors are returned per-path rather than\n * thrown, mirroring `applyPlans`.\n */\nexport async function removeLinks(\n paths: readonly string[],\n options: RemoveOptions = {},\n): Promise<RemovalOutcome[]> {\n const { dryRun = false } = options;\n const out: RemovalOutcome[] = [];\n for (const p of paths) {\n if (dryRun) {\n out.push({ path: p, status: 'dry-remove' });\n continue;\n }\n try {\n await fs.unlink(p);\n out.push({ path: p, status: 'removed' });\n } catch (err) {\n out.push({\n path: p,\n status: 'errored',\n message: err instanceof Error ? err.message : String(err),\n });\n }\n }\n return out;\n}\n\n/**\n * Best-effort `rmdir` of empty managed directories left behind by `unlink`.\n * Failure to remove (e.g. user-owned siblings inside) is silent — the goal is\n * tidiness, not enforcement.\n */\nexport async function pruneEmptyDirs(dirs: readonly string[]): Promise<void> {\n for (const dir of dirs) {\n await fs.rmdir(dir).catch(() => undefined);\n }\n}\n","/**\n * Shared scaffolding for command implementations:\n * - flag plumbing (every command takes the same global flags)\n * - context loading (resolve paths + load config)\n * - common header printing\n * - target-directory enumeration used by clean / unlink\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport pc from 'picocolors';\n\nimport { adapterRegistry } from '../adapters/index.js';\nimport { loadConfig } from '../core/config.js';\nimport { resolvePaths, type ResolvedPaths } from '../core/resolve.js';\nimport type { Adapter, AssetType, ResolvedConfig } from '../core/types.js';\n\nexport interface CommandFlags {\n source?: string;\n target?: string;\n user?: boolean;\n config?: string;\n dryRun?: boolean;\n force?: boolean;\n quiet?: boolean;\n verbose?: boolean;\n strict?: boolean;\n}\n\nexport interface CommandContext {\n paths: ResolvedPaths;\n config: ResolvedConfig;\n}\n\nexport async function loadContext(flags: CommandFlags): Promise<CommandContext> {\n const paths = await resolvePaths({\n ...(flags.source !== undefined ? { source: flags.source } : {}),\n ...(flags.target !== undefined ? { target: flags.target } : {}),\n ...(flags.user !== undefined ? { user: flags.user } : {}),\n });\n const rawConfig = await loadConfig({\n source: paths.source,\n ...(flags.config !== undefined ? { config: flags.config } : {}),\n });\n const config = await gateAdapters(rawConfig, paths.destRoot);\n return { paths, config };\n}\n\n/**\n * Drop adapters whose `rootDir` does not exist under the destination root.\n *\n * This mirrors the legacy `init.sh` \"auto-detect\" behaviour: a missing\n * `.cursor` directory is treated as \"this user does not use cursor on this\n * machine\" rather than \"create one for them\". Users opt-in by `mkdir\n * .cursor` (or running their AI tool which does so).\n */\nasync function gateAdapters(config: ResolvedConfig, destRoot: string): Promise<ResolvedConfig> {\n const live: Adapter[] = [];\n for (const adapter of config.adapters) {\n const root = path.join(destRoot, adapter.rootDir);\n const stat = await fs.stat(root).catch(() => null);\n if (stat?.isDirectory()) live.push(adapter);\n }\n if (live.length === config.adapters.length) return config;\n return { ...config, adapters: live };\n}\n\nexport function printHeader(ctx: CommandContext, mode: string, flags: CommandFlags): void {\n if (flags.quiet) return;\n console.log('');\n console.log(pc.bold(`[agentlink] ${mode}`));\n console.log(` source: ${ctx.paths.source} ${pc.dim(`(${ctx.paths.sourceOrigin})`)}`);\n console.log(` target: ${ctx.paths.destRoot} ${pc.dim(`(${ctx.paths.destOrigin})`)}`);\n const configLine = ctx.config.configPath\n ? `${ctx.config.configPath} ${pc.dim(`(${ctx.config.configOrigin})`)}`\n : pc.dim('(defaults)');\n console.log(` config: ${configLine}`);\n console.log(` adapters: ${ctx.config.adapters.map((a) => a.name).join(', ') || pc.dim('(none)')}`);\n if (flags.dryRun) console.log(pc.yellow(' mode: dry-run'));\n console.log('');\n}\n\n/**\n * Walk the (adapter × assetType) combinations relevant for the run, returning\n * each adapter's set of `targetAssetDir`s. Used by clean / unlink to know\n * which directories to inspect without re-deriving the layout themselves.\n */\nexport function enumerateTargetDirs(ctx: CommandContext): {\n adapter: Adapter;\n assetType: AssetType;\n targetDir: string;\n rootDir: string;\n}[] {\n const out: { adapter: Adapter; assetType: AssetType; targetDir: string; rootDir: string }[] = [];\n const root = ctx.paths.destRoot;\n for (const adapter of ctx.config.adapters) {\n for (const assetType of ctx.config.assetTypes as ReadonlySet<AssetType>) {\n const slot = adapter.layout[assetType];\n if (slot === undefined || slot === false) continue;\n out.push({\n adapter,\n assetType,\n targetDir: path.join(root, adapter.rootDir, slot.targetSubdir),\n rootDir: path.join(root, adapter.rootDir),\n });\n }\n }\n return out;\n}\n\n/**\n * Return every built-in adapter rootDir that exists under `destRoot`,\n * regardless of whether the user has currently enabled it in config.\n *\n * Used by `unlink` and `doctor`'s orphan sweep: those operations must not\n * skip directories that the user just disabled — otherwise the legacy links\n * created when the adapter *was* enabled would be unreachable.\n *\n * `enumerateTargetDirs` (above) keeps gate semantics for sync / clean,\n * which legitimately operate only on what the current config selects.\n */\nexport async function enumerateAllAdapterRoots(\n destRoot: string,\n): Promise<{ name: string; rootDir: string }[]> {\n const out: { name: string; rootDir: string }[] = [];\n for (const [name, preset] of Object.entries(adapterRegistry)) {\n const rootDir = path.join(destRoot, preset.rootDir);\n const stat = await fs.stat(rootDir).catch(() => null);\n if (stat?.isDirectory()) out.push({ name, rootDir });\n }\n return out;\n}\n","import type { Adapter } from '../core/types.js';\n\n/**\n * Claude Code's conventions: nested directories are fine across all asset\n * types, no special suffix requirements.\n */\nexport const claude: Adapter = {\n name: 'claude',\n rootDir: '.claude',\n layout: {\n skills: { targetSubdir: 'skills' },\n rules: { targetSubdir: 'rules' },\n commands: { targetSubdir: 'commands' },\n },\n};\n","import type { Adapter } from '../core/types.js';\n\n/**\n * CodeBuddy supports nested commands and the same `<root>/<asset>/` shape as\n * claude. Kept symmetric with the claude adapter for now; will diverge if the\n * agent gets stricter conventions.\n */\nexport const codebuddy: Adapter = {\n name: 'codebuddy',\n rootDir: '.codebuddy',\n layout: {\n skills: { targetSubdir: 'skills' },\n rules: { targetSubdir: 'rules' },\n commands: { targetSubdir: 'commands' },\n },\n};\n","import type { Adapter } from '../core/types.js';\n\n/**\n * Cursor's filesystem conventions:\n * - `.cursor/skills/<name>/SKILL.md` recursive (nested OK)\n * - `.cursor/rules/<file>.mdc` flat, must use `.mdc` suffix\n * - `.cursor/commands/<file>.md` flat (no nested subdirs)\n * - prompts: not consumed; explicitly skipped\n */\nexport const cursor: Adapter = {\n name: 'cursor',\n rootDir: '.cursor',\n layout: {\n skills: { targetSubdir: 'skills' },\n rules: { targetSubdir: 'rules', flatten: true, ext: '.mdc' },\n commands: { targetSubdir: 'commands', flatten: true },\n prompts: false,\n },\n};\n","import type { Adapter } from '../core/types.js';\nimport { claude } from './claude.js';\nimport { codebuddy } from './codebuddy.js';\nimport { cursor } from './cursor.js';\n\n/**\n * v1 ships with three built-in adapters. The keys here are also the only valid\n * names accepted in `agentlink.config.json`'s `adapters` field — supporting a\n * new agent requires publishing a new package version.\n */\nexport const adapterRegistry = {\n cursor,\n claude,\n codebuddy,\n} as const satisfies Record<string, Adapter>;\n\nexport type AdapterName = keyof typeof adapterRegistry;\n\nexport { claude, codebuddy, cursor };\n","/**\n * Locate, validate, and merge `agentlink.config.json` with built-in adapter\n * presets to produce a {@link ResolvedConfig}.\n *\n * Config and source are **decoupled** — config tells agentlink _what to do_,\n * source tells it _what data to operate on_. Each has its own resolution chain.\n *\n * Config resolution chain (highest first):\n * 1. `--config <path>` flag\n * 2. `AGENTLINK_CONFIG` env var\n * 3. nearest `agentlink.config.json` from cwd up to (but not past) the\n * home directory or filesystem root\n * 4. `~/agentlink.config.json`\n * 5. (none) — fall back to all defaults\n *\n * Validation philosophy:\n * - **Closed asset-type universe**: keys must be one of the literal\n * {@link AssetType} values. Unknown names yield a friendly zod error.\n * - **Closed adapter universe** (v1): `adapters` keys must match a built-in\n * name (cursor / claude / codebuddy).\n * - **Tolerant defaults**: every field is optional; missing config equals\n * an empty `{}`.\n */\n\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport stripJsonComments from 'strip-json-comments';\nimport { z } from 'zod';\n\nimport { adapterRegistry, type AdapterName } from '../adapters/index.js';\nimport { AgentlinkError, InvalidPathError } from './errors.js';\nimport {\n DEFAULT_INCLUDE,\n type Adapter,\n type AssetLayout,\n type AssetType,\n type ResolvedConfig,\n} from './types.js';\n\nexport const CONFIG_FILENAME = 'agentlink.config.json';\nconst ENV_CONFIG_KEY = 'AGENTLINK_CONFIG';\n\nconst ASSET_TYPE_VALUES = ['skills', 'rules', 'commands', 'prompts', 'templates', 'agents'] as const;\nconst ASSET_TYPE_SET: ReadonlySet<AssetType> = new Set(ASSET_TYPE_VALUES);\n\nconst assetTypeSchema = z.enum(ASSET_TYPE_VALUES);\n\n/**\n * `targetSubdir` is `path.join`'d under each adapter's rootDir. Letting users\n * write `..`, absolute paths, or path separators here would let them escape\n * the rootDir sandbox (and silently smear symlinks across the destination).\n *\n * We reject the obviously bad shapes at the schema layer so zod produces a\n * focused, path-aware error message instead of a generic ConfigPatchError\n * later in mergeLayout.\n */\nconst targetSubdirSchema = z\n .string()\n .min(1)\n .refine((s) => !path.isAbsolute(s), {\n message: 'targetSubdir must be relative (no leading `/` or drive letter)',\n })\n .refine((s) => !s.split(/[\\\\/]/).some((seg) => seg === '..'), {\n message: 'targetSubdir cannot traverse upward (`..` segment forbidden)',\n });\n\nconst assetLayoutSchema = z.object({\n targetSubdir: targetSubdirSchema.optional(),\n flatten: z.boolean().optional(),\n include: z.array(z.string().min(1)).optional(),\n exclude: z.array(z.string().min(1)).optional(),\n ext: z.string().min(1).optional(),\n});\n\nconst adapterOverrideSchema = z.union([\n z.boolean(),\n z\n .object({\n // partialRecord: zod v4 z.record(enum,…) is exhaustive by default;\n // partialRecord lets each key be optional, which is what users expect.\n layout: z.partialRecord(assetTypeSchema, assetLayoutSchema).optional(),\n })\n .strict(),\n]);\n\nconst adapterNameSchema = z.enum(\n Object.keys(adapterRegistry) as [AdapterName, ...AdapterName[]],\n);\n\n/**\n * Strict closed-world schema. Unknown root keys, unknown adapter names, and\n * unknown asset types all surface as friendly zod errors.\n */\nconst configSchema = z\n .object({\n include: z.array(assetTypeSchema).optional(),\n exclude: z.array(assetTypeSchema).optional(),\n adapters: z.partialRecord(adapterNameSchema, adapterOverrideSchema).optional(),\n })\n .strict();\n\ntype ParsedConfig = z.infer<typeof configSchema>;\n\nexport class ConfigParseError extends AgentlinkError {\n readonly issues: ReadonlyArray<{ path: string; message: string }>;\n\n constructor(filePath: string, issues: ReadonlyArray<{ path: string; message: string }>) {\n const lines = issues.map(({ path: p, message }) => ` at ${p || '<root>'}: ${message}`);\n super(`invalid agentlink config at ${filePath}\\n${lines.join('\\n')}`);\n this.issues = issues;\n }\n}\n\nexport type ConfigOrigin = 'flag' | 'env' | 'cwd-ancestor' | 'home-global' | 'defaults';\n\nexport interface ResolveConfigOptions {\n /** `--config` flag value. */\n config?: string;\n /** Override `process.cwd()`. */\n cwd?: string;\n /** Override `os.homedir()`. */\n home?: string;\n /** Override `process.env`. */\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface ConfigLocation {\n /** Absolute path that was loaded — `null` if no config file exists. */\n path: string | null;\n origin: ConfigOrigin;\n}\n\nexport interface LoadConfigOptions extends ResolveConfigOptions {\n /** Absolute path to the source `.agents/` — only used to populate ResolvedConfig. */\n source: string;\n}\n\n/**\n * Read + validate + merge the config. Returns a fully resolved view that is\n * safe to feed into command implementations.\n */\nexport async function loadConfig(opts: LoadConfigOptions): Promise<ResolvedConfig> {\n const location = await resolveConfigPath(opts);\n const userConfig = location.path ? await readUserConfig(location.path) : ({} as ParsedConfig);\n\n const adapters = mergeAdapters(userConfig.adapters);\n const assetTypes = resolveAssetTypes(userConfig.include, userConfig.exclude);\n\n return {\n source: opts.source,\n adapters,\n assetTypes,\n configPath: location.path,\n configOrigin: location.origin,\n };\n}\n\n/**\n * Resolve the location of the config file (independent of source). Pure with\n * respect to everything except `fs.stat` — easy to unit-test.\n */\nexport async function resolveConfigPath(opts: ResolveConfigOptions = {}): Promise<ConfigLocation> {\n const cwd = opts.cwd ?? process.cwd();\n const home = opts.home ?? os.homedir();\n const env = opts.env ?? process.env;\n\n if (opts.config !== undefined && opts.config !== '') {\n const abs = await assertConfigFile(expandTilde(opts.config, home), '--config', cwd);\n return { path: abs, origin: 'flag' };\n }\n\n const envValue = env[ENV_CONFIG_KEY];\n if (envValue !== undefined && envValue !== '') {\n const abs = await assertConfigFile(expandTilde(envValue, home), `$${ENV_CONFIG_KEY}`, cwd);\n return { path: abs, origin: 'env' };\n }\n\n const ancestor = await searchAncestors(path.resolve(cwd), home);\n if (ancestor) {\n return { path: ancestor, origin: 'cwd-ancestor' };\n }\n\n const homeConfig = path.join(home, CONFIG_FILENAME);\n if (await isFile(homeConfig)) {\n return { path: homeConfig, origin: 'home-global' };\n }\n\n return { path: null, origin: 'defaults' };\n}\n\n/**\n * Walk up the directory tree from `start`, looking for `agentlink.config.json`.\n * Stops as soon as we leave the home directory (or hit the filesystem root) so\n * configs from unrelated trees never leak in.\n */\nasync function searchAncestors(start: string, home: string): Promise<string | null> {\n const homeAbs = path.resolve(home);\n let current = start;\n while (true) {\n if (current === homeAbs) {\n // Don't probe `~/agentlink.config.json` here — it has its own dedicated\n // tier so the diagnostic origin reads as \"home-global\", not \"ancestor\".\n return null;\n }\n\n const candidate = path.join(current, CONFIG_FILENAME);\n if (await isFile(candidate)) return candidate;\n\n const parent = path.dirname(current);\n if (parent === current) return null; // hit filesystem root\n\n // Stop ascending if we are about to leave the home tree (only when start\n // was inside home). If start was outside home all along, keep walking up.\n if (isInside(current, homeAbs) && !isInside(parent, homeAbs) && parent !== homeAbs) {\n return null;\n }\n current = parent;\n }\n}\n\nasync function assertConfigFile(input: string, flag: string, cwd: string): Promise<string> {\n const abs = path.isAbsolute(input) ? input : path.resolve(cwd, input);\n const stat = await fs.stat(abs).catch(() => null);\n if (!stat) throw new InvalidPathError(flag, abs, 'missing');\n if (!stat.isFile()) throw new InvalidPathError(flag, abs, 'not-a-file');\n return abs;\n}\n\nasync function isFile(p: string): Promise<boolean> {\n const stat = await fs.stat(p).catch(() => null);\n return stat?.isFile() ?? false;\n}\n\nfunction expandTilde(p: string, home: string): string {\n if (p === '~') return home;\n if (p.startsWith('~/')) return path.join(home, p.slice(2));\n return p;\n}\n\nfunction isInside(child: string, parent: string): boolean {\n const c = path.resolve(child);\n const p = path.resolve(parent);\n if (c === p) return true;\n const rel = path.relative(p, c);\n return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);\n}\n\n/**\n * Read the *raw* user config and report which adapter names the user\n * explicitly opted into (i.e. anything not literally `false`). Best-effort —\n * returns an empty set on any IO/parse error so callers (e.g. doctor) can\n * fall back gracefully without hiding the real validation error from the\n * primary {@link loadConfig} pipeline.\n *\n * Centralised here so the JSONC tolerance stays consistent with `loadConfig`\n * (same comment + trailing-comma stripping) — earlier drafts had doctor.ts\n * hand-roll its own regex, which was both wrong on URLs and inconsistent\n * with the loader.\n */\nexport async function readExplicitAdapters(\n configPath: string | null,\n): Promise<ReadonlySet<string>> {\n if (!configPath) return new Set();\n const raw = await fs.readFile(configPath, 'utf8').catch(() => null);\n if (raw === null) return new Set();\n try {\n const parsed = JSON.parse(stripJsonComments(raw, { trailingCommas: true })) as {\n adapters?: Record<string, unknown>;\n };\n if (!parsed.adapters) return new Set();\n return new Set(Object.keys(parsed.adapters).filter((k) => parsed.adapters?.[k] !== false));\n } catch {\n return new Set();\n }\n}\n\nasync function readUserConfig(filePath: string): Promise<ParsedConfig> {\n const raw = await fs.readFile(filePath, 'utf8').catch((err: NodeJS.ErrnoException) => {\n if (err.code === 'ENOENT') return null;\n throw err;\n });\n if (raw === null) return {};\n\n let parsed: unknown;\n try {\n // JSONC: tolerate `//` and `/* */` comments plus trailing commas. The\n // template ships annotated so users can discover schema by reading.\n parsed = JSON.parse(stripJsonComments(raw, { trailingCommas: true }));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new ConfigParseError(filePath, [{ path: '', message: `not valid JSON: ${message}` }]);\n }\n\n const result = configSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.map((seg) => String(seg)).join('.'),\n message: issue.message,\n }));\n throw new ConfigParseError(filePath, issues);\n }\n return result.data;\n}\n\n/**\n * Apply user overrides to built-in adapter presets, returning only the adapters\n * that are enabled. Disabled adapters (`false` override) drop out entirely;\n * adapters not mentioned in user config keep their preset.\n */\nfunction mergeAdapters(\n overrides: ParsedConfig['adapters'] | undefined,\n): readonly Adapter[] {\n const result: Adapter[] = [];\n\n for (const [name, preset] of Object.entries(adapterRegistry) as Array<[AdapterName, Adapter]>) {\n const override = overrides?.[name];\n\n if (override === false) continue;\n\n if (override === undefined || override === true) {\n result.push(preset);\n continue;\n }\n\n result.push({\n name: preset.name,\n rootDir: preset.rootDir,\n layout: mergeLayout(preset.layout, override.layout),\n });\n }\n\n return result;\n}\n\n/**\n * Shape of a single `layout[<assetType>]` entry as it comes out of zod parse.\n * Differs from `Partial<AssetLayout>` because `exactOptionalPropertyTypes`\n * distinguishes \"may be omitted\" from \"may be `undefined`\".\n */\ninterface LayoutPatchValue {\n targetSubdir?: string | undefined;\n flatten?: boolean | undefined;\n include?: string[] | undefined;\n exclude?: string[] | undefined;\n ext?: string | undefined;\n}\n\ntype LayoutPatch = Partial<Record<AssetType, LayoutPatchValue>>;\n\nfunction mergeLayout(\n base: Adapter['layout'],\n patch: LayoutPatch | undefined,\n): Adapter['layout'] {\n if (!patch) return base;\n\n const merged: Adapter['layout'] = { ...base };\n for (const key of Object.keys(patch) as AssetType[]) {\n const value = patch[key];\n if (value === undefined) continue;\n\n const baseLayout = base[key];\n if (baseLayout === undefined || baseLayout === false) {\n const targetSubdir = value.targetSubdir;\n if (targetSubdir === undefined) {\n throw new ConfigPatchError(\n `adapter override for \"${key}\" needs a targetSubdir because the built-in adapter does not define one`,\n );\n }\n merged[key] = compactLayout(targetSubdir, value);\n continue;\n }\n merged[key] = mergeIntoBase(baseLayout, value);\n }\n return merged;\n}\n\n/** Strip `undefined` so the result respects exactOptionalPropertyTypes. */\nfunction compactLayout(targetSubdir: string, v: LayoutPatchValue): AssetLayout {\n const out: { -readonly [K in keyof AssetLayout]: AssetLayout[K] } = { targetSubdir };\n if (v.flatten !== undefined) out.flatten = v.flatten;\n if (v.include !== undefined) out.include = v.include;\n if (v.exclude !== undefined) out.exclude = v.exclude;\n if (v.ext !== undefined) out.ext = v.ext;\n return out;\n}\n\nfunction mergeIntoBase(base: AssetLayout, patch: LayoutPatchValue): AssetLayout {\n const out: { -readonly [K in keyof AssetLayout]: AssetLayout[K] } = { ...base };\n if (patch.targetSubdir !== undefined) out.targetSubdir = patch.targetSubdir;\n if (patch.flatten !== undefined) out.flatten = patch.flatten;\n if (patch.include !== undefined) out.include = patch.include;\n if (patch.exclude !== undefined) out.exclude = patch.exclude;\n if (patch.ext !== undefined) out.ext = patch.ext;\n return out;\n}\n\nclass ConfigPatchError extends AgentlinkError {}\n\n/**\n * Compute the set of asset types that should participate in this run.\n * `include` defaults to {@link DEFAULT_INCLUDE}; `exclude` is always a hard\n * subtraction applied last.\n */\nfunction resolveAssetTypes(\n include: readonly AssetType[] | undefined,\n exclude: readonly AssetType[] | undefined,\n): ReadonlySet<string> {\n const candidates = new Set<string>(include ?? DEFAULT_INCLUDE);\n for (const denied of exclude ?? []) {\n candidates.delete(denied);\n }\n return candidates;\n}\n\nexport const __test__ = {\n ASSET_TYPE_SET,\n CONFIG_FILENAME,\n};\n","/**\n * Domain-specific error types thrown by agentlink core modules.\n *\n * Using a small class hierarchy (rather than plain `Error` with codes) so the\n * CLI layer can `instanceof`-match and render targeted help text without\n * coupling to error message strings.\n */\n\nexport class AgentlinkError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\n/**\n * No `.agents/` directory could be located via any resolution channel.\n * `searched` records every probe and its outcome so the CLI can render a\n * \"Searched: …\" diagnostic block.\n */\nexport class SourceNotFoundError extends AgentlinkError {\n readonly searched: ReadonlyArray<{ origin: string; status: string }>;\n\n constructor(searched: ReadonlyArray<{ origin: string; status: string }>) {\n super('no source .agents/ directory found');\n this.searched = searched;\n }\n}\n\n/**\n * A user-supplied path (via flag or env) is missing or has the wrong shape.\n * `flag` is the originating channel name as the user would recognize it\n * (e.g. `--source`, `AGENTLINK_SOURCE`).\n */\nexport class InvalidPathError extends AgentlinkError {\n readonly flag: string;\n readonly path: string;\n readonly reason: 'missing' | 'not-a-directory' | 'not-a-file';\n\n constructor(flag: string, p: string, reason: 'missing' | 'not-a-directory' | 'not-a-file') {\n super(InvalidPathError.message(flag, p, reason));\n this.flag = flag;\n this.path = p;\n this.reason = reason;\n }\n\n private static message(\n flag: string,\n p: string,\n reason: 'missing' | 'not-a-directory' | 'not-a-file',\n ): string {\n if (reason === 'missing') return `${flag} does not exist: ${p}`;\n if (reason === 'not-a-directory') return `${flag} is not a directory: ${p}`;\n return `${flag} is not a file: ${p}`;\n }\n}\n\n/** Two mutually exclusive options were supplied together (e.g. --target and --user). */\nexport class ConflictError extends AgentlinkError {}\n","/**\n * Asset categories agentlink knows how to symlink.\n * Extend cautiously — adding a value here is a breaking change for adapters.\n */\nexport type AssetType = 'skills' | 'rules' | 'commands' | 'prompts' | 'templates' | 'agents';\n\n/**\n * Per-asset-type projection rule. Sentinel `false` (\"skip this asset entirely\")\n * is represented at the {@link Adapter.layout} value level rather than inside\n * AssetLayout, so that `keyof AssetLayout` stays meaningful for code that maps\n * over its fields.\n */\nexport interface AssetLayout {\n /** Subdirectory under the adapter's `rootDir`, e.g. 'skills' / 'commands'. */\n targetSubdir: string;\n /**\n * If true, source files in nested subdirectories are flattened into the target\n * directory using `__` as the path separator (e.g. `git/commit.md` → `git__commit.md`).\n * Use this for agents that don't support nested command/rule directories (e.g. cursor).\n */\n flatten?: boolean;\n /** Glob patterns (relative to source asset dir) to include. Defaults to all files. */\n include?: readonly string[];\n /** Glob patterns to exclude. Applied after `include`. */\n exclude?: readonly string[];\n /** Force a specific file extension on the link target (e.g. `.mdc` for cursor rules). */\n ext?: string;\n /**\n * Custom rename hook. Receives the relative source path; returns the target file name\n * (without `targetSubdir`). v1: only used by built-in adapters; not exposed via JSON config.\n */\n rename?: (relPath: string) => string;\n}\n\n/** A layout slot is either a real layout or `false` to mean \"skip this asset type\". */\nexport type LayoutSlot = AssetLayout | false;\n\n/**\n * Built-in or user-supplied adapter describing how to project `.agents/` into one AI agent's\n * directory layout.\n */\nexport interface Adapter {\n /** Stable identifier, e.g. 'cursor' / 'claude' / 'codebuddy'. */\n name: string;\n /** Directory under target root, e.g. '.cursor'. */\n rootDir: string;\n /** Per-asset-type projection rules. */\n layout: Partial<Record<AssetType, LayoutSlot>>;\n}\n\n/**\n * Per-adapter overrides as written by the user in JSON config.\n * Value semantics:\n * - `true` : enable built-in adapter with default layout\n * - `false` : disable adapter even if its rootDir exists\n * - `{ layout }` : enable + deep-merge layout overrides onto built-in defaults\n */\nexport type AdapterOverride =\n | boolean\n | { layout?: Partial<Record<AssetType, AssetLayout>> };\n\n/**\n * User-facing JSON configuration loaded from `<source>/agentlink.config.json`.\n * Every field is optional; sensible defaults apply.\n */\nexport interface AgentlinkConfig {\n /**\n * Asset type allowlist. When omitted, falls back to the built-in default\n * (`['skills', 'rules', 'commands']`). Use this to opt into less-common\n * asset types like `prompts` / `templates` / `agents`.\n */\n include?: readonly string[];\n /**\n * Asset type denylist applied AFTER `include`. Always wins over `include`,\n * useful for temporarily silencing one type without rewriting the allowlist.\n */\n exclude?: readonly string[];\n /** Per-adapter overrides; see {@link AdapterOverride}. */\n adapters?: Record<string, AdapterOverride>;\n}\n\n/**\n * Result of {@link loadConfig}: raw JSON has been validated, defaults filled,\n * and built-in adapter presets merged with user overrides. This is what every\n * runtime command (sync/clean/unlink/doctor) consumes.\n */\nexport interface ResolvedConfig {\n /** Absolute path to the source `.agents/` directory. */\n source: string;\n /** Adapters that are enabled, with overrides already applied to layouts. */\n adapters: readonly Adapter[];\n /** Asset type names that should participate in this run. */\n assetTypes: ReadonlySet<string>;\n /** Absolute path of the config file actually loaded; null when no file existed. */\n configPath: string | null;\n /** Where the config came from. `'defaults'` means no file was found. */\n configOrigin: 'flag' | 'env' | 'cwd-ancestor' | 'home-global' | 'defaults';\n}\n\n/** Built-in default for {@link AgentlinkConfig.include}. */\nexport const DEFAULT_INCLUDE = ['skills', 'rules', 'commands'] as const;\n","/**\n * Locate the source `.agents/` directory and the destination root for a sync run.\n *\n * Two independent resolution chains share the same {@link ResolveOptions} and\n * produce a {@link ResolvedPaths} record. Both chains are pure with respect to\n * everything except `fs.stat` probes, so they are easy to unit-test with a\n * tmpdir + injected `cwd / home / env`.\n *\n * Source chain (highest first):\n * 1. `--source <path>` flag\n * 2. `AGENTLINK_SOURCE` environment variable\n * 3. `<cwd>/.agents/` — local-first so team repos override personal globals\n * 4. `<home>/.agents/` — recommended default for the npx workflow\n * 5. (none) → SourceNotFoundError\n *\n * Destination chain (highest first):\n * 1. `--target <path>` flag\n * 2. `--user` → home (user-level install target)\n * 3. (default) cwd\n */\n\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { ConflictError, InvalidPathError, SourceNotFoundError } from './errors.js';\n\nexport type SourceOrigin = 'flag' | 'env' | 'cwd-local' | 'home-global';\nexport type DestOrigin = 'flag' | 'user' | 'cwd';\n\nexport interface ResolveOptions {\n /** `--source` flag value. */\n source?: string;\n /** `--target` flag value. */\n target?: string;\n /** `--user` flag value — link into $HOME. */\n user?: boolean;\n /** Override `process.cwd()`. */\n cwd?: string;\n /** Override `os.homedir()`. */\n home?: string;\n /** Override `process.env`. */\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface ResolvedPaths {\n source: string;\n sourceOrigin: SourceOrigin;\n destRoot: string;\n destOrigin: DestOrigin;\n}\n\nconst ENV_SOURCE_KEY = 'AGENTLINK_SOURCE';\nconst AGENTS_DIRNAME = '.agents';\n\n/**\n * Top-level entry. Resolves source and destination concurrently and emits a\n * Node warning if `destRoot` lives inside `source` (likely a self-link mistake).\n */\nexport async function resolvePaths(opts: ResolveOptions = {}): Promise<ResolvedPaths> {\n const [src, dst] = await Promise.all([resolveSource(opts), resolveDestRoot(opts)]);\n\n if (isInside(dst.path, src.path)) {\n process.emitWarning(\n `destination root \"${dst.path}\" lives inside source \"${src.path}\" — this may create self-references.`,\n { code: 'AGENTLINK_NESTED_DEST' },\n );\n }\n\n return {\n source: src.path,\n sourceOrigin: src.origin,\n destRoot: dst.path,\n destOrigin: dst.origin,\n };\n}\n\nexport async function resolveSource(\n opts: ResolveOptions = {},\n): Promise<{ path: string; origin: SourceOrigin }> {\n const cwd = opts.cwd ?? process.cwd();\n const home = opts.home ?? os.homedir();\n const env = opts.env ?? process.env;\n\n if (opts.source !== undefined && opts.source !== '') {\n const abs = await assertDir(expandTilde(opts.source, home), '--source', cwd);\n return { path: abs, origin: 'flag' };\n }\n\n const envValue = env[ENV_SOURCE_KEY];\n if (envValue !== undefined && envValue !== '') {\n const abs = await assertDir(expandTilde(envValue, home), `$${ENV_SOURCE_KEY}`, cwd);\n return { path: abs, origin: 'env' };\n }\n\n const local = path.join(cwd, AGENTS_DIRNAME);\n if (await isDir(local)) {\n return { path: local, origin: 'cwd-local' };\n }\n\n // Note: `homeAgents` is the source path under home; unrelated to the\n // destination `--user` flag (which maps the *target* root to home).\n const homeAgents = path.join(home, AGENTS_DIRNAME);\n if (await isDir(homeAgents)) {\n return { path: homeAgents, origin: 'home-global' };\n }\n\n throw new SourceNotFoundError([\n { origin: '--source flag', status: 'not provided' },\n { origin: `$${ENV_SOURCE_KEY}`, status: 'not set' },\n { origin: local, status: 'missing' },\n { origin: homeAgents, status: 'missing' },\n ]);\n}\n\nexport async function resolveDestRoot(\n opts: ResolveOptions = {},\n): Promise<{ path: string; origin: DestOrigin }> {\n const cwd = opts.cwd ?? process.cwd();\n const home = opts.home ?? os.homedir();\n\n if (opts.target !== undefined && opts.target !== '' && opts.user) {\n throw new ConflictError('--target and --user are mutually exclusive');\n }\n\n if (opts.target !== undefined && opts.target !== '') {\n const abs = await assertDir(expandTilde(opts.target, home), '--target', cwd);\n return { path: abs, origin: 'flag' };\n }\n\n if (opts.user) {\n return { path: home, origin: 'user' };\n }\n\n return { path: cwd, origin: 'cwd' };\n}\n\n/**\n * Expand a leading `~` or `~/` to the supplied home directory.\n * Intentionally narrower than shell tilde expansion (no `~user` lookup).\n */\nexport function expandTilde(p: string, home: string): string {\n if (p === '~') return home;\n if (p.startsWith('~/')) return path.join(home, p.slice(2));\n return p;\n}\n\nasync function assertDir(input: string, flag: string, cwd: string): Promise<string> {\n const abs = path.isAbsolute(input) ? input : path.resolve(cwd, input);\n const stat = await fs.stat(abs).catch(() => null);\n if (!stat) throw new InvalidPathError(flag, abs, 'missing');\n if (!stat.isDirectory()) throw new InvalidPathError(flag, abs, 'not-a-directory');\n return abs;\n}\n\nasync function isDir(p: string): Promise<boolean> {\n const stat = await fs.stat(p).catch(() => null);\n return stat?.isDirectory() ?? false;\n}\n\n/**\n * `true` iff `child` is inside `parent` (or equal).\n *\n * Compares lexically — neither path is realpath-resolved, matching the rest of\n * the resolver's \"preserve symlinks\" stance.\n */\nexport function isInside(child: string, parent: string): boolean {\n const c = path.resolve(child);\n const p = path.resolve(parent);\n if (c === p) return true;\n const rel = path.relative(p, c);\n return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);\n}\n","/**\n * `agentlink doctor` — read-only health check for the current source/destination\n * pair. Designed to answer \"would `agentlink sync` do something useful here,\n * and would it run cleanly?\" without writing anything to disk.\n *\n * Reports four sections:\n * 1. Adapter availability (rootDir present / config-disabled / missing)\n * 2. Link plan (count + any target-path collisions)\n * 3. Existing links (dangling, orphan)\n * 4. Aggregate verdict — sets `process.exitCode = 1` only on errors;\n * warnings are non-blocking so doctor can be wired into pre-commit\n * hooks without false positives.\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport pc from 'picocolors';\n\nimport { adapterRegistry } from '../adapters/index.js';\nimport { readExplicitAdapters } from '../core/config.js';\nimport { buildLinkPlans } from '../core/layout.js';\nimport { findManagedLinks, findStaleLinks } from '../core/linker.js';\nimport {\n enumerateAllAdapterRoots,\n enumerateTargetDirs,\n loadContext,\n printHeader,\n type CommandFlags,\n} from './_shared.js';\n\nexport async function runDoctor(flags: CommandFlags = {}): Promise<void> {\n const ctx = await loadContext(flags);\n printHeader(ctx, 'doctor', flags);\n\n let warnings = 0;\n let errors = 0;\n\n // Read the *raw* user config so we can distinguish \"user explicitly opted\n // into this adapter\" (warn when its rootDir is missing) from \"default\n // pickup\" (dim when missing — most users don't have all three agents).\n const explicitlyEnabled = await readExplicitAdapters(ctx.config.configPath);\n\n // ── 1. Adapter availability ──────────────────────────────────────────────\n console.log(pc.bold('Adapters'));\n for (const [name, preset] of Object.entries(adapterRegistry)) {\n const root = path.join(ctx.paths.destRoot, preset.rootDir);\n const stat = await fs.stat(root).catch(() => null);\n if (stat?.isDirectory()) {\n console.log(\n ` ${pc.green('✓')} ${name.padEnd(11)} ${pc.dim(preset.rootDir + '/')} ${pc.dim('(active)')}`,\n );\n } else if (explicitlyEnabled.has(name)) {\n warnings++;\n console.log(\n ` ${pc.yellow('⚠')} ${name.padEnd(11)} ${pc.dim(preset.rootDir + '/')} ${pc.yellow('rootDir not found — sync will skip')}`,\n );\n } else {\n console.log(\n ` ${pc.dim('—')} ${name.padEnd(11)} ${pc.dim(preset.rootDir + '/')} ${pc.dim('not in use on this machine')}`,\n );\n }\n }\n console.log('');\n\n // ── 2. Link plan ────────────────────────────────────────────────────────\n const { plans, collisions } = await buildLinkPlans({\n source: ctx.paths.source,\n destRoot: ctx.paths.destRoot,\n config: ctx.config,\n });\n\n console.log(pc.bold('Link plan'));\n console.log(` ${plans.length} plan${plans.length === 1 ? '' : 's'}`);\n if (collisions.length === 0) {\n console.log(` ${pc.green('✓')} no collisions`);\n } else {\n warnings += collisions.length;\n console.log(` ${pc.yellow('⚠')} ${collisions.length} collision(s):`);\n for (const c of collisions) {\n console.log(` ${pc.dim(c.adapterName + '/' + c.assetType)} ${c.target}`);\n for (const s of c.sources) console.log(` ${pc.dim('←')} ${s}`);\n }\n }\n console.log('');\n\n // ── 3. Existing links ───────────────────────────────────────────────────\n const targetDirs = enumerateTargetDirs(ctx).map((t) => t.targetDir);\n const stale = (await Promise.all(targetDirs.map(findStaleLinks))).flat();\n\n // Recursive sweep — orphans can live in asset subdirs that the current\n // config no longer mentions, AND in adapter rootDirs the user has just\n // disabled. Walk every built-in adapter rootDir that exists, ignoring the\n // current `ctx.config.adapters` gate so doctor stays comprehensive.\n const allRoots = await enumerateAllAdapterRoots(ctx.paths.destRoot);\n const managed = (\n await Promise.all(\n allRoots.map((r) => findManagedLinks(r.rootDir, ctx.paths.source, { recursive: true })),\n )\n ).flat();\n const staleSet = new Set(stale);\n const planTargets = new Set(plans.map((p) => p.target));\n const orphans = managed.filter((p) => !planTargets.has(p) && !staleSet.has(p));\n\n console.log(pc.bold('Existing links'));\n if (stale.length === 0 && orphans.length === 0) {\n console.log(` ${pc.green('✓')} all clear`);\n } else {\n if (stale.length > 0) {\n warnings += stale.length;\n console.log(\n ` ${pc.yellow('⚠')} ${stale.length} dangling ${pc.dim('(run `agentlink clean` to drop):')}`,\n );\n for (const p of stale) console.log(` ${p}`);\n }\n if (orphans.length > 0) {\n warnings += orphans.length;\n console.log(\n ` ${pc.yellow('⚠')} ${orphans.length} orphan ${pc.dim('(linked into source but not in current plan; `unlink` then `sync` will rebuild):')}`,\n );\n for (const p of orphans) console.log(` ${p}`);\n }\n }\n console.log('');\n\n // ── 4. Verdict ──────────────────────────────────────────────────────────\n if (warnings + errors === 0) {\n console.log(pc.green('Healthy'));\n return;\n }\n const parts: string[] = [];\n if (warnings > 0) parts.push(pc.yellow(`${warnings} warning${warnings === 1 ? '' : 's'}`));\n if (errors > 0) parts.push(pc.red(`${errors} error${errors === 1 ? '' : 's'}`));\n console.log(`Issues: ${parts.join(', ')}`);\n if (errors > 0) process.exitCode = 1;\n}\n\n","/**\n * Translate a {@link ResolvedConfig} into a flat list of {@link LinkPlan}s\n * that the linker module can execute one symlink at a time.\n *\n * For each `(adapter, assetType)` pair this module:\n * - resolves the source asset directory under `<source>/<assetType>`\n * - resolves the target directory under `<destRoot>/<rootDir>/<targetSubdir>`\n * - applies `flatten` / `ext` / `rename` / `include` / `exclude`\n * - detects target-path collisions and surfaces them as {@link Conflict}s\n *\n * Conflict policy is intentionally _diagnostic only_ here — the caller decides\n * whether to warn (default) or fail (`--strict`).\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport picomatch from 'picomatch';\n\nimport type {\n Adapter,\n AssetLayout,\n AssetType,\n ResolvedConfig,\n} from './types.js';\n\nexport interface LinkPlan {\n /** Absolute source path (file when flattened, file-or-directory otherwise). */\n source: string;\n /** Absolute target path the symlink will live at. */\n target: string;\n /** Asset type bucket this plan came from. */\n assetType: AssetType;\n /** Name of the adapter that produced this plan. */\n adapterName: string;\n /** True when source enumeration recursed into nested files. */\n flattened: boolean;\n}\n\nexport interface Conflict {\n /** Target path that two or more plans claim. */\n target: string;\n /** Source paths competing for the same target. */\n sources: readonly string[];\n /** Asset type / adapter the collision happened in (for diagnostics). */\n adapterName: string;\n assetType: AssetType;\n}\n\nexport interface BuildResult {\n /** Plans whose targets are unique. Safe to apply. */\n plans: readonly LinkPlan[];\n /** Plans dropped because their target collided with another (default policy). */\n collisions: readonly Conflict[];\n}\n\nexport interface BuildOptions {\n /** Absolute path to the source `.agents/` directory. */\n source: string;\n /** Absolute path to the destination root. */\n destRoot: string;\n /** Resolved config (loaded via {@link loadConfig}). */\n config: ResolvedConfig;\n}\n\n/**\n * Build the flat plan list. Pure function — only reads the filesystem; never\n * writes. Safe to call from `--dry-run`.\n */\nexport async function buildLinkPlans(opts: BuildOptions): Promise<BuildResult> {\n const { source, destRoot, config } = opts;\n const draft: LinkPlan[] = [];\n\n for (const adapter of config.adapters) {\n for (const assetType of config.assetTypes as ReadonlySet<AssetType>) {\n const slot = adapter.layout[assetType];\n if (slot === undefined || slot === false) continue;\n\n const sourceAssetDir = path.join(source, assetType);\n if (!(await isDir(sourceAssetDir))) continue;\n\n const targetAssetDir = path.join(destRoot, adapter.rootDir, slot.targetSubdir);\n const planned = slot.flatten\n ? await planFlattened(sourceAssetDir, targetAssetDir, slot, adapter, assetType)\n : await planShallow(sourceAssetDir, targetAssetDir, slot, adapter, assetType);\n\n draft.push(...planned);\n }\n }\n\n return splitByCollision(draft);\n}\n\n/* ────────────────────────── flatten = true ───────────────────────────── */\n\nasync function planFlattened(\n sourceAssetDir: string,\n targetAssetDir: string,\n layout: AssetLayout,\n adapter: Adapter,\n assetType: AssetType,\n): Promise<LinkPlan[]> {\n const files = await walkFiles(sourceAssetDir);\n const includeMatch = makeIncludeMatcher(layout.include);\n const excludeMatch = makeExcludeMatcher(layout.exclude);\n const out: LinkPlan[] = [];\n\n for (const abs of files) {\n const rel = path.relative(sourceAssetDir, abs);\n if (!includeMatch(rel)) continue;\n if (excludeMatch(rel)) continue;\n\n const targetName = applyRename(rel, layout, /* flatten */ true);\n out.push({\n source: abs,\n target: path.join(targetAssetDir, targetName),\n adapterName: adapter.name,\n assetType,\n flattened: true,\n });\n }\n return out;\n}\n\n/* ────────────────────────── flatten = false (default) ───────────────── */\n\nasync function planShallow(\n sourceAssetDir: string,\n targetAssetDir: string,\n layout: AssetLayout,\n adapter: Adapter,\n assetType: AssetType,\n): Promise<LinkPlan[]> {\n const items = await listFirstLevel(sourceAssetDir);\n const includeMatch = makeIncludeMatcher(layout.include);\n const excludeMatch = makeExcludeMatcher(layout.exclude);\n const out: LinkPlan[] = [];\n\n for (const entry of items) {\n if (!includeMatch(entry.name)) continue;\n if (excludeMatch(entry.name)) continue;\n\n const targetName = applyRename(entry.name, layout, /* flatten */ false);\n out.push({\n source: path.join(sourceAssetDir, entry.name),\n target: path.join(targetAssetDir, targetName),\n adapterName: adapter.name,\n assetType,\n flattened: false,\n });\n }\n return out;\n}\n\n/* ────────────────────────── Naming helpers ──────────────────────────── */\n\nconst PATH_SEPARATOR_RE = /[\\\\/]/g;\n\nfunction applyRename(relPath: string, layout: AssetLayout, flatten: boolean): string {\n if (layout.rename) return layout.rename(relPath);\n\n let name = flatten ? relPath.replace(PATH_SEPARATOR_RE, '__') : relPath;\n if (layout.ext) {\n const ext = path.extname(name);\n name = ext ? name.slice(0, -ext.length) + layout.ext : name + layout.ext;\n }\n return name;\n}\n\n/* ────────────────────────── Filesystem walkers ──────────────────────── */\n\nasync function walkFiles(root: string): Promise<string[]> {\n const out: string[] = [];\n await (async function descend(dir: string): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n // Hidden entries (`.gitkeep`, `.DS_Store`, etc.) are placeholder/system\n // files, never real assets. Mirrors `listFirstLevel` behaviour.\n if (entry.name.startsWith('.')) continue;\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await descend(full);\n } else if (entry.isFile() || entry.isSymbolicLink()) {\n out.push(full);\n }\n }\n })(root);\n return out;\n}\n\nasync function listFirstLevel(root: string): Promise<readonly { name: string; isDir: boolean }[]> {\n const entries = await fs.readdir(root, { withFileTypes: true });\n return entries\n .filter((e) => !e.name.startsWith('.'))\n .map((e) => ({ name: e.name, isDir: e.isDirectory() }));\n}\n\nasync function isDir(p: string): Promise<boolean> {\n const stat = await fs.stat(p).catch(() => null);\n return stat?.isDirectory() ?? false;\n}\n\n/* ────────────────────────── Glob matchers ───────────────────────────── */\n\n/**\n * `include` matcher: empty/missing list means \"everything passes\".\n */\nfunction makeIncludeMatcher(patterns: readonly string[] | undefined): (rel: string) => boolean {\n if (!patterns || patterns.length === 0) return () => true;\n const fns = patterns.map((p) => picomatch(p, { dot: true }));\n return (rel) => fns.some((fn) => fn(rel));\n}\n\n/**\n * `exclude` matcher: empty/missing list means \"nothing is excluded\".\n */\nfunction makeExcludeMatcher(patterns: readonly string[] | undefined): (rel: string) => boolean {\n if (!patterns || patterns.length === 0) return () => false;\n const fns = patterns.map((p) => picomatch(p, { dot: true }));\n return (rel) => fns.some((fn) => fn(rel));\n}\n\n/* ────────────────────────── Collision detection ─────────────────────── */\n\nfunction splitByCollision(plans: readonly LinkPlan[]): BuildResult {\n const byTarget = new Map<string, LinkPlan[]>();\n for (const plan of plans) {\n const bucket = byTarget.get(plan.target);\n if (bucket) bucket.push(plan);\n else byTarget.set(plan.target, [plan]);\n }\n\n const accepted: LinkPlan[] = [];\n const collisions: Conflict[] = [];\n for (const [target, bucket] of byTarget) {\n if (bucket.length === 1) {\n accepted.push(bucket[0]!);\n continue;\n }\n const head = bucket[0]!;\n collisions.push({\n target,\n sources: bucket.map((p) => p.source),\n adapterName: head.adapterName,\n assetType: head.assetType,\n });\n }\n return { plans: accepted, collisions };\n}\n","/**\n * `agentlink init` — interactively scaffold a `.agents/` directory and run\n * sync once so the user gets working symlinks immediately.\n *\n * Three knobs the user can tweak via prompts:\n * 1. **Target location** (only when neither `--local` nor `--source` is set):\n * `~/.agents` (recommended) vs `./.agents`.\n * 2. **Asset types** to include — multi-select; defaults to skills/rules/commands.\n * 3. **Adapters** to enable — multi-select; defaults to all three.\n *\n * Non-interactive degrade rules:\n * - `--yes / -y` ⇒ everything defaults, no prompts\n * - stdin not a TTY ⇒ same (suitable for CI / piping)\n * - any flag that already answers a question ⇒ skip that prompt\n *\n * Refuses to clobber a non-empty target unless `--force`.\n */\n\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport pc from 'picocolors';\nimport prompts from 'prompts';\n\nimport { adapterRegistry, type AdapterName } from '../adapters/index.js';\nimport { AgentlinkError } from '../core/errors.js';\nimport { runSync } from './sync.js';\nimport type { CommandFlags } from './_shared.js';\n\nexport interface InitFlags extends CommandFlags {\n local?: boolean;\n yes?: boolean;\n}\n\nexport class InitTargetExistsError extends AgentlinkError {\n constructor(targetPath: string) {\n super(`refusing to scaffold into a non-empty directory: ${targetPath}\\n use --force to merge into the existing files`);\n }\n}\n\nconst ALL_ASSET_TYPES = ['skills', 'rules', 'commands', 'prompts', 'templates', 'agents'] as const;\nconst DEFAULT_ASSET_TYPES = ['skills', 'rules', 'commands'] as const;\nconst ALL_ADAPTER_NAMES = Object.keys(adapterRegistry) as AdapterName[];\n\nconst HERE = path.dirname(fileURLToPath(import.meta.url));\nconst TEMPLATE_CANDIDATES = [\n path.resolve(HERE, '..', 'templates', 'v1'),\n path.resolve(HERE, '..', '..', 'templates', 'v1'),\n];\n\nasync function findTemplatesDir(): Promise<string> {\n for (const candidate of TEMPLATE_CANDIDATES) {\n const stat = await fs.stat(candidate).catch(() => null);\n if (stat?.isDirectory()) return candidate;\n }\n throw new AgentlinkError(\n `agentlink templates dir not found; searched:\\n ${TEMPLATE_CANDIDATES.join('\\n ')}`,\n );\n}\n\ninterface InitAnswers {\n /** Where the source `.agents/` directory is created. */\n target: string;\n targetCategory: 'home' | 'cwd' | 'custom';\n /** Where the `agentlink.config.json` is written (independent of source). */\n configPath: string;\n configCategory: 'home' | 'cwd';\n include: readonly string[];\n adapters: readonly AdapterName[];\n}\n\nexport async function runInit(flags: InitFlags = {}): Promise<void> {\n const answers = await collectAnswers(flags);\n\n if (!flags.quiet) {\n console.log('');\n console.log(pc.bold('[agentlink] init'));\n console.log(` target: ${answers.target}`);\n console.log('');\n }\n\n const created = await scaffoldInto(answers, {\n force: flags.force ?? false,\n quiet: flags.quiet ?? false,\n dryRun: flags.dryRun ?? false,\n });\n if (!created.touched && !flags.quiet) {\n console.log(pc.dim(' no files written; .agents/ already populated'));\n }\n\n if (answers.targetCategory === 'custom' && !flags.quiet) {\n printNonDefaultLocationHint(answers.target);\n }\n\n if (!flags.quiet) console.log('');\n\n // In dry-run mode the source directory was never actually created, so a\n // follow-up sync would just throw on resolveSource. Tell the user what\n // a real run would have done and exit cleanly instead.\n if (flags.dryRun) {\n if (!flags.quiet) {\n console.log(\n pc.dim(\n ` (dry-run) would run \\`agentlink sync\\` against ${answers.target} after scaffolding`,\n ),\n );\n }\n return;\n }\n\n // Best-effort follow-up sync so the user gets immediate symlinks if they\n // already have `.cursor` / `.claude` / `.codebuddy` in cwd. The adapter\n // gate makes this a no-op when none exist.\n await runSync({\n source: answers.target,\n ...(flags.target !== undefined ? { target: flags.target } : {}),\n ...(flags.user !== undefined ? { user: flags.user } : {}),\n ...(flags.quiet !== undefined ? { quiet: flags.quiet } : {}),\n });\n}\n\n/* ────────────────────────── Prompt orchestration ─────────────────────── */\n\nasync function collectAnswers(flags: InitFlags): Promise<InitAnswers> {\n const skipPrompts = flags.yes || !process.stdin.isTTY;\n\n // 1) Target location\n let target: string;\n let targetCategory: InitAnswers['targetCategory'];\n if (flags.source !== undefined && flags.source !== '') {\n target = path.resolve(expandTilde(flags.source));\n targetCategory = categorizeTarget(target);\n } else if (flags.local) {\n target = path.resolve(process.cwd(), '.agents');\n targetCategory = 'cwd';\n } else if (skipPrompts) {\n target = path.resolve(os.homedir(), '.agents');\n targetCategory = 'home';\n } else {\n const choice = await ask<{ kind: 'home' | 'cwd' }>({\n type: 'select',\n name: 'kind',\n message: 'Where should agentlink put your .agents/ directory?',\n choices: [\n {\n title: 'Personal global (~/.agents)',\n description: 'Recommended — share assets across every project',\n value: 'home',\n },\n {\n title: 'Repo-local (./.agents)',\n description: 'Ship a curated .agents/ inside this repository',\n value: 'cwd',\n },\n ],\n initial: 0,\n });\n target =\n choice.kind === 'home'\n ? path.resolve(os.homedir(), '.agents')\n : path.resolve(process.cwd(), '.agents');\n targetCategory = choice.kind;\n }\n\n // 2) Asset types\n let include: readonly string[];\n if (skipPrompts) {\n include = DEFAULT_ASSET_TYPES;\n } else {\n const { values } = await ask<{ values: string[] }>({\n type: 'multiselect',\n name: 'values',\n message: 'Which asset types do you want to manage?',\n hint: 'space to toggle, enter to confirm',\n choices: ALL_ASSET_TYPES.map((name) => ({\n title: name,\n value: name,\n selected: (DEFAULT_ASSET_TYPES as readonly string[]).includes(name),\n })),\n min: 1,\n instructions: false,\n });\n include = values;\n }\n\n // 3) Adapters\n let adapters: readonly AdapterName[];\n if (skipPrompts) {\n adapters = ALL_ADAPTER_NAMES;\n } else {\n const { values } = await ask<{ values: AdapterName[] }>({\n type: 'multiselect',\n name: 'values',\n message: 'Which AI agents do you want to target?',\n hint: 'space to toggle, enter to confirm',\n choices: ALL_ADAPTER_NAMES.map((name) => ({\n title: name,\n value: name,\n selected: true,\n })),\n min: 1,\n instructions: false,\n });\n adapters = values;\n }\n\n // Decide where the config file lands. Config is decoupled from source —\n // whichever location a future `agentlink sync` is most likely to run from.\n const configCategory: InitAnswers['configCategory'] =\n targetCategory === 'cwd' ? 'cwd' : 'home';\n const configPath =\n configCategory === 'home'\n ? path.resolve(os.homedir(), 'agentlink.config.json')\n : path.resolve(process.cwd(), 'agentlink.config.json');\n\n return { target, targetCategory, configPath, configCategory, include, adapters };\n}\n\n/**\n * Wrapper that turns user-cancellation (Ctrl-C) into a thrown error rather\n * than letting `prompts` resolve with `{}` and silently producing an empty\n * config.\n */\nasync function ask<T extends Record<string, unknown>>(\n question: prompts.PromptObject,\n): Promise<T> {\n let cancelled = false;\n const answer = await prompts(question, {\n onCancel: () => {\n cancelled = true;\n return false;\n },\n });\n if (cancelled) throw new AgentlinkError('init cancelled');\n return answer as T;\n}\n\nfunction categorizeTarget(target: string): InitAnswers['targetCategory'] {\n const home = path.resolve(os.homedir(), '.agents');\n const cwd = path.resolve(process.cwd(), '.agents');\n if (target === home) return 'home';\n if (target === cwd) return 'cwd';\n return 'custom';\n}\n\nfunction expandTilde(p: string): string {\n if (p === '~') return os.homedir();\n if (p.startsWith('~/')) return path.join(os.homedir(), p.slice(2));\n return p;\n}\n\n/* ────────────────────────── Scaffolding ─────────────────────────────── */\n\ninterface ScaffoldResult {\n touched: boolean;\n}\n\ninterface ScaffoldOptions {\n force: boolean;\n quiet: boolean;\n /** When true: probe and report what would happen, never touch the filesystem. */\n dryRun: boolean;\n}\n\nasync function scaffoldInto(\n answers: InitAnswers,\n options: ScaffoldOptions,\n): Promise<ScaffoldResult> {\n const { force, quiet, dryRun } = options;\n const target = answers.target;\n const stat = await fs.stat(target).catch(() => null);\n if (stat && !stat.isDirectory()) {\n throw new AgentlinkError(`init target exists but is not a directory: ${target}`);\n }\n\n if (stat && !force) {\n const entries = await fs.readdir(target);\n if (entries.length > 0) throw new InitTargetExistsError(target);\n }\n\n if (!dryRun) {\n await fs.mkdir(target, { recursive: true });\n } else if (!stat && !quiet) {\n console.log(`${pc.dim('[DRY]')} would create ${target}/`);\n }\n\n const templatesDir = await findTemplatesDir();\n\n let written = 0;\n written += await copyStaticAssets(templatesDir, target, answers.include, quiet, dryRun);\n written += await writeConfigFile(answers, quiet, dryRun);\n return { touched: written > 0 };\n}\n\n/**\n * Copy README and `.gitkeep` placeholders for each user-selected asset type.\n * The config file is rendered separately by `writeConfigFile` because its\n * contents depend on the user's answers.\n */\nasync function copyStaticAssets(\n templatesDir: string,\n target: string,\n include: readonly string[],\n quiet: boolean,\n dryRun: boolean,\n): Promise<number> {\n let written = 0;\n\n // README — copy as-is if not already present.\n const readmeSrc = path.join(templatesDir, 'README.md');\n const readmeDest = path.join(target, 'README.md');\n if ((await fs.stat(readmeSrc).catch(() => null)) && !(await fs.stat(readmeDest).catch(() => null))) {\n if (dryRun) {\n if (!quiet) console.log(`${pc.dim('[DRY]')} would create ${readmeDest}`);\n } else {\n await fs.copyFile(readmeSrc, readmeDest);\n if (!quiet) console.log(`${pc.green('[NEW]')} ${readmeDest}`);\n }\n written++;\n }\n\n // Per-asset-type empty directories with a placeholder so they survive `git add`.\n for (const assetType of include) {\n const dir = path.join(target, assetType);\n const keep = path.join(dir, '.gitkeep');\n const keepExists = await fs.stat(keep).catch(() => null);\n if (keepExists) continue;\n\n if (dryRun) {\n if (!quiet) console.log(`${pc.dim('[DRY]')} would create ${keep}`);\n } else {\n await fs.mkdir(dir, { recursive: true });\n await fs.writeFile(keep, '');\n if (!quiet) console.log(`${pc.green('[NEW]')} ${keep}`);\n }\n written++;\n }\n\n return written;\n}\n\nasync function writeConfigFile(\n answers: InitAnswers,\n quiet: boolean,\n dryRun: boolean,\n): Promise<number> {\n const configPath = answers.configPath;\n if (await fs.stat(configPath).catch(() => null)) {\n if (!quiet) console.log(`${pc.cyan('[SKIP]')} ${configPath} (already exists)`);\n return 0;\n }\n if (dryRun) {\n if (!quiet) console.log(`${pc.dim('[DRY]')} would create ${configPath}`);\n return 1;\n }\n await fs.mkdir(path.dirname(configPath), { recursive: true });\n await fs.writeFile(configPath, renderConfig(answers), 'utf8');\n if (!quiet) console.log(`${pc.green('[NEW]')} ${configPath}`);\n return 1;\n}\n\n/**\n * Render `agentlink.config.json` as JSONC with explanatory comments. We hand-\n * format this rather than `JSON.stringify`-ing because the comments are the\n * whole point — users learn the schema by reading the seed file.\n */\nexport function renderConfig(answers: InitAnswers): string {\n const includeJson = JSON.stringify(answers.include);\n const adapterLines = ALL_ADAPTER_NAMES.map((name) => {\n const value = answers.adapters.includes(name) ? 'true' : 'false';\n return ` \"${name}\": ${value}`;\n }).join(',\\n');\n\n return `{\n // Asset type allowlist. Default: [\"skills\", \"rules\", \"commands\"].\n // Add \"prompts\", \"templates\" or \"agents\" here to opt them in.\n \"include\": ${includeJson},\n\n // Asset type denylist (always wins over \\`include\\`).\n \"exclude\": [],\n\n // Per-adapter overrides. Each value can be:\n // - true : enable with built-in defaults\n // - false : disable even if its rootDir exists\n // - { \"layout\": { … } } : enable + override specific asset layouts\n //\n // Example: keep cursor commands nested instead of flattened\n // \"cursor\": { \"layout\": { \"commands\": { \"flatten\": false } } }\n \"adapters\": {\n${adapterLines}\n }\n}\n`;\n}\n\n/* ────────────────────────── Hints ───────────────────────────────────── */\n\nfunction printNonDefaultLocationHint(target: string): void {\n console.log('');\n console.log(pc.bold('Tip — your source is at a non-default location:'));\n console.log(` ${target}`);\n console.log('');\n console.log('Future invocations of `agentlink sync` need to know about it. Either:');\n console.log(` - pass ${pc.cyan(`--source ${target}`)} each time, or`);\n console.log(` - export ${pc.cyan(`AGENTLINK_SOURCE=${target}`)} in your shell rc, or`);\n console.log(` - symlink it: ${pc.cyan(`ln -s ${target} ~/.agents`)}`);\n}\n","import path from 'node:path';\nimport pc from 'picocolors';\n\nimport { buildLinkPlans, type Conflict } from '../core/layout.js';\nimport { applyPlans, findStaleLinks, removeLinks, type LinkOutcome } from '../core/linker.js';\nimport { AgentlinkError } from '../core/errors.js';\nimport {\n enumerateTargetDirs,\n loadContext,\n printHeader,\n type CommandFlags,\n} from './_shared.js';\n\nexport class StrictModeViolation extends AgentlinkError {\n constructor(public readonly conflicts: readonly Conflict[]) {\n super(`${conflicts.length} link target collision(s) under --strict`);\n }\n}\n\nexport async function runSync(flags: CommandFlags = {}): Promise<void> {\n const ctx = await loadContext(flags);\n printHeader(ctx, 'sync', flags);\n\n const { plans, collisions } = await buildLinkPlans({\n source: ctx.paths.source,\n destRoot: ctx.paths.destRoot,\n config: ctx.config,\n });\n\n if (collisions.length > 0) {\n reportCollisions(collisions, flags.quiet ?? false);\n if (flags.strict) {\n throw new StrictModeViolation(collisions);\n }\n }\n\n const outcomes = await applyPlans(plans, {\n ...(flags.dryRun !== undefined ? { dryRun: flags.dryRun } : {}),\n ...(flags.force !== undefined ? { force: flags.force } : {}),\n });\n\n // Sweep up dangling links left from previous syncs whose source files were\n // deleted. Mirrors the legacy init.sh behaviour where every run also cleans.\n const targetDirs = enumerateTargetDirs(ctx).map((t) => t.targetDir);\n const stale = (await Promise.all(targetDirs.map(findStaleLinks))).flat();\n const removed = await removeLinks(stale, flags.dryRun ? { dryRun: true } : {});\n\n reportOutcomes(outcomes, ctx.paths.destRoot, flags.quiet ?? false);\n reportRemovals(removed, flags.quiet ?? false);\n printSummary(outcomes, removed, collisions);\n\n // Any individual link operation that errored should fail the whole run so\n // CI / pre-commit hooks can detect a partially-broken sync. Conflicts are\n // already either warned (default) or thrown (via --strict above), so we\n // only consider link-level failures here.\n const errored =\n outcomes.filter((o) => o.status === 'errored').length +\n removed.filter((r) => r.status === 'errored').length;\n if (errored > 0) process.exitCode = 1;\n}\n\n/* ────────────────────────── Reporting helpers ────────────────────────── */\n\nfunction reportCollisions(collisions: readonly Conflict[], quiet: boolean): void {\n if (quiet) return;\n for (const c of collisions) {\n console.warn(\n pc.yellow(`[WARN] target collision under ${c.adapterName}/${c.assetType}: ${c.target}`),\n );\n for (const s of c.sources) console.warn(pc.dim(` source: ${s}`));\n }\n}\n\nfunction reportOutcomes(\n outcomes: readonly LinkOutcome[],\n destRoot: string,\n quiet: boolean,\n): void {\n for (const o of outcomes) {\n const display = path.relative(destRoot, o.plan.target);\n switch (o.status) {\n case 'created':\n if (!quiet) console.log(`${pc.green('[OK]')} ${display}`);\n break;\n case 'fixed':\n console.log(`${pc.green('[FIX]')} ${display} ${pc.dim(`(${o.message})`)}`);\n break;\n case 'skipped':\n if (!quiet) console.log(`${pc.cyan('[SKIP]')} ${display}`);\n break;\n case 'warned':\n console.warn(`${pc.yellow('[WARN]')} ${display} ${pc.dim(`(${o.message ?? ''})`)}`);\n break;\n case 'errored':\n console.error(`${pc.red('[ERR]')} ${display} ${pc.dim(`(${o.message ?? ''})`)}`);\n break;\n case 'dry-create':\n if (!quiet) console.log(`${pc.dim('[DRY]')} ${display}`);\n break;\n case 'dry-fix':\n console.log(`${pc.dim('[DRY]')} ${display} ${pc.dim(`(would fix: ${o.message})`)}`);\n break;\n }\n }\n}\n\nfunction reportRemovals(\n removals: readonly { path: string; status: string; message?: string }[],\n quiet: boolean,\n): void {\n for (const r of removals) {\n if (r.status === 'removed' && !quiet) {\n console.log(`${pc.yellow('[CLEAN]')} ${r.path}`);\n } else if (r.status === 'dry-remove' && !quiet) {\n console.log(`${pc.dim('[DRY]')} would clean ${r.path}`);\n } else if (r.status === 'errored') {\n console.error(`${pc.red('[ERR]')} ${r.path} ${pc.dim(`(${r.message ?? ''})`)}`);\n }\n }\n}\n\nfunction printSummary(\n outcomes: readonly LinkOutcome[],\n removals: readonly { status: string }[],\n collisions: readonly Conflict[],\n): void {\n const counts: Record<string, number> = {};\n for (const o of outcomes) counts[o.status] = (counts[o.status] ?? 0) + 1;\n const cleaned = removals.filter((r) => r.status === 'removed' || r.status === 'dry-remove').length;\n\n const parts: string[] = [];\n if (counts['created']) parts.push(pc.green(`${counts['created']} created`));\n if (counts['fixed']) parts.push(pc.green(`${counts['fixed']} fixed`));\n if (counts['skipped']) parts.push(pc.cyan(`${counts['skipped']} skipped`));\n if (counts['warned']) parts.push(pc.yellow(`${counts['warned']} warned`));\n if (counts['errored']) parts.push(pc.red(`${counts['errored']} errored`));\n if (counts['dry-create'] || counts['dry-fix']) {\n parts.push(pc.dim(`${(counts['dry-create'] ?? 0) + (counts['dry-fix'] ?? 0)} dry-run`));\n }\n if (cleaned) parts.push(pc.yellow(`${cleaned} cleaned`));\n if (collisions.length) parts.push(pc.yellow(`${collisions.length} collision(s)`));\n\n console.log('');\n console.log(parts.length === 0 ? pc.dim(' no changes') : ` ${parts.join(', ')}`);\n}\n","import pc from 'picocolors';\n\nimport { findManagedLinks, pruneEmptyDirs, removeLinks } from '../core/linker.js';\nimport {\n enumerateAllAdapterRoots,\n loadContext,\n printHeader,\n type CommandFlags,\n} from './_shared.js';\n\nexport async function runUnlink(flags: CommandFlags = {}): Promise<void> {\n const ctx = await loadContext(flags);\n printHeader(ctx, 'unlink', flags);\n\n // `unlink` must be a \"remove everything we ever wrote\" operation. Walk\n // *all* built-in adapter rootDirs that exist under destRoot — not only\n // the ones currently enabled by config — and recurse into each so we\n // also catch links living in asset subdirs that the current `include`\n // list no longer mentions. Otherwise users who disabled an adapter or\n // narrowed `include` would leave orphan symlinks behind.\n const roots = await enumerateAllAdapterRoots(ctx.paths.destRoot);\n const sourceRoot = ctx.paths.source;\n\n const owned = (\n await Promise.all(\n roots.map(({ rootDir }) => findManagedLinks(rootDir, sourceRoot, { recursive: true })),\n )\n ).flat();\n\n if (owned.length === 0) {\n console.log(pc.dim(' no agentlink-managed links found'));\n return;\n }\n\n const outcomes = await removeLinks(owned, flags.dryRun ? { dryRun: true } : {});\n\n let removed = 0;\n let dry = 0;\n let errored = 0;\n for (const o of outcomes) {\n if (o.status === 'removed') {\n removed++;\n if (!flags.quiet) console.log(`${pc.yellow('[UNLINK]')} ${o.path}`);\n } else if (o.status === 'dry-remove') {\n dry++;\n if (!flags.quiet) console.log(`${pc.dim('[DRY]')} would remove ${o.path}`);\n } else if (o.status === 'errored') {\n errored++;\n console.error(`${pc.red('[ERR]')} ${o.path} ${pc.dim(`(${o.message ?? ''})`)}`);\n }\n }\n\n // Best-effort: prune now-empty adapter rootDirs. `pruneEmptyDirs` is silent\n // on non-empty / missing dirs, so we don't bother filtering.\n if (!flags.dryRun) {\n await pruneEmptyDirs(roots.map((r) => r.rootDir));\n }\n\n console.log('');\n const parts: string[] = [];\n if (removed) parts.push(pc.yellow(`${removed} removed`));\n if (dry) parts.push(pc.dim(`${dry} dry-run`));\n if (errored) parts.push(pc.red(`${errored} errored`));\n console.log(parts.length === 0 ? pc.dim(' no changes') : ` ${parts.join(', ')}`);\n\n if (errored > 0) process.exitCode = 1;\n}\n"],"mappings":"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","names":["pc","pc","fs","path","fs","path","path","fs","fs","os","path","isInside","os","expandTilde","path","fs","path","fs","pc","fs","path","pc","fs","path","isDir","pc","path","fs","fs","os","path","pc","path","pc","pc","path","path","fs","pc","expandTilde","os","pc","pc","pc"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/clean.ts","../src/core/linker.ts","../src/commands/_shared.ts","../src/adapters/claude.ts","../src/adapters/codebuddy.ts","../src/adapters/cursor.ts","../src/adapters/index.ts","../src/core/config.ts","../src/core/errors.ts","../src/core/types.ts","../src/core/resolve.ts","../src/commands/doctor.ts","../src/core/layout.ts","../src/commands/init.ts","../src/commands/sync.ts","../src/commands/unlink.ts"],"sourcesContent":["import { cac } from 'cac';\nimport pc from 'picocolors';\n\nimport { runClean } from './commands/clean.js';\nimport { runDoctor } from './commands/doctor.js';\nimport { runInit } from './commands/init.js';\nimport { runSync } from './commands/sync.js';\nimport { runUnlink } from './commands/unlink.js';\nimport type { CommandFlags } from './commands/_shared.js';\nimport {\n AgentlinkError,\n ConflictError,\n InvalidPathError,\n SourceNotFoundError,\n} from './core/errors.js';\nimport { ConfigParseError } from './core/config.js';\nimport { StrictModeViolation } from './commands/sync.js';\n\nconst PKG_NAME = '@cmkk/agentlink';\nconst PKG_VERSION = '0.1.0';\n\nfunction applyGlobalOptions(cli: ReturnType<typeof cac>): void {\n cli\n .option('--source <path>', 'Override the source .agents/ location')\n .option('-t, --target <dir>', 'Run against a specific destination root')\n .option('-u, --user', 'Link into $HOME (user-level install target)')\n .option('--config <path>', 'Load a specific agentlink.config.json')\n .option('-n, --dry-run', 'Preview changes without touching the filesystem')\n .option('-f, --force', 'Replace symlinks that point somewhere unexpected')\n .option('-q, --quiet', 'Only print changes and errors')\n .option('-v, --verbose', 'Print debug-level output')\n .option('--strict', 'Treat collisions as errors instead of warnings');\n}\n\nasync function main(): Promise<void> {\n const cli = cac('agentlink');\n\n cli\n .command('init', 'Create an empty .agents/ scaffold and run sync once')\n .option('--local', 'Scaffold into ./.agents instead of ~/.agents')\n .option('-y, --yes', 'Skip interactive prompts and use defaults (CI-friendly)')\n .action((flags: CommandFlags & { local?: boolean; yes?: boolean }) => runInit(flags));\n\n cli\n .command('sync', 'Refresh all symlinks (default command)')\n .action((flags: CommandFlags) => runSync(flags));\n\n cli\n .command('clean', 'Remove dangling agentlink-managed symlinks')\n .action((flags: CommandFlags) => runClean(flags));\n\n cli\n .command('unlink', 'Remove every symlink agentlink ever created')\n .action((flags: CommandFlags) => runUnlink(flags));\n\n cli\n .command('doctor', 'Diagnose configuration, adapters, and link health')\n .action((flags: CommandFlags) => runDoctor(flags));\n\n cli\n .command('', 'Default command (alias for `sync`)')\n .action((flags: CommandFlags) => runSync(flags));\n\n applyGlobalOptions(cli);\n cli.help();\n cli.version(PKG_VERSION);\n\n try {\n cli.parse(process.argv, { run: false });\n await cli.runMatchedCommand();\n } catch (err) {\n renderError(err);\n process.exit(1);\n }\n}\n\nfunction renderError(err: unknown): void {\n if (err instanceof SourceNotFoundError) {\n console.error(pc.red(`[${PKG_NAME}] no source .agents/ directory found.\\n`));\n console.error('Searched:');\n for (const { origin, status } of err.searched) {\n console.error(` ${origin.padEnd(40)} ${pc.dim(status)}`);\n }\n console.error('');\n console.error('Hints:');\n console.error(' - run `agentlink init` to scaffold ~/.agents/');\n console.error(' - run `agentlink init --local` to scaffold ./.agents/');\n console.error(' - or pass `--source <path>` explicitly');\n return;\n }\n\n if (err instanceof StrictModeViolation) {\n console.error(pc.red(`[${PKG_NAME}] aborted: ${err.message}`));\n console.error(pc.dim(' rerun without --strict to accept the warnings, or fix the source.'));\n return;\n }\n\n if (err instanceof ConfigParseError) {\n console.error(pc.red(`[${PKG_NAME}] ${err.message}`));\n return;\n }\n\n if (err instanceof InvalidPathError || err instanceof ConflictError) {\n console.error(pc.red(`[${PKG_NAME}] ${err.message}`));\n return;\n }\n\n if (err instanceof AgentlinkError) {\n console.error(pc.red(`[${PKG_NAME}] ${err.message}`));\n return;\n }\n\n const msg = err instanceof Error ? err.message : String(err);\n console.error(pc.red(`[${PKG_NAME}] ${msg}`));\n if (process.env['AGENTLINK_DEBUG']) {\n console.error(err);\n }\n}\n\nawait main();\n","import pc from 'picocolors';\n\nimport { findStaleLinks, removeLinks } from '../core/linker.js';\nimport {\n enumerateTargetDirs,\n loadContext,\n printHeader,\n type CommandFlags,\n} from './_shared.js';\n\nexport async function runClean(flags: CommandFlags = {}): Promise<void> {\n const ctx = await loadContext(flags);\n printHeader(ctx, 'clean', flags);\n\n const targets = enumerateTargetDirs(ctx).map((t) => t.targetDir);\n const stale = (await Promise.all(targets.map(findStaleLinks))).flat();\n\n if (stale.length === 0) {\n console.log(pc.dim(' no dangling symlinks found'));\n return;\n }\n\n const outcomes = await removeLinks(stale, flags.dryRun ? { dryRun: true } : {});\n\n let cleaned = 0;\n let dry = 0;\n let errored = 0;\n for (const o of outcomes) {\n if (o.status === 'removed') {\n cleaned++;\n if (!flags.quiet) console.log(`${pc.yellow('[CLEAN]')} ${o.path}`);\n } else if (o.status === 'dry-remove') {\n dry++;\n if (!flags.quiet) console.log(`${pc.dim('[DRY]')} would clean ${o.path}`);\n } else if (o.status === 'errored') {\n errored++;\n console.error(`${pc.red('[ERR]')} ${o.path} ${pc.dim(`(${o.message ?? ''})`)}`);\n }\n }\n\n console.log('');\n const parts: string[] = [];\n if (cleaned) parts.push(pc.yellow(`${cleaned} cleaned`));\n if (dry) parts.push(pc.dim(`${dry} dry-run`));\n if (errored) parts.push(pc.red(`${errored} errored`));\n console.log(parts.length === 0 ? pc.dim(' no changes') : ` ${parts.join(', ')}`);\n\n if (errored > 0) process.exitCode = 1;\n}\n","/**\n * Filesystem layer for agentlink. All symlink reads/writes funnel through this\n * module so command implementations stay declarative and the test suite can\n * exercise edge cases against a real tmpdir.\n *\n * Conventions:\n * - Symlinks are written **relatively** (`path.relative(targetDir, source)`)\n * so they survive moving the project root, mirroring the legacy\n * `init.sh` behaviour.\n * - Existing real files / directories are never overwritten — they always\n * produce a `warned` outcome so the user can decide.\n * - `dry-run` is a hard mode switch: no writes, but every check still runs\n * so the user sees exactly what `sync` _would_ do.\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport type { LinkPlan } from './layout.js';\n\nexport type LinkStatus =\n | 'created' // new symlink written\n | 'skipped' // already pointed at the right place\n | 'fixed' // existed but pointed elsewhere; replaced (force)\n | 'warned' // exists with conflicting kind; left untouched\n | 'errored' // unexpected fs failure\n | 'dry-create' // dry-run: would create\n | 'dry-fix'; // dry-run: would replace\n\nexport interface LinkOutcome {\n plan: LinkPlan;\n status: LinkStatus;\n message?: string;\n}\n\nexport interface ApplyOptions {\n dryRun?: boolean;\n /** Replace symlinks that point somewhere unexpected. */\n force?: boolean;\n}\n\n/**\n * Apply each plan exactly once, returning per-plan outcomes in input order.\n * Never throws on per-plan errors; surfaces them as `errored` outcomes so a\n * single bad file does not halt a large sync.\n */\nexport async function applyPlans(\n plans: readonly LinkPlan[],\n options: ApplyOptions = {},\n): Promise<LinkOutcome[]> {\n const outcomes: LinkOutcome[] = [];\n for (const plan of plans) {\n outcomes.push(await applyOne(plan, options));\n }\n return outcomes;\n}\n\nasync function applyOne(plan: LinkPlan, options: ApplyOptions): Promise<LinkOutcome> {\n const { dryRun = false, force = false } = options;\n const targetDir = path.dirname(plan.target);\n const expected = path.relative(targetDir, plan.source);\n\n try {\n const lstat = await fs.lstat(plan.target).catch(() => null);\n\n if (lstat?.isSymbolicLink()) {\n const current = await fs.readlink(plan.target);\n if (current === expected) return { plan, status: 'skipped' };\n if (force) {\n if (dryRun) {\n return { plan, status: 'dry-fix', message: `${current} -> ${expected}` };\n }\n await fs.unlink(plan.target);\n await fs.symlink(expected, plan.target);\n return { plan, status: 'fixed', message: `${current} -> ${expected}` };\n }\n return {\n plan,\n status: 'warned',\n message: `points to ${current} (use --force to repair)`,\n };\n }\n\n if (lstat) {\n const kind = lstat.isDirectory() ? 'directory' : 'file';\n return { plan, status: 'warned', message: `${kind} already exists at target` };\n }\n\n if (dryRun) {\n return { plan, status: 'dry-create' };\n }\n\n await fs.mkdir(targetDir, { recursive: true });\n await fs.symlink(expected, plan.target);\n return { plan, status: 'created' };\n } catch (err) {\n return { plan, status: 'errored', message: err instanceof Error ? err.message : String(err) };\n }\n}\n\n/* ────────────────────────── Stale link detection ─────────────────────── */\n\n/**\n * Inspect a directory and return the absolute paths of every entry that is a\n * symlink whose target no longer resolves (`fs.stat` fails — dangling).\n *\n * Non-recursive: only the top level of `targetDir` is scanned, matching the\n * legacy `init.sh` behaviour. Symlinks pointing to nested-but-existing files\n * are left alone.\n */\nexport async function findStaleLinks(targetDir: string): Promise<string[]> {\n const entries = await fs.readdir(targetDir, { withFileTypes: true }).catch(() => null);\n if (!entries) return [];\n\n const stale: string[] = [];\n for (const entry of entries) {\n if (!entry.isSymbolicLink()) continue;\n const full = path.join(targetDir, entry.name);\n const exists = await fs.stat(full).catch(() => null);\n if (!exists) stale.push(full);\n }\n return stale;\n}\n\n/* ────────────────────────── Managed-link detection (unlink) ──────────── */\n\n/**\n * Inspect a directory and return symlinks whose resolved target lives under\n * `sourceRoot`. Used by `agentlink unlink` to find every symlink we ever\n * created without requiring a \"manifest\" file.\n *\n * Resolves the symlink target lexically (no `realpath`) so the user can swap\n * the source directory at any time without bricking the unlink path.\n *\n * Defaults to a non-recursive single-directory scan (mirroring what sync\n * publishes one level deep). Pass `{ recursive: true }` to descend into every\n * subdirectory — used by `unlink` and `doctor` to catch links that live in\n * asset subdirs the current config has dropped.\n */\nexport async function findManagedLinks(\n targetDir: string,\n sourceRoot: string,\n options: { recursive?: boolean } = {},\n): Promise<string[]> {\n const sourceAbs = path.resolve(sourceRoot);\n const out: string[] = [];\n\n await (async function descend(dir: string): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => null);\n if (!entries) return;\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isSymbolicLink()) {\n const raw = await fs.readlink(full).catch(() => null);\n if (!raw) continue;\n const resolved = path.isAbsolute(raw) ? path.normalize(raw) : path.resolve(dir, raw);\n if (resolved === sourceAbs || resolved.startsWith(sourceAbs + path.sep)) {\n out.push(full);\n }\n } else if (options.recursive && entry.isDirectory()) {\n await descend(full);\n }\n }\n })(targetDir);\n\n return out;\n}\n\n/* ────────────────────────── Removal helpers ─────────────────────────── */\n\nexport type RemovalStatus = 'removed' | 'dry-remove' | 'errored';\n\nexport interface RemovalOutcome {\n path: string;\n status: RemovalStatus;\n message?: string;\n}\n\nexport interface RemoveOptions {\n dryRun?: boolean;\n}\n\n/**\n * Delete the supplied symlinks. Errors are returned per-path rather than\n * thrown, mirroring `applyPlans`.\n */\nexport async function removeLinks(\n paths: readonly string[],\n options: RemoveOptions = {},\n): Promise<RemovalOutcome[]> {\n const { dryRun = false } = options;\n const out: RemovalOutcome[] = [];\n for (const p of paths) {\n if (dryRun) {\n out.push({ path: p, status: 'dry-remove' });\n continue;\n }\n try {\n await fs.unlink(p);\n out.push({ path: p, status: 'removed' });\n } catch (err) {\n out.push({\n path: p,\n status: 'errored',\n message: err instanceof Error ? err.message : String(err),\n });\n }\n }\n return out;\n}\n\n/**\n * Best-effort `rmdir` of empty managed directories left behind by `unlink`.\n * Failure to remove (e.g. user-owned siblings inside) is silent — the goal is\n * tidiness, not enforcement.\n */\nexport async function pruneEmptyDirs(dirs: readonly string[]): Promise<void> {\n for (const dir of dirs) {\n await fs.rmdir(dir).catch(() => undefined);\n }\n}\n","/**\n * Shared scaffolding for command implementations:\n * - flag plumbing (every command takes the same global flags)\n * - context loading (resolve paths + load config)\n * - common header printing\n * - target-directory enumeration used by clean / unlink\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport pc from 'picocolors';\n\nimport { adapterRegistry } from '../adapters/index.js';\nimport { loadConfigBundle, resolveConfigFromParsed } from '../core/config.js';\nimport { resolvePaths, type ResolvedPaths } from '../core/resolve.js';\nimport type { Adapter, AssetType, ResolvedConfig } from '../core/types.js';\n\nexport interface CommandFlags {\n source?: string;\n target?: string;\n user?: boolean;\n config?: string;\n dryRun?: boolean;\n force?: boolean;\n quiet?: boolean;\n verbose?: boolean;\n strict?: boolean;\n}\n\nexport interface CommandContext {\n paths: ResolvedPaths;\n config: ResolvedConfig;\n}\n\nexport async function loadContext(flags: CommandFlags): Promise<CommandContext> {\n const bundle = await loadConfigBundle({\n ...(flags.config !== undefined ? { config: flags.config } : {}),\n });\n\n const paths = await resolvePaths({\n ...(flags.source !== undefined ? { source: flags.source } : {}),\n ...(flags.target !== undefined ? { target: flags.target } : {}),\n ...(flags.user !== undefined ? { user: flags.user } : {}),\n ...(bundle.parsed.source !== undefined ? { configSource: bundle.parsed.source } : {}),\n ...(bundle.parsed.target !== undefined ? { configTarget: bundle.parsed.target } : {}),\n ...(bundle.parsed.user !== undefined ? { configUser: bundle.parsed.user } : {}),\n ...(bundle.configBase !== null ? { configBase: bundle.configBase } : {}),\n resolvedConfigPath: bundle.location.path,\n });\n\n const rawConfig = resolveConfigFromParsed(bundle.parsed, bundle.location, paths.source);\n const config = await gateAdapters(rawConfig, paths.destRoot);\n return { paths, config };\n}\n\n/**\n * Drop adapters whose `rootDir` does not exist under the destination root.\n *\n * This mirrors the legacy `init.sh` \"auto-detect\" behaviour: a missing\n * `.cursor` directory is treated as \"this user does not use cursor on this\n * machine\" rather than \"create one for them\". Users opt-in by `mkdir\n * .cursor` (or running their AI tool which does so).\n */\nasync function gateAdapters(config: ResolvedConfig, destRoot: string): Promise<ResolvedConfig> {\n const live: Adapter[] = [];\n for (const adapter of config.adapters) {\n const root = path.join(destRoot, adapter.rootDir);\n const stat = await fs.stat(root).catch(() => null);\n if (stat?.isDirectory()) live.push(adapter);\n }\n if (live.length === config.adapters.length) return config;\n return { ...config, adapters: live };\n}\n\nexport function printHeader(ctx: CommandContext, mode: string, flags: CommandFlags): void {\n if (flags.quiet) return;\n console.log('');\n console.log(pc.bold(`[agentlink] ${mode}`));\n console.log(` source: ${ctx.paths.source} ${pc.dim(`(${ctx.paths.sourceOrigin})`)}`);\n console.log(` target: ${ctx.paths.destRoot} ${pc.dim(`(${ctx.paths.destOrigin})`)}`);\n const configLine = ctx.config.configPath\n ? `${ctx.config.configPath} ${pc.dim(`(${ctx.config.configOrigin})`)}`\n : pc.dim('(defaults)');\n console.log(` config: ${configLine}`);\n console.log(` adapters: ${ctx.config.adapters.map((a) => a.name).join(', ') || pc.dim('(none)')}`);\n if (flags.dryRun) console.log(pc.yellow(' mode: dry-run'));\n console.log('');\n}\n\n/**\n * Walk the (adapter × assetType) combinations relevant for the run, returning\n * each adapter's set of `targetAssetDir`s. Used by clean / unlink to know\n * which directories to inspect without re-deriving the layout themselves.\n */\nexport function enumerateTargetDirs(ctx: CommandContext): {\n adapter: Adapter;\n assetType: AssetType;\n targetDir: string;\n rootDir: string;\n}[] {\n const out: { adapter: Adapter; assetType: AssetType; targetDir: string; rootDir: string }[] = [];\n const root = ctx.paths.destRoot;\n for (const adapter of ctx.config.adapters) {\n for (const assetType of ctx.config.assetTypes as ReadonlySet<AssetType>) {\n const slot = adapter.layout[assetType];\n if (slot === undefined || slot === false) continue;\n out.push({\n adapter,\n assetType,\n targetDir: path.join(root, adapter.rootDir, slot.targetSubdir),\n rootDir: path.join(root, adapter.rootDir),\n });\n }\n }\n return out;\n}\n\n/**\n * Return every built-in adapter rootDir that exists under `destRoot`,\n * regardless of whether the user has currently enabled it in config.\n *\n * Used by `unlink` and `doctor`'s orphan sweep: those operations must not\n * skip directories that the user just disabled — otherwise the legacy links\n * created when the adapter *was* enabled would be unreachable.\n *\n * `enumerateTargetDirs` (above) keeps gate semantics for sync / clean,\n * which legitimately operate only on what the current config selects.\n */\nexport async function enumerateAllAdapterRoots(\n destRoot: string,\n): Promise<{ name: string; rootDir: string }[]> {\n const out: { name: string; rootDir: string }[] = [];\n for (const [name, preset] of Object.entries(adapterRegistry)) {\n const rootDir = path.join(destRoot, preset.rootDir);\n const stat = await fs.stat(rootDir).catch(() => null);\n if (stat?.isDirectory()) out.push({ name, rootDir });\n }\n return out;\n}\n","import type { Adapter } from '../core/types.js';\n\n/**\n * Claude Code's conventions: nested directories are fine across all asset\n * types, no special suffix requirements.\n */\nexport const claude: Adapter = {\n name: 'claude',\n rootDir: '.claude',\n layout: {\n skills: { targetSubdir: 'skills' },\n rules: { targetSubdir: 'rules' },\n commands: { targetSubdir: 'commands' },\n },\n};\n","import type { Adapter } from '../core/types.js';\n\n/**\n * CodeBuddy supports nested commands and the same `<root>/<asset>/` shape as\n * claude. Kept symmetric with the claude adapter for now; will diverge if the\n * agent gets stricter conventions.\n */\nexport const codebuddy: Adapter = {\n name: 'codebuddy',\n rootDir: '.codebuddy',\n layout: {\n skills: { targetSubdir: 'skills' },\n rules: { targetSubdir: 'rules' },\n commands: { targetSubdir: 'commands' },\n },\n};\n","import type { Adapter } from '../core/types.js';\n\n/**\n * Cursor's filesystem conventions:\n * - `.cursor/skills/<name>/SKILL.md` recursive (nested OK)\n * - `.cursor/rules/<file>.mdc` flat, must use `.mdc` suffix\n * - `.cursor/commands/<file>.md` flat (no nested subdirs)\n * - prompts: not consumed; explicitly skipped\n */\nexport const cursor: Adapter = {\n name: 'cursor',\n rootDir: '.cursor',\n layout: {\n skills: { targetSubdir: 'skills' },\n rules: { targetSubdir: 'rules', flatten: true, ext: '.mdc' },\n commands: { targetSubdir: 'commands', flatten: true },\n prompts: false,\n },\n};\n","import type { Adapter } from '../core/types.js';\nimport { claude } from './claude.js';\nimport { codebuddy } from './codebuddy.js';\nimport { cursor } from './cursor.js';\n\n/**\n * v1 ships with three built-in adapters. The keys here are also the only valid\n * names accepted in `agentlink.config.json`'s `adapters` field — supporting a\n * new agent requires publishing a new package version.\n */\nexport const adapterRegistry = {\n cursor,\n claude,\n codebuddy,\n} as const satisfies Record<string, Adapter>;\n\nexport type AdapterName = keyof typeof adapterRegistry;\n\nexport { claude, codebuddy, cursor };\n","/**\n * Locate, validate, and merge `agentlink.config.json` with built-in adapter\n * presets to produce a {@link ResolvedConfig}.\n *\n * Config and source are mostly **decoupled** — config primarily describes _how_\n * to project assets, but may also persist optional `source` / `target` defaults\n * so users need not pass flags on every invocation.\n *\n * Config resolution chain (highest first):\n * 1. `--config <path>` flag\n * 2. `AGENTLINK_CONFIG` env var\n * 3. nearest `agentlink.config.json` from cwd up to (but not past) the\n * home directory or filesystem root\n * 4. `~/agentlink.config.json`\n * 5. (none) — fall back to all defaults\n *\n * Validation philosophy:\n * - **Closed asset-type universe**: keys must be one of the literal\n * {@link AssetType} values. Unknown names yield a friendly zod error.\n * - **Closed adapter universe** (v1): `adapters` keys must match a built-in\n * name (cursor / claude / codebuddy).\n * - **Tolerant defaults**: every field is optional; missing config equals\n * an empty `{}`.\n */\n\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport stripJsonComments from 'strip-json-comments';\nimport { z } from 'zod';\n\nimport { adapterRegistry, type AdapterName } from '../adapters/index.js';\nimport { AgentlinkError, InvalidPathError } from './errors.js';\nimport {\n DEFAULT_INCLUDE,\n type Adapter,\n type AssetLayout,\n type AssetType,\n type ResolvedConfig,\n} from './types.js';\n\nexport const CONFIG_FILENAME = 'agentlink.config.json';\nconst ENV_CONFIG_KEY = 'AGENTLINK_CONFIG';\n\nconst ASSET_TYPE_VALUES = ['skills', 'rules', 'commands', 'prompts', 'templates', 'agents'] as const;\nconst ASSET_TYPE_SET: ReadonlySet<AssetType> = new Set(ASSET_TYPE_VALUES);\n\nconst assetTypeSchema = z.enum(ASSET_TYPE_VALUES);\n\n/**\n * `targetSubdir` is `path.join`'d under each adapter's rootDir. Letting users\n * write `..`, absolute paths, or path separators here would let them escape\n * the rootDir sandbox (and silently smear symlinks across the destination).\n *\n * We reject the obviously bad shapes at the schema layer so zod produces a\n * focused, path-aware error message instead of a generic ConfigPatchError\n * later in mergeLayout.\n */\nconst targetSubdirSchema = z\n .string()\n .min(1)\n .refine((s) => !path.isAbsolute(s), {\n message: 'targetSubdir must be relative (no leading `/` or drive letter)',\n })\n .refine((s) => !s.split(/[\\\\/]/).some((seg) => seg === '..'), {\n message: 'targetSubdir cannot traverse upward (`..` segment forbidden)',\n });\n\nconst assetLayoutSchema = z.object({\n targetSubdir: targetSubdirSchema.optional(),\n flatten: z.boolean().optional(),\n include: z.array(z.string().min(1)).optional(),\n exclude: z.array(z.string().min(1)).optional(),\n ext: z.string().min(1).optional(),\n});\n\nconst adapterOverrideSchema = z.union([\n z.boolean(),\n z\n .object({\n // partialRecord: zod v4 z.record(enum,…) is exhaustive by default;\n // partialRecord lets each key be optional, which is what users expect.\n layout: z.partialRecord(assetTypeSchema, assetLayoutSchema).optional(),\n })\n .strict(),\n]);\n\nconst adapterNameSchema = z.enum(\n Object.keys(adapterRegistry) as [AdapterName, ...AdapterName[]],\n);\n\nconst rootPathSchema = z.string().min(1);\n\n/**\n * Strict closed-world schema. Unknown root keys, unknown adapter names, and\n * unknown asset types all surface as friendly zod errors.\n */\nconst configSchema = z\n .object({\n source: rootPathSchema.optional(),\n target: rootPathSchema.optional(),\n user: z.boolean().optional(),\n include: z.array(assetTypeSchema).optional(),\n exclude: z.array(assetTypeSchema).optional(),\n adapters: z.partialRecord(adapterNameSchema, adapterOverrideSchema).optional(),\n })\n .strict()\n .superRefine((val, ctx) => {\n if (val.target !== undefined && val.user === true) {\n ctx.addIssue({\n code: 'custom',\n message: '`target` and `user` are mutually exclusive',\n path: ['target'],\n });\n }\n });\n\ntype ParsedConfig = z.infer<typeof configSchema>;\n\nexport class ConfigParseError extends AgentlinkError {\n readonly issues: ReadonlyArray<{ path: string; message: string }>;\n\n constructor(filePath: string, issues: ReadonlyArray<{ path: string; message: string }>) {\n const lines = issues.map(({ path: p, message }) => ` at ${p || '<root>'}: ${message}`);\n super(`invalid agentlink config at ${filePath}\\n${lines.join('\\n')}`);\n this.issues = issues;\n }\n}\n\nexport type ConfigOrigin = 'flag' | 'env' | 'cwd-ancestor' | 'home-global' | 'defaults';\n\nexport interface ResolveConfigOptions {\n /** `--config` flag value. */\n config?: string;\n /** Override `process.cwd()`. */\n cwd?: string;\n /** Override `os.homedir()`. */\n home?: string;\n /** Override `process.env`. */\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface ConfigLocation {\n /** Absolute path that was loaded — `null` if no config file exists. */\n path: string | null;\n origin: ConfigOrigin;\n}\n\nexport interface LoadConfigOptions extends ResolveConfigOptions {\n /** Absolute path to the source `.agents/` — only used to populate ResolvedConfig. */\n source: string;\n}\n\nexport interface ConfigBundle {\n location: ConfigLocation;\n parsed: ParsedConfig;\n /** Directory containing the loaded config file; `null` when defaults apply. */\n configBase: string | null;\n}\n\n/**\n * Locate and parse the user config once. Used by {@link loadContext} to feed\n * both path resolution and adapter merging without reading the file twice.\n */\nexport async function loadConfigBundle(opts: ResolveConfigOptions = {}): Promise<ConfigBundle> {\n const location = await resolveConfigPath(opts);\n const parsed = location.path ? await readUserConfig(location.path) : ({} as ParsedConfig);\n const configBase = location.path ? path.dirname(location.path) : null;\n return { location, parsed, configBase };\n}\n\n/**\n * Merge a parsed config with built-in presets. The `source` argument is the\n * already-resolved `.agents/` path from {@link resolveSource}.\n */\nexport function resolveConfigFromParsed(\n userConfig: ParsedConfig,\n location: ConfigLocation,\n source: string,\n): ResolvedConfig {\n const adapters = mergeAdapters(userConfig.adapters);\n const assetTypes = resolveAssetTypes(userConfig.include, userConfig.exclude);\n\n return {\n source,\n adapters,\n assetTypes,\n configPath: location.path,\n configOrigin: location.origin,\n };\n}\n\n/**\n * Read + validate + merge the config. Returns a fully resolved view that is\n * safe to feed into command implementations.\n */\nexport async function loadConfig(opts: LoadConfigOptions): Promise<ResolvedConfig> {\n const bundle = await loadConfigBundle(opts);\n return resolveConfigFromParsed(bundle.parsed, bundle.location, opts.source);\n}\n\n/**\n * Resolve the location of the config file (independent of source). Pure with\n * respect to everything except `fs.stat` — easy to unit-test.\n */\nexport async function resolveConfigPath(opts: ResolveConfigOptions = {}): Promise<ConfigLocation> {\n const cwd = opts.cwd ?? process.cwd();\n const home = opts.home ?? os.homedir();\n const env = opts.env ?? process.env;\n\n if (opts.config !== undefined && opts.config !== '') {\n const abs = await assertConfigFile(expandTilde(opts.config, home), '--config', cwd);\n return { path: abs, origin: 'flag' };\n }\n\n const envValue = env[ENV_CONFIG_KEY];\n if (envValue !== undefined && envValue !== '') {\n const abs = await assertConfigFile(expandTilde(envValue, home), `$${ENV_CONFIG_KEY}`, cwd);\n return { path: abs, origin: 'env' };\n }\n\n const ancestor = await searchAncestors(path.resolve(cwd), home);\n if (ancestor) {\n return { path: ancestor, origin: 'cwd-ancestor' };\n }\n\n const homeConfig = path.join(home, CONFIG_FILENAME);\n if (await isFile(homeConfig)) {\n return { path: homeConfig, origin: 'home-global' };\n }\n\n return { path: null, origin: 'defaults' };\n}\n\n/**\n * Walk up the directory tree from `start`, looking for `agentlink.config.json`.\n * Stops as soon as we leave the home directory (or hit the filesystem root) so\n * configs from unrelated trees never leak in.\n */\nasync function searchAncestors(start: string, home: string): Promise<string | null> {\n const homeAbs = path.resolve(home);\n let current = start;\n while (true) {\n if (current === homeAbs) {\n // Don't probe `~/agentlink.config.json` here — it has its own dedicated\n // tier so the diagnostic origin reads as \"home-global\", not \"ancestor\".\n return null;\n }\n\n const candidate = path.join(current, CONFIG_FILENAME);\n if (await isFile(candidate)) return candidate;\n\n const parent = path.dirname(current);\n if (parent === current) return null; // hit filesystem root\n\n // Stop ascending if we are about to leave the home tree (only when start\n // was inside home). If start was outside home all along, keep walking up.\n if (isInside(current, homeAbs) && !isInside(parent, homeAbs) && parent !== homeAbs) {\n return null;\n }\n current = parent;\n }\n}\n\nasync function assertConfigFile(input: string, flag: string, cwd: string): Promise<string> {\n const abs = path.isAbsolute(input) ? input : path.resolve(cwd, input);\n const stat = await fs.stat(abs).catch(() => null);\n if (!stat) throw new InvalidPathError(flag, abs, 'missing');\n if (!stat.isFile()) throw new InvalidPathError(flag, abs, 'not-a-file');\n return abs;\n}\n\nasync function isFile(p: string): Promise<boolean> {\n const stat = await fs.stat(p).catch(() => null);\n return stat?.isFile() ?? false;\n}\n\nfunction expandTilde(p: string, home: string): string {\n if (p === '~') return home;\n if (p.startsWith('~/')) return path.join(home, p.slice(2));\n return p;\n}\n\nfunction isInside(child: string, parent: string): boolean {\n const c = path.resolve(child);\n const p = path.resolve(parent);\n if (c === p) return true;\n const rel = path.relative(p, c);\n return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);\n}\n\n/**\n * Read the *raw* user config and report which adapter names the user\n * explicitly opted into (i.e. anything not literally `false`). Best-effort —\n * returns an empty set on any IO/parse error so callers (e.g. doctor) can\n * fall back gracefully without hiding the real validation error from the\n * primary {@link loadConfig} pipeline.\n *\n * Centralised here so the JSONC tolerance stays consistent with `loadConfig`\n * (same comment + trailing-comma stripping) — earlier drafts had doctor.ts\n * hand-roll its own regex, which was both wrong on URLs and inconsistent\n * with the loader.\n */\nexport async function readExplicitAdapters(\n configPath: string | null,\n): Promise<ReadonlySet<string>> {\n if (!configPath) return new Set();\n const raw = await fs.readFile(configPath, 'utf8').catch(() => null);\n if (raw === null) return new Set();\n try {\n const parsed = JSON.parse(stripJsonComments(raw, { trailingCommas: true })) as {\n adapters?: Record<string, unknown>;\n };\n if (!parsed.adapters) return new Set();\n return new Set(Object.keys(parsed.adapters).filter((k) => parsed.adapters?.[k] !== false));\n } catch {\n return new Set();\n }\n}\n\nasync function readUserConfig(filePath: string): Promise<ParsedConfig> {\n const raw = await fs.readFile(filePath, 'utf8').catch((err: NodeJS.ErrnoException) => {\n if (err.code === 'ENOENT') return null;\n throw err;\n });\n if (raw === null) return {};\n\n let parsed: unknown;\n try {\n // JSONC: tolerate `//` and `/* */` comments plus trailing commas. The\n // template ships annotated so users can discover schema by reading.\n parsed = JSON.parse(stripJsonComments(raw, { trailingCommas: true }));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new ConfigParseError(filePath, [{ path: '', message: `not valid JSON: ${message}` }]);\n }\n\n const result = configSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.map((seg) => String(seg)).join('.'),\n message: issue.message,\n }));\n throw new ConfigParseError(filePath, issues);\n }\n return result.data;\n}\n\n/**\n * Apply user overrides to built-in adapter presets, returning only the adapters\n * that are enabled. Disabled adapters (`false` override) drop out entirely;\n * adapters not mentioned in user config keep their preset.\n */\nfunction mergeAdapters(\n overrides: ParsedConfig['adapters'] | undefined,\n): readonly Adapter[] {\n const result: Adapter[] = [];\n\n for (const [name, preset] of Object.entries(adapterRegistry) as Array<[AdapterName, Adapter]>) {\n const override = overrides?.[name];\n\n if (override === false) continue;\n\n if (override === undefined || override === true) {\n result.push(preset);\n continue;\n }\n\n result.push({\n name: preset.name,\n rootDir: preset.rootDir,\n layout: mergeLayout(preset.layout, override.layout),\n });\n }\n\n return result;\n}\n\n/**\n * Shape of a single `layout[<assetType>]` entry as it comes out of zod parse.\n * Differs from `Partial<AssetLayout>` because `exactOptionalPropertyTypes`\n * distinguishes \"may be omitted\" from \"may be `undefined`\".\n */\ninterface LayoutPatchValue {\n targetSubdir?: string | undefined;\n flatten?: boolean | undefined;\n include?: string[] | undefined;\n exclude?: string[] | undefined;\n ext?: string | undefined;\n}\n\ntype LayoutPatch = Partial<Record<AssetType, LayoutPatchValue>>;\n\nfunction mergeLayout(\n base: Adapter['layout'],\n patch: LayoutPatch | undefined,\n): Adapter['layout'] {\n if (!patch) return base;\n\n const merged: Adapter['layout'] = { ...base };\n for (const key of Object.keys(patch) as AssetType[]) {\n const value = patch[key];\n if (value === undefined) continue;\n\n const baseLayout = base[key];\n if (baseLayout === undefined || baseLayout === false) {\n const targetSubdir = value.targetSubdir;\n if (targetSubdir === undefined) {\n throw new ConfigPatchError(\n `adapter override for \"${key}\" needs a targetSubdir because the built-in adapter does not define one`,\n );\n }\n merged[key] = compactLayout(targetSubdir, value);\n continue;\n }\n merged[key] = mergeIntoBase(baseLayout, value);\n }\n return merged;\n}\n\n/** Strip `undefined` so the result respects exactOptionalPropertyTypes. */\nfunction compactLayout(targetSubdir: string, v: LayoutPatchValue): AssetLayout {\n const out: { -readonly [K in keyof AssetLayout]: AssetLayout[K] } = { targetSubdir };\n if (v.flatten !== undefined) out.flatten = v.flatten;\n if (v.include !== undefined) out.include = v.include;\n if (v.exclude !== undefined) out.exclude = v.exclude;\n if (v.ext !== undefined) out.ext = v.ext;\n return out;\n}\n\nfunction mergeIntoBase(base: AssetLayout, patch: LayoutPatchValue): AssetLayout {\n const out: { -readonly [K in keyof AssetLayout]: AssetLayout[K] } = { ...base };\n if (patch.targetSubdir !== undefined) out.targetSubdir = patch.targetSubdir;\n if (patch.flatten !== undefined) out.flatten = patch.flatten;\n if (patch.include !== undefined) out.include = patch.include;\n if (patch.exclude !== undefined) out.exclude = patch.exclude;\n if (patch.ext !== undefined) out.ext = patch.ext;\n return out;\n}\n\nclass ConfigPatchError extends AgentlinkError {}\n\n/**\n * Compute the set of asset types that should participate in this run.\n * `include` defaults to {@link DEFAULT_INCLUDE}; `exclude` is always a hard\n * subtraction applied last.\n */\nfunction resolveAssetTypes(\n include: readonly AssetType[] | undefined,\n exclude: readonly AssetType[] | undefined,\n): ReadonlySet<string> {\n const candidates = new Set<string>(include ?? DEFAULT_INCLUDE);\n for (const denied of exclude ?? []) {\n candidates.delete(denied);\n }\n return candidates;\n}\n\nexport const __test__ = {\n ASSET_TYPE_SET,\n CONFIG_FILENAME,\n};\n","/**\n * Domain-specific error types thrown by agentlink core modules.\n *\n * Using a small class hierarchy (rather than plain `Error` with codes) so the\n * CLI layer can `instanceof`-match and render targeted help text without\n * coupling to error message strings.\n */\n\nexport class AgentlinkError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\n/**\n * No `.agents/` directory could be located via any resolution channel.\n * `searched` records every probe and its outcome so the CLI can render a\n * \"Searched: …\" diagnostic block.\n */\nexport class SourceNotFoundError extends AgentlinkError {\n readonly searched: ReadonlyArray<{ origin: string; status: string }>;\n\n constructor(searched: ReadonlyArray<{ origin: string; status: string }>) {\n super('no source .agents/ directory found');\n this.searched = searched;\n }\n}\n\n/**\n * A user-supplied path (via flag or env) is missing or has the wrong shape.\n * `flag` is the originating channel name as the user would recognize it\n * (e.g. `--source`, `AGENTLINK_SOURCE`).\n */\nexport class InvalidPathError extends AgentlinkError {\n readonly flag: string;\n readonly path: string;\n readonly reason: 'missing' | 'not-a-directory' | 'not-a-file';\n\n constructor(flag: string, p: string, reason: 'missing' | 'not-a-directory' | 'not-a-file') {\n super(InvalidPathError.message(flag, p, reason));\n this.flag = flag;\n this.path = p;\n this.reason = reason;\n }\n\n private static message(\n flag: string,\n p: string,\n reason: 'missing' | 'not-a-directory' | 'not-a-file',\n ): string {\n if (reason === 'missing') return `${flag} does not exist: ${p}`;\n if (reason === 'not-a-directory') return `${flag} is not a directory: ${p}`;\n return `${flag} is not a file: ${p}`;\n }\n}\n\n/** Two mutually exclusive options were supplied together (e.g. --target and --user). */\nexport class ConflictError extends AgentlinkError {}\n","/**\n * Asset categories agentlink knows how to symlink.\n * Extend cautiously — adding a value here is a breaking change for adapters.\n */\nexport type AssetType = 'skills' | 'rules' | 'commands' | 'prompts' | 'templates' | 'agents';\n\n/**\n * Per-asset-type projection rule. Sentinel `false` (\"skip this asset entirely\")\n * is represented at the {@link Adapter.layout} value level rather than inside\n * AssetLayout, so that `keyof AssetLayout` stays meaningful for code that maps\n * over its fields.\n */\nexport interface AssetLayout {\n /** Subdirectory under the adapter's `rootDir`, e.g. 'skills' / 'commands'. */\n targetSubdir: string;\n /**\n * If true, source files in nested subdirectories are flattened into the target\n * directory using `__` as the path separator (e.g. `git/commit.md` → `git__commit.md`).\n * Use this for agents that don't support nested command/rule directories (e.g. cursor).\n */\n flatten?: boolean;\n /** Glob patterns (relative to source asset dir) to include. Defaults to all files. */\n include?: readonly string[];\n /** Glob patterns to exclude. Applied after `include`. */\n exclude?: readonly string[];\n /** Force a specific file extension on the link target (e.g. `.mdc` for cursor rules). */\n ext?: string;\n /**\n * Custom rename hook. Receives the relative source path; returns the target file name\n * (without `targetSubdir`). v1: only used by built-in adapters; not exposed via JSON config.\n */\n rename?: (relPath: string) => string;\n}\n\n/** A layout slot is either a real layout or `false` to mean \"skip this asset type\". */\nexport type LayoutSlot = AssetLayout | false;\n\n/**\n * Built-in or user-supplied adapter describing how to project `.agents/` into one AI agent's\n * directory layout.\n */\nexport interface Adapter {\n /** Stable identifier, e.g. 'cursor' / 'claude' / 'codebuddy'. */\n name: string;\n /** Directory under target root, e.g. '.cursor'. */\n rootDir: string;\n /** Per-asset-type projection rules. */\n layout: Partial<Record<AssetType, LayoutSlot>>;\n}\n\n/**\n * Per-adapter overrides as written by the user in JSON config.\n * Value semantics:\n * - `true` : enable built-in adapter with default layout\n * - `false` : disable adapter even if its rootDir exists\n * - `{ layout }` : enable + deep-merge layout overrides onto built-in defaults\n */\nexport type AdapterOverride =\n | boolean\n | { layout?: Partial<Record<AssetType, AssetLayout>> };\n\n/**\n * User-facing JSON configuration (`agentlink.config.json`).\n * Every field is optional; sensible defaults apply.\n */\nexport interface AgentlinkConfig {\n /**\n * Persistent default for the `.agents/` source directory.\n * Resolved after `--source` / `AGENTLINK_SOURCE` and `cwd/.agents`, before\n * `~/.agents`. Relative paths are resolved against the config file directory.\n */\n source?: string;\n /**\n * Persistent default destination root (where adapter rootDirs like `.cursor`\n * are expected). Relative paths are resolved against the config file directory.\n * Mutually exclusive with {@link AgentlinkConfig.user}.\n */\n target?: string;\n /** When true, project symlinks into `$HOME` (equivalent to `--user`). */\n user?: boolean;\n /**\n * Asset type allowlist. When omitted, falls back to the built-in default\n * (`['skills', 'rules', 'commands']`). Use this to opt into less-common\n * asset types like `prompts` / `templates` / `agents`.\n */\n include?: readonly string[];\n /**\n * Asset type denylist applied AFTER `include`. Always wins over `include`,\n * useful for temporarily silencing one type without rewriting the allowlist.\n */\n exclude?: readonly string[];\n /** Per-adapter overrides; see {@link AdapterOverride}. */\n adapters?: Record<string, AdapterOverride>;\n}\n\n/**\n * Result of {@link loadConfig}: raw JSON has been validated, defaults filled,\n * and built-in adapter presets merged with user overrides. This is what every\n * runtime command (sync/clean/unlink/doctor) consumes.\n */\nexport interface ResolvedConfig {\n /** Absolute path to the source `.agents/` directory. */\n source: string;\n /** Adapters that are enabled, with overrides already applied to layouts. */\n adapters: readonly Adapter[];\n /** Asset type names that should participate in this run. */\n assetTypes: ReadonlySet<string>;\n /** Absolute path of the config file actually loaded; null when no file existed. */\n configPath: string | null;\n /** Where the config came from. `'defaults'` means no file was found. */\n configOrigin: 'flag' | 'env' | 'cwd-ancestor' | 'home-global' | 'defaults';\n}\n\n/** Built-in default for {@link AgentlinkConfig.include}. */\nexport const DEFAULT_INCLUDE = ['skills', 'rules', 'commands'] as const;\n","/**\n * Locate the source `.agents/` directory and the destination root for a sync run.\n *\n * Two independent resolution chains share the same {@link ResolveOptions} and\n * produce a {@link ResolvedPaths} record. Both chains are pure with respect to\n * everything except `fs.stat` probes, so they are easy to unit-test with a\n * tmpdir + injected `cwd / home / env`.\n *\n * Source chain (highest first):\n * 1. `--source <path>` flag\n * 2. `AGENTLINK_SOURCE` environment variable\n * 3. `<cwd>/.agents/` — local-first so team repos override personal globals\n * 4. `config.source` — from the resolved `agentlink.config.json`\n * 5. `<home>/.agents/` — recommended default for the npx workflow\n * 6. (none) → SourceNotFoundError\n *\n * Destination chain (highest first):\n * 1. `--target <path>` flag\n * 2. `--user` → home (user-level install target)\n * 3. `config.target` / `config.user`\n * 4. (default) cwd\n */\n\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { ConflictError, InvalidPathError, SourceNotFoundError } from './errors.js';\n\nexport type SourceOrigin = 'flag' | 'env' | 'cwd-local' | 'config' | 'home-global';\nexport type DestOrigin = 'flag' | 'user' | 'config' | 'cwd';\n\nexport interface ResolveOptions {\n /** `--source` flag value. */\n source?: string;\n /** `--target` flag value. */\n target?: string;\n /** `--user` flag value — link into $HOME. */\n user?: boolean;\n /** `source` field from the resolved config file. */\n configSource?: string;\n /** `target` field from the resolved config file. */\n configTarget?: string;\n /** `user` field from the resolved config file. */\n configUser?: boolean;\n /** Directory containing the loaded config; used to resolve relative config paths. */\n configBase?: string;\n /** Resolved config file path — diagnostics only (see {@link SourceNotFoundError}). */\n resolvedConfigPath?: string | null;\n /** Override `process.cwd()`. */\n cwd?: string;\n /** Override `os.homedir()`. */\n home?: string;\n /** Override `process.env`. */\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface ResolvedPaths {\n source: string;\n sourceOrigin: SourceOrigin;\n destRoot: string;\n destOrigin: DestOrigin;\n}\n\nconst ENV_SOURCE_KEY = 'AGENTLINK_SOURCE';\nconst AGENTS_DIRNAME = '.agents';\n\n/**\n * Top-level entry. Resolves source and destination concurrently and emits a\n * Node warning if `destRoot` lives inside `source` (likely a self-link mistake).\n */\nexport async function resolvePaths(opts: ResolveOptions = {}): Promise<ResolvedPaths> {\n const [src, dst] = await Promise.all([resolveSource(opts), resolveDestRoot(opts)]);\n\n if (isInside(dst.path, src.path)) {\n process.emitWarning(\n `destination root \"${dst.path}\" lives inside source \"${src.path}\" — this may create self-references.`,\n { code: 'AGENTLINK_NESTED_DEST' },\n );\n }\n\n return {\n source: src.path,\n sourceOrigin: src.origin,\n destRoot: dst.path,\n destOrigin: dst.origin,\n };\n}\n\nexport async function resolveSource(\n opts: ResolveOptions = {},\n): Promise<{ path: string; origin: SourceOrigin }> {\n const cwd = opts.cwd ?? process.cwd();\n const home = opts.home ?? os.homedir();\n const env = opts.env ?? process.env;\n\n if (opts.source !== undefined && opts.source !== '') {\n const abs = await assertDir(expandTilde(opts.source, home), '--source', cwd);\n return { path: abs, origin: 'flag' };\n }\n\n const envValue = env[ENV_SOURCE_KEY];\n if (envValue !== undefined && envValue !== '') {\n const abs = await assertDir(expandTilde(envValue, home), `$${ENV_SOURCE_KEY}`, cwd);\n return { path: abs, origin: 'env' };\n }\n\n const local = path.join(cwd, AGENTS_DIRNAME);\n if (await isDir(local)) {\n return { path: local, origin: 'cwd-local' };\n }\n\n const configSource = opts.configSource;\n if (configSource !== undefined && configSource !== '') {\n const base = opts.configBase ?? cwd;\n const abs = await assertDir(\n resolveConfigPathValue(configSource, base, home),\n 'config.source',\n cwd,\n );\n return { path: abs, origin: 'config' };\n }\n\n // Note: `homeAgents` is the source path under home; unrelated to the\n // destination `--user` flag (which maps the *target* root to home).\n const homeAgents = path.join(home, AGENTS_DIRNAME);\n if (await isDir(homeAgents)) {\n return { path: homeAgents, origin: 'home-global' };\n }\n\n throw new SourceNotFoundError([\n { origin: '--source flag', status: 'not provided' },\n { origin: `$${ENV_SOURCE_KEY}`, status: 'not set' },\n { origin: local, status: localProbeStatus(local) },\n { origin: 'config.source', status: configSourceStatus(opts) },\n { origin: homeAgents, status: 'missing' },\n ]);\n}\n\nfunction localProbeStatus(local: string): string {\n return `missing (if ${local} exists — even empty — it takes priority over config.source)`;\n}\n\nfunction configSourceStatus(opts: ResolveOptions): string {\n if (opts.configSource !== undefined && opts.configSource !== '') {\n return 'not reached';\n }\n if (opts.resolvedConfigPath) {\n return `not set in ${opts.resolvedConfigPath}`;\n }\n return 'no config file loaded';\n}\n\nexport async function resolveDestRoot(\n opts: ResolveOptions = {},\n): Promise<{ path: string; origin: DestOrigin }> {\n const cwd = opts.cwd ?? process.cwd();\n const home = opts.home ?? os.homedir();\n\n if (opts.target !== undefined && opts.target !== '' && opts.user) {\n throw new ConflictError('--target and --user are mutually exclusive');\n }\n\n if (opts.target !== undefined && opts.target !== '') {\n const abs = await assertDir(expandTilde(opts.target, home), '--target', cwd);\n return { path: abs, origin: 'flag' };\n }\n\n if (opts.user) {\n return { path: home, origin: 'user' };\n }\n\n if (opts.configTarget !== undefined && opts.configTarget !== '' && opts.configUser) {\n throw new ConflictError('config `target` and `user` are mutually exclusive');\n }\n\n if (opts.configTarget !== undefined && opts.configTarget !== '') {\n const base = opts.configBase ?? cwd;\n const abs = await assertDir(\n resolveConfigPathValue(opts.configTarget, base, home),\n 'config.target',\n cwd,\n );\n return { path: abs, origin: 'config' };\n }\n\n if (opts.configUser) {\n return { path: home, origin: 'config' };\n }\n\n return { path: cwd, origin: 'cwd' };\n}\n\n/**\n * Resolve a path from config: tilde-expand, then absolute or relative to the\n * config file directory (not cwd).\n */\nexport function resolveConfigPathValue(input: string, configBase: string, home: string): string {\n const expanded = expandTilde(input, home);\n return path.isAbsolute(expanded) ? expanded : path.resolve(configBase, expanded);\n}\n\n/**\n * Expand a leading `~` or `~/` to the supplied home directory.\n * Intentionally narrower than shell tilde expansion (no `~user` lookup).\n */\nexport function expandTilde(p: string, home: string): string {\n if (p === '~') return home;\n if (p.startsWith('~/')) return path.join(home, p.slice(2));\n return p;\n}\n\nasync function assertDir(input: string, flag: string, cwd: string): Promise<string> {\n const abs = path.isAbsolute(input) ? input : path.resolve(cwd, input);\n const stat = await fs.stat(abs).catch(() => null);\n if (!stat) throw new InvalidPathError(flag, abs, 'missing');\n if (!stat.isDirectory()) throw new InvalidPathError(flag, abs, 'not-a-directory');\n return abs;\n}\n\nasync function isDir(p: string): Promise<boolean> {\n const stat = await fs.stat(p).catch(() => null);\n return stat?.isDirectory() ?? false;\n}\n\n/**\n * `true` iff `child` is inside `parent` (or equal).\n *\n * Compares lexically — neither path is realpath-resolved, matching the rest of\n * the resolver's \"preserve symlinks\" stance.\n */\nexport function isInside(child: string, parent: string): boolean {\n const c = path.resolve(child);\n const p = path.resolve(parent);\n if (c === p) return true;\n const rel = path.relative(p, c);\n return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);\n}\n","/**\n * `agentlink doctor` — read-only health check for the current source/destination\n * pair. Designed to answer \"would `agentlink sync` do something useful here,\n * and would it run cleanly?\" without writing anything to disk.\n *\n * Reports four sections:\n * 1. Adapter availability (rootDir present / config-disabled / missing)\n * 2. Link plan (count + any target-path collisions)\n * 3. Existing links (dangling, orphan)\n * 4. Aggregate verdict — sets `process.exitCode = 1` only on errors;\n * warnings are non-blocking so doctor can be wired into pre-commit\n * hooks without false positives.\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport pc from 'picocolors';\n\nimport { adapterRegistry } from '../adapters/index.js';\nimport { readExplicitAdapters } from '../core/config.js';\nimport { buildLinkPlans } from '../core/layout.js';\nimport { findManagedLinks, findStaleLinks } from '../core/linker.js';\nimport {\n enumerateAllAdapterRoots,\n enumerateTargetDirs,\n loadContext,\n printHeader,\n type CommandFlags,\n} from './_shared.js';\n\nexport async function runDoctor(flags: CommandFlags = {}): Promise<void> {\n const ctx = await loadContext(flags);\n printHeader(ctx, 'doctor', flags);\n\n let warnings = 0;\n let errors = 0;\n\n // Read the *raw* user config so we can distinguish \"user explicitly opted\n // into this adapter\" (warn when its rootDir is missing) from \"default\n // pickup\" (dim when missing — most users don't have all three agents).\n const explicitlyEnabled = await readExplicitAdapters(ctx.config.configPath);\n\n // ── 1. Adapter availability ──────────────────────────────────────────────\n console.log(pc.bold('Adapters'));\n for (const [name, preset] of Object.entries(adapterRegistry)) {\n const root = path.join(ctx.paths.destRoot, preset.rootDir);\n const stat = await fs.stat(root).catch(() => null);\n if (stat?.isDirectory()) {\n console.log(\n ` ${pc.green('✓')} ${name.padEnd(11)} ${pc.dim(preset.rootDir + '/')} ${pc.dim('(active)')}`,\n );\n } else if (explicitlyEnabled.has(name)) {\n warnings++;\n console.log(\n ` ${pc.yellow('⚠')} ${name.padEnd(11)} ${pc.dim(preset.rootDir + '/')} ${pc.yellow('rootDir not found — sync will skip')}`,\n );\n } else {\n console.log(\n ` ${pc.dim('—')} ${name.padEnd(11)} ${pc.dim(preset.rootDir + '/')} ${pc.dim('not in use on this machine')}`,\n );\n }\n }\n console.log('');\n\n // ── 2. Link plan ────────────────────────────────────────────────────────\n const { plans, collisions } = await buildLinkPlans({\n source: ctx.paths.source,\n destRoot: ctx.paths.destRoot,\n config: ctx.config,\n });\n\n console.log(pc.bold('Link plan'));\n console.log(` ${plans.length} plan${plans.length === 1 ? '' : 's'}`);\n if (collisions.length === 0) {\n console.log(` ${pc.green('✓')} no collisions`);\n } else {\n warnings += collisions.length;\n console.log(` ${pc.yellow('⚠')} ${collisions.length} collision(s):`);\n for (const c of collisions) {\n console.log(` ${pc.dim(c.adapterName + '/' + c.assetType)} ${c.target}`);\n for (const s of c.sources) console.log(` ${pc.dim('←')} ${s}`);\n }\n }\n console.log('');\n\n // ── 3. Existing links ───────────────────────────────────────────────────\n const targetDirs = enumerateTargetDirs(ctx).map((t) => t.targetDir);\n const stale = (await Promise.all(targetDirs.map(findStaleLinks))).flat();\n\n // Recursive sweep — orphans can live in asset subdirs that the current\n // config no longer mentions, AND in adapter rootDirs the user has just\n // disabled. Walk every built-in adapter rootDir that exists, ignoring the\n // current `ctx.config.adapters` gate so doctor stays comprehensive.\n const allRoots = await enumerateAllAdapterRoots(ctx.paths.destRoot);\n const managed = (\n await Promise.all(\n allRoots.map((r) => findManagedLinks(r.rootDir, ctx.paths.source, { recursive: true })),\n )\n ).flat();\n const staleSet = new Set(stale);\n const planTargets = new Set(plans.map((p) => p.target));\n const orphans = managed.filter((p) => !planTargets.has(p) && !staleSet.has(p));\n\n console.log(pc.bold('Existing links'));\n if (stale.length === 0 && orphans.length === 0) {\n console.log(` ${pc.green('✓')} all clear`);\n } else {\n if (stale.length > 0) {\n warnings += stale.length;\n console.log(\n ` ${pc.yellow('⚠')} ${stale.length} dangling ${pc.dim('(run `agentlink clean` to drop):')}`,\n );\n for (const p of stale) console.log(` ${p}`);\n }\n if (orphans.length > 0) {\n warnings += orphans.length;\n console.log(\n ` ${pc.yellow('⚠')} ${orphans.length} orphan ${pc.dim('(linked into source but not in current plan; `unlink` then `sync` will rebuild):')}`,\n );\n for (const p of orphans) console.log(` ${p}`);\n }\n }\n console.log('');\n\n // ── 4. Verdict ──────────────────────────────────────────────────────────\n if (warnings + errors === 0) {\n console.log(pc.green('Healthy'));\n return;\n }\n const parts: string[] = [];\n if (warnings > 0) parts.push(pc.yellow(`${warnings} warning${warnings === 1 ? '' : 's'}`));\n if (errors > 0) parts.push(pc.red(`${errors} error${errors === 1 ? '' : 's'}`));\n console.log(`Issues: ${parts.join(', ')}`);\n if (errors > 0) process.exitCode = 1;\n}\n\n","/**\n * Translate a {@link ResolvedConfig} into a flat list of {@link LinkPlan}s\n * that the linker module can execute one symlink at a time.\n *\n * For each `(adapter, assetType)` pair this module:\n * - resolves the source asset directory under `<source>/<assetType>`\n * - resolves the target directory under `<destRoot>/<rootDir>/<targetSubdir>`\n * - applies `flatten` / `ext` / `rename` / `include` / `exclude`\n * - detects target-path collisions and surfaces them as {@link Conflict}s\n *\n * Conflict policy is intentionally _diagnostic only_ here — the caller decides\n * whether to warn (default) or fail (`--strict`).\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport picomatch from 'picomatch';\n\nimport type {\n Adapter,\n AssetLayout,\n AssetType,\n ResolvedConfig,\n} from './types.js';\n\nexport interface LinkPlan {\n /** Absolute source path (file when flattened, file-or-directory otherwise). */\n source: string;\n /** Absolute target path the symlink will live at. */\n target: string;\n /** Asset type bucket this plan came from. */\n assetType: AssetType;\n /** Name of the adapter that produced this plan. */\n adapterName: string;\n /** True when source enumeration recursed into nested files. */\n flattened: boolean;\n}\n\nexport interface Conflict {\n /** Target path that two or more plans claim. */\n target: string;\n /** Source paths competing for the same target. */\n sources: readonly string[];\n /** Asset type / adapter the collision happened in (for diagnostics). */\n adapterName: string;\n assetType: AssetType;\n}\n\nexport interface BuildResult {\n /** Plans whose targets are unique. Safe to apply. */\n plans: readonly LinkPlan[];\n /** Plans dropped because their target collided with another (default policy). */\n collisions: readonly Conflict[];\n}\n\nexport interface BuildOptions {\n /** Absolute path to the source `.agents/` directory. */\n source: string;\n /** Absolute path to the destination root. */\n destRoot: string;\n /** Resolved config (loaded via {@link loadConfig}). */\n config: ResolvedConfig;\n}\n\n/**\n * Build the flat plan list. Pure function — only reads the filesystem; never\n * writes. Safe to call from `--dry-run`.\n */\nexport async function buildLinkPlans(opts: BuildOptions): Promise<BuildResult> {\n const { source, destRoot, config } = opts;\n const draft: LinkPlan[] = [];\n\n for (const adapter of config.adapters) {\n for (const assetType of config.assetTypes as ReadonlySet<AssetType>) {\n const slot = adapter.layout[assetType];\n if (slot === undefined || slot === false) continue;\n\n const sourceAssetDir = path.join(source, assetType);\n if (!(await isDir(sourceAssetDir))) continue;\n\n const targetAssetDir = path.join(destRoot, adapter.rootDir, slot.targetSubdir);\n const planned = slot.flatten\n ? await planFlattened(sourceAssetDir, targetAssetDir, slot, adapter, assetType)\n : await planShallow(sourceAssetDir, targetAssetDir, slot, adapter, assetType);\n\n draft.push(...planned);\n }\n }\n\n return splitByCollision(draft);\n}\n\n/* ────────────────────────── flatten = true ───────────────────────────── */\n\nasync function planFlattened(\n sourceAssetDir: string,\n targetAssetDir: string,\n layout: AssetLayout,\n adapter: Adapter,\n assetType: AssetType,\n): Promise<LinkPlan[]> {\n const files = await walkFiles(sourceAssetDir);\n const includeMatch = makeIncludeMatcher(layout.include);\n const excludeMatch = makeExcludeMatcher(layout.exclude);\n const out: LinkPlan[] = [];\n\n for (const abs of files) {\n const rel = path.relative(sourceAssetDir, abs);\n if (!includeMatch(rel)) continue;\n if (excludeMatch(rel)) continue;\n\n const targetName = applyRename(rel, layout, /* flatten */ true);\n out.push({\n source: abs,\n target: path.join(targetAssetDir, targetName),\n adapterName: adapter.name,\n assetType,\n flattened: true,\n });\n }\n return out;\n}\n\n/* ────────────────────────── flatten = false (default) ───────────────── */\n\nasync function planShallow(\n sourceAssetDir: string,\n targetAssetDir: string,\n layout: AssetLayout,\n adapter: Adapter,\n assetType: AssetType,\n): Promise<LinkPlan[]> {\n const items = await listFirstLevel(sourceAssetDir);\n const includeMatch = makeIncludeMatcher(layout.include);\n const excludeMatch = makeExcludeMatcher(layout.exclude);\n const out: LinkPlan[] = [];\n\n for (const entry of items) {\n if (!includeMatch(entry.name)) continue;\n if (excludeMatch(entry.name)) continue;\n\n const targetName = applyRename(entry.name, layout, /* flatten */ false);\n out.push({\n source: path.join(sourceAssetDir, entry.name),\n target: path.join(targetAssetDir, targetName),\n adapterName: adapter.name,\n assetType,\n flattened: false,\n });\n }\n return out;\n}\n\n/* ────────────────────────── Naming helpers ──────────────────────────── */\n\nconst PATH_SEPARATOR_RE = /[\\\\/]/g;\n\nfunction applyRename(relPath: string, layout: AssetLayout, flatten: boolean): string {\n if (layout.rename) return layout.rename(relPath);\n\n let name = flatten ? relPath.replace(PATH_SEPARATOR_RE, '__') : relPath;\n if (layout.ext) {\n const ext = path.extname(name);\n name = ext ? name.slice(0, -ext.length) + layout.ext : name + layout.ext;\n }\n return name;\n}\n\n/* ────────────────────────── Filesystem walkers ──────────────────────── */\n\nasync function walkFiles(root: string): Promise<string[]> {\n const out: string[] = [];\n await (async function descend(dir: string): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n // Hidden entries (`.gitkeep`, `.DS_Store`, etc.) are placeholder/system\n // files, never real assets. Mirrors `listFirstLevel` behaviour.\n if (entry.name.startsWith('.')) continue;\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await descend(full);\n } else if (entry.isFile() || entry.isSymbolicLink()) {\n out.push(full);\n }\n }\n })(root);\n return out;\n}\n\nasync function listFirstLevel(root: string): Promise<readonly { name: string; isDir: boolean }[]> {\n const entries = await fs.readdir(root, { withFileTypes: true });\n return entries\n .filter((e) => !e.name.startsWith('.'))\n .map((e) => ({ name: e.name, isDir: e.isDirectory() }));\n}\n\nasync function isDir(p: string): Promise<boolean> {\n const stat = await fs.stat(p).catch(() => null);\n return stat?.isDirectory() ?? false;\n}\n\n/* ────────────────────────── Glob matchers ───────────────────────────── */\n\n/**\n * `include` matcher: empty/missing list means \"everything passes\".\n */\nfunction makeIncludeMatcher(patterns: readonly string[] | undefined): (rel: string) => boolean {\n if (!patterns || patterns.length === 0) return () => true;\n const fns = patterns.map((p) => picomatch(p, { dot: true }));\n return (rel) => fns.some((fn) => fn(rel));\n}\n\n/**\n * `exclude` matcher: empty/missing list means \"nothing is excluded\".\n */\nfunction makeExcludeMatcher(patterns: readonly string[] | undefined): (rel: string) => boolean {\n if (!patterns || patterns.length === 0) return () => false;\n const fns = patterns.map((p) => picomatch(p, { dot: true }));\n return (rel) => fns.some((fn) => fn(rel));\n}\n\n/* ────────────────────────── Collision detection ─────────────────────── */\n\nfunction splitByCollision(plans: readonly LinkPlan[]): BuildResult {\n const byTarget = new Map<string, LinkPlan[]>();\n for (const plan of plans) {\n const bucket = byTarget.get(plan.target);\n if (bucket) bucket.push(plan);\n else byTarget.set(plan.target, [plan]);\n }\n\n const accepted: LinkPlan[] = [];\n const collisions: Conflict[] = [];\n for (const [target, bucket] of byTarget) {\n if (bucket.length === 1) {\n accepted.push(bucket[0]!);\n continue;\n }\n const head = bucket[0]!;\n collisions.push({\n target,\n sources: bucket.map((p) => p.source),\n adapterName: head.adapterName,\n assetType: head.assetType,\n });\n }\n return { plans: accepted, collisions };\n}\n","/**\n * `agentlink init` — interactively scaffold a `.agents/` directory and run\n * sync once so the user gets working symlinks immediately.\n *\n * Three knobs the user can tweak via prompts:\n * 1. **Target location** (only when neither `--local` nor `--source` is set):\n * `~/.agents` (recommended) vs `./.agents`.\n * 2. **Asset types** to include — multi-select; defaults to skills/rules/commands.\n * 3. **Adapters** to enable — multi-select; defaults to all three.\n *\n * Non-interactive degrade rules:\n * - `--yes / -y` ⇒ everything defaults, no prompts\n * - stdin not a TTY ⇒ same (suitable for CI / piping)\n * - any flag that already answers a question ⇒ skip that prompt\n *\n * Refuses to clobber a non-empty target unless `--force`.\n */\n\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport pc from 'picocolors';\nimport prompts from 'prompts';\n\nimport { adapterRegistry, type AdapterName } from '../adapters/index.js';\nimport { AgentlinkError } from '../core/errors.js';\nimport { runSync } from './sync.js';\nimport type { CommandFlags } from './_shared.js';\n\nexport interface InitFlags extends CommandFlags {\n local?: boolean;\n yes?: boolean;\n}\n\nexport class InitTargetExistsError extends AgentlinkError {\n constructor(targetPath: string) {\n super(`refusing to scaffold into a non-empty directory: ${targetPath}\\n use --force to merge into the existing files`);\n }\n}\n\nconst ALL_ASSET_TYPES = ['skills', 'rules', 'commands', 'prompts', 'templates', 'agents'] as const;\nconst DEFAULT_ASSET_TYPES = ['skills', 'rules', 'commands'] as const;\nconst ALL_ADAPTER_NAMES = Object.keys(adapterRegistry) as AdapterName[];\n\nconst HERE = path.dirname(fileURLToPath(import.meta.url));\nconst TEMPLATE_CANDIDATES = [\n path.resolve(HERE, '..', 'templates', 'v1'),\n path.resolve(HERE, '..', '..', 'templates', 'v1'),\n];\n\nasync function findTemplatesDir(): Promise<string> {\n for (const candidate of TEMPLATE_CANDIDATES) {\n const stat = await fs.stat(candidate).catch(() => null);\n if (stat?.isDirectory()) return candidate;\n }\n throw new AgentlinkError(\n `agentlink templates dir not found; searched:\\n ${TEMPLATE_CANDIDATES.join('\\n ')}`,\n );\n}\n\ninterface InitAnswers {\n /** Where the source `.agents/` directory is created. */\n target: string;\n targetCategory: 'home' | 'cwd' | 'custom';\n /** Where the `agentlink.config.json` is written (independent of source). */\n configPath: string;\n configCategory: 'home' | 'cwd';\n include: readonly string[];\n adapters: readonly AdapterName[];\n}\n\nexport async function runInit(flags: InitFlags = {}): Promise<void> {\n const answers = await collectAnswers(flags);\n\n if (!flags.quiet) {\n console.log('');\n console.log(pc.bold('[agentlink] init'));\n console.log(` target: ${answers.target}`);\n console.log('');\n }\n\n const created = await scaffoldInto(answers, {\n force: flags.force ?? false,\n quiet: flags.quiet ?? false,\n dryRun: flags.dryRun ?? false,\n });\n if (!created.touched && !flags.quiet) {\n console.log(pc.dim(' no files written; .agents/ already populated'));\n }\n\n if (answers.targetCategory === 'custom' && !flags.quiet) {\n printNonDefaultLocationHint(answers.target);\n }\n\n if (!flags.quiet) console.log('');\n\n // In dry-run mode the source directory was never actually created, so a\n // follow-up sync would just throw on resolveSource. Tell the user what\n // a real run would have done and exit cleanly instead.\n if (flags.dryRun) {\n if (!flags.quiet) {\n console.log(\n pc.dim(\n ` (dry-run) would run \\`agentlink sync\\` against ${answers.target} after scaffolding`,\n ),\n );\n }\n return;\n }\n\n // Best-effort follow-up sync so the user gets immediate symlinks if they\n // already have `.cursor` / `.claude` / `.codebuddy` in cwd. The adapter\n // gate makes this a no-op when none exist.\n await runSync({\n source: answers.target,\n ...(flags.target !== undefined ? { target: flags.target } : {}),\n ...(flags.user !== undefined ? { user: flags.user } : {}),\n ...(flags.quiet !== undefined ? { quiet: flags.quiet } : {}),\n });\n}\n\n/* ────────────────────────── Prompt orchestration ─────────────────────── */\n\nasync function collectAnswers(flags: InitFlags): Promise<InitAnswers> {\n const skipPrompts = flags.yes || !process.stdin.isTTY;\n\n // 1) Target location\n let target: string;\n let targetCategory: InitAnswers['targetCategory'];\n if (flags.source !== undefined && flags.source !== '') {\n target = path.resolve(expandTilde(flags.source));\n targetCategory = categorizeTarget(target);\n } else if (flags.local) {\n target = path.resolve(process.cwd(), '.agents');\n targetCategory = 'cwd';\n } else if (skipPrompts) {\n target = path.resolve(os.homedir(), '.agents');\n targetCategory = 'home';\n } else {\n const choice = await ask<{ kind: 'home' | 'cwd' }>({\n type: 'select',\n name: 'kind',\n message: 'Where should agentlink put your .agents/ directory?',\n choices: [\n {\n title: 'Personal global (~/.agents)',\n description: 'Recommended — share assets across every project',\n value: 'home',\n },\n {\n title: 'Repo-local (./.agents)',\n description: 'Ship a curated .agents/ inside this repository',\n value: 'cwd',\n },\n ],\n initial: 0,\n });\n target =\n choice.kind === 'home'\n ? path.resolve(os.homedir(), '.agents')\n : path.resolve(process.cwd(), '.agents');\n targetCategory = choice.kind;\n }\n\n // 2) Asset types\n let include: readonly string[];\n if (skipPrompts) {\n include = DEFAULT_ASSET_TYPES;\n } else {\n const { values } = await ask<{ values: string[] }>({\n type: 'multiselect',\n name: 'values',\n message: 'Which asset types do you want to manage?',\n hint: 'space to toggle, enter to confirm',\n choices: ALL_ASSET_TYPES.map((name) => ({\n title: name,\n value: name,\n selected: (DEFAULT_ASSET_TYPES as readonly string[]).includes(name),\n })),\n min: 1,\n instructions: false,\n });\n include = values;\n }\n\n // 3) Adapters\n let adapters: readonly AdapterName[];\n if (skipPrompts) {\n adapters = ALL_ADAPTER_NAMES;\n } else {\n const { values } = await ask<{ values: AdapterName[] }>({\n type: 'multiselect',\n name: 'values',\n message: 'Which AI agents do you want to target?',\n hint: 'space to toggle, enter to confirm',\n choices: ALL_ADAPTER_NAMES.map((name) => ({\n title: name,\n value: name,\n selected: true,\n })),\n min: 1,\n instructions: false,\n });\n adapters = values;\n }\n\n // Decide where the config file lands. Config is decoupled from source —\n // whichever location a future `agentlink sync` is most likely to run from.\n const configCategory: InitAnswers['configCategory'] =\n targetCategory === 'cwd' ? 'cwd' : 'home';\n const configPath =\n configCategory === 'home'\n ? path.resolve(os.homedir(), 'agentlink.config.json')\n : path.resolve(process.cwd(), 'agentlink.config.json');\n\n return { target, targetCategory, configPath, configCategory, include, adapters };\n}\n\n/**\n * Wrapper that turns user-cancellation (Ctrl-C) into a thrown error rather\n * than letting `prompts` resolve with `{}` and silently producing an empty\n * config.\n */\nasync function ask<T extends Record<string, unknown>>(\n question: prompts.PromptObject,\n): Promise<T> {\n let cancelled = false;\n const answer = await prompts(question, {\n onCancel: () => {\n cancelled = true;\n return false;\n },\n });\n if (cancelled) throw new AgentlinkError('init cancelled');\n return answer as T;\n}\n\nfunction categorizeTarget(target: string): InitAnswers['targetCategory'] {\n const home = path.resolve(os.homedir(), '.agents');\n const cwd = path.resolve(process.cwd(), '.agents');\n if (target === home) return 'home';\n if (target === cwd) return 'cwd';\n return 'custom';\n}\n\nfunction expandTilde(p: string): string {\n if (p === '~') return os.homedir();\n if (p.startsWith('~/')) return path.join(os.homedir(), p.slice(2));\n return p;\n}\n\n/* ────────────────────────── Scaffolding ─────────────────────────────── */\n\ninterface ScaffoldResult {\n touched: boolean;\n}\n\ninterface ScaffoldOptions {\n force: boolean;\n quiet: boolean;\n /** When true: probe and report what would happen, never touch the filesystem. */\n dryRun: boolean;\n}\n\nasync function scaffoldInto(\n answers: InitAnswers,\n options: ScaffoldOptions,\n): Promise<ScaffoldResult> {\n const { force, quiet, dryRun } = options;\n const target = answers.target;\n const stat = await fs.stat(target).catch(() => null);\n if (stat && !stat.isDirectory()) {\n throw new AgentlinkError(`init target exists but is not a directory: ${target}`);\n }\n\n if (stat && !force) {\n const entries = await fs.readdir(target);\n if (entries.length > 0) throw new InitTargetExistsError(target);\n }\n\n if (!dryRun) {\n await fs.mkdir(target, { recursive: true });\n } else if (!stat && !quiet) {\n console.log(`${pc.dim('[DRY]')} would create ${target}/`);\n }\n\n const templatesDir = await findTemplatesDir();\n\n let written = 0;\n written += await copyStaticAssets(templatesDir, target, answers.include, quiet, dryRun);\n written += await writeConfigFile(answers, quiet, dryRun);\n return { touched: written > 0 };\n}\n\n/**\n * Copy README and `.gitkeep` placeholders for each user-selected asset type.\n * The config file is rendered separately by `writeConfigFile` because its\n * contents depend on the user's answers.\n */\nasync function copyStaticAssets(\n templatesDir: string,\n target: string,\n include: readonly string[],\n quiet: boolean,\n dryRun: boolean,\n): Promise<number> {\n let written = 0;\n\n // README — copy as-is if not already present.\n const readmeSrc = path.join(templatesDir, 'README.md');\n const readmeDest = path.join(target, 'README.md');\n if ((await fs.stat(readmeSrc).catch(() => null)) && !(await fs.stat(readmeDest).catch(() => null))) {\n if (dryRun) {\n if (!quiet) console.log(`${pc.dim('[DRY]')} would create ${readmeDest}`);\n } else {\n await fs.copyFile(readmeSrc, readmeDest);\n if (!quiet) console.log(`${pc.green('[NEW]')} ${readmeDest}`);\n }\n written++;\n }\n\n // Per-asset-type empty directories with a placeholder so they survive `git add`.\n for (const assetType of include) {\n const dir = path.join(target, assetType);\n const keep = path.join(dir, '.gitkeep');\n const keepExists = await fs.stat(keep).catch(() => null);\n if (keepExists) continue;\n\n if (dryRun) {\n if (!quiet) console.log(`${pc.dim('[DRY]')} would create ${keep}`);\n } else {\n await fs.mkdir(dir, { recursive: true });\n await fs.writeFile(keep, '');\n if (!quiet) console.log(`${pc.green('[NEW]')} ${keep}`);\n }\n written++;\n }\n\n return written;\n}\n\nasync function writeConfigFile(\n answers: InitAnswers,\n quiet: boolean,\n dryRun: boolean,\n): Promise<number> {\n const configPath = answers.configPath;\n if (await fs.stat(configPath).catch(() => null)) {\n if (!quiet) console.log(`${pc.cyan('[SKIP]')} ${configPath} (already exists)`);\n return 0;\n }\n if (dryRun) {\n if (!quiet) console.log(`${pc.dim('[DRY]')} would create ${configPath}`);\n return 1;\n }\n await fs.mkdir(path.dirname(configPath), { recursive: true });\n await fs.writeFile(configPath, renderConfig(answers), 'utf8');\n if (!quiet) console.log(`${pc.green('[NEW]')} ${configPath}`);\n return 1;\n}\n\n/**\n * Pick a portable `source` value for config: prefer a path relative to the\n * config file; otherwise `~/…` when under home; fall back to absolute.\n */\nexport function formatSourceForConfig(target: string, configPath: string): string {\n const resolved = path.resolve(target);\n const configDir = path.resolve(path.dirname(configPath));\n const home = path.resolve(os.homedir());\n const rel = path.relative(configDir, resolved);\n if (rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel)) {\n return rel;\n }\n if (resolved === home || resolved.startsWith(home + path.sep)) {\n const tail = path.relative(home, resolved);\n return tail === '' ? '~' : `~/${tail}`;\n }\n return resolved;\n}\n\n/**\n * Render `agentlink.config.json` as JSONC with explanatory comments. We hand-\n * format this rather than `JSON.stringify`-ing because the comments are the\n * whole point — users learn the schema by reading the seed file.\n */\nexport function renderConfig(answers: InitAnswers): string {\n const includeJson = JSON.stringify(answers.include);\n const adapterLines = ALL_ADAPTER_NAMES.map((name) => {\n const value = answers.adapters.includes(name) ? 'true' : 'false';\n return ` \"${name}\": ${value}`;\n }).join(',\\n');\n const sourceBlock =\n answers.targetCategory === 'custom'\n ? ` // Persistent default for \\`agentlink sync\\` (relative paths resolve from this file).\n \"source\": ${JSON.stringify(formatSourceForConfig(answers.target, answers.configPath))},\n\n`\n : '';\n\n return `{\n${sourceBlock} // Asset type allowlist. Default: [\"skills\", \"rules\", \"commands\"].\n // Add \"prompts\", \"templates\" or \"agents\" here to opt them in.\n \"include\": ${includeJson},\n\n // Asset type denylist (always wins over \\`include\\`).\n \"exclude\": [],\n\n // Per-adapter overrides. Each value can be:\n // - true : enable with built-in defaults\n // - false : disable even if its rootDir exists\n // - { \"layout\": { … } } : enable + override specific asset layouts\n //\n // Example: keep cursor commands nested instead of flattened\n // \"cursor\": { \"layout\": { \"commands\": { \"flatten\": false } } }\n \"adapters\": {\n${adapterLines}\n }\n}\n`;\n}\n\n/* ────────────────────────── Hints ───────────────────────────────────── */\n\nfunction printNonDefaultLocationHint(target: string): void {\n console.log('');\n console.log(pc.bold('Tip — your source is at a non-default location:'));\n console.log(` ${target}`);\n console.log('');\n console.log('Future invocations of `agentlink sync` will read `source` from your');\n console.log('`agentlink.config.json` (written during init). You can also:');\n console.log(` - pass ${pc.cyan(`--source ${target}`)} to override, or`);\n console.log(` - export ${pc.cyan(`AGENTLINK_SOURCE=${target}`)} in your shell rc, or`);\n console.log(` - symlink it: ${pc.cyan(`ln -s ${target} ~/.agents`)}`);\n}\n","import path from 'node:path';\nimport pc from 'picocolors';\n\nimport { buildLinkPlans, type Conflict } from '../core/layout.js';\nimport { applyPlans, findStaleLinks, removeLinks, type LinkOutcome } from '../core/linker.js';\nimport { AgentlinkError } from '../core/errors.js';\nimport {\n enumerateTargetDirs,\n loadContext,\n printHeader,\n type CommandFlags,\n} from './_shared.js';\n\nexport class StrictModeViolation extends AgentlinkError {\n constructor(public readonly conflicts: readonly Conflict[]) {\n super(`${conflicts.length} link target collision(s) under --strict`);\n }\n}\n\nexport async function runSync(flags: CommandFlags = {}): Promise<void> {\n const ctx = await loadContext(flags);\n printHeader(ctx, 'sync', flags);\n\n const { plans, collisions } = await buildLinkPlans({\n source: ctx.paths.source,\n destRoot: ctx.paths.destRoot,\n config: ctx.config,\n });\n\n if (collisions.length > 0) {\n reportCollisions(collisions, flags.quiet ?? false);\n if (flags.strict) {\n throw new StrictModeViolation(collisions);\n }\n }\n\n const outcomes = await applyPlans(plans, {\n ...(flags.dryRun !== undefined ? { dryRun: flags.dryRun } : {}),\n ...(flags.force !== undefined ? { force: flags.force } : {}),\n });\n\n // Sweep up dangling links left from previous syncs whose source files were\n // deleted. Mirrors the legacy init.sh behaviour where every run also cleans.\n const targetDirs = enumerateTargetDirs(ctx).map((t) => t.targetDir);\n const stale = (await Promise.all(targetDirs.map(findStaleLinks))).flat();\n const removed = await removeLinks(stale, flags.dryRun ? { dryRun: true } : {});\n\n reportOutcomes(outcomes, ctx.paths.destRoot, flags.quiet ?? false);\n reportRemovals(removed, flags.quiet ?? false);\n printSummary(outcomes, removed, collisions);\n\n // Any individual link operation that errored should fail the whole run so\n // CI / pre-commit hooks can detect a partially-broken sync. Conflicts are\n // already either warned (default) or thrown (via --strict above), so we\n // only consider link-level failures here.\n const errored =\n outcomes.filter((o) => o.status === 'errored').length +\n removed.filter((r) => r.status === 'errored').length;\n if (errored > 0) process.exitCode = 1;\n}\n\n/* ────────────────────────── Reporting helpers ────────────────────────── */\n\nfunction reportCollisions(collisions: readonly Conflict[], quiet: boolean): void {\n if (quiet) return;\n for (const c of collisions) {\n console.warn(\n pc.yellow(`[WARN] target collision under ${c.adapterName}/${c.assetType}: ${c.target}`),\n );\n for (const s of c.sources) console.warn(pc.dim(` source: ${s}`));\n }\n}\n\nfunction reportOutcomes(\n outcomes: readonly LinkOutcome[],\n destRoot: string,\n quiet: boolean,\n): void {\n for (const o of outcomes) {\n const display = path.relative(destRoot, o.plan.target);\n switch (o.status) {\n case 'created':\n if (!quiet) console.log(`${pc.green('[OK]')} ${display}`);\n break;\n case 'fixed':\n console.log(`${pc.green('[FIX]')} ${display} ${pc.dim(`(${o.message})`)}`);\n break;\n case 'skipped':\n if (!quiet) console.log(`${pc.cyan('[SKIP]')} ${display}`);\n break;\n case 'warned':\n console.warn(`${pc.yellow('[WARN]')} ${display} ${pc.dim(`(${o.message ?? ''})`)}`);\n break;\n case 'errored':\n console.error(`${pc.red('[ERR]')} ${display} ${pc.dim(`(${o.message ?? ''})`)}`);\n break;\n case 'dry-create':\n if (!quiet) console.log(`${pc.dim('[DRY]')} ${display}`);\n break;\n case 'dry-fix':\n console.log(`${pc.dim('[DRY]')} ${display} ${pc.dim(`(would fix: ${o.message})`)}`);\n break;\n }\n }\n}\n\nfunction reportRemovals(\n removals: readonly { path: string; status: string; message?: string }[],\n quiet: boolean,\n): void {\n for (const r of removals) {\n if (r.status === 'removed' && !quiet) {\n console.log(`${pc.yellow('[CLEAN]')} ${r.path}`);\n } else if (r.status === 'dry-remove' && !quiet) {\n console.log(`${pc.dim('[DRY]')} would clean ${r.path}`);\n } else if (r.status === 'errored') {\n console.error(`${pc.red('[ERR]')} ${r.path} ${pc.dim(`(${r.message ?? ''})`)}`);\n }\n }\n}\n\nfunction printSummary(\n outcomes: readonly LinkOutcome[],\n removals: readonly { status: string }[],\n collisions: readonly Conflict[],\n): void {\n const counts: Record<string, number> = {};\n for (const o of outcomes) counts[o.status] = (counts[o.status] ?? 0) + 1;\n const cleaned = removals.filter((r) => r.status === 'removed' || r.status === 'dry-remove').length;\n\n const parts: string[] = [];\n if (counts['created']) parts.push(pc.green(`${counts['created']} created`));\n if (counts['fixed']) parts.push(pc.green(`${counts['fixed']} fixed`));\n if (counts['skipped']) parts.push(pc.cyan(`${counts['skipped']} skipped`));\n if (counts['warned']) parts.push(pc.yellow(`${counts['warned']} warned`));\n if (counts['errored']) parts.push(pc.red(`${counts['errored']} errored`));\n if (counts['dry-create'] || counts['dry-fix']) {\n parts.push(pc.dim(`${(counts['dry-create'] ?? 0) + (counts['dry-fix'] ?? 0)} dry-run`));\n }\n if (cleaned) parts.push(pc.yellow(`${cleaned} cleaned`));\n if (collisions.length) parts.push(pc.yellow(`${collisions.length} collision(s)`));\n\n console.log('');\n console.log(parts.length === 0 ? pc.dim(' no changes') : ` ${parts.join(', ')}`);\n}\n","import pc from 'picocolors';\n\nimport { findManagedLinks, pruneEmptyDirs, removeLinks } from '../core/linker.js';\nimport {\n enumerateAllAdapterRoots,\n loadContext,\n printHeader,\n type CommandFlags,\n} from './_shared.js';\n\nexport async function runUnlink(flags: CommandFlags = {}): Promise<void> {\n const ctx = await loadContext(flags);\n printHeader(ctx, 'unlink', flags);\n\n // `unlink` must be a \"remove everything we ever wrote\" operation. Walk\n // *all* built-in adapter rootDirs that exist under destRoot — not only\n // the ones currently enabled by config — and recurse into each so we\n // also catch links living in asset subdirs that the current `include`\n // list no longer mentions. Otherwise users who disabled an adapter or\n // narrowed `include` would leave orphan symlinks behind.\n const roots = await enumerateAllAdapterRoots(ctx.paths.destRoot);\n const sourceRoot = ctx.paths.source;\n\n const owned = (\n await Promise.all(\n roots.map(({ rootDir }) => findManagedLinks(rootDir, sourceRoot, { recursive: true })),\n )\n ).flat();\n\n if (owned.length === 0) {\n console.log(pc.dim(' no agentlink-managed links found'));\n return;\n }\n\n const outcomes = await removeLinks(owned, flags.dryRun ? { dryRun: true } : {});\n\n let removed = 0;\n let dry = 0;\n let errored = 0;\n for (const o of outcomes) {\n if (o.status === 'removed') {\n removed++;\n if (!flags.quiet) console.log(`${pc.yellow('[UNLINK]')} ${o.path}`);\n } else if (o.status === 'dry-remove') {\n dry++;\n if (!flags.quiet) console.log(`${pc.dim('[DRY]')} would remove ${o.path}`);\n } else if (o.status === 'errored') {\n errored++;\n console.error(`${pc.red('[ERR]')} ${o.path} ${pc.dim(`(${o.message ?? ''})`)}`);\n }\n }\n\n // Best-effort: prune now-empty adapter rootDirs. `pruneEmptyDirs` is silent\n // on non-empty / missing dirs, so we don't bother filtering.\n if (!flags.dryRun) {\n await pruneEmptyDirs(roots.map((r) => r.rootDir));\n }\n\n console.log('');\n const parts: string[] = [];\n if (removed) parts.push(pc.yellow(`${removed} removed`));\n if (dry) parts.push(pc.dim(`${dry} dry-run`));\n if (errored) parts.push(pc.red(`${errored} errored`));\n console.log(parts.length === 0 ? pc.dim(' no changes') : ` ${parts.join(', ')}`);\n\n if (errored > 0) process.exitCode = 1;\n}\n"],"mappings":";AAAA,SAAS,WAAW;AACpB,OAAOA,SAAQ;;;ACDf,OAAOC,SAAQ;;;ACef,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AA8BjB,eAAsB,WACpB,OACA,UAAwB,CAAC,GACD;AACxB,QAAM,WAA0B,CAAC;AACjC,aAAW,QAAQ,OAAO;AACxB,aAAS,KAAK,MAAM,SAAS,MAAM,OAAO,CAAC;AAAA,EAC7C;AACA,SAAO;AACT;AAEA,eAAe,SAAS,MAAgB,SAA6C;AACnF,QAAM,EAAE,SAAS,OAAO,QAAQ,MAAM,IAAI;AAC1C,QAAM,YAAY,KAAK,QAAQ,KAAK,MAAM;AAC1C,QAAM,WAAW,KAAK,SAAS,WAAW,KAAK,MAAM;AAErD,MAAI;AACF,UAAM,QAAQ,MAAM,GAAG,MAAM,KAAK,MAAM,EAAE,MAAM,MAAM,IAAI;AAE1D,QAAI,OAAO,eAAe,GAAG;AAC3B,YAAM,UAAU,MAAM,GAAG,SAAS,KAAK,MAAM;AAC7C,UAAI,YAAY,SAAU,QAAO,EAAE,MAAM,QAAQ,UAAU;AAC3D,UAAI,OAAO;AACT,YAAI,QAAQ;AACV,iBAAO,EAAE,MAAM,QAAQ,WAAW,SAAS,GAAG,OAAO,OAAO,QAAQ,GAAG;AAAA,QACzE;AACA,cAAM,GAAG,OAAO,KAAK,MAAM;AAC3B,cAAM,GAAG,QAAQ,UAAU,KAAK,MAAM;AACtC,eAAO,EAAE,MAAM,QAAQ,SAAS,SAAS,GAAG,OAAO,OAAO,QAAQ,GAAG;AAAA,MACvE;AACA,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,SAAS,aAAa,OAAO;AAAA,MAC/B;AAAA,IACF;AAEA,QAAI,OAAO;AACT,YAAM,OAAO,MAAM,YAAY,IAAI,cAAc;AACjD,aAAO,EAAE,MAAM,QAAQ,UAAU,SAAS,GAAG,IAAI,4BAA4B;AAAA,IAC/E;AAEA,QAAI,QAAQ;AACV,aAAO,EAAE,MAAM,QAAQ,aAAa;AAAA,IACtC;AAEA,UAAM,GAAG,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC7C,UAAM,GAAG,QAAQ,UAAU,KAAK,MAAM;AACtC,WAAO,EAAE,MAAM,QAAQ,UAAU;AAAA,EACnC,SAAS,KAAK;AACZ,WAAO,EAAE,MAAM,QAAQ,WAAW,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,EAC9F;AACF;AAYA,eAAsB,eAAe,WAAsC;AACzE,QAAM,UAAU,MAAM,GAAG,QAAQ,WAAW,EAAE,eAAe,KAAK,CAAC,EAAE,MAAM,MAAM,IAAI;AACrF,MAAI,CAAC,QAAS,QAAO,CAAC;AAEtB,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,eAAe,EAAG;AAC7B,UAAM,OAAO,KAAK,KAAK,WAAW,MAAM,IAAI;AAC5C,UAAM,SAAS,MAAM,GAAG,KAAK,IAAI,EAAE,MAAM,MAAM,IAAI;AACnD,QAAI,CAAC,OAAQ,OAAM,KAAK,IAAI;AAAA,EAC9B;AACA,SAAO;AACT;AAiBA,eAAsB,iBACpB,WACA,YACA,UAAmC,CAAC,GACjB;AACnB,QAAM,YAAY,KAAK,QAAQ,UAAU;AACzC,QAAM,MAAgB,CAAC;AAEvB,SAAO,eAAe,QAAQ,KAA4B;AACxD,UAAM,UAAU,MAAM,GAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC,EAAE,MAAM,MAAM,IAAI;AAC/E,QAAI,CAAC,QAAS;AACd,eAAW,SAAS,SAAS;AAC3B,YAAM,OAAO,KAAK,KAAK,KAAK,MAAM,IAAI;AACtC,UAAI,MAAM,eAAe,GAAG;AAC1B,cAAM,MAAM,MAAM,GAAG,SAAS,IAAI,EAAE,MAAM,MAAM,IAAI;AACpD,YAAI,CAAC,IAAK;AACV,cAAM,WAAW,KAAK,WAAW,GAAG,IAAI,KAAK,UAAU,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACnF,YAAI,aAAa,aAAa,SAAS,WAAW,YAAY,KAAK,GAAG,GAAG;AACvE,cAAI,KAAK,IAAI;AAAA,QACf;AAAA,MACF,WAAW,QAAQ,aAAa,MAAM,YAAY,GAAG;AACnD,cAAM,QAAQ,IAAI;AAAA,MACpB;AAAA,IACF;AAAA,EACF,GAAG,SAAS;AAEZ,SAAO;AACT;AAoBA,eAAsB,YACpB,OACA,UAAyB,CAAC,GACC;AAC3B,QAAM,EAAE,SAAS,MAAM,IAAI;AAC3B,QAAM,MAAwB,CAAC;AAC/B,aAAW,KAAK,OAAO;AACrB,QAAI,QAAQ;AACV,UAAI,KAAK,EAAE,MAAM,GAAG,QAAQ,aAAa,CAAC;AAC1C;AAAA,IACF;AACA,QAAI;AACF,YAAM,GAAG,OAAO,CAAC;AACjB,UAAI,KAAK,EAAE,MAAM,GAAG,QAAQ,UAAU,CAAC;AAAA,IACzC,SAAS,KAAK;AACZ,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MAC1D,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAOA,eAAsB,eAAe,MAAwC;AAC3E,aAAW,OAAO,MAAM;AACtB,UAAM,GAAG,MAAM,GAAG,EAAE,MAAM,MAAM,MAAS;AAAA,EAC3C;AACF;;;ACpNA,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AACjB,OAAO,QAAQ;;;ACJR,IAAM,SAAkB;AAAA,EAC7B,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,IACN,QAAQ,EAAE,cAAc,SAAS;AAAA,IACjC,OAAO,EAAE,cAAc,QAAQ;AAAA,IAC/B,UAAU,EAAE,cAAc,WAAW;AAAA,EACvC;AACF;;;ACPO,IAAM,YAAqB;AAAA,EAChC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,IACN,QAAQ,EAAE,cAAc,SAAS;AAAA,IACjC,OAAO,EAAE,cAAc,QAAQ;AAAA,IAC/B,UAAU,EAAE,cAAc,WAAW;AAAA,EACvC;AACF;;;ACNO,IAAM,SAAkB;AAAA,EAC7B,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,IACN,QAAQ,EAAE,cAAc,SAAS;AAAA,IACjC,OAAO,EAAE,cAAc,SAAS,SAAS,MAAM,KAAK,OAAO;AAAA,IAC3D,UAAU,EAAE,cAAc,YAAY,SAAS,KAAK;AAAA,IACpD,SAAS;AAAA,EACX;AACF;;;ACRO,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF;;;ACWA,SAAS,YAAYC,WAAU;AAC/B,OAAO,QAAQ;AACf,OAAOC,WAAU;AACjB,OAAO,uBAAuB;AAC9B,SAAS,SAAS;;;ACrBX,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO,WAAW;AAAA,EACzB;AACF;AAOO,IAAM,sBAAN,cAAkC,eAAe;AAAA,EAC7C;AAAA,EAET,YAAY,UAA6D;AACvE,UAAM,oCAAoC;AAC1C,SAAK,WAAW;AAAA,EAClB;AACF;AAOO,IAAM,mBAAN,MAAM,0BAAyB,eAAe;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,MAAc,GAAW,QAAsD;AACzF,UAAM,kBAAiB,QAAQ,MAAM,GAAG,MAAM,CAAC;AAC/C,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,OAAe,QACb,MACA,GACA,QACQ;AACR,QAAI,WAAW,UAAW,QAAO,GAAG,IAAI,oBAAoB,CAAC;AAC7D,QAAI,WAAW,kBAAmB,QAAO,GAAG,IAAI,wBAAwB,CAAC;AACzE,WAAO,GAAG,IAAI,mBAAmB,CAAC;AAAA,EACpC;AACF;AAGO,IAAM,gBAAN,cAA4B,eAAe;AAAC;;;ACwD5C,IAAM,kBAAkB,CAAC,UAAU,SAAS,UAAU;;;AFzEtD,IAAM,kBAAkB;AAC/B,IAAM,iBAAiB;AAEvB,IAAM,oBAAoB,CAAC,UAAU,SAAS,YAAY,WAAW,aAAa,QAAQ;AAC1F,IAAM,iBAAyC,IAAI,IAAI,iBAAiB;AAExE,IAAM,kBAAkB,EAAE,KAAK,iBAAiB;AAWhD,IAAM,qBAAqB,EACxB,OAAO,EACP,IAAI,CAAC,EACL,OAAO,CAAC,MAAM,CAACC,MAAK,WAAW,CAAC,GAAG;AAAA,EAClC,SAAS;AACX,CAAC,EACA,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,OAAO,EAAE,KAAK,CAAC,QAAQ,QAAQ,IAAI,GAAG;AAAA,EAC5D,SAAS;AACX,CAAC;AAEH,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACjC,cAAc,mBAAmB,SAAS;AAAA,EAC1C,SAAS,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC9B,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS;AAAA,EAC7C,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS;AAAA,EAC7C,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAClC,CAAC;AAED,IAAM,wBAAwB,EAAE,MAAM;AAAA,EACpC,EAAE,QAAQ;AAAA,EACV,EACG,OAAO;AAAA;AAAA;AAAA,IAGN,QAAQ,EAAE,cAAc,iBAAiB,iBAAiB,EAAE,SAAS;AAAA,EACvE,CAAC,EACA,OAAO;AACZ,CAAC;AAED,IAAM,oBAAoB,EAAE;AAAA,EAC1B,OAAO,KAAK,eAAe;AAC7B;AAEA,IAAM,iBAAiB,EAAE,OAAO,EAAE,IAAI,CAAC;AAMvC,IAAM,eAAe,EAClB,OAAO;AAAA,EACN,QAAQ,eAAe,SAAS;AAAA,EAChC,QAAQ,eAAe,SAAS;AAAA,EAChC,MAAM,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC3B,SAAS,EAAE,MAAM,eAAe,EAAE,SAAS;AAAA,EAC3C,SAAS,EAAE,MAAM,eAAe,EAAE,SAAS;AAAA,EAC3C,UAAU,EAAE,cAAc,mBAAmB,qBAAqB,EAAE,SAAS;AAC/E,CAAC,EACA,OAAO,EACP,YAAY,CAAC,KAAK,QAAQ;AACzB,MAAI,IAAI,WAAW,UAAa,IAAI,SAAS,MAAM;AACjD,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM,CAAC,QAAQ;AAAA,IACjB,CAAC;AAAA,EACH;AACF,CAAC;AAII,IAAM,mBAAN,cAA+B,eAAe;AAAA,EAC1C;AAAA,EAET,YAAY,UAAkB,QAA0D;AACtF,UAAM,QAAQ,OAAO,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,MAAM,QAAQ,KAAK,QAAQ,KAAK,OAAO,EAAE;AACtF,UAAM,+BAA+B,QAAQ;AAAA,EAAK,MAAM,KAAK,IAAI,CAAC,EAAE;AACpE,SAAK,SAAS;AAAA,EAChB;AACF;AAqCA,eAAsB,iBAAiB,OAA6B,CAAC,GAA0B;AAC7F,QAAM,WAAW,MAAM,kBAAkB,IAAI;AAC7C,QAAM,SAAS,SAAS,OAAO,MAAM,eAAe,SAAS,IAAI,IAAK,CAAC;AACvE,QAAM,aAAa,SAAS,OAAOA,MAAK,QAAQ,SAAS,IAAI,IAAI;AACjE,SAAO,EAAE,UAAU,QAAQ,WAAW;AACxC;AAMO,SAAS,wBACd,YACA,UACA,QACgB;AAChB,QAAM,WAAW,cAAc,WAAW,QAAQ;AAClD,QAAM,aAAa,kBAAkB,WAAW,SAAS,WAAW,OAAO;AAE3E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,SAAS;AAAA,IACrB,cAAc,SAAS;AAAA,EACzB;AACF;AAeA,eAAsB,kBAAkB,OAA6B,CAAC,GAA4B;AAChG,QAAM,MAAM,KAAK,OAAO,QAAQ,IAAI;AACpC,QAAM,OAAO,KAAK,QAAQ,GAAG,QAAQ;AACrC,QAAM,MAAM,KAAK,OAAO,QAAQ;AAEhC,MAAI,KAAK,WAAW,UAAa,KAAK,WAAW,IAAI;AACnD,UAAM,MAAM,MAAM,iBAAiB,YAAY,KAAK,QAAQ,IAAI,GAAG,YAAY,GAAG;AAClF,WAAO,EAAE,MAAM,KAAK,QAAQ,OAAO;AAAA,EACrC;AAEA,QAAM,WAAW,IAAI,cAAc;AACnC,MAAI,aAAa,UAAa,aAAa,IAAI;AAC7C,UAAM,MAAM,MAAM,iBAAiB,YAAY,UAAU,IAAI,GAAG,IAAI,cAAc,IAAI,GAAG;AACzF,WAAO,EAAE,MAAM,KAAK,QAAQ,MAAM;AAAA,EACpC;AAEA,QAAM,WAAW,MAAM,gBAAgBC,MAAK,QAAQ,GAAG,GAAG,IAAI;AAC9D,MAAI,UAAU;AACZ,WAAO,EAAE,MAAM,UAAU,QAAQ,eAAe;AAAA,EAClD;AAEA,QAAM,aAAaA,MAAK,KAAK,MAAM,eAAe;AAClD,MAAI,MAAM,OAAO,UAAU,GAAG;AAC5B,WAAO,EAAE,MAAM,YAAY,QAAQ,cAAc;AAAA,EACnD;AAEA,SAAO,EAAE,MAAM,MAAM,QAAQ,WAAW;AAC1C;AAOA,eAAe,gBAAgB,OAAe,MAAsC;AAClF,QAAM,UAAUA,MAAK,QAAQ,IAAI;AACjC,MAAI,UAAU;AACd,SAAO,MAAM;AACX,QAAI,YAAY,SAAS;AAGvB,aAAO;AAAA,IACT;AAEA,UAAM,YAAYA,MAAK,KAAK,SAAS,eAAe;AACpD,QAAI,MAAM,OAAO,SAAS,EAAG,QAAO;AAEpC,UAAM,SAASA,MAAK,QAAQ,OAAO;AACnC,QAAI,WAAW,QAAS,QAAO;AAI/B,QAAI,SAAS,SAAS,OAAO,KAAK,CAAC,SAAS,QAAQ,OAAO,KAAK,WAAW,SAAS;AAClF,aAAO;AAAA,IACT;AACA,cAAU;AAAA,EACZ;AACF;AAEA,eAAe,iBAAiB,OAAe,MAAc,KAA8B;AACzF,QAAM,MAAMA,MAAK,WAAW,KAAK,IAAI,QAAQA,MAAK,QAAQ,KAAK,KAAK;AACpE,QAAM,OAAO,MAAMC,IAAG,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI;AAChD,MAAI,CAAC,KAAM,OAAM,IAAI,iBAAiB,MAAM,KAAK,SAAS;AAC1D,MAAI,CAAC,KAAK,OAAO,EAAG,OAAM,IAAI,iBAAiB,MAAM,KAAK,YAAY;AACtE,SAAO;AACT;AAEA,eAAe,OAAO,GAA6B;AACjD,QAAM,OAAO,MAAMA,IAAG,KAAK,CAAC,EAAE,MAAM,MAAM,IAAI;AAC9C,SAAO,MAAM,OAAO,KAAK;AAC3B;AAEA,SAAS,YAAY,GAAW,MAAsB;AACpD,MAAI,MAAM,IAAK,QAAO;AACtB,MAAI,EAAE,WAAW,IAAI,EAAG,QAAOD,MAAK,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC;AACzD,SAAO;AACT;AAEA,SAAS,SAAS,OAAe,QAAyB;AACxD,QAAM,IAAIA,MAAK,QAAQ,KAAK;AAC5B,QAAM,IAAIA,MAAK,QAAQ,MAAM;AAC7B,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,MAAMA,MAAK,SAAS,GAAG,CAAC;AAC9B,SAAO,QAAQ,MAAM,CAAC,IAAI,WAAW,IAAI,KAAK,CAACA,MAAK,WAAW,GAAG;AACpE;AAcA,eAAsB,qBACpB,YAC8B;AAC9B,MAAI,CAAC,WAAY,QAAO,oBAAI,IAAI;AAChC,QAAM,MAAM,MAAMC,IAAG,SAAS,YAAY,MAAM,EAAE,MAAM,MAAM,IAAI;AAClE,MAAI,QAAQ,KAAM,QAAO,oBAAI,IAAI;AACjC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,kBAAkB,KAAK,EAAE,gBAAgB,KAAK,CAAC,CAAC;AAG1E,QAAI,CAAC,OAAO,SAAU,QAAO,oBAAI,IAAI;AACrC,WAAO,IAAI,IAAI,OAAO,KAAK,OAAO,QAAQ,EAAE,OAAO,CAAC,MAAM,OAAO,WAAW,CAAC,MAAM,KAAK,CAAC;AAAA,EAC3F,QAAQ;AACN,WAAO,oBAAI,IAAI;AAAA,EACjB;AACF;AAEA,eAAe,eAAe,UAAyC;AACrE,QAAM,MAAM,MAAMA,IAAG,SAAS,UAAU,MAAM,EAAE,MAAM,CAAC,QAA+B;AACpF,QAAI,IAAI,SAAS,SAAU,QAAO;AAClC,UAAM;AAAA,EACR,CAAC;AACD,MAAI,QAAQ,KAAM,QAAO,CAAC;AAE1B,MAAI;AACJ,MAAI;AAGF,aAAS,KAAK,MAAM,kBAAkB,KAAK,EAAE,gBAAgB,KAAK,CAAC,CAAC;AAAA,EACtE,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,IAAI,iBAAiB,UAAU,CAAC,EAAE,MAAM,IAAI,SAAS,mBAAmB,OAAO,GAAG,CAAC,CAAC;AAAA,EAC5F;AAEA,QAAM,SAAS,aAAa,UAAU,MAAM;AAC5C,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OAAO,IAAI,CAAC,WAAW;AAAA,MACjD,MAAM,MAAM,KAAK,IAAI,CAAC,QAAQ,OAAO,GAAG,CAAC,EAAE,KAAK,GAAG;AAAA,MACnD,SAAS,MAAM;AAAA,IACjB,EAAE;AACF,UAAM,IAAI,iBAAiB,UAAU,MAAM;AAAA,EAC7C;AACA,SAAO,OAAO;AAChB;AAOA,SAAS,cACP,WACoB;AACpB,QAAM,SAAoB,CAAC;AAE3B,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,eAAe,GAAoC;AAC7F,UAAM,WAAW,YAAY,IAAI;AAEjC,QAAI,aAAa,MAAO;AAExB,QAAI,aAAa,UAAa,aAAa,MAAM;AAC/C,aAAO,KAAK,MAAM;AAClB;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,MACV,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB,QAAQ,YAAY,OAAO,QAAQ,SAAS,MAAM;AAAA,IACpD,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAiBA,SAAS,YACP,MACA,OACmB;AACnB,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,SAA4B,EAAE,GAAG,KAAK;AAC5C,aAAW,OAAO,OAAO,KAAK,KAAK,GAAkB;AACnD,UAAM,QAAQ,MAAM,GAAG;AACvB,QAAI,UAAU,OAAW;AAEzB,UAAM,aAAa,KAAK,GAAG;AAC3B,QAAI,eAAe,UAAa,eAAe,OAAO;AACpD,YAAM,eAAe,MAAM;AAC3B,UAAI,iBAAiB,QAAW;AAC9B,cAAM,IAAI;AAAA,UACR,yBAAyB,GAAG;AAAA,QAC9B;AAAA,MACF;AACA,aAAO,GAAG,IAAI,cAAc,cAAc,KAAK;AAC/C;AAAA,IACF;AACA,WAAO,GAAG,IAAI,cAAc,YAAY,KAAK;AAAA,EAC/C;AACA,SAAO;AACT;AAGA,SAAS,cAAc,cAAsB,GAAkC;AAC7E,QAAM,MAA8D,EAAE,aAAa;AACnF,MAAI,EAAE,YAAY,OAAW,KAAI,UAAU,EAAE;AAC7C,MAAI,EAAE,YAAY,OAAW,KAAI,UAAU,EAAE;AAC7C,MAAI,EAAE,YAAY,OAAW,KAAI,UAAU,EAAE;AAC7C,MAAI,EAAE,QAAQ,OAAW,KAAI,MAAM,EAAE;AACrC,SAAO;AACT;AAEA,SAAS,cAAc,MAAmB,OAAsC;AAC9E,QAAM,MAA8D,EAAE,GAAG,KAAK;AAC9E,MAAI,MAAM,iBAAiB,OAAW,KAAI,eAAe,MAAM;AAC/D,MAAI,MAAM,YAAY,OAAW,KAAI,UAAU,MAAM;AACrD,MAAI,MAAM,YAAY,OAAW,KAAI,UAAU,MAAM;AACrD,MAAI,MAAM,YAAY,OAAW,KAAI,UAAU,MAAM;AACrD,MAAI,MAAM,QAAQ,OAAW,KAAI,MAAM,MAAM;AAC7C,SAAO;AACT;AAEA,IAAM,mBAAN,cAA+B,eAAe;AAAC;AAO/C,SAAS,kBACP,SACA,SACqB;AACrB,QAAM,aAAa,IAAI,IAAY,WAAW,eAAe;AAC7D,aAAW,UAAU,WAAW,CAAC,GAAG;AAClC,eAAW,OAAO,MAAM;AAAA,EAC1B;AACA,SAAO;AACT;;;AGjbA,SAAS,YAAYC,WAAU;AAC/B,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAuCjB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AAMvB,eAAsB,aAAa,OAAuB,CAAC,GAA2B;AACpF,QAAM,CAAC,KAAK,GAAG,IAAI,MAAM,QAAQ,IAAI,CAAC,cAAc,IAAI,GAAG,gBAAgB,IAAI,CAAC,CAAC;AAEjF,MAAIC,UAAS,IAAI,MAAM,IAAI,IAAI,GAAG;AAChC,YAAQ;AAAA,MACN,qBAAqB,IAAI,IAAI,0BAA0B,IAAI,IAAI;AAAA,MAC/D,EAAE,MAAM,wBAAwB;AAAA,IAClC;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,IAAI;AAAA,IACZ,cAAc,IAAI;AAAA,IAClB,UAAU,IAAI;AAAA,IACd,YAAY,IAAI;AAAA,EAClB;AACF;AAEA,eAAsB,cACpB,OAAuB,CAAC,GACyB;AACjD,QAAM,MAAM,KAAK,OAAO,QAAQ,IAAI;AACpC,QAAM,OAAO,KAAK,QAAQC,IAAG,QAAQ;AACrC,QAAM,MAAM,KAAK,OAAO,QAAQ;AAEhC,MAAI,KAAK,WAAW,UAAa,KAAK,WAAW,IAAI;AACnD,UAAM,MAAM,MAAM,UAAUC,aAAY,KAAK,QAAQ,IAAI,GAAG,YAAY,GAAG;AAC3E,WAAO,EAAE,MAAM,KAAK,QAAQ,OAAO;AAAA,EACrC;AAEA,QAAM,WAAW,IAAI,cAAc;AACnC,MAAI,aAAa,UAAa,aAAa,IAAI;AAC7C,UAAM,MAAM,MAAM,UAAUA,aAAY,UAAU,IAAI,GAAG,IAAI,cAAc,IAAI,GAAG;AAClF,WAAO,EAAE,MAAM,KAAK,QAAQ,MAAM;AAAA,EACpC;AAEA,QAAM,QAAQC,MAAK,KAAK,KAAK,cAAc;AAC3C,MAAI,MAAM,MAAM,KAAK,GAAG;AACtB,WAAO,EAAE,MAAM,OAAO,QAAQ,YAAY;AAAA,EAC5C;AAEA,QAAM,eAAe,KAAK;AAC1B,MAAI,iBAAiB,UAAa,iBAAiB,IAAI;AACrD,UAAM,OAAO,KAAK,cAAc;AAChC,UAAM,MAAM,MAAM;AAAA,MAChB,uBAAuB,cAAc,MAAM,IAAI;AAAA,MAC/C;AAAA,MACA;AAAA,IACF;AACA,WAAO,EAAE,MAAM,KAAK,QAAQ,SAAS;AAAA,EACvC;AAIA,QAAM,aAAaA,MAAK,KAAK,MAAM,cAAc;AACjD,MAAI,MAAM,MAAM,UAAU,GAAG;AAC3B,WAAO,EAAE,MAAM,YAAY,QAAQ,cAAc;AAAA,EACnD;AAEA,QAAM,IAAI,oBAAoB;AAAA,IAC5B,EAAE,QAAQ,iBAAiB,QAAQ,eAAe;AAAA,IAClD,EAAE,QAAQ,IAAI,cAAc,IAAI,QAAQ,UAAU;AAAA,IAClD,EAAE,QAAQ,OAAO,QAAQ,iBAAiB,KAAK,EAAE;AAAA,IACjD,EAAE,QAAQ,iBAAiB,QAAQ,mBAAmB,IAAI,EAAE;AAAA,IAC5D,EAAE,QAAQ,YAAY,QAAQ,UAAU;AAAA,EAC1C,CAAC;AACH;AAEA,SAAS,iBAAiB,OAAuB;AAC/C,SAAO,eAAe,KAAK;AAC7B;AAEA,SAAS,mBAAmB,MAA8B;AACxD,MAAI,KAAK,iBAAiB,UAAa,KAAK,iBAAiB,IAAI;AAC/D,WAAO;AAAA,EACT;AACA,MAAI,KAAK,oBAAoB;AAC3B,WAAO,cAAc,KAAK,kBAAkB;AAAA,EAC9C;AACA,SAAO;AACT;AAEA,eAAsB,gBACpB,OAAuB,CAAC,GACuB;AAC/C,QAAM,MAAM,KAAK,OAAO,QAAQ,IAAI;AACpC,QAAM,OAAO,KAAK,QAAQF,IAAG,QAAQ;AAErC,MAAI,KAAK,WAAW,UAAa,KAAK,WAAW,MAAM,KAAK,MAAM;AAChE,UAAM,IAAI,cAAc,4CAA4C;AAAA,EACtE;AAEA,MAAI,KAAK,WAAW,UAAa,KAAK,WAAW,IAAI;AACnD,UAAM,MAAM,MAAM,UAAUC,aAAY,KAAK,QAAQ,IAAI,GAAG,YAAY,GAAG;AAC3E,WAAO,EAAE,MAAM,KAAK,QAAQ,OAAO;AAAA,EACrC;AAEA,MAAI,KAAK,MAAM;AACb,WAAO,EAAE,MAAM,MAAM,QAAQ,OAAO;AAAA,EACtC;AAEA,MAAI,KAAK,iBAAiB,UAAa,KAAK,iBAAiB,MAAM,KAAK,YAAY;AAClF,UAAM,IAAI,cAAc,mDAAmD;AAAA,EAC7E;AAEA,MAAI,KAAK,iBAAiB,UAAa,KAAK,iBAAiB,IAAI;AAC/D,UAAM,OAAO,KAAK,cAAc;AAChC,UAAM,MAAM,MAAM;AAAA,MAChB,uBAAuB,KAAK,cAAc,MAAM,IAAI;AAAA,MACpD;AAAA,MACA;AAAA,IACF;AACA,WAAO,EAAE,MAAM,KAAK,QAAQ,SAAS;AAAA,EACvC;AAEA,MAAI,KAAK,YAAY;AACnB,WAAO,EAAE,MAAM,MAAM,QAAQ,SAAS;AAAA,EACxC;AAEA,SAAO,EAAE,MAAM,KAAK,QAAQ,MAAM;AACpC;AAMO,SAAS,uBAAuB,OAAe,YAAoB,MAAsB;AAC9F,QAAM,WAAWA,aAAY,OAAO,IAAI;AACxC,SAAOC,MAAK,WAAW,QAAQ,IAAI,WAAWA,MAAK,QAAQ,YAAY,QAAQ;AACjF;AAMO,SAASD,aAAY,GAAW,MAAsB;AAC3D,MAAI,MAAM,IAAK,QAAO;AACtB,MAAI,EAAE,WAAW,IAAI,EAAG,QAAOC,MAAK,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC;AACzD,SAAO;AACT;AAEA,eAAe,UAAU,OAAe,MAAc,KAA8B;AAClF,QAAM,MAAMA,MAAK,WAAW,KAAK,IAAI,QAAQA,MAAK,QAAQ,KAAK,KAAK;AACpE,QAAM,OAAO,MAAMC,IAAG,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI;AAChD,MAAI,CAAC,KAAM,OAAM,IAAI,iBAAiB,MAAM,KAAK,SAAS;AAC1D,MAAI,CAAC,KAAK,YAAY,EAAG,OAAM,IAAI,iBAAiB,MAAM,KAAK,iBAAiB;AAChF,SAAO;AACT;AAEA,eAAe,MAAM,GAA6B;AAChD,QAAM,OAAO,MAAMA,IAAG,KAAK,CAAC,EAAE,MAAM,MAAM,IAAI;AAC9C,SAAO,MAAM,YAAY,KAAK;AAChC;AAQO,SAASJ,UAAS,OAAe,QAAyB;AAC/D,QAAM,IAAIG,MAAK,QAAQ,KAAK;AAC5B,QAAM,IAAIA,MAAK,QAAQ,MAAM;AAC7B,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,MAAMA,MAAK,SAAS,GAAG,CAAC;AAC9B,SAAO,QAAQ,MAAM,CAAC,IAAI,WAAW,IAAI,KAAK,CAACA,MAAK,WAAW,GAAG;AACpE;;;AR3MA,eAAsB,YAAY,OAA8C;AAC9E,QAAM,SAAS,MAAM,iBAAiB;AAAA,IACpC,GAAI,MAAM,WAAW,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,EAC/D,CAAC;AAED,QAAM,QAAQ,MAAM,aAAa;AAAA,IAC/B,GAAI,MAAM,WAAW,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC7D,GAAI,MAAM,WAAW,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC7D,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACvD,GAAI,OAAO,OAAO,WAAW,SAAY,EAAE,cAAc,OAAO,OAAO,OAAO,IAAI,CAAC;AAAA,IACnF,GAAI,OAAO,OAAO,WAAW,SAAY,EAAE,cAAc,OAAO,OAAO,OAAO,IAAI,CAAC;AAAA,IACnF,GAAI,OAAO,OAAO,SAAS,SAAY,EAAE,YAAY,OAAO,OAAO,KAAK,IAAI,CAAC;AAAA,IAC7E,GAAI,OAAO,eAAe,OAAO,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,IACtE,oBAAoB,OAAO,SAAS;AAAA,EACtC,CAAC;AAED,QAAM,YAAY,wBAAwB,OAAO,QAAQ,OAAO,UAAU,MAAM,MAAM;AACtF,QAAM,SAAS,MAAM,aAAa,WAAW,MAAM,QAAQ;AAC3D,SAAO,EAAE,OAAO,OAAO;AACzB;AAUA,eAAe,aAAa,QAAwB,UAA2C;AAC7F,QAAM,OAAkB,CAAC;AACzB,aAAW,WAAW,OAAO,UAAU;AACrC,UAAM,OAAOE,MAAK,KAAK,UAAU,QAAQ,OAAO;AAChD,UAAM,OAAO,MAAMC,IAAG,KAAK,IAAI,EAAE,MAAM,MAAM,IAAI;AACjD,QAAI,MAAM,YAAY,EAAG,MAAK,KAAK,OAAO;AAAA,EAC5C;AACA,MAAI,KAAK,WAAW,OAAO,SAAS,OAAQ,QAAO;AACnD,SAAO,EAAE,GAAG,QAAQ,UAAU,KAAK;AACrC;AAEO,SAAS,YAAY,KAAqB,MAAc,OAA2B;AACxF,MAAI,MAAM,MAAO;AACjB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,GAAG,KAAK,eAAe,IAAI,EAAE,CAAC;AAC1C,UAAQ,IAAI,eAAe,IAAI,MAAM,MAAM,IAAI,GAAG,IAAI,IAAI,IAAI,MAAM,YAAY,GAAG,CAAC,EAAE;AACtF,UAAQ,IAAI,eAAe,IAAI,MAAM,QAAQ,IAAI,GAAG,IAAI,IAAI,IAAI,MAAM,UAAU,GAAG,CAAC,EAAE;AACtF,QAAM,aAAa,IAAI,OAAO,aAC1B,GAAG,IAAI,OAAO,UAAU,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,YAAY,GAAG,CAAC,KAClE,GAAG,IAAI,YAAY;AACvB,UAAQ,IAAI,eAAe,UAAU,EAAE;AACvC,UAAQ,IAAI,eAAe,IAAI,OAAO,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,KAAK,GAAG,IAAI,QAAQ,CAAC,EAAE;AAClG,MAAI,MAAM,OAAQ,SAAQ,IAAI,GAAG,OAAO,qBAAqB,CAAC;AAC9D,UAAQ,IAAI,EAAE;AAChB;AAOO,SAAS,oBAAoB,KAKhC;AACF,QAAM,MAAwF,CAAC;AAC/F,QAAM,OAAO,IAAI,MAAM;AACvB,aAAW,WAAW,IAAI,OAAO,UAAU;AACzC,eAAW,aAAa,IAAI,OAAO,YAAsC;AACvE,YAAM,OAAO,QAAQ,OAAO,SAAS;AACrC,UAAI,SAAS,UAAa,SAAS,MAAO;AAC1C,UAAI,KAAK;AAAA,QACP;AAAA,QACA;AAAA,QACA,WAAWD,MAAK,KAAK,MAAM,QAAQ,SAAS,KAAK,YAAY;AAAA,QAC7D,SAASA,MAAK,KAAK,MAAM,QAAQ,OAAO;AAAA,MAC1C,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAaA,eAAsB,yBACpB,UAC8C;AAC9C,QAAM,MAA2C,CAAC;AAClD,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,eAAe,GAAG;AAC5D,UAAM,UAAUA,MAAK,KAAK,UAAU,OAAO,OAAO;AAClD,UAAM,OAAO,MAAMC,IAAG,KAAK,OAAO,EAAE,MAAM,MAAM,IAAI;AACpD,QAAI,MAAM,YAAY,EAAG,KAAI,KAAK,EAAE,MAAM,QAAQ,CAAC;AAAA,EACrD;AACA,SAAO;AACT;;;AFhIA,eAAsB,SAAS,QAAsB,CAAC,GAAkB;AACtE,QAAM,MAAM,MAAM,YAAY,KAAK;AACnC,cAAY,KAAK,SAAS,KAAK;AAE/B,QAAM,UAAU,oBAAoB,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS;AAC/D,QAAM,SAAS,MAAM,QAAQ,IAAI,QAAQ,IAAI,cAAc,CAAC,GAAG,KAAK;AAEpE,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAIC,IAAG,IAAI,8BAA8B,CAAC;AAClD;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,YAAY,OAAO,MAAM,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC;AAE9E,MAAI,UAAU;AACd,MAAI,MAAM;AACV,MAAI,UAAU;AACd,aAAW,KAAK,UAAU;AACxB,QAAI,EAAE,WAAW,WAAW;AAC1B;AACA,UAAI,CAAC,MAAM,MAAO,SAAQ,IAAI,GAAGA,IAAG,OAAO,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE;AAAA,IACnE,WAAW,EAAE,WAAW,cAAc;AACpC;AACA,UAAI,CAAC,MAAM,MAAO,SAAQ,IAAI,GAAGA,IAAG,IAAI,OAAO,CAAC,kBAAkB,EAAE,IAAI,EAAE;AAAA,IAC5E,WAAW,EAAE,WAAW,WAAW;AACjC;AACA,cAAQ,MAAM,GAAGA,IAAG,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,IAAIA,IAAG,IAAI,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE;AAAA,IAClF;AAAA,EACF;AAEA,UAAQ,IAAI,EAAE;AACd,QAAM,QAAkB,CAAC;AACzB,MAAI,QAAS,OAAM,KAAKA,IAAG,OAAO,GAAG,OAAO,UAAU,CAAC;AACvD,MAAI,IAAK,OAAM,KAAKA,IAAG,IAAI,GAAG,GAAG,UAAU,CAAC;AAC5C,MAAI,QAAS,OAAM,KAAKA,IAAG,IAAI,GAAG,OAAO,UAAU,CAAC;AACpD,UAAQ,IAAI,MAAM,WAAW,IAAIA,IAAG,IAAI,cAAc,IAAI,KAAK,MAAM,KAAK,IAAI,CAAC,EAAE;AAEjF,MAAI,UAAU,EAAG,SAAQ,WAAW;AACtC;;;AWlCA,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AACjB,OAAOC,SAAQ;;;ACFf,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AACjB,OAAO,eAAe;AAoDtB,eAAsB,eAAe,MAA0C;AAC7E,QAAM,EAAE,QAAQ,UAAU,OAAO,IAAI;AACrC,QAAM,QAAoB,CAAC;AAE3B,aAAW,WAAW,OAAO,UAAU;AACrC,eAAW,aAAa,OAAO,YAAsC;AACnE,YAAM,OAAO,QAAQ,OAAO,SAAS;AACrC,UAAI,SAAS,UAAa,SAAS,MAAO;AAE1C,YAAM,iBAAiBA,MAAK,KAAK,QAAQ,SAAS;AAClD,UAAI,CAAE,MAAMC,OAAM,cAAc,EAAI;AAEpC,YAAM,iBAAiBD,MAAK,KAAK,UAAU,QAAQ,SAAS,KAAK,YAAY;AAC7E,YAAM,UAAU,KAAK,UACjB,MAAM,cAAc,gBAAgB,gBAAgB,MAAM,SAAS,SAAS,IAC5E,MAAM,YAAY,gBAAgB,gBAAgB,MAAM,SAAS,SAAS;AAE9E,YAAM,KAAK,GAAG,OAAO;AAAA,IACvB;AAAA,EACF;AAEA,SAAO,iBAAiB,KAAK;AAC/B;AAIA,eAAe,cACb,gBACA,gBACA,QACA,SACA,WACqB;AACrB,QAAM,QAAQ,MAAM,UAAU,cAAc;AAC5C,QAAM,eAAe,mBAAmB,OAAO,OAAO;AACtD,QAAM,eAAe,mBAAmB,OAAO,OAAO;AACtD,QAAM,MAAkB,CAAC;AAEzB,aAAW,OAAO,OAAO;AACvB,UAAM,MAAMA,MAAK,SAAS,gBAAgB,GAAG;AAC7C,QAAI,CAAC,aAAa,GAAG,EAAG;AACxB,QAAI,aAAa,GAAG,EAAG;AAEvB,UAAM,aAAa;AAAA,MAAY;AAAA,MAAK;AAAA;AAAA,MAAsB;AAAA,IAAI;AAC9D,QAAI,KAAK;AAAA,MACP,QAAQ;AAAA,MACR,QAAQA,MAAK,KAAK,gBAAgB,UAAU;AAAA,MAC5C,aAAa,QAAQ;AAAA,MACrB;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAIA,eAAe,YACb,gBACA,gBACA,QACA,SACA,WACqB;AACrB,QAAM,QAAQ,MAAM,eAAe,cAAc;AACjD,QAAM,eAAe,mBAAmB,OAAO,OAAO;AACtD,QAAM,eAAe,mBAAmB,OAAO,OAAO;AACtD,QAAM,MAAkB,CAAC;AAEzB,aAAW,SAAS,OAAO;AACzB,QAAI,CAAC,aAAa,MAAM,IAAI,EAAG;AAC/B,QAAI,aAAa,MAAM,IAAI,EAAG;AAE9B,UAAM,aAAa;AAAA,MAAY,MAAM;AAAA,MAAM;AAAA;AAAA,MAAsB;AAAA,IAAK;AACtE,QAAI,KAAK;AAAA,MACP,QAAQA,MAAK,KAAK,gBAAgB,MAAM,IAAI;AAAA,MAC5C,QAAQA,MAAK,KAAK,gBAAgB,UAAU;AAAA,MAC5C,aAAa,QAAQ;AAAA,MACrB;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAIA,IAAM,oBAAoB;AAE1B,SAAS,YAAY,SAAiB,QAAqB,SAA0B;AACnF,MAAI,OAAO,OAAQ,QAAO,OAAO,OAAO,OAAO;AAE/C,MAAI,OAAO,UAAU,QAAQ,QAAQ,mBAAmB,IAAI,IAAI;AAChE,MAAI,OAAO,KAAK;AACd,UAAM,MAAMA,MAAK,QAAQ,IAAI;AAC7B,WAAO,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,OAAO,MAAM,OAAO,OAAO;AAAA,EACvE;AACA,SAAO;AACT;AAIA,eAAe,UAAU,MAAiC;AACxD,QAAM,MAAgB,CAAC;AACvB,SAAO,eAAe,QAAQ,KAA4B;AACxD,UAAM,UAAU,MAAMD,IAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC7D,eAAW,SAAS,SAAS;AAG3B,UAAI,MAAM,KAAK,WAAW,GAAG,EAAG;AAChC,YAAM,OAAOC,MAAK,KAAK,KAAK,MAAM,IAAI;AACtC,UAAI,MAAM,YAAY,GAAG;AACvB,cAAM,QAAQ,IAAI;AAAA,MACpB,WAAW,MAAM,OAAO,KAAK,MAAM,eAAe,GAAG;AACnD,YAAI,KAAK,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF,GAAG,IAAI;AACP,SAAO;AACT;AAEA,eAAe,eAAe,MAAoE;AAChG,QAAM,UAAU,MAAMD,IAAG,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AAC9D,SAAO,QACJ,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,WAAW,GAAG,CAAC,EACrC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,YAAY,EAAE,EAAE;AAC1D;AAEA,eAAeE,OAAM,GAA6B;AAChD,QAAM,OAAO,MAAMF,IAAG,KAAK,CAAC,EAAE,MAAM,MAAM,IAAI;AAC9C,SAAO,MAAM,YAAY,KAAK;AAChC;AAOA,SAAS,mBAAmB,UAAmE;AAC7F,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG,QAAO,MAAM;AACrD,QAAM,MAAM,SAAS,IAAI,CAAC,MAAM,UAAU,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC;AAC3D,SAAO,CAAC,QAAQ,IAAI,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;AAC1C;AAKA,SAAS,mBAAmB,UAAmE;AAC7F,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG,QAAO,MAAM;AACrD,QAAM,MAAM,SAAS,IAAI,CAAC,MAAM,UAAU,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC;AAC3D,SAAO,CAAC,QAAQ,IAAI,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;AAC1C;AAIA,SAAS,iBAAiB,OAAyC;AACjE,QAAM,WAAW,oBAAI,IAAwB;AAC7C,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAS,SAAS,IAAI,KAAK,MAAM;AACvC,QAAI,OAAQ,QAAO,KAAK,IAAI;AAAA,QACvB,UAAS,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC;AAAA,EACvC;AAEA,QAAM,WAAuB,CAAC;AAC9B,QAAM,aAAyB,CAAC;AAChC,aAAW,CAAC,QAAQ,MAAM,KAAK,UAAU;AACvC,QAAI,OAAO,WAAW,GAAG;AACvB,eAAS,KAAK,OAAO,CAAC,CAAE;AACxB;AAAA,IACF;AACA,UAAM,OAAO,OAAO,CAAC;AACrB,eAAW,KAAK;AAAA,MACd;AAAA,MACA,SAAS,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM;AAAA,MACnC,aAAa,KAAK;AAAA,MAClB,WAAW,KAAK;AAAA,IAClB,CAAC;AAAA,EACH;AACA,SAAO,EAAE,OAAO,UAAU,WAAW;AACvC;;;ADzNA,eAAsB,UAAU,QAAsB,CAAC,GAAkB;AACvE,QAAM,MAAM,MAAM,YAAY,KAAK;AACnC,cAAY,KAAK,UAAU,KAAK;AAEhC,MAAI,WAAW;AACf,MAAI,SAAS;AAKb,QAAM,oBAAoB,MAAM,qBAAqB,IAAI,OAAO,UAAU;AAG1E,UAAQ,IAAIG,IAAG,KAAK,UAAU,CAAC;AAC/B,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,eAAe,GAAG;AAC5D,UAAM,OAAOC,MAAK,KAAK,IAAI,MAAM,UAAU,OAAO,OAAO;AACzD,UAAM,OAAO,MAAMC,IAAG,KAAK,IAAI,EAAE,MAAM,MAAM,IAAI;AACjD,QAAI,MAAM,YAAY,GAAG;AACvB,cAAQ;AAAA,QACN,KAAKF,IAAG,MAAM,QAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC,IAAIA,IAAG,IAAI,OAAO,UAAU,GAAG,CAAC,IAAIA,IAAG,IAAI,UAAU,CAAC;AAAA,MAC7F;AAAA,IACF,WAAW,kBAAkB,IAAI,IAAI,GAAG;AACtC;AACA,cAAQ;AAAA,QACN,KAAKA,IAAG,OAAO,QAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC,IAAIA,IAAG,IAAI,OAAO,UAAU,GAAG,CAAC,IAAIA,IAAG,OAAO,yCAAoC,CAAC;AAAA,MAC3H;AAAA,IACF,OAAO;AACL,cAAQ;AAAA,QACN,KAAKA,IAAG,IAAI,QAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC,IAAIA,IAAG,IAAI,OAAO,UAAU,GAAG,CAAC,IAAIA,IAAG,IAAI,4BAA4B,CAAC;AAAA,MAC7G;AAAA,IACF;AAAA,EACF;AACA,UAAQ,IAAI,EAAE;AAGd,QAAM,EAAE,OAAO,WAAW,IAAI,MAAM,eAAe;AAAA,IACjD,QAAQ,IAAI,MAAM;AAAA,IAClB,UAAU,IAAI,MAAM;AAAA,IACpB,QAAQ,IAAI;AAAA,EACd,CAAC;AAED,UAAQ,IAAIA,IAAG,KAAK,WAAW,CAAC;AAChC,UAAQ,IAAI,KAAK,MAAM,MAAM,QAAQ,MAAM,WAAW,IAAI,KAAK,GAAG,EAAE;AACpE,MAAI,WAAW,WAAW,GAAG;AAC3B,YAAQ,IAAI,KAAKA,IAAG,MAAM,QAAG,CAAC,gBAAgB;AAAA,EAChD,OAAO;AACL,gBAAY,WAAW;AACvB,YAAQ,IAAI,KAAKA,IAAG,OAAO,QAAG,CAAC,IAAI,WAAW,MAAM,gBAAgB;AACpE,eAAW,KAAK,YAAY;AAC1B,cAAQ,IAAI,QAAQA,IAAG,IAAI,EAAE,cAAc,MAAM,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE;AAC5E,iBAAW,KAAK,EAAE,QAAS,SAAQ,IAAI,UAAUA,IAAG,IAAI,QAAG,CAAC,IAAI,CAAC,EAAE;AAAA,IACrE;AAAA,EACF;AACA,UAAQ,IAAI,EAAE;AAGd,QAAM,aAAa,oBAAoB,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS;AAClE,QAAM,SAAS,MAAM,QAAQ,IAAI,WAAW,IAAI,cAAc,CAAC,GAAG,KAAK;AAMvE,QAAM,WAAW,MAAM,yBAAyB,IAAI,MAAM,QAAQ;AAClE,QAAM,WACJ,MAAM,QAAQ;AAAA,IACZ,SAAS,IAAI,CAAC,MAAM,iBAAiB,EAAE,SAAS,IAAI,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC,CAAC;AAAA,EACxF,GACA,KAAK;AACP,QAAM,WAAW,IAAI,IAAI,KAAK;AAC9B,QAAM,cAAc,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AACtD,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC;AAE7E,UAAQ,IAAIA,IAAG,KAAK,gBAAgB,CAAC;AACrC,MAAI,MAAM,WAAW,KAAK,QAAQ,WAAW,GAAG;AAC9C,YAAQ,IAAI,KAAKA,IAAG,MAAM,QAAG,CAAC,YAAY;AAAA,EAC5C,OAAO;AACL,QAAI,MAAM,SAAS,GAAG;AACpB,kBAAY,MAAM;AAClB,cAAQ;AAAA,QACN,KAAKA,IAAG,OAAO,QAAG,CAAC,IAAI,MAAM,MAAM,aAAaA,IAAG,IAAI,kCAAkC,CAAC;AAAA,MAC5F;AACA,iBAAW,KAAK,MAAO,SAAQ,IAAI,QAAQ,CAAC,EAAE;AAAA,IAChD;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,kBAAY,QAAQ;AACpB,cAAQ;AAAA,QACN,KAAKA,IAAG,OAAO,QAAG,CAAC,IAAI,QAAQ,MAAM,WAAWA,IAAG,IAAI,kFAAkF,CAAC;AAAA,MAC5I;AACA,iBAAW,KAAK,QAAS,SAAQ,IAAI,QAAQ,CAAC,EAAE;AAAA,IAClD;AAAA,EACF;AACA,UAAQ,IAAI,EAAE;AAGd,MAAI,WAAW,WAAW,GAAG;AAC3B,YAAQ,IAAIA,IAAG,MAAM,SAAS,CAAC;AAC/B;AAAA,EACF;AACA,QAAM,QAAkB,CAAC;AACzB,MAAI,WAAW,EAAG,OAAM,KAAKA,IAAG,OAAO,GAAG,QAAQ,WAAW,aAAa,IAAI,KAAK,GAAG,EAAE,CAAC;AACzF,MAAI,SAAS,EAAG,OAAM,KAAKA,IAAG,IAAI,GAAG,MAAM,SAAS,WAAW,IAAI,KAAK,GAAG,EAAE,CAAC;AAC9E,UAAQ,IAAI,WAAW,MAAM,KAAK,IAAI,CAAC,EAAE;AACzC,MAAI,SAAS,EAAG,SAAQ,WAAW;AACrC;;;AEpHA,SAAS,YAAYG,WAAU;AAC/B,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,qBAAqB;AAC9B,OAAOC,SAAQ;AACf,OAAO,aAAa;;;ACvBpB,OAAOC,WAAU;AACjB,OAAOC,SAAQ;AAYR,IAAM,sBAAN,cAAkC,eAAe;AAAA,EACtD,YAA4B,WAAgC;AAC1D,UAAM,GAAG,UAAU,MAAM,0CAA0C;AADzC;AAAA,EAE5B;AAAA,EAF4B;AAG9B;AAEA,eAAsB,QAAQ,QAAsB,CAAC,GAAkB;AACrE,QAAM,MAAM,MAAM,YAAY,KAAK;AACnC,cAAY,KAAK,QAAQ,KAAK;AAE9B,QAAM,EAAE,OAAO,WAAW,IAAI,MAAM,eAAe;AAAA,IACjD,QAAQ,IAAI,MAAM;AAAA,IAClB,UAAU,IAAI,MAAM;AAAA,IACpB,QAAQ,IAAI;AAAA,EACd,CAAC;AAED,MAAI,WAAW,SAAS,GAAG;AACzB,qBAAiB,YAAY,MAAM,SAAS,KAAK;AACjD,QAAI,MAAM,QAAQ;AAChB,YAAM,IAAI,oBAAoB,UAAU;AAAA,IAC1C;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,WAAW,OAAO;AAAA,IACvC,GAAI,MAAM,WAAW,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC7D,GAAI,MAAM,UAAU,SAAY,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,EAC5D,CAAC;AAID,QAAM,aAAa,oBAAoB,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS;AAClE,QAAM,SAAS,MAAM,QAAQ,IAAI,WAAW,IAAI,cAAc,CAAC,GAAG,KAAK;AACvE,QAAM,UAAU,MAAM,YAAY,OAAO,MAAM,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC;AAE7E,iBAAe,UAAU,IAAI,MAAM,UAAU,MAAM,SAAS,KAAK;AACjE,iBAAe,SAAS,MAAM,SAAS,KAAK;AAC5C,eAAa,UAAU,SAAS,UAAU;AAM1C,QAAM,UACJ,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,SAC/C,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE;AAChD,MAAI,UAAU,EAAG,SAAQ,WAAW;AACtC;AAIA,SAAS,iBAAiB,YAAiC,OAAsB;AAC/E,MAAI,MAAO;AACX,aAAW,KAAK,YAAY;AAC1B,YAAQ;AAAA,MACNC,IAAG,OAAO,iCAAiC,EAAE,WAAW,IAAI,EAAE,SAAS,KAAK,EAAE,MAAM,EAAE;AAAA,IACxF;AACA,eAAW,KAAK,EAAE,QAAS,SAAQ,KAAKA,IAAG,IAAI,mBAAmB,CAAC,EAAE,CAAC;AAAA,EACxE;AACF;AAEA,SAAS,eACP,UACA,UACA,OACM;AACN,aAAW,KAAK,UAAU;AACxB,UAAM,UAAUC,MAAK,SAAS,UAAU,EAAE,KAAK,MAAM;AACrD,YAAQ,EAAE,QAAQ;AAAA,MAChB,KAAK;AACH,YAAI,CAAC,MAAO,SAAQ,IAAI,GAAGD,IAAG,MAAM,MAAM,CAAC,OAAO,OAAO,EAAE;AAC3D;AAAA,MACF,KAAK;AACH,gBAAQ,IAAI,GAAGA,IAAG,MAAM,OAAO,CAAC,MAAM,OAAO,IAAIA,IAAG,IAAI,IAAI,EAAE,OAAO,GAAG,CAAC,EAAE;AAC3E;AAAA,MACF,KAAK;AACH,YAAI,CAAC,MAAO,SAAQ,IAAI,GAAGA,IAAG,KAAK,QAAQ,CAAC,KAAK,OAAO,EAAE;AAC1D;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,GAAGA,IAAG,OAAO,QAAQ,CAAC,KAAK,OAAO,IAAIA,IAAG,IAAI,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE;AACnF;AAAA,MACF,KAAK;AACH,gBAAQ,MAAM,GAAGA,IAAG,IAAI,OAAO,CAAC,MAAM,OAAO,IAAIA,IAAG,IAAI,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE;AACjF;AAAA,MACF,KAAK;AACH,YAAI,CAAC,MAAO,SAAQ,IAAI,GAAGA,IAAG,IAAI,OAAO,CAAC,MAAM,OAAO,EAAE;AACzD;AAAA,MACF,KAAK;AACH,gBAAQ,IAAI,GAAGA,IAAG,IAAI,OAAO,CAAC,MAAM,OAAO,IAAIA,IAAG,IAAI,eAAe,EAAE,OAAO,GAAG,CAAC,EAAE;AACpF;AAAA,IACJ;AAAA,EACF;AACF;AAEA,SAAS,eACP,UACA,OACM;AACN,aAAW,KAAK,UAAU;AACxB,QAAI,EAAE,WAAW,aAAa,CAAC,OAAO;AACpC,cAAQ,IAAI,GAAGA,IAAG,OAAO,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE;AAAA,IACjD,WAAW,EAAE,WAAW,gBAAgB,CAAC,OAAO;AAC9C,cAAQ,IAAI,GAAGA,IAAG,IAAI,OAAO,CAAC,kBAAkB,EAAE,IAAI,EAAE;AAAA,IAC1D,WAAW,EAAE,WAAW,WAAW;AACjC,cAAQ,MAAM,GAAGA,IAAG,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,IAAIA,IAAG,IAAI,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE;AAAA,IAClF;AAAA,EACF;AACF;AAEA,SAAS,aACP,UACA,UACA,YACM;AACN,QAAM,SAAiC,CAAC;AACxC,aAAW,KAAK,SAAU,QAAO,EAAE,MAAM,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK;AACvE,QAAM,UAAU,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,YAAY,EAAE;AAE5F,QAAM,QAAkB,CAAC;AACzB,MAAI,OAAO,SAAS,EAAG,OAAM,KAAKA,IAAG,MAAM,GAAG,OAAO,SAAS,CAAC,UAAU,CAAC;AAC1E,MAAI,OAAO,OAAO,EAAG,OAAM,KAAKA,IAAG,MAAM,GAAG,OAAO,OAAO,CAAC,QAAQ,CAAC;AACpE,MAAI,OAAO,SAAS,EAAG,OAAM,KAAKA,IAAG,KAAK,GAAG,OAAO,SAAS,CAAC,UAAU,CAAC;AACzE,MAAI,OAAO,QAAQ,EAAG,OAAM,KAAKA,IAAG,OAAO,GAAG,OAAO,QAAQ,CAAC,SAAS,CAAC;AACxE,MAAI,OAAO,SAAS,EAAG,OAAM,KAAKA,IAAG,IAAI,GAAG,OAAO,SAAS,CAAC,UAAU,CAAC;AACxE,MAAI,OAAO,YAAY,KAAK,OAAO,SAAS,GAAG;AAC7C,UAAM,KAAKA,IAAG,IAAI,IAAI,OAAO,YAAY,KAAK,MAAM,OAAO,SAAS,KAAK,EAAE,UAAU,CAAC;AAAA,EACxF;AACA,MAAI,QAAS,OAAM,KAAKA,IAAG,OAAO,GAAG,OAAO,UAAU,CAAC;AACvD,MAAI,WAAW,OAAQ,OAAM,KAAKA,IAAG,OAAO,GAAG,WAAW,MAAM,eAAe,CAAC;AAEhF,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,MAAM,WAAW,IAAIA,IAAG,IAAI,cAAc,IAAI,KAAK,MAAM,KAAK,IAAI,CAAC,EAAE;AACnF;;;AD7GO,IAAM,wBAAN,cAAoC,eAAe;AAAA,EACxD,YAAY,YAAoB;AAC9B,UAAM,oDAAoD,UAAU;AAAA,+CAAkD;AAAA,EACxH;AACF;AAEA,IAAM,kBAAkB,CAAC,UAAU,SAAS,YAAY,WAAW,aAAa,QAAQ;AACxF,IAAM,sBAAsB,CAAC,UAAU,SAAS,UAAU;AAC1D,IAAM,oBAAoB,OAAO,KAAK,eAAe;AAErD,IAAM,OAAOE,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AACxD,IAAM,sBAAsB;AAAA,EAC1BA,MAAK,QAAQ,MAAM,MAAM,aAAa,IAAI;AAAA,EAC1CA,MAAK,QAAQ,MAAM,MAAM,MAAM,aAAa,IAAI;AAClD;AAEA,eAAe,mBAAoC;AACjD,aAAW,aAAa,qBAAqB;AAC3C,UAAM,OAAO,MAAMC,IAAG,KAAK,SAAS,EAAE,MAAM,MAAM,IAAI;AACtD,QAAI,MAAM,YAAY,EAAG,QAAO;AAAA,EAClC;AACA,QAAM,IAAI;AAAA,IACR;AAAA,IAAmD,oBAAoB,KAAK,MAAM,CAAC;AAAA,EACrF;AACF;AAaA,eAAsB,QAAQ,QAAmB,CAAC,GAAkB;AAClE,QAAM,UAAU,MAAM,eAAe,KAAK;AAE1C,MAAI,CAAC,MAAM,OAAO;AAChB,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAIC,IAAG,KAAK,kBAAkB,CAAC;AACvC,YAAQ,IAAI,eAAe,QAAQ,MAAM,EAAE;AAC3C,YAAQ,IAAI,EAAE;AAAA,EAChB;AAEA,QAAM,UAAU,MAAM,aAAa,SAAS;AAAA,IAC1C,OAAO,MAAM,SAAS;AAAA,IACtB,OAAO,MAAM,SAAS;AAAA,IACtB,QAAQ,MAAM,UAAU;AAAA,EAC1B,CAAC;AACD,MAAI,CAAC,QAAQ,WAAW,CAAC,MAAM,OAAO;AACpC,YAAQ,IAAIA,IAAG,IAAI,gDAAgD,CAAC;AAAA,EACtE;AAEA,MAAI,QAAQ,mBAAmB,YAAY,CAAC,MAAM,OAAO;AACvD,gCAA4B,QAAQ,MAAM;AAAA,EAC5C;AAEA,MAAI,CAAC,MAAM,MAAO,SAAQ,IAAI,EAAE;AAKhC,MAAI,MAAM,QAAQ;AAChB,QAAI,CAAC,MAAM,OAAO;AAChB,cAAQ;AAAA,QACNA,IAAG;AAAA,UACD,oDAAoD,QAAQ,MAAM;AAAA,QACpE;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AAKA,QAAM,QAAQ;AAAA,IACZ,QAAQ,QAAQ;AAAA,IAChB,GAAI,MAAM,WAAW,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC7D,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACvD,GAAI,MAAM,UAAU,SAAY,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,EAC5D,CAAC;AACH;AAIA,eAAe,eAAe,OAAwC;AACpE,QAAM,cAAc,MAAM,OAAO,CAAC,QAAQ,MAAM;AAGhD,MAAI;AACJ,MAAI;AACJ,MAAI,MAAM,WAAW,UAAa,MAAM,WAAW,IAAI;AACrD,aAASF,MAAK,QAAQG,aAAY,MAAM,MAAM,CAAC;AAC/C,qBAAiB,iBAAiB,MAAM;AAAA,EAC1C,WAAW,MAAM,OAAO;AACtB,aAASH,MAAK,QAAQ,QAAQ,IAAI,GAAG,SAAS;AAC9C,qBAAiB;AAAA,EACnB,WAAW,aAAa;AACtB,aAASA,MAAK,QAAQI,IAAG,QAAQ,GAAG,SAAS;AAC7C,qBAAiB;AAAA,EACnB,OAAO;AACL,UAAM,SAAS,MAAM,IAA8B;AAAA,MACjD,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACP;AAAA,UACE,OAAO;AAAA,UACP,aAAa;AAAA,UACb,OAAO;AAAA,QACT;AAAA,QACA;AAAA,UACE,OAAO;AAAA,UACP,aAAa;AAAA,UACb,OAAO;AAAA,QACT;AAAA,MACF;AAAA,MACA,SAAS;AAAA,IACX,CAAC;AACD,aACE,OAAO,SAAS,SACZJ,MAAK,QAAQI,IAAG,QAAQ,GAAG,SAAS,IACpCJ,MAAK,QAAQ,QAAQ,IAAI,GAAG,SAAS;AAC3C,qBAAiB,OAAO;AAAA,EAC1B;AAGA,MAAI;AACJ,MAAI,aAAa;AACf,cAAU;AAAA,EACZ,OAAO;AACL,UAAM,EAAE,OAAO,IAAI,MAAM,IAA0B;AAAA,MACjD,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,SAAS,gBAAgB,IAAI,CAAC,UAAU;AAAA,QACtC,OAAO;AAAA,QACP,OAAO;AAAA,QACP,UAAW,oBAA0C,SAAS,IAAI;AAAA,MACpE,EAAE;AAAA,MACF,KAAK;AAAA,MACL,cAAc;AAAA,IAChB,CAAC;AACD,cAAU;AAAA,EACZ;AAGA,MAAI;AACJ,MAAI,aAAa;AACf,eAAW;AAAA,EACb,OAAO;AACL,UAAM,EAAE,OAAO,IAAI,MAAM,IAA+B;AAAA,MACtD,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,SAAS,kBAAkB,IAAI,CAAC,UAAU;AAAA,QACxC,OAAO;AAAA,QACP,OAAO;AAAA,QACP,UAAU;AAAA,MACZ,EAAE;AAAA,MACF,KAAK;AAAA,MACL,cAAc;AAAA,IAChB,CAAC;AACD,eAAW;AAAA,EACb;AAIA,QAAM,iBACJ,mBAAmB,QAAQ,QAAQ;AACrC,QAAM,aACJ,mBAAmB,SACfA,MAAK,QAAQI,IAAG,QAAQ,GAAG,uBAAuB,IAClDJ,MAAK,QAAQ,QAAQ,IAAI,GAAG,uBAAuB;AAEzD,SAAO,EAAE,QAAQ,gBAAgB,YAAY,gBAAgB,SAAS,SAAS;AACjF;AAOA,eAAe,IACb,UACY;AACZ,MAAI,YAAY;AAChB,QAAM,SAAS,MAAM,QAAQ,UAAU;AAAA,IACrC,UAAU,MAAM;AACd,kBAAY;AACZ,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACD,MAAI,UAAW,OAAM,IAAI,eAAe,gBAAgB;AACxD,SAAO;AACT;AAEA,SAAS,iBAAiB,QAA+C;AACvE,QAAM,OAAOA,MAAK,QAAQI,IAAG,QAAQ,GAAG,SAAS;AACjD,QAAM,MAAMJ,MAAK,QAAQ,QAAQ,IAAI,GAAG,SAAS;AACjD,MAAI,WAAW,KAAM,QAAO;AAC5B,MAAI,WAAW,IAAK,QAAO;AAC3B,SAAO;AACT;AAEA,SAASG,aAAY,GAAmB;AACtC,MAAI,MAAM,IAAK,QAAOC,IAAG,QAAQ;AACjC,MAAI,EAAE,WAAW,IAAI,EAAG,QAAOJ,MAAK,KAAKI,IAAG,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC;AACjE,SAAO;AACT;AAeA,eAAe,aACb,SACA,SACyB;AACzB,QAAM,EAAE,OAAO,OAAO,OAAO,IAAI;AACjC,QAAM,SAAS,QAAQ;AACvB,QAAM,OAAO,MAAMH,IAAG,KAAK,MAAM,EAAE,MAAM,MAAM,IAAI;AACnD,MAAI,QAAQ,CAAC,KAAK,YAAY,GAAG;AAC/B,UAAM,IAAI,eAAe,8CAA8C,MAAM,EAAE;AAAA,EACjF;AAEA,MAAI,QAAQ,CAAC,OAAO;AAClB,UAAM,UAAU,MAAMA,IAAG,QAAQ,MAAM;AACvC,QAAI,QAAQ,SAAS,EAAG,OAAM,IAAI,sBAAsB,MAAM;AAAA,EAChE;AAEA,MAAI,CAAC,QAAQ;AACX,UAAMA,IAAG,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,EAC5C,WAAW,CAAC,QAAQ,CAAC,OAAO;AAC1B,YAAQ,IAAI,GAAGC,IAAG,IAAI,OAAO,CAAC,mBAAmB,MAAM,GAAG;AAAA,EAC5D;AAEA,QAAM,eAAe,MAAM,iBAAiB;AAE5C,MAAI,UAAU;AACd,aAAW,MAAM,iBAAiB,cAAc,QAAQ,QAAQ,SAAS,OAAO,MAAM;AACtF,aAAW,MAAM,gBAAgB,SAAS,OAAO,MAAM;AACvD,SAAO,EAAE,SAAS,UAAU,EAAE;AAChC;AAOA,eAAe,iBACb,cACA,QACA,SACA,OACA,QACiB;AACjB,MAAI,UAAU;AAGd,QAAM,YAAYF,MAAK,KAAK,cAAc,WAAW;AACrD,QAAM,aAAaA,MAAK,KAAK,QAAQ,WAAW;AAChD,MAAK,MAAMC,IAAG,KAAK,SAAS,EAAE,MAAM,MAAM,IAAI,KAAM,CAAE,MAAMA,IAAG,KAAK,UAAU,EAAE,MAAM,MAAM,IAAI,GAAI;AAClG,QAAI,QAAQ;AACV,UAAI,CAAC,MAAO,SAAQ,IAAI,GAAGC,IAAG,IAAI,OAAO,CAAC,mBAAmB,UAAU,EAAE;AAAA,IAC3E,OAAO;AACL,YAAMD,IAAG,SAAS,WAAW,UAAU;AACvC,UAAI,CAAC,MAAO,SAAQ,IAAI,GAAGC,IAAG,MAAM,OAAO,CAAC,MAAM,UAAU,EAAE;AAAA,IAChE;AACA;AAAA,EACF;AAGA,aAAW,aAAa,SAAS;AAC/B,UAAM,MAAMF,MAAK,KAAK,QAAQ,SAAS;AACvC,UAAM,OAAOA,MAAK,KAAK,KAAK,UAAU;AACtC,UAAM,aAAa,MAAMC,IAAG,KAAK,IAAI,EAAE,MAAM,MAAM,IAAI;AACvD,QAAI,WAAY;AAEhB,QAAI,QAAQ;AACV,UAAI,CAAC,MAAO,SAAQ,IAAI,GAAGC,IAAG,IAAI,OAAO,CAAC,mBAAmB,IAAI,EAAE;AAAA,IACrE,OAAO;AACL,YAAMD,IAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC,YAAMA,IAAG,UAAU,MAAM,EAAE;AAC3B,UAAI,CAAC,MAAO,SAAQ,IAAI,GAAGC,IAAG,MAAM,OAAO,CAAC,MAAM,IAAI,EAAE;AAAA,IAC1D;AACA;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,gBACb,SACA,OACA,QACiB;AACjB,QAAM,aAAa,QAAQ;AAC3B,MAAI,MAAMD,IAAG,KAAK,UAAU,EAAE,MAAM,MAAM,IAAI,GAAG;AAC/C,QAAI,CAAC,MAAO,SAAQ,IAAI,GAAGC,IAAG,KAAK,QAAQ,CAAC,KAAK,UAAU,mBAAmB;AAC9E,WAAO;AAAA,EACT;AACA,MAAI,QAAQ;AACV,QAAI,CAAC,MAAO,SAAQ,IAAI,GAAGA,IAAG,IAAI,OAAO,CAAC,mBAAmB,UAAU,EAAE;AACzE,WAAO;AAAA,EACT;AACA,QAAMD,IAAG,MAAMD,MAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5D,QAAMC,IAAG,UAAU,YAAY,aAAa,OAAO,GAAG,MAAM;AAC5D,MAAI,CAAC,MAAO,SAAQ,IAAI,GAAGC,IAAG,MAAM,OAAO,CAAC,MAAM,UAAU,EAAE;AAC9D,SAAO;AACT;AAMO,SAAS,sBAAsB,QAAgB,YAA4B;AAChF,QAAM,WAAWF,MAAK,QAAQ,MAAM;AACpC,QAAM,YAAYA,MAAK,QAAQA,MAAK,QAAQ,UAAU,CAAC;AACvD,QAAM,OAAOA,MAAK,QAAQI,IAAG,QAAQ,CAAC;AACtC,QAAM,MAAMJ,MAAK,SAAS,WAAW,QAAQ;AAC7C,MAAI,QAAQ,MAAM,CAAC,IAAI,WAAW,IAAI,KAAK,CAACA,MAAK,WAAW,GAAG,GAAG;AAChE,WAAO;AAAA,EACT;AACA,MAAI,aAAa,QAAQ,SAAS,WAAW,OAAOA,MAAK,GAAG,GAAG;AAC7D,UAAM,OAAOA,MAAK,SAAS,MAAM,QAAQ;AACzC,WAAO,SAAS,KAAK,MAAM,KAAK,IAAI;AAAA,EACtC;AACA,SAAO;AACT;AAOO,SAAS,aAAa,SAA8B;AACzD,QAAM,cAAc,KAAK,UAAU,QAAQ,OAAO;AAClD,QAAM,eAAe,kBAAkB,IAAI,CAAC,SAAS;AACnD,UAAM,QAAQ,QAAQ,SAAS,SAAS,IAAI,IAAI,SAAS;AACzD,WAAO,QAAQ,IAAI,MAAM,KAAK;AAAA,EAChC,CAAC,EAAE,KAAK,KAAK;AACb,QAAM,cACJ,QAAQ,mBAAmB,WACvB;AAAA,cACM,KAAK,UAAU,sBAAsB,QAAQ,QAAQ,QAAQ,UAAU,CAAC,CAAC;AAAA;AAAA,IAG/E;AAEN,SAAO;AAAA,EACP,WAAW;AAAA;AAAA,eAEE,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaxB,YAAY;AAAA;AAAA;AAAA;AAId;AAIA,SAAS,4BAA4B,QAAsB;AACzD,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAIE,IAAG,KAAK,sDAAiD,CAAC;AACtE,UAAQ,IAAI,KAAK,MAAM,EAAE;AACzB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,qEAAqE;AACjF,UAAQ,IAAI,8DAA8D;AAC1E,UAAQ,IAAI,YAAYA,IAAG,KAAK,YAAY,MAAM,EAAE,CAAC,kBAAkB;AACvE,UAAQ,IAAI,cAAcA,IAAG,KAAK,oBAAoB,MAAM,EAAE,CAAC,uBAAuB;AACtF,UAAQ,IAAI,mBAAmBA,IAAG,KAAK,SAAS,MAAM,YAAY,CAAC,EAAE;AACvE;;;AElbA,OAAOG,SAAQ;AAUf,eAAsB,UAAU,QAAsB,CAAC,GAAkB;AACvE,QAAM,MAAM,MAAM,YAAY,KAAK;AACnC,cAAY,KAAK,UAAU,KAAK;AAQhC,QAAM,QAAQ,MAAM,yBAAyB,IAAI,MAAM,QAAQ;AAC/D,QAAM,aAAa,IAAI,MAAM;AAE7B,QAAM,SACJ,MAAM,QAAQ;AAAA,IACZ,MAAM,IAAI,CAAC,EAAE,QAAQ,MAAM,iBAAiB,SAAS,YAAY,EAAE,WAAW,KAAK,CAAC,CAAC;AAAA,EACvF,GACA,KAAK;AAEP,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAIC,IAAG,IAAI,oCAAoC,CAAC;AACxD;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,YAAY,OAAO,MAAM,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC;AAE9E,MAAI,UAAU;AACd,MAAI,MAAM;AACV,MAAI,UAAU;AACd,aAAW,KAAK,UAAU;AACxB,QAAI,EAAE,WAAW,WAAW;AAC1B;AACA,UAAI,CAAC,MAAM,MAAO,SAAQ,IAAI,GAAGA,IAAG,OAAO,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE;AAAA,IACpE,WAAW,EAAE,WAAW,cAAc;AACpC;AACA,UAAI,CAAC,MAAM,MAAO,SAAQ,IAAI,GAAGA,IAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE,IAAI,EAAE;AAAA,IAC9E,WAAW,EAAE,WAAW,WAAW;AACjC;AACA,cAAQ,MAAM,GAAGA,IAAG,IAAI,OAAO,CAAC,OAAO,EAAE,IAAI,IAAIA,IAAG,IAAI,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE;AAAA,IACnF;AAAA,EACF;AAIA,MAAI,CAAC,MAAM,QAAQ;AACjB,UAAM,eAAe,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;AAAA,EAClD;AAEA,UAAQ,IAAI,EAAE;AACd,QAAM,QAAkB,CAAC;AACzB,MAAI,QAAS,OAAM,KAAKA,IAAG,OAAO,GAAG,OAAO,UAAU,CAAC;AACvD,MAAI,IAAK,OAAM,KAAKA,IAAG,IAAI,GAAG,GAAG,UAAU,CAAC;AAC5C,MAAI,QAAS,OAAM,KAAKA,IAAG,IAAI,GAAG,OAAO,UAAU,CAAC;AACpD,UAAQ,IAAI,MAAM,WAAW,IAAIA,IAAG,IAAI,cAAc,IAAI,KAAK,MAAM,KAAK,IAAI,CAAC,EAAE;AAEjF,MAAI,UAAU,EAAG,SAAQ,WAAW;AACtC;;;AhBhDA,IAAM,WAAW;AACjB,IAAM,cAAc;AAEpB,SAAS,mBAAmB,KAAmC;AAC7D,MACG,OAAO,mBAAmB,uCAAuC,EACjE,OAAO,sBAAsB,yCAAyC,EACtE,OAAO,cAAc,6CAA6C,EAClE,OAAO,mBAAmB,uCAAuC,EACjE,OAAO,iBAAiB,iDAAiD,EACzE,OAAO,eAAe,kDAAkD,EACxE,OAAO,eAAe,+BAA+B,EACrD,OAAO,iBAAiB,0BAA0B,EAClD,OAAO,YAAY,gDAAgD;AACxE;AAEA,eAAe,OAAsB;AACnC,QAAM,MAAM,IAAI,WAAW;AAE3B,MACG,QAAQ,QAAQ,qDAAqD,EACrE,OAAO,WAAW,8CAA8C,EAChE,OAAO,aAAa,yDAAyD,EAC7E,OAAO,CAAC,UAA6D,QAAQ,KAAK,CAAC;AAEtF,MACG,QAAQ,QAAQ,wCAAwC,EACxD,OAAO,CAAC,UAAwB,QAAQ,KAAK,CAAC;AAEjD,MACG,QAAQ,SAAS,4CAA4C,EAC7D,OAAO,CAAC,UAAwB,SAAS,KAAK,CAAC;AAElD,MACG,QAAQ,UAAU,6CAA6C,EAC/D,OAAO,CAAC,UAAwB,UAAU,KAAK,CAAC;AAEnD,MACG,QAAQ,UAAU,mDAAmD,EACrE,OAAO,CAAC,UAAwB,UAAU,KAAK,CAAC;AAEnD,MACG,QAAQ,IAAI,oCAAoC,EAChD,OAAO,CAAC,UAAwB,QAAQ,KAAK,CAAC;AAEjD,qBAAmB,GAAG;AACtB,MAAI,KAAK;AACT,MAAI,QAAQ,WAAW;AAEvB,MAAI;AACF,QAAI,MAAM,QAAQ,MAAM,EAAE,KAAK,MAAM,CAAC;AACtC,UAAM,IAAI,kBAAkB;AAAA,EAC9B,SAAS,KAAK;AACZ,gBAAY,GAAG;AACf,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,SAAS,YAAY,KAAoB;AACvC,MAAI,eAAe,qBAAqB;AACtC,YAAQ,MAAMC,IAAG,IAAI,IAAI,QAAQ;AAAA,CAAyC,CAAC;AAC3E,YAAQ,MAAM,WAAW;AACzB,eAAW,EAAE,QAAQ,OAAO,KAAK,IAAI,UAAU;AAC7C,cAAQ,MAAM,KAAK,OAAO,OAAO,EAAE,CAAC,IAAIA,IAAG,IAAI,MAAM,CAAC,EAAE;AAAA,IAC1D;AACA,YAAQ,MAAM,EAAE;AAChB,YAAQ,MAAM,QAAQ;AACtB,YAAQ,MAAM,iDAAiD;AAC/D,YAAQ,MAAM,yDAAyD;AACvE,YAAQ,MAAM,0CAA0C;AACxD;AAAA,EACF;AAEA,MAAI,eAAe,qBAAqB;AACtC,YAAQ,MAAMA,IAAG,IAAI,IAAI,QAAQ,cAAc,IAAI,OAAO,EAAE,CAAC;AAC7D,YAAQ,MAAMA,IAAG,IAAI,qEAAqE,CAAC;AAC3F;AAAA,EACF;AAEA,MAAI,eAAe,kBAAkB;AACnC,YAAQ,MAAMA,IAAG,IAAI,IAAI,QAAQ,KAAK,IAAI,OAAO,EAAE,CAAC;AACpD;AAAA,EACF;AAEA,MAAI,eAAe,oBAAoB,eAAe,eAAe;AACnE,YAAQ,MAAMA,IAAG,IAAI,IAAI,QAAQ,KAAK,IAAI,OAAO,EAAE,CAAC;AACpD;AAAA,EACF;AAEA,MAAI,eAAe,gBAAgB;AACjC,YAAQ,MAAMA,IAAG,IAAI,IAAI,QAAQ,KAAK,IAAI,OAAO,EAAE,CAAC;AACpD;AAAA,EACF;AAEA,QAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAQ,MAAMA,IAAG,IAAI,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;AAC5C,MAAI,QAAQ,IAAI,iBAAiB,GAAG;AAClC,YAAQ,MAAM,GAAG;AAAA,EACnB;AACF;AAEA,MAAM,KAAK;","names":["pc","pc","fs","path","fs","path","path","path","fs","fs","os","path","isInside","os","expandTilde","path","fs","path","fs","pc","fs","path","pc","fs","path","isDir","pc","path","fs","fs","os","path","pc","path","pc","pc","path","path","fs","pc","expandTilde","os","pc","pc","pc"]}
package/dist/index.d.ts CHANGED
@@ -55,10 +55,24 @@ type AdapterOverride = boolean | {
55
55
  layout?: Partial<Record<AssetType, AssetLayout>>;
56
56
  };
57
57
  /**
58
- * User-facing JSON configuration loaded from `<source>/agentlink.config.json`.
58
+ * User-facing JSON configuration (`agentlink.config.json`).
59
59
  * Every field is optional; sensible defaults apply.
60
60
  */
61
61
  interface AgentlinkConfig {
62
+ /**
63
+ * Persistent default for the `.agents/` source directory.
64
+ * Resolved after `--source` / `AGENTLINK_SOURCE` and `cwd/.agents`, before
65
+ * `~/.agents`. Relative paths are resolved against the config file directory.
66
+ */
67
+ source?: string;
68
+ /**
69
+ * Persistent default destination root (where adapter rootDirs like `.cursor`
70
+ * are expected). Relative paths are resolved against the config file directory.
71
+ * Mutually exclusive with {@link AgentlinkConfig.user}.
72
+ */
73
+ target?: string;
74
+ /** When true, project symlinks into `$HOME` (equivalent to `--user`). */
75
+ user?: boolean;
62
76
  /**
63
77
  * Asset type allowlist. When omitted, falls back to the built-in default
64
78
  * (`['skills', 'rules', 'commands']`). Use this to opt into less-common
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmkk/agentlink",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Symlink your .agents/ assets into .cursor / .claude / .codebuddy and other AI agent directories with a single npx.",
5
5
  "keywords": [
6
6
  "ai",
@@ -16,14 +16,13 @@
16
16
  ],
17
17
  "license": "MIT",
18
18
  "author": "rankangkang <rankangkang@foxmail.com>",
19
- "homepage": "https://github.com/rankangkang/dot-agents/tree/main/packages/agentlink#readme",
19
+ "homepage": "https://github.com/rankangkang/agentlink#readme",
20
20
  "repository": {
21
21
  "type": "git",
22
- "url": "git+https://github.com/rankangkang/dot-agents.git",
23
- "directory": "packages/agentlink"
22
+ "url": "git+https://github.com/rankangkang/agentlink.git"
24
23
  },
25
24
  "bugs": {
26
- "url": "https://github.com/rankangkang/dot-agents/issues"
25
+ "url": "https://github.com/rankangkang/agentlink/issues"
27
26
  },
28
27
  "type": "module",
29
28
  "engines": {
@@ -49,15 +48,6 @@
49
48
  "CHANGELOG.md",
50
49
  "LICENSE"
51
50
  ],
52
- "scripts": {
53
- "build": "tsup",
54
- "dev": "tsup --watch",
55
- "test": "vitest run",
56
- "test:watch": "vitest",
57
- "typecheck": "tsc --noEmit",
58
- "prepublishOnly": "pnpm build && pnpm test && pnpm typecheck",
59
- "publish:beta": "pnpm publish --tag beta"
60
- },
61
51
  "publishConfig": {
62
52
  "access": "public"
63
53
  },
@@ -77,5 +67,15 @@
77
67
  "tsup": "^8.5.1",
78
68
  "typescript": "^6.0.3",
79
69
  "vitest": "^4.1.5"
70
+ },
71
+ "scripts": {
72
+ "build": "tsup",
73
+ "dev": "tsup --watch",
74
+ "test": "vitest run",
75
+ "test:watch": "vitest",
76
+ "typecheck": "tsc --noEmit",
77
+ "preversion": "pnpm typecheck && pnpm test && pnpm build",
78
+ "publish:beta": "pnpm publish --tag beta --access public",
79
+ "publish:latest": "pnpm publish --access public"
80
80
  }
81
- }
81
+ }