unfairly 0.8.2 → 0.8.4

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.
Files changed (218) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +15 -0
  3. package/README.md +3 -1
  4. package/dist/auth/browser-flow.d.ts +0 -1
  5. package/dist/auth/browser-flow.js +5 -81
  6. package/dist/cmd/connect.d.ts +0 -1
  7. package/dist/cmd/connect.js +1 -164
  8. package/dist/cmd/init.d.ts +0 -1
  9. package/dist/cmd/init.js +2 -138
  10. package/dist/cmd/invite.d.ts +0 -1
  11. package/dist/cmd/invite.js +4 -73
  12. package/dist/cmd/join.d.ts +0 -1
  13. package/dist/cmd/join.js +1 -91
  14. package/dist/cmd/login.d.ts +0 -1
  15. package/dist/cmd/login.js +3 -41
  16. package/dist/cmd/mcp.d.ts +0 -1
  17. package/dist/cmd/mcp.js +2 -86
  18. package/dist/cmd/pace.d.ts +23 -2
  19. package/dist/cmd/pace.js +4 -393
  20. package/dist/cmd/parse-share-input.d.ts +0 -1
  21. package/dist/cmd/parse-share-input.js +1 -56
  22. package/dist/cmd/pull.d.ts +0 -1
  23. package/dist/cmd/pull.js +7 -143
  24. package/dist/cmd/push.d.ts +0 -1
  25. package/dist/cmd/push.js +1 -85
  26. package/dist/cmd/resolve.d.ts +0 -1
  27. package/dist/cmd/resolve.js +1 -35
  28. package/dist/cmd/setup.d.ts +0 -1
  29. package/dist/cmd/setup.js +2 -345
  30. package/dist/cmd/status-card.d.ts +0 -1
  31. package/dist/cmd/status-card.js +2 -156
  32. package/dist/cmd/status.d.ts +0 -1
  33. package/dist/cmd/status.js +3 -29
  34. package/dist/cmd/watch.d.ts +0 -1
  35. package/dist/cmd/watch.js +1 -26
  36. package/dist/hosts/claude-code.d.ts +0 -1
  37. package/dist/hosts/claude-code.js +3 -180
  38. package/dist/hosts/claude-desktop.d.ts +0 -1
  39. package/dist/hosts/claude-desktop.js +3 -222
  40. package/dist/hosts/codex.d.ts +0 -1
  41. package/dist/hosts/codex.js +20 -234
  42. package/dist/hosts/shell-env.d.ts +0 -1
  43. package/dist/hosts/shell-env.js +9 -81
  44. package/dist/index.d.ts +0 -1
  45. package/dist/index.js +14 -206
  46. package/dist/lib/client.d.ts +0 -1
  47. package/dist/lib/client.js +1 -58
  48. package/dist/lib/events/emit.d.ts +0 -1
  49. package/dist/lib/events/emit.js +1 -28
  50. package/dist/lib/index.d.ts +0 -1
  51. package/dist/lib/index.js +1 -11
  52. package/dist/lib/transport.d.ts +0 -1
  53. package/dist/lib/transport.js +1 -85
  54. package/dist/lib/types/index.d.ts +0 -1
  55. package/dist/lib/types/index.js +1 -50
  56. package/dist/lib/vault/checkpoint.d.ts +0 -1
  57. package/dist/lib/vault/checkpoint.js +1 -18
  58. package/dist/lib/vault/read.d.ts +0 -1
  59. package/dist/lib/vault/read.js +1 -9
  60. package/dist/lib/vault/search.d.ts +0 -1
  61. package/dist/lib/vault/search.js +1 -10
  62. package/dist/pace/device.d.ts +3 -1
  63. package/dist/pace/device.js +2 -148
  64. package/dist/pace/enroll.d.ts +79 -0
  65. package/dist/pace/enroll.js +2 -0
  66. package/dist/pace/identity.d.ts +0 -1
  67. package/dist/pace/identity.js +1 -17
  68. package/dist/pace/install.d.ts +0 -1
  69. package/dist/pace/install.js +4 -127
  70. package/dist/pace/local-evidence.d.ts +0 -1
  71. package/dist/pace/local-evidence.js +2 -228
  72. package/dist/pace/local-sessions.d.ts +0 -1
  73. package/dist/pace/local-sessions.js +1 -254
  74. package/dist/pace/managed-config.d.ts +0 -1
  75. package/dist/pace/managed-config.js +8 -102
  76. package/dist/pace/normalize.d.ts +0 -1
  77. package/dist/pace/normalize.js +1 -293
  78. package/dist/pace/repository.d.ts +0 -1
  79. package/dist/pace/repository.js +2 -120
  80. package/dist/pace/schema.d.ts +0 -1
  81. package/dist/pace/schema.js +1 -134
  82. package/dist/pace/session-summary.d.ts +0 -1
  83. package/dist/pace/session-summary.js +2 -140
  84. package/dist/pace/spool.d.ts +0 -1
  85. package/dist/pace/spool.js +8 -87
  86. package/dist/pace-runtime.d.ts +1 -0
  87. package/dist/pace-runtime.js +2 -0
  88. package/dist/sync/api.d.ts +0 -1
  89. package/dist/sync/api.js +1 -55
  90. package/dist/sync/autoloader.d.ts +0 -1
  91. package/dist/sync/autoloader.js +5 -127
  92. package/dist/sync/conflict.d.ts +0 -1
  93. package/dist/sync/conflict.js +5 -15
  94. package/dist/sync/credentials.d.ts +0 -1
  95. package/dist/sync/credentials.js +1 -51
  96. package/dist/sync/files.d.ts +0 -1
  97. package/dist/sync/files.js +1 -71
  98. package/dist/sync/manifest.d.ts +0 -1
  99. package/dist/sync/manifest.js +1 -37
  100. package/dist/sync/repo-root.d.ts +0 -1
  101. package/dist/sync/repo-root.js +1 -24
  102. package/dist/sync/stitch-claude-md.d.ts +0 -1
  103. package/dist/sync/stitch-claude-md.js +8 -48
  104. package/dist/ui/colors.d.ts +0 -1
  105. package/dist/ui/colors.js +1 -28
  106. package/dist/ui/screens.d.ts +0 -1
  107. package/dist/ui/screens.js +5 -135
  108. package/package.json +8 -14
  109. package/plugin/.cursor-plugin/plugin.json +0 -2
  110. package/plugin/.mcp.json +1 -1
  111. package/plugin/LICENSE +15 -0
  112. package/plugin/README.md +3 -7
  113. package/plugin/gemini/gemini-extension.json +1 -1
  114. package/plugin/mcp.json +1 -1
  115. package/plugin/plugin.json +0 -2
  116. package/plugin/runtime/unfairly-intelligence.mjs +15 -0
  117. package/plugin/scripts/capture.cjs +30 -8
  118. package/plugin/skills/find-company-playbook/SKILL.md +1 -1
  119. package/dist/auth/browser-flow.d.ts.map +0 -1
  120. package/dist/auth/browser-flow.js.map +0 -1
  121. package/dist/cmd/connect.d.ts.map +0 -1
  122. package/dist/cmd/connect.js.map +0 -1
  123. package/dist/cmd/init.d.ts.map +0 -1
  124. package/dist/cmd/init.js.map +0 -1
  125. package/dist/cmd/invite.d.ts.map +0 -1
  126. package/dist/cmd/invite.js.map +0 -1
  127. package/dist/cmd/join.d.ts.map +0 -1
  128. package/dist/cmd/join.js.map +0 -1
  129. package/dist/cmd/login.d.ts.map +0 -1
  130. package/dist/cmd/login.js.map +0 -1
  131. package/dist/cmd/mcp.d.ts.map +0 -1
  132. package/dist/cmd/mcp.js.map +0 -1
  133. package/dist/cmd/pace.d.ts.map +0 -1
  134. package/dist/cmd/pace.js.map +0 -1
  135. package/dist/cmd/parse-share-input.d.ts.map +0 -1
  136. package/dist/cmd/parse-share-input.js.map +0 -1
  137. package/dist/cmd/pull.d.ts.map +0 -1
  138. package/dist/cmd/pull.js.map +0 -1
  139. package/dist/cmd/push.d.ts.map +0 -1
  140. package/dist/cmd/push.js.map +0 -1
  141. package/dist/cmd/resolve.d.ts.map +0 -1
  142. package/dist/cmd/resolve.js.map +0 -1
  143. package/dist/cmd/setup.d.ts.map +0 -1
  144. package/dist/cmd/setup.js.map +0 -1
  145. package/dist/cmd/status-card.d.ts.map +0 -1
  146. package/dist/cmd/status-card.js.map +0 -1
  147. package/dist/cmd/status.d.ts.map +0 -1
  148. package/dist/cmd/status.js.map +0 -1
  149. package/dist/cmd/watch.d.ts.map +0 -1
  150. package/dist/cmd/watch.js.map +0 -1
  151. package/dist/hosts/claude-code.d.ts.map +0 -1
  152. package/dist/hosts/claude-code.js.map +0 -1
  153. package/dist/hosts/claude-desktop.d.ts.map +0 -1
  154. package/dist/hosts/claude-desktop.js.map +0 -1
  155. package/dist/hosts/codex.d.ts.map +0 -1
  156. package/dist/hosts/codex.js.map +0 -1
  157. package/dist/hosts/shell-env.d.ts.map +0 -1
  158. package/dist/hosts/shell-env.js.map +0 -1
  159. package/dist/index.d.ts.map +0 -1
  160. package/dist/index.js.map +0 -1
  161. package/dist/lib/client.d.ts.map +0 -1
  162. package/dist/lib/client.js.map +0 -1
  163. package/dist/lib/events/emit.d.ts.map +0 -1
  164. package/dist/lib/events/emit.js.map +0 -1
  165. package/dist/lib/index.d.ts.map +0 -1
  166. package/dist/lib/index.js.map +0 -1
  167. package/dist/lib/transport.d.ts.map +0 -1
  168. package/dist/lib/transport.js.map +0 -1
  169. package/dist/lib/types/index.d.ts.map +0 -1
  170. package/dist/lib/types/index.js.map +0 -1
  171. package/dist/lib/vault/checkpoint.d.ts.map +0 -1
  172. package/dist/lib/vault/checkpoint.js.map +0 -1
  173. package/dist/lib/vault/read.d.ts.map +0 -1
  174. package/dist/lib/vault/read.js.map +0 -1
  175. package/dist/lib/vault/search.d.ts.map +0 -1
  176. package/dist/lib/vault/search.js.map +0 -1
  177. package/dist/pace/device.d.ts.map +0 -1
  178. package/dist/pace/device.js.map +0 -1
  179. package/dist/pace/identity.d.ts.map +0 -1
  180. package/dist/pace/identity.js.map +0 -1
  181. package/dist/pace/install.d.ts.map +0 -1
  182. package/dist/pace/install.js.map +0 -1
  183. package/dist/pace/local-evidence.d.ts.map +0 -1
  184. package/dist/pace/local-evidence.js.map +0 -1
  185. package/dist/pace/local-sessions.d.ts.map +0 -1
  186. package/dist/pace/local-sessions.js.map +0 -1
  187. package/dist/pace/managed-config.d.ts.map +0 -1
  188. package/dist/pace/managed-config.js.map +0 -1
  189. package/dist/pace/normalize.d.ts.map +0 -1
  190. package/dist/pace/normalize.js.map +0 -1
  191. package/dist/pace/repository.d.ts.map +0 -1
  192. package/dist/pace/repository.js.map +0 -1
  193. package/dist/pace/schema.d.ts.map +0 -1
  194. package/dist/pace/schema.js.map +0 -1
  195. package/dist/pace/session-summary.d.ts.map +0 -1
  196. package/dist/pace/session-summary.js.map +0 -1
  197. package/dist/pace/spool.d.ts.map +0 -1
  198. package/dist/pace/spool.js.map +0 -1
  199. package/dist/sync/api.d.ts.map +0 -1
  200. package/dist/sync/api.js.map +0 -1
  201. package/dist/sync/autoloader.d.ts.map +0 -1
  202. package/dist/sync/autoloader.js.map +0 -1
  203. package/dist/sync/conflict.d.ts.map +0 -1
  204. package/dist/sync/conflict.js.map +0 -1
  205. package/dist/sync/credentials.d.ts.map +0 -1
  206. package/dist/sync/credentials.js.map +0 -1
  207. package/dist/sync/files.d.ts.map +0 -1
  208. package/dist/sync/files.js.map +0 -1
  209. package/dist/sync/manifest.d.ts.map +0 -1
  210. package/dist/sync/manifest.js.map +0 -1
  211. package/dist/sync/repo-root.d.ts.map +0 -1
  212. package/dist/sync/repo-root.js.map +0 -1
  213. package/dist/sync/stitch-claude-md.d.ts.map +0 -1
  214. package/dist/sync/stitch-claude-md.js.map +0 -1
  215. package/dist/ui/colors.d.ts.map +0 -1
  216. package/dist/ui/colors.js.map +0 -1
  217. package/dist/ui/screens.d.ts.map +0 -1
  218. package/dist/ui/screens.js.map +0 -1
