open-think 0.3.3 → 0.3.5

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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getRepoPath,
4
4
  getThinkConfigDir
5
- } from "./chunk-DCTG6IK4.js";
5
+ } from "./chunk-HUBRLTY3.js";
6
6
 
7
7
  // src/lib/git.ts
8
8
  import { execFileSync } from "child_process";
@@ -60,6 +60,7 @@ function ensureThinkDirs() {
60
60
  }
61
61
 
62
62
  export {
63
+ getThinkDir,
63
64
  getThinkConfigDir,
64
65
  getThinkDataDir,
65
66
  getEngramsDir,
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ensureThinkDirs,
4
4
  getEngramDbPath
5
- } from "./chunk-DCTG6IK4.js";
5
+ } from "./chunk-HUBRLTY3.js";
6
6
 
7
7
  // src/db/memory-queries.ts
8
8
  import { v7 as uuidv7 } from "uuid";
@@ -11,8 +11,8 @@ import {
11
11
  listRemoteBranches,
12
12
  migrateToBuckets,
13
13
  readFileFromBranch
14
- } from "./chunk-ZKUJ5M2W.js";
15
- import "./chunk-DCTG6IK4.js";
14
+ } from "./chunk-BBCWF24H.js";
15
+ import "./chunk-HUBRLTY3.js";
16
16
  export {
17
17
  appendAndCommit,
18
18
  branchExists,
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  setLongtermSummary,
15
15
  setSyncCursor,
16
16
  tombstoneMemory
17
- } from "./chunk-OFGWR45G.js";
17
+ } from "./chunk-LN2TIS5R.js";
18
18
  import {
19
19
  appendAndCommit,
20
20
  countBranchFileLines,
@@ -28,18 +28,19 @@ import {
28
28
  migrateToBuckets,
29
29
  readFileFromBranch,
30
30
  saveConfig
31
- } from "./chunk-ZKUJ5M2W.js";
31
+ } from "./chunk-BBCWF24H.js";
32
32
  import {
33
33
  ensureThinkDirs,
34
34
  getCuratorMdPath,
35
35
  getEngramsDir,
36
36
  getLongtermPath,
37
- getThinkDataDir
38
- } from "./chunk-DCTG6IK4.js";
37
+ getThinkDataDir,
38
+ getThinkDir
39
+ } from "./chunk-HUBRLTY3.js";
39
40
 
40
41
  // src/index.ts
41
- import fs11 from "fs";
42
- import path5 from "path";
42
+ import fs13 from "fs";
43
+ import path7 from "path";
43
44
  import { Command as Command20 } from "commander";
44
45
 
45
46
  // src/commands/log.ts
@@ -1010,7 +1011,7 @@ ${shown.length} events` + (entries.length > limit ? ` (showing last ${limit} of
1010
1011
  });
1011
1012
 
1012
1013
  // src/commands/cortex.ts
1013
- import fs8 from "fs";
1014
+ import fs9 from "fs";
1014
1015
  import { Command as Command9 } from "commander";
1015
1016
  import chalk9 from "chalk";
1016
1017
  import readline2 from "readline";
@@ -1555,6 +1556,146 @@ function getSyncAdapter() {
1555
1556
  return null;
1556
1557
  }
1557
1558
 
1559
+ // src/lib/auto-curate.ts
1560
+ import fs8 from "fs";
1561
+ import path5 from "path";
1562
+ import crypto2 from "crypto";
1563
+ import { execFileSync } from "child_process";
1564
+ var DEFAULT_INTERVAL_SECONDS = 300;
1565
+ function getHome() {
1566
+ const home = process.env.HOME;
1567
+ if (!home) throw new Error("HOME environment variable is not set");
1568
+ return home;
1569
+ }
1570
+ function getLaunchAgentsDir() {
1571
+ return path5.join(getHome(), "Library", "LaunchAgents");
1572
+ }
1573
+ function getAgentLabel() {
1574
+ const thinkHome = process.env.THINK_HOME;
1575
+ if (!thinkHome) return "ai.openthink.curate.default";
1576
+ const hash = crypto2.createHash("sha1").update(thinkHome).digest("hex").slice(0, 8);
1577
+ return `ai.openthink.curate.${hash}`;
1578
+ }
1579
+ function getPlistPath(label = getAgentLabel()) {
1580
+ return path5.join(getLaunchAgentsDir(), `${label}.plist`);
1581
+ }
1582
+ function getLogPath() {
1583
+ return path5.join(getThinkDir(), "auto-curate.log");
1584
+ }
1585
+ function resolveThinkBinary() {
1586
+ const arg1 = process.argv[1];
1587
+ if (arg1 && fs8.existsSync(arg1)) return arg1;
1588
+ throw new Error("Could not resolve think binary path");
1589
+ }
1590
+ function resolveNodeBinary() {
1591
+ return process.execPath;
1592
+ }
1593
+ function renderPlist(opts) {
1594
+ const envBlock = opts.thinkHome ? ` <key>EnvironmentVariables</key>
1595
+ <dict>
1596
+ <key>THINK_HOME</key>
1597
+ <string>${escapeXml(opts.thinkHome)}</string>
1598
+ </dict>
1599
+ ` : "";
1600
+ return `<?xml version="1.0" encoding="UTF-8"?>
1601
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1602
+ <plist version="1.0">
1603
+ <dict>
1604
+ <key>Label</key>
1605
+ <string>${escapeXml(opts.label)}</string>
1606
+ <key>ProgramArguments</key>
1607
+ <array>
1608
+ <string>${escapeXml(opts.nodePath)}</string>
1609
+ <string>${escapeXml(opts.thinkPath)}</string>
1610
+ <string>curate</string>
1611
+ <string>--if-idle</string>
1612
+ </array>
1613
+ <key>StartInterval</key>
1614
+ <integer>${opts.intervalSeconds}</integer>
1615
+ <key>RunAtLoad</key>
1616
+ <false/>
1617
+ ${envBlock} <key>StandardOutPath</key>
1618
+ <string>${escapeXml(opts.logPath)}</string>
1619
+ <key>StandardErrorPath</key>
1620
+ <string>${escapeXml(opts.logPath)}</string>
1621
+ </dict>
1622
+ </plist>
1623
+ `;
1624
+ }
1625
+ function escapeXml(s) {
1626
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1627
+ }
1628
+ function installAgent(opts = {}) {
1629
+ if (process.platform !== "darwin") {
1630
+ throw new Error("auto-curate install currently supports macOS only. For Linux, run `think curate --if-idle` from cron or systemd.");
1631
+ }
1632
+ const label = getAgentLabel();
1633
+ const plistPath = getPlistPath(label);
1634
+ const agentsDir = getLaunchAgentsDir();
1635
+ fs8.mkdirSync(agentsDir, { recursive: true });
1636
+ fs8.mkdirSync(getThinkDir(), { recursive: true });
1637
+ const plist = renderPlist({
1638
+ label,
1639
+ nodePath: resolveNodeBinary(),
1640
+ thinkPath: resolveThinkBinary(),
1641
+ thinkHome: process.env.THINK_HOME,
1642
+ intervalSeconds: opts.intervalSeconds ?? DEFAULT_INTERVAL_SECONDS,
1643
+ logPath: getLogPath()
1644
+ });
1645
+ fs8.writeFileSync(plistPath, plist, { mode: 420 });
1646
+ try {
1647
+ execFileSync("launchctl", ["unload", plistPath], { stdio: "ignore" });
1648
+ } catch {
1649
+ }
1650
+ execFileSync("launchctl", ["load", plistPath], { stdio: "ignore" });
1651
+ return { label, plistPath };
1652
+ }
1653
+ function uninstallAgent() {
1654
+ const plistPath = getPlistPath();
1655
+ if (!fs8.existsSync(plistPath)) {
1656
+ return { removed: false, plistPath };
1657
+ }
1658
+ if (process.platform === "darwin") {
1659
+ try {
1660
+ execFileSync("launchctl", ["unload", plistPath], { stdio: "ignore" });
1661
+ } catch {
1662
+ }
1663
+ }
1664
+ fs8.unlinkSync(plistPath);
1665
+ return { removed: true, plistPath };
1666
+ }
1667
+ function getAgentStatus() {
1668
+ const label = getAgentLabel();
1669
+ const plistPath = getPlistPath(label);
1670
+ const installed = fs8.existsSync(plistPath);
1671
+ let loaded = false;
1672
+ let intervalSeconds = null;
1673
+ if (installed && process.platform === "darwin") {
1674
+ try {
1675
+ const out = execFileSync("launchctl", ["list", label], { stdio: ["ignore", "pipe", "ignore"] }).toString();
1676
+ loaded = out.trim().length > 0;
1677
+ } catch {
1678
+ loaded = false;
1679
+ }
1680
+ try {
1681
+ const plist = fs8.readFileSync(plistPath, "utf-8");
1682
+ const match = plist.match(/<key>StartInterval<\/key>\s*<integer>(\d+)<\/integer>/);
1683
+ if (match) intervalSeconds = parseInt(match[1], 10);
1684
+ } catch {
1685
+ }
1686
+ }
1687
+ let lastRunAt = null;
1688
+ const logPath = getLogPath();
1689
+ if (fs8.existsSync(logPath)) {
1690
+ try {
1691
+ const stat = fs8.statSync(logPath);
1692
+ lastRunAt = stat.mtime;
1693
+ } catch {
1694
+ }
1695
+ }
1696
+ return { installed, label, plistPath, loaded, lastRunAt, intervalSeconds };
1697
+ }
1698
+
1558
1699
  // src/commands/cortex.ts
