@rubytech/create-maxy-code 0.1.131 → 0.1.138

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 (128) hide show
  1. package/dist/__tests__/installer-specialist-registration.test.js +5 -40
  2. package/dist/index.js +24 -28
  3. package/dist/specialist-registration.js +0 -15
  4. package/package.json +1 -1
  5. package/payload/platform/lib/graph-search/dist/boosts.d.ts +39 -0
  6. package/payload/platform/lib/graph-search/dist/boosts.d.ts.map +1 -0
  7. package/payload/platform/lib/graph-search/dist/boosts.js +57 -0
  8. package/payload/platform/lib/graph-search/dist/boosts.js.map +1 -0
  9. package/payload/platform/lib/graph-search/dist/dedup.d.ts +29 -0
  10. package/payload/platform/lib/graph-search/dist/dedup.d.ts.map +1 -0
  11. package/payload/platform/lib/graph-search/dist/dedup.js +97 -0
  12. package/payload/platform/lib/graph-search/dist/dedup.js.map +1 -0
  13. package/payload/platform/lib/graph-search/dist/index.d.ts +70 -8
  14. package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
  15. package/payload/platform/lib/graph-search/dist/index.js +323 -62
  16. package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
  17. package/payload/platform/lib/graph-search/dist/query-expansion.d.ts +37 -0
  18. package/payload/platform/lib/graph-search/dist/query-expansion.d.ts.map +1 -0
  19. package/payload/platform/lib/graph-search/dist/query-expansion.js +101 -0
  20. package/payload/platform/lib/graph-search/dist/query-expansion.js.map +1 -0
  21. package/payload/platform/lib/graph-search/dist/route.d.ts +29 -0
  22. package/payload/platform/lib/graph-search/dist/route.d.ts.map +1 -0
  23. package/payload/platform/lib/graph-search/dist/route.js +53 -0
  24. package/payload/platform/lib/graph-search/dist/route.js.map +1 -0
  25. package/payload/platform/lib/graph-search/dist/rrf-fusion.d.ts +31 -0
  26. package/payload/platform/lib/graph-search/dist/rrf-fusion.d.ts.map +1 -0
  27. package/payload/platform/lib/graph-search/dist/rrf-fusion.js +57 -0
  28. package/payload/platform/lib/graph-search/dist/rrf-fusion.js.map +1 -0
  29. package/payload/platform/lib/graph-search/src/__tests__/boosts.test.ts +59 -0
  30. package/payload/platform/lib/graph-search/src/__tests__/dedup.test.ts +83 -0
  31. package/payload/platform/lib/graph-search/src/__tests__/query-expansion.test.ts +96 -0
  32. package/payload/platform/lib/graph-search/src/__tests__/route.test.ts +62 -0
  33. package/payload/platform/lib/graph-search/src/__tests__/rrf-fusion.test.ts +39 -0
  34. package/payload/platform/lib/graph-search/src/boosts.ts +61 -0
  35. package/payload/platform/lib/graph-search/src/dedup.ts +108 -0
  36. package/payload/platform/lib/graph-search/src/index.ts +407 -65
  37. package/payload/platform/lib/graph-search/src/query-expansion.ts +129 -0
  38. package/payload/platform/lib/graph-search/src/route.ts +70 -0
  39. package/payload/platform/lib/graph-search/src/rrf-fusion.ts +62 -0
  40. package/payload/platform/neo4j/schema.cypher +80 -1
  41. package/payload/platform/plugins/admin/hooks/session-end-retrospective.sh +4 -2
  42. package/payload/platform/plugins/admin/mcp/dist/index.js +6 -4
  43. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  44. package/payload/platform/plugins/docs/references/internals.md +36 -10
  45. package/payload/platform/plugins/docs/references/neo4j.md +63 -0
  46. package/payload/platform/plugins/docs/references/session-retrospective.md +11 -4
  47. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js +2 -2
  48. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js.map +1 -1
  49. package/payload/platform/plugins/memory/mcp/dist/index.js +114 -5
  50. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  51. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.d.ts +2 -0
  52. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.d.ts.map +1 -0
  53. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.js +25 -0
  54. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.js.map +1 -0
  55. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.d.ts +2 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.d.ts.map +1 -0
  57. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js +70 -0
  58. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js.map +1 -0
  59. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.d.ts +2 -0
  60. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.d.ts.map +1 -0
  61. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +50 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -0
  63. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts +33 -0
  64. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts.map +1 -0
  65. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js +99 -0
  66. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js.map +1 -0
  67. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts +21 -0
  68. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts.map +1 -1
  69. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js +24 -0
  70. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js.map +1 -1
  71. package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts +13 -0
  72. package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts.map +1 -0
  73. package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js +151 -0
  74. package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js.map +1 -0
  75. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts +55 -0
  76. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts.map +1 -0
  77. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js +290 -0
  78. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js.map +1 -0
  79. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts +19 -0
  80. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -0
  81. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +102 -0
  82. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js.map +1 -0
  83. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js +18 -1
  84. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js.map +1 -1
  85. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js +5 -0
  86. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js.map +1 -1
  87. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js +2 -0
  88. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js.map +1 -1
  89. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -1
  90. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +14 -0
  91. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -1
  92. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +29 -0
  93. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts.map +1 -0
  94. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +22 -0
  95. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js.map +1 -0
  96. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts +31 -1
  97. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
  98. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +182 -50
  99. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
  100. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  101. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +76 -1
  102. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  103. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts +30 -0
  104. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map +1 -0
  105. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js +129 -0
  106. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map +1 -0
  107. package/payload/platform/plugins/memory/mcp/scripts/backfill-typed-edges.ts +72 -0
  108. package/payload/platform/plugins/memory/mcp/scripts/generate-edge-docs.ts +75 -0
  109. package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -0
  110. package/payload/platform/plugins/memory/references/schema-base.md +44 -0
  111. package/payload/platform/scripts/seed-neo4j.sh +49 -0
  112. package/payload/platform/templates/agents/admin/IDENTITY.md +4 -3
  113. package/payload/platform/templates/specialists/agents/database-operator.md +9 -1
  114. package/payload/server/{adminuser-self-heal-QAWOZ3JV.js → adminuser-self-heal-YC47O34W.js} +2 -1
  115. package/payload/server/{chunk-JSBRDJBE.js → chunk-HYQNUVGO.js} +9 -1
  116. package/payload/server/{chunk-HCYM5FLU.js → chunk-T3DJD5QR.js} +3 -1
  117. package/payload/server/maxy-edge.js +3 -2
  118. package/payload/server/public/assets/{admin-FcRHAL-3.js → admin-BM9aXUUh.js} +1 -1
  119. package/payload/server/public/assets/{data-Ds37mflX.js → data-Ds2ECtfP.js} +1 -1
  120. package/payload/server/public/assets/{graph-CmWRhaiS.js → graph-BzxObKMM.js} +1 -1
  121. package/payload/server/public/assets/{graph-labels-Ch2r00Gt.js → graph-labels-Dwgep_nr.js} +1 -1
  122. package/payload/server/public/assets/{page-BcHhJXUt.js → page-BlkBsjP2.js} +1 -1
  123. package/payload/server/public/assets/page-h_SA5SVQ.js +51 -0
  124. package/payload/server/public/data.html +3 -3
  125. package/payload/server/public/graph.html +3 -3
  126. package/payload/server/public/index.html +4 -4
  127. package/payload/server/server.js +803 -76
  128. package/payload/server/public/assets/page-BOtNny_4.js +0 -51
