@shahmilsaari/memory-core 0.2.13 → 0.2.14

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 (3) hide show
  1. package/README.md +33 -6
  2. package/dist/cli.js +365 -53
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@ You decide the architecture. memory-core remembers it. Every AI tool — Copilot
8
8
  npx @shahmilsaari/memory-core init
9
9
  ```
10
10
 
11
- Fully local or cloud — your choice. **v0.2.10**
11
+ Fully local or cloud — your choice. **v0.2.13**
12
12
 
13
13
  ---
14
14
 
@@ -165,6 +165,14 @@ That's it. Every AI agent in your project now has your rules.
165
165
 
166
166
  ## Commands
167
167
 
168
+ For the full CLI reference, examples, and command behavior notes, see [COMMANDS.md](./COMMANDS.md).
169
+
170
+ New setup-management commands:
171
+ - `memory-core status` — show project name, provider/model, agents, hook state, generated files, and health checks
172
+ - `memory-core provider set <provider>` — switch code-checking provider without rerunning `init`
173
+ - `memory-core model set <model>` — update chat or embedding model from the CLI
174
+ - `memory-core model doctor` — verify database, Ollama, model installation, and cloud API key presence
175
+
168
176
  ### `init` — Set up a project
169
177
 
170
178
  ```bash
@@ -566,6 +574,13 @@ npx @shahmilsaari/memory-core sync
566
574
  # Not sure how something was decided? Search.
567
575
  npx @shahmilsaari/memory-core search "caching strategy"
568
576
 
577
+ # Inspect current setup, provider, model, and health checks
578
+ npx @shahmilsaari/memory-core status
579
+
580
+ # Switch code-checking provider or model without rerunning init
581
+ npx @shahmilsaari/memory-core provider set openai --model gpt-4o-mini --api-key $OPENAI_API_KEY
582
+ npx @shahmilsaari/memory-core model set qwen2.5-coder --provider ollama
583
+
569
584
  # See what rules are saved
570
585
  npx @shahmilsaari/memory-core list --type rule
571
586
 
@@ -603,10 +618,15 @@ pgvector isn't installed for your PostgreSQL version. Follow the [pgvector insta
603
618
  Start Ollama: `brew services start ollama` (macOS) or `ollama serve` (Linux).
604
619
 
605
620
  **`Chat model "llama3.2" not found`**
606
- Run `ollama pull llama3.2`. Or switch provider/model by updating `CHAT_PROVIDER` and `CHAT_MODEL` in `.memory-core.env`.
621
+ Run `ollama pull llama3.2`. Or switch provider/model with `npx @shahmilsaari/memory-core provider set ...` or `npx @shahmilsaari/memory-core model set ...`.
607
622
 
608
623
  **Using an API key instead of Ollama for code checking**
609
- During `init` choose OpenAI, Anthropic, or MiniMax when prompted for the check provider. Or set manually in `.memory-core.env`:
624
+ During `init` choose OpenAI, Anthropic, or MiniMax when prompted for the check provider. Or switch later:
625
+ ```bash
626
+ npx @shahmilsaari/memory-core provider set openai --model gpt-4o --api-key sk-...
627
+ ```
628
+
629
+ Equivalent manual `.memory-core.env` settings:
610
630
  ```
611
631
  CHAT_PROVIDER=openai
612
632
  CHAT_MODEL=gpt-4o
@@ -617,6 +637,12 @@ Embeddings always use Ollama (`nomic-embed-text`) regardless of provider.
617
637
  **`DATABASE_URL is not set`**
618
638
  Run `npx @shahmilsaari/memory-core init` — it will create the `.memory-core.env` file for you.
619
639
 
640
+ **Not sure what memory-core is configured to use**
641
+ Run `npx @shahmilsaari/memory-core status`.
642
+
643
+ **Need to verify the model/database setup**
644
+ Run `npx @shahmilsaari/memory-core model doctor`.
645
+
620
646
  **`createdb: role does not exist`**
621
647
  ```bash
622
648
  createuser -s $(whoami)
