@memmo/cli 0.7.1 → 0.7.3

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 (2) hide show
  1. package/dist/index.js +48 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -109,7 +109,15 @@ function forgetRepo(remote) {
109
109
  } catch {
110
110
  }
111
111
  }
112
- var DEFAULT_API_BASE_URL = process.env.MEMMO_API_URL ?? "https://api.memmo.dev";
112
+ function envApiUrl() {
113
+ const v = process.env.MEMMO_API_URL?.trim();
114
+ return v ? v.replace(/\/$/, "") : void 0;
115
+ }
116
+ function envToken() {
117
+ const v = process.env.MEMMO_TOKEN?.trim();
118
+ return v || void 0;
119
+ }
120
+ var DEFAULT_API_BASE_URL = envApiUrl() ?? "https://api.memmo.dev";
113
121
  function detectGitRemote(cwd = process.cwd()) {
114
122
  try {
115
123
  return execSync("git remote get-url origin", {
@@ -122,11 +130,12 @@ function detectGitRemote(cwd = process.cwd()) {
122
130
  }
123
131
  async function resolveProjectConfig(cwd = process.cwd(), opts = {}) {
124
132
  const creds = loadCredentials();
125
- const apiBaseUrl = opts.apiBaseUrl ?? process.env.MEMMO_API_URL ?? creds?.apiBaseUrl ?? DEFAULT_API_BASE_URL;
133
+ const apiBaseUrl = opts.apiBaseUrl ?? envApiUrl() ?? creds?.apiBaseUrl ?? DEFAULT_API_BASE_URL;
134
+ const token = envToken() ?? creds?.token;
126
135
  const explicit = opts.repoId ?? process.env.MEMMO_REPO_ID;
127
136
  if (explicit) return { apiBaseUrl, repoId: explicit };
128
137
  const remote = detectGitRemote(cwd);
129
- if (remote && creds) {
138
+ if (remote && token) {
130
139
  const hit = readRepoCache()[remote];
131
140
  if (hit && hit.apiBaseUrl === apiBaseUrl && Date.now() - hit.at < REPO_CACHE_TTL_MS) {
132
141
  return { apiBaseUrl, repoId: hit.repoId, repoFullName: hit.fullName };
@@ -134,7 +143,7 @@ async function resolveProjectConfig(cwd = process.cwd(), opts = {}) {
134
143
  try {
135
144
  const { apiFetch: apiFetch2 } = await Promise.resolve().then(() => (init_api(), api_exports));
136
145
  const r = await apiFetch2(
137
- { baseUrl: apiBaseUrl, token: creds.token },
146
+ { baseUrl: apiBaseUrl, token },
138
147
  "/repos/resolve",
139
148
  { method: "POST", body: JSON.stringify({ repoRemote: remote }) }
140
149
  );
@@ -612,7 +621,7 @@ async function desktopCommand(opts = {}) {
612
621
  }
613
622
  const creds = loadCredentials();
614
623
  if (!creds) throw new Error("Not logged in. Run `memmo login` first.");
615
- const client = { baseUrl: process.env.MEMMO_API_URL ?? creds.apiBaseUrl, token: creds.token };
624
+ const client = { baseUrl: envApiUrl() ?? creds.apiBaseUrl, token: creds.token };
616
625
  let repoId = opts.repo;
617
626
  let label = repoId;
618
627
  if (!repoId) {
@@ -675,7 +684,7 @@ async function chooseProject(client, wanted, yes = false) {
675
684
  async function initCommand(opts) {
676
685
  const creds = loadCredentials();
677
686
  if (!creds) throw new Error("Not logged in. Run `memmo login` first.");
678
- const client = { baseUrl: process.env.MEMMO_API_URL ?? creds.apiBaseUrl, token: creds.token };
687
+ const client = { baseUrl: envApiUrl() ?? creds.apiBaseUrl, token: creds.token };
679
688
  const cwd = process.cwd();
680
689
  if (!existsSync5(join5(cwd, ".git"))) {
681
690
  console.log("note: no .git here \u2014 run `memmo init` at your repo root.");
@@ -894,14 +903,12 @@ async function mcpCommand(opts = {}) {
894
903
  `/repos/${config.repoId}/proposals`,
895
904
  { method: "POST", body: JSON.stringify({ memories }) }
896
905
  );
897
- return {
898
- content: [
899
- {
900
- type: "text",
901
- text: `Proposed ${result.proposedCount} memory(ies) for review in Memmo.`
902
- }
903
- ]
904
- };
906
+ const parts = [];
907
+ if (result.proposedCount) parts.push(`${result.proposedCount} proposed for review`);
908
+ if (result.autoApproved) parts.push(`${result.autoApproved} auto-approved`);
909
+ if (result.autoRejected) parts.push(`${result.autoRejected} auto-rejected (low confidence)`);
910
+ if (result.skipped) parts.push(`${result.skipped} skipped as already known`);
911
+ return { content: [{ type: "text", text: parts.length ? `Memmo: ${parts.join(", ")}.` : "Memmo: nothing new to record." }] };
905
912
  }
906
913
  );
907
914
  const transport = new StdioServerTransport();
@@ -1858,15 +1865,21 @@ async function postInlineReview(ctx, diff, review) {
1858
1865
  `Memmo: posted review (${payload.comments.length} inline, ${payload.newCount} new findings).`
1859
1866
  );
1860
1867
  }
1868
+ function skipReview(message) {
1869
+ console.error(message);
1870
+ if (!process.env.GITHUB_ACTIONS) process.exitCode = 1;
1871
+ }
1861
1872
  async function reviewCommand(opts = {}) {
1862
1873
  const config = await resolveProjectConfig();
1863
1874
  if (!config) {
1864
- throw new Error(NOT_CONNECTED);
1875
+ return skipReview(
1876
+ process.env.GITHUB_ACTIONS ? "Memmo: this repo isn't connected to a project, or MEMMO_TOKEN isn't a token for it. Open Memmo \u2192 Reviews to fix; skipping the review." : NOT_CONNECTED
1877
+ );
1865
1878
  }
1866
1879
  const creds = loadCredentials();
1867
- const token = process.env.MEMMO_TOKEN ?? creds?.token;
1880
+ const token = envToken() ?? creds?.token;
1868
1881
  if (!token) throw new Error("No API token. Set MEMMO_TOKEN (CI) or run `memmo login`.");
1869
- const baseUrl = opts.api ?? process.env.MEMMO_API_URL ?? config.apiBaseUrl ?? creds?.apiBaseUrl;
1882
+ const baseUrl = opts.api ?? envApiUrl() ?? config.apiBaseUrl ?? creds?.apiBaseUrl;
1870
1883
  if (!baseUrl) throw new Error("No API base URL. Set MEMMO_API_URL or pass --api.");
1871
1884
  const client = { baseUrl, token };
1872
1885
  const base = opts.base ?? process.env.GITHUB_BASE_REF ?? "main";
@@ -1878,10 +1891,22 @@ async function reviewCommand(opts = {}) {
1878
1891
  const ev = readEvent();
1879
1892
  const prTitle = (opts.title ?? ev.title ?? git(["log", "-1", "--pretty=%s"]).trim()).slice(0, 500);
1880
1893
  const prBody = opts.body ?? ev.body;
1881
- const res = await apiFetch(client, `/repos/${config.repoId}/review-diff`, {
1882
- method: "POST",
1883
- body: JSON.stringify({ prTitle, prBody, diff })
1884
- });
1894
+ let res;
1895
+ try {
1896
+ res = await apiFetch(client, `/repos/${config.repoId}/review-diff`, {
1897
+ method: "POST",
1898
+ body: JSON.stringify({ prTitle, prBody, diff })
1899
+ });
1900
+ } catch (e) {
1901
+ const msg = e instanceof Error ? e.message : String(e);
1902
+ if (msg.includes("llm_not_configured")) {
1903
+ return skipReview("Memmo: the project has no AI key yet. Add one in Memmo \u2192 Reviews (step 1); skipping the review.");
1904
+ }
1905
+ if (msg.includes("plan_requires_reviewer")) {
1906
+ return skipReview("Memmo: the PR reviewer needs the Scale plan. Upgrade in Memmo \u2192 Settings \u2192 Subscription; skipping the review.");
1907
+ }
1908
+ throw e;
1909
+ }
1885
1910
  if (res.skipped) {
1886
1911
  console.error("Memmo reviewer is disabled for this repo \u2014 skipping (enable it in the dashboard).");
1887
1912
  return;
@@ -1935,9 +1960,9 @@ async function reviewSyncCommand(opts = {}) {
1935
1960
  throw new Error(NOT_CONNECTED);
1936
1961
  }
1937
1962
  const creds = loadCredentials();
1938
- const token = process.env.MEMMO_TOKEN ?? creds?.token;
1963
+ const token = envToken() ?? creds?.token;
1939
1964
  if (!token) throw new Error("No API token. Set MEMMO_TOKEN (CI) or run `memmo login`.");
1940
- const baseUrl = opts.api ?? process.env.MEMMO_API_URL ?? config.apiBaseUrl ?? creds?.apiBaseUrl;
1965
+ const baseUrl = opts.api ?? envApiUrl() ?? config.apiBaseUrl ?? creds?.apiBaseUrl;
1941
1966
  if (!baseUrl) throw new Error("No API base URL. Set MEMMO_API_URL or pass --api.");
1942
1967
  const client = { baseUrl, token };
1943
1968
  const ev = readEvent();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memmo/cli",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Long-term memory for AI coding agents — CLI + MCP server for Claude Code.",
5
5
  "license": "MIT",
6
6
  "author": "Benjamin Beguin",