@@ -7,7 +7,7 @@ import assert from "node:assert/strict";
7
7
  import { mkdtempSync, mkdirSync, writeFileSync, readlinkSync, lstatSync, rmSync, symlinkSync, readdirSync } from "node:fs";
8
8
  import { tmpdir } from "node:os";
9
9
  import { join, resolve } from "node:path";
10
- import { registerSpecialistAgentsAt, registerPremiumBundleAgents, SpecialistSymlinkCollision } from "../specialist-registration.js";
10
+ import { registerSpecialistAgentsAt, SpecialistSymlinkCollision } from "../specialist-registration.js";
11
11
  function makeTemplates(dir, names) {
12
12
  mkdirSync(dir, { recursive: true });
13
13
  for (const n of names)
@@ -90,45 +90,10 @@ test("skips with reason=no-templates-dir when templates dir is absent", () => {
90
90
  rmSync(agentsDir, { recursive: true, force: true });
91
91
  }
92
92
  });
93
- // Task 349 — premium-bundle agents must be symlinked into the same
94
- // $CLAUDE_CONFIG_DIR/agents/ as base specialists. Pre-349, the installer
95
- // shipped premium-plugins/<bundle>/agents/*.md files in the payload but never
96
- // wired them in, so `--agent listing-curator` (and the rest of the realagent
97
- // quartet) threw `specialist-agent-file-missing` at spawn time.
98
- test("premium bundle: symlinks each agent in the bundle's agents/ into agentsDir", () => {
99
- const installDir = mkdtempSync(join(tmpdir(), "t349-install-"));
100
- const agentsDir = mkdtempSync(join(tmpdir(), "t349-agents-"));
101
- try {
102
- const bundleAgentsDir = join(installDir, "premium-plugins", "real-agent", "agents");
103
- makeTemplates(bundleAgentsDir, ["listing-curator", "negotiator", "valuer", "compliance"]);
104
- const result = registerPremiumBundleAgents(installDir, ["real-agent"], agentsDir);
105
- assert.deepEqual(result.bundles, ["real-agent"]);
106
- for (const n of ["listing-curator", "negotiator", "valuer", "compliance"]) {
107
- const linkPath = join(agentsDir, `${n}.md`);
108
- const stat = lstatSync(linkPath);
109
- assert.ok(stat.isSymbolicLink(), `${n}.md must be a symlink`);
110
- const target = resolve(agentsDir, readlinkSync(linkPath));
111
- assert.equal(target, resolve(bundleAgentsDir, `${n}.md`));
112
- }
113
- }
114
- finally {
115
- rmSync(installDir, { recursive: true, force: true });
116
- rmSync(agentsDir, { recursive: true, force: true });
117
- }
118
- });
119
- test("premium bundle: empty bundle list is a no-op — no error, no writes", () => {
120
- const installDir = mkdtempSync(join(tmpdir(), "t349-install-"));
121
- const agentsDir = mkdtempSync(join(tmpdir(), "t349-agents-"));
122
- try {
123
- const result = registerPremiumBundleAgents(installDir, [], agentsDir);
124
- assert.deepEqual(result.bundles, []);
125
- assert.deepEqual(readdirSync(agentsDir), []);
126
- }
127
- finally {
128
- rmSync(installDir, { recursive: true, force: true });
129
- rmSync(agentsDir, { recursive: true, force: true });
130
- }
131
- });
93
+ // Task 394 — premium-bundle agents previously registered via this same
94
+ // symlink path; they now register through the native Claude Code plugin
95
+ // manifest at bundle time. The two `premium bundle:` tests that asserted
96
+ // the symlink-channel behaviour were deleted with `registerPremiumBundleAgents`.
132
97
  test("collides loudly on a regular file at the destination (no silent overwrite)", () => {
133
98
  const templatesDir = mkdtempSync(join(tmpdir(), "t165-tpl-"));
134
99
  const agentsDir = mkdtempSync(join(tmpdir(), "t165-agents-"));
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { execFileSync, spawn, spawnSync } from "node:child_process";
3
3
  import { existsSync, mkdirSync, writeFileSync, cpSync, readFileSync, rmSync, readdirSync, appendFileSync, openSync, closeSync, chmodSync, statSync, realpathSync } from "node:fs";
4
- import { registerSpecialistAgentsAt, registerPremiumBundleAgents, SpecialistSymlinkCollision } from "./specialist-registration.js";
4
+ import { registerSpecialistAgentsAt, SpecialistSymlinkCollision } from "./specialist-registration.js";
5
5
  import { resolve, join, dirname } from "node:path";
6
6
  import { randomBytes } from "node:crypto";
7
7
  import { resolveInstallPortFromFs, buildMaxyUnitFile, buildClaudeSessionManagerUnitFile, buildClaudePtysSliceUnitFile } from "./port-resolution.js";
@@ -2463,15 +2463,16 @@ function writeEntitlementFromFlag(payload) {
2463
2463
  console.log(line);
2464
2464
  logFile(line);
2465
2465
  }
2466
+ // Task 394 — premium-bundle agents register through the native Claude Code
2467
+ // subagent registry via `.claude-plugin/plugin.json` manifests emitted by
2468
+ // scripts/generate-plugin-manifests.mjs at bundle time, not through a
2469
+ // parallel symlink channel. This function still wires the bundled platform
2470
+ // specialists (database-operator, project-manager, …) under
2471
+ // platform/templates/specialists/agents/, which are not part of a
2472
+ // registered plugin and rely on $CLAUDE_CONFIG_DIR/agents/ resolution.
2466
2473
  function registerSpecialistAgents() {
2467
2474
  try {
2468
2475
  registerSpecialistAgentsAt(join(INSTALL_DIR, "platform/templates/specialists/agents"), join(CLAUDE_CONFIG_DIR, "agents"), (line) => { console.log(line); logFile(line); });
2469
- // Task 349 — symlink each premium-bundle's agents/ into the same
2470
- // $CLAUDE_CONFIG_DIR/agents/ so `--agent <name>` resolves for premium
2471
- // specialists (e.g. realagent's listing-curator, negotiator, valuer,
2472
- // compliance). The pty-spawner reads exactly one agents dir; the bundle
2473
- // payload alone is insufficient without the symlink step here.
2474
- registerPremiumBundleAgents(INSTALL_DIR, resolvePremiumBundles(), join(CLAUDE_CONFIG_DIR, "agents"), (line) => { console.log(line); logFile(line); });
2475
2476
  }
2476
2477
  catch (err) {
2477
2478
  if (err instanceof SpecialistSymlinkCollision) {
@@ -2482,23 +2483,6 @@ function registerSpecialistAgents() {
2482
2483
  throw err;
2483
2484
  }
2484
2485
  }
2485
- // Resolve the brand's premium-bundle list for installer-side iteration.
2486
- // `true` widens to every directory under <INSTALL_DIR>/premium-plugins/;
2487
- // an explicit array is used verbatim; absent/false → no premium bundles.
2488
- function resolvePremiumBundles() {
2489
- const ships = BRAND.shipsPremiumBundles;
2490
- if (Array.isArray(ships))
2491
- return ships;
2492
- if (ships === true) {
2493
- const root = join(INSTALL_DIR, "premium-plugins");
2494
- if (!existsSync(root))
2495
- return [];
2496
- return readdirSync(root, { withFileTypes: true })
2497
- .filter((d) => d.isDirectory())
2498
- .map((d) => d.name);
2499
- }
2500
- return [];
2501
- }
2502
2486
  // ---------------------------------------------------------------------------
2503
2487
  // Account discovery (shared by installService + the post-install summary)
2504
2488
  //
@@ -2567,9 +2551,12 @@ function installServiceDarwin() {
2567
2551
  // Mirror the Linux brand systemd unit's `Environment=` lines into .env.
2568
2552
  // Linux stamps every var explicitly on the unit; darwin has no systemd
2569
2553
  // EnvironmentFile primitive, so the wrapper sources .env before exec'ing
2570
- // node. Any var the boot path requires (PORT, MAXY_UI_INTERNAL_PORT,
2571
- // ACCOUNT_ID, MAXY_PLATFORM_ROOT, NEO4J_URI, CLAUDE_CONFIG_DIR, NODE_ENV)
2572
- // must be written here or the server hard-fails before binding the listener.
2554
+ // node. Any var the boot path or per-request handlers require (PORT,
2555
+ // MAXY_UI_INTERNAL_PORT, CLAUDE_SESSION_MANAGER_PORT, ACCOUNT_ID,
2556
+ // MAXY_PLATFORM_ROOT, NEO4J_URI, CLAUDE_CONFIG_DIR, NODE_ENV) must be
2557
+ // written here or the server hard-fails — boot-throw for module-init
2558
+ // consumers, per-request 500s for handlers like /api/admin/claude-sessions*
2559
+ // that requirePortEnv lazily on the manager port.
2573
2560
  // VNC/X11 vars (DISPLAY, RFB_PORT, WEBSOCKIFY_PORT, CDP_PORT) are Pi-only
2574
2561
  // and excluded — darwin runs no kiosk display stack.
2575
2562
  //
@@ -2604,11 +2591,20 @@ function installServiceDarwin() {
2604
2591
  ["NEO4J_URI", `bolt://localhost:${NEO4J_PORT}`],
2605
2592
  ["PORT", String(PORT)],
2606
2593
  ["MAXY_UI_INTERNAL_PORT", String(PORT)],
2594
+ ["CLAUDE_SESSION_MANAGER_PORT", String(BRAND.claudeSessionManagerPort)],
2607
2595
  ["ACCOUNT_ID", installAccountId],
2608
2596
  ["MAXY_PLATFORM_ROOT", `${INSTALL_DIR}/platform`],
2609
2597
  ["CLAUDE_CONFIG_DIR", `${persistDir}/.claude`],
2610
2598
  ["HOSTNAME", "0.0.0.0"],
2611
2599
  ["KEEP_ALIVE_TIMEOUT", "61000"],
2600
+ // launchd hands the server a bare PATH (/usr/bin:/bin:/usr/sbin:/sbin)
2601
+ // that excludes the two Homebrew prefixes the `claude` binary lives in
2602
+ // under Apple Silicon and Intel. The wrapper sources this file with
2603
+ // `set -a; . "$envPath"; set +a`, so `$PATH` expands at source-time
2604
+ // against the bare launchd PATH and Homebrew prefixes get prepended for
2605
+ // every spawned descendant (claude-session-manager, MCP plugin spawns,
2606
+ // route-level spawn('claude', ...)). Task 386.
2607
+ ["PATH", "/opt/homebrew/bin:/usr/local/bin:$PATH"],
2612
2608
  ]) {
2613
2609
  const re = new RegExp(`^${key}=.*$`, "m");
2614
2610
  if (re.test(envContent)) {
@@ -2619,7 +2615,7 @@ function installServiceDarwin() {
2619
2615
  }
2620
2616
  }
2621
2617
  writeFileSync(envPath, envContent);
2622
- logFile(` .env: DISPLAY_MODE=${DISPLAY_MODE}, EMBED_MODEL=${EMBED_MODEL}, EMBED_DIMENSIONS=${EMBED_DIMS}, NEO4J_URI=bolt://localhost:${NEO4J_PORT}, PORT=${PORT}, MAXY_UI_INTERNAL_PORT=${PORT} (darwin-collapsed), HOSTNAME=0.0.0.0, ACCOUNT_ID=${installAccountId.slice(0, 8)}…, CLAUDE_CONFIG_DIR=${persistDir}/.claude`);
2618
+ logFile(` .env: DISPLAY_MODE=${DISPLAY_MODE}, EMBED_MODEL=${EMBED_MODEL}, EMBED_DIMENSIONS=${EMBED_DIMS}, NEO4J_URI=bolt://localhost:${NEO4J_PORT}, PORT=${PORT}, MAXY_UI_INTERNAL_PORT=${PORT} (darwin-collapsed), CLAUDE_SESSION_MANAGER_PORT=${BRAND.claudeSessionManagerPort}, HOSTNAME=0.0.0.0, ACCOUNT_ID=${installAccountId.slice(0, 8)}…, CLAUDE_CONFIG_DIR=${persistDir}/.claude`);
2623
2619
  }
2624
2620
  catch (err) {
2625
2621
  console.error(` WARNING: failed to write .env to ${envPath}: ${err instanceof Error ? err.message : String(err)}`);
@@ -47,21 +47,6 @@ export function registerSpecialistAgentsAt(templatesDir, agentsDir, log = () =>
47
47
  }
48
48
  return { registered, skipped: null };
49
49
  }
50
- // Task 349 — premium-bundle specialist symlinking. The installer wires each
51
- // brand-shipped premium bundle's `agents/` directory into the same
52
- // $CLAUDE_CONFIG_DIR/agents/ so `--agent <name>` resolves for premium
53
- // specialists (e.g. realagent's listing-curator). `bundles` is the resolved
54
- // list of bundle directory names; an empty list is a no-op (no error, no
55
- // writes). Missing per-bundle `agents/` dirs are tolerated via
56
- // registerSpecialistAgentsAt's existing no-templates-dir skip.
57
- export function registerPremiumBundleAgents(installDir, bundles, agentsDir, log = () => { }) {
58
- const registered = [];
59
- for (const bundle of bundles) {
60
- registerSpecialistAgentsAt(join(installDir, "premium-plugins", bundle, "agents"), agentsDir, log);
61
- registered.push(bundle);
62
- }
63
- return { bundles: registered };
64
- }
65
50
  export function replaceSpecialistSymlink(linkPath, targetAbs) {
66
51
  let lstat = null;
67
52
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.131",
3
+ "version": "0.1.138",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Score boosts (Task 308).
3
+ *
4
+ * Two post-fetch score adjustments that re-rank hybrid hits using
5
+ * properties that the typed-edge hook (Task 305) and compiled-truth
6
+ * migration (Task 306) populate. Both are no-ops on the current corpus
7
+ * until those tasks land — the boost reads a missing property as zero
8
+ * and adds nothing.
9
+ *
10
+ * Boosts are pure functions over the scored hit array. They never query
11
+ * Neo4j; the data they read is already on each hit's `properties` map.
12
+ */
13
+ export interface ScoredHit {
14
+ nodeId: string;
15
+ properties: Record<string, unknown>;
16
+ score: number;
17
+ }
18
+ /**
19
+ * Add a flat fractional bump to any hit whose node carries a non-null
20
+ * `compiledTruth` property. Authoritative summaries beat noisy timeline
21
+ * fragments — this puts compiled rows ahead when the score is close.
22
+ *
23
+ * Default weight 0.15 (+15%); the brief reserves room to tune.
24
+ */
25
+ export declare function applyCompiledTruthBoost<T extends ScoredHit>(hits: T[], weight?: number): T[];
26
+ /**
27
+ * Log-weighted boost scaled by inbound MENTIONS edge count.
28
+ *
29
+ * Formula: `bump = clamp(0.05 + 0.05 * log10(count), 0.05, 0.25)` when
30
+ * count > 0; zero otherwise. So 1 backlink → +5%, 10 → +10%, 100 → +15%,
31
+ * 1000 → +20%, 10000+ → capped at +25%. Backlink-rich nodes (the
32
+ * graph's natural authorities) surface higher.
33
+ *
34
+ * Reads from `hit.properties.backlinkCount` — a property Task 305's
35
+ * post-write hook maintains. Until 305 lands the count is absent and
36
+ * the boost is identity.
37
+ */
38
+ export declare function applyBacklinkBoost<T extends ScoredHit>(hits: T[]): T[];
39
+ //# sourceMappingURL=boosts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boosts.d.ts","sourceRoot":"","sources":["../src/boosts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,SAAS,EACzD,IAAI,EAAE,CAAC,EAAE,EACT,MAAM,SAAO,GACZ,CAAC,EAAE,CASL;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAQtE"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ * Score boosts (Task 308).
4
+ *
5
+ * Two post-fetch score adjustments that re-rank hybrid hits using
6
+ * properties that the typed-edge hook (Task 305) and compiled-truth
7
+ * migration (Task 306) populate. Both are no-ops on the current corpus
8
+ * until those tasks land — the boost reads a missing property as zero
9
+ * and adds nothing.
10
+ *
11
+ * Boosts are pure functions over the scored hit array. They never query
12
+ * Neo4j; the data they read is already on each hit's `properties` map.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.applyCompiledTruthBoost = applyCompiledTruthBoost;
16
+ exports.applyBacklinkBoost = applyBacklinkBoost;
17
+ /**
18
+ * Add a flat fractional bump to any hit whose node carries a non-null
19
+ * `compiledTruth` property. Authoritative summaries beat noisy timeline
20
+ * fragments — this puts compiled rows ahead when the score is close.
21
+ *
22
+ * Default weight 0.15 (+15%); the brief reserves room to tune.
23
+ */
24
+ function applyCompiledTruthBoost(hits, weight = 0.15) {
25
+ if (weight < 0 || weight > 1) {
26
+ throw new Error(`applyCompiledTruthBoost: weight must be in [0,1], got ${weight}`);
27
+ }
28
+ return hits.map((hit) => {
29
+ const compiled = hit.properties["compiledTruth"];
30
+ if (compiled === null || compiled === undefined || compiled === "")
31
+ return hit;
32
+ return { ...hit, score: hit.score * (1 + weight) };
33
+ });
34
+ }
35
+ /**
36
+ * Log-weighted boost scaled by inbound MENTIONS edge count.
37
+ *
38
+ * Formula: `bump = clamp(0.05 + 0.05 * log10(count), 0.05, 0.25)` when
39
+ * count > 0; zero otherwise. So 1 backlink → +5%, 10 → +10%, 100 → +15%,
40
+ * 1000 → +20%, 10000+ → capped at +25%. Backlink-rich nodes (the
41
+ * graph's natural authorities) surface higher.
42
+ *
43
+ * Reads from `hit.properties.backlinkCount` — a property Task 305's
44
+ * post-write hook maintains. Until 305 lands the count is absent and
45
+ * the boost is identity.
46
+ */
47
+ function applyBacklinkBoost(hits) {
48
+ return hits.map((hit) => {
49
+ const raw = hit.properties["backlinkCount"];
50
+ const count = typeof raw === "number" ? raw : Number(raw);
51
+ if (!Number.isFinite(count) || count <= 0)
52
+ return hit;
53
+ const bump = Math.max(0.05, Math.min(0.25, 0.05 + 0.05 * Math.log10(count)));
54
+ return { ...hit, score: hit.score * (1 + bump) };
55
+ });
56
+ }
57
+ //# sourceMappingURL=boosts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boosts.js","sourceRoot":"","sources":["../src/boosts.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;AAeH,0DAYC;AAcD,gDAQC;AAzCD;;;;;;GAMG;AACH,SAAgB,uBAAuB,CACrC,IAAS,EACT,MAAM,GAAG,IAAI;IAEb,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,yDAAyD,MAAM,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACtB,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;QACjD,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE;YAAE,OAAO,GAAG,CAAC;QAC/E,OAAO,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,kBAAkB,CAAsB,IAAS;IAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACtB,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;YAAE,OAAO,GAAG,CAAC;QACtD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7E,OAAO,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * 4-layer dedup (Task 308).
3
+ *
4
+ * Strict superset of the previous nodeId-only dedup. Catches duplicate
5
+ * representations the typed-edge hook (Task 305) and document-ingest
6
+ * pipeline will inevitably create: slug variants, canonical-name
7
+ * variants, and content-hash duplicates.
8
+ *
9
+ * Order: hits are sorted by descending score FIRST, then walked. The
10
+ * highest-score representative wins on every layer; later entries with
11
+ * the same key on any active layer are dropped. Missing values on a
12
+ * layer (no `slug`, no `canonicalName`, no derivable hash) are not
13
+ * collisions — they simply skip that layer for that row.
14
+ */
15
+ export type DedupLayer = "nodeId" | "slug" | "canonicalName" | "contentHash";
16
+ export declare const DEFAULT_LAYERS: DedupLayer[];
17
+ interface DedupHit {
18
+ nodeId: string;
19
+ properties: Record<string, unknown>;
20
+ score: number;
21
+ }
22
+ /**
23
+ * Dedup `hits` across the requested layers. Returns a new array with the
24
+ * highest-scoring representative per collision class, in original
25
+ * descending-score order.
26
+ */
27
+ export declare function dedup<T extends DedupHit>(hits: T[], layers?: DedupLayer[]): T[];
28
+ export {};
29
+ //# sourceMappingURL=dedup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedup.d.ts","sourceRoot":"","sources":["../src/dedup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,eAAe,GAAG,aAAa,CAAC;AAE7E,eAAO,MAAM,cAAc,EAAE,UAAU,EAKtC,CAAC;AAEF,UAAU,QAAQ;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;CACf;AA6BD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,QAAQ,EACtC,IAAI,EAAE,CAAC,EAAE,EACT,MAAM,GAAE,UAAU,EAAmB,GACpC,CAAC,EAAE,CAwCL"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ /**
3
+ * 4-layer dedup (Task 308).
4
+ *
5
+ * Strict superset of the previous nodeId-only dedup. Catches duplicate
6
+ * representations the typed-edge hook (Task 305) and document-ingest
7
+ * pipeline will inevitably create: slug variants, canonical-name
8
+ * variants, and content-hash duplicates.
9
+ *
10
+ * Order: hits are sorted by descending score FIRST, then walked. The
11
+ * highest-score representative wins on every layer; later entries with
12
+ * the same key on any active layer are dropped. Missing values on a
13
+ * layer (no `slug`, no `canonicalName`, no derivable hash) are not
14
+ * collisions — they simply skip that layer for that row.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.DEFAULT_LAYERS = void 0;
18
+ exports.dedup = dedup;
19
+ const node_crypto_1 = require("node:crypto");
20
+ exports.DEFAULT_LAYERS = [
21
+ "nodeId",
22
+ "slug",
23
+ "canonicalName",
24
+ "contentHash",
25
+ ];
26
+ function readSlug(props) {
27
+ const slug = props["slug"];
28
+ return typeof slug === "string" && slug.length > 0 ? slug : null;
29
+ }
30
+ function readCanonicalName(props) {
31
+ const canonical = props["canonicalName"];
32
+ if (typeof canonical === "string" && canonical.length > 0) {
33
+ return canonical.toLowerCase();
34
+ }
35
+ const name = props["name"];
36
+ if (typeof name === "string" && name.length > 0) {
37
+ return name.toLowerCase();
38
+ }
39
+ return null;
40
+ }
41
+ function readContentHash(props) {
42
+ const compiled = props["compiledTruth"];
43
+ const content = props["content"];
44
+ const source = (typeof compiled === "string" && compiled.length > 0)
45
+ ? compiled
46
+ : (typeof content === "string" && content.length > 0 ? content : null);
47
+ if (source === null)
48
+ return null;
49
+ return (0, node_crypto_1.createHash)("sha256").update(source).digest("hex").slice(0, 16);
50
+ }
51
+ /**
52
+ * Dedup `hits` across the requested layers. Returns a new array with the
53
+ * highest-scoring representative per collision class, in original
54
+ * descending-score order.
55
+ */
56
+ function dedup(hits, layers = exports.DEFAULT_LAYERS) {
57
+ const sorted = [...hits].sort((a, b) => b.score - a.score);
58
+ const seen = new Map();
59
+ for (const layer of layers)
60
+ seen.set(layer, new Set());
61
+ const out = [];
62
+ for (const hit of sorted) {
63
+ let collision = false;
64
+ const keys = [];
65
+ for (const layer of layers) {
66
+ let key;
67
+ switch (layer) {
68
+ case "nodeId":
69
+ key = hit.nodeId;
70
+ break;
71
+ case "slug":
72
+ key = readSlug(hit.properties);
73
+ break;
74
+ case "canonicalName":
75
+ key = readCanonicalName(hit.properties);
76
+ break;
77
+ case "contentHash":
78
+ key = readContentHash(hit.properties);
79
+ break;
80
+ }
81
+ if (key === null)
82
+ continue;
83
+ if (seen.get(layer).has(key)) {
84
+ collision = true;
85
+ break;
86
+ }
87
+ keys.push([layer, key]);
88
+ }
89
+ if (collision)
90
+ continue;
91
+ for (const [layer, key] of keys)
92
+ seen.get(layer).add(key);
93
+ out.push(hit);
94
+ }
95
+ return out;
96
+ }
97
+ //# sourceMappingURL=dedup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedup.js","sourceRoot":"","sources":["../src/dedup.ts"],"names":[],"mappings":"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"}
@@ -39,6 +39,10 @@
39
39
  * line with the prefix and fields it wants.
40
40
  */
41
41
  import { type Session } from "neo4j-driver";
42
+ import { DEFAULT_LAYERS, type DedupLayer } from "./dedup.js";
43
+ import { type RetrievalClass } from "./route.js";
44
+ export type { DedupLayer, RetrievalClass };
45
+ export { DEFAULT_LAYERS };
42
46
  /**
43
47
  * Name of the universal fulltext index. Mirrors the `CREATE FULLTEXT
44
48
  * INDEX` declaration in `platform/neo4j/schema.cypher`. The doctrine test
@@ -99,7 +103,17 @@ export interface ScoredNode {
99
103
  */
100
104
  bm25Hit: boolean;
101
105
  }
102
- export type SearchMode = "hybrid" | "bm25";
106
+ /**
107
+ * `"hybrid"` = weighted-sum fusion path (the baseline; the
108
+ * brief calls this `weighted-sum`).
109
+ * `"rrf"` = RRF fusion path (Task 308, flagged via `MAXY_GS_RRF`).
110
+ * `"bm25"` = degraded path after embed failure with `degradeOnEmbedFailure`.
111
+ *
112
+ * UI callers (e.g. `platform/ui/app/graph/page.tsx`) pattern-match on
113
+ * `"bm25"` to render the Ollama-down banner; non-`"bm25"` values collapse
114
+ * to the normal banner, so adding `"rrf"` is wire-compatible.
115
+ */
116
+ export type SearchMode = "hybrid" | "rrf" | "bm25";
103
117
  export interface Bm25OnlyParams {
104
118
  query: string;
105
119
  accountId?: string;
@@ -144,6 +158,30 @@ export interface HybridParams extends Bm25OnlyParams {
144
158
  * default; the lib itself takes no opinion on the value.
145
159
  */
146
160
  vectorThreshold?: number;
161
+ /**
162
+ * Routing hint from the inbound-gateway classifier (Task 304). When
163
+ * the `MAXY_GS_ROUTE` flag is on, the lib picks a label filter and
164
+ * fusion-weight plan per class. Omitted ⇒ `"general"` (unflagged
165
+ * callers see no change).
166
+ */
167
+ retrievalClass?: RetrievalClass;
168
+ /** Per-call override: enable Reciprocal Rank Fusion. */
169
+ enableRrf?: boolean;
170
+ /** Per-call override: enable Haiku multi-query expansion. */
171
+ enableExpansion?: boolean;
172
+ /** Per-call override: enable compiledTruth + backlink boosts. */
173
+ enableBoosts?: boolean;
174
+ /** Per-call override: enable 4-layer dedup (default layers below). */
175
+ enableDedup?: boolean;
176
+ /** Per-call override: enable retrieval-class routing. */
177
+ enableRoute?: boolean;
178
+ /**
179
+ * Layers passed to the 4-layer dedup when `enableDedup` is true.
180
+ * Defaults to `["nodeId","slug","canonicalName","contentHash"]`.
181
+ */
182
+ dedupLayers?: DedupLayer[];
183
+ /** RRF smoothing constant. Default 60 (gbrain default). */
184
+ rrfK?: number;
147
185
  }
148
186
  export interface HybridResponse {
149
187
  mode: SearchMode;
@@ -178,6 +216,21 @@ export interface HybridResponse {
178
216
  suppressed: number;
179
217
  bm25Bypass: number;
180
218
  threshold: number | null;
219
+ /**
220
+ * Per-stage counts surfaced for the caller's `[graph-search:hybrid]`
221
+ * log line (Task 308). Always present; values reflect whichever
222
+ * enhancements ran on this call. `expansions` is 1 when expansion
223
+ * was off, `boosted - fused` is 0 when boosts were no-ops, etc.
224
+ */
225
+ stageCounts: {
226
+ expansions: number;
227
+ vector: number;
228
+ bm25: number;
229
+ fused: number;
230
+ boosted: number;
231
+ deduped: number;
232
+ final: number;
233
+ };
181
234
  }
182
235
  export type EmbedFn = (text: string) => Promise<number[]>;
183
236
  /**
@@ -207,20 +260,29 @@ export declare function bm25Only(session: Session, params: Bm25OnlyParams): Prom
207
260
  * Hybrid vector + BM25 search with graph expansion.
208
261
  *
209
262
  * Sequence:
210
- * 1. embed(query) may throw if Ollama is unreachable. When
263
+ * 1. Resolve flags + route plan. Route may short-circuit (`retrievalClass=none`).
264
+ * 2. Multi-query expansion (Task 308 flag): Haiku produces 3-5 paraphrases.
265
+ * Each paraphrase is embedded and run through vector+BM25 in parallel.
266
+ * 3. embed(query) — may throw if Ollama is unreachable. When
211
267
  * `degradeOnEmbedFailure=true`, caller receives bm25Only() result
212
268
  * with `mode: "bm25"`; otherwise the throw propagates.
213
- * 2. Vector search per label (one query per vector index discovered at
269
+ * 4. Vector search per label (one query per vector index discovered at
214
270
  * boot). Nodes-by-label filter short-circuits when the requested
215
271
  * labels have no index.
216
- * 3. BM25 search on the universal `entity_search` index —
272
+ * 5. BM25 search on the universal `entity_search` index —
217
273
  * same filter semantics as vector half (scope/agent/keyword/trashed).
218
- * 4. Keyword subscriptions (when set): BM25 per keyword + property
274
+ * 6. Keyword subscriptions (when set): BM25 per keyword + property
219
275
  * lookup against `node.keywords` array. Both bypass the agentSlug
220
276
  * filter — subscriptions are scope-inclusive by design (matches
221
- * memory-search.ts comment L319).
222
- * 5. Merge: 0.7*vector + 0.3*bm25_norm, dedup by nodeId, sort.
223
- * 6. Graph expand (1 hop by default, 0 to skip) — notTrashed filter +
277
+ * memory-search.ts comment L319). Runs once, not per expansion.
278
+ * 7. Fusion: weighted-sum (`vw * vector + bw * bm25_norm`) by default,
279
+ * RRF when `MAXY_GS_RRF` is on. Route plan supplies fusion weights.
280
+ * 8. Boosts (Task 308 flag): compiledTruth bump + backlink log-bump.
281
+ * 9. Dedup (Task 308 flag): 4-layer dedup over the union, default layers
282
+ * `[nodeId, slug, canonicalName, contentHash]`.
283
+ * 10. Threshold + sort + slice. Counts (rawMerged / suppressed / bm25Bypass)
284
+ * reflect the pre-threshold and post-threshold populations.
285
+ * 11. Graph expand (1 hop by default, 0 to skip) — notTrashed filter +
224
286
  * scope + agent clauses mirrored.
225
287
  */
226
288
  export declare function hybrid(session: Session, embed: EmbedFn, params: HybridParams): Promise<HybridResponse>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}