@@ -1,16 +1,6 @@
1
- import matter from "gray-matter";
2
- /**
3
- * Build a three-way merge file: server frontmatter (always wins for
4
- * authoritative keys), then a conflict-marked body section. The user
5
- * (or their agent) edits to remove markers, then runs `unfairly resolve`.
6
- */
7
- export function buildConflictFile(args) {
8
- const conflictBody = `<<<<<<< local
9
- ${args.localBody}
1
+ import e from"gray-matter";function d(t){const o=`<<<<<<< local
2
+ ${t.localBody}
10
3
  =======
11
- ${args.serverBody}
12
- >>>>>>> cloud (updated_at ${args.serverUpdatedAt})
13
- `;
14
- return matter.stringify(conflictBody, args.serverFrontmatter);
15
- }
16
- //# sourceMappingURL=conflict.js.map
4
+ ${t.serverBody}
5
+ >>>>>>> cloud (updated_at ${t.serverUpdatedAt})
6
+ `;return e.stringify(o,t.serverFrontmatter)}export{d as buildConflictFile};
@@ -5,4 +5,3 @@
5
5
  export declare function getToken(): Promise<string | null>;
6
6
  export declare function setToken(token: string): Promise<void>;
7
7
  export declare function clearToken(): Promise<void>;
8
- //# sourceMappingURL=credentials.d.ts.map
@@ -1,51 +1 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- import os from "node:os";
4
- const SERVICE = "unfairly";
5
- const ACCOUNT = "default";
6
- const FILE_FALLBACK = path.join(os.homedir(), ".unfairly", "credentials");
7
- /**
8
- * Try OS keychain via keytar; if unavailable (Linux without libsecret, CI,
9
- * etc.), fall back to a 0600 file in ~/.unfairly/credentials.
10
- */
11
- export async function getToken() {
12
- try {
13
- const keytar = (await import("keytar")).default;
14
- const t = await keytar.getPassword(SERVICE, ACCOUNT);
15
- if (t)
16
- return t;
17
- }
18
- catch {
19
- // keytar unavailable; fall through
20
- }
21
- try {
22
- return (await fs.readFile(FILE_FALLBACK, "utf8")).trim();
23
- }
24
- catch {
25
- return null;
26
- }
27
- }
28
- export async function setToken(token) {
29
- try {
30
- const keytar = (await import("keytar")).default;
31
- await keytar.setPassword(SERVICE, ACCOUNT, token);
32
- return;
33
- }
34
- catch {
35
- // fall through
36
- }
37
- await fs.mkdir(path.dirname(FILE_FALLBACK), { recursive: true });
38
- await fs.writeFile(FILE_FALLBACK, token, { mode: 0o600 });
39
- }
40
- export async function clearToken() {
41
- try {
42
- const keytar = (await import("keytar")).default;
43
- await keytar.deletePassword(SERVICE, ACCOUNT);
44
- }
45
- catch { }
46
- try {
47
- await fs.unlink(FILE_FALLBACK);
48
- }
49
- catch { }
50
- }
51
- //# sourceMappingURL=credentials.js.map
1
+ import{promises as a}from"node:fs";import n from"node:path";import c from"node:os";const i="unfairly",o="default",r=n.join(c.homedir(),".unfairly","credentials");async function f(){try{const e=await(await import("keytar")).default.getPassword(i,o);if(e)return e}catch{}try{return(await a.readFile(r,"utf8")).trim()}catch{return null}}async function m(t){try{await(await import("keytar")).default.setPassword(i,o,t);return}catch{}await a.mkdir(n.dirname(r),{recursive:!0}),await a.writeFile(r,t,{mode:384})}async function d(){try{await(await import("keytar")).default.deletePassword(i,o)}catch{}try{await a.unlink(r)}catch{}}export{d as clearToken,f as getToken,m as setToken};
@@ -40,4 +40,3 @@ export declare function readDocRaw(absPath: string): Promise<string>;
40
40
  export declare function readDocFile(absPath: string): Promise<DocFile>;
