@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.
- package/README.md +33 -6
- package/dist/cli.js +365 -53
- 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.
|
|
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
|
|
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
|
|
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 —
|
|
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
|
|
715
|
-
const
|
|
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(
|
|
1304
|
+
function printModelMissing(model2) {
|
|
1305
1305
|
console.log(chalk.yellow(`
|
|
1306
|
-
\u26A0 Chat model "${
|
|
1307
|
-
console.log(chalk.gray(` Pull a model: ollama pull ${
|
|
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 =
|
|
1759
|
-
const chatModel =
|
|
1760
|
-
const
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
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
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
if (chatProvider === "ollama")
|
|
1900
|
-
if (chatApiKey)
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
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;
|