@triflux/core 10.0.0-alpha.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 (75) hide show
  1. package/hooks/agent-route-guard.mjs +109 -0
  2. package/hooks/cross-review-tracker.mjs +122 -0
  3. package/hooks/error-context.mjs +148 -0
  4. package/hooks/hook-manager.mjs +352 -0
  5. package/hooks/hook-orchestrator.mjs +312 -0
  6. package/hooks/hook-registry.json +213 -0
  7. package/hooks/hooks.json +89 -0
  8. package/hooks/keyword-rules.json +581 -0
  9. package/hooks/lib/resolve-root.mjs +59 -0
  10. package/hooks/mcp-config-watcher.mjs +85 -0
  11. package/hooks/pipeline-stop.mjs +76 -0
  12. package/hooks/safety-guard.mjs +106 -0
  13. package/hooks/subagent-verifier.mjs +80 -0
  14. package/hub/assign-callbacks.mjs +133 -0
  15. package/hub/bridge.mjs +799 -0
  16. package/hub/cli-adapter-base.mjs +192 -0
  17. package/hub/codex-adapter.mjs +190 -0
  18. package/hub/codex-compat.mjs +78 -0
  19. package/hub/codex-preflight.mjs +147 -0
  20. package/hub/delegator/contracts.mjs +37 -0
  21. package/hub/delegator/index.mjs +14 -0
  22. package/hub/delegator/schema/delegator-tools.schema.json +250 -0
  23. package/hub/delegator/service.mjs +307 -0
  24. package/hub/delegator/tool-definitions.mjs +35 -0
  25. package/hub/fullcycle.mjs +96 -0
  26. package/hub/gemini-adapter.mjs +179 -0
  27. package/hub/hitl.mjs +143 -0
  28. package/hub/intent.mjs +193 -0
  29. package/hub/lib/process-utils.mjs +361 -0
  30. package/hub/middleware/request-logger.mjs +81 -0
  31. package/hub/paths.mjs +30 -0
  32. package/hub/pipeline/gates/confidence.mjs +56 -0
  33. package/hub/pipeline/gates/consensus.mjs +94 -0
  34. package/hub/pipeline/gates/index.mjs +5 -0
  35. package/hub/pipeline/gates/selfcheck.mjs +82 -0
  36. package/hub/pipeline/index.mjs +318 -0
  37. package/hub/pipeline/state.mjs +191 -0
  38. package/hub/pipeline/transitions.mjs +124 -0
  39. package/hub/platform.mjs +225 -0
  40. package/hub/quality/deslop.mjs +253 -0
  41. package/hub/reflexion.mjs +372 -0
  42. package/hub/research.mjs +146 -0
  43. package/hub/router.mjs +791 -0
  44. package/hub/routing/complexity.mjs +166 -0
  45. package/hub/routing/index.mjs +117 -0
  46. package/hub/routing/q-learning.mjs +336 -0
  47. package/hub/session-fingerprint.mjs +352 -0
  48. package/hub/state.mjs +245 -0
  49. package/hub/team-bridge.mjs +25 -0
  50. package/hub/token-mode.mjs +224 -0
  51. package/hub/workers/worker-utils.mjs +104 -0
  52. package/hud/colors.mjs +88 -0
  53. package/hud/constants.mjs +81 -0
  54. package/hud/hud-qos-status.mjs +206 -0
  55. package/hud/providers/claude.mjs +309 -0
  56. package/hud/providers/codex.mjs +151 -0
  57. package/hud/providers/gemini.mjs +320 -0
  58. package/hud/renderers.mjs +424 -0
  59. package/hud/terminal.mjs +140 -0
  60. package/hud/utils.mjs +287 -0
  61. package/package.json +31 -0
  62. package/scripts/lib/claudemd-manager.mjs +325 -0
  63. package/scripts/lib/context.mjs +67 -0
  64. package/scripts/lib/cross-review-utils.mjs +51 -0
  65. package/scripts/lib/env-probe.mjs +241 -0
  66. package/scripts/lib/gemini-profiles.mjs +85 -0
  67. package/scripts/lib/hook-utils.mjs +14 -0
  68. package/scripts/lib/keyword-rules.mjs +166 -0
  69. package/scripts/lib/logger.mjs +105 -0
  70. package/scripts/lib/mcp-filter.mjs +739 -0
  71. package/scripts/lib/mcp-guard-engine.mjs +940 -0
  72. package/scripts/lib/mcp-manifest.mjs +79 -0
  73. package/scripts/lib/mcp-server-catalog.mjs +118 -0
  74. package/scripts/lib/psmux-info.mjs +119 -0
  75. package/scripts/lib/remote-spawn-transfer.mjs +196 -0