41
41
  /** Atomic write (tmp + rename). */
42
42
  export declare function writeDocFile(absPath: string, content: string): Promise<void>;
43
- //# sourceMappingURL=files.d.ts.map
@@ -1,71 +1 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- import crypto from "node:crypto";
4
- import matter from "gray-matter";
5
- /**
6
- * Sha of the canonical rendered file content. Pass the full markdown
7
- * (frontmatter + body, as written to disk). Trailing whitespace is
8
- * normalized so write→read round-trips don't trip false-dirty.
9
- */
10
- export function computeSha(rendered) {
11
- const h = crypto.createHash("sha256");
12
- h.update(rendered.replace(/\s+$/, ""));
13
- return h.digest("hex").slice(0, 16);
14
- }
15
- function slugify(s) {
16
- return s
17
- .toLowerCase()
18
- .replace(/\s+/g, "-")
19
- .replace(/[^a-z0-9-]/g, "");
20
- }
21
- /** Build a relative path for a doc within a vault directory. */
22
- export function docPath(vaultSlug, sectionName, slug) {
23
- const section = slugify(sectionName ?? "_root");
24
- return path.join(vaultSlug, section, `${slug}.md`);
25
- }
26
- /**
27
- * Build the markdown file content for a server-side doc. The caller
28
- * substitutes the vaultSlug into `path` after this returns; we leave it
29
- * generic so the same builder works for both tracked and cached vault
30
- * roots.
31
- */
32
- export function buildFileFromServer(doc, vaultRoot) {
33
- const fm = {
34
- unfairly_id: doc.id,
35
- slug: doc.slug,
36
- name: doc.name,
37
- section: doc.sections?.name ?? null,
38
- trust_tier: doc.trust_tier,
39
- role: doc.role,
40
- source_url: doc.source_url,
41
- source_type: doc.source_type ?? "markdown",
42
- updated_at: doc.updated_at,
43
- };
44
- const body = doc.markdown_content ?? "";
45
- const content = matter.stringify(body, fm);
46
- return {
47
- path: docPath(vaultRoot, doc.sections?.name ?? null, doc.slug),
48
- content,
49
- sha: computeSha(content),
50
- };
51
- }
52
- /** Read the raw file content (frontmatter + body, no parsing). */
53
- export async function readDocRaw(absPath) {
54
- return fs.readFile(absPath, "utf8");
55
- }
56
- export async function readDocFile(absPath) {
57
- const raw = await fs.readFile(absPath, "utf8");
58
- const parsed = matter(raw);
59
- return {
60
- frontmatter: parsed.data,
61
- body: parsed.content,
62
- };
63
- }
64
- /** Atomic write (tmp + rename). */
65
- export async function writeDocFile(absPath, content) {
66
- const tmp = `${absPath}.tmp.${process.pid}`;
67
- await fs.mkdir(path.dirname(absPath), { recursive: true });
68
- await fs.writeFile(tmp, content, "utf8");
69
- await fs.rename(tmp, absPath);
70
- }
71
- //# sourceMappingURL=files.js.map
1
+ import{promises as n}from"node:fs";import i from"node:path";import u from"node:crypto";import s from"gray-matter";function c(t){const e=u.createHash("sha256");return e.update(t.replace(/\s+$/,"")),e.digest("hex").slice(0,16)}function p(t){return t.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"")}function m(t,e,r){const o=p(e??"_root");return i.join(t,o,`${r}.md`)}function y(t,e){const r={unfairly_id:t.id,slug:t.slug,name:t.name,section:t.sections?.name??null,trust_tier:t.trust_tier,role:t.role,source_url:t.source_url,source_type:t.source_type??"markdown",updated_at:t.updated_at},o=t.markdown_content??"",a=s.stringify(o,r);return{path:m(e,t.sections?.name??null,t.slug),content:a,sha:c(a)}}async function _(t){return n.readFile(t,"utf8")}async function g(t){const e=await n.readFile(t,"utf8"),r=s(e);return{frontmatter:r.data,body:r.content}}async function h(t,e){const r=`${t}.tmp.${process.pid}`;await n.mkdir(i.dirname(t),{recursive:!0}),await n.writeFile(r,e,"utf8"),await n.rename(r,t)}export{y as buildFileFromServer,c as computeSha,m as docPath,g as readDocFile,_ as readDocRaw,h as writeDocFile};
@@ -28,4 +28,3 @@ export declare function readManifest(rootDir: string): Promise<Manifest | null>;
28
28
  */