@@ -647,11 +673,12 @@ Save an ignore pattern: `npx @shahmilsaari/memory-core ignore "your exception he
647
673
  | ✓ | List / remove / edit — full CRUD for stored memories |
648
674
  | ✓ | False positive tagging — `ignore` command saves exceptions for hook and watcher |
649
675
  | ✓ | Reset command — clean up generated files and optionally drop the DB table |
650
- | ✓ | Test suite — Vitest smoke tests for all core commands and providers |
676
+ | ✓ | Test suite — smoke tests for all core commands and providers |
651
677
  | ✓ | Multi-provider code checking — Ollama, OpenAI, Anthropic, MiniMax |
652
- | | Context-aware retrieval — surface the most relevant rules for the file being edited |
678
+ | | Context-aware retrieval — surface the most relevant rules for the file being edited |
679
+ | ✓ | Setup management — `status`, `provider set`, `model set`, `model doctor` |
680
+ | ✓ | `--debug` flag — verbose output for diagnosing hook and watcher issues |
653
681
  | | Model guidance during init — recommend a model based on machine specs |
654
- | | `--debug` flag — verbose output for diagnosing hook and watcher issues |
655
682
  | | Violation → rule pipeline — auto-suggest a new rule when the same violation repeats |
656
683
  | | Rule analytics dashboard — visual breakdown of rule coverage and violations |
657
684
  | | Team sync — shared database so the whole team works from the same rule set |
package/dist/cli.js CHANGED
@@ -711,11 +711,11 @@ import chalk from "chalk";
711
711
 
712
712
  // src/chat.ts
713
713
  function getChatConfig() {
714
- const provider = process.env.CHAT_PROVIDER ?? "ollama";
715
- const model = process.env.CHAT_MODEL ?? process.env.OLLAMA_CHAT_MODEL ?? "llama3.2";
714
+ const provider2 = process.env.CHAT_PROVIDER ?? "ollama";
715
+ const model2 = process.env.CHAT_MODEL ?? process.env.OLLAMA_CHAT_MODEL ?? "llama3.2";
716
716
  return {
717
- provider,
718
- model,
717
+ provider: provider2,
718
+ model: model2,
719
719
  ollamaUrl: process.env.OLLAMA_URL ?? "http://localhost:11434",
720
720
  apiKey: process.env.CHAT_API_KEY ?? ""
721
721
  };
@@ -1301,10 +1301,10 @@ async function checkCi(options = {}) {
1301
1301
  recordViolations(violations);
1302
1302
  process.exit(1);
1303
1303
  }
1304
- function printModelMissing(model) {
1304
+ function printModelMissing(model2) {
1305
1305
  console.log(chalk.yellow(`
1306
- \u26A0 Chat model "${model}" not found in Ollama.`));
1307
- console.log(chalk.gray(` Pull a model: ollama pull ${model}`));
1306
+ \u26A0 Chat model "${model2}" not found in Ollama.`));
1307
+ console.log(chalk.gray(` Pull a model: ollama pull ${model2}`));
1308
1308
  console.log(chalk.gray(" Or set OLLAMA_CHAT_MODEL=<model> in .env"));
1309
1309
  console.log(chalk.gray(" Recommended: llama3.2 | qwen2.5-coder:3b | mistral\n"));
1310
1310
  }
@@ -1697,6 +1697,139 @@ async function checkConnections(dbUrl, ollamaUrl, chatModel) {
1697
1697
  }
1698
1698
  var { version } = JSON.parse(readFileSync6(new URL("../package.json", import.meta.url), "utf-8"));
1699
1699
  var CONFIG_FILE = ".memory-core.json";