1559
1700
  function prompt2(question, defaultValue) {
1560
1701
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
@@ -1595,7 +1736,7 @@ cortexCommand.addCommand(new Command9("setup").description("Configure a sync bac
1595
1736
  const adapter = getSyncAdapter();
1596
1737
  if (adapter) {
1597
1738
  try {
1598
- const { ensureRepoCloned: ensureRepoCloned2 } = await import("./git-TG6OJFBT.js");
1739
+ const { ensureRepoCloned: ensureRepoCloned2 } = await import("./git-CMDUX3KB.js");
1599
1740
  ensureRepoCloned2();
1600
1741
  console.log(chalk9.green("\u2713") + " Repo cloned");
1601
1742
  } catch (err) {
@@ -1636,8 +1777,8 @@ cortexCommand.addCommand(new Command9("list").description("Show all cortexes").a
1636
1777
  const config = getConfig();
1637
1778
  const engramsDir = getEngramsDir();
1638
1779
  const localCortexes = [];
1639
- if (fs8.existsSync(engramsDir)) {
1640
- for (const file of fs8.readdirSync(engramsDir)) {
1780
+ if (fs9.existsSync(engramsDir)) {
1781
+ for (const file of fs9.readdirSync(engramsDir)) {
1641
1782
  if (file.endsWith(".db") && !file.endsWith("-shm") && !file.endsWith("-wal")) {
1642
1783
  localCortexes.push(file.replace(".db", ""));
1643
1784
  }
@@ -1678,7 +1819,7 @@ cortexCommand.addCommand(new Command9("switch").argument("<name>", "Cortex name"
1678
1819
  }
1679
1820
  const engramsDir = getEngramsDir();
1680
1821
  const dbPath = `${engramsDir}/${name}.db`;
1681
- if (!fs8.existsSync(dbPath)) {
1822
+ if (!fs9.existsSync(dbPath)) {
1682
1823
  const adapter = getSyncAdapter();
1683
1824
  if (adapter?.isAvailable()) {
1684
1825
  try {
@@ -1792,272 +1933,467 @@ cortexCommand.addCommand(new Command9("status").description("Show sync status fo
1792
1933
  }
1793
1934
  closeCortexDb(cortex);
1794
1935
  }));
1936
+ var autoCurateCommand = new Command9("auto-curate").description("Manage scheduled background curation (macOS LaunchAgent)");
1937
+ autoCurateCommand.addCommand(new Command9("enable").description("Install a LaunchAgent that runs `think curate --if-idle` every 5 minutes").option("--interval <seconds>", "Scheduler cadence in seconds (default 300)", (v) => parseInt(v, 10)).action((opts) => {
1938
+ try {
1939
+ const { label, plistPath } = installAgent({ intervalSeconds: opts.interval });
1940
+ console.log(chalk9.green("\u2713") + ` Auto-curation enabled`);
1941
+ console.log(chalk9.dim(` Label: ${label}`));
1942
+ console.log(chalk9.dim(` Plist: ${plistPath}`));
1943
+ if (process.env.THINK_HOME) {
1944
+ console.log(chalk9.dim(` THINK_HOME: ${process.env.THINK_HOME}`));
1945
+ }
1946
+ } catch (err) {
1947
+ console.error(chalk9.red(err instanceof Error ? err.message : String(err)));
1948
+ process.exit(1);
1949
+ }
1950
+ }));
1951
+ autoCurateCommand.addCommand(new Command9("disable").description("Remove the auto-curation LaunchAgent for this workspace").action(() => {
1952
+ const { removed, plistPath } = uninstallAgent();
1953
+ if (removed) {
1954
+ console.log(chalk9.green("\u2713") + ` Auto-curation disabled (${plistPath})`);
1955
+ } else {
1956
+ console.log(chalk9.dim(`No auto-curation agent installed (${plistPath})`));
1957
+ }
1958
+ }));
1959
+ autoCurateCommand.addCommand(new Command9("status").description("Show auto-curation scheduler status").action(() => {
1960
+ const s = getAgentStatus();
1961
+ console.log(`Label: ${chalk9.cyan(s.label)}`);
1962
+ console.log(`Installed: ${s.installed ? chalk9.green("yes") : chalk9.dim("no")}`);
1963
+ console.log(`Loaded: ${s.loaded ? chalk9.green("yes") : chalk9.dim("no")}`);
1964
+ if (s.intervalSeconds) {
1965
+ console.log(`Interval: ${s.intervalSeconds}s`);
1966
+ }
1967
+ console.log(`Plist: ${s.plistPath}`);
1968
+ if (s.lastRunAt) {
1969
+ console.log(`Last log: ${s.lastRunAt.toISOString()}`);
1970
+ } else {
1971
+ console.log(`Last log: ${chalk9.dim("(no log file yet)")}`);
1972
+ }
1973
+ }));
1974
+ cortexCommand.addCommand(autoCurateCommand);
1795
1975
 
1796
1976
  // src/commands/curate.ts
1797
1977
  import { Command as Command10 } from "commander";
1798
1978
  import readline3 from "readline";
1799
1979
  import chalk10 from "chalk";
1800
- var curateCommand = new Command10("curate").description("Run curation: evaluate pending engrams and promote to memories").option("--dry-run", "Preview what would be committed without saving").option("--consolidate", "Run long-term memory consolidation only (no curation)").option("--episode <key>", "Curate a specific episode into a narrative memory").action(async (opts) => {
1980
+
1981
+ // src/lib/curate-lock.ts
1982
+ import fs10 from "fs";
1983
+ import path6 from "path";
1984
+ function getLockPath(cortex) {
1985
+ return path6.join(getThinkDir(), `curate-${cortex}.lock`);
1986
+ }
1987
+ function isProcessAlive(pid) {
1988
+ if (!Number.isFinite(pid) || pid <= 0) return false;
1989
+ try {
1990
+ process.kill(pid, 0);
1991
+ return true;
1992
+ } catch (err) {
1993
+ const code = err.code;
1994
+ return code === "EPERM";
1995
+ }
1996
+ }
1997
+ function acquireCurateLock(cortex) {
1998
+ const lockPath = getLockPath(cortex);
1999
+ fs10.mkdirSync(path6.dirname(lockPath), { recursive: true });
2000
+ try {
2001
+ fs10.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
2002
+ return makeAcquired(lockPath);
2003
+ } catch (err) {
2004
+ if (err.code !== "EEXIST") throw err;
2005
+ }
2006
+ let heldByPid = null;
2007
+ try {
2008
+ const raw = fs10.readFileSync(lockPath, "utf-8").trim();
2009
+ const parsed = parseInt(raw, 10);
2010
+ if (Number.isFinite(parsed) && parsed > 0) heldByPid = parsed;
2011
+ } catch {
2012
+ }
2013
+ if (heldByPid && isProcessAlive(heldByPid)) {
2014
+ return { acquired: false, heldByPid };
2015
+ }
2016
+ try {
2017
+ fs10.unlinkSync(lockPath);
2018
+ } catch {
2019
+ }
2020
+ try {
2021
+ fs10.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
2022
+ return makeAcquired(lockPath);
2023
+ } catch (err) {
2024
+ if (err.code === "EEXIST") {
2025
+ let nowHeldBy = null;
2026
+ try {
2027
+ const raw = fs10.readFileSync(lockPath, "utf-8").trim();
2028
+ const parsed = parseInt(raw, 10);
2029
+ if (Number.isFinite(parsed) && parsed > 0) nowHeldBy = parsed;
2030
+ } catch {
2031
+ }
2032
+ return { acquired: false, heldByPid: nowHeldBy };
2033
+ }
2034
+ throw err;
2035
+ }
2036
+ }
2037
+ function makeAcquired(lockPath) {
2038
+ let released = false;
2039
+ const unlinkIfHeld = () => {
2040
+ if (released) return;
2041
+ released = true;
2042
+ try {
2043
+ fs10.unlinkSync(lockPath);
2044
+ } catch {
2045
+ }
2046
+ };
2047
+ const exitHandler = () => {
2048
+ unlinkIfHeld();
2049
+ };
2050
+ const sigintHandler = () => {
2051
+ unlinkIfHeld();
2052
+ process.exit(130);
2053
+ };
2054
+ const sigtermHandler = () => {
2055
+ unlinkIfHeld();
2056
+ process.exit(143);
2057
+ };
2058
+ process.on("exit", exitHandler);
2059
+ process.on("SIGINT", sigintHandler);
2060
+ process.on("SIGTERM", sigtermHandler);
2061
+ const release = () => {
2062
+ if (released) {
2063
+ process.removeListener("exit", exitHandler);
2064
+ process.removeListener("SIGINT", sigintHandler);
2065
+ process.removeListener("SIGTERM", sigtermHandler);
2066
+ return;
2067
+ }
2068
+ unlinkIfHeld();
2069
+ process.removeListener("exit", exitHandler);
2070
+ process.removeListener("SIGINT", sigintHandler);
2071
+ process.removeListener("SIGTERM", sigtermHandler);
2072
+ };
2073
+ return { acquired: true, release };
2074
+ }
2075
+
2076
+ // src/commands/curate.ts
2077
+ var curateCommand = new Command10("curate").description("Run curation: evaluate pending engrams and promote to memories").option("--dry-run", "Preview what would be committed without saving").option("--consolidate", "Run long-term memory consolidation only (no curation)").option("--episode <key>", "Curate a specific episode into a narrative memory").option("--if-idle", "Only curate if the user appears idle (used by auto-curation scheduler)").action(async (opts) => {
1801
2078
  const config = getConfig();
1802
2079
  const cortex = config.cortex?.active;
1803
2080
  if (!cortex) {
2081
+ if (opts.ifIdle) {
2082
+ return;
2083
+ }
1804
2084
  console.error(chalk10.red("No active cortex. Run: think cortex switch <name>"));
1805
2085
  process.exit(1);
1806
2086
  }
1807
2087
  const author = config.cortex.author;
1808
- const adapter = getSyncAdapter();
1809
- if (adapter?.isAvailable()) {
1810
- try {
1811
- const pullResult = await adapter.pull(cortex);
1812
- if (pullResult.pulled > 0) {
1813
- console.log(chalk10.dim(` Pulled ${pullResult.pulled} memories from ${adapter.name}`));
2088
+ let releaseLock = () => {
2089
+ };
2090
+ if (!opts.dryRun) {
2091
+ const lock = acquireCurateLock(cortex);
2092
+ if (!lock.acquired) {
2093
+ if (opts.ifIdle) {
2094
+ if (process.env.THINK_IDLE_DEBUG) {
2095
+ console.log(chalk10.dim(`[auto-curate] skipped: another curation is running (pid ${lock.heldByPid ?? "?"})`));
2096
+ }
2097
+ } else {
2098
+ console.log(chalk10.yellow(`Another curation is already running (pid ${lock.heldByPid ?? "?"}). Skipping.`));
1814
2099
  }
1815
- } catch {
1816
- console.log(chalk10.dim(" Sync pull skipped (remote unavailable)"));
1817
- }
1818
- }
1819
- if (opts.episode) {
1820
- const episodeEngrams = getPendingEpisodeEngrams(cortex, opts.episode);
1821
- if (episodeEngrams.length === 0) {
1822
- console.log(chalk10.dim(`No pending engrams for episode: ${opts.episode}`));
1823
2100
  closeCortexDb(cortex);
1824
2101
  return;
1825
2102
  }
1826
- const existingMemoryRow = getMemoryByEpisodeKey(cortex, opts.episode);
1827
- const existingMemory = existingMemoryRow ? {
1828
- ts: existingMemoryRow.ts,
1829
- author: existingMemoryRow.author,
1830
- content: existingMemoryRow.content,
1831
- source_ids: JSON.parse(existingMemoryRow.source_ids)
1832
- } : null;
1833
- console.log(chalk10.cyan(`Curating episode: ${opts.episode} (${episodeEngrams.length} engrams${existingMemory ? ", updating existing narrative" : ""})...`));
1834
- const prompt3 = assembleEpisodeCurationPrompt({
1835
- episodeKey: opts.episode,
1836
- pendingEngrams: episodeEngrams,
1837
- existingMemory,
1838
- author
1839
- });
1840
- if (opts.dryRun) {
1841
- console.log();
1842
- console.log(chalk10.cyan("Episode prompt would be sent to LLM:"));
1843
- console.log(chalk10.dim(` ${episodeEngrams.length} engrams, ${existingMemory ? "updating" : "creating"} narrative`));
1844
- for (const e of episodeEngrams) {
1845
- const ts = e.created_at.slice(0, 16).replace("T", " ");
1846
- console.log(chalk10.dim(` ${ts}: ${e.content.slice(0, 100)}${e.content.length > 100 ? "..." : ""}`));
2103
+ releaseLock = lock.release;
2104
+ }
2105
+ try {
2106
+ if (opts.ifIdle && !opts.episode && !opts.consolidate) {
2107
+ const shouldRun = shouldRunIdleCuration(cortex, config.cortex);
2108
+ if (!shouldRun.run) {
2109
+ if (process.env.THINK_IDLE_DEBUG) {
2110
+ console.log(chalk10.dim(`[auto-curate] skipped: ${shouldRun.reason}`));
2111
+ }
2112
+ closeCortexDb(cortex);
2113
+ return;
2114
+ }
2115
+ if (process.env.THINK_IDLE_DEBUG) {
2116
+ console.log(chalk10.dim(`[auto-curate] running: ${shouldRun.reason}`));
1847
2117
  }
1848
- closeCortexDb(cortex);
1849
- return;
1850
- }
1851
- let narrative;
1852
- try {
1853
- narrative = await runEpisodeCuration(prompt3);
1854
- } catch (err) {
1855
- const message = err instanceof Error ? err.message : String(err);
1856
- console.error(chalk10.red(`Episode curation failed: ${message}`));
1857
- closeCortexDb(cortex);
1858
- process.exit(1);
1859
- }
1860
- if (existingMemoryRow) {
1861
- tombstoneMemory(cortex, existingMemoryRow.id);
1862
2118
  }
1863
- const allSourceIds = [
1864
- ...existingMemory?.source_ids ?? [],
1865
- ...episodeEngrams.map((e) => e.id)
1866
- ];
1867
- insertMemory(cortex, {
1868
- ts: (/* @__PURE__ */ new Date()).toISOString(),
1869
- author,
1870
- content: narrative,
1871
- source_ids: allSourceIds,
1872
- episode_key: opts.episode
1873
- });
1874
- markPromoted(cortex, episodeEngrams.map((e) => e.id));
2119
+ const adapter = getSyncAdapter();
1875
2120
  if (adapter?.isAvailable()) {
1876
2121
  try {
1877
- const pushResult = await adapter.push(cortex);
1878
- if (pushResult.pushed > 0) {
1879
- console.log(chalk10.dim(` Pushed ${pushResult.pushed} memories to ${adapter.name}`));
2122
+ const pullResult = await adapter.pull(cortex);
2123
+ if (pullResult.pulled > 0) {
2124
+ console.log(chalk10.dim(` Pulled ${pullResult.pulled} memories from ${adapter.name}`));
1880
2125
  }
1881
2126
  } catch {
1882
- console.log(chalk10.dim(" Sync push skipped (remote unavailable)"));
2127
+ console.log(chalk10.dim(" Sync pull skipped (remote unavailable)"));
1883
2128
  }
1884
2129
  }
1885
- console.log();
1886
- console.log(`${chalk10.green("\u2713")} Episode curated: ${opts.episode}`);
1887
- console.log(` ${episodeEngrams.length} engrams synthesized into narrative`);
1888
- closeCortexDb(cortex);
1889
- return;
1890
- }
1891
- const allMemories = getMemories(cortex);
1892
- const memoryEntries = allMemories.map((m) => ({
1893
- ts: m.ts,
1894
- author: m.author,
1895
- content: m.content,
1896
- source_ids: JSON.parse(m.source_ids)
1897
- }));
1898
- const { recent, older } = filterRecentMemories(memoryEntries);
1899
- const longtermSummary = getLongtermSummary(cortex);
1900
- if (opts.consolidate) {
1901
- if (older.length === 0) {
1902
- console.log(chalk10.dim("No memories older than 2 weeks to consolidate."));
1903
- return;
1904
- }
1905
- console.log(chalk10.cyan(`Consolidating ${older.length} older memories into long-term summary...`));
1906
- try {
1907
- const newSummary = await runConsolidation(longtermSummary, older);
2130
+ if (opts.episode) {
2131
+ const episodeEngrams = getPendingEpisodeEngrams(cortex, opts.episode);
2132
+ if (episodeEngrams.length === 0) {
2133
+ console.log(chalk10.dim(`No pending engrams for episode: ${opts.episode}`));
2134
+ closeCortexDb(cortex);
2135
+ return;
2136
+ }
2137
+ const existingMemoryRow = getMemoryByEpisodeKey(cortex, opts.episode);
2138
+ const existingMemory = existingMemoryRow ? {
2139
+ ts: existingMemoryRow.ts,
2140
+ author: existingMemoryRow.author,
2141
+ content: existingMemoryRow.content,
2142
+ source_ids: JSON.parse(existingMemoryRow.source_ids)
2143
+ } : null;
2144
+ console.log(chalk10.cyan(`Curating episode: ${opts.episode} (${episodeEngrams.length} engrams${existingMemory ? ", updating existing narrative" : ""})...`));
2145
+ const prompt3 = assembleEpisodeCurationPrompt({
2146
+ episodeKey: opts.episode,
2147
+ pendingEngrams: episodeEngrams,
2148
+ existingMemory,
2149
+ author
2150
+ });
1908
2151
  if (opts.dryRun) {
1909
2152
  console.log();
1910
- console.log(chalk10.cyan("Proposed long-term summary:"));
1911
- console.log(newSummary);
2153
+ console.log(chalk10.cyan("Episode prompt would be sent to LLM:"));
2154
+ console.log(chalk10.dim(` ${episodeEngrams.length} engrams, ${existingMemory ? "updating" : "creating"} narrative`));
2155
+ for (const e of episodeEngrams) {
2156
+ const ts = e.created_at.slice(0, 16).replace("T", " ");
2157
+ console.log(chalk10.dim(` ${ts}: ${e.content.slice(0, 100)}${e.content.length > 100 ? "..." : ""}`));
2158
+ }
2159
+ closeCortexDb(cortex);
2160
+ return;
2161
+ }
2162
+ let narrative;
2163
+ try {
2164
+ narrative = await runEpisodeCuration(prompt3);
2165
+ } catch (err) {
2166
+ const message = err instanceof Error ? err.message : String(err);
2167
+ console.error(chalk10.red(`Episode curation failed: ${message}`));
2168
+ closeCortexDb(cortex);
2169
+ process.exit(1);
2170
+ }
2171
+ if (existingMemoryRow) {
2172
+ tombstoneMemory(cortex, existingMemoryRow.id);
2173
+ }
2174
+ const allSourceIds = [
2175
+ ...existingMemory?.source_ids ?? [],
2176
+ ...episodeEngrams.map((e) => e.id)
2177
+ ];
2178
+ insertMemory(cortex, {
2179
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
2180
+ author,
2181
+ content: narrative,
2182
+ source_ids: allSourceIds,
2183
+ episode_key: opts.episode
2184
+ });
2185
+ markPromoted(cortex, episodeEngrams.map((e) => e.id));
2186
+ if (adapter?.isAvailable()) {
2187
+ try {
2188
+ const pushResult = await adapter.push(cortex);
2189
+ if (pushResult.pushed > 0) {
2190
+ console.log(chalk10.dim(` Pushed ${pushResult.pushed} memories to ${adapter.name}`));
2191
+ }
2192
+ } catch {
2193
+ console.log(chalk10.dim(" Sync push skipped (remote unavailable)"));
2194
+ }
2195
+ }
2196
+ console.log();
2197
+ console.log(`${chalk10.green("\u2713")} Episode curated: ${opts.episode}`);
2198
+ console.log(` ${episodeEngrams.length} engrams synthesized into narrative`);
2199
+ closeCortexDb(cortex);
2200
+ return;
2201
+ }
2202
+ const allMemories = getMemories(cortex);
2203
+ const memoryEntries = allMemories.map((m) => ({
2204
+ ts: m.ts,
2205
+ author: m.author,
2206
+ content: m.content,
2207
+ source_ids: JSON.parse(m.source_ids)
2208
+ }));
2209
+ const { recent, older } = filterRecentMemories(memoryEntries);
2210
+ const longtermSummary = getLongtermSummary(cortex);
2211
+ if (opts.consolidate) {
2212
+ if (older.length === 0) {
2213
+ console.log(chalk10.dim("No memories older than 2 weeks to consolidate."));
1912
2214
  return;
1913
2215
  }
1914
- setLongtermSummary(cortex, newSummary);
1915
- console.log(chalk10.green("\u2713") + ` Long-term summary updated (${older.length} memories consolidated)`);
2216
+ console.log(chalk10.cyan(`Consolidating ${older.length} older memories into long-term summary...`));
2217
+ try {
2218
+ const newSummary = await runConsolidation(longtermSummary, older);
2219
+ if (opts.dryRun) {
2220
+ console.log();
2221
+ console.log(chalk10.cyan("Proposed long-term summary:"));
2222
+ console.log(newSummary);
2223
+ return;
2224
+ }
2225
+ setLongtermSummary(cortex, newSummary);
2226
+ console.log(chalk10.green("\u2713") + ` Long-term summary updated (${older.length} memories consolidated)`);
2227
+ } catch (err) {
2228
+ const message = err instanceof Error ? err.message : String(err);
2229
+ console.error(chalk10.red(`Consolidation failed: ${message}`));
2230
+ process.exit(1);
2231
+ }
2232
+ return;
2233
+ }
2234
+ const pending = getPendingEngrams(cortex);
2235
+ if (pending.length === 0) {
2236
+ console.log(chalk10.dim("No pending engrams to evaluate."));
2237
+ closeCortexDb(cortex);
2238
+ return;
2239
+ }
2240
+ console.log(chalk10.cyan(`Evaluating ${pending.length} engrams (${recent.length} recent memories, long-term summary ${longtermSummary ? "loaded" : "absent"})...`));
2241
+ const curatorMd = readCuratorMd();
2242
+ const curationPrompt = assembleCurationPrompt({
2243
+ recentMemories: recent,
2244
+ longtermSummary,
2245
+ curatorMd,
2246
+ pendingEngrams: pending,
2247
+ author,
2248
+ selectivity: config.cortex?.selectivity,
2249
+ granularity: config.cortex?.granularity,
2250
+ maxMemoriesPerRun: config.cortex?.maxMemoriesPerRun
2251
+ });
2252
+ let curationResult;
2253
+ try {
2254
+ curationResult = await runCuration(curationPrompt);
1916
2255
  } catch (err) {
1917
2256
  const message = err instanceof Error ? err.message : String(err);
1918
- console.error(chalk10.red(`Consolidation failed: ${message}`));
2257
+ console.error(chalk10.red(`Curation failed: ${message}`));
2258
+ closeCortexDb(cortex);
1919
2259
  process.exit(1);
1920
2260
  }
1921
- return;
1922
- }
1923
- const pending = getPendingEngrams(cortex);
1924
- if (pending.length === 0) {
1925
- console.log(chalk10.dim("No pending engrams to evaluate."));
1926
- closeCortexDb(cortex);
1927
- return;
1928
- }
1929
- console.log(chalk10.cyan(`Evaluating ${pending.length} engrams (${recent.length} recent memories, long-term summary ${longtermSummary ? "loaded" : "absent"})...`));
1930
- const curatorMd = readCuratorMd();
1931
- const curationPrompt = assembleCurationPrompt({
1932
- recentMemories: recent,
1933
- longtermSummary,
1934
- curatorMd,
1935
- pendingEngrams: pending,
1936
- author,
1937
- selectivity: config.cortex?.selectivity,
1938
- granularity: config.cortex?.granularity,
1939
- maxMemoriesPerRun: config.cortex?.maxMemoriesPerRun
1940
- });
1941
- let curationResult;
1942
- try {
1943
- curationResult = await runCuration(curationPrompt);
1944
- } catch (err) {
1945
- const message = err instanceof Error ? err.message : String(err);
1946
- console.error(chalk10.red(`Curation failed: ${message}`));
1947
- closeCortexDb(cortex);
1948
- process.exit(1);
1949
- }
1950
- const newEntries = curationResult.memories;
1951
- for (const entry of newEntries) {
1952
- entry.author = author;
1953
- if (!entry.ts) entry.ts = (/* @__PURE__ */ new Date()).toISOString();
1954
- }
1955
- const promotedIds = /* @__PURE__ */ new Set();
1956
- for (const entry of newEntries) {
1957
- for (const id of entry.source_ids) {
1958
- promotedIds.add(id);
2261
+ const newEntries = curationResult.memories;
2262
+ for (const entry of newEntries) {
2263
+ entry.author = author;
2264
+ if (!entry.ts) entry.ts = (/* @__PURE__ */ new Date()).toISOString();
1959
2265
  }
1960
- }
1961
- const pendingIdSet = new Set(pending.map((e) => e.id));
1962
- const purgedIds = curationResult.purgeIds.filter((id) => pendingIdSet.has(id) && !promotedIds.has(id));
1963
- const heldCount = pending.length - promotedIds.size - purgedIds.length;
1964
- if (opts.dryRun) {
1965
- console.log();
1966
- if (newEntries.length === 0) {
1967
- console.log(chalk10.dim("Curator would produce no new memories."));
1968
- } else {
1969
- console.log(chalk10.cyan("Would append:"));
1970
- for (const entry of newEntries) {
1971
- console.log(chalk10.green(` + `) + `[${entry.ts}] ${entry.content}`);
2266
+ const promotedIds = /* @__PURE__ */ new Set();
2267
+ for (const entry of newEntries) {
2268
+ for (const id of entry.source_ids) {
2269
+ promotedIds.add(id);
1972
2270
  }
1973
2271
  }
1974
- console.log();
1975
- console.log(`${pending.length} evaluated, ${newEntries.length} would promote, ${purgedIds.length} would purge, ${heldCount} would stay pending`);
1976
- closeCortexDb(cortex);
1977
- return;
1978
- }
1979
- if (config.cortex?.confirmBeforeCommit && newEntries.length > 0) {
1980
- console.log();
1981
- console.log(chalk10.cyan("Proposed memories:"));
1982
- for (let i = 0; i < newEntries.length; i++) {
1983
- console.log(chalk10.green(` ${i + 1}. `) + newEntries[i].content);
1984
- }
1985
- console.log();
1986
- const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
1987
- const answer = await new Promise((resolve) => {
1988
- rl.question(" Save these memories? [Y/n/edit] ", (ans) => {
1989
- rl.close();
1990
- resolve(ans.trim().toLowerCase());
1991
- });
1992
- });
1993
- if (answer === "n" || answer === "no") {
1994
- console.log(chalk10.dim(" Aborted. Engrams left as pending."));
2272
+ const pendingIdSet = new Set(pending.map((e) => e.id));
2273
+ const purgedIds = curationResult.purgeIds.filter((id) => pendingIdSet.has(id) && !promotedIds.has(id));
2274
+ const heldCount = pending.length - promotedIds.size - purgedIds.length;
2275
+ if (opts.dryRun) {
2276
+ console.log();
2277
+ if (newEntries.length === 0) {
2278
+ console.log(chalk10.dim("Curator would produce no new memories."));
2279
+ } else {
2280
+ console.log(chalk10.cyan("Would append:"));
2281
+ for (const entry of newEntries) {
2282
+ console.log(chalk10.green(` + `) + `[${entry.ts}] ${entry.content}`);
2283
+ }
2284
+ }
2285
+ console.log();
2286
+ console.log(`${pending.length} evaluated, ${newEntries.length} would promote, ${purgedIds.length} would purge, ${heldCount} would stay pending`);
1995
2287
  closeCortexDb(cortex);
1996
2288
  return;
1997
2289
  }
1998
- if (answer === "e" || answer === "edit") {
2290
+ if (config.cortex?.confirmBeforeCommit && newEntries.length > 0) {
2291
+ console.log();
2292
+ console.log(chalk10.cyan("Proposed memories:"));
1999
2293
  for (let i = 0; i < newEntries.length; i++) {
2000
- const editRl = readline3.createInterface({ input: process.stdin, output: process.stdout });
2001
- const edited = await new Promise((resolve) => {
2002
- editRl.question(` ${i + 1}. ${chalk10.dim("(enter to keep, or type replacement)")}
2294
+ console.log(chalk10.green(` ${i + 1}. `) + newEntries[i].content);
2295
+ }
2296
+ console.log();
2297
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
2298
+ const answer = await new Promise((resolve) => {
2299
+ rl.question(" Save these memories? [Y/n/edit] ", (ans) => {
2300
+ rl.close();
2301
+ resolve(ans.trim().toLowerCase());
2302
+ });
2303
+ });
2304
+ if (answer === "n" || answer === "no") {
2305
+ console.log(chalk10.dim(" Aborted. Engrams left as pending."));
2306
+ closeCortexDb(cortex);
2307
+ return;
2308
+ }
2309
+ if (answer === "e" || answer === "edit") {
2310
+ for (let i = 0; i < newEntries.length; i++) {
2311
+ const editRl = readline3.createInterface({ input: process.stdin, output: process.stdout });
2312
+ const edited = await new Promise((resolve) => {
2313
+ editRl.question(` ${i + 1}. ${chalk10.dim("(enter to keep, or type replacement)")}
2003
2314
  ${newEntries[i].content}
2004
2315
  > `, (ans) => {
2005
- editRl.close();
2006
- resolve(ans.trim());
2316
+ editRl.close();
2317
+ resolve(ans.trim());
2318
+ });
2007
2319
  });
2008
- });
2009
- if (edited) {
2010
- newEntries[i].content = edited;
2320
+ if (edited) {
2321
+ newEntries[i].content = edited;
2322
+ }
2011
2323
  }
2012
2324
  }
2013
2325
  }
2014
- }
2015
- if (newEntries.length > 0) {
2016
- for (const entry of newEntries) {
2017
- insertMemory(cortex, {
2018
- ts: entry.ts,
2019
- author: entry.author,
2020
- content: entry.content,
2021
- source_ids: entry.source_ids,
2022
- decisions: entry.decisions
2023
- });
2326
+ if (newEntries.length > 0) {
2327
+ for (const entry of newEntries) {
2328
+ insertMemory(cortex, {
2329
+ ts: entry.ts,
2330
+ author: entry.author,
2331
+ content: entry.content,
2332
+ source_ids: entry.source_ids,
2333
+ decisions: entry.decisions
2334
+ });
2335
+ }
2024
2336
  }
2025
- }
2026
- if (promotedIds.size > 0) {
2027
- markPromoted(cortex, [...promotedIds]);
2028
- }
2029
- if (purgedIds.length > 0) {
2030
- markPurged(cortex, purgedIds);
2031
- }
2032
- const pruned = pruneExpiredEngrams(cortex);
2033
- if (older.length > 0 && !longtermSummary) {
2034
- console.log(chalk10.dim(` Consolidating ${older.length} older memories into long-term summary...`));
2035
- try {
2036
- const newSummary = await runConsolidation(null, older);
2037
- setLongtermSummary(cortex, newSummary);
2038
- console.log(chalk10.dim(` Long-term summary created`));
2039
- } catch {
2040
- console.log(chalk10.dim(` Long-term consolidation skipped (will retry next run)`));
2337
+ if (promotedIds.size > 0) {
2338
+ markPromoted(cortex, [...promotedIds]);
2041
2339
  }
2042
- }
2043
- if (adapter?.isAvailable() && newEntries.length > 0) {
2044
- try {
2045
- const pushResult = await adapter.push(cortex);
2046
- if (pushResult.pushed > 0) {
2047
- console.log(chalk10.dim(` Pushed ${pushResult.pushed} memories to ${adapter.name}`));
2340
+ if (purgedIds.length > 0) {
2341
+ markPurged(cortex, purgedIds);
2342
+ }
2343
+ const pruned = pruneExpiredEngrams(cortex);
2344
+ if (older.length > 0 && !longtermSummary) {
2345
+ console.log(chalk10.dim(` Consolidating ${older.length} older memories into long-term summary...`));
2346
+ try {
2347
+ const newSummary = await runConsolidation(null, older);
2348
+ setLongtermSummary(cortex, newSummary);
2349
+ console.log(chalk10.dim(` Long-term summary created`));
2350
+ } catch {
2351
+ console.log(chalk10.dim(` Long-term consolidation skipped (will retry next run)`));
2048
2352
  }
2049
- } catch {
2050
- console.log(chalk10.dim(" Sync push skipped (remote unavailable) \u2014 will push on next sync"));
2051
2353
  }
2354
+ if (adapter?.isAvailable() && newEntries.length > 0) {
2355
+ try {
2356
+ const pushResult = await adapter.push(cortex);
2357
+ if (pushResult.pushed > 0) {
2358
+ console.log(chalk10.dim(` Pushed ${pushResult.pushed} memories to ${adapter.name}`));
2359
+ }
2360
+ } catch {
2361
+ console.log(chalk10.dim(" Sync push skipped (remote unavailable) \u2014 will push on next sync"));
2362
+ }
2363
+ }
2364
+ console.log();
2365
+ console.log(`${chalk10.green("\u2713")} Curation complete`);
2366
+ console.log(` ${pending.length} evaluated, ${newEntries.length} promoted, ${purgedIds.length} purged, ${heldCount} still pending`);
2367
+ if (pruned > 0) {
2368
+ console.log(` ${pruned} expired engrams pruned`);
2369
+ }
2370
+ closeCortexDb(cortex);
2371
+ } finally {
2372
+ releaseLock();
2052
2373
  }
2053
- console.log();
2054
- console.log(`${chalk10.green("\u2713")} Curation complete`);
2055
- console.log(` ${pending.length} evaluated, ${newEntries.length} promoted, ${purgedIds.length} purged, ${heldCount} still pending`);
2056
- if (pruned > 0) {
2057
- console.log(` ${pruned} expired engrams pruned`);
2058
- }
2059
- closeCortexDb(cortex);
2060
2374
  });
2375
+ var DEFAULT_IDLE_WINDOW_MINUTES = 3;
2376
+ var DEFAULT_STALE_WINDOW_MINUTES = 60;
2377
+ function shouldRunIdleCuration(cortex, cortexConfig) {
2378
+ const pending = getPendingEngrams(cortex);
2379
+ if (pending.length === 0) {
2380
+ return { run: false, reason: "no pending engrams" };
2381
+ }
2382
+ const idleMinutes = cortexConfig?.idleWindowMinutes ?? DEFAULT_IDLE_WINDOW_MINUTES;
2383
+ const staleMinutes = cortexConfig?.staleWindowMinutes ?? DEFAULT_STALE_WINDOW_MINUTES;
2384
+ const now = Date.now();
2385
+ const oldest = pending[0];
2386
+ const newest = pending[pending.length - 1];
2387
+ const oldestAgeMin = (now - new Date(oldest.created_at).getTime()) / 6e4;
2388
+ const newestAgeMin = (now - new Date(newest.created_at).getTime()) / 6e4;
2389
+ if (oldestAgeMin >= staleMinutes) {
2390
+ return { run: true, reason: `staleness cap hit (oldest pending ${oldestAgeMin.toFixed(1)}min old)` };
2391
+ }
2392
+ if (newestAgeMin < idleMinutes) {
2393
+ return { run: false, reason: `still active (newest engram ${newestAgeMin.toFixed(1)}min old, idle threshold ${idleMinutes}min)` };
2394
+ }
2395
+ return { run: true, reason: `idle (${pending.length} pending, newest ${newestAgeMin.toFixed(1)}min old)` };
2396
+ }
2061
2397
 
2062
2398
  // src/commands/monitor.ts
2063
2399
  import { Command as Command11 } from "commander";
@@ -2122,7 +2458,7 @@ var recallCommand = new Command12("recall").argument("<query>", "What to recall"
2122
2458
  }
2123
2459
  const limit = parseInt(opts.limit, 10);
2124
2460
  if (opts.all) {
2125
- const { getMemories: getMemories2 } = await import("./memory-queries-N4VT5G2E.js");
2461
+ const { getMemories: getMemories2 } = await import("./memory-queries-QKGOKRFR.js");
2126
2462
  const days = parseInt(opts.days, 10);
2127
2463
  const cutoff = new Date(Date.now() - days * 864e5).toISOString();
2128
2464
  const recentMemories = getMemories2(cortex, { since: cutoff });
@@ -2278,7 +2614,7 @@ memoryCommand.addCommand(addCommand);
2278
2614
  // src/commands/curator-cmd.ts
2279
2615
  import { Command as Command14 } from "commander";
2280
2616
  import { spawnSync } from "child_process";
2281
- import fs9 from "fs";
2617
+ import fs11 from "fs";
2282
2618
  import chalk14 from "chalk";
2283
2619
  var CURATOR_TEMPLATE = `# Curator Guidance
2284
2620
 
@@ -2297,8 +2633,8 @@ var curatorCommand = new Command14("curator").description("Manage personal curat
2297
2633
  curatorCommand.addCommand(new Command14("edit").description("Edit your curator guidance in $EDITOR").action(() => {
2298
2634
  ensureThinkDirs();
2299
2635
  const mdPath = getCuratorMdPath();
2300
- if (!fs9.existsSync(mdPath)) {
2301
- fs9.writeFileSync(mdPath, CURATOR_TEMPLATE, "utf-8");
2636
+ if (!fs11.existsSync(mdPath)) {
2637
+ fs11.writeFileSync(mdPath, CURATOR_TEMPLATE, "utf-8");
2302
2638
  }
2303
2639
  const editor = process.env.EDITOR || "vi";
2304
2640
  const result = spawnSync(editor, [mdPath], { stdio: "inherit" });
@@ -2310,8 +2646,8 @@ curatorCommand.addCommand(new Command14("edit").description("Edit your curator g
2310
2646
  }));
2311
2647
  curatorCommand.addCommand(new Command14("show").description("Print your current curator guidance").action(() => {
2312
2648
  const mdPath = getCuratorMdPath();
2313
- if (fs9.existsSync(mdPath)) {
2314
- console.log(fs9.readFileSync(mdPath, "utf-8"));
2649
+ if (fs11.existsSync(mdPath)) {
2650
+ console.log(fs11.readFileSync(mdPath, "utf-8"));
2315
2651
  } else {
2316
2652
  console.log(chalk14.dim("No curator guidance configured. Run: think curator edit"));
2317
2653
  }
@@ -2375,6 +2711,8 @@ var ALLOWED_KEYS = /* @__PURE__ */ new Set([
2375
2711
  "cortex.repo",
2376
2712
  "cortex.active",
2377
2713
  "cortex.engramTTLDays",
2714
+ "cortex.idleWindowMinutes",
2715
+ "cortex.staleWindowMinutes",
2378
2716
  "paused"
2379
2717
  ]);
2380
2718
  var configCommand = new Command17("config").description("View or update think configuration");
@@ -2408,12 +2746,12 @@ configCommand.addCommand(new Command17("set").argument("<key>", "Config key (e.g
2408
2746
 
2409
2747
  // src/commands/update.ts
2410
2748
  import { Command as Command18 } from "commander";
2411
- import { execFileSync } from "child_process";
2749
+ import { execFileSync as execFileSync2 } from "child_process";
2412
2750
  import chalk18 from "chalk";
2413
2751
  var updateCommand = new Command18("update").description("Update think to the latest version").action(() => {
2414
2752
  console.log(chalk18.cyan("Checking for updates..."));
2415
2753
  try {
2416
- const result = execFileSync("npm", ["install", "-g", "open-think@latest"], {
2754
+ const result = execFileSync2("npm", ["install", "-g", "open-think@latest"], {
2417
2755
  encoding: "utf-8",
2418
2756
  stdio: ["pipe", "pipe", "pipe"]
2419
2757
  });
@@ -2431,7 +2769,7 @@ var updateCommand = new Command18("update").description("Update think to the lat
2431
2769
 
2432
2770
  // src/commands/migrate-data.ts
2433
2771
  import { Command as Command19 } from "commander";
2434
- import fs10 from "fs";
2772
+ import fs12 from "fs";
2435
2773
  import chalk19 from "chalk";
2436
2774
  var migrateDataCommand = new Command19("migrate-data").description("Import existing memories from git into local SQLite (one-time migration)").action(async () => {
2437
2775
  const config = getConfig();
@@ -2469,8 +2807,8 @@ var migrateDataCommand = new Command19("migrate-data").description("Import exist
2469
2807
  if (wasInserted) inserted++;
2470
2808
  }
2471
2809
  const ltPath = getLongtermPath(cortex);
2472
- if (fs10.existsSync(ltPath)) {
2473
- const ltContent = fs10.readFileSync(ltPath, "utf-8").trim();
2810
+ if (fs12.existsSync(ltPath)) {
2811
+ const ltContent = fs12.readFileSync(ltPath, "utf-8").trim();
2474
2812
  if (ltContent) {
2475
2813
  setLongtermSummary(cortex, ltContent);
2476
2814
  console.log(chalk19.green(" \u2713") + " Long-term summary migrated");
@@ -2489,8 +2827,8 @@ var migrateDataCommand = new Command19("migrate-data").description("Import exist
2489
2827
  // src/index.ts
2490
2828
  function readPackageVersion() {
2491
2829
  try {
2492
- const pkgPath = path5.join(import.meta.dirname, "..", "package.json");
2493
- return JSON.parse(fs11.readFileSync(pkgPath, "utf-8")).version ?? "0.0.0";
2830
+ const pkgPath = path7.join(import.meta.dirname, "..", "package.json");
2831
+ return JSON.parse(fs13.readFileSync(pkgPath, "utf-8")).version ?? "0.0.0";
2494
2832
  } catch {
2495
2833
  return "0.0.0";
2496
2834
  }
@@ -12,8 +12,8 @@ import {
12
12
  setLongtermSummary,
13
13
  setSyncCursor,
14
14
  tombstoneMemory
15
- } from "./chunk-OFGWR45G.js";
16
- import "./chunk-DCTG6IK4.js";
15
+ } from "./chunk-LN2TIS5R.js";
16
+ import "./chunk-HUBRLTY3.js";
17
17
  export {
18
18
  getLongtermSummary,
19
19
  getMemories,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-think",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "type": "module",
5
5
  "description": "Local-first CLI that gives AI agents persistent, curated memory",
6
6
  "bin": {