29
29
  export declare function writeManifest(rootDir: string, m: Manifest): Promise<void>;
30
30
  export declare function emptyManifest(apiUrl: string): Manifest;
31
- //# sourceMappingURL=manifest.d.ts.map
@@ -1,37 +1 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- const MANIFEST_FILE = "manifest.json";
4
- export function manifestPath(rootDir) {
5
- return path.join(rootDir, ".unfairly", MANIFEST_FILE);
6
- }
7
- export async function readManifest(rootDir) {
8
- try {
9
- const raw = await fs.readFile(manifestPath(rootDir), "utf8");
10
- return JSON.parse(raw);
11
- }
12
- catch (err) {
13
- if (err.code === "ENOENT")
14
- return null;
15
- throw err;
16
- }
17
- }
18
- /**
19
- * Atomic write: tmp file → fsync → rename. Rename is atomic on POSIX,
20
- * preventing a half-written manifest if the process is killed mid-write.
21
- */
22
- export async function writeManifest(rootDir, m) {
23
- const target = manifestPath(rootDir);
24
- const tmp = `${target}.tmp.${process.pid}`;
25
- await fs.mkdir(path.dirname(target), { recursive: true });
26
- await fs.writeFile(tmp, JSON.stringify(m, null, 2), "utf8");
27
- await fs.rename(tmp, target);
28
- }
29
- export function emptyManifest(apiUrl) {
30
- return {
31
- version: 1,
32
- api_url: apiUrl,
33
- vaults: {},
34
- generated_at: new Date().toISOString(),
35
- };
36
- }
37
- //# sourceMappingURL=manifest.js.map
1
+ import{promises as e}from"node:fs";import i from"node:path";const s="manifest.json";function o(t){return i.join(t,".unfairly",s)}async function c(t){try{const r=await e.readFile(o(t),"utf8");return JSON.parse(r)}catch(r){if(r.code==="ENOENT")return null;throw r}}async function p(t,r){const n=o(t),a=`${n}.tmp.${process.pid}`;await e.mkdir(i.dirname(n),{recursive:!0}),await e.writeFile(a,JSON.stringify(r,null,2),"utf8"),await e.rename(a,n)}function m(t){return{version:1,api_url:t,vaults:{},generated_at:new Date().toISOString()}}export{m as emptyManifest,o as manifestPath,c as readManifest,p as writeManifest};
@@ -4,4 +4,3 @@
4
4
  * Accepts `.git` as either dir or file (worktrees use a `.git` file).
5
5
  */
6
6
  export declare function findRepoRoot(start: string): Promise<string | null>;
7
- //# sourceMappingURL=repo-root.d.ts.map
@@ -1,24 +1 @@
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
1
+ import{promises as i}from"node:fs";import r from"node:path";async function s(e){let t=r.resolve(e);for(;;){try{return await i.stat(r.join(t,".git")),t}catch{}const o=r.dirname(t);if(o===t)return null;t=o}}export{s as findRepoRoot};
@@ -6,4 +6,3 @@ export type StitchResult = {
6
6
  message?: string;
7
7
  };
8
8
  export declare function stitchClaudeMd(repoRoot: string): Promise<StitchResult>;