1700
+ var DEFAULT_OLLAMA_URL = "http://localhost:11434";
1701
+ var DEFAULT_EMBEDDING_MODEL = "nomic-embed-text";
1702
+ var DEFAULT_CHAT_MODEL = "llama3.2";
1703
+ function getEnvPath() {
1704
+ const memoryEnv = join6(process.cwd(), ".memory-core.env");
1705
+ if (existsSync6(memoryEnv)) return memoryEnv;
1706
+ const dotEnv = join6(process.cwd(), ".env");
1707
+ return existsSync6(dotEnv) ? dotEnv : memoryEnv;
1708
+ }
1709
+ function parseEnvFile(raw) {
1710
+ const lines = raw.split(/\r?\n/);
1711
+ const values = {};
1712
+ for (const line of lines) {
1713
+ const trimmed = line.trim();
1714
+ if (!trimmed || trimmed.startsWith("#")) continue;
1715
+ const separatorIndex = trimmed.indexOf("=");
1716
+ if (separatorIndex === -1) continue;
1717
+ const key = trimmed.slice(0, separatorIndex).trim();
1718
+ const value = trimmed.slice(separatorIndex + 1).trim();
1719
+ if (key) values[key] = value;
1720
+ }
1721
+ return values;
1722
+ }
1723
+ function readRuntimeEnv() {
1724
+ const envPath = getEnvPath();
1725
+ const fileValues = existsSync6(envPath) ? parseEnvFile(readFileSync6(envPath, "utf-8")) : {};
1726
+ const values = {
1727
+ ...fileValues
1728
+ };
1729
+ for (const [key, value] of Object.entries(process.env)) {
1730
+ if (typeof value === "string" && value !== "") values[key] = value;
1731
+ }
1732
+ return { envPath, values };
1733
+ }
1734
+ function writeRuntimeEnv(values, envPath = getEnvPath()) {
1735
+ const orderedKeys = [
1736
+ "DATABASE_URL",
1737
+ "OLLAMA_URL",
1738
+ "OLLAMA_MODEL",
1739
+ "CHAT_PROVIDER",
1740
+ "CHAT_MODEL",
1741
+ "OLLAMA_CHAT_MODEL",
1742
+ "CHAT_API_KEY"
1743
+ ];
1744
+ const seen = /* @__PURE__ */ new Set();
1745
+ const lines = [];
1746
+ for (const key of orderedKeys) {
1747
+ const value = values[key];
1748
+ if (value) {
1749
+ lines.push(`${key}=${value}`);
1750
+ seen.add(key);
1751
+ }
1752
+ }
1753
+ for (const key of Object.keys(values).sort()) {
1754
+ if (seen.has(key)) continue;
1755
+ const value = values[key];
1756
+ if (value) lines.push(`${key}=${value}`);
1757
+ }
1758
+ writeFileSync5(envPath, `${lines.join("\n")}
1759
+ `, "utf-8");
1760
+ }
1761
+ function applyRuntimeEnv(values) {
1762
+ for (const [key, value] of Object.entries(values)) {
1763
+ process.env[key] = value;
1764
+ }
1765
+ }
1766
+ function ensureEnvFileIgnored(envPath = getEnvPath()) {
1767
+ const envFileName = envPath.split("/").pop() ?? ".memory-core.env";
1768
+ const gitignorePath = join6(process.cwd(), ".gitignore");
1769
+ const existing = existsSync6(gitignorePath) ? readFileSync6(gitignorePath, "utf-8") : "";
1770
+ if (!existing.includes(envFileName)) {
1771
+ appendFileSync(gitignorePath, `${existing ? "\n" : ""}${envFileName}
1772
+ `);
1773
+ }
1774
+ }
1775
+ function normalizeProvider(value) {
1776
+ const provider2 = value.trim().toLowerCase();
1777
+ if (provider2 === "ollama" || provider2 === "openai" || provider2 === "anthropic" || provider2 === "minimax") {
1778
+ return provider2;
1779
+ }
1780
+ throw new Error(`Unsupported provider "${value}". Use: ollama, openai, anthropic, minimax`);
1781
+ }
1782
+ function providerLabel(provider2) {
1783
+ switch (provider2) {
1784
+ case "openai":
1785
+ return "OpenAI";
1786
+ case "anthropic":
1787
+ return "Anthropic";
1788
+ case "minimax":
1789
+ return "MiniMax";
1790
+ default:
1791
+ return "Ollama";
1792
+ }
1793
+ }
1794
+ function redactDatabaseUrl(url) {
1795
+ if (!url) return "(not set)";
1796
+ return url.replace(/:\/\/([^:@/]+)(?::[^@/]+)?@/, "://$1:***@");
1797
+ }
1798
+ function getConfiguredProvider(values) {
1799
+ return normalizeProvider(values.CHAT_PROVIDER ?? "ollama");
1800
+ }
1801
+ function getConfiguredChatModel(values) {
1802
+ return values.CHAT_MODEL ?? values.OLLAMA_CHAT_MODEL ?? DEFAULT_CHAT_MODEL;
1803
+ }
1804
+ async function resolveOllamaInstalledModel(ollamaUrl, model2) {
1805
+ const res = await fetch(`${ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(5e3) });
1806
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1807
+ const data = await res.json();
1808
+ const models = data.models ?? [];
1809
+ const exact = models.find((entry) => entry.name === model2);
1810
+ const prefixed = models.find((entry) => entry.name.startsWith(`${model2}:`));
1811
+ return (exact ?? prefixed)?.name ?? null;
1812
+ }
1813
+ async function verifyDatabaseConnection(dbUrl) {
1814
+ if (!dbUrl) return "DATABASE_URL is not set";
1815
+ try {
1816
+ const { Pool } = (await import("pg")).default;
1817
+ const testPool = new Pool({ connectionString: dbUrl, connectionTimeoutMillis: 5e3 });
1818
+ await testPool.query("SELECT 1");
1819
+ await testPool.end();
1820
+ return null;
1821
+ } catch (err) {
1822
+ return err.message;
1823
+ }
1824
+ }
1825
+ async function verifyOllamaConnection(ollamaUrl) {
1826
+ try {
1827
+ const res = await fetch(`${ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(5e3) });
1828
+ return res.ok ? null : `HTTP ${res.status}`;
1829
+ } catch (err) {
1830
+ return err.message;
1831
+ }
1832
+ }
1700
1833
  function readProjectConfig() {
1701
1834
  const path = join6(process.cwd(), CONFIG_FILE);
1702
1835
  if (!existsSync6(path)) return null;
@@ -1744,6 +1877,130 @@ function printMemoryTable(memories, title = "Rules in memory") {
1744
1877
  });
