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,134 +1 @@
1
- import { z } from "zod";
2
- export const PaceSourceSchema = z.enum([
3
- "codex", "claude_code", "cursor", "gemini_cli", "openrouter", "chatgpt",
4
- "claude_desktop", "claude_web", "gemini_web", "local_filesystem", "unknown",
5
- ]);
6
- export const PaceEventTypeSchema = z.enum([
7
- "session.started", "session.ended", "model.completed", "tool.completed",
8
- "tool.failed", "workflow.stopped", "artifact.created", "artifact.modified",
9
- "desktop.activity", "unknown.observed",
10
- ]);
11
- export const PaceEvidenceLevelSchema = z.enum(["exact", "correlated", "inferred"]);
12
- export const PaceToolCategorySchema = z.enum([
13
- "file_read", "file_write", "code_search", "shell", "version_control",
14
- "web", "connector", "agent", "skill", "other", "unknown",
15
- ]);
16
- export const PaceBlockerCategorySchema = z.enum([
17
- "authentication", "authorization_permission", "approval_required",
18
- "missing_dependency_tool", "configuration", "rate_limit_quota", "tool_failure",
19
- "model_provider_unavailable", "workflow_stopped", "unknown",
20
- ]);
21
- export const PaceModelFamilySchema = z.enum([
22
- "openai_gpt", "anthropic_claude", "google_gemini", "meta_llama",
23
- "mistral", "other", "unknown",
24
- ]);
25
- export const PaceApprovalModeSchema = z.enum(["none", "manual", "automatic", "unknown"]);
26
- export const PacePermissionModeSchema = z.enum([
27
- "read_only", "workspace_write", "elevated", "unrestricted", "unknown",
28
- ]);
29
- export const PaceDurationBucketSchema = z.enum([
30
- "under_1s", "1s_to_10s", "10s_to_60s", "1m_to_5m", "over_5m", "unknown",
31
- ]);
32
- export const PaceExitCodeBucketSchema = z.enum(["success", "failure", "signal", "unknown"]);
33
- const OptionalOpaqueId = z.string().trim().regex(/^[A-Za-z0-9_-]{1,128}$/).optional();
34
- // Legacy schemas exist only so an old local spool can be sanitized in place.
35
- // New collection and network upload use v3 exclusively.
36
- const LegacyAttributesSchema = z.object({
37
- hook_event: z.string().max(128),
38
- approval_mode: z.string().max(128).optional(),
39
- permission_mode: z.string().max(128).optional(),
40
- status: z.string().max(128).optional(),
41
- success: z.boolean().optional(),
42
- exit_code: z.number().int().optional(),
43
- duration_ms: z.number().nonnegative().optional(),
44
- input_tokens: z.number().int().nonnegative().optional(),
45
- output_tokens: z.number().int().nonnegative().optional(),
46
- cached_tokens: z.number().int().nonnegative().optional(),
47
- error_type: z.string().max(128).optional(),
48
- stop_reason: z.string().max(128).optional(),
49
- root_label: z.string().max(128).optional(),
50
- path_fingerprint: z.string().regex(/^[a-f0-9]{64}$/).optional(),
51
- file_extension: z.string().max(32).optional(),
52
- file_name: z.string().max(255).optional(),
53
- size_bytes: z.number().int().nonnegative().optional(),
54
- files_changed: z.number().int().nonnegative().optional(),
55
- local_signal: z.enum(["approved_output", "app_state_change"]).optional(),
56
- source_app: z.enum(["chatgpt", "claude_desktop", "other"]).optional(),
57
- }).strict();
58
- const LegacyEventBaseSchema = z.object({
59
- event_id: z.string().uuid(),
60
- installation_id: z.string().uuid().optional(),
61
- occurred_at: z.string().datetime(),
62
- source: PaceSourceSchema,
63
- event_type: PaceEventTypeSchema,
64
- evidence_level: PaceEvidenceLevelSchema,
65
- session_id: z.string().trim().min(1).max(512).optional(),
66
- model: z.string().trim().min(1).max(512).optional(),
67
- tool: z.string().trim().min(1).max(512).optional(),
68
- skill: z.string().trim().min(1).max(512).optional(),
69
- cwd: z.string().trim().min(1).max(512).optional(),
70
- repository: z.string().trim().min(1).max(512).optional(),
71
- branch: z.string().trim().min(1).max(512).optional(),
72
- commit_sha: z.string().regex(/^[a-f0-9]{7,64}$/i).optional(),
73
- artifact_type: z.enum([
74
- "code", "pull_request", "document", "deck", "spreadsheet", "analysis",
75
- "workflow", "decision", "other",
76
- ]).optional(),
77
- attributes: LegacyAttributesSchema,
78
- });
79
- export const PaceEventV1Schema = LegacyEventBaseSchema.extend({ schema_version: z.literal(1) }).strict();
80
- export const PaceEventV2Schema = LegacyEventBaseSchema.extend({
81
- schema_version: z.literal(2),
82
- workflow_id: z.string().uuid().optional(),
83
- initiative_id: z.string().trim().min(1).max(512).optional(),
84
- playbook_id: z.string().trim().min(1).max(512).optional(),
85
- playbook_version: z.string().trim().min(1).max(512).optional(),
86
- }).strict();
87
- export const PaceEventV3Schema = z.object({
88
- schema_version: z.literal(3),
89
- collection_policy_version: z.literal("metadata-v3"),
90
- event_id: z.string().uuid(),
91
- installation_id: z.string().uuid().optional(),
92
- occurred_at: z.string().datetime(),
93
- source: PaceSourceSchema,
94
- event_type: PaceEventTypeSchema,
95
- evidence_level: PaceEvidenceLevelSchema,
96
- session_id: OptionalOpaqueId,
97
- workflow_id: z.string().uuid().optional(),
98
- initiative_id: OptionalOpaqueId,
99
- playbook_id: OptionalOpaqueId,
100
- playbook_version: OptionalOpaqueId,
101
- repository_id: z.string().uuid().optional(),
102
- commit_sha: z.string().regex(/^[a-f0-9]{40}$/i).optional(),
103
- model_family: PaceModelFamilySchema.optional(),
104
- tool_category: PaceToolCategorySchema.optional(),
105
- blocker_category: PaceBlockerCategorySchema.optional(),
106
- artifact_type: z.enum([
107
- "code", "pull_request", "document", "deck", "spreadsheet", "analysis",
108
- "workflow", "decision", "other",
109
- ]).optional(),
110
- attributes: z.object({
111
- client_event: z.enum([
112
- "session_start", "session_end", "before_agent", "after_agent", "stop",
113
- "tool_success", "tool_failure", "model_complete", "local_output", "local_activity", "unknown",
114
- ]),
115
- approval_mode: PaceApprovalModeSchema.optional(),
116
- permission_mode: PacePermissionModeSchema.optional(),
117
- success: z.boolean().optional(),
118
- exit_code_bucket: PaceExitCodeBucketSchema.optional(),
119
- duration_bucket: PaceDurationBucketSchema.optional(),
120
- input_tokens: z.number().int().nonnegative().optional(),
121
- output_tokens: z.number().int().nonnegative().optional(),
122
- cached_tokens: z.number().int().nonnegative().optional(),
123
- files_changed_bucket: z.enum(["one", "two_to_five", "six_to_twenty", "over_twenty", "unknown"]).optional(),
124
- local_signal: z.enum(["approved_output", "app_state_change"]).optional(),
125
- }).strict(),
126
- }).strict().superRefine((event, context) => {
127
- if (event.commit_sha && !event.repository_id) {
128
- context.addIssue({ code: z.ZodIssueCode.custom, path: ["commit_sha"], message: "commit_sha requires an approved repository_id" });
129
- }
130
- });
131
- export const PaceLegacyEventSchema = z.discriminatedUnion("schema_version", [PaceEventV1Schema, PaceEventV2Schema]);
132
- export const PaceEventSchema = PaceEventV3Schema;
133
- export const PaceBatchSchema = z.object({ events: z.array(PaceEventV3Schema).min(1).max(250) }).strict();
134
- //# sourceMappingURL=schema.js.map
1
+ import{z as e}from"zod";const n=e.enum(["codex","claude_code","cursor","gemini_cli","openrouter","chatgpt","claude_desktop","claude_web","gemini_web","local_filesystem","unknown"]),i=e.enum(["session.started","session.ended","model.completed","tool.completed","tool.failed","workflow.stopped","artifact.created","artifact.modified","desktop.activity","unknown.observed"]),a=e.enum(["exact","correlated","inferred"]),l=e.enum(["file_read","file_write","code_search","shell","version_control","web","connector","agent","skill","other","unknown"]),m=e.enum(["authentication","authorization_permission","approval_required","missing_dependency_tool","configuration","rate_limit_quota","tool_failure","model_provider_unavailable","workflow_stopped","unknown"]),p=e.enum(["openai_gpt","anthropic_claude","google_gemini","meta_llama","mistral","other","unknown"]),_=e.enum(["none","manual","automatic","unknown"]),u=e.enum(["read_only","workspace_write","elevated","unrestricted","unknown"]),d=e.enum(["under_1s","1s_to_10s","10s_to_60s","1m_to_5m","over_5m","unknown"]),g=e.enum(["success","failure","signal","unknown"]),o=e.string().trim().regex(/^[A-Za-z0-9_-]{1,128}$/).optional(),v=e.object({hook_event:e.string().max(128),approval_mode:e.string().max(128).optional(),permission_mode:e.string().max(128).optional(),status:e.string().max(128).optional(),success:e.boolean().optional(),exit_code:e.number().int().optional(),duration_ms:e.number().nonnegative().optional(),input_tokens:e.number().int().nonnegative().optional(),output_tokens:e.number().int().nonnegative().optional(),cached_tokens:e.number().int().nonnegative().optional(),error_type:e.string().max(128).optional(),stop_reason:e.string().max(128).optional(),root_label:e.string().max(128).optional(),path_fingerprint:e.string().regex(/^[a-f0-9]{64}$/).optional(),file_extension:e.string().max(32).optional(),file_name:e.string().max(255).optional(),size_bytes:e.number().int().nonnegative().optional(),files_changed:e.number().int().nonnegative().optional(),local_signal:e.enum(["approved_output","app_state_change"]).optional(),source_app:e.enum(["chatgpt","claude_desktop","other"]).optional()}).strict(),r=e.object({event_id:e.string().uuid(),installation_id:e.string().uuid().optional(),occurred_at:e.string().datetime(),source:n,event_type:i,evidence_level:a,session_id:e.string().trim().min(1).max(512).optional(),model:e.string().trim().min(1).max(512).optional(),tool:e.string().trim().min(1).max(512).optional(),skill:e.string().trim().min(1).max(512).optional(),cwd:e.string().trim().min(1).max(512).optional(),repository:e.string().trim().min(1).max(512).optional(),branch:e.string().trim().min(1).max(512).optional(),commit_sha:e.string().regex(/^[a-f0-9]{7,64}$/i).optional(),artifact_type:e.enum(["code","pull_request","document","deck","spreadsheet","analysis","workflow","decision","other"]).optional(),attributes:v}),h=r.extend({schema_version:e.literal(1)}).strict(),x=r.extend({schema_version:e.literal(2),workflow_id:e.string().uuid().optional(),initiative_id:e.string().trim().min(1).max(512).optional(),playbook_id:e.string().trim().min(1).max(512).optional(),playbook_version:e.string().trim().min(1).max(512).optional()}).strict(),s=e.object({schema_version:e.literal(3),collection_policy_version:e.literal("metadata-v3"),event_id:e.string().uuid(),installation_id:e.string().uuid().optional(),occurred_at:e.string().datetime(),source:n,event_type:i,evidence_level:a,session_id:o,workflow_id:e.string().uuid().optional(),initiative_id:o,playbook_id:o,playbook_version:o,repository_id:e.string().uuid().optional(),commit_sha:e.string().regex(/^[a-f0-9]{40}$/i).optional(),model_family:p.optional(),tool_category:l.optional(),blocker_category:m.optional(),artifact_type:e.enum(["code","pull_request","document","deck","spreadsheet","analysis","workflow","decision","other"]).optional(),attributes:e.object({client_event:e.enum(["session_start","session_end","before_agent","after_agent","stop","tool_success","tool_failure","model_complete","local_output","local_activity","unknown"]),approval_mode:_.optional(),permission_mode:u.optional(),success:e.boolean().optional(),exit_code_bucket:g.optional(),duration_bucket:d.optional(),input_tokens:e.number().int().nonnegative().optional(),output_tokens:e.number().int().nonnegative().optional(),cached_tokens:e.number().int().nonnegative().optional(),files_changed_bucket:e.enum(["one","two_to_five","six_to_twenty","over_twenty","unknown"]).optional(),local_signal:e.enum(["approved_output","app_state_change"]).optional()}).strict()}).strict().superRefine((t,c)=>{t.commit_sha&&!t.repository_id&&c.addIssue({code:e.ZodIssueCode.custom,path:["commit_sha"],message:"commit_sha requires an approved repository_id"})}),b=e.discriminatedUnion("schema_version",[h,x]),f=s,y=e.object({events:e.array(s).min(1).max(250)}).strict();export{_ as PaceApprovalModeSchema,y as PaceBatchSchema,m as PaceBlockerCategorySchema,d as PaceDurationBucketSchema,f as PaceEventSchema,i as PaceEventTypeSchema,h as PaceEventV1Schema,x as PaceEventV2Schema,s as PaceEventV3Schema,a as PaceEvidenceLevelSchema,g as PaceExitCodeBucketSchema,b as PaceLegacyEventSchema,p as PaceModelFamilySchema,u as PacePermissionModeSchema,n as PaceSourceSchema,l as PaceToolCategorySchema};
@@ -176,4 +176,3 @@ export declare function sweepLocalSessions(input: {
176
176
  queued: number;
177
177
  }>;
178
178
  export {};
179
- //# sourceMappingURL=session-summary.d.ts.map
@@ -1,140 +1,2 @@
1
- import fs from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { z } from "zod";
5
- import { collectLocalSessions } from "./local-sessions.js";
6
- import { approvedRepositoryId, readRepositoryPolicy, sessionCommitShas } from "./repository.js";
7
- function readPolicySkills() {
8
- return readRepositoryPolicy()?.skills ?? [];
9
- }
10
- /**
11
- * One content-free summary per AI coding session, uploaded to Unfairly
12
- * Intelligence. This is the unit the server joins to GitHub pull requests and
13
- * prices into spend. Keep in sync with src/lib/ai-pace/sessions.ts.
14
- */
15
- const MODEL_ID = /^(claude-[a-z0-9.-]{1,48}|gpt-[a-z0-9.-]{1,32}|o[1-9][a-z0-9-]{0,20}|gemini-[a-z0-9.-]{1,40}|codex-[a-z0-9.-]{1,32})$/;
16
- const Count = z.number().int().nonnegative();
17
- const SKILL_ID = /^[a-z0-9][a-z0-9:_.-]{0,119}$/;
18
- /** Published plugin skills are namespaced (`superpowers:brainstorming`); a public name, not content. */
19
- const PUBLIC_SKILL = /^[a-z0-9][a-z0-9-]{0,40}:[a-z0-9][a-z0-9-]{0,78}$/;
20
- /**
21
- * Splits a session's skills into names that may be reported (published plugin
22
- * skills and the organization's own catalog) and a count of everything else,
23
- * such as personal or project skills, whose names never leave the device.
24
- */
25
- export function reportableSkills(skills, orgCatalog = []) {
26
- const catalog = new Set(orgCatalog);
27
- const reportable = [...new Set(skills)].filter((skill) => SKILL_ID.test(skill) && (PUBLIC_SKILL.test(skill) || catalog.has(skill)));
28
- return { skills: reportable.slice(0, 20), unlisted: new Set(skills).size - reportable.length };
29
- }
30
- export const SessionSummaryUploadSchema = z.object({
31
- schema_version: z.literal(1),
32
- collection_policy_version: z.literal("metadata-v3"),
33
- installation_id: z.string().uuid(),
34
- session_id: z.string().regex(/^[A-Za-z0-9_-]{1,128}$/),
35
- source: z.enum(["claude_code", "codex"]),
36
- collected_via: z.enum(["hook", "backfill"]),
37
- started_at: z.string().datetime(),
38
- ended_at: z.string().datetime(),
39
- model_id: z.string().regex(MODEL_ID).nullable(),
40
- tokens: z.object({ input: Count, output: Count, cache_read: Count, cache_write: Count }).strict(),
41
- user_turns: Count,
42
- tool_calls: Count,
43
- tool_errors: Count,
44
- approval_denials: Count,
45
- delegated: z.boolean(),
46
- launched_by: z.enum(["claude_code"]).nullable(),
47
- repository_id: z.string().uuid().nullable(),
48
- commit_shas: z.array(z.string().regex(/^[a-f0-9]{40}$/)).max(50),
49
- skills: z.array(z.string().regex(SKILL_ID)).max(20),
50
- unlisted_skills: Count,
51
- }).strict().superRefine((summary, context) => {
52
- if (summary.commit_shas.length && !summary.repository_id) {
53
- context.addIssue({ code: z.ZodIssueCode.custom, path: ["commit_shas"], message: "commit_shas require an approved repository_id" });
54
- }
55
- });
56
- /** Only recognizable model identifiers leave the device; anything else is reported as unknown. */
57
- export function allowlistedModelId(model) {
58
- if (!model)
59
- return null;
60
- const normalized = model.trim().toLowerCase();
61
- return MODEL_ID.test(normalized) ? normalized : null;
62
- }
63
- export function toSessionUpload(session, context) {
64
- const repositoryId = approvedRepositoryId(session.cwd, context.policy) ?? null;
65
- const commits = repositoryId && session.cwd ? sessionCommitShas(session.cwd, session).map((sha) => sha.toLowerCase()) : [];
66
- const skillReport = reportableSkills(session.skills ?? [], context.policy?.skills ?? readPolicySkills());
67
- return SessionSummaryUploadSchema.parse({
68
- schema_version: 1,
69
- collection_policy_version: "metadata-v3",
70
- installation_id: context.installationId,
71
- session_id: session.sessionId.replace(/[^A-Za-z0-9_-]/g, "").slice(0, 128) || "unknown",
72
- source: session.client,
73
- collected_via: context.collectedVia,
74
- started_at: new Date(session.startedAt).toISOString(),
75
- ended_at: new Date(session.endedAt).toISOString(),
76
- model_id: allowlistedModelId(session.primaryModel),
77
- tokens: { input: session.tokens.input, output: session.tokens.output, cache_read: session.tokens.cacheRead, cache_write: session.tokens.cacheWrite },
78
- user_turns: session.userTurns,
79
- tool_calls: session.toolCalls,
80
- tool_errors: session.toolErrors,
81
- approval_denials: session.approvalDenials,
82
- delegated: session.delegated,
83
- launched_by: session.launchedBy,
84
- repository_id: repositoryId,
85
- commit_shas: commits,
86
- skills: skillReport.skills,
87
- unlisted_skills: skillReport.unlisted,
88
- });
89
- }
90
- // Pending uploads, keyed by source and session so a later, fuller summary of a
91
- // still-running session replaces the earlier one.
92
- export function sessionSpoolPath() {
93
- return process.env.UNFAIRLY_PACE_SESSION_SPOOL ?? path.join(os.homedir(), ".unfairly", "pace", "sessions.json");
94
- }
95
- const spoolKey = (summary) => `${summary.source}:${summary.session_id}`;
96
- export async function readSessionSpool() {
97
- try {
98
- const parsed = JSON.parse(await fs.readFile(sessionSpoolPath(), "utf8"));
99
- const valid = {};
100
- for (const [key, value] of Object.entries(parsed)) {
101
- const summary = SessionSummaryUploadSchema.safeParse(value);
102
- if (summary.success)
103
- valid[key] = summary.data;
104
- }
105
- return valid;
106
- }
107
- catch {
108
- return {};
109
- }
110
- }
111
- async function writeSessionSpool(spool) {
112
- const file = sessionSpoolPath();
113
- await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
114
- await fs.writeFile(file, `${JSON.stringify(spool)}\n`, { encoding: "utf8", mode: 0o600 });
115
- }
116
- export async function queueSessionSummaries(summaries) {
117
- const spool = await readSessionSpool();
118
- for (const summary of summaries)
119
- spool[spoolKey(summary)] = summary;
120
- await writeSessionSpool(spool);
121
- return Object.keys(spool).length;
122
- }
123
- export async function removeSessionSummaries(sent) {
124
- const spool = await readSessionSpool();
125
- for (const summary of sent) {
126
- const current = spool[spoolKey(summary)];
127
- // Keep a newer summary that arrived while this batch was uploading.
128
- if (current && current.ended_at === summary.ended_at)
129
- delete spool[spoolKey(summary)];
130
- }
131
- await writeSessionSpool(spool);
132
- }
133
- /** Summarizes every local session whose transcript changed since `since` and queues it for upload. */
134
- export async function sweepLocalSessions(input) {
135
- const sessions = await collectLocalSessions({ since: input.since, home: input.home });
136
- const summaries = sessions.map((session) => toSessionUpload(session, input));
137
- const queued = summaries.length ? await queueSessionSummaries(summaries) : (Object.keys(await readSessionSpool()).length);
138
- return { summarized: summaries.length, queued };
139
- }
140
- //# sourceMappingURL=session-summary.js.map
1
+ import n from"node:fs/promises";import y from"node:os";import d from"node:path";import{z as o}from"zod";import{collectLocalSessions as S}from"./local-sessions.js";import{approvedRepositoryId as g,readRepositoryPolicy as w,sessionCommitShas as k}from"./repository.js";function v(){return w()?.skills??[]}const u=/^(claude-[a-z0-9.-]{1,48}|gpt-[a-z0-9.-]{1,32}|o[1-9][a-z0-9-]{0,20}|gemini-[a-z0-9.-]{1,40}|codex-[a-z0-9.-]{1,32})$/,i=o.number().int().nonnegative(),m=/^[a-z0-9][a-z0-9:_.-]{0,119}$/,z=/^[a-z0-9][a-z0-9-]{0,40}:[a-z0-9][a-z0-9-]{0,78}$/;function I(e,t=[]){const a=new Set(t),r=[...new Set(e)].filter(s=>m.test(s)&&(z.test(s)||a.has(s)));return{skills:r.slice(0,20),unlisted:new Set(e).size-r.length}}const p=o.object({schema_version:o.literal(1),collection_policy_version:o.literal("metadata-v3"),installation_id:o.string().uuid(),session_id:o.string().regex(/^[A-Za-z0-9_-]{1,128}$/),source:o.enum(["claude_code","codex"]),collected_via:o.enum(["hook","backfill"]),started_at:o.string().datetime(),ended_at:o.string().datetime(),model_id:o.string().regex(u).nullable(),tokens:o.object({input:i,output:i,cache_read:i,cache_write:i}).strict(),user_turns:i,tool_calls:i,tool_errors:i,approval_denials:i,delegated:o.boolean(),launched_by:o.enum(["claude_code"]).nullable(),repository_id:o.string().uuid().nullable(),commit_shas:o.array(o.string().regex(/^[a-f0-9]{40}$/)).max(50),skills:o.array(o.string().regex(m)).max(20),unlisted_skills:i}).strict().superRefine((e,t)=>{e.commit_shas.length&&!e.repository_id&&t.addIssue({code:o.ZodIssueCode.custom,path:["commit_shas"],message:"commit_shas require an approved repository_id"})});function b(e){if(!e)return null;const t=e.trim().toLowerCase();return u.test(t)?t:null}function x(e,t){const a=g(e.cwd,t.policy)??null,r=a&&e.cwd?k(e.cwd,e).map(h=>h.toLowerCase()):[],s=I(e.skills??[],t.policy?.skills??v());return p.parse({schema_version:1,collection_policy_version:"metadata-v3",installation_id:t.installationId,session_id:e.sessionId.replace(/[^A-Za-z0-9_-]/g,"").slice(0,128)||"unknown",source:e.client,collected_via:t.collectedVia,started_at:new Date(e.startedAt).toISOString(),ended_at:new Date(e.endedAt).toISOString(),model_id:b(e.primaryModel),tokens:{input:e.tokens.input,output:e.tokens.output,cache_read:e.tokens.cacheRead,cache_write:e.tokens.cacheWrite},user_turns:e.userTurns,tool_calls:e.toolCalls,tool_errors:e.toolErrors,approval_denials:e.approvalDenials,delegated:e.delegated,launched_by:e.launchedBy,repository_id:a,commit_shas:r,skills:s.skills,unlisted_skills:s.unlisted})}function _(){return process.env.UNFAIRLY_PACE_SESSION_SPOOL??d.join(y.homedir(),".unfairly","pace","sessions.json")}const l=e=>`${e.source}:${e.session_id}`;async function c(){try{const e=JSON.parse(await n.readFile(_(),"utf8")),t={};for(const[a,r]of Object.entries(e)){const s=p.safeParse(r);s.success&&(t[a]=s.data)}return t}catch{return{}}}async function f(e){const t=_();await n.mkdir(d.dirname(t),{recursive:!0,mode:448}),await n.writeFile(t,`${JSON.stringify(e)}
2
+ `,{encoding:"utf8",mode:384})}async function L(e){const t=await c();for(const a of e)t[l(a)]=a;return await f(t),Object.keys(t).length}async function D(e){const t=await c();for(const a of e){const r=t[l(a)];r&&r.ended_at===a.ended_at&&delete t[l(a)]}await f(t)}async function R(e){const a=(await S({since:e.since,home:e.home})).map(s=>x(s,e)),r=a.length?await L(a):Object.keys(await c()).length;return{summarized:a.length,queued:r}}export{p as SessionSummaryUploadSchema,b as allowlistedModelId,L as queueSessionSummaries,c as readSessionSpool,D as removeSessionSummaries,I as reportableSkills,_ as sessionSpoolPath,R as sweepLocalSessions,x as toSessionUpload};
@@ -3,4 +3,3 @@ export declare function paceSpoolPath(): string;
3
3
  export declare function appendPaceEvent(event: PaceEvent): Promise<string>;
4
4
  export declare function readPaceEvents(limit?: number): Promise<PaceEvent[]>;
5
5
  export declare function removePaceEvents(ids: Set<string>): Promise<void>;
6
- //# sourceMappingURL=spool.d.ts.map
@@ -1,87 +1,8 @@
1
- import fs from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { migrateLegacyEvent } from "./normalize.js";
5
- import { PaceEventSchema, PaceLegacyEventSchema } from "./schema.js";
6
- export function paceSpoolPath() {
7
- return process.env.UNFAIRLY_PACE_SPOOL ?? path.join(os.homedir(), ".unfairly", "pace", "events.jsonl");
8
- }
9
- export async function appendPaceEvent(event) {
10
- const parsed = PaceEventSchema.parse(event);
11
- const spool = paceSpoolPath();
12
- await fs.mkdir(path.dirname(spool), { recursive: true, mode: 0o700 });
13
- await fs.appendFile(spool, `${JSON.stringify(parsed)}\n`, { encoding: "utf8", mode: 0o600 });
14
- await fs.chmod(spool, 0o600).catch(() => undefined);
15
- return spool;
16
- }
17
- export async function readPaceEvents(limit = 250) {
18
- let raw;
19
- try {
20
- raw = await fs.readFile(paceSpoolPath(), "utf8");
21
- }
22
- catch (error) {
23
- if (error.code === "ENOENT")
24
- return [];
25
- throw error;
26
- }
27
- const events = [];
28
- let needsMigration = false;
29
- for (const line of raw.split("\n")) {
30
- if (!line.trim())
31
- continue;
32
- let candidate;
33
- try {
34
- candidate = JSON.parse(line);
35
- }
36
- catch {
37
- needsMigration = true;
38
- continue;
39
- }
40
- const parsed = PaceEventSchema.safeParse(candidate);
41
- if (parsed.success) {
42
- events.push(parsed.data);
43
- }
44
- else {
45
- const legacy = PaceLegacyEventSchema.safeParse(candidate);
46
- if (legacy.success)
47
- events.push(migrateLegacyEvent(legacy.data));
48
- needsMigration = true;
49
- }
50
- }
51
- if (needsMigration) {
52
- const spool = paceSpoolPath();
53
- await fs.writeFile(spool, events.length ? `${events.map((event) => JSON.stringify(event)).join("\n")}\n` : "", {
54
- encoding: "utf8",
55
- mode: 0o600,
56
- });
57
- }
58
- return events.slice(0, limit);
59
- }
60
- export async function removePaceEvents(ids) {
61
- const spool = paceSpoolPath();
62
- let raw;
63
- try {
64
- raw = await fs.readFile(spool, "utf8");
65
- }
66
- catch (error) {
67
- if (error.code === "ENOENT")
68
- return;
69
- throw error;
70
- }
71
- const remaining = raw.split("\n").filter((line) => {
72
- if (!line.trim())
73
- return false;
74
- try {
75
- const event = JSON.parse(line);
76
- return !event.event_id || !ids.has(event.event_id);
77
- }
78
- catch {
79
- return true;
80
- }
81
- });
82
- await fs.writeFile(spool, remaining.length ? `${remaining.join("\n")}\n` : "", {
83
- encoding: "utf8",
84
- mode: 0o600,
85
- });
86
- }
87
- //# sourceMappingURL=spool.js.map
1
+ import o from"node:fs/promises";import d from"node:os";import p from"node:path";import{migrateLegacyEvent as m}from"./normalize.js";import{PaceEventSchema as u,PaceLegacyEventSchema as l}from"./schema.js";function c(){return process.env.UNFAIRLY_PACE_SPOOL??p.join(d.homedir(),".unfairly","pace","events.jsonl")}async function E(i){const r=u.parse(i),e=c();return await o.mkdir(p.dirname(e),{recursive:!0,mode:448}),await o.appendFile(e,`${JSON.stringify(r)}
2
+ `,{encoding:"utf8",mode:384}),await o.chmod(e,384).catch(()=>{}),e}async function P(i=250){let r;try{r=await o.readFile(c(),"utf8")}catch(t){if(t.code==="ENOENT")return[];throw t}const e=[];let a=!1;for(const t of r.split(`
3
+ `)){if(!t.trim())continue;let n;try{n=JSON.parse(t)}catch{a=!0;continue}const s=u.safeParse(n);if(s.success)e.push(s.data);else{const f=l.safeParse(n);f.success&&e.push(m(f.data)),a=!0}}if(a){const t=c();await o.writeFile(t,e.length?`${e.map(n=>JSON.stringify(n)).join(`
4
+ `)}
5
+ `:"",{encoding:"utf8",mode:384})}return e.slice(0,i)}async function N(i){const r=c();let e;try{e=await o.readFile(r,"utf8")}catch(t){if(t.code==="ENOENT")return;throw t}const a=e.split(`
6
+ `).filter(t=>{if(!t.trim())return!1;try{const n=JSON.parse(t);return!n.event_id||!i.has(n.event_id)}catch{return!0}});await o.writeFile(r,a.length?`${a.join(`
7
+ `)}
8
+ `:"",{encoding:"utf8",mode:384})}export{E as appendPaceEvent,c as paceSpoolPath,P as readPaceEvents,N as removePaceEvents};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import{spawn as l}from"node:child_process";import c from"node:os";import{runPaceCapture as u,runPaceSync as f}from"./cmd/pace.js";import{readOrCreatePaceDevice as w}from"./pace/device.js";import{acquireApprovalLock as d,awaitApproval as m,ensureEnrolled as v,releaseApprovalLock as A,sessionStartOutput as y}from"./pace/enroll.js";const U="https://app.unfairly.ai",p=()=>(process.env.UNFAIRLY_API_URL??U).replace(/\/$/,"");async function h(a,n){const t=await w();if(t.paused)return;const r=c.platform(),e=c.arch();if(r!=="darwin"&&r!=="linux"&&r!=="win32"||e!=="x64"&&e!=="arm64")return;const i=await v({apiUrl:p(),installationId:t.installation_id,platform:r,arch:e,clients:[a],orgHint:process.env.UNFAIRLY_ORG}),o=y(i,n==="sessionStart"?"sessionStart":"SessionStart");if(o&&process.stdout.write(`${o}
2
+ `),i.status==="waiting"){const s=l(process.execPath,[process.argv[1],"await-approval"],{detached:!0,stdio:"ignore",env:process.env});s.on("error",()=>{}),s.unref()}}async function S(){if(await d())try{await m({apiUrl:p()})&&await f()}finally{await A()}}async function L(){const[a,n="unknown",t="Unknown"]=process.argv.slice(2);a==="connect"?await h(n,t):a==="await-approval"?await S():a==="capture"&&await u(n,t)}L().catch(()=>{}).finally(()=>process.exit(0));
@@ -77,4 +77,3 @@ export declare class SyncClient {
77
77
  }>;
78
78
  status(): Promise<StatusResponse>;
79
79
  }