9
- //# sourceMappingURL=stitch-claude-md.d.ts.map
@@ -1,48 +1,8 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- export const AUTOLOADER_LINE = "@.unfairly/CLAUDE.md";
4
- export const SENTINEL = "<!-- unfairly: autoloader -->";
5
- const LINE_RE = /^@\.unfairly\/CLAUDE\.md\s*$/m;
6
- export async function stitchClaudeMd(repoRoot) {
7
- const gitDir = path.join(repoRoot, ".git");
8
- try {
9
- await fs.stat(gitDir);
10
- }
11
- catch {
12
- return { action: "not-a-repo", path: path.join(repoRoot, "CLAUDE.md") };
13
- }
14
- const claudeMdPath = path.join(repoRoot, "CLAUDE.md");
15
- let existing;
16
- try {
17
- existing = await fs.readFile(claudeMdPath, "utf8");
18
- }
19
- catch (err) {
20
- const e = err;
21
- if (e.code !== "ENOENT")
22
- return { action: "error", path: claudeMdPath, message: e.message };
23
- existing = null;
24
- }
25
- if (existing === null) {
26
- const content = `${SENTINEL}\n${AUTOLOADER_LINE}\n`;
27
- try {
28
- await fs.writeFile(claudeMdPath, content, "utf8");
29
- }
30
- catch (err) {
31
- return { action: "error", path: claudeMdPath, message: err.message };
32
- }
33
- return { action: "created", path: claudeMdPath };
34
- }
35
- if (LINE_RE.test(existing)) {
36
- return { action: "noop", path: claudeMdPath };
37
- }
38
- const sep = existing.endsWith("\n") ? "" : "\n";
39
- const next = `${existing}${sep}\n${SENTINEL}\n${AUTOLOADER_LINE}\n`;
40
- try {
41
- await fs.writeFile(claudeMdPath, next, "utf8");
42
- }
43
- catch (err) {
44
- return { action: "error", path: claudeMdPath, message: err.message };
45
- }
46
- return { action: "appended", path: claudeMdPath };
47
- }
48
- //# sourceMappingURL=stitch-claude-md.js.map
1
+ import{promises as a}from"node:fs";import o from"node:path";const s="@.unfairly/CLAUDE.md",c="<!-- unfairly: autoloader -->",d=/^@\.unfairly\/CLAUDE\.md\s*$/m;async function f(i){const u=o.join(i,".git");try{await a.stat(u)}catch{return{action:"not-a-repo",path:o.join(i,"CLAUDE.md")}}const t=o.join(i,"CLAUDE.md");let e;try{e=await a.readFile(t,"utf8")}catch(n){const r=n;if(r.code!=="ENOENT")return{action:"error",path:t,message:r.message};e=null}if(e===null){const n=`${c}
2
+ ${s}
3
+ `;try{await a.writeFile(t,n,"utf8")}catch(r){return{action:"error",path:t,message:r.message}}return{action:"created",path:t}}if(d.test(e))return{action:"noop",path:t};const p=e.endsWith(`
4
+ `)?"":`
5
+ `,m=`${e}${p}
6
+ ${c}
7
+ ${s}
8
+ `;try{await a.writeFile(t,m,"utf8")}catch(n){return{action:"error",path:t,message:n.message}}return{action:"appended",path:t}}export{s as AUTOLOADER_LINE,c as SENTINEL,f as stitchClaudeMd};
@@ -14,4 +14,3 @@ export type Brand = {
14
14
  bold: (s: string) => string;
15
15
  };
16
16
  export declare function brand(env?: ColorEnv): Brand;
17
- //# sourceMappingURL=colors.d.ts.map
package/dist/ui/colors.js CHANGED
@@ -1,28 +1 @@
1
- import { createColors } from "picocolors";
2
- export function isColorEnabled(isTTY) {
3
- if (process.env.NO_COLOR && process.env.NO_COLOR !== "")
4
- return false;
5
- return isTTY;
6
- }
7
- export function brand(env = { enabled: isColorEnabled(Boolean(process.stdout.isTTY)) }) {
8
- if (!env.enabled) {
9
- const identity = (s) => s;
10
- return {
11
- coral: identity, gold: identity, silver: identity, bronze: identity,
12
- dim: identity, ok: identity, warn: identity, err: identity, bold: identity,
13
- };
14
- }
15
- const pc = createColors(true);
16
- return {
17
- coral: (s) => pc.red(s),
18
- gold: (s) => pc.yellow(s),
19
- silver: (s) => pc.white(s),
20
- bronze: (s) => pc.yellow(pc.dim(s)),
21
- dim: (s) => pc.dim(s),
22
- ok: (s) => pc.green(s),
23
- warn: (s) => pc.yellow(s),
24
- err: (s) => pc.red(s),
25
- bold: (s) => pc.bold(s),
26
- };
27
- }
28
- //# sourceMappingURL=colors.js.map
1
+ import{createColors as l}from"picocolors";function t(o){return process.env.NO_COLOR&&process.env.NO_COLOR!==""?!1:o}function i(o={enabled:t(!!process.stdout.isTTY)}){if(!o.enabled){const e=n=>n;return{coral:e,gold:e,silver:e,bronze:e,dim:e,ok:e,warn:e,err:e,bold:e}}const r=l(!0);return{coral:e=>r.red(e),gold:e=>r.yellow(e),silver:e=>r.white(e),bronze:e=>r.yellow(r.dim(e)),dim:e=>r.dim(e),ok:e=>r.green(e),warn:e=>r.yellow(e),err:e=>r.red(e),bold:e=>r.bold(e)}}export{i as brand,t as isColorEnabled};
@@ -41,4 +41,3 @@ export type StatusState = {
41
41
  };
42
42
  export declare function statusCard(state: StatusState, opts: ScreenOpts): string;
43
43
  export declare function renderTo(write: (s: string) => unknown, lines: string[]): void;
