@sofatutor/agent-bridge 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.mjs +247 -64
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ That's it. The wizard walks you through three questions:
|
|
|
14
14
|
|
|
15
15
|
1. **Tools** — VS Code, Cursor, Claude, Pi, or a custom folder.
|
|
16
16
|
2. **Sources** — a Git URL (or local path) that holds your shared skills, e.g. `https://github.com/sofatutor/ai-hub.git`.
|
|
17
|
-
3. **
|
|
17
|
+
3. **What to sync** — a tree of every domain in each source. Tick a domain to take all of it, or open it and tick single skills, agents or files.
|
|
18
18
|
|
|
19
19
|
It saves `.agent-bridge/config.yml`, offers to install git hooks, and syncs immediately.
|
|
20
20
|
|
package/dist/index.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import yaml from "js-yaml";
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { execFileSync, execSync } from "node:child_process";
|
|
9
9
|
import fsExtra from "fs-extra";
|
|
10
|
+
import { Prompt, isCancel } from "@clack/core";
|
|
10
11
|
//#region src/lib/config.ts
|
|
11
12
|
const SAFE_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
12
13
|
const safeName = z.string().min(1).refine((v) => SAFE_NAME_RE.test(v) && v !== "." && v !== "..", { message: "Only [A-Za-z0-9._-] characters allowed, cannot be . or .." });
|
|
@@ -940,8 +941,188 @@ function detectToolRootDuplicates(entries) {
|
|
|
940
941
|
return duplicates;
|
|
941
942
|
}
|
|
942
943
|
//#endregion
|
|
944
|
+
//#region src/lib/tree.ts
|
|
945
|
+
var TreeModel = class {
|
|
946
|
+
selected = /* @__PURE__ */ new Set();
|
|
947
|
+
expanded = /* @__PURE__ */ new Set();
|
|
948
|
+
cursor = 0;
|
|
949
|
+
constructor(roots, opts = {}) {
|
|
950
|
+
this.roots = roots;
|
|
951
|
+
const depth = opts.expandDepth ?? 1;
|
|
952
|
+
const expand = (nodes, d) => {
|
|
953
|
+
if (d >= depth) return;
|
|
954
|
+
for (const n of nodes) if (n.children?.length) {
|
|
955
|
+
this.expanded.add(n);
|
|
956
|
+
expand(n.children, d + 1);
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
expand(roots, 0);
|
|
960
|
+
for (const v of opts.initialSelected ?? []) this.selected.add(v);
|
|
961
|
+
}
|
|
962
|
+
/** Visible rows in display order, honoring collapsed nodes. */
|
|
963
|
+
rows() {
|
|
964
|
+
const out = [];
|
|
965
|
+
const walk = (nodes, depth, parent) => {
|
|
966
|
+
for (const node of nodes) {
|
|
967
|
+
out.push({
|
|
968
|
+
node,
|
|
969
|
+
depth,
|
|
970
|
+
parent
|
|
971
|
+
});
|
|
972
|
+
if (node.children?.length && this.expanded.has(node)) walk(node.children, depth + 1, node);
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
walk(this.roots, 0);
|
|
976
|
+
return out;
|
|
977
|
+
}
|
|
978
|
+
current() {
|
|
979
|
+
return this.rows()[this.cursor];
|
|
980
|
+
}
|
|
981
|
+
isExpanded(node) {
|
|
982
|
+
return this.expanded.has(node);
|
|
983
|
+
}
|
|
984
|
+
leaves(node) {
|
|
985
|
+
if (!node.children?.length) return node.value !== void 0 ? [node.value] : [];
|
|
986
|
+
return node.children.flatMap((c) => this.leaves(c));
|
|
987
|
+
}
|
|
988
|
+
state(node) {
|
|
989
|
+
const leaves = this.leaves(node);
|
|
990
|
+
if (leaves.length === 0) return "none";
|
|
991
|
+
const n = leaves.filter((l) => this.selected.has(l)).length;
|
|
992
|
+
return n === 0 ? "none" : n === leaves.length ? "all" : "some";
|
|
993
|
+
}
|
|
994
|
+
/** Space: leaf toggles; parent selects all descendants unless already all. */
|
|
995
|
+
toggle() {
|
|
996
|
+
const row = this.current();
|
|
997
|
+
if (!row) return;
|
|
998
|
+
const leaves = this.leaves(row.node);
|
|
999
|
+
if (this.state(row.node) === "all") for (const l of leaves) this.selected.delete(l);
|
|
1000
|
+
else for (const l of leaves) this.selected.add(l);
|
|
1001
|
+
}
|
|
1002
|
+
move(delta) {
|
|
1003
|
+
const n = this.rows().length;
|
|
1004
|
+
if (n === 0) return;
|
|
1005
|
+
this.cursor = (this.cursor + delta + n) % n;
|
|
1006
|
+
}
|
|
1007
|
+
/** Right: expand. On a leaf or an open node, nothing happens. */
|
|
1008
|
+
expand() {
|
|
1009
|
+
const row = this.current();
|
|
1010
|
+
if (row?.node.children?.length) this.expanded.add(row.node);
|
|
1011
|
+
}
|
|
1012
|
+
/** Left: collapse an open node; on a closed node or leaf, jump to its parent. */
|
|
1013
|
+
collapse() {
|
|
1014
|
+
const row = this.current();
|
|
1015
|
+
if (!row) return;
|
|
1016
|
+
if (row.node.children?.length && this.expanded.has(row.node)) {
|
|
1017
|
+
this.expanded.delete(row.node);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
if (row.parent) {
|
|
1021
|
+
const idx = this.rows().findIndex((r) => r.node === row.parent);
|
|
1022
|
+
if (idx >= 0) this.cursor = idx;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
toggleExpand() {
|
|
1026
|
+
const row = this.current();
|
|
1027
|
+
if (!row?.node.children?.length) return;
|
|
1028
|
+
if (this.expanded.has(row.node)) this.expanded.delete(row.node);
|
|
1029
|
+
else this.expanded.add(row.node);
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
//#endregion
|
|
1033
|
+
//#region src/lib/tree-prompt.ts
|
|
1034
|
+
const tty = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
1035
|
+
const paint = (code, s) => tty ? `\x1b[${code}m${s}\x1b[39m` : s;
|
|
1036
|
+
const dim = (s) => tty ? `\x1b[2m${s}\x1b[22m` : s;
|
|
1037
|
+
const cyan = (s) => paint(36, s);
|
|
1038
|
+
const green = (s) => paint(32, s);
|
|
1039
|
+
const yellow = (s) => paint(33, s);
|
|
1040
|
+
const red = (s) => paint(31, s);
|
|
1041
|
+
const gray = (s) => paint(90, s);
|
|
1042
|
+
const S_BAR = "│";
|
|
1043
|
+
const S_BAR_END = "└";
|
|
1044
|
+
const CHECK = {
|
|
1045
|
+
none: "◻",
|
|
1046
|
+
some: yellow("◧"),
|
|
1047
|
+
all: green("◼")
|
|
1048
|
+
};
|
|
1049
|
+
function symbol(state) {
|
|
1050
|
+
if (state === "cancel") return red("■");
|
|
1051
|
+
if (state === "error") return yellow("▲");
|
|
1052
|
+
if (state === "submit") return green("◇");
|
|
1053
|
+
return cyan("◆");
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* A checkbox tree. Space toggles the node under the cursor (a parent toggles
|
|
1057
|
+
* everything beneath it), ←/→ collapse/expand, Enter confirms.
|
|
1058
|
+
* Resolves to the selected leaf values, or the clack cancel symbol.
|
|
1059
|
+
*/
|
|
1060
|
+
async function treeSelect(opts) {
|
|
1061
|
+
const model = new TreeModel(opts.tree, {
|
|
1062
|
+
expandDepth: opts.expandDepth,
|
|
1063
|
+
initialSelected: opts.initialValues
|
|
1064
|
+
});
|
|
1065
|
+
const maxItems = Math.max(5, opts.maxItems ?? (process.stdout.rows || 24) - 6);
|
|
1066
|
+
const prompt = new Prompt({
|
|
1067
|
+
validate: () => {
|
|
1068
|
+
if (opts.required !== false && model.selected.size === 0) return "Select at least one item.";
|
|
1069
|
+
},
|
|
1070
|
+
render() {
|
|
1071
|
+
const title = `${gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`;
|
|
1072
|
+
if (this.state === "submit") {
|
|
1073
|
+
const n = model.selected.size;
|
|
1074
|
+
return `${title}${gray(S_BAR)} ${dim(`${n} item${n === 1 ? "" : "s"} selected`)}`;
|
|
1075
|
+
}
|
|
1076
|
+
if (this.state === "cancel") return `${title}${gray(S_BAR)} ${dim("cancelled")}\n${gray(S_BAR)}`;
|
|
1077
|
+
const rows = model.rows();
|
|
1078
|
+
let start = 0;
|
|
1079
|
+
if (rows.length > maxItems) start = Math.min(Math.max(0, model.cursor - Math.floor(maxItems / 2)), rows.length - maxItems);
|
|
1080
|
+
const end = Math.min(rows.length, start + maxItems);
|
|
1081
|
+
const lines = [];
|
|
1082
|
+
if (start > 0) lines.push(`${cyan(S_BAR)} ${dim("…")}`);
|
|
1083
|
+
for (let i = start; i < end; i++) {
|
|
1084
|
+
const { node, depth } = rows[i];
|
|
1085
|
+
const active = i === model.cursor;
|
|
1086
|
+
const arrow = !!node.children?.length ? model.isExpanded(node) ? "▾" : "▸" : " ";
|
|
1087
|
+
const box = CHECK[model.state(node)];
|
|
1088
|
+
const indent = " ".repeat(depth);
|
|
1089
|
+
let label = active ? node.label : dim(node.label);
|
|
1090
|
+
if (node.hint) label += ` ${dim(node.hint)}`;
|
|
1091
|
+
lines.push(`${cyan(S_BAR)} ${indent}${dim(arrow)} ${box} ${label}`);
|
|
1092
|
+
}
|
|
1093
|
+
if (end < rows.length) lines.push(`${cyan(S_BAR)} ${dim("…")}`);
|
|
1094
|
+
const footer = this.state === "error" ? `${yellow(S_BAR_END)} ${yellow(this.error)}` : `${cyan(S_BAR_END)} ${dim("space toggle · ←/→ collapse/expand · enter confirm")}`;
|
|
1095
|
+
return `${title}${lines.join("\n")}\n${footer}\n`;
|
|
1096
|
+
}
|
|
1097
|
+
}, false);
|
|
1098
|
+
prompt.on("cursor", (key) => {
|
|
1099
|
+
switch (key) {
|
|
1100
|
+
case "up":
|
|
1101
|
+
model.move(-1);
|
|
1102
|
+
break;
|
|
1103
|
+
case "down":
|
|
1104
|
+
model.move(1);
|
|
1105
|
+
break;
|
|
1106
|
+
case "left":
|
|
1107
|
+
model.collapse();
|
|
1108
|
+
break;
|
|
1109
|
+
case "right":
|
|
1110
|
+
model.expand();
|
|
1111
|
+
break;
|
|
1112
|
+
case "space":
|
|
1113
|
+
model.toggle();
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
prompt.value = [...model.selected];
|
|
1117
|
+
});
|
|
1118
|
+
prompt.value = [...model.selected];
|
|
1119
|
+
const result = await prompt.prompt();
|
|
1120
|
+
if (isCancel(result)) return result;
|
|
1121
|
+
return [...model.selected];
|
|
1122
|
+
}
|
|
1123
|
+
//#endregion
|
|
943
1124
|
//#region src/lib/version.ts
|
|
944
|
-
const VERSION = "0.
|
|
1125
|
+
const VERSION = "0.15.0";
|
|
945
1126
|
//#endregion
|
|
946
1127
|
//#region src/lib/migrations/index.ts
|
|
947
1128
|
const migrations = [{
|
|
@@ -1609,11 +1790,15 @@ async function promptSources(repoRoot) {
|
|
|
1609
1790
|
return sources;
|
|
1610
1791
|
}
|
|
1611
1792
|
/**
|
|
1612
|
-
*
|
|
1613
|
-
*
|
|
1793
|
+
* One checkbox tree: source → domain → feature type → feature (plus a
|
|
1794
|
+
* `files` group per domain). Ticking a node ticks everything beneath it.
|
|
1795
|
+
* Returns, per source, the picked domains with their `include` lists
|
|
1796
|
+
* (`undefined` include = whole domain).
|
|
1614
1797
|
*/
|
|
1615
|
-
async function
|
|
1616
|
-
const
|
|
1798
|
+
async function promptSelection(repoRoot, sources, toolNames) {
|
|
1799
|
+
const SEP = "\0";
|
|
1800
|
+
const contentsByKey = /* @__PURE__ */ new Map();
|
|
1801
|
+
const tree = [];
|
|
1617
1802
|
for (const source of sources) {
|
|
1618
1803
|
const srcPath = resolveSourcePath(repoRoot, source);
|
|
1619
1804
|
const domains = await listDomains(srcPath);
|
|
@@ -1621,60 +1806,74 @@ async function promptDomains(repoRoot, sources) {
|
|
|
1621
1806
|
p.log.warn(`${source.name}: no domain folders found — nothing to select.`);
|
|
1622
1807
|
continue;
|
|
1623
1808
|
}
|
|
1624
|
-
|
|
1809
|
+
const domainNodes = [];
|
|
1625
1810
|
for (const domain of domains) {
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1628
|
-
|
|
1811
|
+
const contents = await listDomainContents(srcPath, domain, toolNames);
|
|
1812
|
+
contentsByKey.set(`${source.name}${SEP}${domain}`, contents);
|
|
1813
|
+
const prefix = `${source.name}${SEP}${domain}${SEP}`;
|
|
1814
|
+
const children = contents.featureTypes.filter((ft) => ft.features.length > 0).map((ft) => ({
|
|
1815
|
+
label: ft.name,
|
|
1816
|
+
hint: `(${ft.features.length})`,
|
|
1817
|
+
children: ft.features.map((f) => ({
|
|
1818
|
+
label: f,
|
|
1819
|
+
value: `${prefix}${ft.name}/${f}`
|
|
1820
|
+
}))
|
|
1821
|
+
}));
|
|
1822
|
+
if (contents.files.length > 0) children.push({
|
|
1823
|
+
label: "files",
|
|
1824
|
+
children: contents.files.map((f) => ({
|
|
1825
|
+
label: f,
|
|
1826
|
+
value: `${prefix}${f}`
|
|
1827
|
+
}))
|
|
1828
|
+
});
|
|
1829
|
+
const hint = contents.featureTypes.filter((ft) => ft.features.length > 0).map((ft) => `${ft.features.length} ${ft.name}`).join(", ");
|
|
1830
|
+
domainNodes.push(children.length > 0 ? {
|
|
1629
1831
|
label: domain,
|
|
1630
|
-
hint: hint
|
|
1832
|
+
hint: hint ? `(${hint})` : void 0,
|
|
1833
|
+
children
|
|
1834
|
+
} : {
|
|
1835
|
+
label: domain,
|
|
1836
|
+
hint: "(empty)",
|
|
1837
|
+
value: prefix
|
|
1631
1838
|
});
|
|
1632
1839
|
}
|
|
1840
|
+
tree.push({
|
|
1841
|
+
label: source.name,
|
|
1842
|
+
children: domainNodes
|
|
1843
|
+
});
|
|
1633
1844
|
}
|
|
1634
|
-
if (
|
|
1845
|
+
if (tree.length === 0) {
|
|
1635
1846
|
p.cancel("No domains found in any source. Check the source layout: <source>/<domain>/<feature-type>/…");
|
|
1636
1847
|
process.exit(1);
|
|
1637
1848
|
}
|
|
1638
|
-
const picked = await
|
|
1639
|
-
message: "
|
|
1640
|
-
|
|
1849
|
+
const picked = await treeSelect({
|
|
1850
|
+
message: "What do you want to sync? Tick a domain to take all of it, or open it and pick pieces.",
|
|
1851
|
+
tree,
|
|
1852
|
+
expandDepth: 1,
|
|
1641
1853
|
required: true
|
|
1642
1854
|
});
|
|
1643
1855
|
cancelled(picked);
|
|
1644
|
-
const
|
|
1856
|
+
const byDomain = /* @__PURE__ */ new Map();
|
|
1645
1857
|
for (const value of picked) {
|
|
1646
|
-
const
|
|
1647
|
-
const
|
|
1648
|
-
const
|
|
1649
|
-
|
|
1858
|
+
const [sourceName, domain, rel] = value.split(SEP);
|
|
1859
|
+
const key = `${sourceName}${SEP}${domain}`;
|
|
1860
|
+
const set = byDomain.get(key) ?? /* @__PURE__ */ new Set();
|
|
1861
|
+
if (rel) set.add(rel);
|
|
1862
|
+
byDomain.set(key, set);
|
|
1863
|
+
}
|
|
1864
|
+
const result = /* @__PURE__ */ new Map();
|
|
1865
|
+
for (const [key, rels] of byDomain) {
|
|
1866
|
+
const [sourceName, domain] = key.split(SEP);
|
|
1867
|
+
const contents = contentsByKey.get(key);
|
|
1868
|
+
const include = rels.size === 0 ? void 0 : buildInclude(contents, rels);
|
|
1869
|
+
const list = result.get(sourceName) ?? [];
|
|
1870
|
+
list.push(include ? {
|
|
1871
|
+
name: domain,
|
|
1872
|
+
include
|
|
1873
|
+
} : { name: domain });
|
|
1874
|
+
result.set(sourceName, list);
|
|
1650
1875
|
}
|
|
1651
|
-
return
|
|
1652
|
-
}
|
|
1653
|
-
/** Let the user deselect individual features / files inside one domain. */
|
|
1654
|
-
async function promptInclude(srcPath, sourceName, domain, toolNames) {
|
|
1655
|
-
const contents = await listDomainContents(srcPath, domain, toolNames);
|
|
1656
|
-
const options = {};
|
|
1657
|
-
for (const ft of contents.featureTypes) {
|
|
1658
|
-
if (ft.features.length === 0) continue;
|
|
1659
|
-
options[ft.name] = ft.features.map((f) => ({
|
|
1660
|
-
value: `${ft.name}/${f}`,
|
|
1661
|
-
label: f
|
|
1662
|
-
}));
|
|
1663
|
-
}
|
|
1664
|
-
if (contents.files.length > 0) options["files"] = contents.files.map((f) => ({
|
|
1665
|
-
value: f,
|
|
1666
|
-
label: f
|
|
1667
|
-
}));
|
|
1668
|
-
if (Object.keys(options).length === 0) return void 0;
|
|
1669
|
-
const all = Object.values(options).flatMap((o) => o.map((x) => x.value));
|
|
1670
|
-
const picked = await p.groupMultiselect({
|
|
1671
|
-
message: `${sourceName}/${domain}: deselect what you don't want`,
|
|
1672
|
-
options,
|
|
1673
|
-
initialValues: all,
|
|
1674
|
-
required: true
|
|
1675
|
-
});
|
|
1676
|
-
cancelled(picked);
|
|
1677
|
-
return buildInclude(contents, new Set(picked));
|
|
1876
|
+
return result;
|
|
1678
1877
|
}
|
|
1679
1878
|
async function initCommand(cwd, opts) {
|
|
1680
1879
|
const repoRoot = cwd ?? findRepoRoot();
|
|
@@ -1721,24 +1920,8 @@ async function initCommand(cwd, opts) {
|
|
|
1721
1920
|
const tools = await promptTools();
|
|
1722
1921
|
const sources = await promptSources(repoRoot);
|
|
1723
1922
|
await fetchSources(repoRoot, sources);
|
|
1724
|
-
const
|
|
1725
|
-
const
|
|
1726
|
-
message: "Sync everything inside the selected domains? (No = pick individual skills, agents, files…)",
|
|
1727
|
-
initialValue: true
|
|
1728
|
-
});
|
|
1729
|
-
cancelled(everything);
|
|
1730
|
-
const toolNames = tools.map((t) => t.name);
|
|
1731
|
-
for (const source of sources) {
|
|
1732
|
-
const domains = pickedDomains.get(source.name) ?? [];
|
|
1733
|
-
source.domains = [];
|
|
1734
|
-
for (const domain of domains) {
|
|
1735
|
-
const include = everything ? void 0 : await promptInclude(resolveSourcePath(repoRoot, source), source.name, domain, toolNames);
|
|
1736
|
-
source.domains.push(include ? {
|
|
1737
|
-
name: domain,
|
|
1738
|
-
include
|
|
1739
|
-
} : { name: domain });
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1923
|
+
const picked = await promptSelection(repoRoot, sources, tools.map((t) => t.name));
|
|
1924
|
+
for (const source of sources) source.domains = picked.get(source.name) ?? [];
|
|
1742
1925
|
const activeSources = sources.filter((s) => (s.domains?.length ?? 0) > 0);
|
|
1743
1926
|
for (const s of sources) if (!activeSources.includes(s)) p.log.warn(`${s.name}: no domains selected — source dropped from config.`);
|
|
1744
1927
|
await saveConfig(repoRoot, {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["pkg.version"],"sources":["../src/lib/config.ts","../src/lib/git.ts","../src/lib/fs.ts","../src/lib/sources.ts","../src/lib/manifest.ts","../package.json","../src/lib/version.ts","../src/lib/migrations/index.ts","../src/lib/sync.ts","../src/commands/sync.ts","../src/commands/init.ts","../src/commands/opt-out.ts","../src/index.ts"],"sourcesContent":["import { readFile, writeFile, access, mkdir, rm } from 'node:fs/promises';\nimport { join, isAbsolute } from 'node:path';\nimport yaml from 'js-yaml';\nimport { z } from 'zod';\n\n// ---------------------------------------------------------------------------\n// Zod Schemas\n// ---------------------------------------------------------------------------\n\nconst SAFE_NAME_RE = /^[A-Za-z0-9._-]+$/;\n\nconst safeName = z\n .string()\n .min(1)\n .refine((v) => SAFE_NAME_RE.test(v) && v !== '.' && v !== '..', {\n message: 'Only [A-Za-z0-9._-] characters allowed, cannot be . or ..',\n });\n\nconst safeRelativeFolder = z\n .string()\n .min(1)\n .refine(\n (value) => {\n if (isAbsolute(value) || value.includes('\\0')) return false;\n const segments = value.split(/[\\\\/]/).filter((s) => s.length > 0);\n if (segments.length === 0) return false;\n return segments.every(\n (seg) => seg !== '..' && seg !== '.' && /^\\.?[A-Za-z0-9._-]+$/.test(seg)\n );\n },\n { message: 'Must be a relative path using only [A-Za-z0-9._-]' }\n );\n\nconst toolConfigSchema = z.object({\n name: safeName.refine((v) => !v.includes('--'), {\n message: \"Must not contain '--' (reserved for tool-prefix routing)\",\n }),\n folder: safeRelativeFolder,\n});\n\nconst sourceConfigSchema = z.object({\n name: safeName,\n source: z.string().min(1).refine((v) => !v.startsWith('-'), {\n message: \"Must not start with '-'\",\n }),\n branch: z\n .string()\n .refine((v) => /^[A-Za-z0-9._/-]+$/.test(v) && !v.startsWith('-'), {\n message: \"Must match [A-Za-z0-9._/-] and not start with '-'\",\n })\n .optional(),\n});\n\n/**\n * A path inside a domain that should be synced. One or two segments:\n * `skills` → the whole feature type\n * `skills/deploy` → a single feature (folder or file)\n * `AGENTS.md` → a flat file at the domain root\n */\nconst includePath = z\n .string()\n .min(1)\n .refine(\n (v) => {\n const segs = v.split('/');\n return (\n segs.length <= 2 &&\n segs.every((seg) => SAFE_NAME_RE.test(seg) && seg !== '.' && seg !== '..')\n );\n },\n { message: 'Must be <feature-type>, <feature-type>/<feature> or <file> using [A-Za-z0-9._-]' }\n );\n\nconst domainObjectSchema = z.object({\n name: safeName,\n /** Paths to sync from this domain. Omitted = everything. */\n include: z.array(includePath).optional(),\n});\n\n/** Domains are written as objects; a bare string (`- shared`) is accepted as shorthand. */\nconst domainConfigSchema = z.union([\n safeName.transform((name): { name: string; include?: string[] } => ({ name })),\n domainObjectSchema,\n]);\n\nconst bridgeConfigSchema = z\n .object({\n version: z.string().optional(),\n /**\n * Legacy (< 0.14): domains applied to every source. Still honored as the\n * fallback for sources without their own `domains`.\n */\n domains: z.array(safeName).optional(),\n tools: z.array(toolConfigSchema).min(1, \"'tools' must be a non-empty array\"),\n sources: z\n .array(sourceConfigSchema.extend({ domains: z.array(domainConfigSchema).optional() }))\n .min(1, \"'sources' must be a non-empty array\"),\n })\n .superRefine((data, ctx) => {\n data.sources.forEach((s, i) => {\n const domains = s.domains ?? data.domains;\n if (!domains || domains.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Source '${s.name}' has no domains (set 'sources[].domains' or top-level 'domains')`,\n path: ['sources', i, 'domains'],\n });\n }\n const seen = new Set<string>();\n for (const d of s.domains ?? []) {\n if (seen.has(d.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate domain '${d.name}' in source '${s.name}'`,\n path: ['sources', i, 'domains'],\n });\n }\n seen.add(d.name);\n }\n });\n\n // Check unique tool names\n const toolNames = new Set<string>();\n const toolFolders = new Set<string>();\n data.tools.forEach((t, i) => {\n if (toolNames.has(t.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate tool name: '${t.name}'`,\n path: ['tools', i, 'name'],\n });\n }\n toolNames.add(t.name);\n if (toolFolders.has(t.folder)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate tool folder: '${t.folder}'`,\n path: ['tools', i, 'folder'],\n });\n }\n toolFolders.add(t.folder);\n });\n\n // Check unique source names and branch validity\n const sourceNames = new Set<string>();\n data.sources.forEach((s, i) => {\n if (sourceNames.has(s.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate source name: '${s.name}'`,\n path: ['sources', i, 'name'],\n });\n }\n sourceNames.add(s.name);\n\n const isRemote =\n s.source.startsWith('https://') ||\n s.source.startsWith('http://') ||\n s.source.startsWith('file://') ||\n /^[\\w.-]+@[\\w.-]+:/.test(s.source);\n\n if (s.branch && !isRemote) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"'branch' is only valid for remote sources\",\n path: ['sources', i, 'branch'],\n });\n }\n if (!isRemote && !isAbsolute(s.source)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'Local source paths must be absolute',\n path: ['sources', i, 'source'],\n });\n }\n });\n });\n\n// ---------------------------------------------------------------------------\n// Types (inferred from Zod schemas)\n// ---------------------------------------------------------------------------\n\nexport type SourceType = 'git-https' | 'git-ssh' | 'local';\nexport type ToolConfig = z.infer<typeof toolConfigSchema>;\nexport type DomainConfig = z.infer<typeof domainObjectSchema>;\nexport type SourceConfig = z.infer<typeof sourceConfigSchema> & { domains?: DomainConfig[] };\nexport type BridgeConfig = z.infer<typeof bridgeConfigSchema>;\n\n// ---------------------------------------------------------------------------\n// Domain resolution & include filtering\n// ---------------------------------------------------------------------------\n\n/**\n * Domains to scan for a source: its own `domains`, falling back to the legacy\n * top-level `domains` list (everything included).\n */\nexport function sourceDomains(config: BridgeConfig, source: SourceConfig): DomainConfig[] {\n if (source.domains) return source.domains;\n return (config.domains ?? []).map((name) => ({ name }));\n}\n\n/**\n * Whether `relPath` (relative to the domain root, e.g. `skills`,\n * `skills/deploy`, `AGENTS.md`) is selected by the domain's `include` list.\n * No `include` means everything is selected.\n */\nexport function isIncluded(domain: DomainConfig, relPath: string): boolean {\n const inc = domain.include;\n if (!inc) return true;\n return inc.some((entry) => entry === relPath || relPath.startsWith(entry + '/') || entry.startsWith(relPath + '/'));\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const BRIDGE_DIR = '.agent-bridge';\nexport const CONFIG_FILENAME = 'config.yml';\n\n/**\n * Tombstone written by `opt-out`. It lives inside `.agent-bridge/` so it's\n * gitignored by default (the directory's `.gitignore` ignores everything but\n * `config.yml`), keeping opt-out local to a machine. `init`/`sync` honor it so\n * a `postinstall` guard doesn't silently reinstall Agent Bridge on the next\n * `npm install`. Force-add it (`git add -f`) to commit a repo-wide opt-out.\n */\nexport const OPT_OUT_MARKER = join(BRIDGE_DIR, 'optout');\n\n// ---------------------------------------------------------------------------\n// Source type detection\n// ---------------------------------------------------------------------------\n\nexport function detectSourceType(source: string): SourceType {\n if (\n source.startsWith('https://') ||\n source.startsWith('http://') ||\n source.startsWith('file://')\n ) {\n return 'git-https';\n }\n if (/^[\\w.-]+@[\\w.-]+:/.test(source)) {\n return 'git-ssh';\n }\n return 'local';\n}\n\nexport function isRemoteSource(source: string): boolean {\n const type = detectSourceType(source);\n return type === 'git-https' || type === 'git-ssh';\n}\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nexport function bridgeDir(repoRoot: string): string {\n return join(repoRoot, BRIDGE_DIR);\n}\n\nexport function configPath(repoRoot: string): string {\n return join(repoRoot, BRIDGE_DIR, CONFIG_FILENAME);\n}\n\nexport function sourceDir(repoRoot: string, sourceName: string): string {\n return join(repoRoot, BRIDGE_DIR, sourceName);\n}\n\nexport function optOutMarkerPath(repoRoot: string): string {\n return join(repoRoot, OPT_OUT_MARKER);\n}\n\n/** Whether an opt-out tombstone is present at the repo root. */\nexport async function isOptedOut(repoRoot: string): Promise<boolean> {\n try {\n await access(optOutMarkerPath(repoRoot));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Write the opt-out tombstone inside `.agent-bridge/`. Recreates the directory\n * (opt-out deletes it) and its `.gitignore` so the marker is ignored by default.\n */\nexport async function writeOptOutMarker(repoRoot: string): Promise<void> {\n const dir = bridgeDir(repoRoot);\n await mkdir(dir, { recursive: true });\n // Same ignore rules init/sync write: ignore everything but the config.\n await writeFile(\n join(dir, '.gitignore'),\n ['# Ignore cloned sources', '*', '!config.yml', '!.gitignore'].join('\\n') + '\\n',\n 'utf-8'\n );\n await writeFile(\n optOutMarkerPath(repoRoot),\n '# Agent Bridge opt-out marker. Remove this file (or run `agent-bridge init --force`) to re-enable.\\n',\n 'utf-8'\n );\n}\n\n/** Remove the opt-out tombstone if present (idempotent). */\nexport async function removeOptOutMarker(repoRoot: string): Promise<void> {\n await rm(optOutMarkerPath(repoRoot), { force: true });\n}\n\n// ---------------------------------------------------------------------------\n// Config I/O\n// ---------------------------------------------------------------------------\n\nexport async function configExists(repoRoot: string): Promise<boolean> {\n try {\n await access(configPath(repoRoot));\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function loadConfig(repoRoot: string): Promise<BridgeConfig> {\n const raw = await readFile(configPath(repoRoot), 'utf-8');\n const data = yaml.load(raw);\n\n const result = bridgeConfigSchema.safeParse(data);\n if (!result.success) {\n const errors = result.error.issues.map(\n (i) => `${i.path.join('.')}: ${i.message}`\n );\n throw new Error(`Invalid config: ${errors.join('; ')}`);\n }\n\n return result.data;\n}\n\nexport async function saveConfig(\n repoRoot: string,\n config: BridgeConfig\n): Promise<void> {\n const dir = bridgeDir(repoRoot);\n await mkdir(dir, { recursive: true });\n const content = yaml.dump(config, { lineWidth: -1, noRefs: true, skipInvalid: true });\n await writeFile(configPath(repoRoot), content, 'utf-8');\n}\n\n// ---------------------------------------------------------------------------\n// Validation (legacy interface for tests)\n// ---------------------------------------------------------------------------\n\nexport interface ConfigValidationResult {\n ok: boolean;\n errors: string[];\n}\n\nexport function validateConfig(config: unknown): ConfigValidationResult {\n const result = bridgeConfigSchema.safeParse(config);\n if (result.success) {\n return { ok: true, errors: [] };\n }\n const errors = result.error.issues.map(\n (i) => `${i.path.join('.')}: ${i.message}`\n );\n return { ok: false, errors };\n}\n","import { execSync } from 'node:child_process';\nimport { mkdir, writeFile, chmod, readFile, access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport function findRepoRoot(): string {\n try {\n return execSync('git rev-parse --show-toplevel', {\n encoding: 'utf-8',\n stdio: 'pipe',\n }).trim();\n } catch {\n return process.cwd();\n }\n}\n\n/**\n * Check if a directory is inside a Git repository.\n */\nexport function isInGitRepo(cwd?: string): boolean {\n try {\n execSync('git rev-parse --is-inside-work-tree', {\n encoding: 'utf-8',\n stdio: 'pipe',\n cwd,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get the path to the .git/hooks directory.\n */\nexport function getGitHooksDir(repoRoot: string): string {\n return join(repoRoot, '.git', 'hooks');\n}\n\n/**\n * The hook names that Agent Bridge will install.\n */\nexport const AGENT_BRIDGE_HOOKS = ['post-checkout', 'post-merge'] as const;\nexport type AgentBridgeHook = (typeof AGENT_BRIDGE_HOOKS)[number];\n\n/**\n * Marker comment to identify Agent Bridge hooks.\n */\nconst HOOK_MARKER = '# agent-bridge-hook';\n\n/**\n * Generate the hook script content.\n * Runs sync in the background, logging to `.agent-bridge/hook.log`\n * (trimmed to the last ~200 lines) so failures are diagnosable.\n */\nexport function generateHookScript(): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# This hook was installed by Agent Bridge.\n# It runs 'agent-bridge sync' in the background to keep your AI agent\n# configurations up to date.\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel 2>/dev/null)\"\nLOG_DIR=\"\\${REPO_ROOT:-.}/.agent-bridge\"\nLOG_FILE=\"\\${LOG_DIR}/hook.log\"\n\nmkdir -p \"\\$LOG_DIR\" 2>/dev/null\n\n(\n # Wait a moment for git to finish\n sleep 1\n\n {\n echo \"--- $(date '+%Y-%m-%dT%H:%M:%S%z') agent-bridge hook ---\"\n if command -v agent-bridge >/dev/null 2>&1; then\n agent-bridge sync\n elif command -v npx >/dev/null 2>&1; then\n npx @sofatutor/agent-bridge sync\n else\n echo \"agent-bridge not found (install globally or ensure npx is available)\"\n fi\n } >>\"\\$LOG_FILE\" 2>&1\n\n # Keep the log from growing without bound.\n if [ -f \"\\$LOG_FILE\" ]; then\n tail -n 200 \"\\$LOG_FILE\" >\"\\$LOG_FILE.tmp\" && mv \"\\$LOG_FILE.tmp\" \"\\$LOG_FILE\"\n fi\n) </dev/null >/dev/null 2>&1 &\n`;\n}\n\n/**\n * Check if a hook file contains the Agent Bridge marker.\n */\nexport async function hasAgentBridgeHook(hookPath: string): Promise<boolean> {\n try {\n const content = await readFile(hookPath, 'utf-8');\n return content.includes(HOOK_MARKER);\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a hook file exists.\n */\nasync function hookExists(hookPath: string): Promise<boolean> {\n try {\n await access(hookPath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport interface InstallHooksResult {\n installed: AgentBridgeHook[];\n skipped: AgentBridgeHook[];\n errors: Array<{ hook: AgentBridgeHook; error: string }>;\n}\n\n/**\n * Install Agent Bridge git hooks in the repository.\n * \n * @param repoRoot - The root of the git repository\n * @param force - If true, overwrite existing hooks that don't have the marker\n * @returns Result with installed, skipped, and errored hooks\n */\nexport async function installGitHooks(\n repoRoot: string,\n force = false\n): Promise<InstallHooksResult> {\n const result: InstallHooksResult = {\n installed: [],\n skipped: [],\n errors: [],\n };\n\n if (!isInGitRepo(repoRoot)) {\n for (const hook of AGENT_BRIDGE_HOOKS) {\n result.errors.push({ hook, error: 'Not a git repository' });\n }\n return result;\n }\n\n const hooksDir = getGitHooksDir(repoRoot);\n\n // Ensure hooks directory exists\n try {\n await mkdir(hooksDir, { recursive: true });\n } catch (err) {\n for (const hook of AGENT_BRIDGE_HOOKS) {\n result.errors.push({ hook, error: `Failed to create hooks directory: ${err}` });\n }\n return result;\n }\n\n const hookContent = generateHookScript();\n\n for (const hookName of AGENT_BRIDGE_HOOKS) {\n const hookPath = join(hooksDir, hookName);\n\n try {\n const exists = await hookExists(hookPath);\n \n if (exists) {\n const hasMarker = await hasAgentBridgeHook(hookPath);\n \n if (hasMarker) {\n // Already installed, update it\n await writeFile(hookPath, hookContent, 'utf-8');\n await chmod(hookPath, 0o755);\n result.installed.push(hookName);\n } else if (force) {\n // Force overwrite\n await writeFile(hookPath, hookContent, 'utf-8');\n await chmod(hookPath, 0o755);\n result.installed.push(hookName);\n } else {\n // Skip - existing hook without marker\n result.skipped.push(hookName);\n }\n } else {\n // Create new hook\n await writeFile(hookPath, hookContent, 'utf-8');\n await chmod(hookPath, 0o755);\n result.installed.push(hookName);\n }\n } catch (err) {\n result.errors.push({ hook: hookName, error: String(err) });\n }\n }\n\n return result;\n}\n\n/**\n * Rewrite hooks that Agent Bridge installed earlier with the current script.\n * Hooks we did not install (no marker) and missing hooks are left alone.\n */\nexport async function refreshGitHooks(repoRoot: string): Promise<AgentBridgeHook[]> {\n const refreshed: AgentBridgeHook[] = [];\n if (!isInGitRepo(repoRoot)) return refreshed;\n\n const hooksDir = getGitHooksDir(repoRoot);\n for (const hookName of AGENT_BRIDGE_HOOKS) {\n const hookPath = join(hooksDir, hookName);\n if (!(await hasAgentBridgeHook(hookPath))) continue;\n await writeFile(hookPath, generateHookScript(), 'utf-8');\n await chmod(hookPath, 0o755);\n refreshed.push(hookName);\n }\n return refreshed;\n}\n\n/**\n * Remove Agent Bridge git hooks from the repository.\n * Only removes hooks that have the Agent Bridge marker.\n */\nexport async function removeGitHooks(repoRoot: string): Promise<AgentBridgeHook[]> {\n const removed: AgentBridgeHook[] = [];\n\n if (!isInGitRepo(repoRoot)) {\n return removed;\n }\n\n const hooksDir = getGitHooksDir(repoRoot);\n\n for (const hookName of AGENT_BRIDGE_HOOKS) {\n const hookPath = join(hooksDir, hookName);\n \n try {\n if (await hasAgentBridgeHook(hookPath)) {\n const { unlink } = await import('node:fs/promises');\n await unlink(hookPath);\n removed.push(hookName);\n }\n } catch {\n // Ignore errors\n }\n }\n\n return removed;\n}\n","import { readdir, copyFile, stat } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport fsExtra from 'fs-extra';\n\nconst { pathExists, remove, outputFile, readFile: fsReadFile, ensureDir } = fsExtra;\n\n/** Name of the marker file placed inside every synced feature folder. */\nexport const MARKER_FILENAME = '.agentbridge';\n\nexport async function dirExists(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function fileExists(p: string): Promise<boolean> {\n return pathExists(p);\n}\n\nexport async function listFilesRecursive(dir: string): Promise<string[]> {\n const files: string[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n // Skip symlinks entirely: never follow them out of the source tree,\n // and don't attempt to replicate them (keeps the destination simple\n // and avoids symlink-escape vulnerabilities).\n if (entry.isSymbolicLink()) continue;\n // Skip Agent Bridge marker files that may exist in source repos.\n if (entry.name === MARKER_FILENAME) continue;\n if (entry.isDirectory()) {\n const subFiles = await listFilesRecursive(join(dir, entry.name));\n files.push(...subFiles.map((f) => join(entry.name, f)));\n } else if (entry.isFile()) {\n files.push(entry.name);\n }\n }\n return files;\n}\n\n/**\n * Copy all files from `srcDir` into `destDir`, preserving nested structure.\n * Overwrites existing files. Creates directories as needed.\n */\nexport async function copyDirContents(srcDir: string, destDir: string): Promise<void> {\n const files = await listFilesRecursive(srcDir);\n for (const relFile of files) {\n const srcFile = join(srcDir, relFile);\n const destFile = join(destDir, relFile);\n await ensureDir(dirname(destFile));\n await copyFile(srcFile, destFile);\n }\n}\n\n/**\n * Write the `.agentbridge` marker file into a feature folder.\n */\nexport async function writeMarker(featureDir: string): Promise<void> {\n await outputFile(join(featureDir, MARKER_FILENAME), '');\n}\n\n/**\n * Check whether a directory contains the `.agentbridge` marker.\n */\nexport async function hasMarker(featureDir: string): Promise<boolean> {\n return pathExists(join(featureDir, MARKER_FILENAME));\n}\n\n/**\n * Remove a directory and all its contents.\n */\nexport async function removeDir(dir: string): Promise<void> {\n await remove(dir);\n}\n\n/**\n * Remove a single file.\n */\nexport async function removeFile(filePath: string): Promise<void> {\n await remove(filePath);\n}\n\n// ---------------------------------------------------------------------------\n// Manifest helpers (single .agentbridge per feature-type directory)\n// ---------------------------------------------------------------------------\n\n/**\n * Read the manifest file in a directory. Returns list of managed entries.\n * Entries ending with '/' are folders, others are files.\n */\nexport async function readManifest(dir: string): Promise<string[]> {\n const manifestPath = join(dir, MARKER_FILENAME);\n try {\n const content = await fsReadFile(manifestPath, 'utf-8');\n return content.split('\\n').filter((line) => line.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Check if an entry in the manifest is a folder (ends with /).\n */\nexport function isManifestFolder(entry: string): boolean {\n return entry.endsWith('/');\n}\n\n/**\n * Get the base name from a manifest entry (strips trailing / for folders).\n */\nexport function manifestEntryName(entry: string): string {\n return entry.endsWith('/') ? entry.slice(0, -1) : entry;\n}\n\n/**\n * Write a manifest file listing managed entries.\n */\nexport async function writeManifest(dir: string, entries: string[]): Promise<void> {\n const manifestPath = join(dir, MARKER_FILENAME);\n const content = entries.length > 0 ? entries.join('\\n') + '\\n' : '';\n await outputFile(manifestPath, content);\n}\n\n/**\n * Add an entry to the manifest. Creates manifest if it doesn't exist.\n * Use trailing '/' for folders.\n */\nexport async function addToManifest(dir: string, entry: string): Promise<void> {\n const existing = await readManifest(dir);\n if (!existing.includes(entry)) {\n existing.push(entry);\n await writeManifest(dir, existing);\n }\n}\n\n/**\n * Remove an entry from the manifest.\n * Deletes the manifest file entirely if it becomes empty.\n */\nexport async function removeFromManifest(dir: string, entry: string): Promise<void> {\n const existing = await readManifest(dir);\n const updated = existing.filter((e) => e !== entry);\n if (updated.length !== existing.length) {\n if (updated.length === 0) {\n // Remove manifest file when empty\n await remove(join(dir, MARKER_FILENAME));\n } else {\n await writeManifest(dir, updated);\n }\n }\n}\n\n/**\n * Check if an entry is tracked in the manifest.\n */\nexport async function isInManifest(dir: string, entry: string): Promise<boolean> {\n const entries = await readManifest(dir);\n return entries.includes(entry);\n}\n","import { execFileSync } from 'node:child_process';\nimport { mkdir, readdir, rm, writeFile, access } from 'node:fs/promises';\nimport { join, isAbsolute, resolve } from 'node:path';\nimport {\n type SourceConfig,\n type BridgeConfig,\n bridgeDir,\n sourceDir,\n isRemoteSource,\n} from './config.js';\nimport { dirExists } from './fs.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Marker file written inside every directory Agent Bridge manages under\n * `.agent-bridge/`. Used to gate destructive cleanup so we never delete\n * user-placed content.\n */\nconst SOURCE_MARKER = '.agent-bridge-managed';\n\nfunction git(args: string[], cwd?: string): string {\n return execFileSync('git', args, {\n cwd,\n encoding: 'utf-8',\n stdio: 'pipe',\n }).trim();\n}\n\n/**\n * Branch names must not contain shell metacharacters or leading dashes\n * (which could be mistaken for git flags). Conservative but safe.\n */\nfunction assertSafeBranch(branch: string, sourceName: string): void {\n if (!/^[A-Za-z0-9._/-]+$/.test(branch) || branch.startsWith('-')) {\n throw new Error(\n `Source '${sourceName}': invalid branch name '${branch}'. ` +\n `Branch must match [A-Za-z0-9._/-]+ and not start with '-'.`\n );\n }\n}\n\n/**\n * Reject source URLs that begin with '-' to prevent them being interpreted\n * as CLI flags by git.\n */\nfunction assertSafeSourceUrl(source: string, sourceName: string): void {\n if (source.startsWith('-')) {\n throw new Error(\n `Source '${sourceName}': URL must not start with '-' (got '${source}').`\n );\n }\n}\n\nasync function writeSourceMarker(dest: string): Promise<void> {\n await writeFile(\n join(dest, SOURCE_MARKER),\n 'This directory is managed by agent-bridge. Do not edit manually.\\n',\n 'utf-8'\n );\n}\n\nasync function hasSourceMarker(dir: string): Promise<boolean> {\n try {\n await access(join(dir, SOURCE_MARKER));\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Git operations for a single remote source\n// ---------------------------------------------------------------------------\n\nexport async function cloneSource(\n repoRoot: string,\n source: SourceConfig\n): Promise<void> {\n assertSafeSourceUrl(source.source, source.name);\n if (source.branch) assertSafeBranch(source.branch, source.name);\n\n const dest = sourceDir(repoRoot, source.name);\n\n await mkdir(bridgeDir(repoRoot), { recursive: true });\n\n const args = ['clone', '--depth', '1'];\n if (source.branch) {\n args.push('--single-branch', '--branch', source.branch);\n }\n // '--' terminates option parsing so the URL / dest can never be read as flags.\n args.push('--', source.source, dest);\n\n execFileSync('git', args, { stdio: 'pipe' });\n\n await writeSourceMarker(dest);\n}\n\nexport async function fetchSource(\n repoRoot: string,\n source: SourceConfig\n): Promise<void> {\n assertSafeSourceUrl(source.source, source.name);\n if (source.branch) assertSafeBranch(source.branch, source.name);\n\n const dest = sourceDir(repoRoot, source.name);\n\n git(['fetch', '--prune', 'origin'], dest);\n\n if (source.branch) {\n const currentBranch = git(['rev-parse', '--abbrev-ref', 'HEAD'], dest);\n if (currentBranch !== source.branch) {\n // Branch changed in config — ensure we have the ref then check it out.\n // A shallow --single-branch clone only has one branch, so fetch the\n // new branch explicitly before checkout.\n try {\n git(['fetch', '--depth', '1', 'origin', source.branch], dest);\n } catch {\n // If fetch fails, let checkout surface the real error.\n }\n git(['checkout', source.branch], dest);\n }\n }\n\n try {\n git(['pull', '--ff-only'], dest);\n } catch {\n // pull may fail for tags or detached HEAD — that's okay after fetch\n }\n\n // Refresh marker (in case the directory was restored from backup without it).\n await writeSourceMarker(dest);\n}\n\n// ---------------------------------------------------------------------------\n// Local source resolution\n// ---------------------------------------------------------------------------\n\nexport function resolveLocalSource(\n repoRoot: string,\n source: SourceConfig\n): string {\n const raw = source.source;\n if (isAbsolute(raw)) return raw;\n return resolve(repoRoot, raw);\n}\n\n// ---------------------------------------------------------------------------\n// Resolve the effective filesystem path for a source\n// ---------------------------------------------------------------------------\n\nexport function resolveSourcePath(\n repoRoot: string,\n source: SourceConfig\n): string {\n if (isRemoteSource(source.source)) {\n return sourceDir(repoRoot, source.name);\n }\n return resolveLocalSource(repoRoot, source);\n}\n\n// ---------------------------------------------------------------------------\n// Sync all sources\n// ---------------------------------------------------------------------------\n\nexport interface SourceSyncResult {\n name: string;\n action: 'cloned' | 'updated' | 'local';\n error?: string;\n}\n\nexport async function syncSource(\n repoRoot: string,\n source: SourceConfig\n): Promise<SourceSyncResult> {\n if (!isRemoteSource(source.source)) {\n const resolved = resolveLocalSource(repoRoot, source);\n if (!(await dirExists(resolved))) {\n return {\n name: source.name,\n action: 'local',\n error: `Local source path does not exist: ${resolved}\\n Update the path in .agent-bridge/config.yml or run \"agent-bridge init\" to reconfigure.`,\n };\n }\n return { name: source.name, action: 'local' };\n }\n\n const dest = sourceDir(repoRoot, source.name);\n\n if (await dirExists(dest)) {\n try {\n await fetchSource(repoRoot, source);\n return { name: source.name, action: 'updated' };\n } catch (err) {\n return {\n name: source.name,\n action: 'updated',\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n try {\n await cloneSource(repoRoot, source);\n return { name: source.name, action: 'cloned' };\n } catch (err) {\n return {\n name: source.name,\n action: 'cloned',\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport async function syncAllSources(\n repoRoot: string,\n config: BridgeConfig\n): Promise<SourceSyncResult[]> {\n await ensureBridgeGitignore(repoRoot);\n return Promise.all(config.sources.map((source) => syncSource(repoRoot, source)));\n}\n\n// ---------------------------------------------------------------------------\n// Ensure .gitignore in .agent-bridge/\n// ---------------------------------------------------------------------------\n\n/**\n * Write a `.gitignore` inside `.agent-bridge/` that ignores cloned source\n * directories (which are nested git repos) while keeping `config.yml` tracked.\n * Without this, git sees the nested repos as gitlinks/submodules and creates\n * phantom dirty-state changes.\n */\nexport async function ensureBridgeGitignore(repoRoot: string): Promise<void> {\n const bridge = bridgeDir(repoRoot);\n await mkdir(bridge, { recursive: true });\n\n const gitignorePath = join(bridge, '.gitignore');\n const lines = ['# Ignore cloned sources', '*', '!config.yml', '!.gitignore'];\n const content = lines.join('\\n') + '\\n';\n\n await writeFile(gitignorePath, content, 'utf-8');\n}\n\n// ---------------------------------------------------------------------------\n// Remove sources that no longer exist in config\n// ---------------------------------------------------------------------------\n\n/**\n * Remove cloned source directories under `.agent-bridge/` that are no longer\n * referenced in config. Only directories carrying the Agent Bridge marker\n * file are eligible for deletion — user-placed content is always preserved.\n *\n * For backwards compatibility with clones created before the marker existed,\n * directories containing a `.git` folder are also treated as stale.\n */\nexport async function removeStaleSourceDirs(\n repoRoot: string,\n config: BridgeConfig\n): Promise<string[]> {\n const bridge = bridgeDir(repoRoot);\n if (!(await dirExists(bridge))) return [];\n\n const entries = await readdir(bridge, { withFileTypes: true });\n\n const configuredNames = new Set(\n config.sources.filter((s) => isRemoteSource(s.source)).map((s) => s.name)\n );\n\n const removed: string[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (configuredNames.has(entry.name)) continue;\n\n const candidate = join(bridge, entry.name);\n\n if (\n (await hasSourceMarker(candidate)) ||\n (await dirExists(join(candidate, '.git')))\n ) {\n await rm(candidate, { recursive: true, force: true });\n removed.push(entry.name);\n }\n }\n\n return removed;\n}\n","import { readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { type BridgeConfig, type DomainConfig, sourceDomains, isIncluded } from './config.js';\nimport { dirExists, fileExists } from './fs.js';\nimport { resolveSourcePath } from './sources.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nconst TOOL_PREFIX_SEPARATOR = '--';\n\nexport interface Feature {\n name: string;\n /** Raw feature-type directory name (may contain tool prefix) */\n type: string;\n /** Display type with tool prefix stripped (used for destination dir) */\n displayType: string;\n source: string;\n domain: string;\n /** Absolute path to the feature (directory or file) */\n absolutePath: string;\n /** Tool prefix if present (e.g. \"cursor\" from \"cursor--instructions\") */\n toolPrefix?: string;\n /** True if feature is a single file, false if a directory */\n isFile: boolean;\n}\n\nexport interface DuplicateConflict {\n name: string;\n type: string;\n paths: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport { dirExists } from './fs.js';\n\n\nexport function parseToolPrefix(name: string): {\n toolPrefix?: string;\n baseName: string;\n} {\n const idx = name.indexOf(TOOL_PREFIX_SEPARATOR);\n if (idx > 0) {\n return {\n toolPrefix: name.substring(0, idx),\n baseName: name.substring(idx + TOOL_PREFIX_SEPARATOR.length),\n };\n }\n return { baseName: name };\n}\n\nexport function featureMatchesTool(\n feature: Feature,\n toolName: string\n): boolean {\n if (!feature.toolPrefix) return true;\n return feature.toolPrefix === toolName;\n}\n\nexport function featureName(feature: Feature): string {\n if (feature.toolPrefix) {\n return parseToolPrefix(feature.name).baseName;\n }\n return feature.name;\n}\n\n/** @deprecated Use featureName instead */\n/** @deprecated Use featureName directly */\nexport const syncName = featureName;\n\n// ---------------------------------------------------------------------------\n// Discovery\n// ---------------------------------------------------------------------------\n\n/**\n * Discover all feature types across all sources and domains.\n */\nexport async function discoverFeatureTypes(\n repoRoot: string,\n config: BridgeConfig\n): Promise<string[]> {\n const types = new Set<string>();\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n for (const domain of sourceDomains(config, source)) {\n const domainDir = join(srcPath, domain.name);\n if (!(await dirExists(domainDir))) continue;\n\n const entries = await readdir(domainDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && isIncluded(domain, entry.name)) {\n types.add(entry.name);\n }\n }\n }\n }\n\n return [...types].sort();\n}\n\n/**\n * Scan all features across sources × domains × feature types.\n *\n * Structure: `<source-path>/<domain>/<feature-type>/<feature>/` (folder-based)\n * or `<source-path>/<domain>/<feature-type>/<feature.ext>` (file-based)\n */\nexport async function scanFeatures(\n repoRoot: string,\n config: BridgeConfig,\n featureTypes: string[]\n): Promise<Feature[]> {\n const features: Feature[] = [];\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n\n for (const domain of sourceDomains(config, source)) {\n for (const ft of featureTypes) {\n if (!isIncluded(domain, ft)) continue;\n const { toolPrefix: typeToolPrefix, baseName: baseType } =\n parseToolPrefix(ft);\n const ftDir = join(srcPath, domain.name, ft);\n\n if (!(await dirExists(ftDir))) continue;\n\n const entries = await readdir(ftDir, { withFileTypes: true });\n for (const entry of entries) {\n const isFile = entry.isFile();\n const isDir = entry.isDirectory();\n if (!isFile && !isDir) continue;\n if (!isIncluded(domain, `${ft}/${entry.name}`)) continue;\n\n const { toolPrefix: itemToolPrefix } = parseToolPrefix(entry.name);\n const toolPrefix = itemToolPrefix ?? typeToolPrefix;\n\n features.push({\n name: entry.name,\n type: ft,\n displayType: baseType,\n source: source.name,\n domain: domain.name,\n absolutePath: join(ftDir, entry.name),\n toolPrefix,\n isFile,\n });\n }\n }\n }\n }\n\n return features;\n}\n\n// ---------------------------------------------------------------------------\n// Source browsing (used by `init` to offer domains and their contents)\n// ---------------------------------------------------------------------------\n\n/** Top-level directories of a source that can act as domains. */\nexport async function listDomains(srcPath: string): Promise<string[]> {\n if (!(await dirExists(srcPath))) return [];\n const entries = await readdir(srcPath, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules')\n .map((e) => e.name)\n .sort();\n}\n\nexport interface DomainContents {\n /** Feature-type folders and the features inside them. */\n featureTypes: Array<{ name: string; features: string[] }>;\n /** Flat files sync would pick up: well-known root files and `<tool>--` files. */\n files: string[];\n}\n\n/**\n * List what `sync` would consider inside a domain, so the user can pick a\n * subset. `toolNames` filters `<tool>--file` entries to configured tools.\n */\nexport async function listDomainContents(\n srcPath: string,\n domain: string,\n toolNames: Iterable<string>\n): Promise<DomainContents> {\n const domainDir = join(srcPath, domain);\n const tools = new Set(toolNames);\n const result: DomainContents = { featureTypes: [], files: [] };\n if (!(await dirExists(domainDir))) return result;\n\n const entries = await readdir(domainDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue;\n if (entry.isDirectory()) {\n const features = (await readdir(join(domainDir, entry.name), { withFileTypes: true }))\n .filter((f) => (f.isFile() || f.isDirectory()) && !f.name.startsWith('.'))\n .map((f) => f.name)\n .sort();\n result.featureTypes.push({ name: entry.name, features });\n } else if (entry.isFile()) {\n const { toolPrefix } = parseToolPrefix(entry.name);\n if ((ROOT_FILES as readonly string[]).includes(entry.name) || (toolPrefix && tools.has(toolPrefix))) {\n result.files.push(entry.name);\n }\n }\n }\n result.featureTypes.sort((a, b) => a.name.localeCompare(b.name));\n result.files.sort();\n return result;\n}\n\n/** Type re-export so callers don't need to import config.js just for this. */\nexport type { DomainConfig };\n\n// ---------------------------------------------------------------------------\n// Duplicate detection\n// ---------------------------------------------------------------------------\n\nexport function detectDuplicates(features: Feature[]): DuplicateConflict[] {\n const byKey = new Map<string, Feature[]>();\n\n for (const f of features) {\n const linkName = featureName(f);\n const key = `${f.displayType}/${linkName}`;\n const group = byKey.get(key) ?? [];\n group.push(f);\n byKey.set(key, group);\n }\n\n const conflicts: DuplicateConflict[] = [];\n for (const [, group] of byKey) {\n if (group.length > 1) {\n conflicts.push({\n name: featureName(group[0]),\n type: group[0].type,\n paths: group.map((f) => f.absolutePath),\n });\n }\n }\n\n return conflicts;\n}\n\n// ---------------------------------------------------------------------------\n// Root file scanning\n// ---------------------------------------------------------------------------\n\n/**\n * Well-known root files that live at the domain root and should be synced to the\n * workspace root. When a source contains `<domain>/AGENTS.md` (etc.), Agent Bridge\n * copies it to the project root.\n */\nexport const ROOT_FILES = ['AGENTS.md', 'CLAUDE.md', 'SYSTEM.md'] as const;\nexport type RootFileName = (typeof ROOT_FILES)[number];\n\nexport interface RootFile {\n /** The well-known filename (e.g. \"AGENTS.md\") */\n fileName: RootFileName;\n /** Source that provides this file */\n source: string;\n /** Domain where it was found */\n domain: string;\n /** Absolute path to the source file */\n absolutePath: string;\n}\n\nexport interface RootFileDuplicate {\n fileName: RootFileName;\n paths: string[];\n}\n\n/**\n * Scan all sources × domains for well-known root files.\n * Returns one entry per found file.\n */\nexport async function scanRootFiles(\n repoRoot: string,\n config: BridgeConfig\n): Promise<RootFile[]> {\n const found: RootFile[] = [];\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n for (const domain of sourceDomains(config, source)) {\n for (const fileName of ROOT_FILES) {\n if (!isIncluded(domain, fileName)) continue;\n const filePath = join(srcPath, domain.name, fileName);\n if (await fileExists(filePath)) {\n found.push({\n fileName,\n source: source.name,\n domain: domain.name,\n absolutePath: filePath,\n });\n }\n }\n }\n }\n\n return found;\n}\n\n/**\n * Detect duplicate root files (same filename provided by multiple sources/domains).\n */\nexport function detectRootFileDuplicates(\n rootFiles: RootFile[]\n): RootFileDuplicate[] {\n const byName = new Map<RootFileName, RootFile[]>();\n for (const rf of rootFiles) {\n const group = byName.get(rf.fileName) ?? [];\n group.push(rf);\n byName.set(rf.fileName, group);\n }\n\n const duplicates: RootFileDuplicate[] = [];\n for (const [fileName, group] of byName) {\n if (group.length > 1) {\n duplicates.push({\n fileName,\n paths: group.map((rf) => rf.absolutePath),\n });\n }\n }\n\n return duplicates;\n}\n\n// ---------------------------------------------------------------------------\n// Tool root file scanning (tool-prefixed flat files at domain level)\n// ---------------------------------------------------------------------------\n\nexport interface ToolRootEntry {\n /** The tool name this entry targets (e.g. \"pi\") */\n toolName: string;\n /** Destination filename (e.g. \"settings.json\" from \"cursor--settings.json\") */\n name: string;\n /** Source that provides this entry */\n source: string;\n /** Domain where it was found */\n domain: string;\n /** Absolute path to the source file */\n absolutePath: string;\n}\n\nexport interface ToolRootDuplicate {\n /** The tool name */\n toolName: string;\n /** Name of the duplicate entry */\n name: string;\n /** Paths where the duplicates were found */\n paths: string[];\n}\n\n/**\n * Scan all sources × domains for tool-prefixed flat files at the domain level.\n * A file named `cursor--settings.json` targets the tool \"cursor\" with\n * destination filename \"settings.json\".\n */\nexport async function scanToolRootEntries(\n repoRoot: string,\n config: BridgeConfig\n): Promise<ToolRootEntry[]> {\n const entries: ToolRootEntry[] = [];\n const toolNames = new Set(config.tools.map((t) => t.name));\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n for (const domain of sourceDomains(config, source)) {\n const domainDir = join(srcPath, domain.name);\n if (!(await dirExists(domainDir))) continue;\n\n const domainEntries = await readdir(domainDir, { withFileTypes: true });\n for (const entry of domainEntries) {\n if (!entry.isFile() || !isIncluded(domain, entry.name)) continue;\n\n const { toolPrefix, baseName } = parseToolPrefix(entry.name);\n if (!toolPrefix || !toolNames.has(toolPrefix)) continue;\n\n entries.push({\n toolName: toolPrefix,\n name: baseName,\n source: source.name,\n domain: domain.name,\n absolutePath: join(domainDir, entry.name),\n });\n }\n }\n }\n\n return entries;\n}\n\n/**\n * Detect duplicate tool root entries (same tool + name from multiple sources/domains).\n */\nexport function detectToolRootDuplicates(\n entries: ToolRootEntry[]\n): ToolRootDuplicate[] {\n const byKey = new Map<string, ToolRootEntry[]>();\n for (const entry of entries) {\n const key = `${entry.toolName}/${entry.name}`;\n const group = byKey.get(key) ?? [];\n group.push(entry);\n byKey.set(key, group);\n }\n\n const duplicates: ToolRootDuplicate[] = [];\n for (const [, group] of byKey) {\n if (group.length > 1) {\n duplicates.push({\n toolName: group[0].toolName,\n name: group[0].name,\n paths: group.map((e) => e.absolutePath),\n });\n }\n }\n\n return duplicates;\n}\n","","import pkg from '../../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n","import { type BridgeConfig, saveConfig, loadConfig } from '../config.js';\nimport { refreshGitHooks } from '../git.js';\nimport { VERSION } from '../version.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A migration function receives the repo root and current config,\n * and returns the (possibly modified) config. It may also perform\n * filesystem operations (rename dirs, update files, etc.).\n */\nexport type MigrationFn = (\n repoRoot: string,\n config: BridgeConfig\n) => Promise<BridgeConfig>;\n\nexport interface Migration {\n /** Semver version this migration upgrades TO (e.g. \"0.6.0\"). */\n version: string;\n /** Human-readable description shown when running. */\n description: string;\n /** The migration logic. */\n migrate: MigrationFn;\n}\n\nexport interface MigrationResult {\n fromVersion: string;\n toVersion: string;\n applied: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Registry — add new migrations here in semver order\n// ---------------------------------------------------------------------------\n\nexport const migrations: Migration[] = [\n {\n version: '0.14.0',\n description: 'move top-level domains into each source; git hooks run `sync` only',\n migrate: async (repoRoot, config) => {\n // Legacy configs list domains once for all sources. Give every source its\n // own copy (everything included) so the top-level key can go away.\n const { domains, ...rest } = config;\n const sources = config.sources.map((s) =>\n s.domains ? s : { ...s, domains: (domains ?? []).map((name) => ({ name })) }\n );\n // `update` was merged into `sync`; rewrite hooks we installed earlier.\n await refreshGitHooks(repoRoot);\n return { ...rest, sources };\n },\n },\n];\n\n// ---------------------------------------------------------------------------\n// Semver helpers (minimal — no external dep needed)\n// ---------------------------------------------------------------------------\n\n/** Parse \"1.2.3\" or \"1.2.3-beta.1\" into [major, minor, patch]. */\nexport function parseSemver(version: string): [number, number, number] {\n const clean = version.replace(/^v/, '').split('-')[0];\n const parts = clean.split('.').map(Number);\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];\n}\n\n/** Returns -1 | 0 | 1 comparing a to b (ignores prerelease). */\nexport function compareSemver(a: string, b: string): number {\n const [aMaj, aMin, aPat] = parseSemver(a);\n const [bMaj, bMin, bPat] = parseSemver(b);\n\n if (aMaj !== bMaj) return aMaj < bMaj ? -1 : 1;\n if (aMin !== bMin) return aMin < bMin ? -1 : 1;\n if (aPat !== bPat) return aPat < bPat ? -1 : 1;\n return 0;\n}\n\n// ---------------------------------------------------------------------------\n// Migration runner\n// ---------------------------------------------------------------------------\n\n/**\n * Find migrations that should run when upgrading from `fromVersion` to\n * `toVersion`. Returns them sorted in ascending version order.\n */\nexport function pendingMigrations(\n fromVersion: string,\n toVersion: string\n): Migration[] {\n return migrations\n .filter(\n (m) =>\n compareSemver(m.version, fromVersion) > 0 &&\n compareSemver(m.version, toVersion) <= 0\n )\n .sort((a, b) => compareSemver(a.version, b.version));\n}\n\n/**\n * Run all pending migrations between the config's version and the\n * currently installed VERSION. Updates and saves the config afterwards.\n *\n * Returns null if no migration was needed.\n */\nexport async function runMigrations(\n repoRoot: string\n): Promise<MigrationResult | null> {\n let config = await loadConfig(repoRoot);\n const configVersion = config.version ?? '0.0.0';\n\n const cmp = compareSemver(configVersion, VERSION);\n\n // Already current\n if (cmp === 0) return null;\n\n // Config is newer than installed package (downgrade)\n if (cmp > 0) return null;\n\n // Config is older — find and run migrations\n const pending = pendingMigrations(configVersion, VERSION);\n const applied: string[] = [];\n\n for (const migration of pending) {\n config = await migration.migrate(repoRoot, config);\n applied.push(migration.version);\n }\n\n // Always update the version, even if no migrations ran\n // (e.g. patch bump with no structural changes)\n config = { ...config, version: VERSION };\n await saveConfig(repoRoot, config);\n\n return {\n fromVersion: configVersion,\n toVersion: VERSION,\n applied,\n };\n}\n","import { readdir, mkdir, rmdir, copyFile, readFile, writeFile } from 'node:fs/promises';\nimport { join, dirname, basename } from 'node:path';\nimport type { BridgeConfig } from './config.js';\nimport {\n dirExists,\n fileExists,\n copyDirContents,\n removeDir,\n removeFile,\n readManifest,\n writeManifest,\n addToManifest,\n removeFromManifest,\n isManifestFolder,\n manifestEntryName,\n MARKER_FILENAME,\n} from './fs.js';\nimport {\n type Feature,\n type RootFile,\n type ToolRootEntry,\n ROOT_FILES,\n featureMatchesTool,\n featureName,\n} from './manifest.js';\n\n// ---------------------------------------------------------------------------\n// Feature path helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the destination path for a feature inside a tool's folder.\n * For folder-based features: returns the folder path.\n * For file-based features: returns the file path.\n */\nexport function featureDestPath(\n repoRoot: string,\n toolFolder: string,\n featureType: string,\n featureName: string\n): string {\n return join(repoRoot, toolFolder, featureType, featureName);\n}\n\n// ---------------------------------------------------------------------------\n// Conflict detection\n// ---------------------------------------------------------------------------\n\n/**\n * Check whether a folder-based feature destination conflicts with existing user content.\n * Returns `true` when the folder exists and is not tracked in the manifest.\n */\nexport async function checkFolderConflict(featureTypeDir: string, folderName: string): Promise<boolean> {\n const destPath = join(featureTypeDir, folderName);\n if (!(await dirExists(destPath))) return false;\n \n const manifest = await readManifest(featureTypeDir);\n return !manifest.includes(folderName + '/');\n}\n\n/**\n * Check whether a file-based feature destination conflicts with existing user content.\n * Returns `true` when the file exists and is not tracked in the manifest.\n */\nexport async function checkFileConflict(featureTypeDir: string, fileName: string): Promise<boolean> {\n const filePath = join(featureTypeDir, fileName);\n if (!(await fileExists(filePath))) return false;\n \n const manifest = await readManifest(featureTypeDir);\n return !manifest.includes(fileName);\n}\n\n/**\n * Check whether a feature destination conflicts with existing user content.\n * Handles both folder-based and file-based features.\n */\nexport async function checkPathConflict(\n featureTypeDir: string,\n featureName: string,\n isFile: boolean\n): Promise<boolean> {\n if (isFile) {\n return checkFileConflict(featureTypeDir, featureName);\n } else {\n return checkFolderConflict(featureTypeDir, featureName);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Single feature sync\n// ---------------------------------------------------------------------------\n\n/**\n * Sync a folder-based feature: clear destination, copy files, add to manifest.\n */\nexport async function syncFolderFeature(\n sourcePath: string,\n featureTypeDir: string,\n folderName: string\n): Promise<'created' | 'updated'> {\n const destPath = join(featureTypeDir, folderName);\n const existed = await dirExists(destPath);\n\n if (existed) {\n await removeDir(destPath);\n }\n\n await mkdir(destPath, { recursive: true });\n await copyDirContents(sourcePath, destPath);\n await addToManifest(featureTypeDir, folderName + '/');\n\n return existed ? 'updated' : 'created';\n}\n\n/**\n * Sync a file-based feature: copy file, add to manifest.\n */\nexport async function syncFileFeature(\n sourcePath: string,\n featureTypeDir: string,\n fileName: string\n): Promise<'created' | 'updated'> {\n const destPath = join(featureTypeDir, fileName);\n const existed = await fileExists(destPath);\n\n await mkdir(featureTypeDir, { recursive: true });\n await copyFile(sourcePath, destPath);\n await addToManifest(featureTypeDir, fileName);\n\n return existed ? 'updated' : 'created';\n}\n\n/**\n * Sync a feature (folder or file based).\n * @deprecated Use syncFolderFeature or syncFileFeature directly\n */\nexport async function syncFeature(\n sourcePath: string,\n destPath: string\n): Promise<'created' | 'updated'> {\n const featureTypeDir = dirname(destPath);\n const folderName = basename(destPath);\n return syncFolderFeature(sourcePath, featureTypeDir, folderName);\n}\n\n// ---------------------------------------------------------------------------\n// Empty directory cleanup\n// ---------------------------------------------------------------------------\n\nexport async function removeEmptyParents(\n dirPath: string,\n stopAt: string\n): Promise<void> {\n let current = dirPath;\n while (current !== stopAt && current.startsWith(stopAt)) {\n try {\n const entries = await readdir(current);\n if (entries.length > 0) break;\n await rmdir(current);\n current = dirname(current);\n } catch {\n break;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Collect all managed entries from manifests\n// ---------------------------------------------------------------------------\n\ninterface ManagedEntry {\n /** Full path to the file or folder */\n path: string;\n /** Directory containing the manifest (feature-type dir) */\n manifestDir: string;\n /** Entry as it appears in manifest (with trailing / for folders) */\n manifestEntry: string;\n /** True if folder, false if file */\n isFolder: boolean;\n}\n\n/**\n * Recursively collect all managed entries (files and folders) from manifests.\n * Scans for .agentbridge files and reads their contents.\n */\nasync function collectManagedEntries(dir: string): Promise<ManagedEntry[]> {\n if (!(await dirExists(dir))) return [];\n\n const result: ManagedEntry[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n\n // Check if this directory has a manifest\n const manifestEntries = await readManifest(dir);\n for (const entry of manifestEntries) {\n const isFolder = isManifestFolder(entry);\n const name = manifestEntryName(entry);\n result.push({\n path: join(dir, name),\n manifestDir: dir,\n manifestEntry: entry,\n isFolder,\n });\n }\n\n // Recurse into subdirectories (to find manifests in nested feature-type dirs)\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (entry.name === MARKER_FILENAME) continue;\n \n const fullPath = join(dir, entry.name);\n const sub = await collectManagedEntries(fullPath);\n result.push(...sub);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Reconciliation (high-level)\n// ---------------------------------------------------------------------------\n\nexport interface ReconcileResult {\n added: number;\n updated: number;\n removed: number;\n errors: Array<{ path: string; error: string }>;\n}\n\ninterface ExpectedFeature {\n sourcePath: string;\n featureTypeDir: string;\n name: string;\n manifestEntry: string;\n isFile: boolean;\n}\n\nexport async function reconcileFeatures(\n repoRoot: string,\n config: BridgeConfig,\n features: Feature[]\n): Promise<ReconcileResult> {\n let added = 0;\n let updated = 0;\n let removed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n // Phase 1: Compute all expected features\n // Key = full destination path\n const expectedFeatures = new Map<string, ExpectedFeature>();\n\n for (const tool of config.tools) {\n for (const feature of features) {\n if (!featureMatchesTool(feature, tool.name)) continue;\n\n const name = featureName(feature);\n const featureTypeDir = join(repoRoot, tool.folder, feature.displayType);\n const destPath = join(featureTypeDir, name);\n const manifestEntry = feature.isFile ? name : name + '/';\n\n expectedFeatures.set(destPath, {\n sourcePath: feature.absolutePath,\n featureTypeDir,\n name,\n manifestEntry,\n isFile: feature.isFile,\n });\n }\n }\n\n // Phase 2: Remove orphaned managed entries (previously synced but no longer expected)\n for (const tool of config.tools) {\n const toolDir = join(repoRoot, tool.folder);\n const managedEntries = await collectManagedEntries(toolDir);\n\n for (const entry of managedEntries) {\n if (expectedFeatures.has(entry.path)) continue;\n\n try {\n if (entry.isFolder) {\n await removeDir(entry.path);\n } else {\n await removeFile(entry.path);\n }\n await removeFromManifest(entry.manifestDir, entry.manifestEntry);\n await removeEmptyParents(entry.manifestDir, toolDir);\n removed++;\n } catch (err) {\n errors.push({\n path: entry.path,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n // Phase 3: Create / update features (isolate failures so one bad feature\n // doesn't abort the entire sync).\n for (const [destPath, expected] of expectedFeatures) {\n try {\n const result = expected.isFile\n ? await syncFileFeature(\n expected.sourcePath,\n expected.featureTypeDir,\n expected.name\n )\n : await syncFolderFeature(\n expected.sourcePath,\n expected.featureTypeDir,\n expected.name\n );\n\n if (result === 'created') added++;\n else if (result === 'updated') updated++;\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { added, updated, removed, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Root file sync\n// ---------------------------------------------------------------------------\n\nconst ROOT_FILE_MARKER = '<!-- Managed by Agent Bridge -->';\n\n/**\n * Check if a root file at `destPath` is managed by Agent Bridge.\n * A file is managed if it starts with the marker comment.\n */\nexport async function isRootFileManaged(destPath: string): Promise<boolean> {\n if (!(await fileExists(destPath))) return false;\n const content = await readFile(destPath, 'utf-8');\n return content.startsWith(ROOT_FILE_MARKER);\n}\n\nexport interface RootFileSyncResult {\n synced: string[];\n removed: string[];\n errors: Array<{ path: string; error: string }>;\n}\n\n/**\n * Sync root files: copy source root files to the workspace root, and clean up\n * managed root files that are no longer provided by any source.\n */\nexport async function syncRootFiles(\n repoRoot: string,\n rootFiles: RootFile[]\n): Promise<RootFileSyncResult> {\n const synced: string[] = [];\n const removed: string[] = [];\n const errors: Array<{ path: string; error: string }> = [];\n\n // Build map: fileName → rootFile (already deduplicated by caller)\n const expected = new Map<string, RootFile>();\n for (const rf of rootFiles) {\n expected.set(rf.fileName, rf);\n }\n\n // Sync expected root files\n for (const [fileName, rf] of expected) {\n const destPath = join(repoRoot, fileName);\n try {\n // If file exists and is NOT managed by us, skip (don't overwrite user files)\n if (await fileExists(destPath)) {\n if (!(await isRootFileManaged(destPath))) {\n continue;\n }\n }\n\n const sourceContent = await readFile(rf.absolutePath, 'utf-8');\n const managedContent = ROOT_FILE_MARKER + '\\n' + sourceContent;\n await mkdir(dirname(destPath), { recursive: true });\n await writeFile(destPath, managedContent, 'utf-8');\n synced.push(fileName);\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n // Remove managed root files no longer provided by any source\n for (const fileName of ROOT_FILES) {\n if (expected.has(fileName)) continue;\n const destPath = join(repoRoot, fileName);\n try {\n if (await isRootFileManaged(destPath)) {\n await removeFile(destPath);\n removed.push(fileName);\n }\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { synced, removed, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Tool root entry sync (tool-prefixed flat files)\n// ---------------------------------------------------------------------------\n\nexport interface ToolRootSyncResult {\n added: number;\n updated: number;\n removed: number;\n errors: Array<{ path: string; error: string }>;\n}\n\n/**\n * Reconcile tool root entries: sync expected entries and remove orphans.\n * Tool-prefixed flat files (e.g. `cursor--settings.json`) are copied directly\n * into the tool's root folder (e.g. `.cursor/settings.json`).\n */\nexport async function reconcileToolRootEntries(\n repoRoot: string,\n config: BridgeConfig,\n entries: ToolRootEntry[]\n): Promise<ToolRootSyncResult> {\n let added = 0;\n let updated = 0;\n let removed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n // Build tool name → folder mapping\n const toolFolders = new Map<string, string>();\n for (const tool of config.tools) {\n toolFolders.set(tool.name, tool.folder);\n }\n\n // Key = destination path, value = entry info\n const expectedEntries = new Map<\n string,\n { sourcePath: string; name: string; toolDir: string }\n >();\n\n for (const entry of entries) {\n const folder = toolFolders.get(entry.toolName);\n if (!folder) continue;\n\n const toolDir = join(repoRoot, folder);\n const destPath = join(toolDir, entry.name);\n\n expectedEntries.set(destPath, {\n sourcePath: entry.absolutePath,\n name: entry.name,\n toolDir,\n });\n }\n\n // Remove orphaned managed entries from tool root directories\n for (const tool of config.tools) {\n const toolDir = join(repoRoot, tool.folder);\n const manifest = await readManifest(toolDir);\n\n for (const manifestEntry of manifest) {\n const isFolder = isManifestFolder(manifestEntry);\n const name = manifestEntryName(manifestEntry);\n const destPath = join(toolDir, name);\n\n if (expectedEntries.has(destPath)) continue;\n\n try {\n if (isFolder) {\n await removeDir(destPath);\n } else {\n await removeFile(destPath);\n }\n await removeFromManifest(toolDir, manifestEntry);\n removed++;\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n // Sync expected entries (always files)\n for (const [, expected] of expectedEntries) {\n try {\n const result = await syncFileFeature(\n expected.sourcePath,\n expected.toolDir,\n expected.name\n );\n\n if (result === 'created') added++;\n else if (result === 'updated') updated++;\n } catch (err) {\n errors.push({\n path: join(expected.toolDir, expected.name),\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { added, updated, removed, errors };\n}\n","import * as p from '@clack/prompts';\nimport { loadConfig, isOptedOut, OPT_OUT_MARKER } from '../lib/config.js';\nimport { findRepoRoot } from '../lib/git.js';\nimport { runMigrations } from '../lib/migrations/index.js';\nimport {\n discoverFeatureTypes,\n scanFeatures,\n detectDuplicates,\n scanRootFiles,\n detectRootFileDuplicates,\n scanToolRootEntries,\n detectToolRootDuplicates,\n featureMatchesTool,\n featureName,\n} from '../lib/manifest.js';\nimport {\n featureDestPath,\n checkPathConflict,\n reconcileFeatures,\n syncRootFiles,\n reconcileToolRootEntries,\n} from '../lib/sync.js';\nimport { syncAllSources, removeStaleSourceDirs } from '../lib/sources.js';\nimport { join } from 'node:path';\n\nexport async function syncCommand(cwd?: string, _opts?: unknown): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n p.intro('Agent Bridge Sync');\n\n // Respect an opt-out tombstone so a postinstall guard doesn't re-sync.\n if (await isOptedOut(repoRoot)) {\n p.log.warn(`${OPT_OUT_MARKER} present — Agent Bridge is opted out. Skipping sync.`);\n p.outro('Skipped (opted out).');\n return;\n }\n\n const s = p.spinner();\n\n // --- Phase 1: Load & validate config ---\n s.start('Loading configuration…');\n\n // Run pending migrations first so we work with the upgraded config\n const migrationResult = await runMigrations(repoRoot);\n if (migrationResult) {\n p.log.info(\n `Config upgraded ${migrationResult.fromVersion} → ${migrationResult.toVersion}` +\n (migrationResult.applied.length > 0\n ? ` (${migrationResult.applied.length} migration(s))`\n : '')\n );\n }\n\n const config = await loadConfig(repoRoot);\n s.stop('Configuration valid');\n\n // --- Phase 2: Fetch sources (clone new, pull existing) ---\n s.start('Fetching sources…');\n\n const sourceResults = await syncAllSources(repoRoot, config);\n const sourceErrors = sourceResults.filter((r) => r.error);\n if (sourceErrors.length > 0) {\n s.stop('Some sources failed');\n for (const err of sourceErrors) {\n p.log.error(`${err.name}: ${err.error}`);\n }\n process.exit(1);\n }\n\n // Clean up stale source directories\n const staleRemoved = await removeStaleSourceDirs(repoRoot, config);\n if (staleRemoved.length > 0) {\n for (const name of staleRemoved) {\n p.log.info(`Removed stale source: ${name}`);\n }\n }\n\n for (const r of sourceResults) {\n if (r.action !== 'local') {\n p.log.info(`${r.name}: ${r.action}`);\n }\n }\n\n s.stop('Sources up to date');\n\n // --- Phase 3: Discover & validate features ---\n s.start('Discovering features…');\n\n const featureTypes = await discoverFeatureTypes(repoRoot, config);\n const features = await scanFeatures(repoRoot, config, featureTypes);\n const rootFiles = await scanRootFiles(repoRoot, config);\n const toolRootEntries = await scanToolRootEntries(repoRoot, config);\n\n const duplicates = detectDuplicates(features);\n if (duplicates.length > 0) {\n s.stop('Duplicate features detected');\n for (const dup of duplicates) {\n p.log.error(\n `Duplicate \"${dup.name}\" (${dup.type}): ${dup.paths.join(', ')}`\n );\n }\n process.exit(1);\n }\n\n const rootDuplicates = detectRootFileDuplicates(rootFiles);\n if (rootDuplicates.length > 0) {\n s.stop('Duplicate root files detected');\n for (const dup of rootDuplicates) {\n p.log.error(\n `Duplicate \"${dup.fileName}\": ${dup.paths.join(', ')}`\n );\n }\n process.exit(1);\n }\n\n const toolRootDuplicates = detectToolRootDuplicates(toolRootEntries);\n if (toolRootDuplicates.length > 0) {\n s.stop('Duplicate tool root entries detected');\n for (const dup of toolRootDuplicates) {\n p.log.error(\n `Duplicate \"${dup.name}\" for tool \"${dup.toolName}\": ${dup.paths.join(', ')}`\n );\n }\n process.exit(1);\n }\n\n s.stop(`${features.length} features found${rootFiles.length > 0 ? `, ${rootFiles.length} root file(s)` : ''}${toolRootEntries.length > 0 ? `, ${toolRootEntries.length} tool root entr${toolRootEntries.length === 1 ? 'y' : 'ies'}` : ''}`);\n\n // --- Phase 3b: Detect path conflicts ---\n s.start('Checking for path conflicts…');\n\n const conflicts: string[] = [];\n for (const tool of config.tools) {\n for (const feature of features) {\n if (!featureMatchesTool(feature, tool.name)) continue;\n\n const linkName = featureName(feature);\n const featureTypeDir = join(repoRoot, tool.folder, feature.displayType);\n const dest = featureDestPath(\n repoRoot,\n tool.folder,\n feature.displayType,\n linkName\n );\n if (await checkPathConflict(featureTypeDir, linkName, feature.isFile)) {\n conflicts.push(dest);\n }\n }\n }\n\n if (conflicts.length > 0) {\n s.stop('Path conflicts detected');\n for (const c of conflicts) {\n p.log.error(`Conflict: \"${c}\" exists as a real file or directory`);\n }\n p.log.info('Remove or rename the conflicting paths, then re-run sync.');\n process.exit(1);\n }\n\n s.stop('No path conflicts');\n\n // --- Phase 4: Reconcile features ---\n s.start('Reconciling features…');\n\n const result = await reconcileFeatures(repoRoot, config, features);\n\n s.stop('Features reconciled');\n\n p.log.info(\n `Added: ${result.added} Updated: ${result.updated} Removed: ${result.removed}`\n );\n\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n p.outro(`Sync completed with ${result.errors.length} error(s).`);\n process.exit(1);\n }\n\n // --- Phase 5: Sync root files ---\n if (rootFiles.length > 0) {\n s.start('Syncing root files…');\n\n const rootResult = await syncRootFiles(repoRoot, rootFiles);\n\n for (const name of rootResult.synced) {\n p.log.info(`Root file synced: ${name}`);\n }\n for (const name of rootResult.removed) {\n p.log.info(`Root file removed: ${name}`);\n }\n for (const err of rootResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n\n s.stop('Root files synced');\n } else {\n // Clean up any managed root files when no sources provide them\n const rootResult = await syncRootFiles(repoRoot, []);\n for (const name of rootResult.removed) {\n p.log.info(`Root file removed: ${name}`);\n }\n }\n\n // --- Phase 6: Sync tool root entries ---\n s.start('Syncing tool root entries…');\n\n const toolRootResult = await reconcileToolRootEntries(\n repoRoot,\n config,\n toolRootEntries\n );\n\n if (\n toolRootResult.added > 0 ||\n toolRootResult.updated > 0 ||\n toolRootResult.removed > 0\n ) {\n p.log.info(\n `Tool root: Added: ${toolRootResult.added} Updated: ${toolRootResult.updated} Removed: ${toolRootResult.removed}`\n );\n }\n\n if (toolRootResult.errors.length > 0) {\n for (const err of toolRootResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n s.stop('Tool root entries synced with errors');\n p.outro(`Sync completed with ${toolRootResult.errors.length} error(s).`);\n process.exit(1);\n }\n\n s.stop('Tool root entries synced');\n\n p.outro('Sync complete.');\n}\n","import * as p from '@clack/prompts';\nimport { resolve } from 'node:path';\nimport {\n configExists,\n isRemoteSource,\n loadConfig,\n saveConfig,\n isOptedOut,\n removeOptOutMarker,\n OPT_OUT_MARKER,\n type BridgeConfig,\n type DomainConfig,\n type ToolConfig,\n type SourceConfig,\n} from '../lib/config.js';\nimport { findRepoRoot, isInGitRepo, installGitHooks } from '../lib/git.js';\nimport { listDomains, listDomainContents } from '../lib/manifest.js';\nimport { syncSource, resolveSourcePath, ensureBridgeGitignore } from '../lib/sources.js';\nimport { VERSION } from '../lib/version.js';\nimport { syncCommand } from './sync.js';\n\nconst WELL_KNOWN_TOOLS = [\n { value: { name: 'vscode', folder: '.github' }, label: 'VS Code (.github/)' },\n { value: { name: 'cursor', folder: '.cursor' }, label: 'Cursor (.cursor/)' },\n { value: { name: 'claude', folder: '.claude' }, label: 'Claude (.claude/)' },\n { value: { name: 'pi', folder: '.pi' }, label: 'Pi (.pi/)' },\n];\n\nconst WELL_KNOWN_TOOL_MAP: Record<string, ToolConfig> = Object.fromEntries(\n WELL_KNOWN_TOOLS.map((t) => [t.value.name, t.value])\n);\n\nconst CUSTOM_TOOL_SENTINEL: ToolConfig = { name: '__custom__', folder: '__custom__' };\n\nexport interface InitOptions {\n force?: boolean;\n domains?: string;\n tools?: string;\n source?: string[];\n hooks?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Argument parsing (shared by interactive and non-interactive mode)\n// ---------------------------------------------------------------------------\n\n/**\n * Derive a short source name from a URL or local path.\n *\n * Examples:\n * https://github.com/org/repo.git → repo\n * git@github.com:org/repo.git → repo\n * file:///tmp/bare.git → bare\n * /path/to/my-folder → my-folder\n */\nexport function deriveSourceName(source: string): string {\n let segment = source;\n\n // SSH: git@host:org/repo.git → org/repo.git\n const sshMatch = segment.match(/^[\\w.-]+@[\\w.-]+:(.+)$/);\n if (sshMatch) segment = sshMatch[1];\n\n // Strip protocol + host for URLs\n try {\n const url = new URL(segment);\n segment = url.pathname;\n } catch {\n // not a URL — keep as-is (local path or already stripped)\n }\n\n // Take the last path component, strip trailing slashes and .git suffix\n const base = segment.replace(/\\/+$/, '').split('/').pop() ?? segment;\n return base.replace(/\\.git$/, '') || 'source';\n}\n\n/**\n * Parse a comma-separated `--tools` argument into ToolConfig[].\n * Accepts well-known names (cursor, vscode, claude) or `name:folder` pairs.\n */\nexport function parseToolsArg(input: string): ToolConfig[] {\n return input.split(',').map((t) => {\n const trimmed = t.trim();\n if (!trimmed) throw new Error('Empty tool name in --tools');\n\n if (WELL_KNOWN_TOOL_MAP[trimmed]) return WELL_KNOWN_TOOL_MAP[trimmed];\n\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx > 0) {\n return { name: trimmed.slice(0, colonIdx), folder: trimmed.slice(colonIdx + 1) };\n }\n\n throw new Error(\n `Unknown tool \"${trimmed}\". Use a known name (${Object.keys(WELL_KNOWN_TOOL_MAP).join(', ')}) or name:folder format.`\n );\n });\n}\n\n/**\n * Parse a single `--source` argument into a SourceConfig.\n * Supports `#branch` suffix for remote sources.\n */\nexport function parseSourceArg(input: string, repoRoot: string): SourceConfig {\n let source = input.trim();\n let branch: string | undefined;\n\n const hashIdx = source.lastIndexOf('#');\n if (hashIdx > 0) {\n branch = source.slice(hashIdx + 1);\n source = source.slice(0, hashIdx);\n }\n\n if (!source) throw new Error('Empty source in --source');\n\n const name = deriveSourceName(source);\n const entry: SourceConfig = { name, source };\n\n if (!isRemoteSource(entry.source)) {\n entry.source = resolve(repoRoot, entry.source);\n }\n\n if (branch) {\n entry.branch = branch;\n }\n\n return entry;\n}\n\n/**\n * Turn a per-domain selection into the `include` list stored in config.\n * Returns `undefined` when everything is selected (= sync the whole domain).\n * A fully selected feature type collapses to its name (`skills`).\n */\nexport function buildInclude(\n contents: { featureTypes: Array<{ name: string; features: string[] }>; files: string[] },\n selected: Set<string>\n): string[] | undefined {\n const include: string[] = [];\n let everything = true;\n\n for (const ft of contents.featureTypes) {\n const picked = ft.features.filter((f) => selected.has(`${ft.name}/${f}`));\n if (picked.length === ft.features.length) {\n include.push(ft.name);\n } else {\n everything = false;\n include.push(...picked.map((f) => `${ft.name}/${f}`));\n }\n }\n for (const file of contents.files) {\n if (selected.has(file)) include.push(file);\n else everything = false;\n }\n\n return everything ? undefined : include;\n}\n\n// ---------------------------------------------------------------------------\n// Shared steps\n// ---------------------------------------------------------------------------\n\nfunction cancelled(value: unknown): value is symbol {\n if (p.isCancel(value)) {\n p.cancel('Setup cancelled.');\n process.exit(1);\n }\n return false;\n}\n\n/** Clone remote sources / verify local ones. Exits on failure. */\nasync function fetchSources(repoRoot: string, sources: SourceConfig[]): Promise<void> {\n await ensureBridgeGitignore(repoRoot);\n const s = p.spinner();\n s.start('Fetching sources…');\n const results = await Promise.all(sources.map((src) => syncSource(repoRoot, src)));\n const errors = results.filter((r) => r.error);\n if (errors.length > 0) {\n s.stop('Some sources failed');\n for (const err of errors) p.log.error(`${err.name}: ${err.error}`);\n p.cancel('Fix the source URL/path and run `agent-bridge init` again.');\n process.exit(1);\n }\n s.stop(`${sources.length} source(s) ready`);\n}\n\nasync function maybeInstallHooks(repoRoot: string, force: boolean): Promise<void> {\n const hookResult = await installGitHooks(repoRoot, force);\n if (hookResult.installed.length > 0) {\n p.log.success(`Installed git hooks: ${hookResult.installed.join(', ')}`);\n }\n if (hookResult.skipped.length > 0) {\n p.log.warn(`Skipped hooks (existing non-Agent-Bridge hooks): ${hookResult.skipped.join(', ')}`);\n p.log.info('Re-run `agent-bridge init --force` to overwrite, or integrate manually.');\n }\n for (const e of hookResult.errors) {\n p.log.error(`Hook ${e.hook}: ${e.error}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Interactive prompts\n// ---------------------------------------------------------------------------\n\nasync function promptTools(): Promise<ToolConfig[]> {\n const selected = await p.multiselect({\n message: 'Which tools should receive synced files?',\n options: [...WELL_KNOWN_TOOLS, { value: CUSTOM_TOOL_SENTINEL, label: 'Other (add custom tool)' }],\n required: true,\n });\n cancelled(selected);\n\n const tools = (selected as ToolConfig[]).filter((t) => t.name !== CUSTOM_TOOL_SENTINEL.name);\n if (!(selected as ToolConfig[]).some((t) => t.name === CUSTOM_TOOL_SENTINEL.name)) return tools;\n\n for (;;) {\n const name = await p.text({\n message: 'Custom tool name (used for <tool>-- prefix matching)',\n placeholder: 'windsurf',\n validate: (v) => {\n if (!v.trim()) return 'Tool name cannot be empty';\n if (tools.some((t) => t.name === v.trim())) return 'Tool name already used';\n },\n });\n if (p.isCancel(name)) break;\n\n const folder = await p.text({\n message: `Target folder for \"${name}\"`,\n placeholder: `.${name}`,\n validate: (v) => {\n if (!v.trim()) return 'Folder cannot be empty';\n if (tools.some((t) => t.folder === v.trim())) return 'Folder already used by another tool';\n },\n });\n if (p.isCancel(folder)) break;\n\n tools.push({ name: name.trim(), folder: folder.trim() });\n\n const more = await p.confirm({ message: 'Add another custom tool?', initialValue: false });\n if (p.isCancel(more) || !more) break;\n }\n\n if (tools.length === 0) {\n p.cancel('At least one tool is required.');\n process.exit(1);\n }\n return tools;\n}\n\nasync function promptSources(repoRoot: string): Promise<SourceConfig[]> {\n const sources: SourceConfig[] = [];\n p.log.info('Add at least one source — a Git URL or a local folder that follows the domain layout.');\n\n for (;;) {\n const input = await p.text({\n message: sources.length === 0 ? 'Source URL or local path' : 'Another source URL or local path',\n placeholder: 'https://github.com/org/ai-hub.git',\n validate: (v) => {\n if (!v.trim()) return 'Source URL/path cannot be empty';\n const derived = deriveSourceName(v.trim());\n if (sources.some((s) => s.name === derived))\n return `Source name \"${derived}\" (derived from URL) already used`;\n },\n });\n if (p.isCancel(input)) {\n if (sources.length === 0) cancelled(input);\n break;\n }\n\n const entry = parseSourceArg(input, repoRoot);\n if (isRemoteSource(entry.source) && !entry.branch) {\n const branch = await p.text({\n message: 'Branch (leave empty for the remote default)',\n placeholder: 'main',\n defaultValue: '',\n });\n cancelled(branch);\n if ((branch as string).trim()) entry.branch = (branch as string).trim();\n }\n sources.push(entry);\n\n const more = await p.confirm({ message: 'Add another source?', initialValue: false });\n if (p.isCancel(more) || !more) break;\n }\n return sources;\n}\n\n/**\n * Show every domain found in every source as one grouped checklist\n * (group = source). Returns the picked domain names per source.\n */\nasync function promptDomains(\n repoRoot: string,\n sources: SourceConfig[]\n): Promise<Map<string, string[]>> {\n const options: Record<string, Array<{ value: string; label: string; hint?: string }>> = {};\n for (const source of sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n const domains = await listDomains(srcPath);\n if (domains.length === 0) {\n p.log.warn(`${source.name}: no domain folders found — nothing to select.`);\n continue;\n }\n options[source.name] = [];\n for (const domain of domains) {\n const contents = await listDomainContents(srcPath, domain, []);\n const hint = contents.featureTypes\n .filter((ft) => ft.features.length > 0)\n .map((ft) => `${ft.features.length} ${ft.name}`)\n .join(', ');\n options[source.name].push({ value: `${source.name}/${domain}`, label: domain, hint: hint || undefined });\n }\n }\n\n if (Object.keys(options).length === 0) {\n p.cancel('No domains found in any source. Check the source layout: <source>/<domain>/<feature-type>/…');\n process.exit(1);\n }\n\n const picked = await p.groupMultiselect({\n message: 'Which domains do you want to sync? (space = toggle, pick a source to toggle all its domains)',\n options,\n required: true,\n });\n cancelled(picked);\n\n const bySource = new Map<string, string[]>();\n for (const value of picked as string[]) {\n const idx = value.indexOf('/');\n const sourceName = value.slice(0, idx);\n const domain = value.slice(idx + 1);\n bySource.set(sourceName, [...(bySource.get(sourceName) ?? []), domain]);\n }\n return bySource;\n}\n\n/** Let the user deselect individual features / files inside one domain. */\nasync function promptInclude(\n srcPath: string,\n sourceName: string,\n domain: string,\n toolNames: string[]\n): Promise<string[] | undefined> {\n const contents = await listDomainContents(srcPath, domain, toolNames);\n const options: Record<string, Array<{ value: string; label: string }>> = {};\n for (const ft of contents.featureTypes) {\n if (ft.features.length === 0) continue;\n options[ft.name] = ft.features.map((f) => ({ value: `${ft.name}/${f}`, label: f }));\n }\n if (contents.files.length > 0) {\n options['files'] = contents.files.map((f) => ({ value: f, label: f }));\n }\n if (Object.keys(options).length === 0) return undefined;\n\n const all = Object.values(options).flatMap((o) => o.map((x) => x.value));\n const picked = await p.groupMultiselect({\n message: `${sourceName}/${domain}: deselect what you don't want`,\n options,\n initialValues: all,\n required: true,\n });\n cancelled(picked);\n\n return buildInclude(contents, new Set(picked as string[]));\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function initCommand(cwd?: string, opts?: InitOptions): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n // Respect an opt-out tombstone so a postinstall guard doesn't reinstall.\n // `--force` clears it (deliberate re-opt-in).\n if (await isOptedOut(repoRoot)) {\n if (opts?.force) {\n await removeOptOutMarker(repoRoot);\n } else {\n p.log.warn(\n `${OPT_OUT_MARKER} present — Agent Bridge is opted out. ` +\n `Skipping init. Delete the file or run with --force to re-enable.`\n );\n return;\n }\n }\n\n const hasToolsArg = !!opts?.tools;\n const hasSourceArg = !!(opts?.source && opts.source.length > 0);\n\n if (hasToolsArg !== hasSourceArg) {\n p.log.error('Both --tools and --source are required for non-interactive init.');\n process.exit(1);\n }\n\n // --- Non-interactive mode ---\n if (hasToolsArg && hasSourceArg) {\n const tools = parseToolsArg(opts!.tools!);\n const sources = opts!.source!.map((s) => parseSourceArg(s, repoRoot));\n\n const seen = new Set<string>();\n for (const s of sources) {\n if (seen.has(s.name)) {\n throw new Error(`Duplicate source name \"${s.name}\" derived from --source arguments`);\n }\n seen.add(s.name);\n }\n\n await fetchSources(repoRoot, sources);\n\n const domainsArg = opts!.domains\n ? opts!.domains.split(',').map((d) => d.trim()).filter(Boolean)\n : undefined;\n for (const source of sources) {\n const names = domainsArg ?? (await listDomains(resolveSourcePath(repoRoot, source)));\n source.domains = names.map((name): DomainConfig => ({ name }));\n if (source.domains.length === 0) {\n p.log.warn(`${source.name}: no domains found — add some or pass --domains.`);\n }\n }\n\n const config: BridgeConfig = { version: VERSION, tools, sources };\n await saveConfig(repoRoot, config);\n p.log.success('Saved .agent-bridge/config.yml');\n\n if (opts!.hooks && isInGitRepo(repoRoot)) {\n await maybeInstallHooks(repoRoot, opts!.force === true);\n }\n\n p.outro('Done! Run `agent-bridge sync` to sync features.');\n return;\n }\n\n // --- Interactive mode ---\n p.intro('Agent Bridge — Project Setup');\n\n if (await configExists(repoRoot)) {\n const existing = await loadConfig(repoRoot);\n p.log.info(\n `Config already exists with ${existing.sources.length} source(s). Finishing this setup will overwrite it.`\n );\n }\n\n // 1. Tools\n const tools = await promptTools();\n\n // 2. Sources (then fetch them so we can show what's inside)\n const sources = await promptSources(repoRoot);\n await fetchSources(repoRoot, sources);\n\n // 3. Domains, grouped by source\n const pickedDomains = await promptDomains(repoRoot, sources);\n\n // 4. Optional fine-tuning inside each domain\n const everything = await p.confirm({\n message: 'Sync everything inside the selected domains? (No = pick individual skills, agents, files…)',\n initialValue: true,\n });\n cancelled(everything);\n\n const toolNames = tools.map((t) => t.name);\n for (const source of sources) {\n const domains = pickedDomains.get(source.name) ?? [];\n source.domains = [];\n for (const domain of domains) {\n const include = everything\n ? undefined\n : await promptInclude(resolveSourcePath(repoRoot, source), source.name, domain, toolNames);\n source.domains.push(include ? { name: domain, include } : { name: domain });\n }\n }\n // Sources without any picked domain contribute nothing — drop them.\n const activeSources = sources.filter((s) => (s.domains?.length ?? 0) > 0);\n for (const s of sources) {\n if (!activeSources.includes(s)) p.log.warn(`${s.name}: no domains selected — source dropped from config.`);\n }\n\n const config: BridgeConfig = { version: VERSION, tools, sources: activeSources };\n await saveConfig(repoRoot, config);\n p.log.success('Saved .agent-bridge/config.yml — commit this file.');\n\n // 5. Git hooks\n if (isInGitRepo(repoRoot)) {\n const installHooks = await p.confirm({\n message: 'Install git hooks to auto-sync after checkout/merge?',\n initialValue: false,\n });\n if (!p.isCancel(installHooks) && installHooks) {\n await maybeInstallHooks(repoRoot, opts?.force === true);\n }\n }\n\n // 6. Sync right away\n const syncNow = await p.confirm({ message: 'Run `agent-bridge sync` now?', initialValue: true });\n if (!p.isCancel(syncNow) && syncNow) {\n await syncCommand(repoRoot);\n return;\n }\n p.outro('Done! Run `agent-bridge sync` whenever you want to pull the latest features.');\n}\n","import * as p from '@clack/prompts';\nimport {\n bridgeDir,\n configExists,\n loadConfig,\n writeOptOutMarker,\n OPT_OUT_MARKER,\n type BridgeConfig,\n} from '../lib/config.js';\nimport { removeDir, dirExists } from '../lib/fs.js';\nimport { findRepoRoot, isInGitRepo, removeGitHooks } from '../lib/git.js';\nimport { reconcileFeatures, reconcileToolRootEntries } from '../lib/sync.js';\n\nfunction summarizeTools(config: BridgeConfig): string {\n return config.tools.map((t) => t.name).join(', ');\n}\n\nexport async function optOutCommand(\n cwd?: string,\n _opts?: unknown\n): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n p.intro('Agent Bridge Opt-out');\n\n const hasConfig = await configExists(repoRoot);\n let config: BridgeConfig | undefined;\n\n if (hasConfig) {\n config = await loadConfig(repoRoot);\n }\n\n const toolSummary = config ? summarizeTools(config) : 'unknown (no config found)';\n p.log.info(\n `Non-interactive opt-out: removing Agent Bridge managed files for tools: ${toolSummary}`\n );\n\n const s = p.spinner();\n\n let featureErrors = 0;\n let toolRootErrors = 0;\n\n if (config) {\n s.start('Removing synced Agent Bridge files…');\n\n const featureResult = await reconcileFeatures(repoRoot, config, []);\n const toolRootResult = await reconcileToolRootEntries(repoRoot, config, []);\n\n featureErrors = featureResult.errors.length;\n toolRootErrors = toolRootResult.errors.length;\n\n s.stop('Synced files removed');\n\n p.log.info(\n `Features removed: ${featureResult.removed} (errors: ${featureErrors})`\n );\n p.log.info(\n `Tool-root files removed: ${toolRootResult.removed} (errors: ${toolRootErrors})`\n );\n p.log.info('Root files are not removed by opt-out (manifest-only cleanup).');\n\n for (const err of featureResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n for (const err of toolRootResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n } else {\n p.log.warn('No .agent-bridge/config.yml found. Skipping synced file cleanup.');\n }\n\n s.start('Removing Agent Bridge git hooks…');\n const removedHooks = isInGitRepo(repoRoot) ? await removeGitHooks(repoRoot) : [];\n s.stop('Hooks cleanup complete');\n\n if (removedHooks.length > 0) {\n p.log.info(`Removed hooks: ${removedHooks.join(', ')}`);\n } else if (isInGitRepo(repoRoot)) {\n p.log.info('No Agent Bridge hooks found.');\n } else {\n p.log.info('Not a git repository; hook cleanup skipped.');\n }\n\n s.start('Removing .agent-bridge directory…');\n const bridgePath = bridgeDir(repoRoot);\n if (await dirExists(bridgePath)) {\n await removeDir(bridgePath);\n s.stop('.agent-bridge removed');\n } else {\n s.stop('.agent-bridge not found');\n }\n\n // Write a tombstone that survives `.agent-bridge/` deletion so a postinstall\n // guard (or a manual init/sync) won't silently reinstall on the next install.\n await writeOptOutMarker(repoRoot);\n p.log.info(\n `Wrote ${OPT_OUT_MARKER} (gitignored, local to this machine). ` +\n 'Force-add it (`git add -f`) for a repo-wide opt-out. ' +\n 'Run `agent-bridge init --force` to re-enable.'\n );\n\n const totalErrors = featureErrors + toolRootErrors;\n if (totalErrors > 0) {\n p.outro(`Opt-out completed with ${totalErrors} cleanup error(s).`);\n process.exit(1);\n }\n\n p.outro('Opt-out complete. Agent Bridge is removed from this repository.');\n}\n","#!/usr/bin/env node\n\nimport { resolve } from 'node:path';\nimport { stat } from 'node:fs/promises';\nimport { Command } from 'commander';\nimport { initCommand } from './commands/init.js';\nimport { syncCommand } from './commands/sync.js';\nimport { optOutCommand } from './commands/opt-out.js';\nimport { VERSION } from './lib/version.js';\n\nexport interface CliOptions {\n cwd?: string;\n force?: boolean;\n // Init-specific options (ignored by other commands)\n domains?: string;\n tools?: string;\n source?: string[];\n hooks?: boolean;\n}\n\nasync function assertCwdExists(cwd: string): Promise<void> {\n try {\n const s = await stat(cwd);\n if (!s.isDirectory()) {\n throw new Error(`--cwd path is not a directory: ${cwd}`);\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(`--cwd path does not exist: ${cwd}`);\n }\n throw err;\n }\n}\n\nasync function withCwdValidation(\n action: (cwd?: string, opts?: CliOptions) => Promise<void>\n): Promise<(opts: CliOptions) => Promise<void>> {\n return async (opts: CliOptions) => {\n if (opts.cwd) {\n opts.cwd = resolve(opts.cwd);\n await assertCwdExists(opts.cwd);\n }\n await action(opts.cwd, opts);\n };\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n previous.push(value);\n return previous;\n}\n\nconst program = new Command()\n .name('agent-bridge')\n .description('Manage AI tool configurations from multiple sources')\n .version(VERSION, '-v, --version');\n\nprogram\n .command('init')\n .description('Set up Agent Bridge: pick tools, sources and domains (creates .agent-bridge/config.yml)')\n .option('--cwd <path>', 'Override the working directory')\n .option('--force', 'Overwrite existing non-Agent-Bridge git hooks')\n .option('--domains <list>', 'Comma-separated domain list (default: every domain found in each source)')\n .option('--tools <list>', 'Comma-separated tool names (cursor,vscode,claude) or name:folder pairs')\n .option('-s, --source <url>', 'Source URL or path (repeatable, append #branch for branch)', collect, [])\n .option('--hooks', 'Auto-install git hooks without prompting')\n .action(await withCwdValidation(initCommand));\n\nprogram\n .command('sync')\n .description('Fetch the latest sources and sync features into your tool folders')\n .option('--cwd <path>', 'Override the working directory')\n .action(await withCwdValidation(syncCommand));\n\n// `update` was merged into `sync` (0.14.0). Kept hidden so hooks installed by\n// older versions (`agent-bridge update && agent-bridge sync`) keep working.\nprogram\n .command('update', { hidden: true })\n .option('--cwd <path>', 'Override the working directory')\n .action(\n await withCwdValidation(async (cwd) => {\n console.error('`agent-bridge update` is deprecated — running `agent-bridge sync` instead (it fetches sources too).');\n await syncCommand(cwd);\n })\n );\n\nprogram\n .command('opt-out')\n .description('Remove Agent Bridge hooks, synced files, and .agent-bridge state')\n .option('--cwd <path>', 'Override the working directory')\n .action(await withCwdValidation(optOutCommand));\n\nprogram.parse();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["pkg.version"],"sources":["../src/lib/config.ts","../src/lib/git.ts","../src/lib/fs.ts","../src/lib/sources.ts","../src/lib/manifest.ts","../src/lib/tree.ts","../src/lib/tree-prompt.ts","../package.json","../src/lib/version.ts","../src/lib/migrations/index.ts","../src/lib/sync.ts","../src/commands/sync.ts","../src/commands/init.ts","../src/commands/opt-out.ts","../src/index.ts"],"sourcesContent":["import { readFile, writeFile, access, mkdir, rm } from 'node:fs/promises';\nimport { join, isAbsolute } from 'node:path';\nimport yaml from 'js-yaml';\nimport { z } from 'zod';\n\n// ---------------------------------------------------------------------------\n// Zod Schemas\n// ---------------------------------------------------------------------------\n\nconst SAFE_NAME_RE = /^[A-Za-z0-9._-]+$/;\n\nconst safeName = z\n .string()\n .min(1)\n .refine((v) => SAFE_NAME_RE.test(v) && v !== '.' && v !== '..', {\n message: 'Only [A-Za-z0-9._-] characters allowed, cannot be . or ..',\n });\n\nconst safeRelativeFolder = z\n .string()\n .min(1)\n .refine(\n (value) => {\n if (isAbsolute(value) || value.includes('\\0')) return false;\n const segments = value.split(/[\\\\/]/).filter((s) => s.length > 0);\n if (segments.length === 0) return false;\n return segments.every(\n (seg) => seg !== '..' && seg !== '.' && /^\\.?[A-Za-z0-9._-]+$/.test(seg)\n );\n },\n { message: 'Must be a relative path using only [A-Za-z0-9._-]' }\n );\n\nconst toolConfigSchema = z.object({\n name: safeName.refine((v) => !v.includes('--'), {\n message: \"Must not contain '--' (reserved for tool-prefix routing)\",\n }),\n folder: safeRelativeFolder,\n});\n\nconst sourceConfigSchema = z.object({\n name: safeName,\n source: z.string().min(1).refine((v) => !v.startsWith('-'), {\n message: \"Must not start with '-'\",\n }),\n branch: z\n .string()\n .refine((v) => /^[A-Za-z0-9._/-]+$/.test(v) && !v.startsWith('-'), {\n message: \"Must match [A-Za-z0-9._/-] and not start with '-'\",\n })\n .optional(),\n});\n\n/**\n * A path inside a domain that should be synced. One or two segments:\n * `skills` → the whole feature type\n * `skills/deploy` → a single feature (folder or file)\n * `AGENTS.md` → a flat file at the domain root\n */\nconst includePath = z\n .string()\n .min(1)\n .refine(\n (v) => {\n const segs = v.split('/');\n return (\n segs.length <= 2 &&\n segs.every((seg) => SAFE_NAME_RE.test(seg) && seg !== '.' && seg !== '..')\n );\n },\n { message: 'Must be <feature-type>, <feature-type>/<feature> or <file> using [A-Za-z0-9._-]' }\n );\n\nconst domainObjectSchema = z.object({\n name: safeName,\n /** Paths to sync from this domain. Omitted = everything. */\n include: z.array(includePath).optional(),\n});\n\n/** Domains are written as objects; a bare string (`- shared`) is accepted as shorthand. */\nconst domainConfigSchema = z.union([\n safeName.transform((name): { name: string; include?: string[] } => ({ name })),\n domainObjectSchema,\n]);\n\nconst bridgeConfigSchema = z\n .object({\n version: z.string().optional(),\n /**\n * Legacy (< 0.14): domains applied to every source. Still honored as the\n * fallback for sources without their own `domains`.\n */\n domains: z.array(safeName).optional(),\n tools: z.array(toolConfigSchema).min(1, \"'tools' must be a non-empty array\"),\n sources: z\n .array(sourceConfigSchema.extend({ domains: z.array(domainConfigSchema).optional() }))\n .min(1, \"'sources' must be a non-empty array\"),\n })\n .superRefine((data, ctx) => {\n data.sources.forEach((s, i) => {\n const domains = s.domains ?? data.domains;\n if (!domains || domains.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Source '${s.name}' has no domains (set 'sources[].domains' or top-level 'domains')`,\n path: ['sources', i, 'domains'],\n });\n }\n const seen = new Set<string>();\n for (const d of s.domains ?? []) {\n if (seen.has(d.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate domain '${d.name}' in source '${s.name}'`,\n path: ['sources', i, 'domains'],\n });\n }\n seen.add(d.name);\n }\n });\n\n // Check unique tool names\n const toolNames = new Set<string>();\n const toolFolders = new Set<string>();\n data.tools.forEach((t, i) => {\n if (toolNames.has(t.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate tool name: '${t.name}'`,\n path: ['tools', i, 'name'],\n });\n }\n toolNames.add(t.name);\n if (toolFolders.has(t.folder)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate tool folder: '${t.folder}'`,\n path: ['tools', i, 'folder'],\n });\n }\n toolFolders.add(t.folder);\n });\n\n // Check unique source names and branch validity\n const sourceNames = new Set<string>();\n data.sources.forEach((s, i) => {\n if (sourceNames.has(s.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate source name: '${s.name}'`,\n path: ['sources', i, 'name'],\n });\n }\n sourceNames.add(s.name);\n\n const isRemote =\n s.source.startsWith('https://') ||\n s.source.startsWith('http://') ||\n s.source.startsWith('file://') ||\n /^[\\w.-]+@[\\w.-]+:/.test(s.source);\n\n if (s.branch && !isRemote) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"'branch' is only valid for remote sources\",\n path: ['sources', i, 'branch'],\n });\n }\n if (!isRemote && !isAbsolute(s.source)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'Local source paths must be absolute',\n path: ['sources', i, 'source'],\n });\n }\n });\n });\n\n// ---------------------------------------------------------------------------\n// Types (inferred from Zod schemas)\n// ---------------------------------------------------------------------------\n\nexport type SourceType = 'git-https' | 'git-ssh' | 'local';\nexport type ToolConfig = z.infer<typeof toolConfigSchema>;\nexport type DomainConfig = z.infer<typeof domainObjectSchema>;\nexport type SourceConfig = z.infer<typeof sourceConfigSchema> & { domains?: DomainConfig[] };\nexport type BridgeConfig = z.infer<typeof bridgeConfigSchema>;\n\n// ---------------------------------------------------------------------------\n// Domain resolution & include filtering\n// ---------------------------------------------------------------------------\n\n/**\n * Domains to scan for a source: its own `domains`, falling back to the legacy\n * top-level `domains` list (everything included).\n */\nexport function sourceDomains(config: BridgeConfig, source: SourceConfig): DomainConfig[] {\n if (source.domains) return source.domains;\n return (config.domains ?? []).map((name) => ({ name }));\n}\n\n/**\n * Whether `relPath` (relative to the domain root, e.g. `skills`,\n * `skills/deploy`, `AGENTS.md`) is selected by the domain's `include` list.\n * No `include` means everything is selected.\n */\nexport function isIncluded(domain: DomainConfig, relPath: string): boolean {\n const inc = domain.include;\n if (!inc) return true;\n return inc.some((entry) => entry === relPath || relPath.startsWith(entry + '/') || entry.startsWith(relPath + '/'));\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const BRIDGE_DIR = '.agent-bridge';\nexport const CONFIG_FILENAME = 'config.yml';\n\n/**\n * Tombstone written by `opt-out`. It lives inside `.agent-bridge/` so it's\n * gitignored by default (the directory's `.gitignore` ignores everything but\n * `config.yml`), keeping opt-out local to a machine. `init`/`sync` honor it so\n * a `postinstall` guard doesn't silently reinstall Agent Bridge on the next\n * `npm install`. Force-add it (`git add -f`) to commit a repo-wide opt-out.\n */\nexport const OPT_OUT_MARKER = join(BRIDGE_DIR, 'optout');\n\n// ---------------------------------------------------------------------------\n// Source type detection\n// ---------------------------------------------------------------------------\n\nexport function detectSourceType(source: string): SourceType {\n if (\n source.startsWith('https://') ||\n source.startsWith('http://') ||\n source.startsWith('file://')\n ) {\n return 'git-https';\n }\n if (/^[\\w.-]+@[\\w.-]+:/.test(source)) {\n return 'git-ssh';\n }\n return 'local';\n}\n\nexport function isRemoteSource(source: string): boolean {\n const type = detectSourceType(source);\n return type === 'git-https' || type === 'git-ssh';\n}\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nexport function bridgeDir(repoRoot: string): string {\n return join(repoRoot, BRIDGE_DIR);\n}\n\nexport function configPath(repoRoot: string): string {\n return join(repoRoot, BRIDGE_DIR, CONFIG_FILENAME);\n}\n\nexport function sourceDir(repoRoot: string, sourceName: string): string {\n return join(repoRoot, BRIDGE_DIR, sourceName);\n}\n\nexport function optOutMarkerPath(repoRoot: string): string {\n return join(repoRoot, OPT_OUT_MARKER);\n}\n\n/** Whether an opt-out tombstone is present at the repo root. */\nexport async function isOptedOut(repoRoot: string): Promise<boolean> {\n try {\n await access(optOutMarkerPath(repoRoot));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Write the opt-out tombstone inside `.agent-bridge/`. Recreates the directory\n * (opt-out deletes it) and its `.gitignore` so the marker is ignored by default.\n */\nexport async function writeOptOutMarker(repoRoot: string): Promise<void> {\n const dir = bridgeDir(repoRoot);\n await mkdir(dir, { recursive: true });\n // Same ignore rules init/sync write: ignore everything but the config.\n await writeFile(\n join(dir, '.gitignore'),\n ['# Ignore cloned sources', '*', '!config.yml', '!.gitignore'].join('\\n') + '\\n',\n 'utf-8'\n );\n await writeFile(\n optOutMarkerPath(repoRoot),\n '# Agent Bridge opt-out marker. Remove this file (or run `agent-bridge init --force`) to re-enable.\\n',\n 'utf-8'\n );\n}\n\n/** Remove the opt-out tombstone if present (idempotent). */\nexport async function removeOptOutMarker(repoRoot: string): Promise<void> {\n await rm(optOutMarkerPath(repoRoot), { force: true });\n}\n\n// ---------------------------------------------------------------------------\n// Config I/O\n// ---------------------------------------------------------------------------\n\nexport async function configExists(repoRoot: string): Promise<boolean> {\n try {\n await access(configPath(repoRoot));\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function loadConfig(repoRoot: string): Promise<BridgeConfig> {\n const raw = await readFile(configPath(repoRoot), 'utf-8');\n const data = yaml.load(raw);\n\n const result = bridgeConfigSchema.safeParse(data);\n if (!result.success) {\n const errors = result.error.issues.map(\n (i) => `${i.path.join('.')}: ${i.message}`\n );\n throw new Error(`Invalid config: ${errors.join('; ')}`);\n }\n\n return result.data;\n}\n\nexport async function saveConfig(\n repoRoot: string,\n config: BridgeConfig\n): Promise<void> {\n const dir = bridgeDir(repoRoot);\n await mkdir(dir, { recursive: true });\n const content = yaml.dump(config, { lineWidth: -1, noRefs: true, skipInvalid: true });\n await writeFile(configPath(repoRoot), content, 'utf-8');\n}\n\n// ---------------------------------------------------------------------------\n// Validation (legacy interface for tests)\n// ---------------------------------------------------------------------------\n\nexport interface ConfigValidationResult {\n ok: boolean;\n errors: string[];\n}\n\nexport function validateConfig(config: unknown): ConfigValidationResult {\n const result = bridgeConfigSchema.safeParse(config);\n if (result.success) {\n return { ok: true, errors: [] };\n }\n const errors = result.error.issues.map(\n (i) => `${i.path.join('.')}: ${i.message}`\n );\n return { ok: false, errors };\n}\n","import { execSync } from 'node:child_process';\nimport { mkdir, writeFile, chmod, readFile, access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport function findRepoRoot(): string {\n try {\n return execSync('git rev-parse --show-toplevel', {\n encoding: 'utf-8',\n stdio: 'pipe',\n }).trim();\n } catch {\n return process.cwd();\n }\n}\n\n/**\n * Check if a directory is inside a Git repository.\n */\nexport function isInGitRepo(cwd?: string): boolean {\n try {\n execSync('git rev-parse --is-inside-work-tree', {\n encoding: 'utf-8',\n stdio: 'pipe',\n cwd,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get the path to the .git/hooks directory.\n */\nexport function getGitHooksDir(repoRoot: string): string {\n return join(repoRoot, '.git', 'hooks');\n}\n\n/**\n * The hook names that Agent Bridge will install.\n */\nexport const AGENT_BRIDGE_HOOKS = ['post-checkout', 'post-merge'] as const;\nexport type AgentBridgeHook = (typeof AGENT_BRIDGE_HOOKS)[number];\n\n/**\n * Marker comment to identify Agent Bridge hooks.\n */\nconst HOOK_MARKER = '# agent-bridge-hook';\n\n/**\n * Generate the hook script content.\n * Runs sync in the background, logging to `.agent-bridge/hook.log`\n * (trimmed to the last ~200 lines) so failures are diagnosable.\n */\nexport function generateHookScript(): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# This hook was installed by Agent Bridge.\n# It runs 'agent-bridge sync' in the background to keep your AI agent\n# configurations up to date.\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel 2>/dev/null)\"\nLOG_DIR=\"\\${REPO_ROOT:-.}/.agent-bridge\"\nLOG_FILE=\"\\${LOG_DIR}/hook.log\"\n\nmkdir -p \"\\$LOG_DIR\" 2>/dev/null\n\n(\n # Wait a moment for git to finish\n sleep 1\n\n {\n echo \"--- $(date '+%Y-%m-%dT%H:%M:%S%z') agent-bridge hook ---\"\n if command -v agent-bridge >/dev/null 2>&1; then\n agent-bridge sync\n elif command -v npx >/dev/null 2>&1; then\n npx @sofatutor/agent-bridge sync\n else\n echo \"agent-bridge not found (install globally or ensure npx is available)\"\n fi\n } >>\"\\$LOG_FILE\" 2>&1\n\n # Keep the log from growing without bound.\n if [ -f \"\\$LOG_FILE\" ]; then\n tail -n 200 \"\\$LOG_FILE\" >\"\\$LOG_FILE.tmp\" && mv \"\\$LOG_FILE.tmp\" \"\\$LOG_FILE\"\n fi\n) </dev/null >/dev/null 2>&1 &\n`;\n}\n\n/**\n * Check if a hook file contains the Agent Bridge marker.\n */\nexport async function hasAgentBridgeHook(hookPath: string): Promise<boolean> {\n try {\n const content = await readFile(hookPath, 'utf-8');\n return content.includes(HOOK_MARKER);\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a hook file exists.\n */\nasync function hookExists(hookPath: string): Promise<boolean> {\n try {\n await access(hookPath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport interface InstallHooksResult {\n installed: AgentBridgeHook[];\n skipped: AgentBridgeHook[];\n errors: Array<{ hook: AgentBridgeHook; error: string }>;\n}\n\n/**\n * Install Agent Bridge git hooks in the repository.\n * \n * @param repoRoot - The root of the git repository\n * @param force - If true, overwrite existing hooks that don't have the marker\n * @returns Result with installed, skipped, and errored hooks\n */\nexport async function installGitHooks(\n repoRoot: string,\n force = false\n): Promise<InstallHooksResult> {\n const result: InstallHooksResult = {\n installed: [],\n skipped: [],\n errors: [],\n };\n\n if (!isInGitRepo(repoRoot)) {\n for (const hook of AGENT_BRIDGE_HOOKS) {\n result.errors.push({ hook, error: 'Not a git repository' });\n }\n return result;\n }\n\n const hooksDir = getGitHooksDir(repoRoot);\n\n // Ensure hooks directory exists\n try {\n await mkdir(hooksDir, { recursive: true });\n } catch (err) {\n for (const hook of AGENT_BRIDGE_HOOKS) {\n result.errors.push({ hook, error: `Failed to create hooks directory: ${err}` });\n }\n return result;\n }\n\n const hookContent = generateHookScript();\n\n for (const hookName of AGENT_BRIDGE_HOOKS) {\n const hookPath = join(hooksDir, hookName);\n\n try {\n const exists = await hookExists(hookPath);\n \n if (exists) {\n const hasMarker = await hasAgentBridgeHook(hookPath);\n \n if (hasMarker) {\n // Already installed, update it\n await writeFile(hookPath, hookContent, 'utf-8');\n await chmod(hookPath, 0o755);\n result.installed.push(hookName);\n } else if (force) {\n // Force overwrite\n await writeFile(hookPath, hookContent, 'utf-8');\n await chmod(hookPath, 0o755);\n result.installed.push(hookName);\n } else {\n // Skip - existing hook without marker\n result.skipped.push(hookName);\n }\n } else {\n // Create new hook\n await writeFile(hookPath, hookContent, 'utf-8');\n await chmod(hookPath, 0o755);\n result.installed.push(hookName);\n }\n } catch (err) {\n result.errors.push({ hook: hookName, error: String(err) });\n }\n }\n\n return result;\n}\n\n/**\n * Rewrite hooks that Agent Bridge installed earlier with the current script.\n * Hooks we did not install (no marker) and missing hooks are left alone.\n */\nexport async function refreshGitHooks(repoRoot: string): Promise<AgentBridgeHook[]> {\n const refreshed: AgentBridgeHook[] = [];\n if (!isInGitRepo(repoRoot)) return refreshed;\n\n const hooksDir = getGitHooksDir(repoRoot);\n for (const hookName of AGENT_BRIDGE_HOOKS) {\n const hookPath = join(hooksDir, hookName);\n if (!(await hasAgentBridgeHook(hookPath))) continue;\n await writeFile(hookPath, generateHookScript(), 'utf-8');\n await chmod(hookPath, 0o755);\n refreshed.push(hookName);\n }\n return refreshed;\n}\n\n/**\n * Remove Agent Bridge git hooks from the repository.\n * Only removes hooks that have the Agent Bridge marker.\n */\nexport async function removeGitHooks(repoRoot: string): Promise<AgentBridgeHook[]> {\n const removed: AgentBridgeHook[] = [];\n\n if (!isInGitRepo(repoRoot)) {\n return removed;\n }\n\n const hooksDir = getGitHooksDir(repoRoot);\n\n for (const hookName of AGENT_BRIDGE_HOOKS) {\n const hookPath = join(hooksDir, hookName);\n \n try {\n if (await hasAgentBridgeHook(hookPath)) {\n const { unlink } = await import('node:fs/promises');\n await unlink(hookPath);\n removed.push(hookName);\n }\n } catch {\n // Ignore errors\n }\n }\n\n return removed;\n}\n","import { readdir, copyFile, stat } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport fsExtra from 'fs-extra';\n\nconst { pathExists, remove, outputFile, readFile: fsReadFile, ensureDir } = fsExtra;\n\n/** Name of the marker file placed inside every synced feature folder. */\nexport const MARKER_FILENAME = '.agentbridge';\n\nexport async function dirExists(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function fileExists(p: string): Promise<boolean> {\n return pathExists(p);\n}\n\nexport async function listFilesRecursive(dir: string): Promise<string[]> {\n const files: string[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n // Skip symlinks entirely: never follow them out of the source tree,\n // and don't attempt to replicate them (keeps the destination simple\n // and avoids symlink-escape vulnerabilities).\n if (entry.isSymbolicLink()) continue;\n // Skip Agent Bridge marker files that may exist in source repos.\n if (entry.name === MARKER_FILENAME) continue;\n if (entry.isDirectory()) {\n const subFiles = await listFilesRecursive(join(dir, entry.name));\n files.push(...subFiles.map((f) => join(entry.name, f)));\n } else if (entry.isFile()) {\n files.push(entry.name);\n }\n }\n return files;\n}\n\n/**\n * Copy all files from `srcDir` into `destDir`, preserving nested structure.\n * Overwrites existing files. Creates directories as needed.\n */\nexport async function copyDirContents(srcDir: string, destDir: string): Promise<void> {\n const files = await listFilesRecursive(srcDir);\n for (const relFile of files) {\n const srcFile = join(srcDir, relFile);\n const destFile = join(destDir, relFile);\n await ensureDir(dirname(destFile));\n await copyFile(srcFile, destFile);\n }\n}\n\n/**\n * Write the `.agentbridge` marker file into a feature folder.\n */\nexport async function writeMarker(featureDir: string): Promise<void> {\n await outputFile(join(featureDir, MARKER_FILENAME), '');\n}\n\n/**\n * Check whether a directory contains the `.agentbridge` marker.\n */\nexport async function hasMarker(featureDir: string): Promise<boolean> {\n return pathExists(join(featureDir, MARKER_FILENAME));\n}\n\n/**\n * Remove a directory and all its contents.\n */\nexport async function removeDir(dir: string): Promise<void> {\n await remove(dir);\n}\n\n/**\n * Remove a single file.\n */\nexport async function removeFile(filePath: string): Promise<void> {\n await remove(filePath);\n}\n\n// ---------------------------------------------------------------------------\n// Manifest helpers (single .agentbridge per feature-type directory)\n// ---------------------------------------------------------------------------\n\n/**\n * Read the manifest file in a directory. Returns list of managed entries.\n * Entries ending with '/' are folders, others are files.\n */\nexport async function readManifest(dir: string): Promise<string[]> {\n const manifestPath = join(dir, MARKER_FILENAME);\n try {\n const content = await fsReadFile(manifestPath, 'utf-8');\n return content.split('\\n').filter((line) => line.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Check if an entry in the manifest is a folder (ends with /).\n */\nexport function isManifestFolder(entry: string): boolean {\n return entry.endsWith('/');\n}\n\n/**\n * Get the base name from a manifest entry (strips trailing / for folders).\n */\nexport function manifestEntryName(entry: string): string {\n return entry.endsWith('/') ? entry.slice(0, -1) : entry;\n}\n\n/**\n * Write a manifest file listing managed entries.\n */\nexport async function writeManifest(dir: string, entries: string[]): Promise<void> {\n const manifestPath = join(dir, MARKER_FILENAME);\n const content = entries.length > 0 ? entries.join('\\n') + '\\n' : '';\n await outputFile(manifestPath, content);\n}\n\n/**\n * Add an entry to the manifest. Creates manifest if it doesn't exist.\n * Use trailing '/' for folders.\n */\nexport async function addToManifest(dir: string, entry: string): Promise<void> {\n const existing = await readManifest(dir);\n if (!existing.includes(entry)) {\n existing.push(entry);\n await writeManifest(dir, existing);\n }\n}\n\n/**\n * Remove an entry from the manifest.\n * Deletes the manifest file entirely if it becomes empty.\n */\nexport async function removeFromManifest(dir: string, entry: string): Promise<void> {\n const existing = await readManifest(dir);\n const updated = existing.filter((e) => e !== entry);\n if (updated.length !== existing.length) {\n if (updated.length === 0) {\n // Remove manifest file when empty\n await remove(join(dir, MARKER_FILENAME));\n } else {\n await writeManifest(dir, updated);\n }\n }\n}\n\n/**\n * Check if an entry is tracked in the manifest.\n */\nexport async function isInManifest(dir: string, entry: string): Promise<boolean> {\n const entries = await readManifest(dir);\n return entries.includes(entry);\n}\n","import { execFileSync } from 'node:child_process';\nimport { mkdir, readdir, rm, writeFile, access } from 'node:fs/promises';\nimport { join, isAbsolute, resolve } from 'node:path';\nimport {\n type SourceConfig,\n type BridgeConfig,\n bridgeDir,\n sourceDir,\n isRemoteSource,\n} from './config.js';\nimport { dirExists } from './fs.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Marker file written inside every directory Agent Bridge manages under\n * `.agent-bridge/`. Used to gate destructive cleanup so we never delete\n * user-placed content.\n */\nconst SOURCE_MARKER = '.agent-bridge-managed';\n\nfunction git(args: string[], cwd?: string): string {\n return execFileSync('git', args, {\n cwd,\n encoding: 'utf-8',\n stdio: 'pipe',\n }).trim();\n}\n\n/**\n * Branch names must not contain shell metacharacters or leading dashes\n * (which could be mistaken for git flags). Conservative but safe.\n */\nfunction assertSafeBranch(branch: string, sourceName: string): void {\n if (!/^[A-Za-z0-9._/-]+$/.test(branch) || branch.startsWith('-')) {\n throw new Error(\n `Source '${sourceName}': invalid branch name '${branch}'. ` +\n `Branch must match [A-Za-z0-9._/-]+ and not start with '-'.`\n );\n }\n}\n\n/**\n * Reject source URLs that begin with '-' to prevent them being interpreted\n * as CLI flags by git.\n */\nfunction assertSafeSourceUrl(source: string, sourceName: string): void {\n if (source.startsWith('-')) {\n throw new Error(\n `Source '${sourceName}': URL must not start with '-' (got '${source}').`\n );\n }\n}\n\nasync function writeSourceMarker(dest: string): Promise<void> {\n await writeFile(\n join(dest, SOURCE_MARKER),\n 'This directory is managed by agent-bridge. Do not edit manually.\\n',\n 'utf-8'\n );\n}\n\nasync function hasSourceMarker(dir: string): Promise<boolean> {\n try {\n await access(join(dir, SOURCE_MARKER));\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Git operations for a single remote source\n// ---------------------------------------------------------------------------\n\nexport async function cloneSource(\n repoRoot: string,\n source: SourceConfig\n): Promise<void> {\n assertSafeSourceUrl(source.source, source.name);\n if (source.branch) assertSafeBranch(source.branch, source.name);\n\n const dest = sourceDir(repoRoot, source.name);\n\n await mkdir(bridgeDir(repoRoot), { recursive: true });\n\n const args = ['clone', '--depth', '1'];\n if (source.branch) {\n args.push('--single-branch', '--branch', source.branch);\n }\n // '--' terminates option parsing so the URL / dest can never be read as flags.\n args.push('--', source.source, dest);\n\n execFileSync('git', args, { stdio: 'pipe' });\n\n await writeSourceMarker(dest);\n}\n\nexport async function fetchSource(\n repoRoot: string,\n source: SourceConfig\n): Promise<void> {\n assertSafeSourceUrl(source.source, source.name);\n if (source.branch) assertSafeBranch(source.branch, source.name);\n\n const dest = sourceDir(repoRoot, source.name);\n\n git(['fetch', '--prune', 'origin'], dest);\n\n if (source.branch) {\n const currentBranch = git(['rev-parse', '--abbrev-ref', 'HEAD'], dest);\n if (currentBranch !== source.branch) {\n // Branch changed in config — ensure we have the ref then check it out.\n // A shallow --single-branch clone only has one branch, so fetch the\n // new branch explicitly before checkout.\n try {\n git(['fetch', '--depth', '1', 'origin', source.branch], dest);\n } catch {\n // If fetch fails, let checkout surface the real error.\n }\n git(['checkout', source.branch], dest);\n }\n }\n\n try {\n git(['pull', '--ff-only'], dest);\n } catch {\n // pull may fail for tags or detached HEAD — that's okay after fetch\n }\n\n // Refresh marker (in case the directory was restored from backup without it).\n await writeSourceMarker(dest);\n}\n\n// ---------------------------------------------------------------------------\n// Local source resolution\n// ---------------------------------------------------------------------------\n\nexport function resolveLocalSource(\n repoRoot: string,\n source: SourceConfig\n): string {\n const raw = source.source;\n if (isAbsolute(raw)) return raw;\n return resolve(repoRoot, raw);\n}\n\n// ---------------------------------------------------------------------------\n// Resolve the effective filesystem path for a source\n// ---------------------------------------------------------------------------\n\nexport function resolveSourcePath(\n repoRoot: string,\n source: SourceConfig\n): string {\n if (isRemoteSource(source.source)) {\n return sourceDir(repoRoot, source.name);\n }\n return resolveLocalSource(repoRoot, source);\n}\n\n// ---------------------------------------------------------------------------\n// Sync all sources\n// ---------------------------------------------------------------------------\n\nexport interface SourceSyncResult {\n name: string;\n action: 'cloned' | 'updated' | 'local';\n error?: string;\n}\n\nexport async function syncSource(\n repoRoot: string,\n source: SourceConfig\n): Promise<SourceSyncResult> {\n if (!isRemoteSource(source.source)) {\n const resolved = resolveLocalSource(repoRoot, source);\n if (!(await dirExists(resolved))) {\n return {\n name: source.name,\n action: 'local',\n error: `Local source path does not exist: ${resolved}\\n Update the path in .agent-bridge/config.yml or run \"agent-bridge init\" to reconfigure.`,\n };\n }\n return { name: source.name, action: 'local' };\n }\n\n const dest = sourceDir(repoRoot, source.name);\n\n if (await dirExists(dest)) {\n try {\n await fetchSource(repoRoot, source);\n return { name: source.name, action: 'updated' };\n } catch (err) {\n return {\n name: source.name,\n action: 'updated',\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n try {\n await cloneSource(repoRoot, source);\n return { name: source.name, action: 'cloned' };\n } catch (err) {\n return {\n name: source.name,\n action: 'cloned',\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport async function syncAllSources(\n repoRoot: string,\n config: BridgeConfig\n): Promise<SourceSyncResult[]> {\n await ensureBridgeGitignore(repoRoot);\n return Promise.all(config.sources.map((source) => syncSource(repoRoot, source)));\n}\n\n// ---------------------------------------------------------------------------\n// Ensure .gitignore in .agent-bridge/\n// ---------------------------------------------------------------------------\n\n/**\n * Write a `.gitignore` inside `.agent-bridge/` that ignores cloned source\n * directories (which are nested git repos) while keeping `config.yml` tracked.\n * Without this, git sees the nested repos as gitlinks/submodules and creates\n * phantom dirty-state changes.\n */\nexport async function ensureBridgeGitignore(repoRoot: string): Promise<void> {\n const bridge = bridgeDir(repoRoot);\n await mkdir(bridge, { recursive: true });\n\n const gitignorePath = join(bridge, '.gitignore');\n const lines = ['# Ignore cloned sources', '*', '!config.yml', '!.gitignore'];\n const content = lines.join('\\n') + '\\n';\n\n await writeFile(gitignorePath, content, 'utf-8');\n}\n\n// ---------------------------------------------------------------------------\n// Remove sources that no longer exist in config\n// ---------------------------------------------------------------------------\n\n/**\n * Remove cloned source directories under `.agent-bridge/` that are no longer\n * referenced in config. Only directories carrying the Agent Bridge marker\n * file are eligible for deletion — user-placed content is always preserved.\n *\n * For backwards compatibility with clones created before the marker existed,\n * directories containing a `.git` folder are also treated as stale.\n */\nexport async function removeStaleSourceDirs(\n repoRoot: string,\n config: BridgeConfig\n): Promise<string[]> {\n const bridge = bridgeDir(repoRoot);\n if (!(await dirExists(bridge))) return [];\n\n const entries = await readdir(bridge, { withFileTypes: true });\n\n const configuredNames = new Set(\n config.sources.filter((s) => isRemoteSource(s.source)).map((s) => s.name)\n );\n\n const removed: string[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (configuredNames.has(entry.name)) continue;\n\n const candidate = join(bridge, entry.name);\n\n if (\n (await hasSourceMarker(candidate)) ||\n (await dirExists(join(candidate, '.git')))\n ) {\n await rm(candidate, { recursive: true, force: true });\n removed.push(entry.name);\n }\n }\n\n return removed;\n}\n","import { readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { type BridgeConfig, type DomainConfig, sourceDomains, isIncluded } from './config.js';\nimport { dirExists, fileExists } from './fs.js';\nimport { resolveSourcePath } from './sources.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nconst TOOL_PREFIX_SEPARATOR = '--';\n\nexport interface Feature {\n name: string;\n /** Raw feature-type directory name (may contain tool prefix) */\n type: string;\n /** Display type with tool prefix stripped (used for destination dir) */\n displayType: string;\n source: string;\n domain: string;\n /** Absolute path to the feature (directory or file) */\n absolutePath: string;\n /** Tool prefix if present (e.g. \"cursor\" from \"cursor--instructions\") */\n toolPrefix?: string;\n /** True if feature is a single file, false if a directory */\n isFile: boolean;\n}\n\nexport interface DuplicateConflict {\n name: string;\n type: string;\n paths: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport { dirExists } from './fs.js';\n\n\nexport function parseToolPrefix(name: string): {\n toolPrefix?: string;\n baseName: string;\n} {\n const idx = name.indexOf(TOOL_PREFIX_SEPARATOR);\n if (idx > 0) {\n return {\n toolPrefix: name.substring(0, idx),\n baseName: name.substring(idx + TOOL_PREFIX_SEPARATOR.length),\n };\n }\n return { baseName: name };\n}\n\nexport function featureMatchesTool(\n feature: Feature,\n toolName: string\n): boolean {\n if (!feature.toolPrefix) return true;\n return feature.toolPrefix === toolName;\n}\n\nexport function featureName(feature: Feature): string {\n if (feature.toolPrefix) {\n return parseToolPrefix(feature.name).baseName;\n }\n return feature.name;\n}\n\n/** @deprecated Use featureName instead */\n/** @deprecated Use featureName directly */\nexport const syncName = featureName;\n\n// ---------------------------------------------------------------------------\n// Discovery\n// ---------------------------------------------------------------------------\n\n/**\n * Discover all feature types across all sources and domains.\n */\nexport async function discoverFeatureTypes(\n repoRoot: string,\n config: BridgeConfig\n): Promise<string[]> {\n const types = new Set<string>();\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n for (const domain of sourceDomains(config, source)) {\n const domainDir = join(srcPath, domain.name);\n if (!(await dirExists(domainDir))) continue;\n\n const entries = await readdir(domainDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && isIncluded(domain, entry.name)) {\n types.add(entry.name);\n }\n }\n }\n }\n\n return [...types].sort();\n}\n\n/**\n * Scan all features across sources × domains × feature types.\n *\n * Structure: `<source-path>/<domain>/<feature-type>/<feature>/` (folder-based)\n * or `<source-path>/<domain>/<feature-type>/<feature.ext>` (file-based)\n */\nexport async function scanFeatures(\n repoRoot: string,\n config: BridgeConfig,\n featureTypes: string[]\n): Promise<Feature[]> {\n const features: Feature[] = [];\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n\n for (const domain of sourceDomains(config, source)) {\n for (const ft of featureTypes) {\n if (!isIncluded(domain, ft)) continue;\n const { toolPrefix: typeToolPrefix, baseName: baseType } =\n parseToolPrefix(ft);\n const ftDir = join(srcPath, domain.name, ft);\n\n if (!(await dirExists(ftDir))) continue;\n\n const entries = await readdir(ftDir, { withFileTypes: true });\n for (const entry of entries) {\n const isFile = entry.isFile();\n const isDir = entry.isDirectory();\n if (!isFile && !isDir) continue;\n if (!isIncluded(domain, `${ft}/${entry.name}`)) continue;\n\n const { toolPrefix: itemToolPrefix } = parseToolPrefix(entry.name);\n const toolPrefix = itemToolPrefix ?? typeToolPrefix;\n\n features.push({\n name: entry.name,\n type: ft,\n displayType: baseType,\n source: source.name,\n domain: domain.name,\n absolutePath: join(ftDir, entry.name),\n toolPrefix,\n isFile,\n });\n }\n }\n }\n }\n\n return features;\n}\n\n// ---------------------------------------------------------------------------\n// Source browsing (used by `init` to offer domains and their contents)\n// ---------------------------------------------------------------------------\n\n/** Top-level directories of a source that can act as domains. */\nexport async function listDomains(srcPath: string): Promise<string[]> {\n if (!(await dirExists(srcPath))) return [];\n const entries = await readdir(srcPath, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules')\n .map((e) => e.name)\n .sort();\n}\n\nexport interface DomainContents {\n /** Feature-type folders and the features inside them. */\n featureTypes: Array<{ name: string; features: string[] }>;\n /** Flat files sync would pick up: well-known root files and `<tool>--` files. */\n files: string[];\n}\n\n/**\n * List what `sync` would consider inside a domain, so the user can pick a\n * subset. `toolNames` filters `<tool>--file` entries to configured tools.\n */\nexport async function listDomainContents(\n srcPath: string,\n domain: string,\n toolNames: Iterable<string>\n): Promise<DomainContents> {\n const domainDir = join(srcPath, domain);\n const tools = new Set(toolNames);\n const result: DomainContents = { featureTypes: [], files: [] };\n if (!(await dirExists(domainDir))) return result;\n\n const entries = await readdir(domainDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue;\n if (entry.isDirectory()) {\n const features = (await readdir(join(domainDir, entry.name), { withFileTypes: true }))\n .filter((f) => (f.isFile() || f.isDirectory()) && !f.name.startsWith('.'))\n .map((f) => f.name)\n .sort();\n result.featureTypes.push({ name: entry.name, features });\n } else if (entry.isFile()) {\n const { toolPrefix } = parseToolPrefix(entry.name);\n if ((ROOT_FILES as readonly string[]).includes(entry.name) || (toolPrefix && tools.has(toolPrefix))) {\n result.files.push(entry.name);\n }\n }\n }\n result.featureTypes.sort((a, b) => a.name.localeCompare(b.name));\n result.files.sort();\n return result;\n}\n\n/** Type re-export so callers don't need to import config.js just for this. */\nexport type { DomainConfig };\n\n// ---------------------------------------------------------------------------\n// Duplicate detection\n// ---------------------------------------------------------------------------\n\nexport function detectDuplicates(features: Feature[]): DuplicateConflict[] {\n const byKey = new Map<string, Feature[]>();\n\n for (const f of features) {\n const linkName = featureName(f);\n const key = `${f.displayType}/${linkName}`;\n const group = byKey.get(key) ?? [];\n group.push(f);\n byKey.set(key, group);\n }\n\n const conflicts: DuplicateConflict[] = [];\n for (const [, group] of byKey) {\n if (group.length > 1) {\n conflicts.push({\n name: featureName(group[0]),\n type: group[0].type,\n paths: group.map((f) => f.absolutePath),\n });\n }\n }\n\n return conflicts;\n}\n\n// ---------------------------------------------------------------------------\n// Root file scanning\n// ---------------------------------------------------------------------------\n\n/**\n * Well-known root files that live at the domain root and should be synced to the\n * workspace root. When a source contains `<domain>/AGENTS.md` (etc.), Agent Bridge\n * copies it to the project root.\n */\nexport const ROOT_FILES = ['AGENTS.md', 'CLAUDE.md', 'SYSTEM.md'] as const;\nexport type RootFileName = (typeof ROOT_FILES)[number];\n\nexport interface RootFile {\n /** The well-known filename (e.g. \"AGENTS.md\") */\n fileName: RootFileName;\n /** Source that provides this file */\n source: string;\n /** Domain where it was found */\n domain: string;\n /** Absolute path to the source file */\n absolutePath: string;\n}\n\nexport interface RootFileDuplicate {\n fileName: RootFileName;\n paths: string[];\n}\n\n/**\n * Scan all sources × domains for well-known root files.\n * Returns one entry per found file.\n */\nexport async function scanRootFiles(\n repoRoot: string,\n config: BridgeConfig\n): Promise<RootFile[]> {\n const found: RootFile[] = [];\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n for (const domain of sourceDomains(config, source)) {\n for (const fileName of ROOT_FILES) {\n if (!isIncluded(domain, fileName)) continue;\n const filePath = join(srcPath, domain.name, fileName);\n if (await fileExists(filePath)) {\n found.push({\n fileName,\n source: source.name,\n domain: domain.name,\n absolutePath: filePath,\n });\n }\n }\n }\n }\n\n return found;\n}\n\n/**\n * Detect duplicate root files (same filename provided by multiple sources/domains).\n */\nexport function detectRootFileDuplicates(\n rootFiles: RootFile[]\n): RootFileDuplicate[] {\n const byName = new Map<RootFileName, RootFile[]>();\n for (const rf of rootFiles) {\n const group = byName.get(rf.fileName) ?? [];\n group.push(rf);\n byName.set(rf.fileName, group);\n }\n\n const duplicates: RootFileDuplicate[] = [];\n for (const [fileName, group] of byName) {\n if (group.length > 1) {\n duplicates.push({\n fileName,\n paths: group.map((rf) => rf.absolutePath),\n });\n }\n }\n\n return duplicates;\n}\n\n// ---------------------------------------------------------------------------\n// Tool root file scanning (tool-prefixed flat files at domain level)\n// ---------------------------------------------------------------------------\n\nexport interface ToolRootEntry {\n /** The tool name this entry targets (e.g. \"pi\") */\n toolName: string;\n /** Destination filename (e.g. \"settings.json\" from \"cursor--settings.json\") */\n name: string;\n /** Source that provides this entry */\n source: string;\n /** Domain where it was found */\n domain: string;\n /** Absolute path to the source file */\n absolutePath: string;\n}\n\nexport interface ToolRootDuplicate {\n /** The tool name */\n toolName: string;\n /** Name of the duplicate entry */\n name: string;\n /** Paths where the duplicates were found */\n paths: string[];\n}\n\n/**\n * Scan all sources × domains for tool-prefixed flat files at the domain level.\n * A file named `cursor--settings.json` targets the tool \"cursor\" with\n * destination filename \"settings.json\".\n */\nexport async function scanToolRootEntries(\n repoRoot: string,\n config: BridgeConfig\n): Promise<ToolRootEntry[]> {\n const entries: ToolRootEntry[] = [];\n const toolNames = new Set(config.tools.map((t) => t.name));\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n for (const domain of sourceDomains(config, source)) {\n const domainDir = join(srcPath, domain.name);\n if (!(await dirExists(domainDir))) continue;\n\n const domainEntries = await readdir(domainDir, { withFileTypes: true });\n for (const entry of domainEntries) {\n if (!entry.isFile() || !isIncluded(domain, entry.name)) continue;\n\n const { toolPrefix, baseName } = parseToolPrefix(entry.name);\n if (!toolPrefix || !toolNames.has(toolPrefix)) continue;\n\n entries.push({\n toolName: toolPrefix,\n name: baseName,\n source: source.name,\n domain: domain.name,\n absolutePath: join(domainDir, entry.name),\n });\n }\n }\n }\n\n return entries;\n}\n\n/**\n * Detect duplicate tool root entries (same tool + name from multiple sources/domains).\n */\nexport function detectToolRootDuplicates(\n entries: ToolRootEntry[]\n): ToolRootDuplicate[] {\n const byKey = new Map<string, ToolRootEntry[]>();\n for (const entry of entries) {\n const key = `${entry.toolName}/${entry.name}`;\n const group = byKey.get(key) ?? [];\n group.push(entry);\n byKey.set(key, group);\n }\n\n const duplicates: ToolRootDuplicate[] = [];\n for (const [, group] of byKey) {\n if (group.length > 1) {\n duplicates.push({\n toolName: group[0].toolName,\n name: group[0].name,\n paths: group.map((e) => e.absolutePath),\n });\n }\n }\n\n return duplicates;\n}\n","/**\n * Selection model for a checkbox tree. Pure data, no terminal I/O, so it can\n * be unit-tested; `tree-prompt.ts` renders it with @clack/core.\n */\n\nexport interface TreeNode {\n label: string;\n /** Leaf value. Nodes with children have no value of their own. */\n value?: string;\n hint?: string;\n children?: TreeNode[];\n}\n\nexport interface TreeRow {\n node: TreeNode;\n depth: number;\n parent?: TreeNode;\n}\n\nexport type CheckState = 'none' | 'some' | 'all';\n\nexport class TreeModel {\n readonly selected = new Set<string>();\n private readonly expanded = new Set<TreeNode>();\n cursor = 0;\n\n constructor(\n readonly roots: TreeNode[],\n opts: { expandDepth?: number; initialSelected?: Iterable<string> } = {}\n ) {\n const depth = opts.expandDepth ?? 1;\n const expand = (nodes: TreeNode[], d: number) => {\n if (d >= depth) return;\n for (const n of nodes) {\n if (n.children?.length) {\n this.expanded.add(n);\n expand(n.children, d + 1);\n }\n }\n };\n expand(roots, 0);\n for (const v of opts.initialSelected ?? []) this.selected.add(v);\n }\n\n /** Visible rows in display order, honoring collapsed nodes. */\n rows(): TreeRow[] {\n const out: TreeRow[] = [];\n const walk = (nodes: TreeNode[], depth: number, parent?: TreeNode) => {\n for (const node of nodes) {\n out.push({ node, depth, parent });\n if (node.children?.length && this.expanded.has(node)) walk(node.children, depth + 1, node);\n }\n };\n walk(this.roots, 0);\n return out;\n }\n\n current(): TreeRow | undefined {\n return this.rows()[this.cursor];\n }\n\n isExpanded(node: TreeNode): boolean {\n return this.expanded.has(node);\n }\n\n leaves(node: TreeNode): string[] {\n if (!node.children?.length) return node.value !== undefined ? [node.value] : [];\n return node.children.flatMap((c) => this.leaves(c));\n }\n\n state(node: TreeNode): CheckState {\n const leaves = this.leaves(node);\n if (leaves.length === 0) return 'none';\n const n = leaves.filter((l) => this.selected.has(l)).length;\n return n === 0 ? 'none' : n === leaves.length ? 'all' : 'some';\n }\n\n /** Space: leaf toggles; parent selects all descendants unless already all. */\n toggle(): void {\n const row = this.current();\n if (!row) return;\n const leaves = this.leaves(row.node);\n if (this.state(row.node) === 'all') {\n for (const l of leaves) this.selected.delete(l);\n } else {\n for (const l of leaves) this.selected.add(l);\n }\n }\n\n move(delta: 1 | -1): void {\n const n = this.rows().length;\n if (n === 0) return;\n this.cursor = (this.cursor + delta + n) % n;\n }\n\n /** Right: expand. On a leaf or an open node, nothing happens. */\n expand(): void {\n const row = this.current();\n if (row?.node.children?.length) this.expanded.add(row.node);\n }\n\n /** Left: collapse an open node; on a closed node or leaf, jump to its parent. */\n collapse(): void {\n const row = this.current();\n if (!row) return;\n if (row.node.children?.length && this.expanded.has(row.node)) {\n this.expanded.delete(row.node);\n return;\n }\n if (row.parent) {\n const idx = this.rows().findIndex((r) => r.node === row.parent);\n if (idx >= 0) this.cursor = idx;\n }\n }\n\n toggleExpand(): void {\n const row = this.current();\n if (!row?.node.children?.length) return;\n if (this.expanded.has(row.node)) this.expanded.delete(row.node);\n else this.expanded.add(row.node);\n }\n}\n","import { Prompt, isCancel } from '@clack/core';\nimport { TreeModel, type TreeNode, type CheckState } from './tree.js';\n\n// Minimal ANSI styling (matches @clack/prompts' look without adding a dep).\nconst tty = process.stdout.isTTY && !process.env.NO_COLOR;\nconst paint = (code: number, s: string) => (tty ? `\\x1b[${code}m${s}\\x1b[39m` : s);\nconst dim = (s: string) => (tty ? `\\x1b[2m${s}\\x1b[22m` : s);\nconst cyan = (s: string) => paint(36, s);\nconst green = (s: string) => paint(32, s);\nconst yellow = (s: string) => paint(33, s);\nconst red = (s: string) => paint(31, s);\nconst gray = (s: string) => paint(90, s);\n\nconst S_BAR = '│';\nconst S_BAR_END = '└';\nconst CHECK: Record<CheckState, string> = { none: '◻', some: yellow('◧'), all: green('◼') };\n\nfunction symbol(state: string): string {\n if (state === 'cancel') return red('■');\n if (state === 'error') return yellow('▲');\n if (state === 'submit') return green('◇');\n return cyan('◆');\n}\n\nexport interface TreeSelectOptions {\n message: string;\n tree: TreeNode[];\n /** How many levels start expanded (default 1 = roots open). */\n expandDepth?: number;\n initialValues?: string[];\n required?: boolean;\n /** Max visible rows (default: terminal height − 6, at least 5). */\n maxItems?: number;\n}\n\n/**\n * A checkbox tree. Space toggles the node under the cursor (a parent toggles\n * everything beneath it), ←/→ collapse/expand, Enter confirms.\n * Resolves to the selected leaf values, or the clack cancel symbol.\n */\nexport async function treeSelect(opts: TreeSelectOptions): Promise<string[] | symbol> {\n const model = new TreeModel(opts.tree, {\n expandDepth: opts.expandDepth,\n initialSelected: opts.initialValues,\n });\n const maxItems = Math.max(5, opts.maxItems ?? (process.stdout.rows || 24) - 6);\n\n const prompt = new Prompt(\n {\n validate: () => {\n if (opts.required !== false && model.selected.size === 0) return 'Select at least one item.';\n },\n render() {\n const title = `${gray(S_BAR)}\\n${symbol(this.state)} ${opts.message}\\n`;\n if (this.state === 'submit') {\n const n = model.selected.size;\n return `${title}${gray(S_BAR)} ${dim(`${n} item${n === 1 ? '' : 's'} selected`)}`;\n }\n if (this.state === 'cancel') {\n return `${title}${gray(S_BAR)} ${dim('cancelled')}\\n${gray(S_BAR)}`;\n }\n\n const rows = model.rows();\n // Keep the cursor inside a window of `maxItems` rows.\n let start = 0;\n if (rows.length > maxItems) {\n start = Math.min(Math.max(0, model.cursor - Math.floor(maxItems / 2)), rows.length - maxItems);\n }\n const end = Math.min(rows.length, start + maxItems);\n\n const lines: string[] = [];\n if (start > 0) lines.push(`${cyan(S_BAR)} ${dim('…')}`);\n for (let i = start; i < end; i++) {\n const { node, depth } = rows[i];\n const active = i === model.cursor;\n const hasKids = !!node.children?.length;\n const arrow = hasKids ? (model.isExpanded(node) ? '▾' : '▸') : ' ';\n const box = CHECK[model.state(node)];\n const indent = ' '.repeat(depth);\n let label = active ? node.label : dim(node.label);\n if (node.hint) label += ` ${dim(node.hint)}`;\n lines.push(`${cyan(S_BAR)} ${indent}${dim(arrow)} ${box} ${label}`);\n }\n if (end < rows.length) lines.push(`${cyan(S_BAR)} ${dim('…')}`);\n\n const footer =\n this.state === 'error'\n ? `${yellow(S_BAR_END)} ${yellow(this.error)}`\n : `${cyan(S_BAR_END)} ${dim('space toggle · ←/→ collapse/expand · enter confirm')}`;\n return `${title}${lines.join('\\n')}\\n${footer}\\n`;\n },\n },\n false\n );\n\n prompt.on('cursor', (key) => {\n switch (key) {\n case 'up':\n model.move(-1);\n break;\n case 'down':\n model.move(1);\n break;\n case 'left':\n model.collapse();\n break;\n case 'right':\n model.expand();\n break;\n case 'space':\n model.toggle();\n break;\n }\n prompt.value = [...model.selected];\n });\n prompt.value = [...model.selected];\n\n const result = await prompt.prompt();\n if (isCancel(result)) return result;\n return [...model.selected];\n}\n","","import pkg from '../../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n","import { type BridgeConfig, saveConfig, loadConfig } from '../config.js';\nimport { refreshGitHooks } from '../git.js';\nimport { VERSION } from '../version.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A migration function receives the repo root and current config,\n * and returns the (possibly modified) config. It may also perform\n * filesystem operations (rename dirs, update files, etc.).\n */\nexport type MigrationFn = (\n repoRoot: string,\n config: BridgeConfig\n) => Promise<BridgeConfig>;\n\nexport interface Migration {\n /** Semver version this migration upgrades TO (e.g. \"0.6.0\"). */\n version: string;\n /** Human-readable description shown when running. */\n description: string;\n /** The migration logic. */\n migrate: MigrationFn;\n}\n\nexport interface MigrationResult {\n fromVersion: string;\n toVersion: string;\n applied: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Registry — add new migrations here in semver order\n// ---------------------------------------------------------------------------\n\nexport const migrations: Migration[] = [\n {\n version: '0.14.0',\n description: 'move top-level domains into each source; git hooks run `sync` only',\n migrate: async (repoRoot, config) => {\n // Legacy configs list domains once for all sources. Give every source its\n // own copy (everything included) so the top-level key can go away.\n const { domains, ...rest } = config;\n const sources = config.sources.map((s) =>\n s.domains ? s : { ...s, domains: (domains ?? []).map((name) => ({ name })) }\n );\n // `update` was merged into `sync`; rewrite hooks we installed earlier.\n await refreshGitHooks(repoRoot);\n return { ...rest, sources };\n },\n },\n];\n\n// ---------------------------------------------------------------------------\n// Semver helpers (minimal — no external dep needed)\n// ---------------------------------------------------------------------------\n\n/** Parse \"1.2.3\" or \"1.2.3-beta.1\" into [major, minor, patch]. */\nexport function parseSemver(version: string): [number, number, number] {\n const clean = version.replace(/^v/, '').split('-')[0];\n const parts = clean.split('.').map(Number);\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];\n}\n\n/** Returns -1 | 0 | 1 comparing a to b (ignores prerelease). */\nexport function compareSemver(a: string, b: string): number {\n const [aMaj, aMin, aPat] = parseSemver(a);\n const [bMaj, bMin, bPat] = parseSemver(b);\n\n if (aMaj !== bMaj) return aMaj < bMaj ? -1 : 1;\n if (aMin !== bMin) return aMin < bMin ? -1 : 1;\n if (aPat !== bPat) return aPat < bPat ? -1 : 1;\n return 0;\n}\n\n// ---------------------------------------------------------------------------\n// Migration runner\n// ---------------------------------------------------------------------------\n\n/**\n * Find migrations that should run when upgrading from `fromVersion` to\n * `toVersion`. Returns them sorted in ascending version order.\n */\nexport function pendingMigrations(\n fromVersion: string,\n toVersion: string\n): Migration[] {\n return migrations\n .filter(\n (m) =>\n compareSemver(m.version, fromVersion) > 0 &&\n compareSemver(m.version, toVersion) <= 0\n )\n .sort((a, b) => compareSemver(a.version, b.version));\n}\n\n/**\n * Run all pending migrations between the config's version and the\n * currently installed VERSION. Updates and saves the config afterwards.\n *\n * Returns null if no migration was needed.\n */\nexport async function runMigrations(\n repoRoot: string\n): Promise<MigrationResult | null> {\n let config = await loadConfig(repoRoot);\n const configVersion = config.version ?? '0.0.0';\n\n const cmp = compareSemver(configVersion, VERSION);\n\n // Already current\n if (cmp === 0) return null;\n\n // Config is newer than installed package (downgrade)\n if (cmp > 0) return null;\n\n // Config is older — find and run migrations\n const pending = pendingMigrations(configVersion, VERSION);\n const applied: string[] = [];\n\n for (const migration of pending) {\n config = await migration.migrate(repoRoot, config);\n applied.push(migration.version);\n }\n\n // Always update the version, even if no migrations ran\n // (e.g. patch bump with no structural changes)\n config = { ...config, version: VERSION };\n await saveConfig(repoRoot, config);\n\n return {\n fromVersion: configVersion,\n toVersion: VERSION,\n applied,\n };\n}\n","import { readdir, mkdir, rmdir, copyFile, readFile, writeFile } from 'node:fs/promises';\nimport { join, dirname, basename } from 'node:path';\nimport type { BridgeConfig } from './config.js';\nimport {\n dirExists,\n fileExists,\n copyDirContents,\n removeDir,\n removeFile,\n readManifest,\n writeManifest,\n addToManifest,\n removeFromManifest,\n isManifestFolder,\n manifestEntryName,\n MARKER_FILENAME,\n} from './fs.js';\nimport {\n type Feature,\n type RootFile,\n type ToolRootEntry,\n ROOT_FILES,\n featureMatchesTool,\n featureName,\n} from './manifest.js';\n\n// ---------------------------------------------------------------------------\n// Feature path helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the destination path for a feature inside a tool's folder.\n * For folder-based features: returns the folder path.\n * For file-based features: returns the file path.\n */\nexport function featureDestPath(\n repoRoot: string,\n toolFolder: string,\n featureType: string,\n featureName: string\n): string {\n return join(repoRoot, toolFolder, featureType, featureName);\n}\n\n// ---------------------------------------------------------------------------\n// Conflict detection\n// ---------------------------------------------------------------------------\n\n/**\n * Check whether a folder-based feature destination conflicts with existing user content.\n * Returns `true` when the folder exists and is not tracked in the manifest.\n */\nexport async function checkFolderConflict(featureTypeDir: string, folderName: string): Promise<boolean> {\n const destPath = join(featureTypeDir, folderName);\n if (!(await dirExists(destPath))) return false;\n \n const manifest = await readManifest(featureTypeDir);\n return !manifest.includes(folderName + '/');\n}\n\n/**\n * Check whether a file-based feature destination conflicts with existing user content.\n * Returns `true` when the file exists and is not tracked in the manifest.\n */\nexport async function checkFileConflict(featureTypeDir: string, fileName: string): Promise<boolean> {\n const filePath = join(featureTypeDir, fileName);\n if (!(await fileExists(filePath))) return false;\n \n const manifest = await readManifest(featureTypeDir);\n return !manifest.includes(fileName);\n}\n\n/**\n * Check whether a feature destination conflicts with existing user content.\n * Handles both folder-based and file-based features.\n */\nexport async function checkPathConflict(\n featureTypeDir: string,\n featureName: string,\n isFile: boolean\n): Promise<boolean> {\n if (isFile) {\n return checkFileConflict(featureTypeDir, featureName);\n } else {\n return checkFolderConflict(featureTypeDir, featureName);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Single feature sync\n// ---------------------------------------------------------------------------\n\n/**\n * Sync a folder-based feature: clear destination, copy files, add to manifest.\n */\nexport async function syncFolderFeature(\n sourcePath: string,\n featureTypeDir: string,\n folderName: string\n): Promise<'created' | 'updated'> {\n const destPath = join(featureTypeDir, folderName);\n const existed = await dirExists(destPath);\n\n if (existed) {\n await removeDir(destPath);\n }\n\n await mkdir(destPath, { recursive: true });\n await copyDirContents(sourcePath, destPath);\n await addToManifest(featureTypeDir, folderName + '/');\n\n return existed ? 'updated' : 'created';\n}\n\n/**\n * Sync a file-based feature: copy file, add to manifest.\n */\nexport async function syncFileFeature(\n sourcePath: string,\n featureTypeDir: string,\n fileName: string\n): Promise<'created' | 'updated'> {\n const destPath = join(featureTypeDir, fileName);\n const existed = await fileExists(destPath);\n\n await mkdir(featureTypeDir, { recursive: true });\n await copyFile(sourcePath, destPath);\n await addToManifest(featureTypeDir, fileName);\n\n return existed ? 'updated' : 'created';\n}\n\n/**\n * Sync a feature (folder or file based).\n * @deprecated Use syncFolderFeature or syncFileFeature directly\n */\nexport async function syncFeature(\n sourcePath: string,\n destPath: string\n): Promise<'created' | 'updated'> {\n const featureTypeDir = dirname(destPath);\n const folderName = basename(destPath);\n return syncFolderFeature(sourcePath, featureTypeDir, folderName);\n}\n\n// ---------------------------------------------------------------------------\n// Empty directory cleanup\n// ---------------------------------------------------------------------------\n\nexport async function removeEmptyParents(\n dirPath: string,\n stopAt: string\n): Promise<void> {\n let current = dirPath;\n while (current !== stopAt && current.startsWith(stopAt)) {\n try {\n const entries = await readdir(current);\n if (entries.length > 0) break;\n await rmdir(current);\n current = dirname(current);\n } catch {\n break;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Collect all managed entries from manifests\n// ---------------------------------------------------------------------------\n\ninterface ManagedEntry {\n /** Full path to the file or folder */\n path: string;\n /** Directory containing the manifest (feature-type dir) */\n manifestDir: string;\n /** Entry as it appears in manifest (with trailing / for folders) */\n manifestEntry: string;\n /** True if folder, false if file */\n isFolder: boolean;\n}\n\n/**\n * Recursively collect all managed entries (files and folders) from manifests.\n * Scans for .agentbridge files and reads their contents.\n */\nasync function collectManagedEntries(dir: string): Promise<ManagedEntry[]> {\n if (!(await dirExists(dir))) return [];\n\n const result: ManagedEntry[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n\n // Check if this directory has a manifest\n const manifestEntries = await readManifest(dir);\n for (const entry of manifestEntries) {\n const isFolder = isManifestFolder(entry);\n const name = manifestEntryName(entry);\n result.push({\n path: join(dir, name),\n manifestDir: dir,\n manifestEntry: entry,\n isFolder,\n });\n }\n\n // Recurse into subdirectories (to find manifests in nested feature-type dirs)\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (entry.name === MARKER_FILENAME) continue;\n \n const fullPath = join(dir, entry.name);\n const sub = await collectManagedEntries(fullPath);\n result.push(...sub);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Reconciliation (high-level)\n// ---------------------------------------------------------------------------\n\nexport interface ReconcileResult {\n added: number;\n updated: number;\n removed: number;\n errors: Array<{ path: string; error: string }>;\n}\n\ninterface ExpectedFeature {\n sourcePath: string;\n featureTypeDir: string;\n name: string;\n manifestEntry: string;\n isFile: boolean;\n}\n\nexport async function reconcileFeatures(\n repoRoot: string,\n config: BridgeConfig,\n features: Feature[]\n): Promise<ReconcileResult> {\n let added = 0;\n let updated = 0;\n let removed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n // Phase 1: Compute all expected features\n // Key = full destination path\n const expectedFeatures = new Map<string, ExpectedFeature>();\n\n for (const tool of config.tools) {\n for (const feature of features) {\n if (!featureMatchesTool(feature, tool.name)) continue;\n\n const name = featureName(feature);\n const featureTypeDir = join(repoRoot, tool.folder, feature.displayType);\n const destPath = join(featureTypeDir, name);\n const manifestEntry = feature.isFile ? name : name + '/';\n\n expectedFeatures.set(destPath, {\n sourcePath: feature.absolutePath,\n featureTypeDir,\n name,\n manifestEntry,\n isFile: feature.isFile,\n });\n }\n }\n\n // Phase 2: Remove orphaned managed entries (previously synced but no longer expected)\n for (const tool of config.tools) {\n const toolDir = join(repoRoot, tool.folder);\n const managedEntries = await collectManagedEntries(toolDir);\n\n for (const entry of managedEntries) {\n if (expectedFeatures.has(entry.path)) continue;\n\n try {\n if (entry.isFolder) {\n await removeDir(entry.path);\n } else {\n await removeFile(entry.path);\n }\n await removeFromManifest(entry.manifestDir, entry.manifestEntry);\n await removeEmptyParents(entry.manifestDir, toolDir);\n removed++;\n } catch (err) {\n errors.push({\n path: entry.path,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n // Phase 3: Create / update features (isolate failures so one bad feature\n // doesn't abort the entire sync).\n for (const [destPath, expected] of expectedFeatures) {\n try {\n const result = expected.isFile\n ? await syncFileFeature(\n expected.sourcePath,\n expected.featureTypeDir,\n expected.name\n )\n : await syncFolderFeature(\n expected.sourcePath,\n expected.featureTypeDir,\n expected.name\n );\n\n if (result === 'created') added++;\n else if (result === 'updated') updated++;\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { added, updated, removed, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Root file sync\n// ---------------------------------------------------------------------------\n\nconst ROOT_FILE_MARKER = '<!-- Managed by Agent Bridge -->';\n\n/**\n * Check if a root file at `destPath` is managed by Agent Bridge.\n * A file is managed if it starts with the marker comment.\n */\nexport async function isRootFileManaged(destPath: string): Promise<boolean> {\n if (!(await fileExists(destPath))) return false;\n const content = await readFile(destPath, 'utf-8');\n return content.startsWith(ROOT_FILE_MARKER);\n}\n\nexport interface RootFileSyncResult {\n synced: string[];\n removed: string[];\n errors: Array<{ path: string; error: string }>;\n}\n\n/**\n * Sync root files: copy source root files to the workspace root, and clean up\n * managed root files that are no longer provided by any source.\n */\nexport async function syncRootFiles(\n repoRoot: string,\n rootFiles: RootFile[]\n): Promise<RootFileSyncResult> {\n const synced: string[] = [];\n const removed: string[] = [];\n const errors: Array<{ path: string; error: string }> = [];\n\n // Build map: fileName → rootFile (already deduplicated by caller)\n const expected = new Map<string, RootFile>();\n for (const rf of rootFiles) {\n expected.set(rf.fileName, rf);\n }\n\n // Sync expected root files\n for (const [fileName, rf] of expected) {\n const destPath = join(repoRoot, fileName);\n try {\n // If file exists and is NOT managed by us, skip (don't overwrite user files)\n if (await fileExists(destPath)) {\n if (!(await isRootFileManaged(destPath))) {\n continue;\n }\n }\n\n const sourceContent = await readFile(rf.absolutePath, 'utf-8');\n const managedContent = ROOT_FILE_MARKER + '\\n' + sourceContent;\n await mkdir(dirname(destPath), { recursive: true });\n await writeFile(destPath, managedContent, 'utf-8');\n synced.push(fileName);\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n // Remove managed root files no longer provided by any source\n for (const fileName of ROOT_FILES) {\n if (expected.has(fileName)) continue;\n const destPath = join(repoRoot, fileName);\n try {\n if (await isRootFileManaged(destPath)) {\n await removeFile(destPath);\n removed.push(fileName);\n }\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { synced, removed, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Tool root entry sync (tool-prefixed flat files)\n// ---------------------------------------------------------------------------\n\nexport interface ToolRootSyncResult {\n added: number;\n updated: number;\n removed: number;\n errors: Array<{ path: string; error: string }>;\n}\n\n/**\n * Reconcile tool root entries: sync expected entries and remove orphans.\n * Tool-prefixed flat files (e.g. `cursor--settings.json`) are copied directly\n * into the tool's root folder (e.g. `.cursor/settings.json`).\n */\nexport async function reconcileToolRootEntries(\n repoRoot: string,\n config: BridgeConfig,\n entries: ToolRootEntry[]\n): Promise<ToolRootSyncResult> {\n let added = 0;\n let updated = 0;\n let removed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n // Build tool name → folder mapping\n const toolFolders = new Map<string, string>();\n for (const tool of config.tools) {\n toolFolders.set(tool.name, tool.folder);\n }\n\n // Key = destination path, value = entry info\n const expectedEntries = new Map<\n string,\n { sourcePath: string; name: string; toolDir: string }\n >();\n\n for (const entry of entries) {\n const folder = toolFolders.get(entry.toolName);\n if (!folder) continue;\n\n const toolDir = join(repoRoot, folder);\n const destPath = join(toolDir, entry.name);\n\n expectedEntries.set(destPath, {\n sourcePath: entry.absolutePath,\n name: entry.name,\n toolDir,\n });\n }\n\n // Remove orphaned managed entries from tool root directories\n for (const tool of config.tools) {\n const toolDir = join(repoRoot, tool.folder);\n const manifest = await readManifest(toolDir);\n\n for (const manifestEntry of manifest) {\n const isFolder = isManifestFolder(manifestEntry);\n const name = manifestEntryName(manifestEntry);\n const destPath = join(toolDir, name);\n\n if (expectedEntries.has(destPath)) continue;\n\n try {\n if (isFolder) {\n await removeDir(destPath);\n } else {\n await removeFile(destPath);\n }\n await removeFromManifest(toolDir, manifestEntry);\n removed++;\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n // Sync expected entries (always files)\n for (const [, expected] of expectedEntries) {\n try {\n const result = await syncFileFeature(\n expected.sourcePath,\n expected.toolDir,\n expected.name\n );\n\n if (result === 'created') added++;\n else if (result === 'updated') updated++;\n } catch (err) {\n errors.push({\n path: join(expected.toolDir, expected.name),\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { added, updated, removed, errors };\n}\n","import * as p from '@clack/prompts';\nimport { loadConfig, isOptedOut, OPT_OUT_MARKER } from '../lib/config.js';\nimport { findRepoRoot } from '../lib/git.js';\nimport { runMigrations } from '../lib/migrations/index.js';\nimport {\n discoverFeatureTypes,\n scanFeatures,\n detectDuplicates,\n scanRootFiles,\n detectRootFileDuplicates,\n scanToolRootEntries,\n detectToolRootDuplicates,\n featureMatchesTool,\n featureName,\n} from '../lib/manifest.js';\nimport {\n featureDestPath,\n checkPathConflict,\n reconcileFeatures,\n syncRootFiles,\n reconcileToolRootEntries,\n} from '../lib/sync.js';\nimport { syncAllSources, removeStaleSourceDirs } from '../lib/sources.js';\nimport { join } from 'node:path';\n\nexport async function syncCommand(cwd?: string, _opts?: unknown): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n p.intro('Agent Bridge Sync');\n\n // Respect an opt-out tombstone so a postinstall guard doesn't re-sync.\n if (await isOptedOut(repoRoot)) {\n p.log.warn(`${OPT_OUT_MARKER} present — Agent Bridge is opted out. Skipping sync.`);\n p.outro('Skipped (opted out).');\n return;\n }\n\n const s = p.spinner();\n\n // --- Phase 1: Load & validate config ---\n s.start('Loading configuration…');\n\n // Run pending migrations first so we work with the upgraded config\n const migrationResult = await runMigrations(repoRoot);\n if (migrationResult) {\n p.log.info(\n `Config upgraded ${migrationResult.fromVersion} → ${migrationResult.toVersion}` +\n (migrationResult.applied.length > 0\n ? ` (${migrationResult.applied.length} migration(s))`\n : '')\n );\n }\n\n const config = await loadConfig(repoRoot);\n s.stop('Configuration valid');\n\n // --- Phase 2: Fetch sources (clone new, pull existing) ---\n s.start('Fetching sources…');\n\n const sourceResults = await syncAllSources(repoRoot, config);\n const sourceErrors = sourceResults.filter((r) => r.error);\n if (sourceErrors.length > 0) {\n s.stop('Some sources failed');\n for (const err of sourceErrors) {\n p.log.error(`${err.name}: ${err.error}`);\n }\n process.exit(1);\n }\n\n // Clean up stale source directories\n const staleRemoved = await removeStaleSourceDirs(repoRoot, config);\n if (staleRemoved.length > 0) {\n for (const name of staleRemoved) {\n p.log.info(`Removed stale source: ${name}`);\n }\n }\n\n for (const r of sourceResults) {\n if (r.action !== 'local') {\n p.log.info(`${r.name}: ${r.action}`);\n }\n }\n\n s.stop('Sources up to date');\n\n // --- Phase 3: Discover & validate features ---\n s.start('Discovering features…');\n\n const featureTypes = await discoverFeatureTypes(repoRoot, config);\n const features = await scanFeatures(repoRoot, config, featureTypes);\n const rootFiles = await scanRootFiles(repoRoot, config);\n const toolRootEntries = await scanToolRootEntries(repoRoot, config);\n\n const duplicates = detectDuplicates(features);\n if (duplicates.length > 0) {\n s.stop('Duplicate features detected');\n for (const dup of duplicates) {\n p.log.error(\n `Duplicate \"${dup.name}\" (${dup.type}): ${dup.paths.join(', ')}`\n );\n }\n process.exit(1);\n }\n\n const rootDuplicates = detectRootFileDuplicates(rootFiles);\n if (rootDuplicates.length > 0) {\n s.stop('Duplicate root files detected');\n for (const dup of rootDuplicates) {\n p.log.error(\n `Duplicate \"${dup.fileName}\": ${dup.paths.join(', ')}`\n );\n }\n process.exit(1);\n }\n\n const toolRootDuplicates = detectToolRootDuplicates(toolRootEntries);\n if (toolRootDuplicates.length > 0) {\n s.stop('Duplicate tool root entries detected');\n for (const dup of toolRootDuplicates) {\n p.log.error(\n `Duplicate \"${dup.name}\" for tool \"${dup.toolName}\": ${dup.paths.join(', ')}`\n );\n }\n process.exit(1);\n }\n\n s.stop(`${features.length} features found${rootFiles.length > 0 ? `, ${rootFiles.length} root file(s)` : ''}${toolRootEntries.length > 0 ? `, ${toolRootEntries.length} tool root entr${toolRootEntries.length === 1 ? 'y' : 'ies'}` : ''}`);\n\n // --- Phase 3b: Detect path conflicts ---\n s.start('Checking for path conflicts…');\n\n const conflicts: string[] = [];\n for (const tool of config.tools) {\n for (const feature of features) {\n if (!featureMatchesTool(feature, tool.name)) continue;\n\n const linkName = featureName(feature);\n const featureTypeDir = join(repoRoot, tool.folder, feature.displayType);\n const dest = featureDestPath(\n repoRoot,\n tool.folder,\n feature.displayType,\n linkName\n );\n if (await checkPathConflict(featureTypeDir, linkName, feature.isFile)) {\n conflicts.push(dest);\n }\n }\n }\n\n if (conflicts.length > 0) {\n s.stop('Path conflicts detected');\n for (const c of conflicts) {\n p.log.error(`Conflict: \"${c}\" exists as a real file or directory`);\n }\n p.log.info('Remove or rename the conflicting paths, then re-run sync.');\n process.exit(1);\n }\n\n s.stop('No path conflicts');\n\n // --- Phase 4: Reconcile features ---\n s.start('Reconciling features…');\n\n const result = await reconcileFeatures(repoRoot, config, features);\n\n s.stop('Features reconciled');\n\n p.log.info(\n `Added: ${result.added} Updated: ${result.updated} Removed: ${result.removed}`\n );\n\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n p.outro(`Sync completed with ${result.errors.length} error(s).`);\n process.exit(1);\n }\n\n // --- Phase 5: Sync root files ---\n if (rootFiles.length > 0) {\n s.start('Syncing root files…');\n\n const rootResult = await syncRootFiles(repoRoot, rootFiles);\n\n for (const name of rootResult.synced) {\n p.log.info(`Root file synced: ${name}`);\n }\n for (const name of rootResult.removed) {\n p.log.info(`Root file removed: ${name}`);\n }\n for (const err of rootResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n\n s.stop('Root files synced');\n } else {\n // Clean up any managed root files when no sources provide them\n const rootResult = await syncRootFiles(repoRoot, []);\n for (const name of rootResult.removed) {\n p.log.info(`Root file removed: ${name}`);\n }\n }\n\n // --- Phase 6: Sync tool root entries ---\n s.start('Syncing tool root entries…');\n\n const toolRootResult = await reconcileToolRootEntries(\n repoRoot,\n config,\n toolRootEntries\n );\n\n if (\n toolRootResult.added > 0 ||\n toolRootResult.updated > 0 ||\n toolRootResult.removed > 0\n ) {\n p.log.info(\n `Tool root: Added: ${toolRootResult.added} Updated: ${toolRootResult.updated} Removed: ${toolRootResult.removed}`\n );\n }\n\n if (toolRootResult.errors.length > 0) {\n for (const err of toolRootResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n s.stop('Tool root entries synced with errors');\n p.outro(`Sync completed with ${toolRootResult.errors.length} error(s).`);\n process.exit(1);\n }\n\n s.stop('Tool root entries synced');\n\n p.outro('Sync complete.');\n}\n","import * as p from '@clack/prompts';\nimport { resolve } from 'node:path';\nimport {\n configExists,\n isRemoteSource,\n loadConfig,\n saveConfig,\n isOptedOut,\n removeOptOutMarker,\n OPT_OUT_MARKER,\n type BridgeConfig,\n type DomainConfig,\n type ToolConfig,\n type SourceConfig,\n} from '../lib/config.js';\nimport { findRepoRoot, isInGitRepo, installGitHooks } from '../lib/git.js';\nimport { listDomains, listDomainContents } from '../lib/manifest.js';\nimport { syncSource, resolveSourcePath, ensureBridgeGitignore } from '../lib/sources.js';\nimport { treeSelect } from '../lib/tree-prompt.js';\nimport { type TreeNode } from '../lib/tree.js';\nimport { VERSION } from '../lib/version.js';\nimport { syncCommand } from './sync.js';\n\nconst WELL_KNOWN_TOOLS = [\n { value: { name: 'vscode', folder: '.github' }, label: 'VS Code (.github/)' },\n { value: { name: 'cursor', folder: '.cursor' }, label: 'Cursor (.cursor/)' },\n { value: { name: 'claude', folder: '.claude' }, label: 'Claude (.claude/)' },\n { value: { name: 'pi', folder: '.pi' }, label: 'Pi (.pi/)' },\n];\n\nconst WELL_KNOWN_TOOL_MAP: Record<string, ToolConfig> = Object.fromEntries(\n WELL_KNOWN_TOOLS.map((t) => [t.value.name, t.value])\n);\n\nconst CUSTOM_TOOL_SENTINEL: ToolConfig = { name: '__custom__', folder: '__custom__' };\n\nexport interface InitOptions {\n force?: boolean;\n domains?: string;\n tools?: string;\n source?: string[];\n hooks?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Argument parsing (shared by interactive and non-interactive mode)\n// ---------------------------------------------------------------------------\n\n/**\n * Derive a short source name from a URL or local path.\n *\n * Examples:\n * https://github.com/org/repo.git → repo\n * git@github.com:org/repo.git → repo\n * file:///tmp/bare.git → bare\n * /path/to/my-folder → my-folder\n */\nexport function deriveSourceName(source: string): string {\n let segment = source;\n\n // SSH: git@host:org/repo.git → org/repo.git\n const sshMatch = segment.match(/^[\\w.-]+@[\\w.-]+:(.+)$/);\n if (sshMatch) segment = sshMatch[1];\n\n // Strip protocol + host for URLs\n try {\n const url = new URL(segment);\n segment = url.pathname;\n } catch {\n // not a URL — keep as-is (local path or already stripped)\n }\n\n // Take the last path component, strip trailing slashes and .git suffix\n const base = segment.replace(/\\/+$/, '').split('/').pop() ?? segment;\n return base.replace(/\\.git$/, '') || 'source';\n}\n\n/**\n * Parse a comma-separated `--tools` argument into ToolConfig[].\n * Accepts well-known names (cursor, vscode, claude) or `name:folder` pairs.\n */\nexport function parseToolsArg(input: string): ToolConfig[] {\n return input.split(',').map((t) => {\n const trimmed = t.trim();\n if (!trimmed) throw new Error('Empty tool name in --tools');\n\n if (WELL_KNOWN_TOOL_MAP[trimmed]) return WELL_KNOWN_TOOL_MAP[trimmed];\n\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx > 0) {\n return { name: trimmed.slice(0, colonIdx), folder: trimmed.slice(colonIdx + 1) };\n }\n\n throw new Error(\n `Unknown tool \"${trimmed}\". Use a known name (${Object.keys(WELL_KNOWN_TOOL_MAP).join(', ')}) or name:folder format.`\n );\n });\n}\n\n/**\n * Parse a single `--source` argument into a SourceConfig.\n * Supports `#branch` suffix for remote sources.\n */\nexport function parseSourceArg(input: string, repoRoot: string): SourceConfig {\n let source = input.trim();\n let branch: string | undefined;\n\n const hashIdx = source.lastIndexOf('#');\n if (hashIdx > 0) {\n branch = source.slice(hashIdx + 1);\n source = source.slice(0, hashIdx);\n }\n\n if (!source) throw new Error('Empty source in --source');\n\n const name = deriveSourceName(source);\n const entry: SourceConfig = { name, source };\n\n if (!isRemoteSource(entry.source)) {\n entry.source = resolve(repoRoot, entry.source);\n }\n\n if (branch) {\n entry.branch = branch;\n }\n\n return entry;\n}\n\n/**\n * Turn a per-domain selection into the `include` list stored in config.\n * Returns `undefined` when everything is selected (= sync the whole domain).\n * A fully selected feature type collapses to its name (`skills`).\n */\nexport function buildInclude(\n contents: { featureTypes: Array<{ name: string; features: string[] }>; files: string[] },\n selected: Set<string>\n): string[] | undefined {\n const include: string[] = [];\n let everything = true;\n\n for (const ft of contents.featureTypes) {\n const picked = ft.features.filter((f) => selected.has(`${ft.name}/${f}`));\n if (picked.length === ft.features.length) {\n include.push(ft.name);\n } else {\n everything = false;\n include.push(...picked.map((f) => `${ft.name}/${f}`));\n }\n }\n for (const file of contents.files) {\n if (selected.has(file)) include.push(file);\n else everything = false;\n }\n\n return everything ? undefined : include;\n}\n\n// ---------------------------------------------------------------------------\n// Shared steps\n// ---------------------------------------------------------------------------\n\nfunction cancelled(value: unknown): value is symbol {\n if (p.isCancel(value)) {\n p.cancel('Setup cancelled.');\n process.exit(1);\n }\n return false;\n}\n\n/** Clone remote sources / verify local ones. Exits on failure. */\nasync function fetchSources(repoRoot: string, sources: SourceConfig[]): Promise<void> {\n await ensureBridgeGitignore(repoRoot);\n const s = p.spinner();\n s.start('Fetching sources…');\n const results = await Promise.all(sources.map((src) => syncSource(repoRoot, src)));\n const errors = results.filter((r) => r.error);\n if (errors.length > 0) {\n s.stop('Some sources failed');\n for (const err of errors) p.log.error(`${err.name}: ${err.error}`);\n p.cancel('Fix the source URL/path and run `agent-bridge init` again.');\n process.exit(1);\n }\n s.stop(`${sources.length} source(s) ready`);\n}\n\nasync function maybeInstallHooks(repoRoot: string, force: boolean): Promise<void> {\n const hookResult = await installGitHooks(repoRoot, force);\n if (hookResult.installed.length > 0) {\n p.log.success(`Installed git hooks: ${hookResult.installed.join(', ')}`);\n }\n if (hookResult.skipped.length > 0) {\n p.log.warn(`Skipped hooks (existing non-Agent-Bridge hooks): ${hookResult.skipped.join(', ')}`);\n p.log.info('Re-run `agent-bridge init --force` to overwrite, or integrate manually.');\n }\n for (const e of hookResult.errors) {\n p.log.error(`Hook ${e.hook}: ${e.error}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Interactive prompts\n// ---------------------------------------------------------------------------\n\nasync function promptTools(): Promise<ToolConfig[]> {\n const selected = await p.multiselect({\n message: 'Which tools should receive synced files?',\n options: [...WELL_KNOWN_TOOLS, { value: CUSTOM_TOOL_SENTINEL, label: 'Other (add custom tool)' }],\n required: true,\n });\n cancelled(selected);\n\n const tools = (selected as ToolConfig[]).filter((t) => t.name !== CUSTOM_TOOL_SENTINEL.name);\n if (!(selected as ToolConfig[]).some((t) => t.name === CUSTOM_TOOL_SENTINEL.name)) return tools;\n\n for (;;) {\n const name = await p.text({\n message: 'Custom tool name (used for <tool>-- prefix matching)',\n placeholder: 'windsurf',\n validate: (v) => {\n if (!v.trim()) return 'Tool name cannot be empty';\n if (tools.some((t) => t.name === v.trim())) return 'Tool name already used';\n },\n });\n if (p.isCancel(name)) break;\n\n const folder = await p.text({\n message: `Target folder for \"${name}\"`,\n placeholder: `.${name}`,\n validate: (v) => {\n if (!v.trim()) return 'Folder cannot be empty';\n if (tools.some((t) => t.folder === v.trim())) return 'Folder already used by another tool';\n },\n });\n if (p.isCancel(folder)) break;\n\n tools.push({ name: name.trim(), folder: folder.trim() });\n\n const more = await p.confirm({ message: 'Add another custom tool?', initialValue: false });\n if (p.isCancel(more) || !more) break;\n }\n\n if (tools.length === 0) {\n p.cancel('At least one tool is required.');\n process.exit(1);\n }\n return tools;\n}\n\nasync function promptSources(repoRoot: string): Promise<SourceConfig[]> {\n const sources: SourceConfig[] = [];\n p.log.info('Add at least one source — a Git URL or a local folder that follows the domain layout.');\n\n for (;;) {\n const input = await p.text({\n message: sources.length === 0 ? 'Source URL or local path' : 'Another source URL or local path',\n placeholder: 'https://github.com/org/ai-hub.git',\n validate: (v) => {\n if (!v.trim()) return 'Source URL/path cannot be empty';\n const derived = deriveSourceName(v.trim());\n if (sources.some((s) => s.name === derived))\n return `Source name \"${derived}\" (derived from URL) already used`;\n },\n });\n if (p.isCancel(input)) {\n if (sources.length === 0) cancelled(input);\n break;\n }\n\n const entry = parseSourceArg(input, repoRoot);\n if (isRemoteSource(entry.source) && !entry.branch) {\n const branch = await p.text({\n message: 'Branch (leave empty for the remote default)',\n placeholder: 'main',\n defaultValue: '',\n });\n cancelled(branch);\n if ((branch as string).trim()) entry.branch = (branch as string).trim();\n }\n sources.push(entry);\n\n const more = await p.confirm({ message: 'Add another source?', initialValue: false });\n if (p.isCancel(more) || !more) break;\n }\n return sources;\n}\n\n/**\n * One checkbox tree: source → domain → feature type → feature (plus a\n * `files` group per domain). Ticking a node ticks everything beneath it.\n * Returns, per source, the picked domains with their `include` lists\n * (`undefined` include = whole domain).\n */\nasync function promptSelection(\n repoRoot: string,\n sources: SourceConfig[],\n toolNames: string[]\n): Promise<Map<string, DomainConfig[]>> {\n // Leaf value: `<source>\\u0000<domain>\\u0000<relPath>`; a domain with no\n // syncable content becomes a leaf with an empty relPath (= whole domain).\n const SEP = '\\u0000';\n const contentsByKey = new Map<string, Awaited<ReturnType<typeof listDomainContents>>>();\n const tree: TreeNode[] = [];\n\n for (const source of sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n const domains = await listDomains(srcPath);\n if (domains.length === 0) {\n p.log.warn(`${source.name}: no domain folders found — nothing to select.`);\n continue;\n }\n const domainNodes: TreeNode[] = [];\n for (const domain of domains) {\n const contents = await listDomainContents(srcPath, domain, toolNames);\n contentsByKey.set(`${source.name}${SEP}${domain}`, contents);\n const prefix = `${source.name}${SEP}${domain}${SEP}`;\n const children: TreeNode[] = contents.featureTypes\n .filter((ft) => ft.features.length > 0)\n .map((ft) => ({\n label: ft.name,\n hint: `(${ft.features.length})`,\n children: ft.features.map((f) => ({ label: f, value: `${prefix}${ft.name}/${f}` })),\n }));\n if (contents.files.length > 0) {\n children.push({ label: 'files', children: contents.files.map((f) => ({ label: f, value: `${prefix}${f}` })) });\n }\n const hint = contents.featureTypes\n .filter((ft) => ft.features.length > 0)\n .map((ft) => `${ft.features.length} ${ft.name}`)\n .join(', ');\n domainNodes.push(\n children.length > 0\n ? { label: domain, hint: hint ? `(${hint})` : undefined, children }\n : { label: domain, hint: '(empty)', value: prefix }\n );\n }\n tree.push({ label: source.name, children: domainNodes });\n }\n\n if (tree.length === 0) {\n p.cancel('No domains found in any source. Check the source layout: <source>/<domain>/<feature-type>/…');\n process.exit(1);\n }\n\n const picked = await treeSelect({\n message: 'What do you want to sync? Tick a domain to take all of it, or open it and pick pieces.',\n tree,\n expandDepth: 1,\n required: true,\n });\n cancelled(picked);\n\n // Group selected leaves by source/domain, then compress into include lists.\n const byDomain = new Map<string, Set<string>>();\n for (const value of picked as string[]) {\n const [sourceName, domain, rel] = value.split(SEP);\n const key = `${sourceName}${SEP}${domain}`;\n const set = byDomain.get(key) ?? new Set<string>();\n if (rel) set.add(rel);\n byDomain.set(key, set);\n }\n\n const result = new Map<string, DomainConfig[]>();\n for (const [key, rels] of byDomain) {\n const [sourceName, domain] = key.split(SEP);\n const contents = contentsByKey.get(key)!;\n const include = rels.size === 0 ? undefined : buildInclude(contents, rels);\n const list = result.get(sourceName) ?? [];\n list.push(include ? { name: domain, include } : { name: domain });\n result.set(sourceName, list);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport async function initCommand(cwd?: string, opts?: InitOptions): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n // Respect an opt-out tombstone so a postinstall guard doesn't reinstall.\n // `--force` clears it (deliberate re-opt-in).\n if (await isOptedOut(repoRoot)) {\n if (opts?.force) {\n await removeOptOutMarker(repoRoot);\n } else {\n p.log.warn(\n `${OPT_OUT_MARKER} present — Agent Bridge is opted out. ` +\n `Skipping init. Delete the file or run with --force to re-enable.`\n );\n return;\n }\n }\n\n const hasToolsArg = !!opts?.tools;\n const hasSourceArg = !!(opts?.source && opts.source.length > 0);\n\n if (hasToolsArg !== hasSourceArg) {\n p.log.error('Both --tools and --source are required for non-interactive init.');\n process.exit(1);\n }\n\n // --- Non-interactive mode ---\n if (hasToolsArg && hasSourceArg) {\n const tools = parseToolsArg(opts!.tools!);\n const sources = opts!.source!.map((s) => parseSourceArg(s, repoRoot));\n\n const seen = new Set<string>();\n for (const s of sources) {\n if (seen.has(s.name)) {\n throw new Error(`Duplicate source name \"${s.name}\" derived from --source arguments`);\n }\n seen.add(s.name);\n }\n\n await fetchSources(repoRoot, sources);\n\n const domainsArg = opts!.domains\n ? opts!.domains.split(',').map((d) => d.trim()).filter(Boolean)\n : undefined;\n for (const source of sources) {\n const names = domainsArg ?? (await listDomains(resolveSourcePath(repoRoot, source)));\n source.domains = names.map((name): DomainConfig => ({ name }));\n if (source.domains.length === 0) {\n p.log.warn(`${source.name}: no domains found — add some or pass --domains.`);\n }\n }\n\n const config: BridgeConfig = { version: VERSION, tools, sources };\n await saveConfig(repoRoot, config);\n p.log.success('Saved .agent-bridge/config.yml');\n\n if (opts!.hooks && isInGitRepo(repoRoot)) {\n await maybeInstallHooks(repoRoot, opts!.force === true);\n }\n\n p.outro('Done! Run `agent-bridge sync` to sync features.');\n return;\n }\n\n // --- Interactive mode ---\n p.intro('Agent Bridge — Project Setup');\n\n if (await configExists(repoRoot)) {\n const existing = await loadConfig(repoRoot);\n p.log.info(\n `Config already exists with ${existing.sources.length} source(s). Finishing this setup will overwrite it.`\n );\n }\n\n // 1. Tools\n const tools = await promptTools();\n\n // 2. Sources (then fetch them so we can show what's inside)\n const sources = await promptSources(repoRoot);\n await fetchSources(repoRoot, sources);\n\n // 3. One tree: domains and their contents, per source\n const picked = await promptSelection(repoRoot, sources, tools.map((t) => t.name));\n for (const source of sources) {\n source.domains = picked.get(source.name) ?? [];\n }\n // Sources without any picked domain contribute nothing — drop them.\n const activeSources = sources.filter((s) => (s.domains?.length ?? 0) > 0);\n for (const s of sources) {\n if (!activeSources.includes(s)) p.log.warn(`${s.name}: no domains selected — source dropped from config.`);\n }\n\n const config: BridgeConfig = { version: VERSION, tools, sources: activeSources };\n await saveConfig(repoRoot, config);\n p.log.success('Saved .agent-bridge/config.yml — commit this file.');\n\n // 4. Git hooks\n if (isInGitRepo(repoRoot)) {\n const installHooks = await p.confirm({\n message: 'Install git hooks to auto-sync after checkout/merge?',\n initialValue: false,\n });\n if (!p.isCancel(installHooks) && installHooks) {\n await maybeInstallHooks(repoRoot, opts?.force === true);\n }\n }\n\n // 5. Sync right away\n const syncNow = await p.confirm({ message: 'Run `agent-bridge sync` now?', initialValue: true });\n if (!p.isCancel(syncNow) && syncNow) {\n await syncCommand(repoRoot);\n return;\n }\n p.outro('Done! Run `agent-bridge sync` whenever you want to pull the latest features.');\n}\n","import * as p from '@clack/prompts';\nimport {\n bridgeDir,\n configExists,\n loadConfig,\n writeOptOutMarker,\n OPT_OUT_MARKER,\n type BridgeConfig,\n} from '../lib/config.js';\nimport { removeDir, dirExists } from '../lib/fs.js';\nimport { findRepoRoot, isInGitRepo, removeGitHooks } from '../lib/git.js';\nimport { reconcileFeatures, reconcileToolRootEntries } from '../lib/sync.js';\n\nfunction summarizeTools(config: BridgeConfig): string {\n return config.tools.map((t) => t.name).join(', ');\n}\n\nexport async function optOutCommand(\n cwd?: string,\n _opts?: unknown\n): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n p.intro('Agent Bridge Opt-out');\n\n const hasConfig = await configExists(repoRoot);\n let config: BridgeConfig | undefined;\n\n if (hasConfig) {\n config = await loadConfig(repoRoot);\n }\n\n const toolSummary = config ? summarizeTools(config) : 'unknown (no config found)';\n p.log.info(\n `Non-interactive opt-out: removing Agent Bridge managed files for tools: ${toolSummary}`\n );\n\n const s = p.spinner();\n\n let featureErrors = 0;\n let toolRootErrors = 0;\n\n if (config) {\n s.start('Removing synced Agent Bridge files…');\n\n const featureResult = await reconcileFeatures(repoRoot, config, []);\n const toolRootResult = await reconcileToolRootEntries(repoRoot, config, []);\n\n featureErrors = featureResult.errors.length;\n toolRootErrors = toolRootResult.errors.length;\n\n s.stop('Synced files removed');\n\n p.log.info(\n `Features removed: ${featureResult.removed} (errors: ${featureErrors})`\n );\n p.log.info(\n `Tool-root files removed: ${toolRootResult.removed} (errors: ${toolRootErrors})`\n );\n p.log.info('Root files are not removed by opt-out (manifest-only cleanup).');\n\n for (const err of featureResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n for (const err of toolRootResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n } else {\n p.log.warn('No .agent-bridge/config.yml found. Skipping synced file cleanup.');\n }\n\n s.start('Removing Agent Bridge git hooks…');\n const removedHooks = isInGitRepo(repoRoot) ? await removeGitHooks(repoRoot) : [];\n s.stop('Hooks cleanup complete');\n\n if (removedHooks.length > 0) {\n p.log.info(`Removed hooks: ${removedHooks.join(', ')}`);\n } else if (isInGitRepo(repoRoot)) {\n p.log.info('No Agent Bridge hooks found.');\n } else {\n p.log.info('Not a git repository; hook cleanup skipped.');\n }\n\n s.start('Removing .agent-bridge directory…');\n const bridgePath = bridgeDir(repoRoot);\n if (await dirExists(bridgePath)) {\n await removeDir(bridgePath);\n s.stop('.agent-bridge removed');\n } else {\n s.stop('.agent-bridge not found');\n }\n\n // Write a tombstone that survives `.agent-bridge/` deletion so a postinstall\n // guard (or a manual init/sync) won't silently reinstall on the next install.\n await writeOptOutMarker(repoRoot);\n p.log.info(\n `Wrote ${OPT_OUT_MARKER} (gitignored, local to this machine). ` +\n 'Force-add it (`git add -f`) for a repo-wide opt-out. ' +\n 'Run `agent-bridge init --force` to re-enable.'\n );\n\n const totalErrors = featureErrors + toolRootErrors;\n if (totalErrors > 0) {\n p.outro(`Opt-out completed with ${totalErrors} cleanup error(s).`);\n process.exit(1);\n }\n\n p.outro('Opt-out complete. Agent Bridge is removed from this repository.');\n}\n","#!/usr/bin/env node\n\nimport { resolve } from 'node:path';\nimport { stat } from 'node:fs/promises';\nimport { Command } from 'commander';\nimport { initCommand } from './commands/init.js';\nimport { syncCommand } from './commands/sync.js';\nimport { optOutCommand } from './commands/opt-out.js';\nimport { VERSION } from './lib/version.js';\n\nexport interface CliOptions {\n cwd?: string;\n force?: boolean;\n // Init-specific options (ignored by other commands)\n domains?: string;\n tools?: string;\n source?: string[];\n hooks?: boolean;\n}\n\nasync function assertCwdExists(cwd: string): Promise<void> {\n try {\n const s = await stat(cwd);\n if (!s.isDirectory()) {\n throw new Error(`--cwd path is not a directory: ${cwd}`);\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(`--cwd path does not exist: ${cwd}`);\n }\n throw err;\n }\n}\n\nasync function withCwdValidation(\n action: (cwd?: string, opts?: CliOptions) => Promise<void>\n): Promise<(opts: CliOptions) => Promise<void>> {\n return async (opts: CliOptions) => {\n if (opts.cwd) {\n opts.cwd = resolve(opts.cwd);\n await assertCwdExists(opts.cwd);\n }\n await action(opts.cwd, opts);\n };\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n previous.push(value);\n return previous;\n}\n\nconst program = new Command()\n .name('agent-bridge')\n .description('Manage AI tool configurations from multiple sources')\n .version(VERSION, '-v, --version');\n\nprogram\n .command('init')\n .description('Set up Agent Bridge: pick tools, sources and domains (creates .agent-bridge/config.yml)')\n .option('--cwd <path>', 'Override the working directory')\n .option('--force', 'Overwrite existing non-Agent-Bridge git hooks')\n .option('--domains <list>', 'Comma-separated domain list (default: every domain found in each source)')\n .option('--tools <list>', 'Comma-separated tool names (cursor,vscode,claude) or name:folder pairs')\n .option('-s, --source <url>', 'Source URL or path (repeatable, append #branch for branch)', collect, [])\n .option('--hooks', 'Auto-install git hooks without prompting')\n .action(await withCwdValidation(initCommand));\n\nprogram\n .command('sync')\n .description('Fetch the latest sources and sync features into your tool folders')\n .option('--cwd <path>', 'Override the working directory')\n .action(await withCwdValidation(syncCommand));\n\n// `update` was merged into `sync` (0.14.0). Kept hidden so hooks installed by\n// older versions (`agent-bridge update && agent-bridge sync`) keep working.\nprogram\n .command('update', { hidden: true })\n .option('--cwd <path>', 'Override the working directory')\n .action(\n await withCwdValidation(async (cwd) => {\n console.error('`agent-bridge update` is deprecated — running `agent-bridge sync` instead (it fetches sources too).');\n await syncCommand(cwd);\n })\n );\n\nprogram\n .command('opt-out')\n .description('Remove Agent Bridge hooks, synced files, and .agent-bridge state')\n .option('--cwd <path>', 'Override the working directory')\n .action(await withCwdValidation(optOutCommand));\n\nprogram.parse();\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sofatutor/agent-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Sync AI agent configurations (skills, agents, prompts) from shared sources into your project's tool directories.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"typecheck": "tsc --noEmit"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"@clack/core": "^0.4.1",
|
|
33
34
|
"@clack/prompts": "^0.9.1",
|
|
34
35
|
"commander": "^14.0.3",
|
|
35
36
|
"fs-extra": "^11.3.4",
|