@decibelsystems/tools 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/agentCommands.d.ts +0 -1
  3. package/dist/agentCommands.d.ts.map +1 -1
  4. package/dist/agentCommands.js +19 -8
  5. package/dist/agentCommands.js.map +1 -1
  6. package/dist/agentPresence.d.ts +5 -3
  7. package/dist/agentPresence.d.ts.map +1 -1
  8. package/dist/agentPresence.js +20 -29
  9. package/dist/agentPresence.js.map +1 -1
  10. package/dist/architectAdrs.d.ts +13 -1
  11. package/dist/architectAdrs.d.ts.map +1 -1
  12. package/dist/architectAdrs.js +83 -1
  13. package/dist/architectAdrs.js.map +1 -1
  14. package/dist/artifacts.d.ts +60 -0
  15. package/dist/artifacts.d.ts.map +1 -0
  16. package/dist/artifacts.js +208 -0
  17. package/dist/artifacts.js.map +1 -0
  18. package/dist/asks.d.ts +93 -0
  19. package/dist/asks.d.ts.map +1 -0
  20. package/dist/asks.js +383 -0
  21. package/dist/asks.js.map +1 -0
  22. package/dist/chat.d.ts +47 -0
  23. package/dist/chat.d.ts.map +1 -0
  24. package/dist/chat.js +288 -0
  25. package/dist/chat.js.map +1 -0
  26. package/dist/daemon.d.ts +14 -3
  27. package/dist/daemon.d.ts.map +1 -1
  28. package/dist/daemon.js +44 -14
  29. package/dist/daemon.js.map +1 -1
  30. package/dist/daemonConfig.d.ts +22 -0
  31. package/dist/daemonConfig.d.ts.map +1 -1
  32. package/dist/daemonConfig.js +11 -0
  33. package/dist/daemonConfig.js.map +1 -1
  34. package/dist/domain/issue.d.ts +19 -15
  35. package/dist/domain/issue.d.ts.map +1 -1
  36. package/dist/domain/issue.js +4 -32
  37. package/dist/domain/issue.js.map +1 -1
  38. package/dist/domain/issueCodec.d.ts.map +1 -1
  39. package/dist/domain/issueCodec.js +10 -0
  40. package/dist/domain/issueCodec.js.map +1 -1
  41. package/dist/domain/issueRepository.d.ts +35 -2
  42. package/dist/domain/issueRepository.d.ts.map +1 -1
  43. package/dist/domain/issueRepository.js +40 -1
  44. package/dist/domain/issueRepository.js.map +1 -1
  45. package/dist/domain/uid.d.ts +16 -0
  46. package/dist/domain/uid.d.ts.map +1 -0
  47. package/dist/domain/uid.js +43 -0
  48. package/dist/domain/uid.js.map +1 -0
  49. package/dist/facades/definitions.d.ts.map +1 -1
  50. package/dist/facades/definitions.js +46 -3
  51. package/dist/facades/definitions.js.map +1 -1
  52. package/dist/hq/defineAgent.d.ts.map +1 -1
  53. package/dist/hq/defineAgent.js +7 -11
  54. package/dist/hq/defineAgent.js.map +1 -1
  55. package/dist/httpServer.d.ts.map +1 -1
  56. package/dist/httpServer.js +201 -48
  57. package/dist/httpServer.js.map +1 -1
  58. package/dist/identity.d.ts +123 -0
  59. package/dist/identity.d.ts.map +1 -0
  60. package/dist/identity.js +312 -0
  61. package/dist/identity.js.map +1 -0
  62. package/dist/kernel.d.ts.map +1 -1
  63. package/dist/kernel.js +24 -1
  64. package/dist/kernel.js.map +1 -1
  65. package/dist/killSwitch.d.ts +117 -0
  66. package/dist/killSwitch.d.ts.map +1 -0
  67. package/dist/killSwitch.js +161 -0
  68. package/dist/killSwitch.js.map +1 -0
  69. package/dist/lib/atomicWrite.d.ts +23 -0
  70. package/dist/lib/atomicWrite.d.ts.map +1 -1
  71. package/dist/lib/atomicWrite.js +84 -0
  72. package/dist/lib/atomicWrite.js.map +1 -1
  73. package/dist/lib/recordIdAllocator.d.ts +21 -5
  74. package/dist/lib/recordIdAllocator.d.ts.map +1 -1
  75. package/dist/lib/recordIdAllocator.js +25 -7
  76. package/dist/lib/recordIdAllocator.js.map +1 -1
  77. package/dist/orgId.d.ts +23 -0
  78. package/dist/orgId.d.ts.map +1 -0
  79. package/dist/orgId.js +73 -0
  80. package/dist/orgId.js.map +1 -0
  81. package/dist/pairCli.d.ts +2 -0
  82. package/dist/pairCli.d.ts.map +1 -0
  83. package/dist/pairCli.js +97 -0
  84. package/dist/pairCli.js.map +1 -0
  85. package/dist/peerBroker.d.ts +40 -0
  86. package/dist/peerBroker.d.ts.map +1 -0
  87. package/dist/peerBroker.js +70 -0
  88. package/dist/peerBroker.js.map +1 -0
  89. package/dist/peerBrokerSupervisor.d.ts +21 -0
  90. package/dist/peerBrokerSupervisor.d.ts.map +1 -0
  91. package/dist/peerBrokerSupervisor.js +131 -0
  92. package/dist/peerBrokerSupervisor.js.map +1 -0
  93. package/dist/projectPaths.d.ts.map +1 -1
  94. package/dist/projectPaths.js +1 -19
  95. package/dist/projectPaths.js.map +1 -1
  96. package/dist/projectRegistry.d.ts +22 -0
  97. package/dist/projectRegistry.d.ts.map +1 -1
  98. package/dist/projectRegistry.js +58 -2
  99. package/dist/projectRegistry.js.map +1 -1
  100. package/dist/pty/boundary.d.ts +39 -0
  101. package/dist/pty/boundary.d.ts.map +1 -0
  102. package/dist/pty/boundary.js +100 -0
  103. package/dist/pty/boundary.js.map +1 -0
  104. package/dist/pty/errors.d.ts +6 -0
  105. package/dist/pty/errors.d.ts.map +1 -0
  106. package/dist/pty/errors.js +12 -0
  107. package/dist/pty/errors.js.map +1 -0
  108. package/dist/pty/fleet.d.ts +159 -0
  109. package/dist/pty/fleet.d.ts.map +1 -0
  110. package/dist/pty/fleet.js +543 -0
  111. package/dist/pty/fleet.js.map +1 -0
  112. package/dist/pty/launchProfile.d.ts +45 -0
  113. package/dist/pty/launchProfile.d.ts.map +1 -0
  114. package/dist/pty/launchProfile.js +149 -0
  115. package/dist/pty/launchProfile.js.map +1 -0
  116. package/dist/pty/openTerminal.d.ts +48 -0
  117. package/dist/pty/openTerminal.d.ts.map +1 -0
  118. package/dist/pty/openTerminal.js +185 -0
  119. package/dist/pty/openTerminal.js.map +1 -0
  120. package/dist/pty/pairDialog.d.ts +16 -0
  121. package/dist/pty/pairDialog.d.ts.map +1 -0
  122. package/dist/pty/pairDialog.js +121 -0
  123. package/dist/pty/pairDialog.js.map +1 -0
  124. package/dist/pty/pairing.d.ts +86 -0
  125. package/dist/pty/pairing.d.ts.map +1 -0
  126. package/dist/pty/pairing.js +351 -0
  127. package/dist/pty/pairing.js.map +1 -0
  128. package/dist/pty/routes.d.ts +49 -0
  129. package/dist/pty/routes.d.ts.map +1 -0
  130. package/dist/pty/routes.js +624 -0
  131. package/dist/pty/routes.js.map +1 -0
  132. package/dist/pty/sessions.d.ts +167 -0
  133. package/dist/pty/sessions.d.ts.map +1 -0
  134. package/dist/pty/sessions.js +513 -0
  135. package/dist/pty/sessions.js.map +1 -0
  136. package/dist/pty/tmux.d.ts +109 -0
  137. package/dist/pty/tmux.d.ts.map +1 -0
  138. package/dist/pty/tmux.js +271 -0
  139. package/dist/pty/tmux.js.map +1 -0
  140. package/dist/roster.d.ts +37 -0
  141. package/dist/roster.d.ts.map +1 -0
  142. package/dist/roster.js +126 -0
  143. package/dist/roster.js.map +1 -0
  144. package/dist/runtime/daemonMeta.d.ts +53 -0
  145. package/dist/runtime/daemonMeta.d.ts.map +1 -0
  146. package/dist/runtime/daemonMeta.js +121 -0
  147. package/dist/runtime/daemonMeta.js.map +1 -0
  148. package/dist/runtime/ensureRuntime.d.ts.map +1 -1
  149. package/dist/runtime/ensureRuntime.js +5 -10
  150. package/dist/runtime/ensureRuntime.js.map +1 -1
  151. package/dist/sentinelIssues.d.ts +16 -0
  152. package/dist/sentinelIssues.d.ts.map +1 -1
  153. package/dist/sentinelIssues.js +42 -1
  154. package/dist/sentinelIssues.js.map +1 -1
  155. package/dist/server.js +66 -5
  156. package/dist/server.js.map +1 -1
  157. package/dist/toolConfig.d.ts.map +1 -1
  158. package/dist/toolConfig.js +12 -0
  159. package/dist/toolConfig.js.map +1 -1
  160. package/dist/tools/agentic/agentQueue.d.ts.map +1 -1
  161. package/dist/tools/agentic/agentQueue.js +4 -0
  162. package/dist/tools/agentic/agentQueue.js.map +1 -1
  163. package/dist/tools/architect/index.d.ts +1 -0
  164. package/dist/tools/architect/index.d.ts.map +1 -1
  165. package/dist/tools/architect/index.js +45 -1
  166. package/dist/tools/architect/index.js.map +1 -1
  167. package/dist/tools/architect.d.ts.map +1 -1
  168. package/dist/tools/architect.js +59 -8
  169. package/dist/tools/architect.js.map +1 -1
  170. package/dist/tools/artifacts.d.ts +5 -0
  171. package/dist/tools/artifacts.d.ts.map +1 -0
  172. package/dist/tools/artifacts.js +89 -0
  173. package/dist/tools/artifacts.js.map +1 -0
  174. package/dist/tools/asks.d.ts +6 -0
  175. package/dist/tools/asks.d.ts.map +1 -0
  176. package/dist/tools/asks.js +104 -0
  177. package/dist/tools/asks.js.map +1 -0
  178. package/dist/tools/coordinator/index.d.ts +1 -0
  179. package/dist/tools/coordinator/index.d.ts.map +1 -1
  180. package/dist/tools/coordinator/index.js +42 -2
  181. package/dist/tools/coordinator/index.js.map +1 -1
  182. package/dist/tools/data-inspector.d.ts +1 -1
  183. package/dist/tools/data-inspector.d.ts.map +1 -1
  184. package/dist/tools/data-inspector.js +1 -1
  185. package/dist/tools/data-inspector.js.map +1 -1
  186. package/dist/tools/dojo/index.d.ts.map +1 -1
  187. package/dist/tools/dojo/index.js +52 -3
  188. package/dist/tools/dojo/index.js.map +1 -1
  189. package/dist/tools/dojo.d.ts +62 -1
  190. package/dist/tools/dojo.d.ts.map +1 -1
  191. package/dist/tools/dojo.js +84 -1
  192. package/dist/tools/dojo.js.map +1 -1
  193. package/dist/tools/guardian.d.ts +7 -0
  194. package/dist/tools/guardian.d.ts.map +1 -1
  195. package/dist/tools/guardian.js +146 -9
  196. package/dist/tools/guardian.js.map +1 -1
  197. package/dist/tools/index.d.ts.map +1 -1
  198. package/dist/tools/index.js +6 -0
  199. package/dist/tools/index.js.map +1 -1
  200. package/dist/tools/killswitch/index.d.ts +13 -0
  201. package/dist/tools/killswitch/index.d.ts.map +1 -0
  202. package/dist/tools/killswitch/index.js +138 -0
  203. package/dist/tools/killswitch/index.js.map +1 -0
  204. package/dist/tools/oracle/index.d.ts.map +1 -1
  205. package/dist/tools/oracle/index.js +8 -0
  206. package/dist/tools/oracle/index.js.map +1 -1
  207. package/dist/tools/oracle.d.ts +7 -0
  208. package/dist/tools/oracle.d.ts.map +1 -1
  209. package/dist/tools/oracle.js +22 -6
  210. package/dist/tools/oracle.js.map +1 -1
  211. package/dist/tools/provenance.d.ts +11 -2
  212. package/dist/tools/provenance.d.ts.map +1 -1
  213. package/dist/tools/provenance.js +26 -24
  214. package/dist/tools/provenance.js.map +1 -1
  215. package/dist/tools/registry/index.d.ts.map +1 -1
  216. package/dist/tools/registry/index.js +24 -0
  217. package/dist/tools/registry/index.js.map +1 -1
  218. package/dist/tools/sentinel/index.d.ts.map +1 -1
  219. package/dist/tools/sentinel/index.js +54 -2
  220. package/dist/tools/sentinel/index.js.map +1 -1
  221. package/dist/tools/sentinel.d.ts +50 -0
  222. package/dist/tools/sentinel.d.ts.map +1 -1
  223. package/dist/tools/sentinel.js +72 -7
  224. package/dist/tools/sentinel.js.map +1 -1
  225. package/dist/tools/shared/runTracker.d.ts +2 -0
  226. package/dist/tools/shared/runTracker.d.ts.map +1 -1
  227. package/dist/tools/shared/runTracker.js +38 -0
  228. package/dist/tools/shared/runTracker.js.map +1 -1
  229. package/dist/tools/swarm.d.ts +3 -1
  230. package/dist/tools/swarm.d.ts.map +1 -1
  231. package/dist/tools/swarm.js +6 -12
  232. package/dist/tools/swarm.js.map +1 -1
  233. package/dist/tools/voice/index.d.ts +1 -0
  234. package/dist/tools/voice/index.d.ts.map +1 -1
  235. package/dist/tools/voice/index.js +33 -2
  236. package/dist/tools/voice/index.js.map +1 -1
  237. package/dist/tools/voice.d.ts +48 -1
  238. package/dist/tools/voice.d.ts.map +1 -1
  239. package/dist/tools/voice.js +63 -3
  240. package/dist/tools/voice.js.map +1 -1
  241. package/dist/transports/bridge.d.ts.map +1 -1
  242. package/dist/transports/bridge.js +2 -1
  243. package/dist/transports/bridge.js.map +1 -1
  244. package/dist/transports/mcp.d.ts.map +1 -1
  245. package/dist/transports/mcp.js +2 -1
  246. package/dist/transports/mcp.js.map +1 -1
  247. package/dist/version.d.ts +5 -0
  248. package/dist/version.d.ts.map +1 -0
  249. package/dist/version.js +23 -0
  250. package/dist/version.js.map +1 -0
  251. package/package.json +13 -2
  252. package/templates/AGENT.md +3 -0
  253. package/templates/CLAUDE-decibel.md +11 -0