1745
1878
  console.log(chalk3.gray("\n Use: memory-core remove <id> | memory-core edit <id>\n"));
1746
1879
  }
1880
+ function printStatusLine(label, value) {
1881
+ console.log(` ${chalk3.dim(label.padEnd(18))} ${value}`);
1882
+ }
1883
+ async function runModelDoctor() {
1884
+ const { envPath, values } = readRuntimeEnv();
1885
+ const provider2 = getConfiguredProvider(values);
1886
+ const model2 = getConfiguredChatModel(values);
1887
+ const ollamaUrl = values.OLLAMA_URL ?? DEFAULT_OLLAMA_URL;
1888
+ const embeddingModel = values.OLLAMA_MODEL ?? DEFAULT_EMBEDDING_MODEL;
1889
+ const dbUrl = values.DATABASE_URL ?? "";
1890
+ console.log(chalk3.bold("\n memory-core model doctor\n"));
1891
+ printStatusLine("Env file", existsSync6(envPath) ? envPath : `${envPath} ${chalk3.yellow("(will be created on first write)")}`);
1892
+ printStatusLine("Provider", provider2);
1893
+ printStatusLine("Chat model", model2);
1894
+ printStatusLine("Embedding model", embeddingModel);
1895
+ printStatusLine("Ollama URL", ollamaUrl);
1896
+ console.log();
1897
+ let ok = true;
1898
+ const dbError = await verifyDatabaseConnection(dbUrl);
1899
+ if (dbError) {
1900
+ ok = false;
1901
+ console.log(chalk3.red(" \u2717 PostgreSQL ") + chalk3.dim(dbError));
1902
+ } else {
1903
+ console.log(chalk3.green(" \u2713 PostgreSQL ") + chalk3.dim("connected"));
1904
+ }
1905
+ const ollamaError = await verifyOllamaConnection(ollamaUrl);
1906
+ if (ollamaError) {
1907
+ ok = false;
1908
+ console.log(chalk3.red(" \u2717 Ollama ") + chalk3.dim(ollamaError));
1909
+ } else {
1910
+ console.log(chalk3.green(" \u2713 Ollama ") + chalk3.dim("reachable"));
1911
+ }
1912
+ if (!ollamaError) {
1913
+ try {
1914
+ const installedEmbeddingModel = await resolveOllamaInstalledModel(ollamaUrl, embeddingModel);
1915
+ if (installedEmbeddingModel) {
1916
+ console.log(chalk3.green(" \u2713 Embedding ") + chalk3.dim(`${installedEmbeddingModel} installed`));
1917
+ } else {
1918
+ ok = false;
1919
+ console.log(chalk3.red(" \u2717 Embedding ") + chalk3.dim(`${embeddingModel} not installed in Ollama`));
1920
+ }
1921
+ } catch (err) {
1922
+ ok = false;
1923
+ console.log(chalk3.red(" \u2717 Embedding ") + chalk3.dim(err.message));
1924
+ }
1925
+ }
1926
+ if (provider2 === "ollama") {
1927
+ if (ollamaError) {
1928
+ ok = false;
1929
+ console.log(chalk3.red(" \u2717 Chat model ") + chalk3.dim("cannot verify while Ollama is unreachable"));
1930
+ } else {
1931
+ try {
1932
+ const installedChatModel = await resolveOllamaInstalledModel(ollamaUrl, model2);
1933
+ if (installedChatModel) {
1934
+ console.log(chalk3.green(" \u2713 Chat model ") + chalk3.dim(`${installedChatModel} installed`));
1935
+ } else {
1936
+ ok = false;
1937
+ console.log(chalk3.red(" \u2717 Chat model ") + chalk3.dim(`${model2} not installed in Ollama`));
1938
+ }
1939
+ } catch (err) {
1940
+ ok = false;
1941
+ console.log(chalk3.red(" \u2717 Chat model ") + chalk3.dim(err.message));
1942
+ }
1943
+ }
1944
+ } else {
1945
+ if (!values.CHAT_API_KEY) {
1946
+ ok = false;
1947
+ console.log(chalk3.red(` \u2717 ${providerLabel(provider2)} API`) + chalk3.dim(" CHAT_API_KEY is missing"));
1948
+ } else {
1949
+ console.log(chalk3.green(` \u2713 ${providerLabel(provider2)} API`) + chalk3.dim(" key configured"));
1950
+ console.log(chalk3.gray(" Remote provider connectivity is not verified live by doctor."));
1951
+ }
1952
+ }
1953
+ console.log();
1954
+ return { ok, provider: provider2, model: model2 };
1955
+ }
1956
+ async function printProjectStatus() {
1957
+ const config = readProjectConfig();
1958
+ const { envPath, values } = readRuntimeEnv();
1959
+ const provider2 = getConfiguredProvider(values);
1960
+ const model2 = getConfiguredChatModel(values);
1961
+ const architectures = inferProjectArchitectures(process.cwd(), config);
1962
+ const generatedFiles = OUTPUT_FILES.map((entry) => entry.path).filter((relativePath) => existsSync6(join6(process.cwd(), relativePath)));
1963
+ const hookPath = join6(process.cwd(), ".git", "hooks", "pre-commit");
1964
+ const memoryFilePath = join6(process.cwd(), MEMORY_FILE);
1965
+ const statsPath = join6(process.cwd(), ".memory-core-stats.json");
1966
+ const dbError = await verifyDatabaseConnection(values.DATABASE_URL ?? "");
1967
+ const ollamaError = await verifyOllamaConnection(values.OLLAMA_URL ?? DEFAULT_OLLAMA_URL);
1968
+ console.log(chalk3.bold("\n memory-core status\n"));
1969
+ printStatusLine("Project", config?.projectName ?? process.cwd().split("/").pop() ?? "unknown");
1970
+ printStatusLine("Project type", config?.projectType ?? chalk3.yellow("not initialized"));
1971
+ printStatusLine("Language", config?.language ?? detectProject().language);
1972
+ printStatusLine("Backend arch", config?.backendArchitecture ?? chalk3.gray("\u2014"));
1973
+ printStatusLine("Frontend fw", config?.frontendFramework ?? chalk3.gray("\u2014"));
1974
+ printStatusLine("Architectures", architectures.length ? architectures.join(", ") : chalk3.gray("none detected"));
1975
+ printStatusLine("Agents", config?.agents?.length ? `${config.agents.length} selected` : chalk3.gray("none saved"));
1976
+ printStatusLine("Caveman", config?.caveman?.enabled ? `enabled (${config.caveman.intensity})` : "disabled");
1977
+ printStatusLine("Allow patterns", String(getAllowPatterns(config).length));
1978
+ printStatusLine("Env file", `${existsSync6(envPath) ? "present" : "missing"} (${envPath.split("/").pop()})`);
1979
+ printStatusLine("Memory file", existsSync6(memoryFilePath) ? MEMORY_FILE : chalk3.gray("not exported"));
1980
+ printStatusLine("Project config", existsSync6(join6(process.cwd(), CONFIG_FILE)) ? CONFIG_FILE : chalk3.gray("missing"));
1981
+ printStatusLine("Generated files", String(generatedFiles.length));
1982
+ printStatusLine("Hook", existsSync6(hookPath) ? "installed" : "not installed");
1983
+ printStatusLine("Stats file", existsSync6(statsPath) ? ".memory-core-stats.json" : chalk3.gray("none"));
1984
+ console.log();
1985
+ printStatusLine("Database URL", redactDatabaseUrl(values.DATABASE_URL ?? ""));
1986
+ printStatusLine("Ollama URL", values.OLLAMA_URL ?? DEFAULT_OLLAMA_URL);
1987
+ printStatusLine("Embedding model", values.OLLAMA_MODEL ?? DEFAULT_EMBEDDING_MODEL);
1988
+ printStatusLine("Chat provider", provider2);
1989
+ printStatusLine("Chat model", model2);
1990
+ console.log();
1991
+ console.log(
1992
+ dbError ? chalk3.red(" \u2717 PostgreSQL ") + chalk3.dim(dbError) : chalk3.green(" \u2713 PostgreSQL ") + chalk3.dim("connected")
1993
+ );
1994
+ console.log(
1995
+ ollamaError ? chalk3.red(" \u2717 Ollama ") + chalk3.dim(ollamaError) : chalk3.green(" \u2713 Ollama ") + chalk3.dim("reachable")
1996
+ );
1997
+ if (provider2 !== "ollama") {
1998
+ console.log(
1999
+ values.CHAT_API_KEY ? chalk3.green(` \u2713 ${providerLabel(provider2)} API`) + chalk3.dim(" key configured") : chalk3.red(` \u2717 ${providerLabel(provider2)} API`) + chalk3.dim(" CHAT_API_KEY is missing")
2000
+ );
2001
+ }
2002
+ console.log();
2003
+ }
1747
2004
  var program = new Command();
