open-think 0.2.1 → 0.2.3
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.
|
@@ -136,8 +136,8 @@ function createOrphanBranch(branchName) {
|
|
|
136
136
|
} catch {
|
|
137
137
|
}
|
|
138
138
|
const repoPath = getRepoPath();
|
|
139
|
-
fs3.writeFileSync(path3.join(repoPath, "
|
|
140
|
-
runGit(["add", "
|
|
139
|
+
fs3.writeFileSync(path3.join(repoPath, "000001.jsonl"), "", "utf-8");
|
|
140
|
+
runGit(["add", "000001.jsonl"]);
|
|
141
141
|
runGit(["commit", "-m", `init: create cortex ${branchName}`]);
|
|
142
142
|
runGit(["push", "--set-upstream", "origin", branchName]);
|
|
143
143
|
}
|
|
@@ -151,9 +151,9 @@ function readFileFromBranch(branchName, filePath) {
|
|
|
151
151
|
return null;
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
function appendAndCommit(branchName, newLines, commitMessage, maxRetries = 3) {
|
|
154
|
+
function appendAndCommit(branchName, newLines, commitMessage, maxRetries = 3, targetFile = "memories.jsonl") {
|
|
155
155
|
const repoPath = getRepoPath();
|
|
156
|
-
const
|
|
156
|
+
const filePath = path3.join(repoPath, targetFile);
|
|
157
157
|
try {
|
|
158
158
|
runGit(["switch", branchName]);
|
|
159
159
|
} catch {
|
|
@@ -168,12 +168,12 @@ function appendAndCommit(branchName, newLines, commitMessage, maxRetries = 3) {
|
|
|
168
168
|
runGit(["rebase", "--abort"]);
|
|
169
169
|
} catch {
|
|
170
170
|
}
|
|
171
|
-
throw new Error(`Rebase conflict on ${branchName}. This should not happen with append-only files
|
|
171
|
+
throw new Error(`Rebase conflict on ${branchName}. This should not happen with append-only files.`);
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
const content = newLines.join("\n") + "\n";
|
|
175
|
-
fs3.appendFileSync(
|
|
176
|
-
runGit(["add",
|
|
175
|
+
fs3.appendFileSync(filePath, content, "utf-8");
|
|
176
|
+
runGit(["add", targetFile]);
|
|
177
177
|
runGit(["commit", "-m", commitMessage]);
|
|
178
178
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
179
179
|
try {
|
|
@@ -194,6 +194,58 @@ function listRemoteBranches() {
|
|
|
194
194
|
const output = runGit(["ls-remote", "--heads", "origin"]);
|
|
195
195
|
return output.trim().split("\n").filter(Boolean).map((line) => line.split(" ")[1]?.replace("refs/heads/", "")).filter(Boolean);
|
|
196
196
|
}
|
|
197
|
+
function listBranchFiles(branchName, extension) {
|
|
198
|
+
try {
|
|
199
|
+
const output = runGit(["ls-tree", "--name-only", `origin/${branchName}`]);
|
|
200
|
+
let files = output.split("\n").filter(Boolean);
|
|
201
|
+
if (extension) {
|
|
202
|
+
files = files.filter((f) => f.endsWith(extension));
|
|
203
|
+
}
|
|
204
|
+
return files.sort();
|
|
205
|
+
} catch {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function countBranchFileLines(branchName, filePath) {
|
|
210
|
+
const content = readFileFromBranch(branchName, filePath);
|
|
211
|
+
if (!content) return 0;
|
|
212
|
+
return content.trim().split("\n").filter(Boolean).length;
|
|
213
|
+
}
|
|
214
|
+
function migrateToBuckets(branchName) {
|
|
215
|
+
const repoPath = getRepoPath();
|
|
216
|
+
try {
|
|
217
|
+
runGit(["switch", branchName]);
|
|
218
|
+
} catch {
|
|
219
|
+
runGit(["switch", "-c", branchName, `origin/${branchName}`]);
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
runGit(["pull", "--rebase", "origin", branchName]);
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
const legacyPath = path3.join(repoPath, "memories.jsonl");
|
|
226
|
+
const bucketPath = path3.join(repoPath, "000001.jsonl");
|
|
227
|
+
if (fs3.existsSync(legacyPath) && !fs3.existsSync(bucketPath)) {
|
|
228
|
+
const preMigrationRef = runGit(["rev-parse", "HEAD"]);
|
|
229
|
+
fs3.renameSync(legacyPath, bucketPath);
|
|
230
|
+
runGit(["add", "-A"]);
|
|
231
|
+
runGit(["commit", "-m", "migrate: memories.jsonl -> 000001.jsonl"]);
|
|
232
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
233
|
+
try {
|
|
234
|
+
runGit(["push", "origin", branchName]);
|
|
235
|
+
return;
|
|
236
|
+
} catch {
|
|
237
|
+
if (attempt === 3) {
|
|
238
|
+
try {
|
|
239
|
+
runGit(["reset", "--hard", preMigrationRef]);
|
|
240
|
+
} catch {
|
|
241
|
+
}
|
|
242
|
+
throw new Error("Migration push failed after 3 attempts \u2014 local commit rolled back");
|
|
243
|
+
}
|
|
244
|
+
runGit(["pull", "--rebase", "origin", branchName]);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
197
249
|
|
|
198
250
|
export {
|
|
199
251
|
getThinkDataDir,
|
|
@@ -212,5 +264,8 @@ export {
|
|
|
212
264
|
readFileFromBranch,
|
|
213
265
|
appendAndCommit,
|
|
214
266
|
getFileLog,
|
|
215
|
-
listRemoteBranches
|
|
267
|
+
listRemoteBranches,
|
|
268
|
+
listBranchFiles,
|
|
269
|
+
countBranchFileLines,
|
|
270
|
+
migrateToBuckets
|
|
216
271
|
};
|
|
@@ -2,20 +2,26 @@
|
|
|
2
2
|
import {
|
|
3
3
|
appendAndCommit,
|
|
4
4
|
branchExists,
|
|
5
|
+
countBranchFileLines,
|
|
5
6
|
createOrphanBranch,
|
|
6
7
|
ensureRepoCloned,
|
|
7
8
|
fetchBranch,
|
|
8
9
|
getFileLog,
|
|
10
|
+
listBranchFiles,
|
|
9
11
|
listRemoteBranches,
|
|
12
|
+
migrateToBuckets,
|
|
10
13
|
readFileFromBranch
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-N4VAGRBF.js";
|
|
12
15
|
export {
|
|
13
16
|
appendAndCommit,
|
|
14
17
|
branchExists,
|
|
18
|
+
countBranchFileLines,
|
|
15
19
|
createOrphanBranch,
|
|
16
20
|
ensureRepoCloned,
|
|
17
21
|
fetchBranch,
|
|
18
22
|
getFileLog,
|
|
23
|
+
listBranchFiles,
|
|
19
24
|
listRemoteBranches,
|
|
25
|
+
migrateToBuckets,
|
|
20
26
|
readFileFromBranch
|
|
21
27
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node --no-warnings=ExperimentalWarning
|
|
2
2
|
import {
|
|
3
3
|
appendAndCommit,
|
|
4
|
+
countBranchFileLines,
|
|
4
5
|
createOrphanBranch,
|
|
5
6
|
ensureRepoCloned,
|
|
6
7
|
ensureThinkDirs,
|
|
@@ -12,10 +13,12 @@ import {
|
|
|
12
13
|
getEngramsDir,
|
|
13
14
|
getLongtermPath,
|
|
14
15
|
getThinkDataDir,
|
|
16
|
+
listBranchFiles,
|
|
15
17
|
listRemoteBranches,
|
|
18
|
+
migrateToBuckets,
|
|
16
19
|
readFileFromBranch,
|
|
17
20
|
saveConfig
|
|
18
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-N4VAGRBF.js";
|
|
19
22
|
|
|
20
23
|
// src/index.ts
|
|
21
24
|
import fs11 from "fs";
|
|
@@ -1498,13 +1501,45 @@ var GitSyncAdapter = class {
|
|
|
1498
1501
|
const config = getConfig();
|
|
1499
1502
|
return !!config.cortex?.repo;
|
|
1500
1503
|
}
|
|
1504
|
+
ensureMigrated(cortex, branchFiles) {
|
|
1505
|
+
const hasNumbered = branchFiles.some((f) => /^\d{6}\.jsonl$/.test(f));
|
|
1506
|
+
if (!hasNumbered) {
|
|
1507
|
+
const hasLegacy = readFileFromBranch(cortex, "memories.jsonl") !== null;
|
|
1508
|
+
if (hasLegacy) {
|
|
1509
|
+
migrateToBuckets(cortex);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
determineBucketFile(cortex, branchFiles) {
|
|
1514
|
+
const config = getConfig();
|
|
1515
|
+
const bucketSize = config.cortex?.bucketSize ?? 500;
|
|
1516
|
+
const numbered = branchFiles.filter((f) => /^\d{6}\.jsonl$/.test(f));
|
|
1517
|
+
if (numbered.length === 0) return "000001.jsonl";
|
|
1518
|
+
const latestFile = numbered[numbered.length - 1];
|
|
1519
|
+
const lineCount = countBranchFileLines(cortex, latestFile);
|
|
1520
|
+
if (lineCount >= bucketSize) {
|
|
1521
|
+
const nextNum = parseInt(latestFile.replace(".jsonl", ""), 10) + 1;
|
|
1522
|
+
return String(nextNum).padStart(6, "0") + ".jsonl";
|
|
1523
|
+
}
|
|
1524
|
+
return latestFile;
|
|
1525
|
+
}
|
|
1501
1526
|
async push(cortex) {
|
|
1502
1527
|
const result = { pushed: 0, pulled: 0, errors: [] };
|
|
1503
1528
|
ensureRepoCloned();
|
|
1529
|
+
fetchBranch(cortex);
|
|
1504
1530
|
const cursorStr = getSyncCursor(cortex, "git", "push");
|
|
1505
1531
|
const lastVersion = cursorStr ? parseInt(cursorStr, 10) : 0;
|
|
1532
|
+
const branchFiles = listBranchFiles(cortex, ".jsonl");
|
|
1533
|
+
try {
|
|
1534
|
+
this.ensureMigrated(cortex, branchFiles);
|
|
1535
|
+
} catch (err) {
|
|
1536
|
+
result.errors.push(`Migration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1537
|
+
return result;
|
|
1538
|
+
}
|
|
1539
|
+
const currentFiles = branchFiles.some((f) => /^\d{6}\.jsonl$/.test(f)) ? branchFiles : listBranchFiles(cortex, ".jsonl");
|
|
1506
1540
|
const newMemories = getMemoriesBySyncVersion(cortex, lastVersion);
|
|
1507
1541
|
if (newMemories.length === 0) return result;
|
|
1542
|
+
const targetFile = this.determineBucketFile(cortex, currentFiles);
|
|
1508
1543
|
const newLines = newMemories.map((m) => JSON.stringify({
|
|
1509
1544
|
ts: m.ts,
|
|
1510
1545
|
author: m.author,
|
|
@@ -1518,7 +1553,7 @@ var GitSyncAdapter = class {
|
|
|
1518
1553
|
const maxVersion = Math.max(...newMemories.map((m) => m.sync_version));
|
|
1519
1554
|
setSyncCursor(cortex, "git", "push", String(maxVersion));
|
|
1520
1555
|
try {
|
|
1521
|
-
appendAndCommit(cortex, newLines, commitMsg);
|
|
1556
|
+
appendAndCommit(cortex, newLines, commitMsg, 3, targetFile);
|
|
1522
1557
|
result.pushed = newMemories.length;
|
|
1523
1558
|
} catch (err) {
|
|
1524
1559
|
setSyncCursor(cortex, "git", "push", String(lastVersion));
|
|
@@ -1526,16 +1561,7 @@ var GitSyncAdapter = class {
|
|
|
1526
1561
|
}
|
|
1527
1562
|
return result;
|
|
1528
1563
|
}
|
|
1529
|
-
|
|
1530
|
-
const result = { pushed: 0, pulled: 0, errors: [] };
|
|
1531
|
-
try {
|
|
1532
|
-
ensureRepoCloned();
|
|
1533
|
-
fetchBranch(cortex);
|
|
1534
|
-
} catch (err) {
|
|
1535
|
-
result.errors.push(err instanceof Error ? err.message : String(err));
|
|
1536
|
-
return result;
|
|
1537
|
-
}
|
|
1538
|
-
const memoriesRaw = readFileFromBranch(cortex, "memories.jsonl") ?? "";
|
|
1564
|
+
processMemories(cortex, memoriesRaw, result) {
|
|
1539
1565
|
const memories = parseMemoriesJsonl(memoriesRaw);
|
|
1540
1566
|
for (const m of memories) {
|
|
1541
1567
|
const id = deterministicId(m.ts, m.author, m.content);
|
|
@@ -1553,6 +1579,55 @@ var GitSyncAdapter = class {
|
|
|
1553
1579
|
});
|
|
1554
1580
|
if (wasInserted) result.pulled++;
|
|
1555
1581
|
}
|
|
1582
|
+
}
|
|
1583
|
+
async pull(cortex) {
|
|
1584
|
+
const result = { pushed: 0, pulled: 0, errors: [] };
|
|
1585
|
+
try {
|
|
1586
|
+
ensureRepoCloned();
|
|
1587
|
+
fetchBranch(cortex);
|
|
1588
|
+
} catch (err) {
|
|
1589
|
+
result.errors.push(err instanceof Error ? err.message : String(err));
|
|
1590
|
+
return result;
|
|
1591
|
+
}
|
|
1592
|
+
const config = getConfig();
|
|
1593
|
+
const onboardingDepth = config.cortex?.onboardingDepth ?? 1500;
|
|
1594
|
+
const bucketSize = config.cortex?.bucketSize ?? 500;
|
|
1595
|
+
const files = listBranchFiles(cortex, ".jsonl").filter((f) => /^\d{6}\.jsonl$/.test(f)).sort();
|
|
1596
|
+
if (files.length === 0) {
|
|
1597
|
+
const memoriesRaw = readFileFromBranch(cortex, "memories.jsonl") ?? "";
|
|
1598
|
+
if (memoriesRaw) {
|
|
1599
|
+
this.processMemories(cortex, memoriesRaw, result);
|
|
1600
|
+
}
|
|
1601
|
+
return result;
|
|
1602
|
+
}
|
|
1603
|
+
const pullCursor = getSyncCursor(cortex, "git", "pull_file");
|
|
1604
|
+
let filesToRead;
|
|
1605
|
+
if (!pullCursor) {
|
|
1606
|
+
const numFiles = Math.ceil(onboardingDepth / bucketSize);
|
|
1607
|
+
filesToRead = files.slice(-numFiles);
|
|
1608
|
+
} else {
|
|
1609
|
+
const cursorIndex = files.indexOf(pullCursor);
|
|
1610
|
+
if (cursorIndex === -1) {
|
|
1611
|
+
const numFiles = Math.ceil(onboardingDepth / bucketSize);
|
|
1612
|
+
filesToRead = files.slice(-numFiles);
|
|
1613
|
+
} else {
|
|
1614
|
+
filesToRead = files.slice(cursorIndex);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
let lastReadFile = null;
|
|
1618
|
+
for (const file of filesToRead) {
|
|
1619
|
+
const raw = readFileFromBranch(cortex, file);
|
|
1620
|
+
if (raw === null) {
|
|
1621
|
+
break;
|
|
1622
|
+
}
|
|
1623
|
+
lastReadFile = file;
|
|
1624
|
+
if (raw.trim()) {
|
|
1625
|
+
this.processMemories(cortex, raw, result);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
if (lastReadFile) {
|
|
1629
|
+
setSyncCursor(cortex, "git", "pull_file", lastReadFile);
|
|
1630
|
+
}
|
|
1556
1631
|
return result;
|
|
1557
1632
|
}
|
|
1558
1633
|
async sync(cortex) {
|
|
@@ -1623,7 +1698,7 @@ cortexCommand.addCommand(new Command9("setup").description("Configure a sync bac
|
|
|
1623
1698
|
const adapter = getSyncAdapter();
|
|
1624
1699
|
if (adapter) {
|
|
1625
1700
|
try {
|
|
1626
|
-
const { ensureRepoCloned: ensureRepoCloned2 } = await import("./git-
|
|
1701
|
+
const { ensureRepoCloned: ensureRepoCloned2 } = await import("./git-R4CVMKV7.js");
|
|
1627
1702
|
ensureRepoCloned2();
|
|
1628
1703
|
console.log(chalk9.green("\u2713") + " Repo cloned");
|
|
1629
1704
|
} catch (err) {
|
|
@@ -2172,7 +2247,52 @@ var recallCommand = new Command12("recall").argument("<query>", "What to recall"
|
|
|
2172
2247
|
// src/commands/memory.ts
|
|
2173
2248
|
import { Command as Command13 } from "commander";
|
|
2174
2249
|
import chalk13 from "chalk";
|
|
2175
|
-
var
|
|
2250
|
+
var addCommand = new Command13("add").description("Add a memory directly, bypassing curation").argument("<message>", "The memory content").option("--no-push", "Skip pushing to remote after adding").option("--silent", "Suppress output").action(async function(message, opts) {
|
|
2251
|
+
const globalOpts = this.optsWithGlobals();
|
|
2252
|
+
const config = getConfig();
|
|
2253
|
+
const cortex = globalOpts.cortex ?? config.cortex?.active;
|
|
2254
|
+
if (!cortex) {
|
|
2255
|
+
console.error(chalk13.red("No active cortex. Run: think cortex switch <name>"));
|
|
2256
|
+
process.exit(1);
|
|
2257
|
+
}
|
|
2258
|
+
const author = config.cortex?.author ?? "unknown";
|
|
2259
|
+
const validated = validateEngramContent(message);
|
|
2260
|
+
message = validated.content;
|
|
2261
|
+
if (!opts.silent && validated.warnings.length > 0) {
|
|
2262
|
+
for (const w of validated.warnings) {
|
|
2263
|
+
console.log(chalk13.yellow(` \u26A0 ${w}`));
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
const row = insertMemory(cortex, {
|
|
2267
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2268
|
+
author,
|
|
2269
|
+
content: message,
|
|
2270
|
+
source_ids: []
|
|
2271
|
+
});
|
|
2272
|
+
if (!opts.silent) {
|
|
2273
|
+
const badge = chalk13.cyan(`[${cortex}]`);
|
|
2274
|
+
const ts = chalk13.gray(row.ts.slice(0, 16).replace("T", " "));
|
|
2275
|
+
console.log(`${chalk13.green("\u2713")} ${badge} memory added ${ts}`);
|
|
2276
|
+
console.log(` ${row.content}`);
|
|
2277
|
+
}
|
|
2278
|
+
if (opts.push) {
|
|
2279
|
+
const adapter = getSyncAdapter();
|
|
2280
|
+
if (adapter?.isAvailable()) {
|
|
2281
|
+
try {
|
|
2282
|
+
const result = await adapter.push(cortex);
|
|
2283
|
+
if (!opts.silent && result.pushed > 0) {
|
|
2284
|
+
console.log(chalk13.dim(` Pushed ${result.pushed} memories to ${adapter.name}`));
|
|
2285
|
+
}
|
|
2286
|
+
} catch {
|
|
2287
|
+
if (!opts.silent) {
|
|
2288
|
+
console.log(chalk13.dim(" Push skipped (remote unavailable) \u2014 will push on next sync"));
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
closeCortexDb(cortex);
|
|
2294
|
+
});
|
|
2295
|
+
async function showMemories(opts) {
|
|
2176
2296
|
const config = getConfig();
|
|
2177
2297
|
const cortex = config.cortex?.active;
|
|
2178
2298
|
if (!cortex) {
|
|
@@ -2200,7 +2320,11 @@ var memoryCommand = new Command13("memory").description("Show current memories f
|
|
|
2200
2320
|
console.log(chalk13.dim(`
|
|
2201
2321
|
${memories.length} memories`));
|
|
2202
2322
|
closeCortexDb(cortex);
|
|
2323
|
+
}
|
|
2324
|
+
var memoryCommand = new Command13("memory").description("Show current memories from local store").option("--history", "Show recent memory timeline").action(async (opts) => {
|
|
2325
|
+
await showMemories(opts);
|
|
2203
2326
|
});
|
|
2327
|
+
memoryCommand.addCommand(addCommand);
|
|
2204
2328
|
|
|
2205
2329
|
// src/commands/curator-cmd.ts
|
|
2206
2330
|
import { Command as Command14 } from "commander";
|