44
- //# sourceMappingURL=screens.d.ts.map
@@ -1,135 +1,5 @@
1
- import { brand } from "./colors.js";
2
- export function defaultOpts() {
3
- const cols = Number(process.stdout.columns ?? 80);
4
- return {
5
- color: Boolean(process.stdout.isTTY) && !process.env.NO_COLOR,
6
- width: cols > 0 ? cols : 80,
7
- };
8
- }
9
- function colorsFor(opts) {
10
- return brand({ enabled: opts.color });
11
- }
12
- export function banner(opts) {
13
- const c = colorsFor(opts);
14
- const line1 = " ✦ U N F A I R L Y ✦";
15
- const line2 = " the multiplayer brain for ai";
16
- return `${c.coral(line1)}\n${c.dim(line2)}`;
17
- }
18
- export function stepHeader(n, total, label, opts) {
19
- const c = colorsFor(opts);
20
- return `${c.dim(`[${n}/${total}]`)} ${c.dim(label)}`;
21
- }
22
- export function result(kind, text, opts) {
23
- const c = colorsFor(opts);
24
- const glyph = kind === "ok" ? c.ok("✓") :
25
- kind === "err" ? c.err("✗") :
26
- kind === "warn" ? c.warn("⚠") :
27
- kind === "skip" ? c.dim("·") :
28
- c.dim("◆");
29
- return ` ${glyph} ${text}`;
30
- }
31
- export function divider(opts) {
32
- const c = colorsFor(opts);
33
- const len = Math.max(20, Math.min(45, opts.width - 4));
34
- // Box-drawing dash for TTY; plain ASCII for piped/CI consumers.
35
- const ch = opts.color ? "─" : "-";
36
- return c.dim(ch.repeat(len));
37
- }
38
- export function handoffCard(state, opts) {
39
- const c = colorsFor(opts);
40
- // Inner width 50 fits the longest dynamic line ("You joined Unfairly · N vaults · M docs"
41
- // for realistic N, M) plus 3-space inner padding on each side, with a couple cols slack.
42
- const inner = 50;
43
- const pad = (s) => s + " ".repeat(Math.max(0, inner - visibleLen(s)));
44
- const lines = [];
45
- // Box-drawing chars in TTY mode; ASCII in plain mode so piped/CI consumers
46
- // don't end up with mangled unicode.
47
- const tl = opts.color ? "╔" : "+";
48
- const tr = opts.color ? "╗" : "+";
49
- const bl = opts.color ? "╚" : "+";
50
- const br = opts.color ? "╝" : "+";
51
- const h = opts.color ? "═" : "-";
52
- const v = opts.color ? "║" : "|";
53
- const border = h.repeat(inner);
54
- lines.push(` ${c.coral(`${tl}${border}${tr}`)}`);
55
- lines.push(` ${c.coral(v)}${pad("")}${c.coral(v)}`);
56
- lines.push(` ${c.coral(v)}${pad(centered(opts.color ? "✦ PLAYER 2 READY ✦" : "* PLAYER 2 READY *", inner, c.bold))}${c.coral(v)}`);
57
- lines.push(` ${c.coral(v)}${pad("")}${c.coral(v)}`);
58
- lines.push(` ${c.coral(v)}${pad(` You joined ${c.bold("Unfairly")} · ${c.dim(`${state.vaultCount} vaults · ${state.docCount} docs`)} `)}${c.coral(v)}`);
59
- if (state.recentMove) {
60
- const m = state.recentMove;
61
- lines.push(` ${c.coral(v)}${pad(` ${c.silver(`${m.who} shipped "${truncate(m.title, 22)}" ${m.ago}`)} `)}${c.coral(v)}`);
62
- }
63
- lines.push(` ${c.coral(v)}${pad("")}${c.coral(v)}`);
64
- const arrow = opts.color ? "▶" : ">";
65
- lines.push(` ${c.coral(v)}${pad(` ${c.coral(arrow)} ${state.hint} `)}${c.coral(v)}`);
66
- lines.push(` ${c.coral(v)}${pad(` ${c.dim("or run ")}${c.bold("claude")}${c.dim(" / ")}${c.bold("codex")}${c.dim(" here in this repo")} `)}${c.coral(v)}`);
67
- lines.push(` ${c.coral(v)}${pad("")}${c.coral(v)}`);
68
- lines.push(` ${c.coral(v)}${pad(` ${c.dim("the brain follows you across surfaces.")} `)}${c.coral(v)}`);
69
- lines.push(` ${c.coral(v)}${pad("")}${c.coral(v)}`);
70
- lines.push(` ${c.coral(`${bl}${border}${br}`)}`);
71
- return lines.join("\n");
72
- }
73
- export function statusCard(state, opts) {
74
- const c = colorsFor(opts);
75
- const lines = [];
76
- lines.push(` ${c.coral("✦ UNFAIRLY ✦")} ${c.dim("player 2 — daily check-in")}`);
77
- lines.push("");
78
- lines.push(` ${c.dim("HOSTS WIRED")}`);
79
- lines.push(` ${hostGlyph(state.hosts.claudeDesktop, c)} Claude Desktop ${hostGlyph(state.hosts.claudeCode, c)} Claude Code ${hostGlyph(state.hosts.codex, c)} Codex`);
80
- lines.push("");
81
- if (state.workspace) {
82
- lines.push(` ${c.dim("WORKSPACE")}`);
83
- const ws = state.workspace;
84
- const pulled = ws.lastPulledAt ? `last pulled ${timeAgo(ws.lastPulledAt)}` : c.warn("not pulled yet");
85
- lines.push(` ${ws.cwd} ${c.dim("·")} ${ws.vaultCount} vaults · ${ws.docCount} docs ${c.dim("·")} ${c.dim(pulled)}`);
86
- lines.push("");
87
- }
88
- if (state.recent.length > 0) {
89
- lines.push(` ${c.dim("TEAM MOVES (last 24h)")}`);
90
- for (const m of state.recent.slice(0, 5)) {
91
- lines.push(` ${c.coral("●")} ${c.bold(m.who)} · ${m.room} · ${c.dim(m.ago)} · "${m.title}"`);
92
- }
93
- lines.push("");
94
- }
95
- lines.push(` ${c.coral("▶")} ${c.dim("run")} ${c.bold("unfairly pull")} ${c.dim("to refresh · or just open Claude Desktop.")}`);
96
- return lines.join("\n");
97
- }
98
- function hostGlyph(s, c) {
99
- if (s === "wired")
100
- return c.ok("✓");
101
- if (s === "rotted")
102
- return c.warn("⚠");
103
- return c.dim("·");
104
- }
105
- export function renderTo(write, lines) {
106
- for (const l of lines)
107
- write(`${l}\n`);
108
- }
109
- // --- helpers ---
110
- function visibleLen(s) {
111
- // eslint-disable-next-line no-control-regex
112
- return s.replace(/\x1b\[[0-9;]*m/g, "").length;
113
- }
114
- function centered(s, width, decorator) {
115
- const v = visibleLen(s);
116
- const pad = Math.max(0, Math.floor((width - v) / 2));
117
- const decorated = decorator ? decorator(s) : s;
118
- return " ".repeat(pad) + decorated;
119
- }
120
- function truncate(s, max) {
121
- return s.length <= max ? s : s.slice(0, max - 1) + "…";
122
- }
123
- function timeAgo(iso) {
124
- const diff = Date.now() - new Date(iso).getTime();
125
- const mins = Math.floor(diff / 60000);
126
- if (mins < 1)
127
- return "just now";
128
- if (mins < 60)
129
- return `${mins}m ago`;
130
- const hrs = Math.floor(mins / 60);
131
- if (hrs < 24)
132
- return `${hrs}h ago`;
133
- return `${Math.floor(hrs / 24)}d ago`;
134
- }
135
- //# sourceMappingURL=screens.js.map
1
+ import{brand as b}from"./colors.js";function M(){const r=Number(process.stdout.columns??80);return{color:!!process.stdout.isTTY&&!process.env.NO_COLOR,width:r>0?r:80}}function $(r){return b({enabled:r.color})}function A(r){const c=$(r);return`${c.coral(" \u2726 U N F A I R L Y \u2726")}
2
+ ${c.dim(" the multiplayer brain for ai")}`}function y(r,c,o,n){const e=$(n);return`${e.dim(`[${r}/${c}]`)} ${e.dim(o)}`}function R(r,c,o){const n=$(o);return` ${r==="ok"?n.ok("\u2713"):r==="err"?n.err("\u2717"):r==="warn"?n.warn("\u26A0"):r==="skip"?n.dim("\xB7"):n.dim("\u25C6")} ${c}`}function D(r){const c=$(r),o=Math.max(20,Math.min(45,r.width-4)),n=r.color?"\u2500":"-";return c.dim(n.repeat(o))}function E(r,c){const o=$(c),n=50,e=u=>u+" ".repeat(Math.max(0,n-i(u))),l=[],d=c.color?"\u2554":"+",h=c.color?"\u2557":"+",p=c.color?"\u255A":"+",f=c.color?"\u255D":"+",m=c.color?"\u2550":"-",t=c.color?"\u2551":"|",a=m.repeat(n);if(l.push(` ${o.coral(`${d}${a}${h}`)}`),l.push(` ${o.coral(t)}${e("")}${o.coral(t)}`),l.push(` ${o.coral(t)}${e(x(c.color?"\u2726 PLAYER 2 READY \u2726":"* PLAYER 2 READY *",n,o.bold))}${o.coral(t)}`),l.push(` ${o.coral(t)}${e("")}${o.coral(t)}`),l.push(` ${o.coral(t)}${e(` You joined ${o.bold("Unfairly")} \xB7 ${o.dim(`${r.vaultCount} vaults \xB7 ${r.docCount} docs`)} `)}${o.coral(t)}`),r.recentMove){const u=r.recentMove;l.push(` ${o.coral(t)}${e(` ${o.silver(`${u.who} shipped "${C(u.title,22)}" ${u.ago}`)} `)}${o.coral(t)}`)}l.push(` ${o.coral(t)}${e("")}${o.coral(t)}`);const w=c.color?"\u25B6":">";return l.push(` ${o.coral(t)}${e(` ${o.coral(w)} ${r.hint} `)}${o.coral(t)}`),l.push(` ${o.coral(t)}${e(` ${o.dim("or run ")}${o.bold("claude")}${o.dim(" / ")}${o.bold("codex")}${o.dim(" here in this repo")} `)}${o.coral(t)}`),l.push(` ${o.coral(t)}${e("")}${o.coral(t)}`),l.push(` ${o.coral(t)}${e(` ${o.dim("the brain follows you across surfaces.")} `)}${o.coral(t)}`),l.push(` ${o.coral(t)}${e("")}${o.coral(t)}`),l.push(` ${o.coral(`${p}${a}${f}`)}`),l.join(`
3
+ `)}function Y(r,c){const o=$(c),n=[];if(n.push(` ${o.coral("\u2726 UNFAIRLY \u2726")} ${o.dim("player 2 \u2014 daily check-in")}`),n.push(""),n.push(` ${o.dim("HOSTS WIRED")}`),n.push(` ${s(r.hosts.claudeDesktop,o)} Claude Desktop ${s(r.hosts.claudeCode,o)} Claude Code ${s(r.hosts.codex,o)} Codex`),n.push(""),r.workspace){n.push(` ${o.dim("WORKSPACE")}`);const e=r.workspace,l=e.lastPulledAt?`last pulled ${g(e.lastPulledAt)}`:o.warn("not pulled yet");n.push(` ${e.cwd} ${o.dim("\xB7")} ${e.vaultCount} vaults \xB7 ${e.docCount} docs ${o.dim("\xB7")} ${o.dim(l)}`),n.push("")}if(r.recent.length>0){n.push(` ${o.dim("TEAM MOVES (last 24h)")}`);for(const e of r.recent.slice(0,5))n.push(` ${o.coral("\u25CF")} ${o.bold(e.who)} \xB7 ${e.room} \xB7 ${o.dim(e.ago)} \xB7 "${e.title}"`);n.push("")}return n.push(` ${o.coral("\u25B6")} ${o.dim("run")} ${o.bold("unfairly pull")} ${o.dim("to refresh \xB7 or just open Claude Desktop.")}`),n.join(`
4
+ `)}function s(r,c){return r==="wired"?c.ok("\u2713"):r==="rotted"?c.warn("\u26A0"):c.dim("\xB7")}function k(r,c){for(const o of c)r(`${o}
5
+ `)}function i(r){return r.replace(/\x1b\[[0-9;]*m/g,"").length}function x(r,c,o){const n=i(r),e=Math.max(0,Math.floor((c-n)/2)),l=o?o(r):r;return" ".repeat(e)+l}function C(r,c){return r.length<=c?r:r.slice(0,c-1)+"\u2026"}function g(r){const c=Date.now()-new Date(r).getTime(),o=Math.floor(c/6e4);if(o<1)return"just now";if(o<60)return`${o}m ago`;const n=Math.floor(o/60);return n<24?`${n}h ago`:`${Math.floor(n/24)}d ago`}export{A as banner,M as defaultOpts,D as divider,E as handoffCard,k as renderTo,R as result,Y as statusCard,y as stepHeader};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unfairly",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "description": "CLI + stdio MCP + library SDK for the Unfairly multiplayer context layer — sync vaults, run agents, emit events",
5
5
  "keywords": [
6
6
  "unfairly",
@@ -14,15 +14,7 @@
14
14
  "sdk"
15
15
  ],
16
16
  "homepage": "https://unfairly.ai",
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://github.com/Unfairly-AI/unfairly-app.git",
20
- "directory": "cli"
21
- },
22
- "bugs": {
23
- "url": "https://github.com/Unfairly-AI/unfairly-app/issues"
24
- },
25
- "license": "MIT",
17
+ "license": "UNLICENSED",
26
18
  "author": "Unfairly <bob@hq.unfairly.ai>",
27
19
  "type": "module",
28
20
  "main": "./dist/lib/index.js",
@@ -50,11 +42,12 @@
50
42
  "./package.json": "./package.json"
51
43
  },
