unfairly 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +16 -7
- package/dist/cmd/init.d.ts +2 -3
- package/dist/cmd/init.d.ts.map +1 -1
- package/dist/cmd/init.js +60 -44
- package/dist/cmd/init.js.map +1 -1
- package/dist/cmd/pull.d.ts.map +1 -1
- package/dist/cmd/pull.js +16 -2
- package/dist/cmd/pull.js.map +1 -1
- package/dist/cmd/setup.d.ts +19 -19
- package/dist/cmd/setup.d.ts.map +1 -1
- package/dist/cmd/setup.js +267 -87
- package/dist/cmd/setup.js.map +1 -1
- package/dist/cmd/status-card.d.ts +2 -0
- package/dist/cmd/status-card.d.ts.map +1 -0
- package/dist/cmd/status-card.js +156 -0
- package/dist/cmd/status-card.js.map +1 -0
- package/dist/cmd/status.d.ts.map +1 -1
- package/dist/cmd/status.js +3 -0
- package/dist/cmd/status.js.map +1 -1
- package/dist/hosts/claude-code.d.ts +48 -10
- package/dist/hosts/claude-code.d.ts.map +1 -1
- package/dist/hosts/claude-code.js +102 -29
- package/dist/hosts/claude-code.js.map +1 -1
- package/dist/hosts/claude-desktop.d.ts +47 -0
- package/dist/hosts/claude-desktop.d.ts.map +1 -0
- package/dist/hosts/claude-desktop.js +88 -0
- package/dist/hosts/claude-desktop.js.map +1 -0
- package/dist/index.js +58 -27
- package/dist/index.js.map +1 -1
- package/dist/sync/repo-root.d.ts +7 -0
- package/dist/sync/repo-root.d.ts.map +1 -0
- package/dist/sync/repo-root.js +24 -0
- package/dist/sync/repo-root.js.map +1 -0
- package/dist/sync/stitch-claude-md.d.ts +9 -0
- package/dist/sync/stitch-claude-md.d.ts.map +1 -0
- package/dist/sync/stitch-claude-md.js +48 -0
- package/dist/sync/stitch-claude-md.js.map +1 -0
- package/dist/ui/colors.d.ts +17 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +28 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/ui/screens.d.ts +44 -0
- package/dist/ui/screens.d.ts.map +1 -0
- package/dist/ui/screens.js +135 -0
- package/dist/ui/screens.js.map +1 -0
- package/package.json +32 -7
package/dist/cmd/status.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/cmd/status.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/cmd/status.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;gBACtE,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,GAAG;oBAAE,QAAQ,EAAE,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACZ,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,CAAC,cAAc,IAAI,OAAO,CAAC;QAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,cAAc,IAAI,OAAO,CAAC;QAC3C,OAAO,CAAC,GAAG,CACT,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,MAAM,aAAa,MAAM,eAAe,QAAQ,EAAE,CAChF,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export type ClaudeCodeResult = {
|
|
2
2
|
status: "configured";
|
|
3
|
-
|
|
4
|
-
} | {
|
|
5
|
-
status: "already-registered";
|
|
3
|
+
path: string;
|
|
6
4
|
message: string;
|
|
7
5
|
} | {
|
|
8
6
|
status: "not-installed";
|
|
@@ -10,14 +8,54 @@ export type ClaudeCodeResult = {
|
|
|
10
8
|
status: "error";
|
|
11
9
|
message: string;
|
|
12
10
|
};
|
|
11
|
+
export type ClaudeCodeEntry = {
|
|
12
|
+
type: "http";
|
|
13
|
+
url: string;
|
|
14
|
+
headers: {
|
|
15
|
+
Authorization: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
type ClaudeConfig = {
|
|
19
|
+
mcpServers?: Record<string, unknown>;
|
|
20
|
+
projects?: Record<string, {
|
|
21
|
+
mcpServers?: Record<string, unknown>;
|
|
22
|
+
}>;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Path to Claude Code's user-level config. Returns null on Linux only if
|
|
27
|
+
* HOME is unset (effectively never).
|
|
28
|
+
*/
|
|
29
|
+
export declare function configPath(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Build the canonical user-scope unfairly entry. Inlines the literal token —
|
|
32
|
+
* matches the Claude Desktop pattern. Keeps the token out of any process
|
|
33
|
+
* argv (the previous `claude mcp add --header=…` form leaked it via
|
|
34
|
+
* `/proc/<pid>/cmdline` for the lifetime of the subprocess).
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildEntry(apiUrl: string, token: string): ClaudeCodeEntry;
|
|
37
|
+
/**
|
|
38
|
+
* Merge our canonical unfairly entry into ~/.claude.json:
|
|
39
|
+
* - Writes the user-scope entry at root `mcpServers.unfairly`.
|
|
40
|
+
* - Strips any project-scoped `projects[<cwd>].mcpServers.unfairly` left over
|
|
41
|
+
* from previous local-scope runs of setup. Without this, a stale entry in
|
|
42
|
+
* the active project would shadow the user-scope one and silently keep an
|
|
43
|
+
* old (or empty) bearer.
|
|
44
|
+
* - Preserves all unrelated keys.
|
|
45
|
+
*/
|
|
46
|
+
export declare function mergeIntoConfig(existing: ClaudeConfig, apiUrl: string, token: string): ClaudeConfig;
|
|
13
47
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
48
|
+
* Wire Unfairly into Claude Code by mutating ~/.claude.json directly. We
|
|
49
|
+
* intentionally do NOT shell out to `claude mcp add --header=…` — that form
|
|
50
|
+
* exposes the bearer token to `ps aux` and `/proc/<pid>/cmdline` for the
|
|
51
|
+
* duration of the subprocess. Same threat model as Claude Desktop, same
|
|
52
|
+
* mitigation: write the JSON ourselves.
|
|
18
53
|
*
|
|
19
|
-
* Skips silently
|
|
20
|
-
*
|
|
54
|
+
* Skips silently only when the `claude` binary is not in PATH. If the binary
|
|
55
|
+
* exists but `~/.claude.json` doesn't yet (fresh install, before first
|
|
56
|
+
* launch of Claude Code), we create the file with our entry — that's what
|
|
57
|
+
* `claude mcp add` would have done implicitly.
|
|
21
58
|
*/
|
|
22
|
-
export declare function configureClaudeCode(apiUrl: string): Promise<ClaudeCodeResult>;
|
|
59
|
+
export declare function configureClaudeCode(apiUrl: string, token: string): Promise<ClaudeCodeResult>;
|
|
60
|
+
export {};
|
|
23
61
|
//# sourceMappingURL=claude-code.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/hosts/claude-code.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/hosts/claude-code.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,gBAAgB,GACxB;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,MAAM,EAAE,eAAe,CAAA;CAAE,GAC3B;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACpC,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACpE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAMzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,YAAY,CAuBd;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,CAAC,CA6C3B"}
|
|
@@ -1,44 +1,117 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
2
5
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
* Path to Claude Code's user-level config. Returns null on Linux only if
|
|
7
|
+
* HOME is unset (effectively never).
|
|
8
|
+
*/
|
|
9
|
+
export function configPath() {
|
|
10
|
+
return path.join(os.homedir(), ".claude.json");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build the canonical user-scope unfairly entry. Inlines the literal token —
|
|
14
|
+
* matches the Claude Desktop pattern. Keeps the token out of any process
|
|
15
|
+
* argv (the previous `claude mcp add --header=…` form leaked it via
|
|
16
|
+
* `/proc/<pid>/cmdline` for the lifetime of the subprocess).
|
|
17
|
+
*/
|
|
18
|
+
export function buildEntry(apiUrl, token) {
|
|
19
|
+
return {
|
|
20
|
+
type: "http",
|
|
21
|
+
url: `${apiUrl.replace(/\/$/, "")}/api/mcp`,
|
|
22
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Merge our canonical unfairly entry into ~/.claude.json:
|
|
27
|
+
* - Writes the user-scope entry at root `mcpServers.unfairly`.
|
|
28
|
+
* - Strips any project-scoped `projects[<cwd>].mcpServers.unfairly` left over
|
|
29
|
+
* from previous local-scope runs of setup. Without this, a stale entry in
|
|
30
|
+
* the active project would shadow the user-scope one and silently keep an
|
|
31
|
+
* old (or empty) bearer.
|
|
32
|
+
* - Preserves all unrelated keys.
|
|
33
|
+
*/
|
|
34
|
+
export function mergeIntoConfig(existing, apiUrl, token) {
|
|
35
|
+
const next = { ...existing };
|
|
36
|
+
const servers = { ...(existing.mcpServers ?? {}) };
|
|
37
|
+
servers.unfairly = buildEntry(apiUrl, token);
|
|
38
|
+
next.mcpServers = servers;
|
|
39
|
+
if (existing.projects) {
|
|
40
|
+
const projects = {};
|
|
41
|
+
for (const [k, v] of Object.entries(existing.projects)) {
|
|
42
|
+
// Only touch the project entry when it actually has an `unfairly`
|
|
43
|
+
// mcpServer to strip. Preserves the original shape (including the
|
|
44
|
+
// absence of `mcpServers` entirely) for unrelated projects.
|
|
45
|
+
if (v.mcpServers && "unfairly" in v.mcpServers) {
|
|
46
|
+
const ps = { ...v.mcpServers };
|
|
47
|
+
delete ps.unfairly;
|
|
48
|
+
projects[k] = { ...v, mcpServers: ps };
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
projects[k] = v;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
next.projects = projects;
|
|
55
|
+
}
|
|
56
|
+
return next;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Wire Unfairly into Claude Code by mutating ~/.claude.json directly. We
|
|
60
|
+
* intentionally do NOT shell out to `claude mcp add --header=…` — that form
|
|
61
|
+
* exposes the bearer token to `ps aux` and `/proc/<pid>/cmdline` for the
|
|
62
|
+
* duration of the subprocess. Same threat model as Claude Desktop, same
|
|
63
|
+
* mitigation: write the JSON ourselves.
|
|
7
64
|
*
|
|
8
|
-
* Skips silently
|
|
9
|
-
*
|
|
65
|
+
* Skips silently only when the `claude` binary is not in PATH. If the binary
|
|
66
|
+
* exists but `~/.claude.json` doesn't yet (fresh install, before first
|
|
67
|
+
* launch of Claude Code), we create the file with our entry — that's what
|
|
68
|
+
* `claude mcp add` would have done implicitly.
|
|
10
69
|
*/
|
|
11
|
-
export async function configureClaudeCode(apiUrl) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (e.code === "ENOENT") {
|
|
70
|
+
export async function configureClaudeCode(apiUrl, token) {
|
|
71
|
+
const probe = spawnSync("claude", ["--version"], { encoding: "utf-8" });
|
|
72
|
+
if (probe.error) {
|
|
73
|
+
const e = probe.error;
|
|
74
|
+
if (e.code === "ENOENT")
|
|
17
75
|
return { status: "not-installed" };
|
|
18
|
-
}
|
|
19
76
|
return { status: "error", message: e.message };
|
|
20
77
|
}
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
78
|
+
const cfgPath = configPath();
|
|
79
|
+
let raw;
|
|
80
|
+
try {
|
|
81
|
+
raw = await fs.readFile(cfgPath, "utf8");
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
const e = err;
|
|
85
|
+
if (e.code === "ENOENT") {
|
|
86
|
+
// Fresh Claude Code install — binary exists but config not yet
|
|
87
|
+
// created. Treat as empty and let the writeFile below create it.
|
|
88
|
+
raw = "";
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
return { status: "error", message: `Could not read ${cfgPath}: ${e.message}` };
|
|
92
|
+
}
|
|
29
93
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
94
|
+
let parsed;
|
|
95
|
+
try {
|
|
96
|
+
parsed = raw.trim() === "" ? {} : JSON.parse(raw);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
34
99
|
return {
|
|
35
|
-
status: "
|
|
36
|
-
message:
|
|
100
|
+
status: "error",
|
|
101
|
+
message: `Refusing to overwrite — config is not valid JSON: ${err.message}`,
|
|
37
102
|
};
|
|
38
103
|
}
|
|
104
|
+
const merged = mergeIntoConfig(parsed, apiUrl, token);
|
|
105
|
+
try {
|
|
106
|
+
await fs.writeFile(cfgPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
return { status: "error", message: err.message };
|
|
110
|
+
}
|
|
39
111
|
return {
|
|
40
|
-
status: "
|
|
41
|
-
|
|
112
|
+
status: "configured",
|
|
113
|
+
path: cfgPath,
|
|
114
|
+
message: "wired unfairly into ~/.claude.json (user scope, literal token)",
|
|
42
115
|
};
|
|
43
116
|
}
|
|
44
117
|
//# sourceMappingURL=claude-code.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/hosts/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/hosts/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type ClaudeDesktopResult = {
|
|
2
|
+
status: "configured";
|
|
3
|
+
path: string;
|
|
4
|
+
action: "created" | "updated";
|
|
5
|
+
} | {
|
|
6
|
+
status: "not-installed";
|
|
7
|
+
path: string;
|
|
8
|
+
} | {
|
|
9
|
+
status: "error";
|
|
10
|
+
path: string;
|
|
11
|
+
message: string;
|
|
12
|
+
};
|
|
13
|
+
export type UnfairlyEntry = {
|
|
14
|
+
type: "http";
|
|
15
|
+
url: string;
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
type ConfigShape = {
|
|
21
|
+
mcpServers?: Record<string, unknown>;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Path to Claude Desktop's config file by platform. Returns null on Linux
|
|
26
|
+
* (Claude Desktop is Mac/Windows only per official docs).
|
|
27
|
+
*/
|
|
28
|
+
export declare function configPath(): string | null;
|
|
29
|
+
export declare function buildEntry(apiUrl: string, token: string): UnfairlyEntry;
|
|
30
|
+
/**
|
|
31
|
+
* Merge our canonical unfairly entry into an existing config object.
|
|
32
|
+
* - Preserves other mcpServers
|
|
33
|
+
* - Replaces any existing unfairly entry (including stale tokens / placeholder)
|
|
34
|
+
* - Preserves all unrelated top-level keys
|
|
35
|
+
*/
|
|
36
|
+
export declare function mergeIntoConfig(existing: ConfigShape, apiUrl: string, token: string): {
|
|
37
|
+
mcpServers: Record<string, unknown>;
|
|
38
|
+
} & ConfigShape;
|
|
39
|
+
/**
|
|
40
|
+
* Patch Claude Desktop's config to wire the Unfairly MCP server.
|
|
41
|
+
* - If config file does not exist → not-installed (do NOT create).
|
|
42
|
+
* - If file exists but is malformed JSON → error (don't overwrite precious config).
|
|
43
|
+
* - Otherwise → merge canonical block, write back.
|
|
44
|
+
*/
|
|
45
|
+
export declare function configureClaudeDesktop(apiUrl: string, token: string): Promise<ClaudeDesktopResult>;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=claude-desktop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-desktop.d.ts","sourceRoot":"","sources":["../../src/hosts/claude-desktop.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAC3B;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,GACrE;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACpC,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAU1C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAYvE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ;IAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,WAAW,CAMvD;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,mBAAmB,CAAC,CAqC9B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Path to Claude Desktop's config file by platform. Returns null on Linux
|
|
6
|
+
* (Claude Desktop is Mac/Windows only per official docs).
|
|
7
|
+
*/
|
|
8
|
+
export function configPath() {
|
|
9
|
+
const home = os.homedir();
|
|
10
|
+
if (process.platform === "darwin") {
|
|
11
|
+
return path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
12
|
+
}
|
|
13
|
+
if (process.platform === "win32") {
|
|
14
|
+
const appdata = process.env.APPDATA ?? path.join(home, "AppData", "Roaming");
|
|
15
|
+
return path.join(appdata, "Claude", "claude_desktop_config.json");
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
export function buildEntry(apiUrl, token) {
|
|
20
|
+
const url = `${apiUrl.replace(/\/$/, "")}/api/mcp`;
|
|
21
|
+
// Inline the literal token. macOS GUI apps don't inherit env vars from
|
|
22
|
+
// ~/.zshenv (only LaunchAgent env via `launchctl setenv`), so the
|
|
23
|
+
// `${UNFAIRLY_MCP_TOKEN}` placeholder pattern silently fails. Claude Desktop
|
|
24
|
+
// already stores credentials on disk for other MCP servers — this is the
|
|
25
|
+
// standard pattern.
|
|
26
|
+
return {
|
|
27
|
+
type: "http",
|
|
28
|
+
url,
|
|
29
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Merge our canonical unfairly entry into an existing config object.
|
|
34
|
+
* - Preserves other mcpServers
|
|
35
|
+
* - Replaces any existing unfairly entry (including stale tokens / placeholder)
|
|
36
|
+
* - Preserves all unrelated top-level keys
|
|
37
|
+
*/
|
|
38
|
+
export function mergeIntoConfig(existing, apiUrl, token) {
|
|
39
|
+
const next = { ...existing };
|
|
40
|
+
const servers = { ...(existing.mcpServers ?? {}) };
|
|
41
|
+
servers.unfairly = buildEntry(apiUrl, token);
|
|
42
|
+
next.mcpServers = servers;
|
|
43
|
+
return next;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Patch Claude Desktop's config to wire the Unfairly MCP server.
|
|
47
|
+
* - If config file does not exist → not-installed (do NOT create).
|
|
48
|
+
* - If file exists but is malformed JSON → error (don't overwrite precious config).
|
|
49
|
+
* - Otherwise → merge canonical block, write back.
|
|
50
|
+
*/
|
|
51
|
+
export async function configureClaudeDesktop(apiUrl, token) {
|
|
52
|
+
const cfgPath = configPath();
|
|
53
|
+
if (!cfgPath) {
|
|
54
|
+
return { status: "not-installed", path: "(Claude Desktop not available on this platform)" };
|
|
55
|
+
}
|
|
56
|
+
let existing;
|
|
57
|
+
try {
|
|
58
|
+
existing = await fs.readFile(cfgPath, "utf8");
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const e = err;
|
|
62
|
+
if (e.code === "ENOENT")
|
|
63
|
+
return { status: "not-installed", path: cfgPath };
|
|
64
|
+
return { status: "error", path: cfgPath, message: `Could not read ${cfgPath}: ${e.message}` };
|
|
65
|
+
}
|
|
66
|
+
let parsed;
|
|
67
|
+
try {
|
|
68
|
+
parsed = existing.trim() === "" ? {} : JSON.parse(existing);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
return {
|
|
72
|
+
status: "error",
|
|
73
|
+
path: cfgPath,
|
|
74
|
+
message: `Refusing to overwrite — config is not valid JSON: ${err.message}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const had = parsed.mcpServers && parsed.mcpServers.unfairly !== undefined;
|
|
78
|
+
const merged = mergeIntoConfig(parsed, apiUrl, token);
|
|
79
|
+
const serialized = JSON.stringify(merged, null, 2) + "\n";
|
|
80
|
+
try {
|
|
81
|
+
await fs.writeFile(cfgPath, serialized, "utf8");
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
return { status: "error", path: cfgPath, message: err.message };
|
|
85
|
+
}
|
|
86
|
+
return { status: "configured", path: cfgPath, action: had ? "updated" : "created" };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=claude-desktop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-desktop.js","sourceRoot":"","sources":["../../src/hosts/claude-desktop.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
import { promises as fs } from "node:fs";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { dirname, join } from "node:path";
|
|
5
6
|
import yargs from "yargs";
|
|
@@ -7,37 +8,28 @@ import { hideBin } from "yargs/helpers";
|
|
|
7
8
|
const pkg = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"));
|
|
8
9
|
await yargs(hideBin(process.argv))
|
|
9
10
|
.scriptName("unfairly")
|
|
10
|
-
.usage("$0
|
|
11
|
+
.usage("$0 [cmd] [args]")
|
|
11
12
|
.version(pkg.version)
|
|
12
|
-
.command("
|
|
13
|
-
const { runSetup } = await import("./cmd/setup.js");
|
|
14
|
-
await runSetup();
|
|
15
|
-
})
|
|
16
|
-
.command("login", "Authorize via browser; print export lines for ~/.zshenv (Codex / scripts)", (y) => y.option("quiet", {
|
|
13
|
+
.command("$0", "First-run: wire MCP across hosts, bind workspace if in a repo, show status otherwise", (y) => y.option("yes", {
|
|
17
14
|
type: "boolean",
|
|
18
|
-
alias: "
|
|
15
|
+
alias: "y",
|
|
19
16
|
default: false,
|
|
20
|
-
describe: "
|
|
17
|
+
describe: "Skip confirmation prompts",
|
|
21
18
|
}), async (argv) => {
|
|
22
|
-
|
|
23
|
-
await runLogin({ quiet: argv.quiet });
|
|
19
|
+
await runBare({ yes: argv.yes });
|
|
24
20
|
})
|
|
25
|
-
.command("
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
describe: "mcp_* token for non-interactive runs. Or set UNFAIRLY_TOKEN env. Default: browser OAuth.",
|
|
21
|
+
.command("setup", "Wire MCP for Claude Desktop / Claude Code / Codex on this machine", (y) => y.option("yes", { type: "boolean", alias: "y", default: false }), async (argv) => {
|
|
22
|
+
const { runSetup } = await import("./cmd/setup.js");
|
|
23
|
+
await runSetup({ chainInit: false, yes: argv.yes });
|
|
29
24
|
})
|
|
30
|
-
.option("
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
default: false,
|
|
34
|
-
describe: "Skip the .unfairly/ overwrite confirmation",
|
|
25
|
+
.command("login", "Authorize via browser; print export lines for ~/.zshenv", (y) => y.option("quiet", { type: "boolean", alias: "q", default: false }), async (argv) => {
|
|
26
|
+
const { runLogin } = await import("./cmd/login.js");
|
|
27
|
+
await runLogin({ quiet: argv.quiet });
|
|
35
28
|
})
|
|
36
|
-
.
|
|
37
|
-
type: "
|
|
38
|
-
default: false
|
|
39
|
-
|
|
40
|
-
}), async (argv) => {
|
|
29
|
+
.command("init", "Bind .unfairly/ to cwd and pull all accessible vaults", (y) => y
|
|
30
|
+
.option("token", { type: "string" })
|
|
31
|
+
.option("yes", { type: "boolean", alias: "y", default: false })
|
|
32
|
+
.option("no-browser", { type: "boolean", default: false }), async (argv) => {
|
|
41
33
|
const { runInit } = await import("./cmd/init.js");
|
|
42
34
|
await runInit({
|
|
43
35
|
token: argv.token,
|
|
@@ -61,16 +53,55 @@ await yargs(hideBin(process.argv))
|
|
|
61
53
|
const { runResolve } = await import("./cmd/resolve.js");
|
|
62
54
|
await runResolve(argv.path);
|
|
63
55
|
})
|
|
64
|
-
.command("watch", "Watch .unfairly/ for changes
|
|
56
|
+
.command("watch", "Watch .unfairly/ for changes; debounced auto-push", () => { }, async () => {
|
|
65
57
|
const { runWatch } = await import("./cmd/watch.js");
|
|
66
58
|
await runWatch();
|
|
67
59
|
})
|
|
68
|
-
.command("mcp", "Run as
|
|
60
|
+
.command("mcp", "Run as stdio MCP server (sync tools)", () => { }, async () => {
|
|
69
61
|
const { runMcp } = await import("./cmd/mcp.js");
|
|
70
62
|
await runMcp();
|
|
71
63
|
})
|
|
72
|
-
.demandCommand(1)
|
|
73
64
|
.strict()
|
|
74
65
|
.help()
|
|
75
66
|
.parse();
|
|
67
|
+
async function runBare(opts) {
|
|
68
|
+
const startCwd = process.cwd();
|
|
69
|
+
const { getToken } = await import("./sync/credentials.js");
|
|
70
|
+
const { findRepoRoot } = await import("./sync/repo-root.js");
|
|
71
|
+
const repoRoot = await findRepoRoot(startCwd);
|
|
72
|
+
// If we found a repo root and we're in a subdir, chdir up so init/pull/
|
|
73
|
+
// stitch all see the same .unfairly/ at the repo root. Explicit subcommands
|
|
74
|
+
// (`unfairly init`) keep their existing cwd-relative behavior — only the
|
|
75
|
+
// bare entrypoint promotes to repoRoot.
|
|
76
|
+
if (repoRoot && repoRoot !== startCwd) {
|
|
77
|
+
process.chdir(repoRoot);
|
|
78
|
+
}
|
|
79
|
+
const effectiveCwd = process.cwd();
|
|
80
|
+
const token = await getToken();
|
|
81
|
+
const manifestExists = await pathExists(join(effectiveCwd, ".unfairly", "manifest.json"));
|
|
82
|
+
if (!token) {
|
|
83
|
+
// Cold install — full setup, chain init when in repo
|
|
84
|
+
const { runSetup } = await import("./cmd/setup.js");
|
|
85
|
+
await runSetup({ chainInit: Boolean(repoRoot), yes: opts.yes });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!manifestExists && repoRoot) {
|
|
89
|
+
// Token exists, no workspace bound, in a repo — offer init only
|
|
90
|
+
const { runInit } = await import("./cmd/init.js");
|
|
91
|
+
await runInit({ yes: opts.yes });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Daily check-in
|
|
95
|
+
const { runStatusCard } = await import("./cmd/status-card.js");
|
|
96
|
+
await runStatusCard();
|
|
97
|
+
}
|
|
98
|
+
async function pathExists(p) {
|
|
99
|
+
try {
|
|
100
|
+
await fs.access(p);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
76
107
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CACV,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,EACnE,MAAM,CACP,CACF,CAAC;AAEF,MAAM,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KAC/B,UAAU,CAAC,UAAU,CAAC;KACtB,KAAK,CAAC,iBAAiB,CAAC;KACxB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;KACpB,OAAO,CACN,OAAO,EACP,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk up from start until we find a directory containing `.git`. Returns
|
|
3
|
+
* the directory, or null if we reach the filesystem root with no hit.
|
|
4
|
+
* Accepts `.git` as either dir or file (worktrees use a `.git` file).
|
|
5
|
+
*/
|
|
6
|
+
export declare function findRepoRoot(start: string): Promise<string | null>;
|
|
7
|
+
//# sourceMappingURL=repo-root.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-root.d.ts","sourceRoot":"","sources":["../../src/sync/repo-root.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Walk up from start until we find a directory containing `.git`. Returns
|
|
5
|
+
* the directory, or null if we reach the filesystem root with no hit.
|
|
6
|
+
* Accepts `.git` as either dir or file (worktrees use a `.git` file).
|
|
7
|
+
*/
|
|
8
|
+
export async function findRepoRoot(start) {
|
|
9
|
+
let dir = path.resolve(start);
|
|
10
|
+
while (true) {
|
|
11
|
+
try {
|
|
12
|
+
await fs.stat(path.join(dir, ".git"));
|
|
13
|
+
return dir;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// not here, keep walking
|
|
17
|
+
}
|
|
18
|
+
const parent = path.dirname(dir);
|
|
19
|
+
if (parent === dir)
|
|
20
|
+
return null;
|
|
21
|
+
dir = parent;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=repo-root.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-root.js","sourceRoot":"","sources":["../../src/sync/repo-root.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa;IAC9C,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;YACtC,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const AUTOLOADER_LINE = "@.unfairly/CLAUDE.md";
|
|
2
|
+
export declare const SENTINEL = "<!-- unfairly: autoloader -->";
|
|
3
|
+
export type StitchResult = {
|
|
4
|
+
action: "created" | "appended" | "noop" | "not-a-repo" | "error";
|
|
5
|
+
path: string;
|
|
6
|
+
message?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function stitchClaudeMd(repoRoot: string): Promise<StitchResult>;
|
|
9
|
+
//# sourceMappingURL=stitch-claude-md.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stitch-claude-md.d.ts","sourceRoot":"","sources":["../../src/sync/stitch-claude-md.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,yBAAyB,CAAC;AACtD,eAAO,MAAM,QAAQ,kCAAkC,CAAC;AAExD,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,YAAY,GAAG,OAAO,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAIF,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAwC5E"}
|