1748
2005
  program.name("memory-core").description("Universal AI memory core \u2014 generate AI context files for all coding agents").version(version);
1749
2006
  program.command("init").description("Initialize memory-core in the current project").option("--quick", "Use smart defaults and skip optional prompts").action(async (opts) => {
@@ -1755,25 +2012,19 @@ program.command("init").description("Initialize memory-core in the current proje
1755
2012
  if (!hasEnv && quick) {
1756
2013
  const dbUser = process.env.USER ?? process.env.USERNAME ?? "postgres";
1757
2014
  const dbUrl = `postgresql://${dbUser}@localhost:5432/memory_core`;
1758
- const ollamaUrl = "http://localhost:11434";
1759
- const chatModel = "llama3.2";
1760
- const envContent = [
1761
- `DATABASE_URL=${dbUrl}`,
1762
- `OLLAMA_URL=${ollamaUrl}`,
1763
- `OLLAMA_MODEL=nomic-embed-text`,
1764
- `OLLAMA_CHAT_MODEL=${chatModel}`
1765
- ].join("\n") + "\n";
1766
- writeFileSync5(envPath, envContent);
1767
- process.env.DATABASE_URL = dbUrl;
1768
- process.env.OLLAMA_URL = ollamaUrl;
1769
- process.env.OLLAMA_MODEL = "nomic-embed-text";
1770
- process.env.OLLAMA_CHAT_MODEL = chatModel;
1771
- const gitignorePath2 = join6(process.cwd(), ".gitignore");
1772
- const gitignore = existsSync6(gitignorePath2) ? readFileSync6(gitignorePath2, "utf-8") : "";
1773
- if (!gitignore.includes(".memory-core.env")) {
1774
- appendFileSync(gitignorePath2, `${gitignore ? "\n" : ""}.memory-core.env
1775
- `);
1776
- }
2015
+ const ollamaUrl = DEFAULT_OLLAMA_URL;
2016
+ const chatModel = DEFAULT_CHAT_MODEL;
2017
+ const envValues = {
2018
+ DATABASE_URL: dbUrl,
2019
+ OLLAMA_URL: ollamaUrl,
2020
+ OLLAMA_MODEL: DEFAULT_EMBEDDING_MODEL,
2021
+ CHAT_PROVIDER: "ollama",
2022
+ CHAT_MODEL: chatModel,
2023
+ OLLAMA_CHAT_MODEL: chatModel
2024
+ };
2025
+ writeRuntimeEnv(envValues, envPath);
2026
+ applyRuntimeEnv(envValues);
2027
+ ensureEnvFileIgnored(envPath);
1777
2028
  console.log(chalk3.green(" \u2713 .memory-core.env created with local defaults"));
1778
2029
  } else if (!hasEnv) {
1779
2030
  console.log(chalk3.dim(" No .memory-core.env found \u2014 let's set up your connection.\n"));
@@ -1889,33 +2140,18 @@ program.command("init").description("Initialize memory-core in the current proje
1889
2140
  });
1890
2141
  console.log(chalk3.green(` \u2713 ${chatProvider} / ${chatModel} configured`));
1891
2142
  }
1892
- const envLines = [
1893
- `DATABASE_URL=${dbUrl}`,
1894
- `OLLAMA_URL=${ollamaUrl}`,
1895
- `OLLAMA_MODEL=nomic-embed-text`,
1896
- `CHAT_PROVIDER=${chatProvider}`,
1897
- `CHAT_MODEL=${chatModel}`
1898
- ];
1899
- if (chatProvider === "ollama") envLines.push(`OLLAMA_CHAT_MODEL=${chatModel}`);
1900
- if (chatApiKey) envLines.push(`CHAT_API_KEY=${chatApiKey}`);
1901
- const envContent = envLines.join("\n") + "\n";
1902
- writeFileSync5(envPath, envContent);
1903
- process.env.DATABASE_URL = dbUrl;
1904
- process.env.OLLAMA_URL = ollamaUrl;
1905
- process.env.OLLAMA_MODEL = "nomic-embed-text";
1906
- process.env.CHAT_PROVIDER = chatProvider;
1907
- process.env.CHAT_MODEL = chatModel;
1908
- if (chatProvider === "ollama") process.env.OLLAMA_CHAT_MODEL = chatModel;
1909
- if (chatApiKey) process.env.CHAT_API_KEY = chatApiKey;
1910
- const gitignorePath2 = join6(process.cwd(), ".gitignore");
1911
- if (existsSync6(gitignorePath2)) {
1912
- const gi = readFileSync6(gitignorePath2, "utf-8");
1913
- if (!gi.includes(".memory-core.env")) {
1914
- appendFileSync(gitignorePath2, "\n.memory-core.env\n");
1915
- }
1916
- } else {
1917
- writeFileSync5(gitignorePath2, ".memory-core.env\n");
1918
- }
2143
+ const envValues = {
2144
+ DATABASE_URL: dbUrl,
2145
+ OLLAMA_URL: ollamaUrl,
2146
+ OLLAMA_MODEL: DEFAULT_EMBEDDING_MODEL,
2147
+ CHAT_PROVIDER: chatProvider,
2148
+ CHAT_MODEL: chatModel
2149
+ };
2150
+ if (chatProvider === "ollama") envValues.OLLAMA_CHAT_MODEL = chatModel;
2151
+ if (chatApiKey) envValues.CHAT_API_KEY = chatApiKey;
2152
+ writeRuntimeEnv(envValues, envPath);
2153
+ applyRuntimeEnv(envValues);
2154
+ ensureEnvFileIgnored(envPath);
1919
2155
  console.log(chalk3.green("\n \u2713 .memory-core.env created"));
1920
2156
  console.log(chalk3.gray(" Added to .gitignore \u2014 your DB credentials stay local.\n"));
1921
2157
  }
@@ -2617,6 +2853,82 @@ read:
2617
2853
  console.log(chalk3.bold("\n Every AI agent now follows your memory globally.\n"));
2618
2854
  await closePool();
2619
2855
  });