52
44
  "scripts": {
53
- "build": "tsc && chmod +x dist/index.js",
45
+ "build": "tsc && node scripts/minify-dist.mjs && chmod +x dist/index.js",
54
46
  "dev": "tsx src/index.ts",
55
47
  "test": "vitest run",
56
- "prepack": "npm run build && node scripts/stage-intelligence-plugin.mjs",
57
- "prepublishOnly": "npm run build"
48
+ "prepack": "npm run build && node scripts/bundle-plugin-runtime.mjs && node scripts/stage-intelligence-plugin.mjs",
49
+ "prepublishOnly": "npm run build",
50
+ "bundle:plugin-runtime": "node scripts/bundle-plugin-runtime.mjs"
58
51
  },
59
52
  "dependencies": {
60
53
  "@modelcontextprotocol/sdk": "^1.0.4",
@@ -79,6 +72,7 @@
79
72
  "dist",
80
73
  "plugin",
81
74
  "README.md",
82
- "CHANGELOG.md"
75
+ "CHANGELOG.md",
76
+ "LICENSE"
83
77
  ]
84
78
  }
@@ -7,8 +7,6 @@
7
7
  "email": "bob@hq.unfairly.ai"
8
8
  },
9
9
  "homepage": "https://intelligence.unfairly.ai",
