open-think 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +98 -61
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -13,8 +13,8 @@ import { v7 as uuidv7 } from "uuid";
13
13
  import { startOfWeek, endOfWeek, subWeeks } from "date-fns";
14
14
 
15
15
  // src/db/client.ts
16
- import path from "path";
17
- import fs from "fs";
16
+ import path2 from "path";
17
+ import fs2 from "fs";
18
18
  import { DatabaseSync } from "node:sqlite";
19
19
 
20
20
  // src/db/schema.ts
@@ -44,17 +44,75 @@ function ensureSchema(db2) {
44
44
  `);
45
45
  }
46
46
 
47
+ // src/lib/paths.ts
48
+ import path from "path";
49
+ import fs from "fs";
50
+ function getHome() {
51
+ const home = process.env.HOME;
52
+ if (!home) {
53
+ throw new Error("HOME environment variable is not set");
54
+ }
55
+ return home;
56
+ }
57
+ function sanitizeName(name) {
58
+ if (!name || /[\/\\\.]{2}/.test(name) || /[^a-zA-Z0-9_-]/.test(name)) {
59
+ throw new Error(`Invalid cortex name: "${name}". Use only alphanumeric characters, hyphens, and underscores.`);
60
+ }
61
+ return name;
62
+ }
63
+ function getThinkHome() {
64
+ const thinkHome = process.env.THINK_HOME;
65
+ if (thinkHome === void 0 || thinkHome === "") return null;
66
+ return thinkHome;
67
+ }
68
+ function getThinkDir() {
69
+ return getThinkHome() ?? path.join(getHome(), ".think");
70
+ }
71
+ function getThinkConfigDir() {
72
+ const thinkHome = getThinkHome();
73
+ if (thinkHome) return path.join(thinkHome, "config");
74
+ const xdgConfig = process.env.XDG_CONFIG_HOME || path.join(getHome(), ".config");
75
+ return path.join(xdgConfig, "think");
76
+ }
77
+ function getThinkDataDir() {
78
+ const thinkHome = getThinkHome();
79
+ if (thinkHome) return path.join(thinkHome, "data");
80
+ const xdgData = process.env.XDG_DATA_HOME || path.join(getHome(), ".local", "share");
81
+ return path.join(xdgData, "think");
82
+ }
83
+ function getEngramsDir() {
84
+ return path.join(getThinkDir(), "engrams");
85
+ }
86
+ function getEngramDbPath(cortexName) {
87
+ return path.join(getEngramsDir(), `${sanitizeName(cortexName)}.db`);
88
+ }
89
+ function getRepoPath() {
90
+ return path.join(getThinkDir(), "repo");
91
+ }
92
+ function getLongtermDir() {
93
+ return path.join(getThinkDir(), "longterm");
94
+ }
95
+ function getLongtermPath(cortexName) {
96
+ return path.join(getLongtermDir(), `${sanitizeName(cortexName)}.md`);
97
+ }
98
+ function getCuratorMdPath() {
99
+ return path.join(getThinkDir(), "curator.md");
100
+ }
101
+ function ensureThinkDirs() {
102
+ fs.mkdirSync(getEngramsDir(), { recursive: true });
103
+ fs.mkdirSync(getLongtermDir(), { recursive: true });
104
+ }
105
+
47
106
  // src/db/client.ts
48
107
  var db = null;
49
108
  function getDataDir() {
50
- const xdgData = process.env.XDG_DATA_HOME || path.join(process.env.HOME, ".local", "share");
51
- return path.join(xdgData, "think");
109
+ return getThinkDataDir();
52
110
  }
53
111
  function getDb() {
54
112
  if (db) return db;
55
113
  const dataDir = getDataDir();
56
- fs.mkdirSync(dataDir, { recursive: true });
57
- const dbPath = path.join(dataDir, "think.db");
114
+ fs2.mkdirSync(dataDir, { recursive: true });
115
+ const dbPath = path2.join(dataDir, "think.db");
58
116
  db = new DatabaseSync(dbPath);
59
117
  db.exec("PRAGMA journal_mode = WAL");
60
118
  db.exec("PRAGMA synchronous = NORMAL");
@@ -131,25 +189,24 @@ function deleteEntriesByContent(pattern) {
131
189
  }
132
190
 
133
191
  // src/lib/config.ts
134
- import path2 from "path";
135
- import fs2 from "fs";
192
+ import path3 from "path";
193
+ import fs3 from "fs";
136
194
  import { v4 as uuidv4 } from "uuid";
137
195
  function getConfigDir() {
138
- const xdgConfig = process.env.XDG_CONFIG_HOME || path2.join(process.env.HOME, ".config");
139
- return path2.join(xdgConfig, "think");
196
+ return getThinkConfigDir();
140
197
  }
141
198
  function configPath() {
142
- return path2.join(getConfigDir(), "config.json");
199
+ return path3.join(getConfigDir(), "config.json");
143
200
  }
144
201
  function saveConfig(config) {
145
202
  const dir = getConfigDir();
146
- fs2.mkdirSync(dir, { recursive: true });
147
- fs2.writeFileSync(configPath(), JSON.stringify(config, null, 2) + "\n", "utf-8");
203
+ fs3.mkdirSync(dir, { recursive: true });
204
+ fs3.writeFileSync(configPath(), JSON.stringify(config, null, 2) + "\n", "utf-8");
148
205
  }
149
206
  function getConfig() {
150
207
  const fp = configPath();
151
- if (fs2.existsSync(fp)) {
152
- const raw = fs2.readFileSync(fp, "utf-8");
208
+ if (fs3.existsSync(fp)) {
209
+ const raw = fs3.readFileSync(fp, "utf-8");
153
210
  return JSON.parse(raw);
154
211
  }
155
212
  const config = {
@@ -165,50 +222,6 @@ import { v7 as uuidv72 } from "uuid";
165
222
 
166
223
  // src/db/engrams.ts
167
224
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
168
-
169
- // src/lib/paths.ts
170
- import path3 from "path";
171
- import fs3 from "fs";
172
- function getHome() {
173
- const home = process.env.HOME;
174
- if (!home) {
175
- throw new Error("HOME environment variable is not set");
176
- }
177
- return home;
178
- }
179
- function sanitizeName(name) {
180
- if (!name || /[\/\\\.]{2}/.test(name) || /[^a-zA-Z0-9_-]/.test(name)) {
181
- throw new Error(`Invalid cortex name: "${name}". Use only alphanumeric characters, hyphens, and underscores.`);
182
- }
183
- return name;
184
- }
185
- function getThinkDir() {
186
- return path3.join(getHome(), ".think");
187
- }
188
- function getEngramsDir() {
189
- return path3.join(getThinkDir(), "engrams");
190
- }
191
- function getEngramDbPath(cortexName) {
192
- return path3.join(getEngramsDir(), `${sanitizeName(cortexName)}.db`);
193
- }
194
- function getRepoPath() {
195
- return path3.join(getThinkDir(), "repo");
196
- }
197
- function getLongtermDir() {
198
- return path3.join(getThinkDir(), "longterm");
199
- }
200
- function getLongtermPath(cortexName) {
201
- return path3.join(getLongtermDir(), `${sanitizeName(cortexName)}.md`);
202
- }
203
- function getCuratorMdPath() {
204
- return path3.join(getThinkDir(), "curator.md");
205
- }
206
- function ensureThinkDirs() {
207
- fs3.mkdirSync(getEngramsDir(), { recursive: true });
208
- fs3.mkdirSync(getLongtermDir(), { recursive: true });
209
- }
210
-
211
- // src/db/engrams.ts
212
225
  var dbs = /* @__PURE__ */ new Map();
213
226
  function ensureEngramSchema(db2) {
214
227
  db2.exec(`
@@ -1169,7 +1182,25 @@ function assembleCurationPrompt(params) {
1169
1182
  const memoriesText = params.existingMemories.length > 0 ? params.existingMemories.map((m) => `- [${m.ts}] ${m.author}: ${m.content}`).join("\n") : "(no memories yet)";
1170
1183
  const curatorMdText = params.curatorMd ?? "(none provided)";
1171
1184
  const engramsText = params.pendingEngrams.map((e) => `- [${e.created_at}] (id: ${e.id}) ${e.content}`).join("\n");
1172
- return BASE_CURATION_PROMPT.replace("{existing_memories}", memoriesText).replace("{curator_md}", curatorMdText).replace("{pending_engrams}", engramsText);
1185
+ let prompt3 = BASE_CURATION_PROMPT.replace("{existing_memories}", memoriesText).replace("{curator_md}", curatorMdText).replace("{pending_engrams}", engramsText);
1186
+ const tuning = [];
1187
+ if (params.selectivity === "high") {
1188
+ tuning.push("Be very selective. Only promote clearly significant events: major decisions, shipped deliverables, critical blockers, direction changes. Skip routine commits, minor fixes, and incremental progress.");
1189
+ } else if (params.selectivity === "low") {
1190
+ tuning.push("Be inclusive. Promote most work events that have any team relevance. Only drop purely administrative or personal events.");
1191
+ }
1192
+ if (params.granularity === "summary") {
1193
+ tuning.push("Consolidate related events into single memory entries. Prefer fewer, broader memories over many specific ones.");
1194
+ } else if (params.granularity === "detailed") {
1195
+ tuning.push("Keep memories specific and granular. Each distinct event or decision should be its own memory entry. Do not roll up multiple events into one.");
1196
+ }
1197
+ if (params.maxMemoriesPerRun && params.maxMemoriesPerRun > 0) {
1198
+ tuning.push(`Produce at most ${params.maxMemoriesPerRun} memory entries from this batch. If more events are significant, prioritize the most important.`);
1199
+ }
1200
+ if (tuning.length > 0) {
1201
+ prompt3 += "\n\nAdditional instructions:\n" + tuning.map((t) => `- ${t}`).join("\n");
1202
+ }
1203
+ return prompt3;
1173
1204
  }
1174
1205
  function parseMemoriesJsonl(content) {
1175
1206
  if (!content.trim()) return [];
@@ -1264,7 +1295,10 @@ var curateCommand = new Command10("curate").description("Run curation: evaluate
1264
1295
  existingMemories,
1265
1296
  curatorMd,
1266
1297
  pendingEngrams: pending,
1267
- author
1298
+ author,
1299
+ selectivity: config.cortex?.selectivity,
1300
+ granularity: config.cortex?.granularity,
1301
+ maxMemoriesPerRun: config.cortex?.maxMemoriesPerRun
1268
1302
  });
1269
1303
  let newEntries;
1270
1304
  try {
@@ -1589,6 +1623,9 @@ import chalk17 from "chalk";
1589
1623
  var ALLOWED_KEYS = /* @__PURE__ */ new Set([
1590
1624
  "cortex.curateEveryN",
1591
1625
  "cortex.confirmBeforeCommit",
1626
+ "cortex.selectivity",
1627
+ "cortex.granularity",
1628
+ "cortex.maxMemoriesPerRun",
1592
1629
  "cortex.author",
1593
1630
  "cortex.repo",
1594
1631
  "cortex.active",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-think",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "Local-first CLI that gives AI agents persistent, curated memory",
6
6
  "bin": {