80
- //# sourceMappingURL=api.d.ts.map
package/dist/sync/api.js CHANGED
@@ -1,55 +1 @@
1
- export class SyncClient {
2
- apiUrl;
3
- token;
4
- constructor(apiUrl, token) {
5
- this.apiUrl = apiUrl;
6
- this.token = token;
7
- }
8
- headers() {
9
- return {
10
- Authorization: `Bearer ${this.token}`,
11
- "Content-Type": "application/json",
12
- };
13
- }
14
- async pull(vault, since) {
15
- const url = new URL(`${this.apiUrl}/api/sync/pull`);
16
- url.searchParams.set("vault", vault);
17
- if (since)
18
- url.searchParams.set("since", since);
19
- const res = await fetch(url, { headers: this.headers() });
20
- if (!res.ok) {
21
- throw new Error(`pull failed: ${res.status} ${await res.text()}`);
22
- }
23
- return res.json();
24
- }
25
- /** Loop pull until has_more=false, merging pages. */
26
- async pullAll(vault, since) {
27
- const merged = await this.pull(vault, since);
28
- while (merged.has_more && merged.next_cursor) {
29
- const next = await this.pull(vault, merged.next_cursor);
30
- merged.docs.push(...next.docs);
31
- merged.deleted.push(...next.deleted);
32
- merged.next_cursor = next.next_cursor;
33
- merged.has_more = next.has_more;
34
- }
35
- return merged;
36
- }
37
- async push(args) {
38
- const res = await fetch(`${this.apiUrl}/api/sync/push`, {
39
- method: "POST",
40
- headers: this.headers(),
41
- body: JSON.stringify(args),
42
- });
43
- return { status: res.status, data: (await res.json()) };
44
- }
45
- async status() {
46
- const res = await fetch(`${this.apiUrl}/api/sync/status`, {
47
- headers: this.headers(),
48
- });
49
- if (!res.ok) {
50
- throw new Error(`status failed: ${res.status} ${await res.text()}`);
51
- }
52
- return res.json();
53
- }
54
- }
55
- //# sourceMappingURL=api.js.map
1
+ class n{apiUrl;token;constructor(t,e){this.apiUrl=t,this.token=e}headers(){return{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"}}async pull(t,e){const s=new URL(`${this.apiUrl}/api/sync/pull`);s.searchParams.set("vault",t),e&&s.searchParams.set("since",e);const a=await fetch(s,{headers:this.headers()});if(!a.ok)throw new Error(`pull failed: ${a.status} ${await a.text()}`);return a.json()}async pullAll(t,e){const s=await this.pull(t,e);for(;s.has_more&&s.next_cursor;){const a=await this.pull(t,s.next_cursor);s.docs.push(...a.docs),s.deleted.push(...a.deleted),s.next_cursor=a.next_cursor,s.has_more=a.has_more}return s}async push(t){const e=await fetch(`${this.apiUrl}/api/sync/push`,{method:"POST",headers:this.headers(),body:JSON.stringify(t)});return{status:e.status,data:await e.json()}}async status(){const t=await fetch(`${this.apiUrl}/api/sync/status`,{headers:this.headers()});if(!t.ok)throw new Error(`status failed: ${t.status} ${await t.text()}`);return t.json()}}export{n as SyncClient};
@@ -4,4 +4,3 @@ import type { Manifest } from "./manifest.js";
4
4
  * frontmatter, build the autoloader index, and write CLAUDE.md + .gitignore.
5
5
  */
6
6
  export declare function writeAutoloader(unfairlyRoot: string, manifest: Manifest): Promise<void>;
7
- //# sourceMappingURL=autoloader.d.ts.map
@@ -1,128 +1,8 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- import matter from "gray-matter";
4
- const SENTINEL_HEADER = "# Unfairly Vault Context (auto-generated by `unfairly`)";
5
- const GENERATED_CLAUDE_MD = "CLAUDE.md";
6
- /**
7
- * After `unfairly pull` populates .unfairly/, scan local files, parse
8
- * frontmatter, build the autoloader index, and write CLAUDE.md + .gitignore.
9
- */
10
- export async function writeAutoloader(unfairlyRoot, manifest) {
11
- const docs = await scanLocalDocs(unfairlyRoot, manifest);
12
- const goldPrimitives = docs
13
- .filter((d) => d.trust_tier === "gold" && d.role === "primitive")
14
- .sort((a, b) => (b.updated_at ?? "").localeCompare(a.updated_at ?? ""))
15
- .slice(0, 8);
16
- const recentSilver = docs
17
- .filter((d) => d.trust_tier === "silver")
18
- .sort((a, b) => (b.updated_at ?? "").localeCompare(a.updated_at ?? ""))
19
- .slice(0, 5);
20
- const claudeMd = renderClaudeMd(goldPrimitives, recentSilver);
21
- await fs.writeFile(path.join(unfairlyRoot, GENERATED_CLAUDE_MD), claudeMd, "utf8");
22
- // .gitignore: write only if missing. The generated file invites users to
23
- // uncomment !vault/ re-includes — regenerating on every pull would wipe them.
24
- const gitignorePath = path.join(unfairlyRoot, ".gitignore");
25
- try {
26
- await fs.access(gitignorePath);
27
- }
28
- catch {
29
- await fs.writeFile(gitignorePath, renderGitignore(), "utf8");
30
- }
31
- }
32
- async function scanLocalDocs(root, manifest) {
33
- const entries = [];
34
- // manifest.vaults is Record<string, ManifestVault> keyed by slug.
35
- for (const vault of Object.values(manifest.vaults)) {
36
- // Repo-bound vaults live at the root; non-tracked vaults live under cache/.
37
- const vaultRoot = vault.track_in_git
38
- ? path.join(root, vault.slug)
39
- : path.join(root, "cache", vault.slug);
40
- try {
41
- await walkAndParse(vaultRoot, root, vault.slug, entries);
42
- }
43
- catch {
44
- // Vault dir doesn't exist yet (e.g., empty vault) — fine, skip.
45
- }
46
- }
47
- return entries;
48
- }
49
- async function walkAndParse(dir, unfairlyRoot, vaultSlug, out) {
50
- const generatedRootPath = path.join(unfairlyRoot, GENERATED_CLAUDE_MD);
51
- const items = await fs.readdir(dir, { withFileTypes: true });
52
- for (const item of items) {
53
- const full = path.join(dir, item.name);
54
- if (item.isDirectory()) {
55
- await walkAndParse(full, unfairlyRoot, vaultSlug, out);
56
- }
57
- else if (item.name.endsWith(".md")) {
58
- // Skip ONLY our own generated file (not user docs that happen to be named CLAUDE.md).
59
- if (path.resolve(full) === path.resolve(generatedRootPath))
60
- continue;
61
- try {
62
- const raw = await fs.readFile(full, "utf8");
63
- const { data } = matter(raw);
64
- // Normalize to forward slashes — @-import processors expect POSIX-style paths.
65
- const relative = path.relative(unfairlyRoot, full).split(path.sep).join("/");
66
- // gray-matter / js-yaml auto-coerces ISO 8601 timestamps to Date — normalize to string.
67
- const updatedAtRaw = data.updated_at;
68
- const updated_at = updatedAtRaw instanceof Date
69
- ? updatedAtRaw.toISOString()
70
- : typeof updatedAtRaw === "string"
71
- ? updatedAtRaw
72
- : undefined;
73
- out.push({
74
- relativePath: relative,
75
- trust_tier: typeof data.trust_tier === "string" ? data.trust_tier : undefined,
76
- role: typeof data.role === "string" ? data.role : undefined,
77
- updated_at,
78
- vaultSlug,
79
- });
80
- }
81
- catch {
82
- // Unparseable (malformed YAML, encoding) — skip.
83
- }
84
- }
85
- }
86
- }
87
- function renderClaudeMd(gold, silver) {
88
- const lines = [];
89
- lines.push(SENTINEL_HEADER);
90
- lines.push("");
91
- lines.push("This directory mirrors your accessible Unfairly vaults. Files below are");
92
- lines.push("the active canon — read them before answering questions about this team's");
93
- lines.push("architecture, decisions, brand, or in-flight work.");
94
- lines.push("");
95
- lines.push("## Canon (gold, top primitives)");
96
- if (gold.length === 0) {
97
- lines.push("(No gold primitives in your accessible vaults yet. Run `unfairly pull` after the team seeds canon docs.)");
98
- }
99
- else {
100
- for (const d of gold)
101
- lines.push(`@${d.relativePath}`);
102
- }
103
- lines.push("");
104
- lines.push("## Working knowledge (silver, recently touched)");
105
- if (silver.length === 0) {
106
- lines.push("(No recent silver docs.)");
107
- }
108
- else {
109
- for (const d of silver)
110
- lines.push(`@${d.relativePath}`);
111
- }
112
- lines.push("");
113
- lines.push("## Conventions");
114
- lines.push("- Trust tier = audience signal, not visibility filter. All tiers are working memory.");
115
- lines.push("- This file is regenerated on every `unfairly init` or `unfairly pull`.");
116
- lines.push(" Don't hand-edit — your changes will be overwritten.");
117
- lines.push("- For real-time multiplayer state (current checkpoints, applied edits,");
118
- lines.push(" pending proposals, online teammates), call `enter_org` via MCP.");
119
- lines.push(" This filesystem mirror is static-as-of-last-pull.");
120
- return lines.join("\n") + "\n";
121
- }
122
- function renderGitignore() {
123
- return `# Default: ignore everything in this directory.
1
+ import{promises as c}from"node:fs";import n from"node:path";import y from"gray-matter";const v="# Unfairly Vault Context (auto-generated by `unfairly`)",h="CLAUDE.md";async function E(t,r){const e=await w(t,r),i=e.filter(s=>s.trust_tier==="gold"&&s.role==="primitive").sort((s,l)=>(l.updated_at??"").localeCompare(s.updated_at??"")).slice(0,8),o=e.filter(s=>s.trust_tier==="silver").sort((s,l)=>(l.updated_at??"").localeCompare(s.updated_at??"")).slice(0,5),p=_(i,o);await c.writeFile(n.join(t,h),p,"utf8");const a=n.join(t,".gitignore");try{await c.access(a)}catch{await c.writeFile(a,b(),"utf8")}}async function w(t,r){const e=[];for(const i of Object.values(r.vaults)){const o=i.track_in_git?n.join(t,i.slug):n.join(t,"cache",i.slug);try{await f(o,t,i.slug,e)}catch{}}return e}async function f(t,r,e,i){const o=n.join(r,h),p=await c.readdir(t,{withFileTypes:!0});for(const a of p){const s=n.join(t,a.name);if(a.isDirectory())await f(s,r,e,i);else if(a.name.endsWith(".md")){if(n.resolve(s)===n.resolve(o))continue;try{const l=await c.readFile(s,"utf8"),{data:u}=y(l),g=n.relative(r,s).split(n.sep).join("/"),d=u.updated_at,m=d instanceof Date?d.toISOString():typeof d=="string"?d:void 0;i.push({relativePath:g,trust_tier:typeof u.trust_tier=="string"?u.trust_tier:void 0,role:typeof u.role=="string"?u.role:void 0,updated_at:m,vaultSlug:e})}catch{}}}}function _(t,r){const e=[];if(e.push(v),e.push(""),e.push("This directory mirrors your accessible Unfairly vaults. Files below are"),e.push("the active canon \u2014 read them before answering questions about this team's"),e.push("architecture, decisions, brand, or in-flight work."),e.push(""),e.push("## Canon (gold, top primitives)"),t.length===0)e.push("(No gold primitives in your accessible vaults yet. Run `unfairly pull` after the team seeds canon docs.)");else for(const i of t)e.push(`@${i.relativePath}`);if(e.push(""),e.push("## Working knowledge (silver, recently touched)"),r.length===0)e.push("(No recent silver docs.)");else for(const i of r)e.push(`@${i.relativePath}`);return e.push(""),e.push("## Conventions"),e.push("- Trust tier = audience signal, not visibility filter. All tiers are working memory."),e.push("- This file is regenerated on every `unfairly init` or `unfairly pull`."),e.push(" Don't hand-edit \u2014 your changes will be overwritten."),e.push("- For real-time multiplayer state (current checkpoints, applied edits,"),e.push(" pending proposals, online teammates), call `enter_org` via MCP."),e.push(" This filesystem mirror is static-as-of-last-pull."),e.join(`
2
+ `)+`
3
+ `}function b(){return`# Default: ignore everything in this directory.
124
4
  # .unfairly/ is a local cache of vault content. By default, none of it is
125
- # committed — agents read from disk; the source of truth is in cloud vaults.
5
+ # committed \u2014 agents read from disk; the source of truth is in cloud vaults.
126
6
  *
127
7
 
128
8
  # Re-include the autoloader and this gitignore so Claude Code finds them:
@@ -133,6 +13,4 @@ function renderGitignore() {
133
13
  # whose docs should be versioned alongside code), add explicit re-includes:
134
14
  # !unfairly-app/
135
15
  # !unfairly-app/**
136
- `;
137
- }
138
- //# sourceMappingURL=autoloader.js.map
16
+ `}export{E as writeAutoloader};
@@ -9,4 +9,3 @@ export declare function buildConflictFile(args: {
9
9
  serverBody: string;
10
10
  serverUpdatedAt: string;
11
11
  }): string;
12
- //# sourceMappingURL=conflict.d.ts.map