10
- "repository": "https://github.com/Unfairly-AI/unfairly-app",
11
- "license": "MIT",
12
10
  "keywords": ["analytics", "developer-tools", "governance", "playbooks"],
13
11
  "skills": "./skills/",
14
12
  "hooks": "./hooks/cursor-hooks.json",
package/plugin/.mcp.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "unfairly": {
4
4
  "command": "npx",
5
- "args": ["-y", "unfairly@0.8.2", "mcp"]
5
+ "args": ["-y", "unfairly@0.8.4", "mcp"]
6
6
  }
7
7
  }
8
8
  }
package/plugin/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright (c) 2026 Unfairly AI. All rights reserved.
2
+
3
+ This software is proprietary and confidential. You may install and run it to
4
+ use Unfairly services under the terms at https://unfairly.ai. No other license
5
+ is granted to use, copy, modify, merge, publish, distribute, sublicense, or
6
+ sell copies of this software, in whole or in part, or to reverse engineer it,
7
+ except as expressly permitted in a separate written agreement with Unfairly AI.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
12
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
13
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
14
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
15
+ SOFTWARE.
package/plugin/README.md CHANGED
@@ -2,15 +2,11 @@
2
2
 
3
3
  The Unfairly plugin is the portable workflow component for Unfairly Intelligence, backed by the existing `unfairly` CLI. Its root Agent Plugins manifest carries the shared skills and MCP definition. Codex/ChatGPT Work use the OpenAI extension, Claude Code uses the Claude compatibility manifest, Cursor uses the native Cursor overlay, and Gemini CLI loads the nested `gemini/` extension. All lifecycle hooks call the compatibility command `unfairly pace capture` and emit the same content-free event contract. Employee-facing commands use `unfairly intelligence`.
4
4
 
5
- The plugin intentionally contains only thin compatibility manifests. Identity, redaction, Git enrichment, durable local spooling, upload, and policy live in the shared CLI core.
5
+ The plugin carries its own bundled runtime (`runtime/unfairly-intelligence.mjs`, generated from `cli/src/pace-runtime.ts` by `npm run bundle:plugin-runtime` in `cli/`), so installing the plugin is the whole install. Hooks call `scripts/capture.cjs`, which prefers the bundled runtime and falls back to a runtime placed by `npx unfairly` or a global `unfairly`. Identity, redaction, Git enrichment, durable local spooling, upload, and policy live in the shared CLI core.
6
6
 
7
- Employees install it through the existing one-command path:
7
+ Employees install it from their AI client's plugin screen, using the public marketplace `Unfairly-AI/unfairly-intelligence` (built from this directory by `scripts/build-intelligence-marketplace.mjs`). An administrator sends them `https://intelligence.unfairly.ai/install?org=<org-id>`, which walks through each client and shows the Claude managed-settings snippet for pushing it to everyone. On the first session start the plugin prints a link; the employee approves the machine in the browser (choosing the organization if they belong to several), and the machine receives its own revocable collector credential. A background poller picks up the approval within a few seconds, so the same session connects, and the first sync uploads the last 90 days of sessions after fetching the approved-repository list. After that, session summaries upload at every session start and end.
8
8
 
9
- ```bash
10
- npx unfairly@latest
11
- ```
12
-
13
- The command places a version-pinned runtime and marketplace under `~/.unfairly`, activates every detected hook-capable client, and confirms enrollment with the Intelligence API. No global npm install or administrator permission is required. A newly installed AI client is picked up the next time the command runs. `unfairly intelligence install` is the explicit repair/reinstall command.
9
+ The terminal path still works: `npx unfairly@latest setup --org <org-id>` installs and enrolls, and `unfairly intelligence backfill --days 90` uploads past sessions from the local transcripts.
14
10
 
15
11
  Before activation, inspect the exact contract with `unfairly intelligence policy`. Use `unfairly intelligence status` to see enabled clients, queue depth, policy/runtime/plugin versions, attestation expiry, and last upload. Collection can be stopped without losing installation identity with `unfairly intelligence pause` and restarted with `unfairly intelligence resume`. `unfairly intelligence uninstall` removes only tracked Unfairly-managed files and local Intelligence state.
16
12
 
@@ -6,7 +6,7 @@
6
6
  "mcpServers": {
7
7
  "unfairly": {
8
8
  "command": "npx",
9
- "args": ["-y", "unfairly@0.8.2", "mcp"]
9
+ "args": ["-y", "unfairly@0.8.4", "mcp"]
10
10
  }
11
11
  }
12
12
  }
package/plugin/mcp.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "unfairly": {
5
5
  "type": "stdio",
6
6
  "command": "npx",
7
- "args": ["-y", "unfairly@0.8.2", "mcp"]
7
+ "args": ["-y", "unfairly@0.8.4", "mcp"]
8
8
  }
9
9
  }
10
10
  }
@@ -9,8 +9,6 @@
9
9
  "url": "https://unfairly.ai"
10
10
  },
11
11
  "homepage": "https://intelligence.unfairly.ai",
12
- "repository": "https://github.com/Unfairly-AI/unfairly-app",
13
- "license": "MIT",
14
12
  "keywords": ["analytics", "developer-tools", "governance", "playbooks"],
15
13
  "extensions": {
16
14
  "com.openai": {