@@ -0,0 +1,581 @@
1
+ {
2
+ "rules": [
3
+ {
4
+ "id": "tfx-cancel",
5
+ "patterns": [
6
+ {
7
+ "source": "\\b(canceltfx|stoptfx)\\b",
8
+ "flags": "i"
9
+ }
10
+ ],
11
+ "skill": null,
12
+ "action": "suppress_all",
13
+ "priority": 0,
14
+ "supersedes": [
15
+ "tfx-multi",
16
+ "tfx-auto",
17
+ "tfx-auto-codex",
18
+ "tfx-codex",
19
+ "tfx-gemini"
20
+ ],
21
+ "exclusive": true,
22
+ "state": null,
23
+ "mcp_route": null
24
+ },
25
+ {
26
+ "id": "tfx-multi",
27
+ "patterns": [
28
+ {
29
+ "source": "(?<!\\b(?:my|the|our|omc|oh-my-claudecode)\\s)\\btfx[\\s-]?multi\\b",
30
+ "flags": "i"
31
+ }
32
+ ],
33
+ "skill": "tfx-multi",
34
+ "priority": 1,
35
+ "supersedes": [],
36
+ "exclusive": false,
37
+ "state": null,
38
+ "mcp_route": null
39
+ },
40
+ {
41
+ "id": "tfx-codex-swarm",
42
+ "patterns": [
43
+ {
44
+ "source": "\\btfx[\\s-]?codex[\\s-]?swarm\\b",
45
+ "flags": "i"
46
+ },
47
+ {
48
+ "source": "\\bcodex[\\s-]?swarm\\b",
49
+ "flags": "i"
50
+ }
51
+ ],
52
+ "skill": "tfx-codex-swarm",
53
+ "priority": 1,
54
+ "supersedes": [
55
+ "tfx-codex"
56
+ ],
57
+ "exclusive": false,
58
+ "state": null,
59
+ "mcp_route": null
60
+ },
61
+ {
62
+ "id": "tfx-remote-spawn",
63
+ "patterns": [
64
+ {
65
+ "source": "\\btfx[\\s-]?remote[\\s-]?spawn\\b",
66
+ "flags": "i"
67
+ },
68
+ {
69
+ "source": "\\bremote[\\s-]?spawn\\b",
70
+ "flags": "i"
71
+ }
72
+ ],
73
+ "skill": "tfx-remote-spawn",
74
+ "priority": 1,
75
+ "supersedes": [],
76
+ "exclusive": false,
77
+ "state": null,
78
+ "mcp_route": null
79
+ },
80
+ {
81
+ "id": "tfx-auto-codex",
82
+ "patterns": [
83
+ {
84
+ "source": "\\btfx[\\s-]?auto[\\s-]?codex\\b",
85
+ "flags": "i"
86
+ }
87
+ ],
88
+ "skill": "tfx-auto-codex",
89
+ "priority": 1,
90
+ "supersedes": [
91
+ "tfx-auto",
92
+ "tfx-codex"
93
+ ],
94
+ "exclusive": false,
95
+ "state": null,
96
+ "mcp_route": null
97
+ },
98
+ {
99
+ "id": "tfx-auto",
100
+ "patterns": [
101
+ {
102
+ "source": "\\btfx[\\s-]?auto\\b",
103
+ "flags": "i"
104
+ }
105
+ ],
106
+ "skill": "tfx-auto",
107
+ "priority": 2,
108
+ "supersedes": [],
109
+ "exclusive": false,
110
+ "state": null,
111
+ "mcp_route": null
112
+ },
113
+ {
114
+ "id": "tfx-codex",
115
+ "patterns": [
116
+ {
117
+ "source": "\\btfx[\\s-]?codex\\b(?![\\s-]?swarm)",
118
+ "flags": "i"
119
+ }
120
+ ],
121
+ "skill": "tfx-codex",
122
+ "priority": 3,
123
+ "supersedes": [],
124
+ "exclusive": false,
125
+ "state": null,
126
+ "mcp_route": null
127
+ },
128
+ {
129
+ "id": "tfx-gemini",
130
+ "patterns": [
131
+ {
132
+ "source": "\\btfx[\\s-]?gemini\\b",
133
+ "flags": "i"
134
+ }
135
+ ],
136
+ "skill": "tfx-gemini",
137
+ "priority": 3,
138
+ "supersedes": [],
139
+ "exclusive": false,
140
+ "state": null,
141
+ "mcp_route": null
142
+ },
143
+ {
144
+ "id": "tfx-hub",
145
+ "patterns": [
146
+ {
147
+ "source": "\\btfx[\\s-]?hub\\b",
148
+ "flags": "i"
149
+ }
150
+ ],
151
+ "skill": "tfx-hub",
152
+ "priority": 2,
153
+ "supersedes": [],
154
+ "exclusive": false,
155
+ "state": null,
156
+ "mcp_route": null
157
+ },
158
+ {
159
+ "id": "tfx-doctor",
160
+ "patterns": [
161
+ {
162
+ "source": "\\btfx[\\s-]?doctor\\b",
163
+ "flags": "i"
164
+ }
165
+ ],
166
+ "skill": "tfx-doctor",
167
+ "priority": 2,
168
+ "supersedes": [],
169
+ "exclusive": false,
170
+ "state": null,
171
+ "mcp_route": null
172
+ },
173
+ {
174
+ "id": "tfx-setup",
175
+ "patterns": [
176
+ {
177
+ "source": "\\btfx[\\s-]?setup\\b",
178
+ "flags": "i"
179
+ }
180
+ ],
181
+ "skill": "tfx-setup",
182
+ "priority": 2,
183
+ "supersedes": [],
184
+ "exclusive": false,
185
+ "state": null,
186
+ "mcp_route": null
187
+ },
188
+ {
189
+ "id": "tfx-autoresearch",
190
+ "patterns": [
191
+ {
192
+ "source": "\\btfx[\\s-]?autoresearch\\b",
193
+ "flags": "i"
194
+ }
195
+ ],
196
+ "skill": "tfx-autoresearch",
197
+ "priority": 2,
198
+ "supersedes": [],
199
+ "exclusive": false,
200
+ "state": null,
201
+ "mcp_route": null
202
+ },
203
+ {
204
+ "id": "tfx-deep-interview",
205
+ "patterns": [
206
+ {
207
+ "source": "\\btfx[\\s-]?deep[\\s-]?interview\\b",
208
+ "flags": "i"
209
+ }
210
+ ],
211
+ "skill": "tfx-deep-interview",
212
+ "priority": 2,
213
+ "supersedes": [],
214
+ "exclusive": false,
215
+ "state": null,
216
+ "mcp_route": null
217
+ },
218
+ {
219
+ "id": "handoff-route",
220
+ "patterns": [
221
+ {
222
+ "source": "(?:^|(?<=\\s))(핸드오프|handoff|세션\\s*넘기)[\\s]*(생성|만들|써줘|작성|넘겨)",
223
+ "flags": "i"
224
+ }
225
+ ],
226
+ "skill": null,
227
+ "action": "handoff",
228
+ "priority": 5,
229
+ "supersedes": [],
230
+ "exclusive": false,
231
+ "state": null,
232
+ "mcp_route": null
233
+ },
234
+ {
235
+ "id": "notion-route",
236
+ "patterns": [
237
+ {
238
+ "source": "(?:^|(?<=\\s))(노션|notion)[\\s-]?(페이지|글|조회|생성|수정|검색)(?:$|(?=\\s))",
239
+ "flags": "i"
240
+ },
241
+ {
242
+ "source": "(노션|\\bnotion\\b)",
243
+ "flags": "i"
244
+ }
245
+ ],
246
+ "skill": null,
247
+ "priority": 10,
248
+ "supersedes": [],
249
+ "exclusive": false,
250
+ "state": null,
251
+ "mcp_route": "gemini"
252
+ },
253
+ {
254
+ "id": "chrome-route",
255
+ "patterns": [
256
+ {
257
+ "source": "(?:^|(?<=\\s))(크롬|chrome|브라우저)[\\s-]?(열|접속|로그인|navigate|click)(?:$|(?=\\s))",
258
+ "flags": "i"
259
+ },
260
+ {
261
+ "source": "(크롬|\\bchrome\\b)",
262
+ "flags": "i"
263
+ }
264
+ ],
265
+ "skill": null,
266
+ "priority": 10,
267
+ "supersedes": [],
268
+ "exclusive": false,
269
+ "state": null,
270
+ "mcp_route": "gemini"
271
+ },
272
+ {
273
+ "id": "jira-route",
274
+ "patterns": [
275
+ {
276
+ "source": "(?:^|(?<=\\s))(jira|지라)[\\s-]?(이슈|티켓|생성|조회|스프린트)(?:$|(?=\\s))",
277
+ "flags": "i"
278
+ },
279
+ {
280
+ "source": "(지라|\\bjira\\b)",
281
+ "flags": "i"
282
+ }
283
+ ],
284
+ "skill": null,
285
+ "priority": 10,
286
+ "supersedes": [],
287
+ "exclusive": false,
288
+ "state": null,
289
+ "mcp_route": "codex"
290
+ },
291
+ {
292
+ "id": "mail-route",
293
+ "patterns": [
294
+ {
295
+ "source": "(?:^|(?<=\\s))(메일|gmail|이메일)[\\s-]?(보내|읽|검색|draft)(?:$|(?=\\s))",
296
+ "flags": "i"
297
+ },
298
+ {
299
+ "source": "(메일|이메일|\\bgmail\\b)",
300
+ "flags": "i"
301
+ }
302
+ ],
303
+ "skill": null,
304
+ "priority": 10,
305
+ "supersedes": [],
306
+ "exclusive": false,
307
+ "state": null,
308
+ "mcp_route": "gemini"
309
+ },
310
+ {
311
+ "id": "calendar-route",
312
+ "patterns": [
313
+ {
314
+ "source": "(?:^|(?<=\\s))(일정|캘린더|calendar)[\\s-]?(생성|조회|추가)(?:$|(?=\\s))",
315
+ "flags": "i"
316
+ },
317
+ {
318
+ "source": "(일정|캘린더|\\bcalendar\\b)",
319
+ "flags": "i"
320
+ }
321
+ ],
322
+ "skill": null,
323
+ "priority": 10,
324
+ "supersedes": [],
325
+ "exclusive": false,
326
+ "state": null,
327
+ "mcp_route": "gemini"
328
+ },
329
+ {
330
+ "id": "playwright-route",
331
+ "patterns": [
332
+ {
333
+ "source": "(?:^|(?<=\\s))(playwright|브라우저\\s*테스트)(?:$|(?=\\s))",
334
+ "flags": "i"
335
+ }
336
+ ],
337
+ "skill": null,
338
+ "priority": 10,
339
+ "supersedes": [],
340
+ "exclusive": false,
341
+ "state": null,
342
+ "mcp_route": "gemini"
343
+ },
344
+ {
345
+ "id": "canva-route",
346
+ "patterns": [
347
+ {
348
+ "source": "(?:^|(?<=\\s))(canva|캔바|디자인\\s*생성)(?:$|(?=\\s))",
349
+ "flags": "i"
350
+ }
351
+ ],
352
+ "skill": null,
353
+ "priority": 10,
354
+ "supersedes": [],
355
+ "exclusive": false,
356
+ "state": null,
357
+ "mcp_route": "gemini"
358
+ },
359
+ {
360
+ "id": "confluence-route",
361
+ "patterns": [
362
+ {
363
+ "source": "(?:^|(?<=\\s))(confluence|위키|wiki)[\\s-]?(페이지|문서|조회|생성|수정|검색)(?:$|(?=\\s))",
364
+ "flags": "i"
365
+ },
366
+ {
367
+ "source": "(위키|\\bconfluence\\b|\\bwiki\\b)",
368
+ "flags": "i"
369
+ }
370
+ ],
371
+ "skill": null,
372
+ "priority": 10,
373
+ "supersedes": [],
374
+ "exclusive": false,
375
+ "state": null,
376
+ "mcp_route": "gemini"
377
+ },
378
+ {
379
+ "id": "slack-route",
380
+ "patterns": [
381
+ {
382
+ "source": "(?:^|(?<=\\s))(slack|슬랙)[\\s-]?(메시지|채널|보내|읽|검색|알림)(?:$|(?=\\s))",
383
+ "flags": "i"
384
+ },
385
+ {
386
+ "source": "(슬랙|\\bslack\\b)",
387
+ "flags": "i"
388
+ }
389
+ ],
390
+ "skill": null,
391
+ "priority": 10,
392
+ "supersedes": [],
393
+ "exclusive": false,
394
+ "state": null,
395
+ "mcp_route": "gemini"
396
+ },
397
+ {
398
+ "id": "github-route",
399
+ "patterns": [
400
+ {
401
+ "source": "(?:^|(?<=\\s))(github|깃허브)[\\s-]?(이슈|issue|PR|pull|커밋|commit|리포|repo)(?:$|(?=\\s))",
402
+ "flags": "i"
403
+ },
404
+ {
405
+ "source": "(깃허브|\\bgithub\\b)",
406
+ "flags": "i"
407
+ }
408
+ ],
409
+ "skill": null,
410
+ "priority": 10,
411
+ "supersedes": [],
412
+ "exclusive": false,
413
+ "state": null,
414
+ "mcp_route": "codex"
415
+ },
416
+ {
417
+ "id": "gstack-checkpoint",
418
+ "patterns": [
419
+ {
420
+ "source": "(?:어디까지\\s*했|뭐\\s*하고\\s*있었|뭘\\s*해야\\s*하|까먹|기억이\\s*안|이어서\\s*하|중단\\s*지점)",
421
+ "flags": "i"
422
+ },
423
+ {
424
+ "source": "\\b(?:checkpoint|resume|pick\\s*up|where\\s*was\\s*I)\\b",
425
+ "flags": "i"
426
+ }
427
+ ],
428
+ "skill": "checkpoint",
429
+ "priority": 5,
430
+ "supersedes": [],
431
+ "exclusive": false,
432
+ "state": null,
433
+ "mcp_route": null
434
+ },
435
+ {
436
+ "id": "gstack-office-hours",
437
+ "patterns": [
438
+ {
439
+ "source": "(?:아이디어\\s*(?:있|정리|브레인)|뭘\\s*만들|제품\\s*구상)",
440
+ "flags": "i"
441
+ },
442
+ {
443
+ "source": "\\boffice[\\s-]?hours\\b",
444
+ "flags": "i"
445
+ }
446
+ ],
447
+ "skill": "office-hours",
448
+ "priority": 5,
449
+ "supersedes": [],
450
+ "exclusive": false,
451
+ "state": null,
452
+ "mcp_route": null
453
+ },
454
+ {
455
+ "id": "gstack-ship",
456
+ "patterns": [
457
+ {
458
+ "source": "(?:배포해|PR\\s*만들|릴리스\\s*해|머지하고\\s*배포)",
459
+ "flags": "i"
460
+ },
461
+ {
462
+ "source": "\\bship\\b(?!.*tfx)",
463
+ "flags": "i"
464
+ }
465
+ ],
466
+ "skill": "ship",
467
+ "priority": 5,
468
+ "supersedes": [],
469
+ "exclusive": false,
470
+ "state": null,
471
+ "mcp_route": null
472
+ },
473
+ {
474
+ "id": "gstack-investigate",
475
+ "patterns": [
476
+ {
477
+ "source": "(?:왜\\s*(?:안\\s*돼|터져|에러)|원인\\s*(?:찾아|분석)|root\\s*cause)",
478
+ "flags": "i"
479
+ },
480
+ {
481
+ "source": "\\binvestigate\\b",
482
+ "flags": "i"
483
+ }
484
+ ],
485
+ "skill": "investigate",
486
+ "priority": 5,
487
+ "supersedes": [],
488
+ "exclusive": false,
489
+ "state": null,
490
+ "mcp_route": null
491
+ },
492
+ {
493
+ "id": "gstack-cso",
494
+ "patterns": [
495
+ {
496
+ "source": "(?:보안\\s*(?:감사|점검|리뷰|스캔)|\\bcso\\b|\\bOWASP\\b|\\bSTRIDE\\b)",
497
+ "flags": "i"
498
+ }
499
+ ],
500
+ "skill": "cso",
501
+ "priority": 5,
502
+ "supersedes": [],
503
+ "exclusive": false,
504
+ "state": null,
505
+ "mcp_route": null
506
+ },
507
+ {
508
+ "id": "gstack-qa-browser",
509
+ "patterns": [
510
+ {
511
+ "source": "(?:사이트|브라우저|웹)\\s*(?:QA|테스트|확인|점검)",
512
+ "flags": "i"
513
+ },
514
+ {
515
+ "source": "(?:클릭|접속).*(?:테스트|확인)",
516
+ "flags": "i"
517
+ }
518
+ ],
519
+ "skill": "qa",
520
+ "priority": 5,
521
+ "supersedes": [],
522
+ "exclusive": false,
523
+ "state": null,
524
+ "mcp_route": null
525
+ },
526
+ {
527
+ "id": "gstack-retro",
528
+ "patterns": [
529
+ {
530
+ "source": "(?:회고|이번\\s*주\\s*뭐\\s*했|주간\\s*리뷰|뭘\\s*했지)",
531
+ "flags": "i"
532
+ },
533
+ {
534
+ "source": "\\bretro\\b",
535
+ "flags": "i"
536
+ }
537
+ ],
538
+ "skill": "retro",
539
+ "priority": 5,
540
+ "supersedes": [],
541
+ "exclusive": false,
542
+ "state": null,
543
+ "mcp_route": null
544
+ },
545
+ {
546
+ "id": "gstack-autoplan",
547
+ "patterns": [
548
+ {
549
+ "source": "(?:자동\\s*리뷰|리뷰\\s*파이프라인|전체\\s*리뷰)",
550
+ "flags": "i"
551
+ },
552
+ {
553
+ "source": "\\bautoplan\\b",
554
+ "flags": "i"
555
+ }
556
+ ],
557
+ "skill": "autoplan",
558
+ "priority": 5,
559
+ "supersedes": [],
560
+ "exclusive": false,
561
+ "state": null,
562
+ "mcp_route": null
563
+ },
564
+ {
565
+ "id": "suppress-omc-team",
566
+ "patterns": [
567
+ {
568
+ "source": "(?<!tfx[\\s-]?)(팀|\\bteam\\b)",
569
+ "flags": "i"
570
+ }
571
+ ],
572
+ "skill": null,
573
+ "action": "suppress_omc",
574
+ "priority": 20,
575
+ "supersedes": [],
576
+ "exclusive": false,
577
+ "state": null,
578
+ "mcp_route": null
579
+ }
580
+ ]
581
+ }
@@ -0,0 +1,59 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const BREADCRUMB_PATH = join(homedir(), ".claude", "scripts", ".tfx-pkg-root");
7
+
8
+ function normalizeCandidate(candidate) {
9
+ if (typeof candidate !== "string") return null;
10
+ const value = candidate.trim();
11
+ if (!value) return null;
12
+ return value;
13
+ }
14
+
15
+ function isValidPluginRoot(candidate) {
16
+ const root = normalizeCandidate(candidate);
17
+ if (!root) return false;
18
+ return existsSync(join(root, "hooks", "hook-orchestrator.mjs"));
19
+ }
20
+
21
+ function toPluginRootFromUrl(url) {
22
+ if (typeof url !== "string" || !url) return null;
23
+ try {
24
+ const sourceDir = dirname(fileURLToPath(url));
25
+ const hookScoped = sourceDir.match(/^(.*?)[\\/]hooks(?:[\\/].*)?$/);
26
+ if (hookScoped?.[1]) return hookScoped[1];
27
+ return resolve(sourceDir, "..");
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ function readBreadcrumbRoot() {
34
+ if (!existsSync(BREADCRUMB_PATH)) return null;
35
+ try {
36
+ return normalizeCandidate(readFileSync(BREADCRUMB_PATH, "utf8"));
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ export function resolvePluginRoot(callerUrl) {
43
+ const breadcrumbRoot = readBreadcrumbRoot();
44
+ if (isValidPluginRoot(breadcrumbRoot)) return breadcrumbRoot;
45
+
46
+ const envRoot = normalizeCandidate(process.env.CLAUDE_PLUGIN_ROOT);
47
+ if (isValidPluginRoot(envRoot)) return envRoot;
48
+
49
+ const callerFallback = toPluginRootFromUrl(callerUrl);
50
+ if (isValidPluginRoot(callerFallback)) return callerFallback;
51
+
52
+ const moduleFallback = toPluginRootFromUrl(import.meta.url) || process.cwd();
53
+ process.stderr.write(
54
+ `[resolve-root] warning: failed to resolve plugin root from breadcrumb/env/caller; fallback=${moduleFallback}\n`
55
+ );
56
+ return moduleFallback;
57
+ }
58
+
59
+ export const PLUGIN_ROOT = resolvePluginRoot(import.meta.url);