2856
+ var provider = program.command("provider").description("Manage the code-checking provider configuration");
2857
+ provider.command("set <name>").description("Set the code-checking provider (ollama, openai, anthropic, minimax)").option("--model <model>", "Chat model to set alongside the provider").option("--api-key <key>", "API key for cloud providers").action(async (name, opts) => {
2858
+ try {
2859
+ const providerName = normalizeProvider(name);
2860
+ const runtimeEnv = readRuntimeEnv();
2861
+ const values = { ...runtimeEnv.values };
2862
+ values.CHAT_PROVIDER = providerName;
2863
+ values.OLLAMA_URL = values.OLLAMA_URL ?? DEFAULT_OLLAMA_URL;
2864
+ values.OLLAMA_MODEL = values.OLLAMA_MODEL ?? DEFAULT_EMBEDDING_MODEL;
2865
+ if (opts.model) {
2866
+ values.CHAT_MODEL = opts.model;
2867
+ } else if (!values.CHAT_MODEL && !values.OLLAMA_CHAT_MODEL) {
2868
+ values.CHAT_MODEL = DEFAULT_CHAT_MODEL;
2869
+ }
2870
+ if (providerName === "ollama") {
2871
+ const model2 = values.CHAT_MODEL ?? values.OLLAMA_CHAT_MODEL ?? DEFAULT_CHAT_MODEL;
2872
+ values.CHAT_MODEL = model2;
2873
+ values.OLLAMA_CHAT_MODEL = model2;
2874
+ delete values.CHAT_API_KEY;
2875
+ } else {
2876
+ delete values.OLLAMA_CHAT_MODEL;
2877
+ if (opts.apiKey) {
2878
+ values.CHAT_API_KEY = opts.apiKey;
2879
+ } else if (!values.CHAT_API_KEY) {
2880
+ values.CHAT_API_KEY = await input({
2881
+ message: `${providerLabel(providerName)} API key?`
2882
+ });
2883
+ }
2884
+ }
2885
+ writeRuntimeEnv(values, runtimeEnv.envPath);
2886
+ applyRuntimeEnv(values);
2887
+ ensureEnvFileIgnored(runtimeEnv.envPath);
2888
+ console.log(chalk3.green(`Updated provider: ${providerName}`));
2889
+ console.log(chalk3.gray(` Chat model: ${getConfiguredChatModel(values)}`));
2890
+ } catch (err) {
2891
+ console.error(chalk3.red(`Provider update failed: ${err.message}`));
2892
+ process.exit(1);
2893
+ }
2894
+ });
2895
+ var model = program.command("model").description("Manage code-checking and embedding models");
2896
+ model.command("set <name>").description("Set the chat model used for code checking").option("--provider <provider>", "Override provider while setting the model").option("--embedding", "Set the embedding model instead of the chat model").action(async (name, opts) => {
2897
+ try {
2898
+ const runtimeEnv = readRuntimeEnv();
2899
+ const values = { ...runtimeEnv.values };
2900
+ if (opts.provider) values.CHAT_PROVIDER = normalizeProvider(opts.provider);
2901
+ const providerName = getConfiguredProvider(values);
2902
+ if (opts.embedding) {
2903
+ values.OLLAMA_MODEL = name;
2904
+ } else {
2905
+ values.CHAT_MODEL = name;
2906
+ if (providerName === "ollama") values.OLLAMA_CHAT_MODEL = name;
2907
+ }
2908
+ values.OLLAMA_URL = values.OLLAMA_URL ?? DEFAULT_OLLAMA_URL;
2909
+ values.OLLAMA_MODEL = values.OLLAMA_MODEL ?? DEFAULT_EMBEDDING_MODEL;
2910
+ writeRuntimeEnv(values, runtimeEnv.envPath);
2911
+ applyRuntimeEnv(values);
2912
+ ensureEnvFileIgnored(runtimeEnv.envPath);
2913
+ console.log(chalk3.green(`Updated ${opts.embedding ? "embedding" : "chat"} model: ${name}`));
2914
+ console.log(chalk3.gray(` Provider: ${providerName}`));
2915
+ } catch (err) {
2916
+ console.error(chalk3.red(`Model update failed: ${err.message}`));
2917
+ process.exit(1);
2918
+ }
2919
+ });
2920
+ model.command("doctor").description("Verify provider, model, database, and Ollama setup").action(async () => {
2921
+ const result = await runModelDoctor();
2922
+ if (!result.ok) process.exit(1);
2923
+ });
2924
+ program.command("status").description("Show the current memory-core project and runtime setup").action(async () => {
2925
+ try {
2926
+ await printProjectStatus();
2927
+ } catch (err) {
2928
+ console.error(chalk3.red(`Status failed: ${err.message}`));
2929
+ process.exit(1);
2930
+ }
2931
+ });
2620
2932
  var hook = program.command("hook").description("Manage the pre-commit rule enforcement hook");
2621
2933
  hook.command("install").description("Install pre-commit hook (advisory mode by default \u2014 logs violations, never blocks)").option("--advisory", "Log violations but never block commits (default)").option("--strict", "Block commits that violate your rules").action((opts) => {
2622
2934
  const advisory = opts.strict ? false : true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shahmilsaari/memory-core",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "Universal AI memory core — generate AI context files from architecture profiles with RAG support",
5
5
  "type": "module",
6
6
  "bin": {