prism-mcp-server 20.7.1 → 20.8.0
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 +24 -0
- package/dist/connect.js +89 -1
- package/dist/storage/index.js +57 -5
- package/dist/tools/ledgerHandlers.js +68 -0
- package/package.json +9 -59
package/README.md
CHANGED
|
@@ -56,6 +56,30 @@ Prism works locally without an account, API key, or cloud subscription. Add a
|
|
|
56
56
|
Synalux subscription when you want cloud memory, paid-tier skills, or team
|
|
57
57
|
features.
|
|
58
58
|
|
|
59
|
+
### Install as a plugin
|
|
60
|
+
|
|
61
|
+
Prism also ships as a plugin, which registers the MCP server and the startup
|
|
62
|
+
skill for you.
|
|
63
|
+
|
|
64
|
+
**Claude Code** — from the community marketplace:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
/plugin marketplace add anthropics/claude-plugins-community
|
|
68
|
+
/plugin install synalux-prism@claude-community
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Codex** — this repository is itself a plugin marketplace:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
codex plugin marketplace add dcostenco/prism-coder
|
|
75
|
+
codex plugin add synalux-prism@prism
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The plugin registers `prism-mcp` via `npx -y prism-mcp-server`. If you already
|
|
79
|
+
configured Prism by hand — `prism connect` writes an `mcp_servers.prism-mcp`
|
|
80
|
+
entry — you have that server twice under one key. Install the plugin **or**
|
|
81
|
+
run `prism connect`, not both.
|
|
82
|
+
|
|
59
83
|
### What `prism connect` changes about host subagents
|
|
60
84
|
|
|
61
85
|
`connect` steers bounded work to `prism_infer` on your machine rather than to
|
package/dist/connect.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { accessSync, closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
1
|
+
import { accessSync, closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep, win32 as win32Path } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -1287,6 +1287,29 @@ function registerCodexTomlHost(definition, entry, dryRun, refresh, beforeCommit)
|
|
|
1287
1287
|
return result(definition, "error", `could not parse config: ${error instanceof Error ? error.message : String(error)}`);
|
|
1288
1288
|
}
|
|
1289
1289
|
}
|
|
1290
|
+
// A Codex plugin may already register prism-mcp. Writing our own entry would
|
|
1291
|
+
// configure the same server twice under one key, so every Prism tool appears
|
|
1292
|
+
// in duplicate and load order decides which wins. Skip instead, and say why —
|
|
1293
|
+
// an unexplained no-op is worse than the duplicate it prevents.
|
|
1294
|
+
// Build the set of plugins Codex will actually load: present in [plugins]
|
|
1295
|
+
// and not explicitly disabled. Detection must not fire on a stale cache
|
|
1296
|
+
// entry for a plugin that is disabled or gone.
|
|
1297
|
+
const enabledPluginKeys = new Set();
|
|
1298
|
+
const pluginsTable = isJsonObject(config) ? config.plugins : undefined;
|
|
1299
|
+
if (isJsonObject(pluginsTable)) {
|
|
1300
|
+
for (const [key, entry] of Object.entries(pluginsTable)) {
|
|
1301
|
+
if (isJsonObject(entry) && entry.enabled !== false)
|
|
1302
|
+
enabledPluginKeys.add(key);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
const providingPlugin = codexPluginProvidesPrismMcp(dirname(configPath), enabledPluginKeys);
|
|
1306
|
+
if (providingPlugin && !locateCodexManagedBlock(originalText ?? "")) {
|
|
1307
|
+
// "existing" rather than a new status: the server IS already registered,
|
|
1308
|
+
// just by the plugin rather than by us. Nothing to do is the truth.
|
|
1309
|
+
return result(definition, "existing", `the Codex plugin ${providingPlugin} already registers prism-mcp; ` +
|
|
1310
|
+
"not adding a second registration under the same key. Remove the plugin " +
|
|
1311
|
+
"if you would rather prism connect manage this server.");
|
|
1312
|
+
}
|
|
1290
1313
|
let managedBlock;
|
|
1291
1314
|
try {
|
|
1292
1315
|
managedBlock = locateCodexManagedBlock(originalText ?? "");
|
|
@@ -1377,6 +1400,71 @@ function serializeCodexManagedBlock(entry, existingText) {
|
|
|
1377
1400
|
const serialized = stringifyToml({ mcp_servers: { "prism-mcp": entry } }).trimEnd();
|
|
1378
1401
|
return `${CODEX_MANAGED_START}\n${serialized}\n${CODEX_MANAGED_END}\n`.replaceAll("\n", newline);
|
|
1379
1402
|
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Is a Codex plugin already providing the prism-mcp server?
|
|
1405
|
+
*
|
|
1406
|
+
* Installing the plugin and running `prism connect` both register a server
|
|
1407
|
+
* under the key `prism-mcp`, so doing both leaves the same server configured
|
|
1408
|
+
* twice — every Prism tool appears in duplicate, and which one wins depends on
|
|
1409
|
+
* load order. Previously this was only documented ("install the plugin OR run
|
|
1410
|
+
* prism connect, not both"), which puts the burden on the user to remember.
|
|
1411
|
+
*
|
|
1412
|
+
* Detection reads the installed plugin's own manifest rather than matching a
|
|
1413
|
+
* plugin NAME, so it keeps working if the plugin is renamed or vendored:
|
|
1414
|
+
* <codexHome>/plugins/cache/<marketplace>/<plugin>/<version>/.mcp.json
|
|
1415
|
+
*
|
|
1416
|
+
* A cache manifest is necessary but NOT sufficient: a disabled or half-removed
|
|
1417
|
+
* plugin leaves its .mcp.json on disk (verified 2026-08-06 — flipping
|
|
1418
|
+
* `enabled = false` in config.toml does not clear the cache). Counting file
|
|
1419
|
+
* presence alone would make `prism connect` skip its own registration for a
|
|
1420
|
+
* plugin that is not actually providing the server, leaving the user with no
|
|
1421
|
+
* prism-mcp at all. So the plugin@marketplace key must ALSO be enabled in the
|
|
1422
|
+
* caller's parsed config for it to count.
|
|
1423
|
+
*/
|
|
1424
|
+
export function codexPluginProvidesPrismMcp(codexHome, enabledPluginKeys) {
|
|
1425
|
+
const cacheRoot = join(codexHome, "plugins", "cache");
|
|
1426
|
+
let marketplaces;
|
|
1427
|
+
try {
|
|
1428
|
+
marketplaces = readdirSync(cacheRoot);
|
|
1429
|
+
}
|
|
1430
|
+
catch {
|
|
1431
|
+
return null; // no plugins installed
|
|
1432
|
+
}
|
|
1433
|
+
for (const marketplace of marketplaces) {
|
|
1434
|
+
let plugins;
|
|
1435
|
+
try {
|
|
1436
|
+
plugins = readdirSync(join(cacheRoot, marketplace));
|
|
1437
|
+
}
|
|
1438
|
+
catch {
|
|
1439
|
+
continue;
|
|
1440
|
+
}
|
|
1441
|
+
for (const plugin of plugins) {
|
|
1442
|
+
let versions;
|
|
1443
|
+
try {
|
|
1444
|
+
versions = readdirSync(join(cacheRoot, marketplace, plugin));
|
|
1445
|
+
}
|
|
1446
|
+
catch {
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
for (const version of versions) {
|
|
1450
|
+
const manifest = join(cacheRoot, marketplace, plugin, version, ".mcp.json");
|
|
1451
|
+
try {
|
|
1452
|
+
const parsed = JSON.parse(readFileSync(manifest, "utf8"));
|
|
1453
|
+
const key = `${plugin}@${marketplace}`;
|
|
1454
|
+
if (enabledPluginKeys.has(key) &&
|
|
1455
|
+
parsed?.mcpServers &&
|
|
1456
|
+
Object.prototype.hasOwnProperty.call(parsed.mcpServers, "prism-mcp")) {
|
|
1457
|
+
return key;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
catch {
|
|
1461
|
+
// absent or unreadable manifest — not a provider
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return null;
|
|
1467
|
+
}
|
|
1380
1468
|
function validateCodexCandidate(text) {
|
|
1381
1469
|
const parsed = parseToml(text);
|
|
1382
1470
|
if (!isJsonObject(parsed) || !isJsonObject(parsed.mcp_servers)) {
|
package/dist/storage/index.js
CHANGED
|
@@ -5,12 +5,60 @@ import { getSetting } from "./configStorage.js";
|
|
|
5
5
|
export function isValidHttpUrl(url) {
|
|
6
6
|
try {
|
|
7
7
|
const parsed = new URL(url);
|
|
8
|
-
|
|
8
|
+
if (parsed.protocol === "https:")
|
|
9
|
+
return true;
|
|
10
|
+
// Plain http is accepted ONLY for loopback, where the traffic never leaves
|
|
11
|
+
// the machine — the local Supabase stack runs on 127.0.0.1:54321.
|
|
12
|
+
//
|
|
13
|
+
// Every caller of this function is gating a CLOUD backend, so accepting
|
|
14
|
+
// http for a remote host meant session content — summaries, decisions,
|
|
15
|
+
// filenames — could be sent unencrypted. The privacy policy states this
|
|
16
|
+
// traffic travels over TLS; before this change that was true only because
|
|
17
|
+
// the default base URL happens to be https, not because anything enforced
|
|
18
|
+
// it. A published claim should be guaranteed by the code, not by a default
|
|
19
|
+
// the user can silently override.
|
|
20
|
+
if (parsed.protocol !== "http:")
|
|
21
|
+
return false;
|
|
22
|
+
const host = parsed.hostname;
|
|
23
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
9
24
|
}
|
|
10
25
|
catch {
|
|
11
26
|
return false;
|
|
12
27
|
}
|
|
13
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Upgrade a remote http:// cloud URL to https:// rather than rejecting it.
|
|
31
|
+
*
|
|
32
|
+
* Rejecting was the first fix for the plaintext gap, and it closed the hole
|
|
33
|
+
* but reported it badly: a user who set an http base URL got "credentials are
|
|
34
|
+
* missing or invalid", which says nothing about the protocol being the
|
|
35
|
+
* problem. Upgrading keeps the guarantee — session content never leaves over
|
|
36
|
+
* plaintext — while letting a working configuration keep working.
|
|
37
|
+
*
|
|
38
|
+
* Loopback is left alone: http on 127.0.0.1 never crosses a network, and the
|
|
39
|
+
* local Supabase stack serves plain http on 54321.
|
|
40
|
+
*
|
|
41
|
+
* The upgrade is announced, not silent. If the host genuinely has no TLS
|
|
42
|
+
* listener the connection fails afterwards, and the log line is what explains
|
|
43
|
+
* why.
|
|
44
|
+
*/
|
|
45
|
+
export function upgradeInsecureCloudUrl(raw) {
|
|
46
|
+
try {
|
|
47
|
+
const parsed = new URL(raw);
|
|
48
|
+
if (parsed.protocol !== "http:")
|
|
49
|
+
return raw;
|
|
50
|
+
const host = parsed.hostname;
|
|
51
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]")
|
|
52
|
+
return raw;
|
|
53
|
+
parsed.protocol = "https:";
|
|
54
|
+
const upgraded = parsed.toString().replace(/\/+$/, "");
|
|
55
|
+
debugLog(`[Prism Storage] Upgraded ${raw} to ${upgraded}: session content is never sent over plaintext to a remote host.`);
|
|
56
|
+
return upgraded;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return raw;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
14
62
|
/**
|
|
15
63
|
* Probe for synalux credentials: env vars first, then config DB.
|
|
16
64
|
* Returns true if usable credentials are now in process.env.
|
|
@@ -21,12 +69,14 @@ export async function ensureSynaluxCredentials() {
|
|
|
21
69
|
// Re-check process.env directly: SYNALUX_CONFIGURED is captured at module
|
|
22
70
|
// load, so credentials injected later by another caller would be invisible
|
|
23
71
|
// to it. Mirrors ensureSupabaseCredentials below.
|
|
24
|
-
const
|
|
72
|
+
const rawEnvUrl = process.env.PRISM_SYNALUX_BASE_URL?.trim() || process.env.SYNALUX_BASE_URL?.trim();
|
|
73
|
+
const envUrl = rawEnvUrl ? upgradeInsecureCloudUrl(rawEnvUrl) : rawEnvUrl;
|
|
25
74
|
const envKey = process.env.PRISM_SYNALUX_API_KEY?.trim();
|
|
26
75
|
if (envUrl && envKey && isValidHttpUrl(envUrl))
|
|
27
76
|
return true;
|
|
28
|
-
const
|
|
77
|
+
const rawUrl = (await getSetting("PRISM_SYNALUX_BASE_URL"))?.trim() ||
|
|
29
78
|
(await getSetting("SYNALUX_BASE_URL"))?.trim();
|
|
79
|
+
const url = rawUrl ? upgradeInsecureCloudUrl(rawUrl) : rawUrl;
|
|
30
80
|
const key = (await getSetting("PRISM_SYNALUX_API_KEY"))?.trim();
|
|
31
81
|
if (url && key && isValidHttpUrl(url)) {
|
|
32
82
|
process.env.PRISM_SYNALUX_BASE_URL = url;
|
|
@@ -43,11 +93,13 @@ export async function ensureSynaluxCredentials() {
|
|
|
43
93
|
async function ensureSupabaseCredentials() {
|
|
44
94
|
if (SUPABASE_CONFIGURED)
|
|
45
95
|
return true;
|
|
46
|
-
const
|
|
96
|
+
const rawEnvUrl = process.env.SUPABASE_URL?.trim();
|
|
97
|
+
const envUrl = rawEnvUrl ? upgradeInsecureCloudUrl(rawEnvUrl) : rawEnvUrl;
|
|
47
98
|
const envKey = process.env.SUPABASE_KEY?.trim();
|
|
48
99
|
if (envUrl && envKey && isValidHttpUrl(envUrl))
|
|
49
100
|
return true;
|
|
50
|
-
const
|
|
101
|
+
const rawUrl = (await getSetting("SUPABASE_URL"))?.trim();
|
|
102
|
+
const url = rawUrl ? upgradeInsecureCloudUrl(rawUrl) : rawUrl;
|
|
51
103
|
const key = (await getSetting("SUPABASE_KEY"))?.trim();
|
|
52
104
|
if (url && key && isValidHttpUrl(url)) {
|
|
53
105
|
process.env.SUPABASE_URL = url;
|
|
@@ -1689,6 +1689,64 @@ async function createLocalStartupStorage() {
|
|
|
1689
1689
|
await storage.initialize(true);
|
|
1690
1690
|
return storage;
|
|
1691
1691
|
}
|
|
1692
|
+
/** Project name for the first-run demo memory. Its own project so it can never
|
|
1693
|
+
* mix with real work, and trivially removable as a unit. */
|
|
1694
|
+
const DEMO_PROJECT = "prism-demo";
|
|
1695
|
+
/**
|
|
1696
|
+
* First-run seed-and-show: write one demo ledger entry, then READ IT BACK
|
|
1697
|
+
* through the storage layer and render from the read-back row.
|
|
1698
|
+
*
|
|
1699
|
+
* The read-back is the point. Rendering the local variable would prove
|
|
1700
|
+
* nothing — a broken storage backend would still print a convincing
|
|
1701
|
+
* "recalled" block. Rendering only what getLedgerEntries returned means the
|
|
1702
|
+
* block the user sees IS evidence the save→recall loop works on their
|
|
1703
|
+
* machine. If either half fails we return null and the greeting simply
|
|
1704
|
+
* omits the demo — a first run must never break on a demo.
|
|
1705
|
+
*/
|
|
1706
|
+
export async function seedAndRecallDemoMemory(conversationId) {
|
|
1707
|
+
try {
|
|
1708
|
+
const storage = await getStorage();
|
|
1709
|
+
// Idempotence before insert: the first_bootstrap_at marker is
|
|
1710
|
+
// check-then-set, so two hosts bootstrapping a fresh machine at once BOTH
|
|
1711
|
+
// take the first-run branch (observed setups run several agents
|
|
1712
|
+
// concurrently). Seeding only when no demo row exists narrows that race
|
|
1713
|
+
// from "every concurrent first run inserts" to a near-simultaneous
|
|
1714
|
+
// read-read window, and the loser still renders the winner's row — the
|
|
1715
|
+
// user sees one demo either way.
|
|
1716
|
+
const existing = (await storage.getLedgerEntries({
|
|
1717
|
+
project: `eq.${DEMO_PROJECT}`,
|
|
1718
|
+
user_id: `eq.${PRISM_USER_ID}`,
|
|
1719
|
+
limit: "1",
|
|
1720
|
+
}));
|
|
1721
|
+
if (existing.length === 0) {
|
|
1722
|
+
await storage.saveLedger({
|
|
1723
|
+
project: DEMO_PROJECT,
|
|
1724
|
+
conversation_id: conversationId,
|
|
1725
|
+
user_id: PRISM_USER_ID,
|
|
1726
|
+
summary: "Prism saved this memory during your first session to demonstrate recall.",
|
|
1727
|
+
todos: ["Try it yourself: ask your agent to `session_save_ledger` at the end of this session"],
|
|
1728
|
+
decisions: ["Demo memory — delete anytime with session_forget_memory or from the dashboard"],
|
|
1729
|
+
keywords: ["demo", "first-run"],
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
const rows = (await storage.getLedgerEntries({
|
|
1733
|
+
project: `eq.${DEMO_PROJECT}`,
|
|
1734
|
+
user_id: `eq.${PRISM_USER_ID}`,
|
|
1735
|
+
order: "created_at.desc",
|
|
1736
|
+
limit: "1",
|
|
1737
|
+
}));
|
|
1738
|
+
const recalled = rows[0];
|
|
1739
|
+
if (!recalled?.summary)
|
|
1740
|
+
return null;
|
|
1741
|
+
const todo = Array.isArray(recalled.todos) && recalled.todos[0] ? `\n - TODO it carried: ${recalled.todos[0]}` : "";
|
|
1742
|
+
return (`- 🧠 **Watch this — Prism just saved a memory and recalled it from disk:**\n` +
|
|
1743
|
+
` - "${recalled.summary}"${todo}\n` +
|
|
1744
|
+
` - This round-trip is what every future session gets: your decisions, TODOs, and changed files, back the moment you return. (Demo lives in the \`${DEMO_PROJECT}\` project — delete it anytime.)`);
|
|
1745
|
+
}
|
|
1746
|
+
catch {
|
|
1747
|
+
return null;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1692
1750
|
export async function sessionBootstrapHandler(args = {}, options = {}) {
|
|
1693
1751
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
1694
1752
|
throw new Error("Invalid arguments for session_bootstrap");
|
|
@@ -1759,7 +1817,17 @@ export async function sessionBootstrapHandler(args = {}, options = {}) {
|
|
|
1759
1817
|
if (isFirstRun) {
|
|
1760
1818
|
// Action-first instead of absence-first: every line is a capability or
|
|
1761
1819
|
// a next step. The wizard exists and is well-built; route to it.
|
|
1820
|
+
//
|
|
1821
|
+
// Seed-and-show: a memory product's payoff is structurally deferred to
|
|
1822
|
+
// session 2 — "it remembered" can't be felt until you come back. So the
|
|
1823
|
+
// first run seeds one clearly-marked demo memory and shows it RECALLED,
|
|
1824
|
+
// read back through the real storage layer (not string theater), so the
|
|
1825
|
+
// save→recall loop is felt in session 1. The demo lives in its own
|
|
1826
|
+
// project so it never mixes with real work, and this branch only runs
|
|
1827
|
+
// once — the first_bootstrap_at marker above is durable.
|
|
1828
|
+
const demoRecall = await seedAndRecallDemoMemory(conversationId);
|
|
1762
1829
|
const firstRunText = `${greeting}\n\n` +
|
|
1830
|
+
(demoRecall ? `${demoRecall}\n` : "") +
|
|
1763
1831
|
`- 🚀 **Get started:** run the \`onboarding_wizard\` tool (guided setup, ~3 minutes)\n` +
|
|
1764
1832
|
`${dashboardLine}\n` +
|
|
1765
1833
|
`- 💾 **Already working?** \`session_save_ledger\` records this session; the next one resumes with full context\n\n` +
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prism-mcp-server",
|
|
3
|
-
"version": "20.
|
|
3
|
+
"version": "20.8.0",
|
|
4
4
|
"mcpName": "io.github.dcostenco/prism-coder",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Persistent session memory for AI coding agents that never leaves your machine \u2014 including the on-device model that reasons over it. Restores your prior decisions, open TODOs, and changed files across sessions; adds associative recall of related past work, semantic drift detection, and local inference. Local-first by default. Works with Claude Code, Cursor, and Codex.",
|
|
6
6
|
"module": "index.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "dist/server.js",
|
|
@@ -35,68 +35,18 @@
|
|
|
35
35
|
"import": "node dist/utils/universalImporter.js"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|
|
38
|
-
"mcp",
|
|
39
38
|
"mcp-server",
|
|
40
|
-
"model-context-protocol",
|
|
41
|
-
"ai-agent",
|
|
42
39
|
"session-memory",
|
|
43
40
|
"agent-memory",
|
|
44
|
-
"mind-palace",
|
|
45
41
|
"local-first",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"telepathy",
|
|
51
|
-
"morning-briefing",
|
|
52
|
-
"reality-drift",
|
|
53
|
-
"code-mode-templates",
|
|
54
|
-
"semantic-search",
|
|
55
|
-
"vector-search",
|
|
56
|
-
"pgvector",
|
|
57
|
-
"f32-blob",
|
|
58
|
-
"concurrency-control",
|
|
59
|
-
"mcp-prompts",
|
|
60
|
-
"mcp-resources",
|
|
61
|
-
"google-gemini",
|
|
62
|
-
"brave-search",
|
|
63
|
-
"llm",
|
|
64
|
-
"claude-desktop",
|
|
65
|
-
"supabase",
|
|
66
|
-
"progressive-context-loading",
|
|
67
|
-
"knowledge-accumulation",
|
|
68
|
-
"ai-memory",
|
|
69
|
-
"ai-tools",
|
|
70
|
-
"typescript",
|
|
71
|
-
"rag",
|
|
72
|
-
"embeddings",
|
|
73
|
-
"cursor",
|
|
42
|
+
"local-inference",
|
|
43
|
+
"drift-detection",
|
|
44
|
+
"knowledge-graph",
|
|
45
|
+
"claude-code",
|
|
74
46
|
"codex",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"zero-config",
|
|
79
|
-
"auto-capture",
|
|
80
|
-
"dashboard",
|
|
81
|
-
"actr",
|
|
82
|
-
"cognitive-memory",
|
|
83
|
-
"cognitive-architecture",
|
|
84
|
-
"activation-memory",
|
|
85
|
-
"spreading-activation",
|
|
86
|
-
"hebbian-learning",
|
|
87
|
-
"multi-hop-reasoning",
|
|
88
|
-
"rejection-gate",
|
|
89
|
-
"episodic-semantic",
|
|
90
|
-
"dark-factory",
|
|
91
|
-
"autonomous-pipeline",
|
|
92
|
-
"fail-closed",
|
|
93
|
-
"anti-sycophancy",
|
|
94
|
-
"local-embeddings",
|
|
95
|
-
"transformers-js",
|
|
96
|
-
"nomic-embed",
|
|
97
|
-
"business",
|
|
98
|
-
"enterprise",
|
|
99
|
-
"hipaa"
|
|
47
|
+
"cursor",
|
|
48
|
+
"offline",
|
|
49
|
+
"rag"
|
|
100
50
|
],
|
|
101
51
|
"homepage": "https://github.com/dcostenco/prism-coder",
|
|
102
52
|
"repository": {
|