@made-by-moonlight/athene-core 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +241 -0
  3. package/dist/activity-events.d.ts +42 -0
  4. package/dist/activity-events.d.ts.map +1 -0
  5. package/dist/activity-events.js +192 -0
  6. package/dist/activity-events.js.map +1 -0
  7. package/dist/activity-log.d.ts +71 -0
  8. package/dist/activity-log.d.ts.map +1 -0
  9. package/dist/activity-log.js +203 -0
  10. package/dist/activity-log.js.map +1 -0
  11. package/dist/activity-signal.d.ts +20 -0
  12. package/dist/activity-signal.d.ts.map +1 -0
  13. package/dist/activity-signal.js +91 -0
  14. package/dist/activity-signal.js.map +1 -0
  15. package/dist/agent-report.d.ts +148 -0
  16. package/dist/agent-report.d.ts.map +1 -0
  17. package/dist/agent-report.js +516 -0
  18. package/dist/agent-report.js.map +1 -0
  19. package/dist/agent-selection.d.ts +31 -0
  20. package/dist/agent-selection.d.ts.map +1 -0
  21. package/dist/agent-selection.js +69 -0
  22. package/dist/agent-selection.js.map +1 -0
  23. package/dist/agent-workspace-hooks.d.ts +74 -0
  24. package/dist/agent-workspace-hooks.d.ts.map +1 -0
  25. package/dist/agent-workspace-hooks.js +988 -0
  26. package/dist/agent-workspace-hooks.js.map +1 -0
  27. package/dist/atomic-write.d.ts +6 -0
  28. package/dist/atomic-write.d.ts.map +1 -0
  29. package/dist/atomic-write.js +49 -0
  30. package/dist/atomic-write.js.map +1 -0
  31. package/dist/cleanup-stack.d.ts +37 -0
  32. package/dist/cleanup-stack.d.ts.map +1 -0
  33. package/dist/cleanup-stack.js +45 -0
  34. package/dist/cleanup-stack.js.map +1 -0
  35. package/dist/code-review-manager.d.ts +118 -0
  36. package/dist/code-review-manager.d.ts.map +1 -0
  37. package/dist/code-review-manager.js +719 -0
  38. package/dist/code-review-manager.js.map +1 -0
  39. package/dist/code-review-store.d.ts +114 -0
  40. package/dist/code-review-store.d.ts.map +1 -0
  41. package/dist/code-review-store.js +346 -0
  42. package/dist/code-review-store.js.map +1 -0
  43. package/dist/config-generator.d.ts +84 -0
  44. package/dist/config-generator.d.ts.map +1 -0
  45. package/dist/config-generator.js +295 -0
  46. package/dist/config-generator.js.map +1 -0
  47. package/dist/config.d.ts +55 -0
  48. package/dist/config.d.ts.map +1 -0
  49. package/dist/config.js +852 -0
  50. package/dist/config.js.map +1 -0
  51. package/dist/daemon-children.d.ts +55 -0
  52. package/dist/daemon-children.d.ts.map +1 -0
  53. package/dist/daemon-children.js +435 -0
  54. package/dist/daemon-children.js.map +1 -0
  55. package/dist/dashboard-notifications.d.ts +42 -0
  56. package/dist/dashboard-notifications.d.ts.map +1 -0
  57. package/dist/dashboard-notifications.js +123 -0
  58. package/dist/dashboard-notifications.js.map +1 -0
  59. package/dist/events-db.d.ts +39 -0
  60. package/dist/events-db.d.ts.map +1 -0
  61. package/dist/events-db.js +185 -0
  62. package/dist/events-db.js.map +1 -0
  63. package/dist/feature-flags.d.ts +2 -0
  64. package/dist/feature-flags.d.ts.map +1 -0
  65. package/dist/feature-flags.js +9 -0
  66. package/dist/feature-flags.js.map +1 -0
  67. package/dist/feedback-tools.d.ts +97 -0
  68. package/dist/feedback-tools.d.ts.map +1 -0
  69. package/dist/feedback-tools.js +161 -0
  70. package/dist/feedback-tools.js.map +1 -0
  71. package/dist/file-lock.d.ts +5 -0
  72. package/dist/file-lock.d.ts.map +1 -0
  73. package/dist/file-lock.js +59 -0
  74. package/dist/file-lock.js.map +1 -0
  75. package/dist/format-automated-comments.d.ts +18 -0
  76. package/dist/format-automated-comments.d.ts.map +1 -0
  77. package/dist/gh-trace.d.ts +57 -0
  78. package/dist/gh-trace.d.ts.map +1 -0
  79. package/dist/gh-trace.js +320 -0
  80. package/dist/gh-trace.js.map +1 -0
  81. package/dist/git-activity.d.ts +10 -0
  82. package/dist/git-activity.d.ts.map +1 -0
  83. package/dist/git-activity.js +30 -0
  84. package/dist/git-activity.js.map +1 -0
  85. package/dist/global-config.d.ts +1085 -0
  86. package/dist/global-config.d.ts.map +1 -0
  87. package/dist/global-config.js +1067 -0
  88. package/dist/global-config.js.map +1 -0
  89. package/dist/index.d.ts +91 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +59 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/key-value.d.ts +7 -0
  94. package/dist/key-value.d.ts.map +1 -0
  95. package/dist/key-value.js +24 -0
  96. package/dist/key-value.js.map +1 -0
  97. package/dist/lifecycle-manager.d.ts +22 -0
  98. package/dist/lifecycle-manager.d.ts.map +1 -0
  99. package/dist/lifecycle-manager.js +2813 -0
  100. package/dist/lifecycle-manager.js.map +1 -0
  101. package/dist/lifecycle-state.d.ts +28 -0
  102. package/dist/lifecycle-state.d.ts.map +1 -0
  103. package/dist/lifecycle-state.js +446 -0
  104. package/dist/lifecycle-state.js.map +1 -0
  105. package/dist/lifecycle-status-decisions.d.ts +85 -0
  106. package/dist/lifecycle-status-decisions.d.ts.map +1 -0
  107. package/dist/lifecycle-status-decisions.js +262 -0
  108. package/dist/lifecycle-status-decisions.js.map +1 -0
  109. package/dist/lifecycle-transition.d.ts +81 -0
  110. package/dist/lifecycle-transition.d.ts.map +1 -0
  111. package/dist/lifecycle-transition.js +207 -0
  112. package/dist/lifecycle-transition.js.map +1 -0
  113. package/dist/metadata.d.ts +54 -0
  114. package/dist/metadata.d.ts.map +1 -0
  115. package/dist/metadata.js +484 -0
  116. package/dist/metadata.js.map +1 -0
  117. package/dist/migration/storage-v2.d.ts +76 -0
  118. package/dist/migration/storage-v2.d.ts.map +1 -0
  119. package/dist/migration/storage-v2.js +1614 -0
  120. package/dist/migration/storage-v2.js.map +1 -0
  121. package/dist/notification-data.d.ts +135 -0
  122. package/dist/notification-data.d.ts.map +1 -0
  123. package/dist/notification-data.js +204 -0
  124. package/dist/notification-data.js.map +1 -0
  125. package/dist/notification-observability.d.ts +21 -0
  126. package/dist/notification-observability.d.ts.map +1 -0
  127. package/dist/notification-observability.js +154 -0
  128. package/dist/notification-observability.js.map +1 -0
  129. package/dist/notifier-resolution.d.ts +14 -0
  130. package/dist/notifier-resolution.d.ts.map +1 -0
  131. package/dist/notifier-resolution.js +23 -0
  132. package/dist/notifier-resolution.js.map +1 -0
  133. package/dist/observability.d.ts +100 -0
  134. package/dist/observability.d.ts.map +1 -0
  135. package/dist/observability.js +535 -0
  136. package/dist/observability.js.map +1 -0
  137. package/dist/opencode-agents-md.d.ts +3 -0
  138. package/dist/opencode-agents-md.d.ts.map +1 -0
  139. package/dist/opencode-agents-md.js +40 -0
  140. package/dist/opencode-agents-md.js.map +1 -0
  141. package/dist/opencode-config.d.ts +2 -0
  142. package/dist/opencode-config.d.ts.map +1 -0
  143. package/dist/opencode-config.js +17 -0
  144. package/dist/opencode-config.js.map +1 -0
  145. package/dist/opencode-session-id.d.ts +2 -0
  146. package/dist/opencode-session-id.d.ts.map +1 -0
  147. package/dist/opencode-session-id.js +12 -0
  148. package/dist/opencode-session-id.js.map +1 -0
  149. package/dist/opencode-shared.d.ts +80 -0
  150. package/dist/opencode-shared.d.ts.map +1 -0
  151. package/dist/opencode-shared.js +202 -0
  152. package/dist/opencode-shared.js.map +1 -0
  153. package/dist/orchestrator-prompt.d.ts +19 -0
  154. package/dist/orchestrator-prompt.d.ts.map +1 -0
  155. package/dist/orchestrator-prompt.js +130 -0
  156. package/dist/orchestrator-prompt.js.map +1 -0
  157. package/dist/orchestrator-session-strategy.d.ts +5 -0
  158. package/dist/orchestrator-session-strategy.d.ts.map +1 -0
  159. package/dist/orchestrator-session-strategy.js +13 -0
  160. package/dist/orchestrator-session-strategy.js.map +1 -0
  161. package/dist/paths.d.ts +145 -0
  162. package/dist/paths.d.ts.map +1 -0
  163. package/dist/paths.js +288 -0
  164. package/dist/paths.js.map +1 -0
  165. package/dist/platform.d.ts +32 -0
  166. package/dist/platform.d.ts.map +1 -0
  167. package/dist/platform.js +211 -0
  168. package/dist/platform.js.map +1 -0
  169. package/dist/plugin-registry.d.ts +15 -0
  170. package/dist/plugin-registry.d.ts.map +1 -0
  171. package/dist/plugin-registry.js +499 -0
  172. package/dist/plugin-registry.js.map +1 -0
  173. package/dist/portfolio-projects.d.ts +7 -0
  174. package/dist/portfolio-projects.d.ts.map +1 -0
  175. package/dist/portfolio-projects.js +65 -0
  176. package/dist/portfolio-projects.js.map +1 -0
  177. package/dist/portfolio-registry.d.ts +42 -0
  178. package/dist/portfolio-registry.d.ts.map +1 -0
  179. package/dist/portfolio-registry.js +311 -0
  180. package/dist/portfolio-registry.js.map +1 -0
  181. package/dist/portfolio-routing.d.ts +5 -0
  182. package/dist/portfolio-routing.d.ts.map +1 -0
  183. package/dist/portfolio-routing.js +24 -0
  184. package/dist/portfolio-routing.js.map +1 -0
  185. package/dist/portfolio-session-service.d.ts +15 -0
  186. package/dist/portfolio-session-service.d.ts.map +1 -0
  187. package/dist/portfolio-session-service.js +206 -0
  188. package/dist/portfolio-session-service.js.map +1 -0
  189. package/dist/process-cache.d.ts +32 -0
  190. package/dist/process-cache.d.ts.map +1 -0
  191. package/dist/process-cache.js +44 -0
  192. package/dist/process-cache.js.map +1 -0
  193. package/dist/project-resolver.d.ts +5 -0
  194. package/dist/project-resolver.d.ts.map +1 -0
  195. package/dist/project-resolver.js +20 -0
  196. package/dist/project-resolver.js.map +1 -0
  197. package/dist/prompt-builder.d.ts +42 -0
  198. package/dist/prompt-builder.d.ts.map +1 -0
  199. package/dist/prompt-builder.js +182 -0
  200. package/dist/prompt-builder.js.map +1 -0
  201. package/dist/prompts/orchestrator.md.js +4 -0
  202. package/dist/prompts/orchestrator.md.js.map +1 -0
  203. package/dist/query-activity-events.d.ts +42 -0
  204. package/dist/query-activity-events.d.ts.map +1 -0
  205. package/dist/query-activity-events.js +170 -0
  206. package/dist/query-activity-events.js.map +1 -0
  207. package/dist/recovery/actions.d.ts +7 -0
  208. package/dist/recovery/actions.d.ts.map +1 -0
  209. package/dist/recovery/index.d.ts +8 -0
  210. package/dist/recovery/index.d.ts.map +1 -0
  211. package/dist/recovery/logger.d.ts +12 -0
  212. package/dist/recovery/logger.d.ts.map +1 -0
  213. package/dist/recovery/manager.d.ts +24 -0
  214. package/dist/recovery/manager.d.ts.map +1 -0
  215. package/dist/recovery/scanner.d.ts +11 -0
  216. package/dist/recovery/scanner.d.ts.map +1 -0
  217. package/dist/recovery/types.d.ts +170 -0
  218. package/dist/recovery/types.d.ts.map +1 -0
  219. package/dist/recovery/validator.d.ts +8 -0
  220. package/dist/recovery/validator.d.ts.map +1 -0
  221. package/dist/report-watcher.d.ts +93 -0
  222. package/dist/report-watcher.d.ts.map +1 -0
  223. package/dist/report-watcher.js +182 -0
  224. package/dist/report-watcher.js.map +1 -0
  225. package/dist/scm-webhook-utils.d.ts +6 -0
  226. package/dist/scm-webhook-utils.d.ts.map +1 -0
  227. package/dist/scm-webhook-utils.js +36 -0
  228. package/dist/scm-webhook-utils.js.map +1 -0
  229. package/dist/session-manager.d.ts +22 -0
  230. package/dist/session-manager.d.ts.map +1 -0
  231. package/dist/session-manager.js +3077 -0
  232. package/dist/session-manager.js.map +1 -0
  233. package/dist/spawn-target.d.ts +23 -0
  234. package/dist/spawn-target.d.ts.map +1 -0
  235. package/dist/spawn-target.js +39 -0
  236. package/dist/spawn-target.js.map +1 -0
  237. package/dist/storage-key.d.ts +9 -0
  238. package/dist/storage-key.d.ts.map +1 -0
  239. package/dist/storage-key.js +59 -0
  240. package/dist/storage-key.js.map +1 -0
  241. package/dist/tmux.d.ts +39 -0
  242. package/dist/tmux.d.ts.map +1 -0
  243. package/dist/tmux.js +141 -0
  244. package/dist/tmux.js.map +1 -0
  245. package/dist/types.d.ts +1496 -0
  246. package/dist/types.d.ts.map +1 -0
  247. package/dist/types.js +215 -0
  248. package/dist/types.js.map +1 -0
  249. package/dist/update-cache.d.ts +59 -0
  250. package/dist/update-cache.d.ts.map +1 -0
  251. package/dist/update-cache.js +77 -0
  252. package/dist/update-cache.js.map +1 -0
  253. package/dist/utils/metadata-flatten.d.ts +3 -0
  254. package/dist/utils/metadata-flatten.d.ts.map +1 -0
  255. package/dist/utils/metadata-flatten.js +18 -0
  256. package/dist/utils/metadata-flatten.js.map +1 -0
  257. package/dist/utils/pr.d.ts +7 -0
  258. package/dist/utils/pr.d.ts.map +1 -0
  259. package/dist/utils/pr.js +97 -0
  260. package/dist/utils/pr.js.map +1 -0
  261. package/dist/utils/session-from-metadata.d.ts +16 -0
  262. package/dist/utils/session-from-metadata.d.ts.map +1 -0
  263. package/dist/utils/session-from-metadata.js +87 -0
  264. package/dist/utils/session-from-metadata.js.map +1 -0
  265. package/dist/utils/session-id.d.ts +4 -0
  266. package/dist/utils/session-id.d.ts.map +1 -0
  267. package/dist/utils/session-id.js +9 -0
  268. package/dist/utils/session-id.js.map +1 -0
  269. package/dist/utils/validation.d.ts +9 -0
  270. package/dist/utils/validation.d.ts.map +1 -0
  271. package/dist/utils/validation.js +45 -0
  272. package/dist/utils/validation.js.map +1 -0
  273. package/dist/utils.d.ts +65 -0
  274. package/dist/utils.d.ts.map +1 -0
  275. package/dist/utils.js +189 -0
  276. package/dist/utils.js.map +1 -0
  277. package/dist/version-compare.d.ts +27 -0
  278. package/dist/version-compare.d.ts.map +1 -0
  279. package/dist/version-compare.js +121 -0
  280. package/dist/version-compare.js.map +1 -0
  281. package/dist/windows-pty-registry.d.ts +27 -0
  282. package/dist/windows-pty-registry.d.ts.map +1 -0
  283. package/dist/windows-pty-registry.js +109 -0
  284. package/dist/windows-pty-registry.js.map +1 -0
  285. package/package.json +110 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolio-registry.d.ts","sourceRoot":"","sources":["../src/portfolio-registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAOpB,OAAO,EAML,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAC;AA+G5B,iFAAiF;AACjF,wBAAgB,cAAc,IAAI,mBAAmB,CAWpD;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAI7D;AAED,uDAAuD;AACvD,wBAAgB,eAAe,IAAI,oBAAoB,CAWtD;AAED,qDAAqD;AACrD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAIjE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,IAAI,CAItF;AAqHD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,gBAAgB,EAAE,CAErD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,gBAAgB,EAAE,CASjD;AAED,iHAAiH;AACjH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,gBAAgB,CAAC,EAAE,MAAM,EACzB,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM,CASR;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAYzD;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAG5E"}