@@ -0,0 +1,97 @@
1
+ // ============================================================================
2
+ // `decibel-tools pair` — the only place a browser pairing is approved
3
+ // ============================================================================
4
+ // ADR-0012 (this repo): approval is unreachable from any browser. It happens
5
+ // here, in a terminal on this machine, by typing the code HQ is showing — which
6
+ // proves the person approving can see both sides. `pair list` deliberately does
7
+ // NOT print pending codes: a code read from here instead of from HQ proves
8
+ // nothing.
9
+ // ============================================================================
10
+ import { createInterface } from 'readline';
11
+ import { approveByCode, listPairings, pairingsPath, revokePairing } from './pty/pairing.js';
12
+ function ago(iso) {
13
+ if (!iso)
14
+ return '-';
15
+ const s = Math.max(0, Math.round((Date.now() - Date.parse(iso)) / 1000));
16
+ if (s < 60)
17
+ return `${s}s ago`;
18
+ if (s < 3600)
19
+ return `${Math.round(s / 60)}m ago`;
20
+ if (s < 86400)
21
+ return `${Math.round(s / 3600)}h ago`;
22
+ return `${Math.round(s / 86400)}d ago`;
23
+ }
24
+ function describe(p) {
25
+ return `${p.origin} "${p.label}" requested ${ago(p.requested_at)}`;
26
+ }
27
+ async function confirm(question) {
28
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
29
+ const answer = await new Promise((resolve) => rl.question(question, resolve));
30
+ rl.close();
31
+ return /^y(es)?$/i.test(answer.trim());
32
+ }
33
+ const USAGE = `Usage:
34
+ decibel-tools pair list pending requests and approved browsers
35
+ decibel-tools pair approve <code> approve the request showing <code> in HQ
36
+ decibel-tools pair revoke <id> remove a pairing (closes its live sessions)
37
+
38
+ Store: ${'~/.decibel/pairings.yaml'} (public keys only, mode 0600)`;
39
+ export async function runPairCli(argv) {
40
+ const [cmd, arg, ...rest] = argv;
41
+ const yes = rest.includes('--yes') || arg === '--yes';
42
+ if (cmd === 'list') {
43
+ const all = listPairings();
44
+ const pending = all.filter((p) => p.status === 'pending');
45
+ const approved = all.filter((p) => p.status === 'approved');
46
+ console.log(`Pending (${pending.length}) — approve with the code HQ shows:`);
47
+ for (const p of pending)
48
+ console.log(` ${p.id} ${describe(p)}`);
49
+ console.log(`Approved (${approved.length}):`);
50
+ for (const p of approved)
51
+ console.log(` ${p.id} ${p.origin} "${p.label}" approved ${ago(p.approved_at)}, last used ${ago(p.last_used)}`);
52
+ if (!all.length)
53
+ console.log(` (none — ${pairingsPath()})`);
54
+ return 0;
55
+ }
56
+ if (cmd === 'approve') {
57
+ if (!arg || !/^\d{6}$/.test(arg)) {
58
+ console.error('Type the 6-digit code HQ is showing: decibel-tools pair approve <code>');
59
+ return 2;
60
+ }
61
+ const pending = listPairings().filter((p) => p.status === 'pending');
62
+ const target = pending.find((p) => p.code === arg);
63
+ if (!target) {
64
+ console.error(`No pending request with code ${arg}. Requests expire after 10 minutes; start again in HQ.`);
65
+ return 1;
66
+ }
67
+ console.log(`Pairing request: ${describe(target)}`);
68
+ if (pending.length > 1) {
69
+ console.log(`WARNING: ${pending.length} requests are pending at once. If you did not start all of them, approve nothing and check HQ.`);
70
+ }
71
+ if (!yes) {
72
+ if (!process.stdin.isTTY) {
73
+ console.error('Refusing to approve without a terminal to confirm in. Re-run interactively (or pass --yes if you are certain).');
74
+ return 1;
75
+ }
76
+ if (!(await confirm(`Let ${target.origin} act on this machine's daemon? [y/N] `))) {
77
+ console.log('Not approved.');
78
+ return 1;
79
+ }
80
+ }
81
+ const p = await approveByCode(arg);
82
+ console.log(`Approved ${p.id} (${p.origin}). Revoke any time: decibel-tools pair revoke ${p.id}`);
83
+ return 0;
84
+ }
85
+ if (cmd === 'revoke') {
86
+ if (!arg) {
87
+ console.error('decibel-tools pair revoke <id> (ids: decibel-tools pair list)');
88
+ return 2;
89
+ }
90
+ const removed = await revokePairing(arg);
91
+ console.log(removed ? `Revoked ${arg}. Its live sessions close within 5s.` : `No pairing ${arg}.`);
92
+ return removed ? 0 : 1;
93
+ }
94
+ console.log(USAGE);
95
+ return cmd ? 2 : 0;
96
+ }
97
+ //# sourceMappingURL=pairCli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairCli.js","sourceRoot":"","sources":["../src/pairCli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ export interface BrokerPeer {
2
+ id: string;
3
+ pid: number;
4
+ cwd: string;
5
+ git_root: string | null;
6
+ /** Controlling tty without /dev/ (e.g. "ttys004"); null when detached. */
7
+ tty?: string | null;
8
+ summary: string;
9
+ registered_at: string;
10
+ last_seen: string;
11
+ }
12
+ /** Live peers on this machine. Empty when the broker is down; never throws. */
13
+ export declare function fetchPeers(): Promise<BrokerPeer[]>;
14
+ export interface BrokerMessage {
15
+ id: number;
16
+ from_id: string;
17
+ to_id: string;
18
+ text: string;
19
+ sent_at: string;
20
+ }
21
+ export declare function brokerRegister(req: {
22
+ pid: number;
23
+ cwd: string;
24
+ git_root: string | null;
25
+ tty: string | null;
26
+ summary: string;
27
+ }): Promise<{
28
+ id: string;
29
+ }>;
30
+ export declare function brokerHeartbeat(id: string): Promise<unknown>;
31
+ export declare function brokerUnregister(id: string): Promise<unknown>;
32
+ /** Undelivered messages for a peer. The broker marks them delivered as it returns them. */
33
+ export declare function brokerPoll(id: string): Promise<BrokerMessage[]>;
34
+ export declare function brokerSend(fromId: string, toId: string, text: string): Promise<{
35
+ ok: boolean;
36
+ error?: string;
37
+ }>;
38
+ export declare function getHumanPeerId(): string | null;
39
+ export declare function setHumanPeerId(id: string | null): void;
40
+ //# sourceMappingURL=peerBroker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peerBroker.d.ts","sourceRoot":"","sources":["../src/peerBroker.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,+EAA+E;AAC/E,wBAAsB,UAAU,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAaxD;AAMD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD,wBAAgB,cAAc,CAAC,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAEvJ;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE5D;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7D;AAED,2FAA2F;AAC3F,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAGrE;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAOrH;AAMD,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AACD,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEtD"}
@@ -0,0 +1,70 @@
1
+ // ============================================================================
2
+ // claude-peers broker — the local peer bus (127.0.0.1:7899)
3
+ // ============================================================================
4
+ // Its own module so a reader of live peers (hosted sessions, the coordinator's
5
+ // `sessions` action) does not import agentPresence, and with it supabase-js.
6
+ // This module reaches localhost only.
7
+ // ============================================================================
8
+ const BROKER_URL = `http://127.0.0.1:${process.env.CLAUDE_PEERS_PORT ?? '7899'}`;
9
+ /** Live peers on this machine. Empty when the broker is down; never throws. */
10
+ export async function fetchPeers() {
11
+ try {
12
+ const res = await fetch(`${BROKER_URL}/list-peers`, {
13
+ method: 'POST',
14
+ headers: { 'Content-Type': 'application/json' },
15
+ body: JSON.stringify({ requester_id: 'decibel-presence', scope: 'machine' }),
16
+ signal: AbortSignal.timeout(2000),
17
+ });
18
+ if (!res.ok)
19
+ return [];
20
+ return (await res.json());
21
+ }
22
+ catch {
23
+ return [];
24
+ }
25
+ }
26
+ async function brokerPost(path, body, timeoutMs = 3000) {
27
+ const res = await fetch(`${BROKER_URL}${path}`, {
28
+ method: 'POST',
29
+ headers: { 'Content-Type': 'application/json' },
30
+ body: JSON.stringify(body),
31
+ signal: AbortSignal.timeout(timeoutMs),
32
+ });
33
+ if (!res.ok)
34
+ throw new Error(`broker ${path}: HTTP ${res.status}`);
35
+ return (await res.json().catch(() => ({})));
36
+ }
37
+ export function brokerRegister(req) {
38
+ return brokerPost('/register', req);
39
+ }
40
+ export function brokerHeartbeat(id) {
41
+ return brokerPost('/heartbeat', { id });
42
+ }
43
+ export function brokerUnregister(id) {
44
+ return brokerPost('/unregister', { id });
45
+ }
46
+ /** Undelivered messages for a peer. The broker marks them delivered as it returns them. */
47
+ export async function brokerPoll(id) {
48
+ const r = await brokerPost('/poll-messages', { id });
49
+ return r.messages ?? [];
50
+ }
51
+ export async function brokerSend(fromId, toId, text) {
52
+ try {
53
+ const r = await brokerPost('/send-message', { from_id: fromId, to_id: toId, text });
54
+ return { ok: r.ok === true, ...(r.error ? { error: r.error } : {}) };
55
+ }
56
+ catch (e) {
57
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
58
+ }
59
+ }
60
+ // The human peer's current broker id (set by src/chat.ts). It lives here, not
61
+ // in chat.ts, so the session roster and the command dispatcher can read it
62
+ // without importing the chat loop.
63
+ let humanPeerId = null;
64
+ export function getHumanPeerId() {
65
+ return humanPeerId;
66
+ }
67
+ export function setHumanPeerId(id) {
68
+ humanPeerId = id;
69
+ }
70
+ //# sourceMappingURL=peerBroker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peerBroker.js","sourceRoot":"","sources":["../src/peerBroker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export interface BrokerSupervisorConfig {
2
+ /** argv for the broker. command[0] must be an absolute path to an executable. */
3
+ command?: string[];
4
+ }
5
+ /** True when the broker answers /health. Never throws. */
6
+ export declare function isBrokerHealthy(timeoutMs?: number): Promise<boolean>;
7
+ /**
8
+ * Why a configured command cannot be used, or null when it can. Absolute paths
9
+ * only, for the same reason as extensions.allow: a bare name would be resolved
10
+ * against whatever PATH launchd handed the daemon, which is not the user's.
11
+ */
12
+ export declare function validateBrokerCommand(command: unknown): string | null;
13
+ /**
14
+ * Keep the broker up. Returns a stop function that ends supervision; it
15
+ * deliberately leaves a running broker running (see header).
16
+ */
17
+ export declare function startBrokerSupervisor(config: BrokerSupervisorConfig | undefined, opts?: {
18
+ checkMs?: number;
19
+ logDir?: string;
20
+ }): () => void;
21
+ //# sourceMappingURL=peerBrokerSupervisor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peerBrokerSupervisor.d.ts","sourceRoot":"","sources":["../src/peerBrokerSupervisor.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,sBAAsB;IACrC,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAMD,0DAA0D;AAC1D,wBAAsB,eAAe,CAAC,SAAS,SAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,CASrF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAWrE;AAYD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,GAAG,SAAS,EAC1C,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/C,MAAM,IAAI,CAsDZ"}
@@ -0,0 +1,131 @@
1
+ // ============================================================================
2
+ // claude-peers broker supervision (ISS-0193)
3
+ // ============================================================================
4
+ // The broker was started by whichever peer happened to come up first. When that
5
+ // was Claude Desktop, the broker lived in Desktop's process group, and quitting
6
+ // Desktop took peer messaging down for every session on the machine. The
7
+ // daemon already depends on the broker (chat.ts, agentCommands.ts,
8
+ // agentPresence.ts), so it is the natural owner: this keeps one alive.
9
+ //
10
+ // Opt-in. With no `peers.broker.command` in ~/.decibel/config.yaml this does
11
+ // nothing, which is the case for everyone who does not run claude-peers.
12
+ //
13
+ // The broker is spawned DETACHED: its own session, so it is not in the
14
+ // daemon's process group, and launchd stopping the daemon does not stop it.
15
+ // A daemon restart therefore never drops peers; the next daemon finds the
16
+ // broker healthy and leaves it alone.
17
+ // ============================================================================
18
+ import { spawn } from 'child_process';
19
+ import { existsSync, mkdirSync, openSync, closeSync } from 'fs';
20
+ import { isAbsolute, join } from 'path';
21
+ import { homedir } from 'os';
22
+ import { log } from './config.js';
23
+ const CHECK_MS = 30_000;
24
+ const HEALTH_TIMEOUT_MS = 1_500;
25
+ function brokerPort() {
26
+ return process.env.CLAUDE_PEERS_PORT ?? '7899';
27
+ }
28
+ /** True when the broker answers /health. Never throws. */
29
+ export async function isBrokerHealthy(timeoutMs = HEALTH_TIMEOUT_MS) {
30
+ try {
31
+ const res = await fetch(`http://127.0.0.1:${brokerPort()}/health`, {
32
+ signal: AbortSignal.timeout(timeoutMs),
33
+ });
34
+ return res.ok;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ /**
41
+ * Why a configured command cannot be used, or null when it can. Absolute paths
42
+ * only, for the same reason as extensions.allow: a bare name would be resolved
43
+ * against whatever PATH launchd handed the daemon, which is not the user's.
44
+ */
45
+ export function validateBrokerCommand(command) {
46
+ if (!Array.isArray(command) || command.length === 0) {
47
+ return 'peers.broker.command must be a non-empty list';
48
+ }
49
+ if (!command.every((a) => typeof a === 'string' && a.length > 0)) {
50
+ return 'peers.broker.command entries must be non-empty strings';
51
+ }
52
+ const exe = command[0];
53
+ if (!isAbsolute(exe))
54
+ return `peers.broker.command[0] must be an absolute path, got "${exe}"`;
55
+ if (!existsSync(exe))
56
+ return `peers.broker.command[0] does not exist: ${exe}`;
57
+ return null;
58
+ }
59
+ function isAlive(pid) {
60
+ if (!pid)
61
+ return false;
62
+ try {
63
+ process.kill(pid, 0);
64
+ return true;
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
70
+ /**
71
+ * Keep the broker up. Returns a stop function that ends supervision; it
72
+ * deliberately leaves a running broker running (see header).
73
+ */
74
+ export function startBrokerSupervisor(config, opts = {}) {
75
+ if (!config?.command)
76
+ return () => { };
77
+ const problem = validateBrokerCommand(config.command);
78
+ if (problem) {
79
+ log(`Peers: broker supervision disabled: ${problem}`);
80
+ return () => { };
81
+ }
82
+ const [exe, ...args] = config.command;
83
+ const logDir = opts.logDir ?? join(homedir(), '.decibel', 'logs');
84
+ let stopped = false;
85
+ let checking = false;
86
+ let childPid;
87
+ const check = async () => {
88
+ if (stopped || checking)
89
+ return;
90
+ checking = true;
91
+ try {
92
+ if (await isBrokerHealthy())
93
+ return;
94
+ // A broker we started may still be binding its port. Give it until the
95
+ // next check rather than starting a second one that will lose the race.
96
+ if (isAlive(childPid))
97
+ return;
98
+ mkdirSync(logDir, { recursive: true });
99
+ const fd = openSync(join(logDir, 'peers-broker.log'), 'a');
100
+ try {
101
+ const child = spawn(exe, args, {
102
+ detached: true,
103
+ stdio: ['ignore', fd, fd],
104
+ env: process.env,
105
+ });
106
+ child.on('error', (err) => log(`Peers: broker failed to start: ${err.message}`));
107
+ child.unref();
108
+ childPid = child.pid;
109
+ log(`Peers: broker was down, started it (PID ${child.pid})`);
110
+ }
111
+ finally {
112
+ closeSync(fd);
113
+ }
114
+ }
115
+ catch (err) {
116
+ log(`Peers: broker check failed: ${err instanceof Error ? err.message : String(err)}`);
117
+ }
118
+ finally {
119
+ checking = false;
120
+ }
121
+ };
122
+ void check();
123
+ const timer = setInterval(() => void check(), opts.checkMs ?? CHECK_MS);
124
+ timer.unref?.();
125
+ log(`Peers: supervising the claude-peers broker on port ${brokerPort()}`);
126
+ return () => {
127
+ stopped = true;
128
+ clearInterval(timer);
129
+ };
130
+ }
131
+ //# sourceMappingURL=peerBrokerSupervisor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peerBrokerSupervisor.js","sourceRoot":"","sources":["../src/peerBrokerSupervisor.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"projectPaths.d.ts","sourceRoot":"","sources":["../src/projectPaths.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAkCD;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAYxB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAezC"}
1
+ {"version":3,"file":"projectPaths.d.ts","sourceRoot":"","sources":["../src/projectPaths.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAiBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAYxB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAezC"}
@@ -4,29 +4,11 @@
4
4
  // Helps MCP tools resolve project roots from project IDs.
5
5
  // Uses registry-based resolution with fallback to dynamic discovery.
6
6
  // ============================================================================
7
- import fs from 'fs';
8
7
  import path from 'path';
9
- import { resolveProject, listProjects } from './projectRegistry.js';
8
+ import { resolveProject, listProjects, findDecibelDir } from './projectRegistry.js';
10
9
  // ============================================================================
11
10
  // Path Resolution Helpers
12
11
  // ============================================================================
13
- /**
14
- * Walk up directory tree looking for a .decibel folder
15
- */
16
- function findDecibelDir(start) {
17
- let current = path.resolve(start);
18
- while (true) {
19
- const candidate = path.join(current, '.decibel');
20
- if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
21
- return current; // Return the project root, not the .decibel folder
22
- }
23
- const parent = path.dirname(current);
24
- if (parent === current)
25
- break;
26
- current = parent;
27
- }
28
- return undefined;
29
- }
30
12
  /**
31
13
  * Get the project name from a project root directory
32
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"projectPaths.js","sourceRoot":"","sources":["../src/projectPaths.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAC/E,0DAA0D;AAC1D,qEAAqE;AACrE,+EAA+E;AAE/E,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,cAAc,EAAgB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAYlF,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E;;GAEG;AACH,SAAS,cAAc,CAAC,KAAa;IACnC,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAClC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACjD,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YACrE,OAAO,OAAO,CAAC,CAAC,mDAAmD;QACrE,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,MAAM,KAAK,OAAO;YAAE,MAAM;QAC9B,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,WAAmB;IAChD,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACpC,CAAC;AAED,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,KAAK,GAAiB,cAAc,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO;YACL,SAAS,EAAE,KAAK,CAAC,EAAE;YACnB,WAAW,EAAE,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,EAAE;YACnC,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,wDAAwD;QACxD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,kBAAkB,GAAG,YAAY,EAAE,CAAC;IAC1C,MAAM,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAe,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAE9D,qDAAqD;IACrD,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC7E,MAAM,cAAc,GAAG,cAAc,CAAC,kBAAkB,CAAC,CAAC;IAC1D,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,OAAO,GAAG,qBAAqB,CAAC,cAAc,CAAC,CAAC;QACtD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"projectPaths.js","sourceRoot":"","sources":["../src/projectPaths.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAC/E,0DAA0D;AAC1D,qEAAqE;AACrE,+EAA+E;AAE/E,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,cAAc,EAAgB,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAYlG,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E;;GAEG;AACH,SAAS,qBAAqB,CAAC,WAAmB;IAChD,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACpC,CAAC;AAED,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,KAAK,GAAiB,cAAc,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO;YACL,SAAS,EAAE,KAAK,CAAC,EAAE;YACnB,WAAW,EAAE,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,EAAE;YACnC,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,wDAAwD;QACxD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,kBAAkB,GAAG,YAAY,EAAE,CAAC;IAC1C,MAAM,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAe,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAE9D,qDAAqD;IACrD,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC7E,MAAM,cAAc,GAAG,cAAc,CAAC,kBAAkB,CAAC,CAAC;IAC1D,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,OAAO,GAAG,qBAAqB,CAAC,cAAc,CAAC,CAAC;QACtD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -50,6 +50,28 @@ export declare function listCandidatePaths(entry: ProjectEntry): string[];
50
50
  * at the right location.
51
51
  */
52
52
  export declare function getEffectivePath(entry: ProjectEntry): EffectivePath;
53
+ /**
54
+ * Walk up directory tree looking for a .decibel folder.
55
+ *
56
+ * The global config directory ENDS the walk rather than merely being skipped.
57
+ * Skipping it was the first fix and it was not enough: the Windows CI leg then
58
+ * climbed straight past the fake HOME the test had set up and returned the real
59
+ * one above it (`C:\Users\runneradmin` -> project "RUNNER~1"). The general
60
+ * case is the same shape — a stray `.decibel` in an ancestor of every home
61
+ * directory, `/Users` or `C:\Users`, would be handed to every user on the
62
+ * machine as their project. Nothing above your home directory is your project,
63
+ * so reaching it means the answer is "no project", not "keep looking".
64
+ *
65
+ * Without this, any cwd under $HOME that is not itself a project resolved to a
66
+ * "project" rooted at HOME with an id of basename(HOME), and every write then
67
+ * landed in the directory all projects share (ISS-0177).
68
+ *
69
+ * This constrains discovery only. Explicit resolution — a registry entry,
70
+ * DECIBEL_PROJECT_ROOT, an absolute path — still resolves HOME for anyone who
71
+ * genuinely means it, and a real checkout under $HOME is still found, because
72
+ * the walk reaches it long before it reaches home.
73
+ */
74
+ export declare function findDecibelDir(start: string): string | undefined;
53
75
  /**
54
76
  * Resolve a project ID or alias to its filesystem path.
55
77
  *
@@ -1 +1 @@
1
- {"version":3,"file":"projectRegistry.d.ts","sourceRoot":"","sources":["../src/projectRegistry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"projectRegistry.d.ts","sourceRoot":"","sources":["../src/projectRegistry.ts"],"names":[],"mappings":"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"}
@@ -218,13 +218,69 @@ function saveRegistry(registry) {
218
218
  // Discovery Helpers
219
219
  // ============================================================================
220
220
  /**
221
- * Walk up directory tree looking for a .decibel folder
221
+ * The global Decibel config directory, ~/.decibel — home to projects.json,
222
+ * device.json, daemon.meta, config.yaml and the hooks. It is named exactly like
223
+ * a project's `.decibel/` but it is NOT a project, and the walk-up below must
224
+ * never return HOME as a project root (ISS-0177).
225
+ *
226
+ * Resolved per call rather than captured at import so it tracks $HOME, the way
227
+ * daemonConfig does.
228
+ */
229
+ function globalConfigDir() {
230
+ return path.join(os.homedir(), '.decibel');
231
+ }
232
+ /**
233
+ * Do two paths name the same directory as the filesystem sees it?
234
+ *
235
+ * A string compare lies on Windows in two ways that both showed up in the CI
236
+ * run that found ISS-0177: cwd came back as the 8.3 short name
237
+ * (C:\Users\RUNNER~1) while os.homedir() returned C:\Users\runneradmin, and
238
+ * case can differ freely. realpath.native resolves the short name to the real
239
+ * one; the case fold is win32-only, since POSIX paths are case-sensitive.
222
240
  */
223
- function findDecibelDir(start) {
241
+ function isSamePath(a, b) {
242
+ const canonical = (p) => {
243
+ let resolved = path.resolve(p);
244
+ try {
245
+ resolved = fs.realpathSync.native(resolved);
246
+ }
247
+ catch {
248
+ // Not on disk (yet) — the resolved form is the best we can do.
249
+ }
250
+ return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
251
+ };
252
+ return canonical(a) === canonical(b);
253
+ }
254
+ /**
255
+ * Walk up directory tree looking for a .decibel folder.
256
+ *
257
+ * The global config directory ENDS the walk rather than merely being skipped.
258
+ * Skipping it was the first fix and it was not enough: the Windows CI leg then
259
+ * climbed straight past the fake HOME the test had set up and returned the real
260
+ * one above it (`C:\Users\runneradmin` -> project "RUNNER~1"). The general
261
+ * case is the same shape — a stray `.decibel` in an ancestor of every home
262
+ * directory, `/Users` or `C:\Users`, would be handed to every user on the
263
+ * machine as their project. Nothing above your home directory is your project,
264
+ * so reaching it means the answer is "no project", not "keep looking".
265
+ *
266
+ * Without this, any cwd under $HOME that is not itself a project resolved to a
267
+ * "project" rooted at HOME with an id of basename(HOME), and every write then
268
+ * landed in the directory all projects share (ISS-0177).
269
+ *
270
+ * This constrains discovery only. Explicit resolution — a registry entry,
271
+ * DECIBEL_PROJECT_ROOT, an absolute path — still resolves HOME for anyone who
272
+ * genuinely means it, and a real checkout under $HOME is still found, because
273
+ * the walk reaches it long before it reaches home.
274
+ */
275
+ export function findDecibelDir(start) {
276
+ const globalDir = globalConfigDir();
224
277
  let current = path.resolve(start);
225
278
  while (true) {
226
279
  const candidate = path.join(current, '.decibel');
227
280
  if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
281
+ if (isSamePath(candidate, globalDir)) {
282
+ return undefined; // Reached ~/.decibel — the config dir, and the ceiling.
283
+ }
228
284
  return current; // Return the project root, not the .decibel folder
229
285
  }
230
286
  const parent = path.dirname(current);