@gamaze/hicortex 0.7.0 → 0.10.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 +57 -39
- package/dist/claude-md.d.ts +9 -21
- package/dist/claude-md.js +9 -241
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +29 -11
- package/dist/consolidate.js +76 -26
- package/dist/db.js +24 -0
- package/dist/embedder.d.ts +11 -0
- package/dist/embedder.js +27 -0
- package/dist/extensions.d.ts +41 -88
- package/dist/extensions.js +36 -61
- package/dist/features.d.ts +21 -25
- package/dist/features.js +47 -83
- package/dist/graph.d.ts +1 -1
- package/dist/graph.js +13 -7
- package/dist/hermes-transcript-reader.d.ts +27 -0
- package/dist/hermes-transcript-reader.js +134 -0
- package/dist/index.d.ts +16 -4
- package/dist/index.js +252 -344
- package/dist/init.d.ts +41 -1
- package/dist/init.js +545 -190
- package/dist/lesson-selection.d.ts +62 -0
- package/dist/lesson-selection.js +159 -0
- package/dist/lessons-context.d.ts +17 -0
- package/dist/lessons-context.js +96 -0
- package/dist/llm.d.ts +42 -29
- package/dist/llm.js +89 -270
- package/dist/mcp-server.d.ts +0 -1
- package/dist/mcp-server.js +407 -88
- package/dist/nightly.d.ts +9 -6
- package/dist/nightly.js +197 -357
- package/dist/oc-transcript-reader.d.ts +20 -0
- package/dist/oc-transcript-reader.js +61 -0
- package/dist/pi-transcript-reader.d.ts +1 -0
- package/dist/prompts.d.ts +5 -0
- package/dist/prompts.js +29 -0
- package/dist/status.js +22 -2
- package/dist/storage.d.ts +7 -1
- package/dist/storage.js +28 -7
- package/dist/transcript-reader.d.ts +19 -0
- package/dist/transcript-reader.js +17 -3
- package/dist/types.d.ts +16 -0
- package/dist/types.js +7 -0
- package/dist/uninstall.js +31 -1
- package/hermes-plugin/hicortex/README.md +77 -0
- package/hermes-plugin/hicortex/__init__.py +17 -0
- package/hermes-plugin/hicortex/client.py +162 -0
- package/hermes-plugin/hicortex/config.py +105 -0
- package/hermes-plugin/hicortex/plugin.yaml +12 -0
- package/hermes-plugin/hicortex/provider.py +432 -0
- package/openclaw.plugin.json +17 -44
- package/package.json +7 -5
- package/dist/pro-loader.d.ts +0 -33
- package/dist/pro-loader.js +0 -187
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Your agents learn from every session — successes and mistakes. Hicortex captures experiences, distills lessons, and applies them automatically. Connect multiple agents to shared memory and they improve together, overnight.
|
|
4
4
|
|
|
5
|
-
Works with **Claude Code**, **
|
|
5
|
+
Works with **Claude Code**, **Hermes**, **OpenClaw**, **Pi**, and any MCP-compatible agent.
|
|
6
6
|
|
|
7
7
|
**Website:** [hicortex.gamaze.com](https://hicortex.gamaze.com) · **Docs:** [hicortex.gamaze.com/docs](https://hicortex.gamaze.com/docs/)
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ Works with **Claude Code**, **Pi**, **OpenClaw**, and any MCP-compatible agent.
|
|
|
12
12
|
npx @gamaze/hicortex init
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Detects
|
|
15
|
+
Detects available LLM candidates (Ollama models, Claude CLI, API keys from env/Hermes/.env/Claude Code settings/OpenClaw), presents a numbered list, and asks you to choose. Installs a persistent MCP server daemon and registers with Claude Code. One command.
|
|
16
16
|
|
|
17
17
|
## Install — Client Mode (multi-client)
|
|
18
18
|
|
|
@@ -20,28 +20,36 @@ Detects your environment, installs a persistent MCP server daemon, auto-detects
|
|
|
20
20
|
npx @gamaze/hicortex init --server https://your-server.example.com
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Connects to a remote Hicortex server.
|
|
23
|
+
Connects to a remote Hicortex server. No local database or local LLM needed. The nightly job denoises sessions locally (no LLM — just strips tool noise), then POSTs the denoised text to the server. The server distills, embeds, and stores. Raw session content never leaves the machine.
|
|
24
24
|
|
|
25
25
|
## Install — OpenClaw
|
|
26
26
|
|
|
27
|
+
The OC plugin is a recall-only adapter (lessons + memory tools): it requires a Hicortex server. Run the server once on the same machine (or point the plugin at a remote server via `serverUrl` config). Capture happens automatically — the machine's Hicortex nightly reads OpenClaw's session files (`~/.openclaw/agents/*/sessions/`) alongside Claude Code and Hermes sessions.
|
|
28
|
+
|
|
27
29
|
```bash
|
|
30
|
+
# 1. Start the Hicortex server (once, on the machine running OC)
|
|
31
|
+
npx @gamaze/hicortex init
|
|
32
|
+
|
|
33
|
+
# 2. Install the plugin
|
|
28
34
|
openclaw plugins install @gamaze/hicortex
|
|
29
35
|
openclaw gateway restart
|
|
30
36
|
```
|
|
31
37
|
|
|
38
|
+
The plugin connects to `http://127.0.0.1:8787` by default. For a remote server, add `serverUrl` and `authToken` (find the token via `hicortex status` on the server) to the plugin config in `~/.openclaw/openclaw.json`.
|
|
39
|
+
|
|
32
40
|
## Requirements
|
|
33
41
|
|
|
34
42
|
- Node.js 18+
|
|
35
|
-
- LLM
|
|
36
|
-
-
|
|
43
|
+
- **Server mode:** LLM required — Ollama 9b+ (recommended), Claude CLI, or API key (Anthropic, OpenAI, etc.). ~500MB disk for database + embedding model.
|
|
44
|
+
- **Client mode:** No local LLM needed. Node.js 18+ and network access to the server are sufficient.
|
|
45
|
+
- **OC plugin:** Requires a running Hicortex server. No local LLM, database, or embedder in the plugin itself.
|
|
37
46
|
|
|
38
47
|
## What Happens Automatically
|
|
39
48
|
|
|
40
49
|
| When | What | How |
|
|
41
50
|
|------|------|-----|
|
|
42
|
-
| Agent start | Recent lessons injected into context |
|
|
43
|
-
|
|
|
44
|
-
| Nightly | Distill → score → reflect → link → inject | Automatic pipeline |
|
|
51
|
+
| Agent start | Recent lessons fetched fresh and injected into context | CC SessionStart hook (calls `hicortex lessons-context`) / Hermes plugin prefetch / OC `before_agent_start` hook |
|
|
52
|
+
| Nightly | Denoise sessions → POST /distill → server distills + embeds + stores → consolidate (score, reflect, link, decay) | Automatic pipeline — no manual steps |
|
|
45
53
|
|
|
46
54
|
## Agent Tools (MCP)
|
|
47
55
|
|
|
@@ -64,7 +72,8 @@ Skills: `/learn` to save explicit learnings.
|
|
|
64
72
|
npx @gamaze/hicortex server # Start MCP server (port 8787)
|
|
65
73
|
npx @gamaze/hicortex init # Set up server mode
|
|
66
74
|
npx @gamaze/hicortex init --server <url> # Set up client mode
|
|
67
|
-
npx @gamaze/hicortex nightly # Run distill + consolidate
|
|
75
|
+
npx @gamaze/hicortex nightly # Run distill + consolidate (full nightly)
|
|
76
|
+
npx @gamaze/hicortex nightly --capture-only # Capture only, skip consolidation (safe for sub-daily runs)
|
|
68
77
|
npx @gamaze/hicortex nightly --dry-run # Preview without changes
|
|
69
78
|
npx @gamaze/hicortex status # Show config, DB stats
|
|
70
79
|
npx @gamaze/hicortex uninstall # Remove CC integration (keeps DB)
|
|
@@ -77,8 +86,8 @@ npx @gamaze/hicortex uninstall # Remove CC integration (keeps DB
|
|
|
77
86
|
┌──────────┐ ┌──────────────┐ ┌──────────┐
|
|
78
87
|
│CC sessions│ │ Shared DB │ │CC sessions│
|
|
79
88
|
│ ↓ │ POST │ │ POST │ ↓ │
|
|
80
|
-
│
|
|
81
|
-
│ (
|
|
89
|
+
│ Denoise │──/distill──→│Distill+Store │←/distill─│ Denoise │
|
|
90
|
+
│ (no LLM) │ │ ↓ │ │ (no LLM) │
|
|
82
91
|
│ │ MCP │ Consolidate │ MCP │ │
|
|
83
92
|
│ CC ←│──(search)───│ (score,link, │──(search)→│ CC │
|
|
84
93
|
│ │ │ reflect) │ │ │
|
|
@@ -99,15 +108,16 @@ Config at `~/.hicortex/config.json`. Created by `init`. Key options:
|
|
|
99
108
|
|-------|-------------|
|
|
100
109
|
| `mode` | `"server"` (default) or `"client"` |
|
|
101
110
|
| `serverUrl` | Remote server URL (client mode) |
|
|
102
|
-
| `llmModel` | Model for importance scoring |
|
|
103
|
-
| `distillModel` | Model for session distillation
|
|
104
|
-
| `distillBaseUrl` | Separate Ollama instance for distillation |
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `moduleIndexTokenBudget` | Max tokens for domain index in
|
|
111
|
+
| `llmModel` | Model for importance scoring (server mode) |
|
|
112
|
+
| `distillModel` | Model for session distillation — 9b+ recommended (server mode) |
|
|
113
|
+
| `distillBaseUrl` | Separate Ollama instance for distillation (server mode) |
|
|
114
|
+
| `distillFallback` | `"strict"` (default) — abort on remote distill failure, retry next run; `"local"` — fall back to base model (lower quality, 0.9.0 behaviour) |
|
|
115
|
+
| `reflectModel` | Model for nightly reflection — largest available (server mode) |
|
|
116
|
+
| `reflectBaseUrl` | Separate Ollama instance for reflection (server mode) |
|
|
117
|
+
| `authToken` | Bearer token for endpoint auth. Generated on first `init` in server mode. Find the active token with `hicortex status` or in `~/.hicortex/config.json`. |
|
|
118
|
+
| `licenseKey` | Commercial license key (optional; for display in `hicortex status`) |
|
|
119
|
+
| `moduleIndexTokenBudget` | Max tokens for domain index in lessons context (default: 500) |
|
|
120
|
+
| `nightlyHour` | Local hour (0–23) for the nightly job installed by `init` (defaults: client 2, server 3). Applied on fresh installs; existing schedules are never overwritten |
|
|
111
121
|
| `telemetry` | Anonymous usage telemetry, `false` to opt out |
|
|
112
122
|
|
|
113
123
|
Full docs: [hicortex.gamaze.com/docs/configuration.html](https://hicortex.gamaze.com/docs/configuration.html)
|
|
@@ -117,20 +127,19 @@ Full docs: [hicortex.gamaze.com/docs/configuration.html](https://hicortex.gamaze
|
|
|
117
127
|
| Endpoint | Method | Auth | Description |
|
|
118
128
|
|----------|--------|------|-------------|
|
|
119
129
|
| `/health` | GET | No | Server status, memory count, version |
|
|
120
|
-
| `/
|
|
130
|
+
| `/distill` | POST | Yes | Canonical capture endpoint (0.9.0+). Accepts denoised session text (`text` string or `messages` array), distills server-side, stores. Used by both server-mode and client-mode nightly jobs. |
|
|
131
|
+
| `/search` | GET | Yes | Semantic memory search |
|
|
132
|
+
| `/context` | GET | Yes | Recent context memories |
|
|
133
|
+
| `/lessons` | GET | Yes | Lessons + memory index (used by CC SessionStart hook) |
|
|
134
|
+
| `/ingest` | POST | Yes | Legacy: accept a single pre-distilled memory from older clients |
|
|
121
135
|
| `/sse` | GET | Yes | MCP SSE stream for agent connections |
|
|
122
136
|
| `/messages` | POST | Yes | MCP message endpoint |
|
|
123
137
|
|
|
124
|
-
##
|
|
138
|
+
## License
|
|
125
139
|
|
|
126
|
-
|
|
127
|
-
|------|-------|----------|---------|
|
|
128
|
-
| Free | $0 | 250 | Unlimited (trial) |
|
|
129
|
-
| Pro | $9/month | Unlimited | Single |
|
|
130
|
-
| Team | $29/month | Unlimited | Unlimited |
|
|
131
|
-
| Lifetime | $149 | Unlimited | Single |
|
|
140
|
+
**Personal and noncommercial use is free** under the [PolyForm Noncommercial License 1.0.0](https://polyformproject.org/licenses/noncommercial/1.0.0). Commercial use (for-profit businesses, client work, revenue-generating products) requires a per-seat license — see [hicortex.gamaze.com](https://hicortex.gamaze.com).
|
|
132
141
|
|
|
133
|
-
|
|
142
|
+
Versions ≤ 0.7.1 published to npm remain MIT-licensed.
|
|
134
143
|
|
|
135
144
|
## Uninstall
|
|
136
145
|
|
|
@@ -147,19 +156,26 @@ Optional config (add to plugin entry in `~/.openclaw/openclaw.json`):
|
|
|
147
156
|
|
|
148
157
|
| Field | Default | Description |
|
|
149
158
|
|-------|---------|-------------|
|
|
150
|
-
| `
|
|
151
|
-
| `
|
|
152
|
-
| `
|
|
153
|
-
| `llmModel` | _(auto)_ | Override model for scoring and distillation |
|
|
154
|
-
| `reflectModel` | _(auto)_ | Override model for nightly reflection |
|
|
155
|
-
| `consolidateHour` | `2` | Hour (0-23, local time) for nightly consolidation |
|
|
156
|
-
| `dbPath` | _(auto)_ | Custom database path |
|
|
159
|
+
| `serverUrl` | `http://127.0.0.1:8787` | Hicortex server URL. Change for remote servers. |
|
|
160
|
+
| `authToken` | _(none)_ | Bearer token. Localhost bypasses auth; required for remote servers. Get the token from `hicortex status` on the server. |
|
|
161
|
+
| `licenseKey` | _(none)_ | Commercial license key. Optional; only affects the display in `hicortex status`. |
|
|
157
162
|
|
|
158
|
-
|
|
163
|
+
## LLM Configuration
|
|
164
|
+
|
|
165
|
+
LLM selection is **user-controlled**: `npx @gamaze/hicortex init` detects candidates and asks you to choose. Nothing is silently auto-applied at runtime.
|
|
166
|
+
|
|
167
|
+
| Method | Config key | Notes |
|
|
168
|
+
|--------|-----------|-------|
|
|
169
|
+
| Ollama (local) | `llmBackend: "ollama"` | Set by init; no API key needed |
|
|
170
|
+
| Claude CLI | `llmBackend: "claude-cli"` | Uses CC subscription; no API key needed |
|
|
171
|
+
| Custom provider | `llmBaseUrl` + `llmApiKey` | Any OpenAI-compatible endpoint |
|
|
172
|
+
| Hicortex env vars | `HICORTEX_LLM_BASE_URL` + `HICORTEX_LLM_API_KEY` | Override at runtime |
|
|
173
|
+
|
|
174
|
+
If no LLM is configured, the server starts in **recall-only mode**: search, lessons, and context work; `/distill` and consolidation are disabled. Run `npx @gamaze/hicortex init` to configure.
|
|
159
175
|
|
|
160
176
|
## Database
|
|
161
177
|
|
|
162
|
-
Canonical location: `~/.hicortex/hicortex.db`.
|
|
178
|
+
Canonical location: `~/.hicortex/hicortex.db`. The OC plugin no longer owns its own database — it is a thin client to the server. Previously, OC installations at `~/.openclaw/data/hicortex.db` were migrated automatically on upgrade; this migration path remains in the server's `resolveDbPath` for any pre-0.10.0 installations.
|
|
163
179
|
|
|
164
180
|
## Development
|
|
165
181
|
|
|
@@ -174,7 +190,9 @@ npm test
|
|
|
174
190
|
|
|
175
191
|
**Tools not visible to agent (OC):** The plugin auto-adds tools to `tools.allow` on startup. Restart the gateway after install.
|
|
176
192
|
|
|
177
|
-
**
|
|
193
|
+
**OC plugin: "Server unreachable":** The plugin requires a running Hicortex server. Run `npx @gamaze/hicortex init` on the same machine, or set `serverUrl` in the plugin config to point at a remote server.
|
|
194
|
+
|
|
195
|
+
**LLM auto-config failed:** Check logs for `[hicortex] WARNING`. Add `llmBaseUrl` to plugin config or set `HICORTEX_LLM_BASE_URL` env var (applies to server setup, not the OC plugin itself).
|
|
178
196
|
|
|
179
197
|
**No lessons generated:** Reflection requires an LLM. Check that your provider is accessible and has sufficient quota.
|
|
180
198
|
|
package/dist/claude-md.d.ts
CHANGED
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLAUDE.md
|
|
2
|
+
* CLAUDE.md block management — removeLessonsBlock only (0.9.0+).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Lesson injection was removed in 0.9.0: lessons are now fetched at query time
|
|
5
|
+
* via the CC SessionStart hook (`hicortex lessons-context`) and by the Hermes
|
|
6
|
+
* plugin's prefetch/system_prompt_block. File-based injection caused EPERM
|
|
7
|
+
* errors on macOS, stale blocks, and unwinnable multi-file bookkeeping.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* removeLessonsBlock is kept so init/uninstall can strip the old static block
|
|
10
|
+
* from machines upgrading from 0.8.0 and earlier.
|
|
10
11
|
*/
|
|
11
|
-
import type Database from "better-sqlite3";
|
|
12
|
-
/**
|
|
13
|
-
* Inject lessons, memory index, and project context into CLAUDE.md.
|
|
14
|
-
* Creates the file if it doesn't exist.
|
|
15
|
-
* Replaces existing block if present, appends if not.
|
|
16
|
-
*/
|
|
17
|
-
export declare function injectLessons(db: Database.Database, options?: {
|
|
18
|
-
claudeMdPath?: string;
|
|
19
|
-
stateDir?: string;
|
|
20
|
-
project?: string;
|
|
21
|
-
}): Promise<{
|
|
22
|
-
lessonsCount: number;
|
|
23
|
-
path: string;
|
|
24
|
-
}>;
|
|
25
12
|
/**
|
|
26
13
|
* Remove the Hicortex Learnings block from CLAUDE.md.
|
|
27
|
-
* Used by
|
|
14
|
+
* Used by init (migration from 0.8.0) and uninstall.
|
|
15
|
+
* Returns true if a block was found and removed.
|
|
28
16
|
*/
|
|
29
17
|
export declare function removeLessonsBlock(claudeMdPath?: string): boolean;
|
package/dist/claude-md.js
CHANGED
|
@@ -1,259 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* CLAUDE.md
|
|
3
|
+
* CLAUDE.md block management — removeLessonsBlock only (0.9.0+).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Lesson injection was removed in 0.9.0: lessons are now fetched at query time
|
|
6
|
+
* via the CC SessionStart hook (`hicortex lessons-context`) and by the Hermes
|
|
7
|
+
* plugin's prefetch/system_prompt_block. File-based injection caused EPERM
|
|
8
|
+
* errors on macOS, stale blocks, and unwinnable multi-file bookkeeping.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* removeLessonsBlock is kept so init/uninstall can strip the old static block
|
|
11
|
+
* from machines upgrading from 0.8.0 and earlier.
|
|
11
12
|
*/
|
|
12
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
-
if (k2 === undefined) k2 = k;
|
|
14
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
-
}
|
|
18
|
-
Object.defineProperty(o, k2, desc);
|
|
19
|
-
}) : (function(o, m, k, k2) {
|
|
20
|
-
if (k2 === undefined) k2 = k;
|
|
21
|
-
o[k2] = m[k];
|
|
22
|
-
}));
|
|
23
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
-
}) : function(o, v) {
|
|
26
|
-
o["default"] = v;
|
|
27
|
-
});
|
|
28
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
-
var ownKeys = function(o) {
|
|
30
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
-
var ar = [];
|
|
32
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
-
return ar;
|
|
34
|
-
};
|
|
35
|
-
return ownKeys(o);
|
|
36
|
-
};
|
|
37
|
-
return function (mod) {
|
|
38
|
-
if (mod && mod.__esModule) return mod;
|
|
39
|
-
var result = {};
|
|
40
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
-
__setModuleDefault(result, mod);
|
|
42
|
-
return result;
|
|
43
|
-
};
|
|
44
|
-
})();
|
|
45
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
-
exports.injectLessons = injectLessons;
|
|
47
14
|
exports.removeLessonsBlock = removeLessonsBlock;
|
|
48
15
|
const node_fs_1 = require("node:fs");
|
|
49
16
|
const node_path_1 = require("node:path");
|
|
50
17
|
const node_os_1 = require("node:os");
|
|
51
|
-
const storage = __importStar(require("./storage.js"));
|
|
52
|
-
const features_js_1 = require("./features.js");
|
|
53
|
-
const extensions_js_1 = require("./extensions.js");
|
|
54
|
-
const state_js_1 = require("./state.js");
|
|
55
18
|
const START_MARKER = "<!-- HICORTEX-LEARNINGS:START -->";
|
|
56
19
|
const END_MARKER = "<!-- HICORTEX-LEARNINGS:END -->";
|
|
57
20
|
const DEFAULT_CLAUDE_MD = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", "CLAUDE.md");
|
|
58
|
-
/**
|
|
59
|
-
* Inject lessons, memory index, and project context into CLAUDE.md.
|
|
60
|
-
* Creates the file if it doesn't exist.
|
|
61
|
-
* Replaces existing block if present, appends if not.
|
|
62
|
-
*/
|
|
63
|
-
async function injectLessons(db, options = {}) {
|
|
64
|
-
const claudeMdPath = options.claudeMdPath ?? DEFAULT_CLAUDE_MD;
|
|
65
|
-
// Determine limits based on license
|
|
66
|
-
const maxLessons = (0, features_js_1.lessonsLimit)();
|
|
67
|
-
// Load MODULE_INDEX from state for domain-aware selection
|
|
68
|
-
const state = (0, state_js_1.loadState)(options.stateDir);
|
|
69
|
-
const moduleIndex = state.moduleIndex;
|
|
70
|
-
// --- Lessons ---
|
|
71
|
-
const lessons = storage.getLessons(db, 30, options.project);
|
|
72
|
-
const selected = await (0, extensions_js_1.getLessonSelector)().select(lessons, {
|
|
73
|
-
maxLessons,
|
|
74
|
-
project: options.project,
|
|
75
|
-
moduleIndex,
|
|
76
|
-
});
|
|
77
|
-
const lessonLines = selected.map((l) => {
|
|
78
|
-
const titleMatch = l.content.match(/## Lesson: (.+)/);
|
|
79
|
-
const typeMatch = l.content.match(/\*\*Type:\*\* (\w+)/);
|
|
80
|
-
const severityMatch = l.content.match(/\*\*Severity:\*\* (\w+)/);
|
|
81
|
-
const title = titleMatch ? titleMatch[1] : l.content.slice(0, 150);
|
|
82
|
-
const meta = [severityMatch?.[1], typeMatch?.[1]].filter(Boolean).join(", ");
|
|
83
|
-
return `- ${title}${meta ? ` (${meta})` : ""}`;
|
|
84
|
-
});
|
|
85
|
-
// --- Memory Index ---
|
|
86
|
-
const tokenBudget = readModuleIndexTokenBudget();
|
|
87
|
-
const indexLines = buildModuleIndex(db, tokenBudget, moduleIndex);
|
|
88
|
-
const totalCount = storage.countMemories(db);
|
|
89
|
-
const lessonCount = lessons.length;
|
|
90
|
-
const sourceCount = countSources(db);
|
|
91
|
-
// --- Current Project Context ---
|
|
92
|
-
const currentProject = detectCurrentProject(claudeMdPath);
|
|
93
|
-
const projectContext = currentProject
|
|
94
|
-
? buildProjectContext(db, currentProject)
|
|
95
|
-
: [];
|
|
96
|
-
// --- Build Block ---
|
|
97
|
-
const blockParts = [START_MARKER, "## Hicortex Memory"];
|
|
98
|
-
// Mandatory instruction
|
|
99
|
-
blockParts.push("", "You have access to shared long-term memory across all agents and sessions.", "BEFORE making decisions, search memory: `hicortex_search` for prior decisions on the same topic.", "Use `hicortex_context` at session start for recent project state.");
|
|
100
|
-
// Lessons
|
|
101
|
-
if (lessonLines.length > 0) {
|
|
102
|
-
blockParts.push("", "### Lessons (updated nightly)");
|
|
103
|
-
blockParts.push(...lessonLines);
|
|
104
|
-
}
|
|
105
|
-
// Project context
|
|
106
|
-
if (projectContext.length > 0) {
|
|
107
|
-
blockParts.push("", `### This Project (${currentProject})`);
|
|
108
|
-
blockParts.push(...projectContext);
|
|
109
|
-
}
|
|
110
|
-
// Memory index
|
|
111
|
-
if (indexLines.length > 0) {
|
|
112
|
-
blockParts.push("", "### Memory Index");
|
|
113
|
-
blockParts.push(...indexLines);
|
|
114
|
-
blockParts.push(`${totalCount} memories, ${lessonCount} lessons, ${sourceCount} agents. Search with \`hicortex_search\`.`);
|
|
115
|
-
}
|
|
116
|
-
blockParts.push(END_MARKER);
|
|
117
|
-
const block = blockParts.join("\n");
|
|
118
|
-
// --- Write ---
|
|
119
|
-
let content = "";
|
|
120
|
-
try {
|
|
121
|
-
content = (0, node_fs_1.readFileSync)(claudeMdPath, "utf-8");
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
// File doesn't exist — will create it
|
|
125
|
-
}
|
|
126
|
-
const startIdx = content.indexOf(START_MARKER);
|
|
127
|
-
const endIdx = content.indexOf(END_MARKER);
|
|
128
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
129
|
-
content =
|
|
130
|
-
content.slice(0, startIdx) +
|
|
131
|
-
block +
|
|
132
|
-
content.slice(endIdx + END_MARKER.length);
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
if (content.length > 0 && !content.endsWith("\n"))
|
|
136
|
-
content += "\n";
|
|
137
|
-
if (content.length > 0)
|
|
138
|
-
content += "\n";
|
|
139
|
-
content += block + "\n";
|
|
140
|
-
}
|
|
141
|
-
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(claudeMdPath), { recursive: true });
|
|
142
|
-
(0, node_fs_1.writeFileSync)(claudeMdPath, content);
|
|
143
|
-
return { lessonsCount: selected.length, path: claudeMdPath };
|
|
144
|
-
}
|
|
145
|
-
const DEFAULT_MODULE_INDEX_TOKEN_BUDGET = 500;
|
|
146
|
-
// BPE tokenizers average 3.5-4.5 chars/token for English prose. 4 is conservative.
|
|
147
|
-
const CHARS_PER_TOKEN = 4;
|
|
148
|
-
/**
|
|
149
|
-
* Read moduleIndexTokenBudget from config.json. Defaults to 500.
|
|
150
|
-
*/
|
|
151
|
-
function readModuleIndexTokenBudget() {
|
|
152
|
-
try {
|
|
153
|
-
const configPath = (0, node_path_1.join)((0, node_os_1.homedir)(), ".hicortex", "config.json");
|
|
154
|
-
const config = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
|
|
155
|
-
return typeof config.moduleIndexTokenBudget === "number"
|
|
156
|
-
? config.moduleIndexTokenBudget
|
|
157
|
-
: DEFAULT_MODULE_INDEX_TOKEN_BUDGET;
|
|
158
|
-
}
|
|
159
|
-
catch {
|
|
160
|
-
return DEFAULT_MODULE_INDEX_TOKEN_BUDGET;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Build structured MODULE_INDEX block for injection.
|
|
165
|
-
* Uses domain-grouped format if MODULE_INDEX is available,
|
|
166
|
-
* falls back to flat "project: count" format otherwise.
|
|
167
|
-
*/
|
|
168
|
-
function buildModuleIndex(db, tokenBudget, moduleIndex) {
|
|
169
|
-
if (moduleIndex && moduleIndex.domains.length > 0) {
|
|
170
|
-
const charBudget = tokenBudget * CHARS_PER_TOKEN;
|
|
171
|
-
let charCount = 0;
|
|
172
|
-
const lines = [];
|
|
173
|
-
for (const domain of moduleIndex.domains) {
|
|
174
|
-
const kwStr = domain.keywords.length > 0
|
|
175
|
-
? `: ${domain.keywords.join(", ")}`
|
|
176
|
-
: "";
|
|
177
|
-
const domainLine = `${domain.name} (${domain.memoryCount} memories, ${domain.lessonCount} lessons)${kwStr}`;
|
|
178
|
-
const projectLine = ` ${domain.projects.join(" | ")}`;
|
|
179
|
-
const blockChars = domainLine.length + projectLine.length + 2; // +2 for newlines
|
|
180
|
-
if (charCount + blockChars > charBudget && lines.length > 0)
|
|
181
|
-
break;
|
|
182
|
-
lines.push(domainLine);
|
|
183
|
-
lines.push(projectLine);
|
|
184
|
-
charCount += blockChars;
|
|
185
|
-
}
|
|
186
|
-
return lines;
|
|
187
|
-
}
|
|
188
|
-
// Fallback: flat project index (OSS default)
|
|
189
|
-
try {
|
|
190
|
-
const rows = db
|
|
191
|
-
.prepare(`SELECT project, COUNT(*) as cnt FROM memories
|
|
192
|
-
WHERE project IS NOT NULL
|
|
193
|
-
GROUP BY project ORDER BY cnt DESC LIMIT 10`)
|
|
194
|
-
.all();
|
|
195
|
-
return [rows.map((r) => `${r.project}: ${r.cnt}`).join(" | ")];
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
return [];
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Count distinct source agents.
|
|
203
|
-
*/
|
|
204
|
-
function countSources(db) {
|
|
205
|
-
try {
|
|
206
|
-
return db
|
|
207
|
-
.prepare("SELECT COUNT(DISTINCT source_agent) as cnt FROM memories")
|
|
208
|
-
.get().cnt;
|
|
209
|
-
}
|
|
210
|
-
catch {
|
|
211
|
-
return 0;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Detect current project from the CLAUDE.md path.
|
|
216
|
-
* CC puts project-specific CLAUDE.md files in ~/.claude/projects/<encoded-path>/
|
|
217
|
-
* The global ~/.claude/CLAUDE.md has no project context.
|
|
218
|
-
*/
|
|
219
|
-
function detectCurrentProject(claudeMdPath) {
|
|
220
|
-
// Global CLAUDE.md — no project
|
|
221
|
-
if (claudeMdPath === DEFAULT_CLAUDE_MD)
|
|
222
|
-
return null;
|
|
223
|
-
// Project CLAUDE.md: ~/.claude/projects/-Users-foo-myproject/CLAUDE.md
|
|
224
|
-
// Extract the last segment of the encoded path
|
|
225
|
-
const match = claudeMdPath.match(/projects\/[^/]*-([^/]+)\//);
|
|
226
|
-
if (match)
|
|
227
|
-
return match[1];
|
|
228
|
-
return null;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Build recent decisions/facts for a specific project.
|
|
232
|
-
* Returns formatted lines like: "- Shipped v0.4.1 with multi-client (2026-03-28)"
|
|
233
|
-
*/
|
|
234
|
-
function buildProjectContext(db, project) {
|
|
235
|
-
try {
|
|
236
|
-
const rows = db
|
|
237
|
-
.prepare(`SELECT content, created_at FROM memories
|
|
238
|
-
WHERE project = ? AND memory_type IN ('decision', 'episode')
|
|
239
|
-
ORDER BY created_at DESC LIMIT 5`)
|
|
240
|
-
.all(project);
|
|
241
|
-
return rows.map((r) => {
|
|
242
|
-
const date = r.created_at?.slice(0, 10) ?? "";
|
|
243
|
-
// Extract first meaningful line from content
|
|
244
|
-
const lines = r.content.split("\n").filter((l) => l.trim().length > 10);
|
|
245
|
-
const summary = lines.find((l) => l.startsWith("- ") || l.startsWith("### "))?.replace(/^[-#\s]+/, "").slice(0, 120) ??
|
|
246
|
-
r.content.slice(0, 120);
|
|
247
|
-
return `- ${summary} (${date})`;
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
return [];
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
21
|
/**
|
|
255
22
|
* Remove the Hicortex Learnings block from CLAUDE.md.
|
|
256
|
-
* Used by
|
|
23
|
+
* Used by init (migration from 0.8.0) and uninstall.
|
|
24
|
+
* Returns true if a block was found and removed.
|
|
257
25
|
*/
|
|
258
26
|
function removeLessonsBlock(claudeMdPath = DEFAULT_CLAUDE_MD) {
|
|
259
27
|
let content;
|
package/dist/cli.d.ts
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Commands:
|
|
6
6
|
* server Start the MCP HTTP/SSE server (persistent daemon)
|
|
7
7
|
* init Detect existing setup and configure for CC/OC
|
|
8
|
-
* nightly Run
|
|
9
|
-
* nightly --
|
|
8
|
+
* nightly Run capture + consolidate (manual trigger)
|
|
9
|
+
* nightly --capture-only Capture only, skip consolidation
|
|
10
|
+
* nightly --status Show nightly pipeline health check
|
|
10
11
|
* status Show config, DB stats, adapter status
|
|
11
12
|
* uninstall Clean removal of CC integration
|
|
12
13
|
*/
|
package/dist/cli.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* Commands:
|
|
7
7
|
* server Start the MCP HTTP/SSE server (persistent daemon)
|
|
8
8
|
* init Detect existing setup and configure for CC/OC
|
|
9
|
-
* nightly Run
|
|
10
|
-
* nightly --
|
|
9
|
+
* nightly Run capture + consolidate (manual trigger)
|
|
10
|
+
* nightly --capture-only Capture only, skip consolidation
|
|
11
|
+
* nightly --status Show nightly pipeline health check
|
|
11
12
|
* status Show config, DB stats, adapter status
|
|
12
13
|
* uninstall Clean removal of CC integration
|
|
13
14
|
*/
|
|
@@ -50,8 +51,9 @@ switch (command) {
|
|
|
50
51
|
}
|
|
51
52
|
else {
|
|
52
53
|
const dryRun = args.includes("--dry-run");
|
|
54
|
+
const captureOnly = args.includes("--capture-only");
|
|
53
55
|
import("./nightly.js").then(({ runNightly }) => {
|
|
54
|
-
runNightly({ dryRun }).catch((err) => {
|
|
56
|
+
runNightly({ dryRun, captureOnly }).catch((err) => {
|
|
55
57
|
console.error("[hicortex] Nightly pipeline failed:", err);
|
|
56
58
|
process.exit(1);
|
|
57
59
|
});
|
|
@@ -75,24 +77,40 @@ switch (command) {
|
|
|
75
77
|
});
|
|
76
78
|
});
|
|
77
79
|
break;
|
|
80
|
+
case "lessons-context":
|
|
81
|
+
// CC SessionStart hook: fetch lessons from the configured server and print
|
|
82
|
+
// a Markdown block to stdout. Fail-soft — any error = silent exit 0 so a
|
|
83
|
+
// broken hook never blocks a CC session.
|
|
84
|
+
import("./lessons-context.js").then(({ fetchLessonsContext }) => {
|
|
85
|
+
fetchLessonsContext()
|
|
86
|
+
.then((block) => {
|
|
87
|
+
if (block)
|
|
88
|
+
process.stdout.write(block + "\n");
|
|
89
|
+
process.exit(0);
|
|
90
|
+
})
|
|
91
|
+
.catch(() => process.exit(0));
|
|
92
|
+
}).catch(() => process.exit(0));
|
|
93
|
+
break;
|
|
78
94
|
default:
|
|
79
95
|
console.log(`Hicortex — Human-like memory for self-improving AI agents
|
|
80
96
|
|
|
81
97
|
Usage: hicortex <command> [options]
|
|
82
98
|
|
|
83
99
|
Commands:
|
|
84
|
-
server
|
|
85
|
-
init
|
|
86
|
-
init --server <url> Set up as client (remote server
|
|
87
|
-
nightly
|
|
88
|
-
|
|
89
|
-
|
|
100
|
+
server Start the MCP HTTP/SSE server (server mode)
|
|
101
|
+
init Set up Hicortex (server mode, local DB + daemon)
|
|
102
|
+
init --server <url> Set up as client (remote server)
|
|
103
|
+
nightly Run nightly denoise + capture + consolidate
|
|
104
|
+
lessons-context Fetch lessons and print Markdown to stdout (CC SessionStart hook)
|
|
105
|
+
status Show current configuration and stats
|
|
106
|
+
uninstall Remove CC integration (preserves DB)
|
|
90
107
|
|
|
91
108
|
Options:
|
|
92
109
|
server --port <n> Port (default: 8787)
|
|
93
110
|
server --host <h> Host (default: 127.0.0.1)
|
|
94
|
-
nightly --dry-run
|
|
95
|
-
nightly --
|
|
111
|
+
nightly --dry-run Preview without changes
|
|
112
|
+
nightly --capture-only Capture only, skip consolidation (safe to run multiple times/day)
|
|
113
|
+
nightly --status Show nightly pipeline health
|
|
96
114
|
|
|
97
115
|
Examples:
|
|
98
116
|
npx @gamaze/hicortex server
|