@@ -0,0 +1,311 @@
1
+ import { existsSync, readFileSync, mkdirSync } from 'node:fs';
2
+ import { basename, resolve } from 'node:path';
3
+ import { getPreferencesPath, getRegisteredPath, getPortfolioDir, generateSessionPrefix } from './paths.js';
4
+ import { loadLocalProjectConfig, registerProjectInGlobalConfig, loadGlobalConfig, saveGlobalConfig, getGlobalConfigPath } from './global-config.js';
5
+ import { atomicWriteFileSync } from './atomic-write.js';
6
+ import { loadConfig } from './config.js';
7
+
8
+ /**
9
+ * Portfolio registry — global-config-backed project projection plus user preferences.
10
+ *
11
+ * The global config is the canonical source of truth for which projects exist.
12
+ * Preferences provide a lightweight UI overlay for ordering, pinning, visibility,
13
+ * and display names.
14
+ */
15
+ function normalizePath(path) {
16
+ return resolve(path);
17
+ }
18
+ function isRecord(value) {
19
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20
+ }
21
+ function parsePortfolioRegistered(value) {
22
+ if (!isRecord(value) || value.version !== 1 || !Array.isArray(value.projects)) {
23
+ return null;
24
+ }
25
+ const projects = [];
26
+ for (const project of value.projects) {
27
+ if (!isRecord(project) || typeof project.path !== "string" || typeof project.addedAt !== "string") {
28
+ return null;
29
+ }
30
+ if ("configProjectKey" in project &&
31
+ project.configProjectKey !== undefined &&
32
+ typeof project.configProjectKey !== "string") {
33
+ return null;
34
+ }
35
+ projects.push({
36
+ path: project.path,
37
+ addedAt: project.addedAt,
38
+ ...(typeof project.configProjectKey === "string"
39
+ ? { configProjectKey: project.configProjectKey }
40
+ : {}),
41
+ });
42
+ }
43
+ return { version: 1, projects };
44
+ }
45
+ function parsePortfolioPreferences(value) {
46
+ if (!isRecord(value) || value.version !== 1) {
47
+ return null;
48
+ }
49
+ if ("defaultProjectId" in value && value.defaultProjectId !== undefined && typeof value.defaultProjectId !== "string") {
50
+ return null;
51
+ }
52
+ if ("projectOrder" in value &&
53
+ value.projectOrder !== undefined &&
54
+ (!Array.isArray(value.projectOrder) || value.projectOrder.some((id) => typeof id !== "string"))) {
55
+ return null;
56
+ }
57
+ if ("projects" in value && value.projects !== undefined) {
58
+ if (!isRecord(value.projects)) {
59
+ return null;
60
+ }
61
+ for (const prefs of Object.values(value.projects)) {
62
+ if (!isRecord(prefs)) {
63
+ return null;
64
+ }
65
+ if ("pinned" in prefs && prefs.pinned !== undefined && typeof prefs.pinned !== "boolean") {
66
+ return null;
67
+ }
68
+ if ("enabled" in prefs && prefs.enabled !== undefined && typeof prefs.enabled !== "boolean") {
69
+ return null;
70
+ }
71
+ if ("displayName" in prefs &&
72
+ prefs.displayName !== undefined &&
73
+ typeof prefs.displayName !== "string") {
74
+ return null;
75
+ }
76
+ }
77
+ }
78
+ return {
79
+ version: 1,
80
+ ...(typeof value.defaultProjectId === "string" ? { defaultProjectId: value.defaultProjectId } : {}),
81
+ ...(Array.isArray(value.projectOrder) ? { projectOrder: value.projectOrder } : {}),
82
+ ...(isRecord(value.projects)
83
+ ? {
84
+ projects: Object.fromEntries(Object.entries(value.projects).map(([id, prefs]) => [
85
+ id,
86
+ {
87
+ ...(isRecord(prefs) && typeof prefs.pinned === "boolean"
88
+ ? { pinned: prefs.pinned }
89
+ : {}),
90
+ ...(isRecord(prefs) && typeof prefs.enabled === "boolean"
91
+ ? { enabled: prefs.enabled }
92
+ : {}),
93
+ ...(isRecord(prefs) && typeof prefs.displayName === "string"
94
+ ? { displayName: prefs.displayName }
95
+ : {}),
96
+ },
97
+ ])),
98
+ }
99
+ : {}),
100
+ };
101
+ }
102
+ /** Load registered projects from registered.json (legacy compatibility only). */
103
+ function loadRegistered() {
104
+ const path = getRegisteredPath();
105
+ if (!existsSync(path)) {
106
+ return { version: 1, projects: [] };
107
+ }
108
+ try {
109
+ const content = readFileSync(path, "utf-8");
110
+ return parsePortfolioRegistered(JSON.parse(content)) ?? { version: 1, projects: [] };
111
+ }
112
+ catch {
113
+ return { version: 1, projects: [] };
114
+ }
115
+ }
116
+ /** Save registered projects to registered.json (legacy compatibility only). */
117
+ function saveRegistered(reg) {
118
+ const dir = getPortfolioDir();
119
+ mkdirSync(dir, { recursive: true });
120
+ atomicWriteFileSync(getRegisteredPath(), JSON.stringify(reg, null, 2));
121
+ }
122
+ /** Load portfolio preferences from preferences.json */
123
+ function loadPreferences() {
124
+ const path = getPreferencesPath();
125
+ if (!existsSync(path)) {
126
+ return { version: 1 };
127
+ }
128
+ try {
129
+ const content = readFileSync(path, "utf-8");
130
+ return parsePortfolioPreferences(JSON.parse(content)) ?? { version: 1 };
131
+ }
132
+ catch {
133
+ return { version: 1 };
134
+ }
135
+ }
136
+ /** Save portfolio preferences to preferences.json */
137
+ function savePreferences(prefs) {
138
+ const dir = getPortfolioDir();
139
+ mkdirSync(dir, { recursive: true });
140
+ atomicWriteFileSync(getPreferencesPath(), JSON.stringify(prefs, null, 2));
141
+ }
142
+ /**
143
+ * Atomically update preferences: reads the current state, applies the updater,
144
+ * and writes back in a single synchronous block. This prevents lost updates
145
+ * from concurrent async handlers that might interleave between read and write.
146
+ */
147
+ function updatePreferences(updater) {
148
+ const prefs = loadPreferences();
149
+ updater(prefs);
150
+ savePreferences(prefs);
151
+ }
152
+ function applyPreferences(projects, preferences) {
153
+ const projectMap = new Map(projects.map((project) => [project.id, project]));
154
+ if (preferences.projects) {
155
+ for (const [id, prefs] of Object.entries(preferences.projects)) {
156
+ const project = projectMap.get(id);
157
+ if (!project)
158
+ continue;
159
+ if (prefs.pinned !== undefined)
160
+ project.pinned = prefs.pinned;
161
+ if (prefs.enabled !== undefined)
162
+ project.enabled = prefs.enabled;
163
+ if (prefs.displayName)
164
+ project.name = prefs.displayName;
165
+ }
166
+ }
167
+ const orderMap = new Map();
168
+ if (preferences.projectOrder) {
169
+ for (let i = 0; i < preferences.projectOrder.length; i++) {
170
+ orderMap.set(preferences.projectOrder[i], i);
171
+ }
172
+ }
173
+ return [...projectMap.values()].sort((a, b) => {
174
+ if (a.pinned && !b.pinned)
175
+ return -1;
176
+ if (!a.pinned && b.pinned)
177
+ return 1;
178
+ const orderA = orderMap.get(a.id) ?? Infinity;
179
+ const orderB = orderMap.get(b.id) ?? Infinity;
180
+ if (orderA !== orderB)
181
+ return orderA - orderB;
182
+ return a.name.localeCompare(b.name);
183
+ });
184
+ }
185
+ function projectFromGlobalConfig() {
186
+ try {
187
+ const config = loadConfig(getGlobalConfigPath());
188
+ const allEntries = [
189
+ ...Object.entries(config.projects).map(([id, project]) => ({
190
+ id,
191
+ name: project.name ?? id,
192
+ configPath: config.configPath,
193
+ configProjectKey: id,
194
+ repoPath: project.path,
195
+ repo: project.repo,
196
+ defaultBranch: project.defaultBranch,
197
+ sessionPrefix: project.sessionPrefix ?? generateSessionPrefix(id),
198
+ source: "config",
199
+ enabled: true,
200
+ pinned: false,
201
+ lastSeenAt: new Date().toISOString(),
202
+ })),
203
+ ...Object.entries(config.degradedProjects).map(([id, project]) => ({
204
+ id,
205
+ name: id,
206
+ configPath: config.configPath,
207
+ configProjectKey: id,
208
+ repoPath: project.path,
209
+ sessionPrefix: generateSessionPrefix(id),
210
+ source: "config",
211
+ enabled: true,
212
+ pinned: false,
213
+ lastSeenAt: new Date().toISOString(),
214
+ resolveError: project.resolveError,
215
+ })),
216
+ ];
217
+ return allEntries;
218
+ }
219
+ catch {
220
+ return [];
221
+ }
222
+ }
223
+ function fallbackPortfolioFromLoadedConfig() {
224
+ try {
225
+ const config = loadConfig();
226
+ return [
227
+ ...Object.entries(config.projects).map(([id, project]) => ({
228
+ id,
229
+ name: project.name ?? id,
230
+ configPath: config.configPath,
231
+ configProjectKey: id,
232
+ repoPath: project.path,
233
+ repo: project.repo,
234
+ defaultBranch: project.defaultBranch,
235
+ sessionPrefix: project.sessionPrefix ?? generateSessionPrefix(id),
236
+ source: "config",
237
+ enabled: true,
238
+ pinned: false,
239
+ lastSeenAt: new Date().toISOString(),
240
+ })),
241
+ ...Object.entries(config.degradedProjects).map(([id, project]) => ({
242
+ id,
243
+ name: id,
244
+ configPath: config.configPath,
245
+ configProjectKey: id,
246
+ repoPath: project.path,
247
+ sessionPrefix: generateSessionPrefix(id),
248
+ source: "config",
249
+ enabled: true,
250
+ pinned: false,
251
+ lastSeenAt: new Date().toISOString(),
252
+ resolveError: project.resolveError,
253
+ })),
254
+ ];
255
+ }
256
+ catch {
257
+ return [];
258
+ }
259
+ }
260
+ /**
261
+ * Backward-compatible alias for callers that previously treated "discovered"
262
+ * projects as the portfolio base. Projects are now projected from the
263
+ * canonical registry, so discovery simply returns that projection.
264
+ */
265
+ function discoverProjects() {
266
+ return projectFromGlobalConfig();
267
+ }
268
+ /**
269
+ * Build the portfolio directly from the canonical global registry, with
270
+ * preferences layered on top. Falls back to the currently loaded config when
271
+ * no global config exists yet.
272
+ */
273
+ function getPortfolio() {
274
+ const preferences = loadPreferences();
275
+ const projects = projectFromGlobalConfig();
276
+ if (projects.length > 0) {
277
+ return applyPreferences(projects, preferences);
278
+ }
279
+ return applyPreferences(fallbackPortfolioFromLoadedConfig(), preferences);
280
+ }
281
+ /** Register a project into the canonical global config registry. Returns the effective registered project ID. */
282
+ function registerProject(repoPath, configProjectKey, displayName, options) {
283
+ const normalizedRepoPath = normalizePath(repoPath);
284
+ const localConfig = loadLocalProjectConfig(normalizedRepoPath);
285
+ if (!localConfig) {
286
+ throw new Error(`No local project config found at ${normalizedRepoPath}`);
287
+ }
288
+ const projectId = configProjectKey ?? basename(normalizedRepoPath);
289
+ return registerProjectInGlobalConfig(projectId, displayName ?? projectId, normalizedRepoPath, localConfig, options);
290
+ }
291
+ /** Remove a project from the canonical global config registry. */
292
+ function unregisterProject(projectId) {
293
+ const globalConfig = loadGlobalConfig();
294
+ if (!globalConfig?.projects[projectId])
295
+ return;
296
+ const { [projectId]: _removedProject, ...remainingProjects } = globalConfig.projects;
297
+ globalConfig.projects = remainingProjects;
298
+ if (globalConfig.projectOrder) {
299
+ const nextProjectOrder = globalConfig.projectOrder.filter((id) => id !== projectId);
300
+ globalConfig.projectOrder = nextProjectOrder.length > 0 ? nextProjectOrder : undefined;
301
+ }
302
+ saveGlobalConfig(globalConfig);
303
+ }
304
+ /** Refresh is a no-op for the global-registry-backed portfolio. */
305
+ function refreshProject(_projectId, _configPath) {
306
+ // Canonical portfolio entries are read directly from global config, so there
307
+ // is no separate last-seen registry to update.
308
+ }
309
+
310
+ export { discoverProjects, getPortfolio, loadPreferences, loadRegistered, refreshProject, registerProject, savePreferences, saveRegistered, unregisterProject, updatePreferences };
311
+ //# sourceMappingURL=portfolio-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolio-registry.js","sources":["../src/portfolio-registry.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;AAAA;;;;;;AAMG;AA0BH,SAAS,aAAa,CAAC,IAAY,EAAA;AACjC,IAAA,OAAO,OAAO,CAAC,IAAI,CAAC;AACtB;AAEA,SAAS,QAAQ,CAAC,KAAc,EAAA;AAC9B,IAAA,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;AAC7E;AAEA,SAAS,wBAAwB,CAAC,KAAc,EAAA;IAC9C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE;AAC7E,QAAA,OAAO,IAAI;IACb;IAEA,MAAM,QAAQ,GAAoC,EAAE;AACpD,IAAA,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE;QACpC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EAAE;AACjG,YAAA,OAAO,IAAI;QACb;QACA,IACE,kBAAkB,IAAI,OAAO;YAC7B,OAAO,CAAC,gBAAgB,KAAK,SAAS;AACtC,YAAA,OAAO,OAAO,CAAC,gBAAgB,KAAK,QAAQ,EAC5C;AACA,YAAA,OAAO,IAAI;QACb;QAEA,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,OAAO,EAAE,OAAO,CAAC,OAAO;AACxB,YAAA,IAAI,OAAO,OAAO,CAAC,gBAAgB,KAAK;AACtC,kBAAE,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;kBAC5C,EAAE,CAAC;AACR,SAAA,CAAC;IACJ;AAEA,IAAA,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE;AACjC;AAEA,SAAS,yBAAyB,CAAC,KAAc,EAAA;AAC/C,IAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,EAAE;AAC3C,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,IAAI,kBAAkB,IAAI,KAAK,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,gBAAgB,KAAK,QAAQ,EAAE;AACrH,QAAA,OAAO,IAAI;IACb;IAEA,IACE,cAAc,IAAI,KAAK;QACvB,KAAK,CAAC,YAAY,KAAK,SAAS;AAChC,SAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,EAC/F;AACA,QAAA,OAAO,IAAI;IACb;IAEA,IAAI,UAAU,IAAI,KAAK,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE;QACvD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE;AAC7B,YAAA,OAAO,IAAI;QACb;AAEA,QAAA,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE;AACjD,YAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AACpB,gBAAA,OAAO,IAAI;YACb;AACA,YAAA,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE;AACxF,gBAAA,OAAO,IAAI;YACb;AACA,YAAA,IAAI,SAAS,IAAI,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE;AAC3F,gBAAA,OAAO,IAAI;YACb;YACA,IACE,aAAa,IAAI,KAAK;gBACtB,KAAK,CAAC,WAAW,KAAK,SAAS;AAC/B,gBAAA,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,EACrC;AACA,gBAAA,OAAO,IAAI;YACb;QACF;IACF;IAEA,OAAO;AACL,QAAA,OAAO,EAAE,CAAC;QACV,IAAI,OAAO,KAAK,CAAC,gBAAgB,KAAK,QAAQ,GAAG,EAAE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC;QACnG,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC;AAClF,QAAA,IAAI,QAAQ,CAAC,KAAK,CAAC,QAAQ;AACzB,cAAE;gBACE,QAAQ,EAAE,MAAM,CAAC,WAAW,CAC1B,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK;oBAClD,EAAE;AACF,oBAAA;wBACE,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK;AAC7C,8BAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM;8BACtB,EAAE,CAAC;wBACP,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK;AAC9C,8BAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO;8BACxB,EAAE,CAAC;wBACP,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,WAAW,KAAK;AAClD,8BAAE,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW;8BAChC,EAAE,CAAC;AACR,qBAAA;AACF,iBAAA,CAAC,CACH;AACF;cACD,EAAE,CAAC;KACR;AACH;AAEA;SACgB,cAAc,GAAA;AAC5B,IAAA,MAAM,IAAI,GAAG,iBAAiB,EAAE;AAChC,IAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;QACrB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;IACrC;AACA,IAAA,IAAI;QACF,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC;AAC3C,QAAA,OAAO,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;IACtF;AAAE,IAAA,MAAM;QACN,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;IACrC;AACF;AAEA;AACM,SAAU,cAAc,CAAC,GAAwB,EAAA;AACrD,IAAA,MAAM,GAAG,GAAG,eAAe,EAAE;IAC7B,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACnC,IAAA,mBAAmB,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACxE;AAEA;SACgB,eAAe,GAAA;AAC7B,IAAA,MAAM,IAAI,GAAG,kBAAkB,EAAE;AACjC,IAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;AACrB,QAAA,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;IACvB;AACA,IAAA,IAAI;QACF,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC;AAC3C,QAAA,OAAO,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE;IACzE;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;IACvB;AACF;AAEA;AACM,SAAU,eAAe,CAAC,KAA2B,EAAA;AACzD,IAAA,MAAM,GAAG,GAAG,eAAe,EAAE;IAC7B,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACnC,IAAA,mBAAmB,CAAC,kBAAkB,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC3E;AAEA;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,OAA8C,EAAA;AAC9E,IAAA,MAAM,KAAK,GAAG,eAAe,EAAE;IAC/B,OAAO,CAAC,KAAK,CAAC;IACd,eAAe,CAAC,KAAK,CAAC;AACxB;AAEA,SAAS,gBAAgB,CACvB,QAA4B,EAC5B,WAAiC,EAAA;IAEjC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;AAE5E,IAAA,IAAI,WAAW,CAAC,QAAQ,EAAE;AACxB,QAAA,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE;YAC9D,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;AAClC,YAAA,IAAI,CAAC,OAAO;gBAAE;AAEd,YAAA,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;AAAE,gBAAA,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;AAC7D,YAAA,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;AAAE,gBAAA,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO;YAChE,IAAI,KAAK,CAAC,WAAW;AAAE,gBAAA,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,WAAW;QACzD;IACF;AAEA,IAAA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB;AAC1C,IAAA,IAAI,WAAW,CAAC,YAAY,EAAE;AAC5B,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACxD,YAAA,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9C;IACF;AAEA,IAAA,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAI;AAC5C,QAAA,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM;YAAE,OAAO,EAAE;AACpC,QAAA,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM;AAAE,YAAA,OAAO,CAAC;AAEnC,QAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,QAAQ;AAC7C,QAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,QAAQ;QAC7C,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,MAAM,GAAG,MAAM;QAE7C,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;AACrC,IAAA,CAAC,CAAC;AACJ;AAEA,SAAS,uBAAuB,GAAA;AAC9B,IAAA,IAAI;AACF,QAAA,MAAM,MAAM,GAAG,UAAU,CAAC,mBAAmB,EAAE,CAAC;AAChD,QAAA,MAAM,UAAU,GAAG;YACjB,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM;gBACzD,EAAE;AACF,gBAAA,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;gBACxB,UAAU,EAAE,MAAM,CAAC,UAAU;AAC7B,gBAAA,gBAAgB,EAAE,EAAE;gBACpB,QAAQ,EAAE,OAAO,CAAC,IAAI;gBAEtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,qBAAqB,CAAC,EAAE,CAAC;AACjE,gBAAA,MAAM,EAAE,QAAiB;AACzB,gBAAA,OAAO,EAAE,IAAI;AACb,gBAAA,MAAM,EAAE,KAAK;AACb,gBAAA,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;AACrC,aAAA,CAAC,CAAC;YACH,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM;gBACjE,EAAE;AACF,gBAAA,IAAI,EAAE,EAAE;gBACR,UAAU,EAAE,MAAM,CAAC,UAAU;AAC7B,gBAAA,gBAAgB,EAAE,EAAE;gBACpB,QAAQ,EAAE,OAAO,CAAC,IAAI;AAEtB,gBAAA,aAAa,EAAE,qBAAqB,CAAC,EAAE,CAAC;AACxC,gBAAA,MAAM,EAAE,QAAiB;AACzB,gBAAA,OAAO,EAAE,IAAI;AACb,gBAAA,MAAM,EAAE,KAAK;AACb,gBAAA,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACpC,YAAY,EAAE,OAAO,CAAC,YAAY;AACnC,aAAA,CAAC,CAAC;SACJ;AACD,QAAA,OAAO,UAAU;IACnB;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,EAAE;IACX;AACF;AAEA,SAAS,iCAAiC,GAAA;AACxC,IAAA,IAAI;AACF,QAAA,MAAM,MAAM,GAAG,UAAU,EAAE;QAC3B,OAAO;YACL,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM;gBACzD,EAAE;AACF,gBAAA,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;gBACxB,UAAU,EAAE,MAAM,CAAC,UAAU;AAC7B,gBAAA,gBAAgB,EAAE,EAAE;gBACpB,QAAQ,EAAE,OAAO,CAAC,IAAI;gBAEtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,qBAAqB,CAAC,EAAE,CAAC;AACjE,gBAAA,MAAM,EAAE,QAAiB;AACzB,gBAAA,OAAO,EAAE,IAAI;AACb,gBAAA,MAAM,EAAE,KAAK;AACb,gBAAA,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;AACrC,aAAA,CAAC,CAAC;YACH,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM;gBACjE,EAAE;AACF,gBAAA,IAAI,EAAE,EAAE;gBACR,UAAU,EAAE,MAAM,CAAC,UAAU;AAC7B,gBAAA,gBAAgB,EAAE,EAAE;gBACpB,QAAQ,EAAE,OAAO,CAAC,IAAI;AAEtB,gBAAA,aAAa,EAAE,qBAAqB,CAAC,EAAE,CAAC;AACxC,gBAAA,MAAM,EAAE,QAAiB;AACzB,gBAAA,OAAO,EAAE,IAAI;AACb,gBAAA,MAAM,EAAE,KAAK;AACb,gBAAA,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACpC,YAAY,EAAE,OAAO,CAAC,YAAY;AACnC,aAAA,CAAC,CAAC;SACJ;IACH;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,EAAE;IACX;AACF;AAEA;;;;AAIG;SACa,gBAAgB,GAAA;IAC9B,OAAO,uBAAuB,EAAE;AAClC;AAEA;;;;AAIG;SACa,YAAY,GAAA;AAC1B,IAAA,MAAM,WAAW,GAAG,eAAe,EAAE;AACrC,IAAA,MAAM,QAAQ,GAAG,uBAAuB,EAAE;AAE1C,IAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;AACvB,QAAA,OAAO,gBAAgB,CAAC,QAAQ,EAAE,WAAW,CAAC;IAChD;AAEA,IAAA,OAAO,gBAAgB,CAAC,iCAAiC,EAAE,EAAE,WAAW,CAAC;AAC3E;AAEA;AACM,SAAU,eAAe,CAC7B,QAAgB,EAChB,gBAAyB,EACzB,WAAoB,EACpB,OAAgC,EAAA;AAEhC,IAAA,MAAM,kBAAkB,GAAG,aAAa,CAAC,QAAQ,CAAC;AAClD,IAAA,MAAM,WAAW,GAAG,sBAAsB,CAAC,kBAAkB,CAAC;IAC9D,IAAI,CAAC,WAAW,EAAE;AAChB,QAAA,MAAM,IAAI,KAAK,CAAC,oCAAoC,kBAAkB,CAAA,CAAE,CAAC;IAC3E;IAEA,MAAM,SAAS,GAAG,gBAAgB,IAAI,QAAQ,CAAC,kBAAkB,CAAC;AAClE,IAAA,OAAO,6BAA6B,CAAC,SAAS,EAAE,WAAW,IAAI,SAAS,EAAE,kBAAkB,EAAE,WAAW,EAAE,OAAO,CAAC;AACrH;AAEA;AACM,SAAU,iBAAiB,CAAC,SAAiB,EAAA;AACjD,IAAA,MAAM,YAAY,GAAG,gBAAgB,EAAE;AACvC,IAAA,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,SAAS,CAAC;QAAE;AAExC,IAAA,MAAM,EAAE,CAAC,SAAS,GAAG,eAAe,EAAE,GAAG,iBAAiB,EAAE,GAAG,YAAY,CAAC,QAAQ;AACpF,IAAA,YAAY,CAAC,QAAQ,GAAG,iBAAiB;AACzC,IAAA,IAAI,YAAY,CAAC,YAAY,EAAE;AAC7B,QAAA,MAAM,gBAAgB,GAAG,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,SAAS,CAAC;AACnF,QAAA,YAAY,CAAC,YAAY,GAAG,gBAAgB,CAAC,MAAM,GAAG,CAAC,GAAG,gBAAgB,GAAG,SAAS;IACxF;IAEA,gBAAgB,CAAC,YAAY,CAAC;AAChC;AAEA;AACM,SAAU,cAAc,CAAC,UAAkB,EAAE,WAAmB,EAAA;;;AAGtE;;;;"}
@@ -0,0 +1,5 @@
1
+ import type { PortfolioProject, PortfolioSession } from "./types.js";
2
+ export declare function resolvePortfolioProject(portfolio: PortfolioProject[], projectId: string): PortfolioProject | undefined;
3
+ export declare function resolvePortfolioSession(portfolio: PortfolioProject[], projectId: string, sessionId: string): Promise<PortfolioSession | undefined>;
4
+ export declare function derivePortfolioProjectId(configProjectKey: string, existingIds: Set<string>): string;
5
+ //# sourceMappingURL=portfolio-routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolio-routing.d.ts","sourceRoot":"","sources":["../src/portfolio-routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGrE,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,SAAS,EAAE,MAAM,GAChB,gBAAgB,GAAG,SAAS,CAE9B;AAED,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,gBAAgB,EAAE,EAC7B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAMvC;AAED,wBAAgB,wBAAwB,CACtC,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GACvB,MAAM,CAQR"}
@@ -0,0 +1,24 @@
1
+ import { listPortfolioSessions } from './portfolio-session-service.js';
2
+
3
+ function resolvePortfolioProject(portfolio, projectId) {
4
+ return portfolio.find((project) => project.id === projectId);
5
+ }
6
+ async function resolvePortfolioSession(portfolio, projectId, sessionId) {
7
+ const project = resolvePortfolioProject(portfolio, projectId);
8
+ if (!project)
9
+ return undefined;
10
+ const sessions = await listPortfolioSessions([project]);
11
+ return sessions.find((entry) => entry.session.id === sessionId);
12
+ }
13
+ function derivePortfolioProjectId(configProjectKey, existingIds) {
14
+ if (!existingIds.has(configProjectKey))
15
+ return configProjectKey;
16
+ let suffix = 2;
17
+ while (existingIds.has(`${configProjectKey}-${suffix}`)) {
18
+ suffix++;
19
+ }
20
+ return `${configProjectKey}-${suffix}`;
21
+ }
22
+
23
+ export { derivePortfolioProjectId, resolvePortfolioProject, resolvePortfolioSession };
24
+ //# sourceMappingURL=portfolio-routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolio-routing.js","sources":["../src/portfolio-routing.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAGM,SAAU,uBAAuB,CACrC,SAA6B,EAC7B,SAAiB,EAAA;AAEjB,IAAA,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,EAAE,KAAK,SAAS,CAAC;AAC9D;AAEO,eAAe,uBAAuB,CAC3C,SAA6B,EAC7B,SAAiB,EACjB,SAAiB,EAAA;IAEjB,MAAM,OAAO,GAAG,uBAAuB,CAAC,SAAS,EAAE,SAAS,CAAC;AAC7D,IAAA,IAAI,CAAC,OAAO;AAAE,QAAA,OAAO,SAAS;IAE9B,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,CAAC,OAAO,CAAC,CAAC;AACvD,IAAA,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,SAAS,CAAC;AACjE;AAEM,SAAU,wBAAwB,CACtC,gBAAwB,EACxB,WAAwB,EAAA;AAExB,IAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,gBAAgB,CAAC;AAAE,QAAA,OAAO,gBAAgB;IAE/D,IAAI,MAAM,GAAG,CAAC;IACd,OAAO,WAAW,CAAC,GAAG,CAAC,CAAA,EAAG,gBAAgB,CAAA,CAAA,EAAI,MAAM,CAAA,CAAE,CAAC,EAAE;AACvD,QAAA,MAAM,EAAE;IACV;AACA,IAAA,OAAO,CAAA,EAAG,gBAAgB,CAAA,CAAA,EAAI,MAAM,EAAE;AACxC;;;;"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Portfolio session service — lightweight cross-project session aggregation.
3
+ *
4
+ * Uses async I/O to avoid blocking the Node.js event loop in web server contexts.
5
+ * Reads session metadata files directly without constructing SessionManagers.
6
+ */
7
+ import { type PortfolioProject, type PortfolioSession } from "./types.js";
8
+ export declare function listPortfolioSessions(portfolio: PortfolioProject[], opts?: {
9
+ perProjectTimeoutMs?: number;
10
+ }): Promise<PortfolioSession[]>;
11
+ export declare function getPortfolioSessionCounts(portfolio: PortfolioProject[]): Promise<Record<string, {
12
+ total: number;
13
+ active: number;
14
+ }>>;
15
+ //# sourceMappingURL=portfolio-session-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolio-session-service.d.ts","sourceRoot":"","sources":["../src/portfolio-session-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAyD,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAA0D,MAAM,YAAY,CAAC;AAezL,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,IAAI,CAAC,EAAE;IAAE,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAE,GACtC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAwB7B;AAsGD,wBAAsB,yBAAyB,CAAC,SAAS,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CA4DzI"}
@@ -0,0 +1,206 @@
1
+ import { readdir, stat, readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { isOrchestratorSession } from './types.js';
4
+ import { getProjectSessionsDir } from './paths.js';
5
+ import { deriveLegacyStatus } from './lifecycle-state.js';
6
+ import { flattenToStringRecord } from './utils/metadata-flatten.js';
7
+ import { sessionFromMetadata } from './utils/session-from-metadata.js';
8
+
9
+ /**
10
+ * Portfolio session service — lightweight cross-project session aggregation.
11
+ *
12
+ * Uses async I/O to avoid blocking the Node.js event loop in web server contexts.
13
+ * Reads session metadata files directly without constructing SessionManagers.
14
+ */
15
+ const JSON_EXTENSION = ".json";
16
+ function tryParseJson(value) {
17
+ try {
18
+ return JSON.parse(value);
19
+ }
20
+ catch {
21
+ return undefined;
22
+ }
23
+ }
24
+ const DEFAULT_PER_PROJECT_TIMEOUT_MS = 3_000;
25
+ const VALID_SESSION_ID = /^[a-zA-Z0-9_-]+$/;
26
+ async function listPortfolioSessions(portfolio, opts) {
27
+ const timeout = opts?.perProjectTimeoutMs ?? DEFAULT_PER_PROJECT_TIMEOUT_MS;
28
+ const results = [];
29
+ for (const project of portfolio) {
30
+ if (!project.enabled || project.resolveError)
31
+ continue;
32
+ try {
33
+ let timerId;
34
+ const projectResults = await Promise.race([
35
+ loadProjectSessions(project).finally(() => {
36
+ if (timerId !== undefined)
37
+ clearTimeout(timerId);
38
+ }),
39
+ new Promise((resolve) => {
40
+ timerId = setTimeout(() => resolve([]), timeout);
41
+ }),
42
+ ]);
43
+ results.push(...projectResults);
44
+ }
45
+ catch {
46
+ // Skip projects whose session dirs can't be read
47
+ }
48
+ }
49
+ return results;
50
+ }
51
+ async function loadProjectSessions(project) {
52
+ const results = [];
53
+ const sessionsDir = getProjectSessionsDir(project.id);
54
+ let entries;
55
+ try {
56
+ entries = await readdir(sessionsDir);
57
+ }
58
+ catch {
59
+ return results; // Dir doesn't exist
60
+ }
61
+ for (const name of entries) {
62
+ if (!name.endsWith(JSON_EXTENSION))
63
+ continue;
64
+ if (name === "archive" || name.startsWith("."))
65
+ continue;
66
+ const sessionId = name.slice(0, -JSON_EXTENSION.length);
67
+ if (!VALID_SESSION_ID.test(sessionId))
68
+ continue;
69
+ try {
70
+ const filePath = join(sessionsDir, name);
71
+ const fileStat = await stat(filePath);
72
+ if (!fileStat.isFile())
73
+ continue;
74
+ const content = await readFile(filePath, "utf-8");
75
+ const raw = flattenToStringRecord(JSON.parse(content));
76
+ // Exclude orchestrator sessions from portfolio listings
77
+ if (isOrchestratorSession({ id: sessionId, metadata: raw }))
78
+ continue;
79
+ const metadata = rawToMetadata(raw);
80
+ const session = metadataToSession(sessionId, project, metadata);
81
+ results.push({ session, project });
82
+ }
83
+ catch {
84
+ continue;
85
+ }
86
+ }
87
+ return results;
88
+ }
89
+ function rawToMetadata(raw) {
90
+ const lifecycle = raw["lifecycle"] ? tryParseJson(raw["lifecycle"]) : undefined;
91
+ const storedStatus = raw["status"];
92
+ const status = (lifecycle ? deriveLegacyStatus(lifecycle) : undefined) ?? storedStatus ?? "unknown";
93
+ return {
94
+ worktree: raw["worktree"] ?? "",
95
+ branch: raw["branch"] ?? "",
96
+ status,
97
+ tmuxName: raw["tmuxName"],
98
+ issue: raw["issue"],
99
+ pr: raw["pr"],
100
+ summary: raw["summary"],
101
+ project: raw["project"],
102
+ agent: raw["agent"],
103
+ createdAt: raw["createdAt"],
104
+ runtimeHandle: raw["runtimeHandle"] ? tryParseJson(raw["runtimeHandle"]) : undefined,
105
+ restoredAt: raw["restoredAt"],
106
+ role: raw["role"],
107
+ lifecycle,
108
+ };
109
+ }
110
+ function metadataToRecord(metadata) {
111
+ const record = {};
112
+ for (const [key, value] of Object.entries(metadata)) {
113
+ if (typeof value === "string") {
114
+ record[key] = value;
115
+ }
116
+ else if (typeof value === "object" && value !== null) {
117
+ record[key] = JSON.stringify(value);
118
+ }
119
+ else if (typeof value === "number") {
120
+ record[key] = String(value);
121
+ }
122
+ }
123
+ return record;
124
+ }
125
+ /** Convert raw metadata to a Session object (lightweight, no plugin init) */
126
+ function metadataToSession(sessionId, project, metadata) {
127
+ // Use the most recent timestamp available as lastActivityAt
128
+ const timestamps = [metadata.createdAt, metadata.restoredAt].filter((timestamp) => typeof timestamp === "string" && timestamp.length > 0);
129
+ const lastActivity = timestamps.length > 0
130
+ ? new Date(Math.max(...timestamps.map((timestamp) => new Date(timestamp).getTime())))
131
+ : new Date();
132
+ return sessionFromMetadata(sessionId, metadataToRecord(metadata), {
133
+ projectId: project.id,
134
+ workspacePathFallback: project.repoPath,
135
+ status: metadata.status || "spawning",
136
+ activity: null,
137
+ runtimeHandle: metadata.runtimeHandle ?? null,
138
+ createdAt: metadata.createdAt ? new Date(metadata.createdAt) : new Date(),
139
+ lastActivityAt: lastActivity,
140
+ restoredAt: metadata.restoredAt ? new Date(metadata.restoredAt) : undefined,
141
+ });
142
+ }
143
+ async function getPortfolioSessionCounts(portfolio) {
144
+ const counts = {};
145
+ const TERMINAL = new Set(["killed", "terminated", "done", "cleanup", "errored", "merged"]);
146
+ for (const project of portfolio) {
147
+ if (!project.enabled || project.resolveError) {
148
+ counts[project.id] = { total: 0, active: 0 };
149
+ continue;
150
+ }
151
+ try {
152
+ const sessionsDir = getProjectSessionsDir(project.id);
153
+ let entries;
154
+ try {
155
+ entries = await readdir(sessionsDir);
156
+ }
157
+ catch {
158
+ counts[project.id] = { total: 0, active: 0 };
159
+ continue;
160
+ }
161
+ let total = 0;
162
+ let active = 0;
163
+ for (const name of entries) {
164
+ if (!name.endsWith(JSON_EXTENSION))
165
+ continue;
166
+ if (name === "archive" || name.startsWith("."))
167
+ continue;
168
+ const sessionId = name.slice(0, -JSON_EXTENSION.length);
169
+ if (!VALID_SESSION_ID.test(sessionId))
170
+ continue;
171
+ try {
172
+ const filePath = join(sessionsDir, name);
173
+ const fileStat = await stat(filePath);
174
+ if (!fileStat.isFile())
175
+ continue;
176
+ const content = await readFile(filePath, "utf-8");
177
+ const raw = flattenToStringRecord(JSON.parse(content));
178
+ // Exclude orchestrator sessions from portfolio counts
179
+ if (isOrchestratorSession({ id: sessionId, metadata: raw }))
180
+ continue;
181
+ total++;
182
+ // Derive status from lifecycle when not stored (post-migration JSON)
183
+ let sessionStatus = raw["status"];
184
+ if (!sessionStatus && raw["lifecycle"]) {
185
+ const lifecycle = tryParseJson(raw["lifecycle"]);
186
+ if (lifecycle)
187
+ sessionStatus = deriveLegacyStatus(lifecycle);
188
+ }
189
+ if (!TERMINAL.has(sessionStatus ?? ""))
190
+ active++;
191
+ }
192
+ catch {
193
+ continue;
194
+ }
195
+ }
196
+ counts[project.id] = { total, active };
197
+ }
198
+ catch {
199
+ counts[project.id] = { total: 0, active: 0 };
200
+ }
201
+ }
202
+ return counts;
203
+ }
204
+
205
+ export { getPortfolioSessionCounts, listPortfolioSessions };
206
+ //# sourceMappingURL=portfolio-session-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolio-session-service.js","sources":["../src/portfolio-session-service.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Process-scoped async memoization for expensive checks shared across plugins.
3
+ *
4
+ * Use cases: prerequisite checks (binary present, auth valid) that multiple
5
+ * plugins want to perform but only need to actually run once per CLI
6
+ * invocation. Cache key chooses the dedup boundary — plugins that share a
7
+ * key share the result.
8
+ *
9
+ * Both successes and failures are cached: if a check fails the user must fix
10
+ * the underlying issue and re-run, so re-checking within the same process is
11
+ * pointless and would muddy the error stream with duplicate messages.
12
+ *
13
+ * **Key namespacing convention:**
14
+ *
15
+ * The cache is shared across every caller in the process, so two plugins
16
+ * passing the same key are explicitly opting into shared state. That is the
17
+ * intended use for cross-cutting checks like the `gh` CLI auth status (used
18
+ * by both `tracker-github` and `scm-github`).
19
+ *
20
+ * For plugin-internal caching (where you do *not* want sharing), namespace
21
+ * the key with your plugin name to avoid silent collisions:
22
+ * - shared cross-plugin check: `"gh-cli-auth"` (intentional sharing)
23
+ * - plugin-internal check: `"tracker-github:rate-limit-check"`
24
+ *
25
+ * If two plugins use the same key for semantically different work, callers
26
+ * will silently receive each other's resolved values — a debugging nightmare.
27
+ * When in doubt, namespace.
28
+ */
29
+ export declare function memoizeAsync<T>(key: string, fn: () => Promise<T>): Promise<T>;
30
+ /** Test-only — clears the process cache. */
31
+ export declare function _clearProcessCacheForTests(): void;
32
+ //# sourceMappingURL=process-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-cache.d.ts","sourceRoot":"","sources":["../src/process-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAO7E;AAED,4CAA4C;AAC5C,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}