@memoraone/mcp 0.1.44 → 0.1.46

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.
package/dist/cli.cjs CHANGED
@@ -231,7 +231,7 @@ var require_package = __commonJS({
231
231
  "package.json"(exports2, module2) {
232
232
  module2.exports = {
233
233
  name: "@memoraone/mcp",
234
- version: "0.1.44",
234
+ version: "0.1.46",
235
235
  type: "module",
236
236
  main: "dist/index.cjs",
237
237
  exports: {
@@ -497,8 +497,8 @@ async function acquireLocalLock(lockName, options = {}) {
497
497
  while (retries <= maxRetries) {
498
498
  try {
499
499
  try {
500
- const stat4 = await fs3.stat(lockPath);
501
- if (Date.now() - stat4.mtimeMs > maxLockAgeMs) {
500
+ const stat6 = await fs3.stat(lockPath);
501
+ if (Date.now() - stat6.mtimeMs > maxLockAgeMs) {
502
502
  await fs3.unlink(lockPath);
503
503
  }
504
504
  } catch (err) {
@@ -531,7 +531,7 @@ async function acquireLocalLock(lockName, options = {}) {
531
531
  `[memoraone-mcp] Failed to acquire lock ${lockName} after ${maxRetries} retries`
532
532
  );
533
533
  }
534
- await new Promise((resolve40) => setTimeout(resolve40, retryDelayMs));
534
+ await new Promise((resolve43) => setTimeout(resolve43, retryDelayMs));
535
535
  continue;
536
536
  }
537
537
  throw err;
@@ -1092,9 +1092,9 @@ var init_memoraClient = __esm({
1092
1092
  });
1093
1093
 
1094
1094
  // src/localState/localConnectClient.ts
1095
- async function requestJson(baseUrl, method, path44, options = {}) {
1095
+ async function requestJson(baseUrl, method, path47, options = {}) {
1096
1096
  const fetchImpl = options.fetchImpl ?? fetch;
1097
- const url = `${baseUrl.replace(/\/+$/, "")}${path44.startsWith("/") ? path44 : `/${path44}`}`;
1097
+ const url = `${baseUrl.replace(/\/+$/, "")}${path47.startsWith("/") ? path47 : `/${path47}`}`;
1098
1098
  const res = await fetchImpl(url, {
1099
1099
  method,
1100
1100
  headers: {
@@ -1117,6 +1117,85 @@ async function requestJson(baseUrl, method, path44, options = {}) {
1117
1117
  }
1118
1118
  return { status: res.status, statusText: res.statusText, ok: res.ok, json };
1119
1119
  }
1120
+ function requireString(data, key, responseName) {
1121
+ const value = data[key];
1122
+ if (typeof value !== "string" || value.length === 0) {
1123
+ throw new Error(`[memoraone-mcp] Invalid ${responseName} response`);
1124
+ }
1125
+ return value;
1126
+ }
1127
+ function normalizePluginPairingSource(source) {
1128
+ if (source !== "cursor" && source !== "jetbrains") {
1129
+ throw new Error("[memoraone-mcp] Pairing source must be cursor or jetbrains");
1130
+ }
1131
+ return source;
1132
+ }
1133
+ function pairingRepositoryName(workspaceRoot) {
1134
+ const repositoryName = path8.basename(path8.resolve(workspaceRoot)).trim();
1135
+ if (repositoryName.length === 0 || repositoryName.length > 256 || repositoryName.includes("/") || repositoryName.includes("\\")) {
1136
+ throw new Error("[memoraone-mcp] Workspace basename is not safe pairing metadata");
1137
+ }
1138
+ return repositoryName;
1139
+ }
1140
+ async function createPluginPairingRequest(apiUrl, input2, options = {}) {
1141
+ const body = {
1142
+ repository_name: pairingRepositoryName(input2.workspaceRoot),
1143
+ source: normalizePluginPairingSource(input2.source)
1144
+ };
1145
+ const res = await requestJson(apiUrl, "POST", "/v1/local-connect/pairing-requests", {
1146
+ body,
1147
+ fetchImpl: options.fetchImpl
1148
+ });
1149
+ const data = res.json ?? {};
1150
+ return {
1151
+ requestId: requireString(data, "request_id", "pairing create"),
1152
+ pollingVerifier: requireString(data, "polling_verifier", "pairing create"),
1153
+ authorizationUrl: requireString(data, "authorization_url", "pairing create"),
1154
+ expiresAt: requireString(data, "expires_at", "pairing create")
1155
+ };
1156
+ }
1157
+ async function pollPluginPairingRequest(apiUrl, request, options = {}) {
1158
+ const requestId = encodeURIComponent(request.requestId);
1159
+ const res = await requestJson(
1160
+ apiUrl,
1161
+ "POST",
1162
+ `/v1/local-connect/pairing-requests/${requestId}/poll`,
1163
+ {
1164
+ body: { polling_verifier: request.pollingVerifier },
1165
+ fetchImpl: options.fetchImpl
1166
+ }
1167
+ );
1168
+ const data = res.json ?? {};
1169
+ const expiresAt = requireString(data, "expires_at", "pairing poll");
1170
+ if (data.status === "pending") {
1171
+ return { status: "pending", expiresAt };
1172
+ }
1173
+ if (data.status === "ready") {
1174
+ const code = requireString(data, "code", "pairing poll");
1175
+ if (!code.startsWith("mcc_")) {
1176
+ throw new Error("[memoraone-mcp] Invalid pairing poll response");
1177
+ }
1178
+ return { status: "ready", code, expiresAt };
1179
+ }
1180
+ throw new Error("[memoraone-mcp] Invalid pairing poll response");
1181
+ }
1182
+ async function cancelPluginPairingRequest(apiUrl, request, options = {}) {
1183
+ const requestId = encodeURIComponent(request.requestId);
1184
+ const res = await requestJson(
1185
+ apiUrl,
1186
+ "POST",
1187
+ `/v1/local-connect/pairing-requests/${requestId}/cancel`,
1188
+ {
1189
+ body: { polling_verifier: request.pollingVerifier },
1190
+ fetchImpl: options.fetchImpl
1191
+ }
1192
+ );
1193
+ const data = res.json ?? {};
1194
+ if (data.status !== "cancelled") {
1195
+ throw new Error("[memoraone-mcp] Invalid pairing cancel response");
1196
+ }
1197
+ return { status: "cancelled" };
1198
+ }
1120
1199
  function assertSafeRedeemBody(body) {
1121
1200
  if ("canonical_root" in body) {
1122
1201
  throw new Error("[memoraone-mcp] redeem body must not include canonical_root");
@@ -1273,6 +1352,30 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
1273
1352
  legacyM1WarningPath
1274
1353
  };
1275
1354
  }
1355
+ async function lookupLocalBindingIdentity(workspaceRoot, options = {}) {
1356
+ const resolved = path9.resolve(workspaceRoot);
1357
+ const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
1358
+ const index = await loadPathIndex(options.homeDir);
1359
+ const lookup = lookupPathIndex(index, resolved, identity);
1360
+ if (lookup.kind !== "path") {
1361
+ return { status: "unbound", workspaceRoot: resolved };
1362
+ }
1363
+ const record = await readBindingRecord(lookup.repositoryBindingId, options.homeDir);
1364
+ if (!record) {
1365
+ return { status: "unbound", workspaceRoot: resolved };
1366
+ }
1367
+ if (path9.resolve(record.workspaceRoot) !== resolved) {
1368
+ return { status: "unbound", workspaceRoot: resolved };
1369
+ }
1370
+ if (!identityMatchesStored(record.filesystemIdentity, identity)) {
1371
+ return { status: "unbound", workspaceRoot: resolved };
1372
+ }
1373
+ return {
1374
+ status: "resolved",
1375
+ workspaceRoot: resolved,
1376
+ repositoryBindingId: record.repositoryBindingId
1377
+ };
1378
+ }
1276
1379
  async function resolveLocalBinding(workspaceRoot, options = {}) {
1277
1380
  const resolved = path9.resolve(workspaceRoot);
1278
1381
  const { repositoryBindingId, legacyM1WarningPath } = await ensureRepositoryBindingForRoot(
@@ -3549,11 +3652,11 @@ var init_repoFingerprint = __esm({
3549
3652
  };
3550
3653
  resolveGitDir = (gitPath) => {
3551
3654
  try {
3552
- const stat4 = fs13.statSync(gitPath);
3553
- if (stat4.isDirectory()) {
3655
+ const stat6 = fs13.statSync(gitPath);
3656
+ if (stat6.isDirectory()) {
3554
3657
  return gitPath;
3555
3658
  }
3556
- if (stat4.isFile()) {
3659
+ if (stat6.isFile()) {
3557
3660
  const content = fs13.readFileSync(gitPath, "utf8");
3558
3661
  const match = content.match(/^gitdir:\s*(.+)$/m);
3559
3662
  if (match) {
@@ -3644,8 +3747,8 @@ function getKnownJetBrainsMcpConfigLocations(homeDir, repoRoot) {
3644
3747
  }
3645
3748
  async function isZeroByteConfigFile(filePath) {
3646
3749
  if (!await pathExists3(filePath)) return false;
3647
- const stat4 = await fs14.stat(filePath);
3648
- return stat4.size === 0;
3750
+ const stat6 = await fs14.stat(filePath);
3751
+ return stat6.size === 0;
3649
3752
  }
3650
3753
  function stripDebugEnvVars(env2) {
3651
3754
  const next = {};
@@ -3847,7 +3950,7 @@ function formatOptionalJetBrainsHandshakeUnavailableDetail(detail) {
3847
3950
  async function verifyJetBrainsMcpHandshake(options) {
3848
3951
  const timeoutMs = options.timeoutMs ?? 15e3;
3849
3952
  const { server } = options;
3850
- return new Promise((resolve40) => {
3953
+ return new Promise((resolve43) => {
3851
3954
  let settled = false;
3852
3955
  const finish = (ok, detail, optionalUnavailable) => {
3853
3956
  if (settled) return;
@@ -3857,7 +3960,7 @@ async function verifyJetBrainsMcpHandshake(options) {
3857
3960
  child.kill();
3858
3961
  } catch {
3859
3962
  }
3860
- resolve40({ ok, detail, optionalUnavailable });
3963
+ resolve43({ ok, detail, optionalUnavailable });
3861
3964
  };
3862
3965
  const child = (0, import_node_child_process4.spawn)(server.command, [...server.args], {
3863
3966
  env: { ...process.env, ...server.env },
@@ -9528,6 +9631,14 @@ async function findRepoRoot(startDir) {
9528
9631
  function stripLeadingLineComments18(text) {
9529
9632
  return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
9530
9633
  }
9634
+ function memoraUsagePolicy() {
9635
+ return `- For every user question, check whether MemoraOne can provide relevant context before answering. If it can, use MemoraOne first.
9636
+ - Do not silently substitute IDE session history, local files, git history, or model memory when MemoraOne can provide relevant context.
9637
+ - Use **\`memora_status\`** for repository/project connection identity.
9638
+ - Use **\`memora_ask_with_memory\`** for previous questions/answers, conversation/history, decisions, facts, preferences, project context, and questions like "what did I/you say, ask, decide, or do before?"
9639
+ - Skip MemoraOne only when the question is clearly unrelated.
9640
+ - After the interaction: if anything meaningful happened\u2014question, answer, decision, discovery, action, correction, failure, or change\u2014record it back to MemoraOne so the next agent knows about it, especially if MemoraOne was not used before answering. Use **\`memora_post_event\`** and **\`memora_log_change_summary\`** according to their intended semantics; do not write trivial noise.`;
9641
+ }
9531
9642
  function cursorRuleBody() {
9532
9643
  return `${MANAGED_MARKER}
9533
9644
 
@@ -9537,7 +9648,7 @@ This repository uses **MemoraOne** via the MCP server named **memoraone** (repo-
9537
9648
 
9538
9649
  ### Tools
9539
9650
 
9540
- - Before answering questions about **prior decisions**, **remembered facts**, **identity or personal recall**, **preferences**, **repo history**, or **what to do next**, call **\`memora_ask_with_memory\`** so replies stay aligned with MemoraOne memory.
9651
+ ${memoraUsagePolicy()}
9541
9652
  - Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. \`cursor\`), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
9542
9653
  - Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
9543
9654
 
@@ -9555,7 +9666,7 @@ This repo is set up to use **MemoraOne** through MCP where your editor exposes i
9555
9666
 
9556
9667
  ## Behavior
9557
9668
 
9558
- - For questions about **earlier decisions**, **stored facts**, **personal or identity recall**, **preferences**, **project history**, or **recommended next steps**, use MemoraOne memory tools (e.g. **\`memora_ask_with_memory\`**) when available before answering.
9669
+ ${memoraUsagePolicy()}
9559
9670
  - Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. your agent name), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
9560
9671
  - Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
9561
9672
 
@@ -10317,6 +10428,7 @@ async function runSetupIdeFiles(o) {
10317
10428
  }
10318
10429
  const cursorContent = `---
10319
10430
  description: MemoraOne MCP \u2014 IDE agent instructions
10431
+ alwaysApply: true
10320
10432
  ---
10321
10433
 
10322
10434
  ` + cursorRuleBody();
@@ -12618,6 +12730,578 @@ var init_connectCommand = __esm({
12618
12730
  }
12619
12731
  });
12620
12732
 
12733
+ // src/pluginPairing.ts
12734
+ function backendErrorCode(error) {
12735
+ if (!(error instanceof MemoraOneHttpError) || !error.body || typeof error.body !== "object") {
12736
+ return void 0;
12737
+ }
12738
+ const code = error.body.error;
12739
+ return typeof code === "string" ? code : void 0;
12740
+ }
12741
+ function mapPairingError(error) {
12742
+ const code = backendErrorCode(error);
12743
+ if (code === "pairing_rejected") return "rejected";
12744
+ if (code === "pairing_cancelled") return "cancelled";
12745
+ if (code === "pairing_consumed") return "consumed";
12746
+ if (code === "pairing_expired") return "expired";
12747
+ if (code === "invalid_pairing_verifier" || code === "pairing_not_found" || code === "bad_request") {
12748
+ return "invalid";
12749
+ }
12750
+ if (error instanceof MemoraOneHttpError && error.status === 410) return "expired";
12751
+ if (error instanceof MemoraOneHttpError && (error.status === 400 || error.status === 401)) {
12752
+ return "invalid";
12753
+ }
12754
+ return "backend_failure";
12755
+ }
12756
+ async function beginPluginRepositoryPairing(options) {
12757
+ const workspaceRoot = path44.resolve(options.workspaceRoot);
12758
+ const apiUrl = (options.apiUrl ?? config2.apiUrl).replace(/\/+$/, "");
12759
+ if (options.source !== "cursor" && options.source !== "jetbrains") {
12760
+ throw new PluginPairingStartError("invalid");
12761
+ }
12762
+ try {
12763
+ pairingRepositoryName(workspaceRoot);
12764
+ } catch {
12765
+ throw new PluginPairingStartError("invalid");
12766
+ }
12767
+ let request;
12768
+ try {
12769
+ request = await createPluginPairingRequest(
12770
+ apiUrl,
12771
+ { workspaceRoot, source: options.source },
12772
+ { fetchImpl: options.fetchImpl }
12773
+ );
12774
+ } catch (error) {
12775
+ const status = mapPairingError(error);
12776
+ throw new PluginPairingStartError(status === "invalid" ? "invalid" : "backend_failure");
12777
+ }
12778
+ return new PluginRepositoryPairingSession(
12779
+ request,
12780
+ workspaceRoot,
12781
+ options.source,
12782
+ apiUrl,
12783
+ options.fetchImpl,
12784
+ options.connectOptions
12785
+ );
12786
+ }
12787
+ var path44, PluginPairingStartError, PluginRepositoryPairingSession;
12788
+ var init_pluginPairing = __esm({
12789
+ "src/pluginPairing.ts"() {
12790
+ path44 = __toESM(require("path"), 1);
12791
+ init_memoraClient();
12792
+ init_config();
12793
+ init_connectCommand();
12794
+ init_localConnectClient();
12795
+ PluginPairingStartError = class extends Error {
12796
+ constructor(status) {
12797
+ super(
12798
+ status === "invalid" ? "[memoraone-mcp] Invalid plugin pairing request" : "[memoraone-mcp] Plugin pairing request could not be created"
12799
+ );
12800
+ this.status = status;
12801
+ this.name = "PluginPairingStartError";
12802
+ }
12803
+ };
12804
+ PluginRepositoryPairingSession = class {
12805
+ constructor(request, workspaceRoot, source, apiUrl, fetchImpl, connectOptions = {}) {
12806
+ this.workspaceRoot = workspaceRoot;
12807
+ this.source = source;
12808
+ this.apiUrl = apiUrl;
12809
+ this.fetchImpl = fetchImpl;
12810
+ this.connectOptions = connectOptions;
12811
+ this.request = request;
12812
+ }
12813
+ /** Opaque, short-lived request metadata. Cleared when the session becomes terminal. */
12814
+ get requestId() {
12815
+ return this.request?.requestId;
12816
+ }
12817
+ get authorizationUrl() {
12818
+ return this.request?.authorizationUrl;
12819
+ }
12820
+ get expiresAt() {
12821
+ return this.request?.expiresAt;
12822
+ }
12823
+ finish(status) {
12824
+ this.terminalStatus = status;
12825
+ this.request = void 0;
12826
+ }
12827
+ async poll() {
12828
+ if (!this.request) {
12829
+ return {
12830
+ status: this.terminalStatus === "cancelled" ? "cancelled" : "consumed",
12831
+ operation: "poll"
12832
+ };
12833
+ }
12834
+ let polled;
12835
+ try {
12836
+ polled = await pollPluginPairingRequest(this.apiUrl, this.request, {
12837
+ fetchImpl: this.fetchImpl
12838
+ });
12839
+ } catch (error) {
12840
+ const status = mapPairingError(error);
12841
+ if (status !== "backend_failure") this.finish(status);
12842
+ return { status, operation: "poll" };
12843
+ }
12844
+ if (polled.status === "pending") {
12845
+ this.request.expiresAt = polled.expiresAt;
12846
+ return polled;
12847
+ }
12848
+ try {
12849
+ const connected = await runConnectCommand({
12850
+ ...this.connectOptions,
12851
+ code: polled.code,
12852
+ cwd: this.workspaceRoot,
12853
+ apiUrl: this.apiUrl,
12854
+ ideType: this.source,
12855
+ fetchImpl: this.connectOptions.fetchImpl ?? this.fetchImpl,
12856
+ // Plugin pairing configures only the IDE that initiated the pairing.
12857
+ configureIdes: true,
12858
+ setupIdeOptions: {
12859
+ ...this.connectOptions.setupIdeOptions,
12860
+ targets: {
12861
+ cursor: this.source === "cursor",
12862
+ vscode: false,
12863
+ jetbrains: this.source === "jetbrains",
12864
+ claudeCode: false,
12865
+ windsurf: false,
12866
+ opencode: false,
12867
+ codex: false,
12868
+ kiro: false,
12869
+ kiroCli: false,
12870
+ rooCode: false,
12871
+ cline: false,
12872
+ clineCli: false,
12873
+ claudeDesktop: false,
12874
+ zed: false,
12875
+ visualStudio: false,
12876
+ continue: false,
12877
+ copilotCli: false,
12878
+ auggie: false,
12879
+ geminiCli: false,
12880
+ antigravity: false,
12881
+ goose: false,
12882
+ junie: false,
12883
+ xcode: false,
12884
+ copilotJetBrains: false,
12885
+ copilotVisualStudio: false
12886
+ }
12887
+ }
12888
+ });
12889
+ if (connected.exitCode !== 0) {
12890
+ this.finish("backend_failure");
12891
+ return { status: "backend_failure", operation: "connect" };
12892
+ }
12893
+ this.finish("consumed");
12894
+ return {
12895
+ status: "ready",
12896
+ repositoryBindingId: connected.repositoryBindingId
12897
+ };
12898
+ } catch {
12899
+ this.finish("backend_failure");
12900
+ return { status: "backend_failure", operation: "connect" };
12901
+ }
12902
+ }
12903
+ async cancel() {
12904
+ if (!this.request) {
12905
+ const status = this.terminalStatus === "cancelled" ? "cancelled" : "consumed";
12906
+ return status === "cancelled" ? { status } : { status, operation: "cancel" };
12907
+ }
12908
+ try {
12909
+ await cancelPluginPairingRequest(this.apiUrl, this.request, {
12910
+ fetchImpl: this.fetchImpl
12911
+ });
12912
+ this.finish("cancelled");
12913
+ return { status: "cancelled" };
12914
+ } catch (error) {
12915
+ const status = mapPairingError(error);
12916
+ if (status !== "backend_failure") this.finish(status);
12917
+ return status === "cancelled" ? { status } : { status, operation: "cancel" };
12918
+ }
12919
+ }
12920
+ };
12921
+ }
12922
+ });
12923
+
12924
+ // src/pluginPairCommand.ts
12925
+ var pluginPairCommand_exports = {};
12926
+ __export(pluginPairCommand_exports, {
12927
+ DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS: () => DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS,
12928
+ PLUGIN_PAIR_USAGE: () => PLUGIN_PAIR_USAGE,
12929
+ cliPluginPair: () => cliPluginPair,
12930
+ parsePluginPairArgv: () => parsePluginPairArgv
12931
+ });
12932
+ function isPluginPairingSource(value) {
12933
+ return value === "cursor" || value === "jetbrains";
12934
+ }
12935
+ function parsePluginPairArgv(argv) {
12936
+ let workspaceRoot;
12937
+ let source;
12938
+ let apiUrl;
12939
+ for (let i = 0; i < argv.length; i++) {
12940
+ const a = argv[i];
12941
+ if (a === "--workspace-root") {
12942
+ const value = argv[++i];
12943
+ if (!value || value.startsWith("-")) return { error: PLUGIN_PAIR_USAGE };
12944
+ workspaceRoot = value;
12945
+ continue;
12946
+ }
12947
+ if (a.startsWith("--workspace-root=")) {
12948
+ const value = a.slice("--workspace-root=".length);
12949
+ if (!value) return { error: PLUGIN_PAIR_USAGE };
12950
+ workspaceRoot = value;
12951
+ continue;
12952
+ }
12953
+ if (a === "--source") {
12954
+ const value = argv[++i];
12955
+ if (!value || value.startsWith("-")) return { error: PLUGIN_PAIR_USAGE };
12956
+ source = value;
12957
+ continue;
12958
+ }
12959
+ if (a.startsWith("--source=")) {
12960
+ const value = a.slice("--source=".length);
12961
+ if (!value) return { error: PLUGIN_PAIR_USAGE };
12962
+ source = value;
12963
+ continue;
12964
+ }
12965
+ if (a === "--api-url") {
12966
+ const value = argv[++i];
12967
+ if (!value || value.startsWith("-")) return { error: PLUGIN_PAIR_USAGE };
12968
+ apiUrl = value;
12969
+ continue;
12970
+ }
12971
+ if (a.startsWith("--api-url=")) {
12972
+ const value = a.slice("--api-url=".length);
12973
+ if (!value) return { error: PLUGIN_PAIR_USAGE };
12974
+ apiUrl = value;
12975
+ continue;
12976
+ }
12977
+ if (a.startsWith("-")) {
12978
+ return { error: `Unknown plugin-pair option: ${a}` };
12979
+ }
12980
+ return { error: PLUGIN_PAIR_USAGE };
12981
+ }
12982
+ return { workspaceRoot, source, apiUrl };
12983
+ }
12984
+ function writeEvent(event, stdoutWrite) {
12985
+ const line = `${JSON.stringify(event)}
12986
+ `;
12987
+ if (SECRET_OUTPUT_RE.test(line)) {
12988
+ stdoutWrite(`${JSON.stringify({ event: "fatal", message: "Plugin pairing failed" })}
12989
+ `);
12990
+ return;
12991
+ }
12992
+ stdoutWrite(line);
12993
+ }
12994
+ function delay(ms, signal) {
12995
+ if (ms <= 0 || signal?.aborted) return Promise.resolve();
12996
+ return new Promise((resolve43) => {
12997
+ const timer = setTimeout(finish, ms);
12998
+ const onAbort = () => finish();
12999
+ function finish() {
13000
+ clearTimeout(timer);
13001
+ signal?.removeEventListener("abort", onAbort);
13002
+ resolve43();
13003
+ }
13004
+ signal?.addEventListener("abort", onAbort, { once: true });
13005
+ });
13006
+ }
13007
+ async function resolveWorkspaceRoot(workspaceRoot) {
13008
+ const resolved = path45.resolve(workspaceRoot);
13009
+ try {
13010
+ const st = await fs38.stat(resolved);
13011
+ if (!st.isDirectory()) {
13012
+ return { error: "workspace-root must be a directory" };
13013
+ }
13014
+ } catch {
13015
+ return { error: "workspace-root does not exist" };
13016
+ }
13017
+ return resolved;
13018
+ }
13019
+ function isRetryablePollFailure(result) {
13020
+ return result.status === "backend_failure" && result.operation === "poll";
13021
+ }
13022
+ function terminalEventFromPoll(result) {
13023
+ if (result.status === "pending" || result.status === "ready") return void 0;
13024
+ if (result.operation) {
13025
+ return { event: result.status, operation: result.operation };
13026
+ }
13027
+ return { event: result.status };
13028
+ }
13029
+ async function runPluginPairSession(session, options) {
13030
+ const { workspaceRoot, source, pollIntervalMs, signal, stdoutWrite } = options;
13031
+ const requestId = session.requestId;
13032
+ const authorizationUrl = session.authorizationUrl;
13033
+ const expiresAt = session.expiresAt;
13034
+ if (!requestId || !authorizationUrl || !expiresAt) {
13035
+ writeEvent({ event: "fatal", message: "Plugin pairing failed" }, stdoutWrite);
13036
+ return 1;
13037
+ }
13038
+ writeEvent(
13039
+ {
13040
+ event: "started",
13041
+ requestId,
13042
+ authorizationUrl,
13043
+ expiresAt,
13044
+ workspaceRoot,
13045
+ source
13046
+ },
13047
+ stdoutWrite
13048
+ );
13049
+ const cancelAndExit = async () => {
13050
+ const cancelled = await session.cancel();
13051
+ if (cancelled.status === "cancelled") {
13052
+ writeEvent({ event: "cancelled" }, stdoutWrite);
13053
+ } else {
13054
+ writeEvent(
13055
+ { event: cancelled.status, operation: cancelled.operation },
13056
+ stdoutWrite
13057
+ );
13058
+ }
13059
+ return 1;
13060
+ };
13061
+ while (!signal?.aborted) {
13062
+ const result = await session.poll();
13063
+ if (signal?.aborted) {
13064
+ if (result.status === "ready") {
13065
+ writeEvent(
13066
+ {
13067
+ event: "ready",
13068
+ repositoryBindingId: result.repositoryBindingId,
13069
+ workspaceRoot,
13070
+ source,
13071
+ status: "connected"
13072
+ },
13073
+ stdoutWrite
13074
+ );
13075
+ return 0;
13076
+ }
13077
+ return cancelAndExit();
13078
+ }
13079
+ if (result.status === "pending") {
13080
+ writeEvent({ event: "pending", expiresAt: result.expiresAt }, stdoutWrite);
13081
+ await delay(pollIntervalMs, signal);
13082
+ continue;
13083
+ }
13084
+ if (result.status === "ready") {
13085
+ writeEvent(
13086
+ {
13087
+ event: "ready",
13088
+ repositoryBindingId: result.repositoryBindingId,
13089
+ workspaceRoot,
13090
+ source,
13091
+ status: "connected"
13092
+ },
13093
+ stdoutWrite
13094
+ );
13095
+ return 0;
13096
+ }
13097
+ if (isRetryablePollFailure(result)) {
13098
+ await delay(pollIntervalMs, signal);
13099
+ continue;
13100
+ }
13101
+ const terminal = terminalEventFromPoll(result);
13102
+ if (terminal) {
13103
+ writeEvent(terminal, stdoutWrite);
13104
+ return 1;
13105
+ }
13106
+ writeEvent({ event: "fatal", message: "Plugin pairing failed" }, stdoutWrite);
13107
+ return 1;
13108
+ }
13109
+ return cancelAndExit();
13110
+ }
13111
+ async function cliPluginPair(argv, options = {}) {
13112
+ const stdoutWrite = options.stdoutWrite ?? ((chunk) => process.stdout.write(chunk));
13113
+ const parsed2 = parsePluginPairArgv(argv);
13114
+ if (parsed2.error) {
13115
+ writeEvent({ event: "fatal", message: parsed2.error }, stdoutWrite);
13116
+ return 1;
13117
+ }
13118
+ if (!parsed2.workspaceRoot || !parsed2.source) {
13119
+ writeEvent({ event: "fatal", message: PLUGIN_PAIR_USAGE }, stdoutWrite);
13120
+ return 1;
13121
+ }
13122
+ if (!isPluginPairingSource(parsed2.source)) {
13123
+ writeEvent({ event: "invalid" }, stdoutWrite);
13124
+ return 1;
13125
+ }
13126
+ const workspaceRootOrError = await resolveWorkspaceRoot(parsed2.workspaceRoot);
13127
+ if (typeof workspaceRootOrError !== "string") {
13128
+ writeEvent({ event: "fatal", message: workspaceRootOrError.error }, stdoutWrite);
13129
+ return 1;
13130
+ }
13131
+ const workspaceRoot = workspaceRootOrError;
13132
+ const source = parsed2.source;
13133
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS;
13134
+ const beginPairing = options.beginPairing ?? beginPluginRepositoryPairing;
13135
+ try {
13136
+ const binding = await resolveAuthoritativeBinding(workspaceRoot);
13137
+ if (binding.status === "connected") {
13138
+ return 0;
13139
+ }
13140
+ } catch (error) {
13141
+ if (!(error instanceof ReconnectRequiredError)) {
13142
+ throw error;
13143
+ }
13144
+ }
13145
+ const abortController = new AbortController();
13146
+ const signal = options.signal ?? abortController.signal;
13147
+ const onStop = () => abortController.abort();
13148
+ const installSignalHandlers = options.installSignalHandlers === true;
13149
+ if (installSignalHandlers) {
13150
+ process.on("SIGINT", onStop);
13151
+ process.on("SIGTERM", onStop);
13152
+ }
13153
+ try {
13154
+ let session;
13155
+ try {
13156
+ session = await beginPairing({
13157
+ workspaceRoot,
13158
+ source,
13159
+ apiUrl: parsed2.apiUrl,
13160
+ fetchImpl: options.fetchImpl,
13161
+ connectOptions: options.connectOptions
13162
+ });
13163
+ } catch (error) {
13164
+ if (error instanceof PluginPairingStartError) {
13165
+ writeEvent({ event: error.status, operation: "start" }, stdoutWrite);
13166
+ return 1;
13167
+ }
13168
+ writeEvent({ event: "fatal", message: "Plugin pairing failed" }, stdoutWrite);
13169
+ return 1;
13170
+ }
13171
+ return await runPluginPairSession(session, {
13172
+ workspaceRoot,
13173
+ source,
13174
+ pollIntervalMs,
13175
+ signal,
13176
+ stdoutWrite
13177
+ });
13178
+ } finally {
13179
+ if (installSignalHandlers) {
13180
+ process.off("SIGINT", onStop);
13181
+ process.off("SIGTERM", onStop);
13182
+ }
13183
+ }
13184
+ }
13185
+ var fs38, path45, PLUGIN_PAIR_USAGE, DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS, SECRET_OUTPUT_RE;
13186
+ var init_pluginPairCommand = __esm({
13187
+ "src/pluginPairCommand.ts"() {
13188
+ fs38 = __toESM(require("fs/promises"), 1);
13189
+ path45 = __toESM(require("path"), 1);
13190
+ init_pluginPairing();
13191
+ init_projectBinding();
13192
+ init_tokenRefreshCoordinator();
13193
+ PLUGIN_PAIR_USAGE = "Usage: memoraone-mcp plugin-pair --workspace-root <path> --source <cursor|jetbrains> [--api-url <url>]";
13194
+ DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS = 1e3;
13195
+ SECRET_OUTPUT_RE = /\b(mcc_|mpv_|mia_|mir_|polling_verifier|pollingVerifier|access_token|refresh_token|api[_-]?key)\b/i;
13196
+ }
13197
+ });
13198
+
13199
+ // src/runtimeIdentityCommand.ts
13200
+ var runtimeIdentityCommand_exports = {};
13201
+ __export(runtimeIdentityCommand_exports, {
13202
+ RUNTIME_IDENTITY_USAGE: () => RUNTIME_IDENTITY_USAGE,
13203
+ cliRuntimeIdentity: () => cliRuntimeIdentity,
13204
+ parseRuntimeIdentityArgv: () => parseRuntimeIdentityArgv
13205
+ });
13206
+ function parseRuntimeIdentityArgv(argv) {
13207
+ let workspaceRoot;
13208
+ for (let i = 0; i < argv.length; i++) {
13209
+ const a = argv[i];
13210
+ if (a === "--workspace-root") {
13211
+ const value = argv[++i];
13212
+ if (!value || value.startsWith("-")) return { error: RUNTIME_IDENTITY_USAGE };
13213
+ workspaceRoot = value;
13214
+ continue;
13215
+ }
13216
+ if (a.startsWith("--workspace-root=")) {
13217
+ const value = a.slice("--workspace-root=".length);
13218
+ if (!value) return { error: RUNTIME_IDENTITY_USAGE };
13219
+ workspaceRoot = value;
13220
+ continue;
13221
+ }
13222
+ if (a.startsWith("-")) {
13223
+ return { error: `Unknown runtime-identity option: ${a}` };
13224
+ }
13225
+ return { error: RUNTIME_IDENTITY_USAGE };
13226
+ }
13227
+ return { workspaceRoot };
13228
+ }
13229
+ function writeJson(payload, stdoutWrite) {
13230
+ const line = `${JSON.stringify(payload)}
13231
+ `;
13232
+ if (SECRET_OUTPUT_RE2.test(line)) {
13233
+ stdoutWrite(`${JSON.stringify({ status: "error", error: "Runtime identity failed" })}
13234
+ `);
13235
+ return;
13236
+ }
13237
+ stdoutWrite(line);
13238
+ }
13239
+ async function resolveWorkspaceRoot2(workspaceRoot) {
13240
+ const resolved = path46.resolve(workspaceRoot);
13241
+ try {
13242
+ const st = await fs39.stat(resolved);
13243
+ if (!st.isDirectory()) {
13244
+ return { error: "workspace-root must be a directory", workspaceRoot: resolved };
13245
+ }
13246
+ } catch {
13247
+ return { error: "workspace-root does not exist", workspaceRoot: resolved };
13248
+ }
13249
+ return { workspaceRoot: resolved };
13250
+ }
13251
+ async function cliRuntimeIdentity(argv, options = {}) {
13252
+ const stdoutWrite = options.stdoutWrite ?? ((chunk) => process.stdout.write(chunk));
13253
+ const parsed2 = parseRuntimeIdentityArgv(argv);
13254
+ if (parsed2.error) {
13255
+ writeJson({ status: "error", error: parsed2.error }, stdoutWrite);
13256
+ return 1;
13257
+ }
13258
+ if (!parsed2.workspaceRoot) {
13259
+ writeJson({ status: "error", error: RUNTIME_IDENTITY_USAGE }, stdoutWrite);
13260
+ return 1;
13261
+ }
13262
+ const workspaceRootOrError = await resolveWorkspaceRoot2(parsed2.workspaceRoot);
13263
+ if ("error" in workspaceRootOrError) {
13264
+ writeJson(
13265
+ {
13266
+ status: "error",
13267
+ error: workspaceRootOrError.error,
13268
+ workspaceRoot: workspaceRootOrError.workspaceRoot
13269
+ },
13270
+ stdoutWrite
13271
+ );
13272
+ return 1;
13273
+ }
13274
+ const lookupIdentity = options.lookupIdentity ?? lookupLocalBindingIdentity;
13275
+ try {
13276
+ const result = await lookupIdentity(workspaceRootOrError.workspaceRoot, {
13277
+ homeDir: options.homeDir,
13278
+ identityDeps: options.identityDeps
13279
+ });
13280
+ writeJson(result, stdoutWrite);
13281
+ return 0;
13282
+ } catch {
13283
+ writeJson(
13284
+ {
13285
+ status: "error",
13286
+ error: "Runtime identity failed",
13287
+ workspaceRoot: workspaceRootOrError.workspaceRoot
13288
+ },
13289
+ stdoutWrite
13290
+ );
13291
+ return 1;
13292
+ }
13293
+ }
13294
+ var fs39, path46, RUNTIME_IDENTITY_USAGE, SECRET_OUTPUT_RE2;
13295
+ var init_runtimeIdentityCommand = __esm({
13296
+ "src/runtimeIdentityCommand.ts"() {
13297
+ fs39 = __toESM(require("fs/promises"), 1);
13298
+ path46 = __toESM(require("path"), 1);
13299
+ init_resolveLocalBinding();
13300
+ RUNTIME_IDENTITY_USAGE = "Usage: memoraone-mcp runtime-identity --workspace-root <path>";
13301
+ SECRET_OUTPUT_RE2 = /\b(mcc_|mpv_|mia_|mir_|polling_verifier|pollingVerifier|access_token|refresh_token|api[_-]?key|projectId|project_id)\b/i;
13302
+ }
13303
+ });
13304
+
12621
13305
  // src/bridgeClientRoots.ts
12622
13306
  function isInitializeDebugEnabled(env2 = process.env) {
12623
13307
  return TRUTHY.has(String(env2.MEMORAONE_DEBUG_INIT ?? "").trim().toLowerCase());
@@ -12758,8 +13442,8 @@ var init_bridgeClientRoots = __esm({
12758
13442
  if (this.closed) {
12759
13443
  return null;
12760
13444
  }
12761
- return new Promise((resolve40) => {
12762
- this.waiters.push(resolve40);
13445
+ return new Promise((resolve43) => {
13446
+ this.waiters.push(resolve43);
12763
13447
  });
12764
13448
  }
12765
13449
  /** Re-queue lines read during an intermediate protocol step (e.g. roots/list) for the main bridge loop. */
@@ -12986,9 +13670,9 @@ function extractJsonRpcId(line) {
12986
13670
  }
12987
13671
  }
12988
13672
  function connectWithRetry(socketPath, log, maxRetries, retryDelayMs, connect2) {
12989
- return new Promise((resolve40, reject) => {
13673
+ return new Promise((resolve43, reject) => {
12990
13674
  const tryConnect = (attempt) => {
12991
- connect2(socketPath).then(resolve40).catch((err) => {
13675
+ connect2(socketPath).then(resolve43).catch((err) => {
12992
13676
  if (attempt >= maxRetries) {
12993
13677
  reject(err);
12994
13678
  return;
@@ -13184,8 +13868,8 @@ var init_bridgeProxy = __esm({
13184
13868
  this.maxRetries = options.maxRetries ?? 5;
13185
13869
  this.retryDelayMs = options.retryDelayMs ?? 200;
13186
13870
  this.lineReader = options.lineReader ?? null;
13187
- this.connectImpl = options.connect ?? ((socketPath) => new Promise((resolve40, reject) => {
13188
- const socket = net.connect(socketPath, () => resolve40(socket));
13871
+ this.connectImpl = options.connect ?? ((socketPath) => new Promise((resolve43, reject) => {
13872
+ const socket = net.connect(socketPath, () => resolve43(socket));
13189
13873
  socket.on("error", reject);
13190
13874
  }));
13191
13875
  this.spawnDaemonImpl = options.spawnDaemon ?? (async (binding) => {
@@ -13317,7 +14001,7 @@ var init_bridgeProxy = __esm({
13317
14001
  }
13318
14002
  waitForDaemonJsonRpcResponse(id, timeoutMs = 3e4) {
13319
14003
  const key = this.waiterKey(id);
13320
- return new Promise((resolve40, reject) => {
14004
+ return new Promise((resolve43, reject) => {
13321
14005
  const timer = setTimeout(() => {
13322
14006
  this.pendingDaemonResponseWaiters.delete(key);
13323
14007
  reject(
@@ -13326,7 +14010,7 @@ var init_bridgeProxy = __esm({
13326
14010
  )
13327
14011
  );
13328
14012
  }, timeoutMs);
13329
- this.pendingDaemonResponseWaiters.set(key, { resolve: resolve40, reject, timer });
14013
+ this.pendingDaemonResponseWaiters.set(key, { resolve: resolve43, reject, timer });
13330
14014
  });
13331
14015
  }
13332
14016
  notifyDaemonResponseWaiters(line) {
@@ -13513,6 +14197,8 @@ if (args.includes("--help") || args.includes("-h")) {
13513
14197
  console.log(
13514
14198
  `Usage: memoraone-mcp [--version] [--help]
13515
14199
  memoraone-mcp connect <code> [--api-url <url>] [--verbose]
14200
+ memoraone-mcp plugin-pair --workspace-root <path> --source <cursor|jetbrains> [--api-url <url>]
14201
+ memoraone-mcp runtime-identity --workspace-root <path>
13516
14202
  memoraone-mcp [--daemon --binding-id <mrb_\u2026> [--ide ${IDE_TYPE_CLI_CHOICES}]]
13517
14203
  memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains|--claude-code|--windsurf|--opencode|--codex|--kiro|--kiro-cli|--cline|--cline-cli|--claude-desktop|--zed|--visual-studio|--copilot-cli|--auggie|--antigravity|--goose|--junie|--xcode|--copilot-jetbrains|--copilot-visual-studio] [--force] [--dry-run] [--no-gitignore] [--cleanup] [--dev] [--repair] [--workspace-root <path>] [--api-url <url>] [--verbose]
13518
14204
  Cursor API environment (with --cursor or --all): --local (node + built cli.cjs + local API) | --staging (npx + staging API)
@@ -13535,6 +14221,28 @@ if (args[0] === "cleanup") {
13535
14221
  `);
13536
14222
  process.exit(1);
13537
14223
  });
14224
+ } else if (args[0] === "plugin-pair") {
14225
+ Promise.resolve().then(() => (init_pluginPairCommand(), pluginPairCommand_exports)).then(
14226
+ ({ cliPluginPair: cliPluginPair2 }) => cliPluginPair2(args.slice(1), { installSignalHandlers: true })
14227
+ ).then((code) => process.exit(code)).catch((err) => {
14228
+ process.stdout.write(
14229
+ `${JSON.stringify({ event: "fatal", message: "Plugin pairing failed" })}
14230
+ `
14231
+ );
14232
+ process.stderr.write(`[memoraone-mcp] plugin-pair fatal: ${String(err)}
14233
+ `);
14234
+ process.exit(1);
14235
+ });
14236
+ } else if (args[0] === "runtime-identity") {
14237
+ Promise.resolve().then(() => (init_runtimeIdentityCommand(), runtimeIdentityCommand_exports)).then(({ cliRuntimeIdentity: cliRuntimeIdentity2 }) => cliRuntimeIdentity2(args.slice(1))).then((code) => process.exit(code)).catch((err) => {
14238
+ process.stdout.write(
14239
+ `${JSON.stringify({ status: "error", error: "Runtime identity failed" })}
14240
+ `
14241
+ );
14242
+ process.stderr.write(`[memoraone-mcp] runtime-identity fatal: ${String(err)}
14243
+ `);
14244
+ process.exit(1);
14245
+ });
13538
14246
  } else if (args[0] === "setup-ide-files") {
13539
14247
  Promise.resolve().then(() => (init_setupIdeFiles(), setupIdeFiles_exports)).then(({ cliSetupIdeFiles: cliSetupIdeFiles2 }) => cliSetupIdeFiles2(args.slice(1))).then((code) => process.exit(code)).catch((err) => {
13540
14248
  process.stderr.write(`[memoraone-mcp] setup-ide-files fatal: ${String(err)}
package/dist/daemon.cjs CHANGED
@@ -3437,7 +3437,7 @@ async function main(opts = {}) {
3437
3437
  registeredToolNames.push("memora_get_personal_context");
3438
3438
  server.tool(
3439
3439
  "memora_status",
3440
- "Return non-secret project binding metadata for this MCP session",
3440
+ "Return non-secret project binding metadata for this MCP session. Use this for repository/project connection identity: which repository or project this session is bound to. Prefer this over guessing from local files, git remotes, or IDE context.",
3441
3441
  bindingStatusShape,
3442
3442
  async () => runWithSessionContext(sessionContext, async () => {
3443
3443
  if (!runtime.authoritativeBinding) return notInitializedResult;
@@ -3455,7 +3455,7 @@ async function main(opts = {}) {
3455
3455
  runtime,
3456
3456
  sessionContext,
3457
3457
  "memora_ask_with_memory",
3458
- "Answer using saved workspace knowledge and timeline. In user-visible replies, state facts directly; do not attribute answers to tools, storage, retrieval, or memory-style framing unless the user asks how you know.",
3458
+ 'Answer using saved workspace knowledge and timeline. Use this before answering previous questions/answers, conversation/history, decisions, facts, preferences, project context, or questions like "what did I/you say, ask, decide, or do before?". Do not silently substitute IDE session history, local files, git history, or model memory when MemoraOne can provide relevant context. Skip only when the question is clearly unrelated. In user-visible replies, state facts directly; do not attribute answers to tools, storage, retrieval, or memory-style framing unless the user asks how you know.',
3459
3459
  askWithMemoryShape,
3460
3460
  async (args) => {
3461
3461
  const result = await handleAskWithMemory(runtime.client, args);
package/dist/index.cjs CHANGED
@@ -3363,7 +3363,7 @@ async function main(opts = {}) {
3363
3363
  registeredToolNames.push("memora_get_personal_context");
3364
3364
  server.tool(
3365
3365
  "memora_status",
3366
- "Return non-secret project binding metadata for this MCP session",
3366
+ "Return non-secret project binding metadata for this MCP session. Use this for repository/project connection identity: which repository or project this session is bound to. Prefer this over guessing from local files, git remotes, or IDE context.",
3367
3367
  bindingStatusShape,
3368
3368
  async () => runWithSessionContext(sessionContext, async () => {
3369
3369
  if (!runtime.authoritativeBinding) return notInitializedResult;
@@ -3381,7 +3381,7 @@ async function main(opts = {}) {
3381
3381
  runtime,
3382
3382
  sessionContext,
3383
3383
  "memora_ask_with_memory",
3384
- "Answer using saved workspace knowledge and timeline. In user-visible replies, state facts directly; do not attribute answers to tools, storage, retrieval, or memory-style framing unless the user asks how you know.",
3384
+ 'Answer using saved workspace knowledge and timeline. Use this before answering previous questions/answers, conversation/history, decisions, facts, preferences, project context, or questions like "what did I/you say, ask, decide, or do before?". Do not silently substitute IDE session history, local files, git history, or model memory when MemoraOne can provide relevant context. Skip only when the question is clearly unrelated. In user-visible replies, state facts directly; do not attribute answers to tools, storage, retrieval, or memory-style framing unless the user asks how you know.',
3385
3385
  askWithMemoryShape,
3386
3386
  async (args) => {
3387
3387
  const result = await handleAskWithMemory(runtime.client, args);
@@ -7526,6 +7526,14 @@ async function findRepoRoot(startDir) {
7526
7526
  function stripLeadingLineComments18(text) {
7527
7527
  return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
7528
7528
  }
7529
+ function memoraUsagePolicy() {
7530
+ return `- For every user question, check whether MemoraOne can provide relevant context before answering. If it can, use MemoraOne first.
7531
+ - Do not silently substitute IDE session history, local files, git history, or model memory when MemoraOne can provide relevant context.
7532
+ - Use **\`memora_status\`** for repository/project connection identity.
7533
+ - Use **\`memora_ask_with_memory\`** for previous questions/answers, conversation/history, decisions, facts, preferences, project context, and questions like "what did I/you say, ask, decide, or do before?"
7534
+ - Skip MemoraOne only when the question is clearly unrelated.
7535
+ - After the interaction: if anything meaningful happened\u2014question, answer, decision, discovery, action, correction, failure, or change\u2014record it back to MemoraOne so the next agent knows about it, especially if MemoraOne was not used before answering. Use **\`memora_post_event\`** and **\`memora_log_change_summary\`** according to their intended semantics; do not write trivial noise.`;
7536
+ }
7529
7537
  function cursorRuleBody() {
7530
7538
  return `${MANAGED_MARKER}
7531
7539
 
@@ -7535,7 +7543,7 @@ This repository uses **MemoraOne** via the MCP server named **memoraone** (repo-
7535
7543
 
7536
7544
  ### Tools
7537
7545
 
7538
- - Before answering questions about **prior decisions**, **remembered facts**, **identity or personal recall**, **preferences**, **repo history**, or **what to do next**, call **\`memora_ask_with_memory\`** so replies stay aligned with MemoraOne memory.
7546
+ ${memoraUsagePolicy()}
7539
7547
  - Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. \`cursor\`), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
7540
7548
  - Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
7541
7549
 
@@ -7553,7 +7561,7 @@ This repo is set up to use **MemoraOne** through MCP where your editor exposes i
7553
7561
 
7554
7562
  ## Behavior
7555
7563
 
7556
- - For questions about **earlier decisions**, **stored facts**, **personal or identity recall**, **preferences**, **project history**, or **recommended next steps**, use MemoraOne memory tools (e.g. **\`memora_ask_with_memory\`**) when available before answering.
7564
+ ${memoraUsagePolicy()}
7557
7565
  - Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. your agent name), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
7558
7566
  - Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
7559
7567
 
@@ -7927,6 +7935,7 @@ async function runSetupIdeFiles(o) {
7927
7935
  }
7928
7936
  const cursorContent = `---
7929
7937
  description: MemoraOne MCP \u2014 IDE agent instructions
7938
+ alwaysApply: true
7930
7939
  ---
7931
7940
 
7932
7941
  ` + cursorRuleBody();
@@ -9972,8 +9981,38 @@ var PluginRepositoryPairingSession = class {
9972
9981
  apiUrl: this.apiUrl,
9973
9982
  ideType: this.source,
9974
9983
  fetchImpl: this.connectOptions.fetchImpl ?? this.fetchImpl,
9975
- // The marketplace plugin is the authoritative IDE launch source.
9976
- configureIdes: false
9984
+ // Plugin pairing configures only the IDE that initiated the pairing.
9985
+ configureIdes: true,
9986
+ setupIdeOptions: {
9987
+ ...this.connectOptions.setupIdeOptions,
9988
+ targets: {
9989
+ cursor: this.source === "cursor",
9990
+ vscode: false,
9991
+ jetbrains: this.source === "jetbrains",
9992
+ claudeCode: false,
9993
+ windsurf: false,
9994
+ opencode: false,
9995
+ codex: false,
9996
+ kiro: false,
9997
+ kiroCli: false,
9998
+ rooCode: false,
9999
+ cline: false,
10000
+ clineCli: false,
10001
+ claudeDesktop: false,
10002
+ zed: false,
10003
+ visualStudio: false,
10004
+ continue: false,
10005
+ copilotCli: false,
10006
+ auggie: false,
10007
+ geminiCli: false,
10008
+ antigravity: false,
10009
+ goose: false,
10010
+ junie: false,
10011
+ xcode: false,
10012
+ copilotJetBrains: false,
10013
+ copilotVisualStudio: false
10014
+ }
10015
+ }
9977
10016
  });
9978
10017
  if (connected.exitCode !== 0) {
9979
10018
  this.finish("backend_failure");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memoraone/mcp",
3
- "version": "0.1.44",
3
+ "version": "0.1.46",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "exports": {